From 73e6ce75b0d52bad9791a15b4f0692af7c1188bf Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 16 Jul 2023 12:38:32 +0500 Subject: [PATCH 001/205] Replace "tell version to client" part with simple ack because YAGNI --- .../core/network/NetworkClient.java | 7 +---- .../core/network/NetworkEventSource.java | 31 +------------------ .../core/network/NetworkServer.java | 25 +++++++++++---- .../core/world/DhClientWorld.java | 2 +- 4 files changed, 22 insertions(+), 43 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index f9b4b9dbc..04e91b98f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -59,7 +59,7 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable private void registerHandlers() { - this.registerHandler(HelloMessage.class, (helloMessage, channelContext) -> + this.registerAckHandler(HelloMessage.class, channelContext -> { LOGGER.info("Connected to server: "+channelContext.channel().remoteAddress()); }); @@ -136,11 +136,6 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable @Override public void close() { - if (this.closeReason != null) - { - LOGGER.error(this.closeReason); - } - if (this.connectionState == EConnectionState.CLOSED) { return; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index f12967cb6..97f9789a4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -2,10 +2,8 @@ package com.seibel.distanthorizons.core.network; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.network.messages.AckMessage; -import com.seibel.distanthorizons.core.network.messages.HelloMessage; import com.seibel.distanthorizons.core.network.protocol.INetworkMessage; import com.seibel.distanthorizons.core.network.protocol.MessageHandler; -import com.seibel.distanthorizons.coreapi.ModInfo; import io.netty.channel.ChannelHandlerContext; import org.apache.logging.log4j.Logger; @@ -17,29 +15,8 @@ public abstract class NetworkEventSource implements AutoCloseable private static final Logger LOGGER = DhLoggerBuilder.getLogger(); protected final MessageHandler messageHandler = new MessageHandler(); - protected String closeReason = null; - - - public NetworkEventSource() - { - this.registerHandler(HelloMessage.class, (helloMessage, channelContext) -> - { - if (helloMessage.version != ModInfo.PROTOCOL_VERSION) - { - try - { - String closeReason = "Ignoring message from channel ["+channelContext.name()+"], due to version mismatch. Expected version: ["+ModInfo.PROTOCOL_VERSION+"], received version: ["+helloMessage.version+"]."; - LOGGER.info(closeReason); - this.close(closeReason); - } - catch (Exception e) - { - throw new RuntimeException(e); - } - } - }); - } + public void registerHandler(Class clazz, BiConsumer handler) { this.messageHandler.registerHandler(clazz, handler); } @@ -54,10 +31,4 @@ public abstract class NetworkEventSource implements AutoCloseable }); } - public void close(String reason) throws Exception - { - this.closeReason = reason; - this.close(); - } - } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java index b66e79879..8645a6967 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java @@ -1,10 +1,12 @@ package com.seibel.distanthorizons.core.network; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.network.messages.AckMessage; import com.seibel.distanthorizons.core.network.messages.CloseReasonMessage; import com.seibel.distanthorizons.core.network.messages.CloseMessage; import com.seibel.distanthorizons.core.network.messages.HelloMessage; import com.seibel.distanthorizons.core.network.protocol.NetworkChannelInitializer; +import com.seibel.distanthorizons.coreapi.ModInfo; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; @@ -41,7 +43,23 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable this.registerHandler(HelloMessage.class, (helloMessage, channelContext) -> { LOGGER.info("Client connected: "+channelContext.channel().remoteAddress()); - channelContext.channel().writeAndFlush(new HelloMessage()); + + if (helloMessage.version != ModInfo.PROTOCOL_VERSION) + { + try + { + String disconnectReason = "Version mismatch. Server version: ["+ModInfo.PROTOCOL_VERSION+"], client version: ["+helloMessage.version+"]."; + LOGGER.info("Disconnecting the client ["+channelContext.name()+"]: "+disconnectReason); + this.disconnectClient(channelContext, disconnectReason); + } + catch (Exception e) + { + throw new RuntimeException(e); + } + return; + } + + channelContext.writeAndFlush(new AckMessage(HelloMessage.class)); }); this.registerHandler(CloseMessage.class, (closeMessage, channelContext) -> @@ -83,11 +101,6 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable @Override public void close() { - if (this.closeReason != null) - { - LOGGER.error(this.closeReason); - } - if (this.isClosed) { return; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java index 6a159bfd4..24a4df791 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java @@ -50,7 +50,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld } private void registerNetworkHandlers() { - networkClient.registerHandler(HelloMessage.class, (msg, ctx) -> { + networkClient.registerAckHandler(HelloMessage.class, ctx -> { ctx.writeAndFlush(new PlayerUUIDMessage(MC_CLIENT.getPlayerUUID())); }); From 02acfaa3ed0fd067f06fd477097092d383615d3b Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Wed, 19 Jul 2023 12:19:39 +0500 Subject: [PATCH 002/205] Finer control over message handling (untested) --- .../RemoteFullDataFileHandler.java | 44 +++++++++++++++++-- .../SubDimensionLevelMatcher.java | 6 +-- .../core/level/DhClientLevel.java | 6 ++- .../core/network/ChildNetworkEventSource.java | 43 ++++++++++++++++++ .../core/network/NetworkClient.java | 7 ++- .../core/network/NetworkEventSource.java | 44 ++++++++++++++++--- .../core/network/NetworkServer.java | 5 ++- .../core/network/protocol/MessageHandler.java | 29 ++++-------- .../core/world/DhClientWorld.java | 3 +- 9 files changed, 149 insertions(+), 38 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/ChildNetworkEventSource.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java index 575dd2666..d24e70beb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java @@ -1,12 +1,50 @@ package com.seibel.distanthorizons.core.file.fullDatafile; +import com.google.common.collect.HashMultimap; +import com.google.common.collect.Multimap; +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.level.IDhLevel; +import com.seibel.distanthorizons.core.network.ChildNetworkEventSource; +import com.seibel.distanthorizons.core.network.NetworkClient; +import com.seibel.distanthorizons.core.pos.DhSectionPos; +import io.netty.channel.ChannelHandlerContext; -import java.io.File; +import java.util.concurrent.CompletableFuture; public class RemoteFullDataFileHandler extends FullDataFileHandler { - public RemoteFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure) { super(level, saveStructure); } - + private final Multimap chunkRequests = HashMultimap.create(); + + public RemoteFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure, ChildNetworkEventSource eventSource) { + super(level, saveStructure); + this.registerNetworkHandlers(eventSource); + } + + private void registerNetworkHandlers(ChildNetworkEventSource eventSource) { + //eventSource.registerHandler(); + } + + @Override + public CompletableFuture read(DhSectionPos pos) { + // TODO read and force update somehow instead ???? + return super.read(pos).handle((fullDataSource, throwable) -> { + if (fullDataSource == null) { + + } + + return fullDataSource; + }); + } + + @Override + public void close() { + super.close(); + + + } + + private static class ChunkRequest { + + } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java index e99c91f13..183be035d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java @@ -216,14 +216,14 @@ public class SubDimensionLevelMatcher implements AutoCloseable IClientLevelWrapper clientLevelWrapper = null; if (clientLevelWrapper == null) { - // TODO level shouldn't be null, continuing would probably cause a null pointer crash + // TODO Sub dimension level matcher is incomplete LOGGER.info(this.getClass().getSimpleName() + " implementation incomplete. Unable to get LOD data file from generic folder without [" + IClientLevelWrapper.class.getSimpleName() + "]."); break; } - IDhLevel tempLevel = new DhClientLevel(new ClientOnlySaveStructure(), clientLevelWrapper); + IDhLevel tempLevel = null; // new DhClientLevel(new ClientOnlySaveStructure(), clientLevelWrapper, ???); IFullDataSourceProvider fileHandler = new FullDataFileHandler(tempLevel, tempLevel.getSaveStructure()); CompletableFuture testDataSource = fileHandler.read(new DhSectionPos(playerChunkPos)); - IFullDataSource lodDataSource = testDataSource.get(); + IFullDataSource lodDataSource = testDataSource.get(); // convert the data source into a raw LOD data array diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 7dd641339..367e59444 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -5,6 +5,8 @@ import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider import com.seibel.distanthorizons.core.file.fullDatafile.RemoteFullDataFileHandler; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.network.ChildNetworkEventSource; +import com.seibel.distanthorizons.core.network.NetworkClient; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; @@ -30,11 +32,11 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel // constructor // //=============// - public DhClientLevel(AbstractSaveStructure saveStructure, IClientLevelWrapper clientLevelWrapper) + public DhClientLevel(AbstractSaveStructure saveStructure, IClientLevelWrapper clientLevelWrapper, ChildNetworkEventSource eventSource) { this.levelWrapper = clientLevelWrapper; this.saveStructure = saveStructure; - dataFileHandler = new RemoteFullDataFileHandler(this, saveStructure); + dataFileHandler = new RemoteFullDataFileHandler(this, saveStructure, eventSource); clientside = new ClientLevelModule(this); clientside.startRenderer(); LOGGER.info("Started DHLevel for "+this.levelWrapper+" with saves at "+this.saveStructure); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/ChildNetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/ChildNetworkEventSource.java new file mode 100644 index 000000000..9ccd8c0dd --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/ChildNetworkEventSource.java @@ -0,0 +1,43 @@ +package com.seibel.distanthorizons.core.network; + +import com.seibel.distanthorizons.core.network.protocol.INetworkMessage; +import io.netty.channel.ChannelHandlerContext; + +import java.util.function.BiConsumer; + +/** Provides a way to register network message handlers which are expected to be removed later. */ +public final class ChildNetworkEventSource extends NetworkEventSource +{ + private final TParent parent; + private boolean isClosed = false; + + public ChildNetworkEventSource(TParent parent) + { + this.parent = parent; + } + public ChildNetworkEventSource(ChildNetworkEventSource child) + { + this(child.parent); + } + + @Override public void registerHandler(Class handlerClass, BiConsumer handlerImplementation) + { + if (isClosed) return; + + if (!this.hasHandler(handlerClass)) + { + parent.registerHandler(handlerClass, this::handleMessage); + } + + super.registerHandler(handlerClass, handlerImplementation); + } + + @Override public void close() + { + isClosed = true; + for (Class handlerClass : this.handlers.keySet()) + { + parent.removeHandler(handlerClass, this::handleMessage); + } + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index 04e91b98f..b35586bdf 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -4,6 +4,7 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.network.messages.CloseMessage; import com.seibel.distanthorizons.core.network.messages.CloseReasonMessage; import com.seibel.distanthorizons.core.network.messages.HelloMessage; +import com.seibel.distanthorizons.core.network.protocol.MessageHandler; import com.seibel.distanthorizons.core.network.protocol.NetworkChannelInitializer; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; @@ -41,7 +42,7 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable .group(this.workerGroup) .channel(NioSocketChannel.class) .option(ChannelOption.SO_KEEPALIVE, true) - .handler(new NetworkChannelInitializer(this.messageHandler)); + .handler(new NetworkChannelInitializer(new MessageHandler(this::handleMessage))); private EConnectionState connectionState; private Channel channel; @@ -132,7 +133,7 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable this.connectionState = EConnectionState.RECONNECT_FORCE; this.channel.disconnect(); } - + @Override public void close() { @@ -144,6 +145,8 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable this.connectionState = EConnectionState.CLOSED; this.workerGroup.shutdownGracefully().syncUninterruptibly(); this.channel.close().syncUninterruptibly(); + + super.close(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index 97f9789a4..1c88922fd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -3,26 +3,48 @@ package com.seibel.distanthorizons.core.network; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.network.messages.AckMessage; import com.seibel.distanthorizons.core.network.protocol.INetworkMessage; -import com.seibel.distanthorizons.core.network.protocol.MessageHandler; import io.netty.channel.ChannelHandlerContext; import org.apache.logging.log4j.Logger; +import java.util.*; import java.util.function.BiConsumer; import java.util.function.Consumer; -public abstract class NetworkEventSource implements AutoCloseable +public abstract class NetworkEventSource { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + protected final Map, Set>> handlers = new HashMap<>(); - protected final MessageHandler messageHandler = new MessageHandler(); - + protected boolean hasHandler(Class handlerClass) + { + return this.handlers.containsKey(handlerClass); + } - public void registerHandler(Class clazz, BiConsumer handler) { this.messageHandler.registerHandler(clazz, handler); } + protected void handleMessage(INetworkMessage message, ChannelHandlerContext channelContext) + { + Set> handlerList = this.handlers.get(message.getClass()); + if (handlerList == null || handlerList.isEmpty()) + { + LOGGER.warn("Unhandled message type: " + message.getClass().getSimpleName()); + return; + } + + for (BiConsumer handler : handlerList) + { + handler.accept(message, channelContext); + } + } + + public void registerHandler(Class handlerClass, BiConsumer handlerImplementation) + { + this.handlers.computeIfAbsent(handlerClass, missingHandlerClass -> new HashSet<>()) + .add((BiConsumer) handlerImplementation); + } public void registerAckHandler(Class clazz, Consumer handler) { - this.messageHandler.registerHandler(AckMessage.class, (ackMessage, channelContext) -> + this.registerHandler(AckMessage.class, (ackMessage, channelContext) -> { if (ackMessage.messageType == clazz) { @@ -31,4 +53,14 @@ public abstract class NetworkEventSource implements AutoCloseable }); } + protected void removeHandler(Class handlerClass, BiConsumer handlerImplementation) + { + this.handlers.computeIfAbsent(handlerClass, missingHandlerClass -> new HashSet<>()) + .remove(handlerImplementation); + } + + public void close() + { + this.handlers.clear(); + } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java index 8645a6967..6e69459d2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java @@ -5,6 +5,7 @@ import com.seibel.distanthorizons.core.network.messages.AckMessage; import com.seibel.distanthorizons.core.network.messages.CloseReasonMessage; import com.seibel.distanthorizons.core.network.messages.CloseMessage; import com.seibel.distanthorizons.core.network.messages.HelloMessage; +import com.seibel.distanthorizons.core.network.protocol.MessageHandler; import com.seibel.distanthorizons.core.network.protocol.NetworkChannelInitializer; import com.seibel.distanthorizons.coreapi.ModInfo; import io.netty.bootstrap.ServerBootstrap; @@ -74,7 +75,7 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable .group(this.bossGroup, this.workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.DEBUG)) - .childHandler(new NetworkChannelInitializer(this.messageHandler)); + .childHandler(new NetworkChannelInitializer(new MessageHandler(this::handleMessage))); ChannelFuture bindFuture = bootstrap.bind(this.port); bindFuture.addListener((ChannelFuture channelFuture) -> @@ -111,6 +112,8 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable this.workerGroup.shutdownGracefully().syncUninterruptibly(); this.bossGroup.shutdownGracefully().syncUninterruptibly(); LOGGER.info("Network server has been closed."); + + super.close(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java index 9fc9626d2..7a36c0196 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java @@ -19,35 +19,24 @@ public class MessageHandler extends SimpleChannelInboundHandler { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - private final Map, List>> handlers = new HashMap<>(); + private final BiConsumer messageConsumer; - - - public void registerHandler(Class handlerClass, BiConsumer handlerImplementation) + public MessageHandler(BiConsumer messageConsumer) { - this.handlers.computeIfAbsent(handlerClass, missingHandlerClass -> new LinkedList<>()) - .add((BiConsumer) handlerImplementation); + this.messageConsumer = messageConsumer; } @Override protected void channelRead0(ChannelHandlerContext channelContext, INetworkMessage message) { - LOGGER.trace("Received message: "+message.getClass().getSimpleName()); - - List> handlerList = this.handlers.get(message.getClass()); - if (handlerList == null) - { - LOGGER.warn("Unhandled message type: "+message.getClass().getSimpleName()); - return; - } - - for (BiConsumer handler : handlerList) - { - handler.accept(message, channelContext); - } + LOGGER.trace("Received message: " + message.getClass().getSimpleName()); + this.messageConsumer.accept(message, channelContext); } @Override - public void channelInactive(@NotNull ChannelHandlerContext channelContext) { this.channelRead0(channelContext, new CloseMessage()); } + public void channelInactive(@NotNull ChannelHandlerContext channelContext) + { + this.channelRead0(channelContext, new CloseMessage()); + } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java index 24a4df791..17e9ef3b0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java @@ -4,6 +4,7 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.level.DhClientLevel; +import com.seibel.distanthorizons.core.network.ChildNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkClient; import com.seibel.distanthorizons.core.network.messages.*; import com.seibel.distanthorizons.core.network.messages.PlayerUUIDMessage; @@ -85,7 +86,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld return null; } - return new DhClientLevel(this.saveStructure, clientLevelWrapper); + return new DhClientLevel(this.saveStructure, clientLevelWrapper, new ChildNetworkEventSource<>(networkClient)); }); } From 218411902e26bf3a369130f26a8177c493eda396 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Wed, 19 Jul 2023 15:54:17 +0500 Subject: [PATCH 003/205] Incomplete something --- .../RemoteFullDataFileHandler.java | 36 ++++----- .../core/level/DhClientLevel.java | 4 +- .../core/network/NetworkClient.java | 4 + .../IFutureTrackableNetworkMessage.java | 8 ++ .../network/future/NetworkRequestTracker.java | 73 +++++++++++++++++++ .../network/messages/ChunkRequestMessage.java | 24 ++++++ .../messages/ChunkResponseMessage.java | 22 ++++++ .../messages/RequestChunksMessage.java | 21 ------ .../network/protocol/MessageRegistry.java | 2 +- .../core/pos/DhSectionPos.java | 16 +++- .../core/world/DhClientWorld.java | 5 +- .../core/world/DhServerWorld.java | 4 +- 12 files changed, 167 insertions(+), 52 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/future/IFutureTrackableNetworkMessage.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/future/NetworkRequestTracker.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkRequestMessage.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkResponseMessage.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/RequestChunksMessage.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java index d24e70beb..656108692 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java @@ -1,50 +1,44 @@ package com.seibel.distanthorizons.core.file.fullDatafile; -import com.google.common.collect.HashMultimap; -import com.google.common.collect.Multimap; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.network.ChildNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkClient; +import com.seibel.distanthorizons.core.network.future.NetworkRequestTracker; +import com.seibel.distanthorizons.core.network.messages.ChunkRequestMessage; +import com.seibel.distanthorizons.core.network.messages.ChunkResponseMessage; import com.seibel.distanthorizons.core.pos.DhSectionPos; -import io.netty.channel.ChannelHandlerContext; import java.util.concurrent.CompletableFuture; public class RemoteFullDataFileHandler extends FullDataFileHandler { - private final Multimap chunkRequests = HashMultimap.create(); - - public RemoteFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure, ChildNetworkEventSource eventSource) { + private final NetworkClient networkClient; + private final NetworkRequestTracker chunkRequestTracker; + + public RemoteFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure, NetworkClient networkClient) { super(level, saveStructure); - this.registerNetworkHandlers(eventSource); - } - - private void registerNetworkHandlers(ChildNetworkEventSource eventSource) { - //eventSource.registerHandler(); + this.networkClient = networkClient; + this.chunkRequestTracker = new NetworkRequestTracker<>(networkClient, ChunkResponseMessage.class); } @Override public CompletableFuture read(DhSectionPos pos) { // TODO read and force update somehow instead ???? - return super.read(pos).handle((fullDataSource, throwable) -> { - if (fullDataSource == null) { - + return super.read(pos).thenCompose((fullDataSource) -> { + if (fullDataSource != null) { + return CompletableFuture.completedFuture(fullDataSource); } - return fullDataSource; + CompletableFuture responseFuture = chunkRequestTracker.sendRequest(networkClient.getChannel(), new ChunkRequestMessage()); + }); } @Override public void close() { super.close(); - - - } - - private static class ChunkRequest { - + chunkRequestTracker.close(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 367e59444..42a9f8045 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -32,11 +32,11 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel // constructor // //=============// - public DhClientLevel(AbstractSaveStructure saveStructure, IClientLevelWrapper clientLevelWrapper, ChildNetworkEventSource eventSource) + public DhClientLevel(AbstractSaveStructure saveStructure, IClientLevelWrapper clientLevelWrapper, NetworkClient networkClient) { this.levelWrapper = clientLevelWrapper; this.saveStructure = saveStructure; - dataFileHandler = new RemoteFullDataFileHandler(this, saveStructure, eventSource); + dataFileHandler = new RemoteFullDataFileHandler(this, saveStructure, networkClient); clientside = new ClientLevelModule(this); clientside.startRenderer(); LOGGER.info("Started DHLevel for "+this.levelWrapper+" with saves at "+this.saveStructure); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index b35586bdf..468da601b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -134,6 +134,10 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable this.channel.disconnect(); } + public Channel getChannel() { + return channel; + } + @Override public void close() { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/future/IFutureTrackableNetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/future/IFutureTrackableNetworkMessage.java new file mode 100644 index 000000000..be3514034 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/future/IFutureTrackableNetworkMessage.java @@ -0,0 +1,8 @@ +package com.seibel.distanthorizons.core.network.future; + +import com.seibel.distanthorizons.core.network.protocol.INetworkMessage; + +public interface IFutureTrackableNetworkMessage> extends INetworkMessage +{ + TKey getRequestKey(); +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/future/NetworkRequestTracker.java b/core/src/main/java/com/seibel/distanthorizons/core/network/future/NetworkRequestTracker.java new file mode 100644 index 000000000..9eaffcd82 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/future/NetworkRequestTracker.java @@ -0,0 +1,73 @@ +package com.seibel.distanthorizons.core.network.future; + +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; +import com.seibel.distanthorizons.core.network.ChildNetworkEventSource; +import com.seibel.distanthorizons.core.network.NetworkEventSource; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundInvoker; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.CompletableFuture; + +public class NetworkRequestTracker + , TKey extends Comparable> + implements AutoCloseable +{ + private final ChildNetworkEventSource eventSource; + + private final Set knownChannels = new HashSet<>(); + private final Table> pendingFutures = HashBasedTable.create(); + + public NetworkRequestTracker( + NetworkEventSource eventSource, + Class responseClass) + { + this.eventSource = new ChildNetworkEventSource<>(eventSource); + registerNetworkHandlers(responseClass); + } + + private void registerNetworkHandlers(Class responseClass) + { + this.eventSource.registerHandler(responseClass, (msg, ctx) -> { + CompletableFuture future = pendingFutures.remove(ctx.channel(), msg.getRequestKey()); + if (future != null) { + future.complete(msg); + } + }); + } + + public CompletableFuture sendRequest(Channel channel, IFutureTrackableNetworkMessage msg) + { + if (knownChannels.add(channel)) + { + channel.closeFuture().addListener(closeFuture -> + { + pendingFutures.row(channel).values().removeIf(responseFuture -> + { + responseFuture.completeExceptionally(closeFuture.cause()); + return true; + }); + }); + } + + CompletableFuture responseFuture = new CompletableFuture<>(); + pendingFutures.put(channel, msg.getRequestKey(), responseFuture); + + channel.writeAndFlush(msg).addListener(writeFuture -> { + if (writeFuture.cause() != null) { + responseFuture.completeExceptionally(writeFuture.cause()); + } + }); + return responseFuture; + } + + @Override public void close() + { + this.eventSource.close(); + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkRequestMessage.java new file mode 100644 index 000000000..0c9a4d7c5 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkRequestMessage.java @@ -0,0 +1,24 @@ +package com.seibel.distanthorizons.core.network.messages; + +import com.seibel.distanthorizons.core.network.future.IFutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.pos.DhSectionPos; +import io.netty.buffer.ByteBuf; + +public class ChunkRequestMessage implements IFutureTrackableNetworkMessage +{ + public DhSectionPos dhSectionPos; + + @Override public DhSectionPos getRequestKey() { return dhSectionPos; } + + @Override + public void encode(ByteBuf out) + { + + } + + @Override + public void decode(ByteBuf in) + { + + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkResponseMessage.java new file mode 100644 index 000000000..cc2fa1a0b --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkResponseMessage.java @@ -0,0 +1,22 @@ +package com.seibel.distanthorizons.core.network.messages; + +import com.seibel.distanthorizons.core.network.future.IFutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.pos.DhSectionPos; +import io.netty.buffer.ByteBuf; + +public class ChunkResponseMessage implements IFutureTrackableNetworkMessage +{ + public DhSectionPos dhSectionPos; + + @Override public DhSectionPos getRequestKey() { return dhSectionPos; } + + @Override public void encode(ByteBuf out) + { + + } + + @Override public void decode(ByteBuf in) + { + + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/RequestChunksMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/RequestChunksMessage.java deleted file mode 100644 index 15312601e..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/RequestChunksMessage.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.seibel.distanthorizons.core.network.messages; - -import com.seibel.distanthorizons.core.network.protocol.INetworkMessage; -import io.netty.buffer.ByteBuf; - -public class RequestChunksMessage implements INetworkMessage -{ - - @Override - public void encode(ByteBuf out) - { - - } - - @Override - public void decode(ByteBuf in) - { - - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java index ea952c4ad..0344e272f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java @@ -29,7 +29,7 @@ public class MessageRegistry this.registerMessage(AckMessage.class, AckMessage::new); this.registerMessage(PlayerUUIDMessage.class, PlayerUUIDMessage::new); this.registerMessage(RemotePlayerConfigMessage.class, RemotePlayerConfigMessage::new); - this.registerMessage(RequestChunksMessage.class, RequestChunksMessage::new); + this.registerMessage(ChunkRequestMessage.class, ChunkRequestMessage::new); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java index ae9edde62..87d87b9bd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java @@ -3,6 +3,7 @@ package com.seibel.distanthorizons.core.pos; import com.seibel.distanthorizons.core.enums.ELodDirection; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import com.seibel.distanthorizons.core.util.LodUtil; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.function.Consumer; @@ -18,7 +19,7 @@ import java.util.function.Consumer; * @author Leetom * @version 2022-11-6 */ -public class DhSectionPos +public class DhSectionPos implements Comparable { /** * The lowest detail level a Section position can hold. @@ -238,6 +239,18 @@ public class DhSectionPos this.sectionZ == that.sectionZ; } + @Override public int compareTo(@NotNull DhSectionPos o) + { + if (this.sectionDetailLevel != o.sectionDetailLevel) + return this.sectionDetailLevel - o.sectionDetailLevel; + if (this.sectionX != o.sectionX) + return this.sectionX - o.sectionX; + if (this.sectionZ != o.sectionZ) + return this.sectionZ - o.sectionZ; + + return 0; + } + @Override public int hashCode() { @@ -245,5 +258,4 @@ public class DhSectionPos Integer.hashCode(this.sectionX) ^ // XOR Integer.hashCode(this.sectionZ); } - } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java index 17e9ef3b0..e2fa70bdf 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java @@ -4,7 +4,6 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.level.DhClientLevel; -import com.seibel.distanthorizons.core.network.ChildNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkClient; import com.seibel.distanthorizons.core.network.messages.*; import com.seibel.distanthorizons.core.network.messages.PlayerUUIDMessage; @@ -65,7 +64,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld networkClient.registerAckHandler(RemotePlayerConfigMessage.class, ctx -> { // TODO Actually request chunks - ctx.writeAndFlush(new RequestChunksMessage()); + ctx.writeAndFlush(new ChunkRequestMessage()); }); } @@ -86,7 +85,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld return null; } - return new DhClientLevel(this.saveStructure, clientLevelWrapper, new ChildNetworkEventSource<>(networkClient)); + return new DhClientLevel(this.saveStructure, clientLevelWrapper, networkClient); }); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java index dbbea1b33..ab76ee2e3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java @@ -7,7 +7,7 @@ import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.network.NetworkServer; import com.seibel.distanthorizons.core.network.messages.*; -import com.seibel.distanthorizons.core.network.messages.RequestChunksMessage; +import com.seibel.distanthorizons.core.network.messages.ChunkRequestMessage; import com.seibel.distanthorizons.core.network.objects.RemotePlayer; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; @@ -86,7 +86,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld channelContext.writeAndFlush(new AckMessage(RemotePlayerConfigMessage.class)); }); - this.networkServer.registerHandler(RequestChunksMessage.class, (msg, ctx) -> + this.networkServer.registerHandler(ChunkRequestMessage.class, (msg, ctx) -> { LOGGER.info("RequestChunksMessage"); // hasReceivedChunkRequest should be false somewhere ??? From 79408c081a11ba1aedd9fdcfaa2cb61c5f98c9ad Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Wed, 19 Jul 2023 17:40:38 +0500 Subject: [PATCH 004/205] Close network request tacker properly --- .../network/future/NetworkRequestTracker.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/future/NetworkRequestTracker.java b/core/src/main/java/com/seibel/distanthorizons/core/network/future/NetworkRequestTracker.java index 9eaffcd82..f04dc5d46 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/future/NetworkRequestTracker.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/future/NetworkRequestTracker.java @@ -44,16 +44,7 @@ public class NetworkRequestTracker public CompletableFuture sendRequest(Channel channel, IFutureTrackableNetworkMessage msg) { if (knownChannels.add(channel)) - { - channel.closeFuture().addListener(closeFuture -> - { - pendingFutures.row(channel).values().removeIf(responseFuture -> - { - responseFuture.completeExceptionally(closeFuture.cause()); - return true; - }); - }); - } + channel.closeFuture().addListener(closeFuture -> completeAllExceptionally(channel, closeFuture.cause())); CompletableFuture responseFuture = new CompletableFuture<>(); pendingFutures.put(channel, msg.getRequestKey(), responseFuture); @@ -66,8 +57,17 @@ public class NetworkRequestTracker return responseFuture; } + private void completeAllExceptionally(Channel channel, Throwable cause) { + for (CompletableFuture responseFuture : pendingFutures.row(channel).values()) { + responseFuture.completeExceptionally(cause); + }; + pendingFutures.row(channel).clear(); + } + @Override public void close() { this.eventSource.close(); + for (Channel channel : pendingFutures.rowKeySet()) + this.completeAllExceptionally(channel, new Exception(this.getClass().getSimpleName()+" is closed.")); } } From 9a0c9e9b7d0a0f821c782a0b23fd361fbe019429 Mon Sep 17 00:00:00 2001 From: Steveplays28 Date: Wed, 19 Jul 2023 16:48:20 +0200 Subject: [PATCH 005/205] feat: Implement syncing of `DhSectionPos` for chunk requests/responses --- .../RemoteFullDataFileHandler.java | 12 ++- .../network/messages/ChunkRequestMessage.java | 14 +++- .../messages/ChunkResponseMessage.java | 8 +- .../core/pos/DhSectionPos.java | 24 +++++- .../core/world/DhClientWorld.java | 4 +- .../core/world/DhServerWorld.java | 82 +++++++++++-------- .../misc/IServerPlayerWrapper.java | 5 +- 7 files changed, 99 insertions(+), 50 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java index 656108692..1daaa6962 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java @@ -10,6 +10,7 @@ import com.seibel.distanthorizons.core.network.messages.ChunkRequestMessage; import com.seibel.distanthorizons.core.network.messages.ChunkResponseMessage; import com.seibel.distanthorizons.core.pos.DhSectionPos; +import java.nio.file.FileAlreadyExistsException; import java.util.concurrent.CompletableFuture; public class RemoteFullDataFileHandler extends FullDataFileHandler @@ -25,14 +26,11 @@ public class RemoteFullDataFileHandler extends FullDataFileHandler @Override public CompletableFuture read(DhSectionPos pos) { - // TODO read and force update somehow instead ???? + // TODO: LOD data file updating is probably incomplete return super.read(pos).thenCompose((fullDataSource) -> { - if (fullDataSource != null) { - return CompletableFuture.completedFuture(fullDataSource); - } - - CompletableFuture responseFuture = chunkRequestTracker.sendRequest(networkClient.getChannel(), new ChunkRequestMessage()); - + FullDataMetaFile metaFile = this.getLoadOrMakeFile(pos, true); + CompletableFuture responseFuture = chunkRequestTracker.sendRequest(networkClient.getChannel(), new ChunkRequestMessage(pos)); + return onDataFileUpdate(fullDataSource, metaFile, iFullDataSource -> {}, iFullDataSource -> true); }); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkRequestMessage.java index 0c9a4d7c5..72eb85870 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkRequestMessage.java @@ -7,17 +7,23 @@ import io.netty.buffer.ByteBuf; public class ChunkRequestMessage implements IFutureTrackableNetworkMessage { public DhSectionPos dhSectionPos; - + + public ChunkRequestMessage() {} + + public ChunkRequestMessage(DhSectionPos dhSectionPos) { + this.dhSectionPos = dhSectionPos; + } + @Override public DhSectionPos getRequestKey() { return dhSectionPos; } - + @Override - public void encode(ByteBuf out) + public void encode(ByteBuf out) { } @Override - public void decode(ByteBuf in) + public void decode(ByteBuf in) { } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkResponseMessage.java index cc2fa1a0b..d2abba9b7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkResponseMessage.java @@ -7,12 +7,18 @@ import io.netty.buffer.ByteBuf; public class ChunkResponseMessage implements IFutureTrackableNetworkMessage { public DhSectionPos dhSectionPos; + + public ChunkResponseMessage() {} + + public ChunkResponseMessage(DhSectionPos dhSectionPos) { + this.dhSectionPos = dhSectionPos; + } @Override public DhSectionPos getRequestKey() { return dhSectionPos; } @Override public void encode(ByteBuf out) { - + } @Override public void decode(ByteBuf in) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java index 87d87b9bd..129d4deff 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java @@ -1,8 +1,10 @@ package com.seibel.distanthorizons.core.pos; import com.seibel.distanthorizons.core.enums.ELodDirection; +import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import com.seibel.distanthorizons.core.util.LodUtil; +import io.netty.buffer.ByteBuf; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -19,7 +21,7 @@ import java.util.function.Consumer; * @author Leetom * @version 2022-11-6 */ -public class DhSectionPos implements Comparable +public class DhSectionPos implements Comparable, INetworkObject { /** * The lowest detail level a Section position can hold. @@ -32,12 +34,12 @@ public class DhSectionPos implements Comparable public final static byte SECTION_REGION_DETAIL_LEVEL = SECTION_MINIMUM_DETAIL_LEVEL + LodUtil.REGION_DETAIL_LEVEL; - public final byte sectionDetailLevel; + public byte sectionDetailLevel; /** in a sectionDetailLevel grid */ - public final int sectionX; + public int sectionX; /** in a sectionDetailLevel grid */ - public final int sectionZ; + public int sectionZ; @@ -258,4 +260,18 @@ public class DhSectionPos implements Comparable Integer.hashCode(this.sectionX) ^ // XOR Integer.hashCode(this.sectionZ); } + + @Override + public void encode(ByteBuf out) { + out.writeByte(this.sectionDetailLevel); + out.writeInt(this.sectionX); + out.writeInt(this.sectionZ); + } + + @Override + public void decode(ByteBuf in) { + this.sectionDetailLevel = in.readByte(); + this.sectionX = in.readInt(); + this.sectionZ = in.readInt(); + } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java index e2fa70bdf..b36702b75 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java @@ -9,6 +9,8 @@ import com.seibel.distanthorizons.core.network.messages.*; import com.seibel.distanthorizons.core.network.messages.PlayerUUIDMessage; import com.seibel.distanthorizons.core.network.messages.RemotePlayerConfigMessage; import com.seibel.distanthorizons.core.network.objects.RemotePlayer; +import com.seibel.distanthorizons.core.pos.DhBlockPos2D; +import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.util.objects.EventLoop; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; @@ -64,7 +66,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld networkClient.registerAckHandler(RemotePlayerConfigMessage.class, ctx -> { // TODO Actually request chunks - ctx.writeAndFlush(new ChunkRequestMessage()); + ctx.writeAndFlush(new ChunkRequestMessage(new DhSectionPos(new DhBlockPos2D(0, 0)))); }); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java index ab76ee2e3..fac91db65 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java @@ -2,6 +2,8 @@ package com.seibel.distanthorizons.core.world; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; +import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile; +import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; import com.seibel.distanthorizons.core.file.structure.LocalSaveStructure; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.level.IDhLevel; @@ -9,6 +11,7 @@ import com.seibel.distanthorizons.core.network.NetworkServer; import com.seibel.distanthorizons.core.network.messages.*; import com.seibel.distanthorizons.core.network.messages.ChunkRequestMessage; import com.seibel.distanthorizons.core.network.objects.RemotePlayer; +import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; @@ -16,6 +19,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import io.netty.channel.ChannelHandlerContext; import java.io.File; +import java.nio.file.FileAlreadyExistsException; import java.util.HashMap; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -24,32 +28,32 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld { private final HashMap levels; public final LocalSaveStructure saveStructure; - + private final NetworkServer networkServer; private final HashMap playersByUUID; private final BiMap playersByConnection; - - - + + + public DhServerWorld() { super(EWorldEnvironment.Server_Only); - + this.saveStructure = new LocalSaveStructure(); this.levels = new HashMap<>(); - + // TODO move to global payload once server specific configs are implemented this.networkServer = new NetworkServer(25049); this.playersByUUID = new HashMap<>(); this.playersByConnection = HashBiMap.create(); this.registerNetworkHandlers(); - + LOGGER.info("Started "+DhServerWorld.class.getSimpleName()+" of type "+this.environment); } private void registerNetworkHandlers() { - this.networkServer.registerHandler(CloseMessage.class, (closeMessage, channelContext) -> + this.networkServer.registerHandler(CloseMessage.class, (closeMessage, channelContext) -> { RemotePlayer dhPlayer = this.playersByConnection.remove(channelContext); if (dhPlayer != null) @@ -57,43 +61,57 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld dhPlayer.channelContext = null; } }); - - this.networkServer.registerHandler(PlayerUUIDMessage.class, (playerUUIDMessage, channelContext) -> + + this.networkServer.registerHandler(PlayerUUIDMessage.class, (playerUUIDMessage, channelContext) -> { RemotePlayer dhPlayer = this.playersByUUID.get(playerUUIDMessage.playerUUID); - + if (dhPlayer == null) { this.networkServer.disconnectClient(channelContext, "Player is not logged in."); return; } - + if (dhPlayer.channelContext != null) { this.networkServer.disconnectClient(channelContext, "Another connection is already in use."); return; } - + dhPlayer.channelContext = channelContext; this.playersByConnection.put(channelContext, dhPlayer); - + channelContext.writeAndFlush(new AckMessage(PlayerUUIDMessage.class)); }); - - this.networkServer.registerHandler(RemotePlayerConfigMessage.class, (dhRemotePlayerConfigMessage, channelContext) -> + + this.networkServer.registerHandler(RemotePlayerConfigMessage.class, (dhRemotePlayerConfigMessage, channelContext) -> { // TODO Take notice of received payload and possibly echo back a constrained version channelContext.writeAndFlush(new AckMessage(RemotePlayerConfigMessage.class)); }); - + this.networkServer.registerHandler(ChunkRequestMessage.class, (msg, ctx) -> { - LOGGER.info("RequestChunksMessage"); + if (msg.dhSectionPos == null) { + LOGGER.warn("RequestChunksMessage received with null msg.dhSectionPos"); + return; + } + + LOGGER.info("RequestChunksMessage received at pos ({}, {}) with detail level {}", msg.dhSectionPos.sectionX, msg.dhSectionPos.sectionZ, msg.dhSectionPos.sectionDetailLevel); // hasReceivedChunkRequest should be false somewhere ??? // to avoid sending updates until client says at least something about its state + + // TODO: Get level from the player that sent the packet + DhServerLevel level = this.getLevel(playersByConnection.get(ctx).serverPlayer.getLevel()); + + // TODO: Add level to packet + level.serverside.worldGenTick(new DhBlockPos2D(msg.dhSectionPos.sectionX, msg.dhSectionPos.sectionZ)); + + // Send chunk response message back + ctx.writeAndFlush(new ChunkResponseMessage(msg.dhSectionPos)); }); } - + public void addPlayer(IServerPlayerWrapper serverPlayer) { this.playersByUUID.put(serverPlayer.getUUID(), new RemotePlayer(serverPlayer)); @@ -107,7 +125,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld this.networkServer.disconnectClient(channelContext, "You are being disconnected."); } } - + @Override public DhServerLevel getOrLoadLevel(ILevelWrapper wrapper) { @@ -115,7 +133,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld { return null; } - + return this.levels.computeIfAbsent((IServerLevelWrapper) wrapper, (serverLevelWrapper) -> { File levelFile = this.saveStructure.getLevelFolder(wrapper); @@ -123,7 +141,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld return new DhServerLevel(this.saveStructure, serverLevelWrapper); }); } - + @Override public DhServerLevel getLevel(ILevelWrapper wrapper) { @@ -131,13 +149,13 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld { return null; } - + return this.levels.get(wrapper); } - + @Override public Iterable getAllLoadedLevels() { return this.levels.values(); } - + @Override public void unloadLevel(ILevelWrapper wrapper) { @@ -145,24 +163,24 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld { return; } - + if (this.levels.containsKey(wrapper)) { LOGGER.info("Unloading level {} ", this.levels.get(wrapper)); this.levels.remove(wrapper).close(); } } - + public void serverTick() { this.levels.values().forEach(DhServerLevel::serverTick); } - + public void doWorldGen() { this.levels.values().forEach(DhServerLevel::doWorldGen); } - + @Override public CompletableFuture saveAndFlush() { return CompletableFuture.allOf(this.levels.values().stream().map(DhServerLevel::saveAsync).toArray(CompletableFuture[]::new)); } - + @Override public void close() { @@ -173,9 +191,9 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld LOGGER.info("Unloading level " + level.getLevelWrapper().getDimensionType().getDimensionName()); level.close(); } - + this.levels.clear(); LOGGER.info("Closed DhWorld of type "+this.environment); } - + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java index 329ca7c54..6b08113e4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java @@ -1,10 +1,13 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.misc; import com.seibel.distanthorizons.api.interfaces.IDhApiUnsafeWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import java.util.UUID; -public interface IServerPlayerWrapper extends IDhApiUnsafeWrapper +public interface IServerPlayerWrapper extends IDhApiUnsafeWrapper { UUID getUUID(); + + IServerLevelWrapper getLevel(); } From 4ac1c0d4b37a31f72a4a5c61afa4bb0b5987d982 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Wed, 19 Jul 2023 22:29:33 +0500 Subject: [PATCH 006/205] Sending some responses Rejoin is horribly broken --- .../fullDatafile/RemoteFullDataFileHandler.java | 12 +++++++++++- .../core/network/future/NetworkRequestTracker.java | 3 +-- .../core/network/messages/ChunkRequestMessage.java | 5 +++-- .../network/messages/ChunkResponseMessage.java | 11 +++++++---- .../core/network/protocol/MessageRegistry.java | 14 ++++++++++++-- .../distanthorizons/core/world/DhClientWorld.java | 2 +- 6 files changed, 35 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java index 1daaa6962..3526df785 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java @@ -3,18 +3,22 @@ package com.seibel.distanthorizons.core.file.fullDatafile; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.level.IDhLevel; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.network.ChildNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkClient; import com.seibel.distanthorizons.core.network.future.NetworkRequestTracker; import com.seibel.distanthorizons.core.network.messages.ChunkRequestMessage; import com.seibel.distanthorizons.core.network.messages.ChunkResponseMessage; import com.seibel.distanthorizons.core.pos.DhSectionPos; +import org.apache.logging.log4j.Logger; import java.nio.file.FileAlreadyExistsException; import java.util.concurrent.CompletableFuture; public class RemoteFullDataFileHandler extends FullDataFileHandler { + protected static final Logger LOGGER = DhLoggerBuilder.getLogger(); + private final NetworkClient networkClient; private final NetworkRequestTracker chunkRequestTracker; @@ -28,8 +32,14 @@ public class RemoteFullDataFileHandler extends FullDataFileHandler public CompletableFuture read(DhSectionPos pos) { // TODO: LOD data file updating is probably incomplete return super.read(pos).thenCompose((fullDataSource) -> { + CompletableFuture responseFuture = chunkRequestTracker.sendRequest(networkClient.getChannel(), new ChunkRequestMessage(pos)) + .exceptionally(throwable -> { + LOGGER.error(throwable); + return null; + }); + responseFuture.thenAccept(response -> LOGGER.info("ChunkResponseMessage "+response.dhSectionPos)); + FullDataMetaFile metaFile = this.getLoadOrMakeFile(pos, true); - CompletableFuture responseFuture = chunkRequestTracker.sendRequest(networkClient.getChannel(), new ChunkRequestMessage(pos)); return onDataFileUpdate(fullDataSource, metaFile, iFullDataSource -> {}, iFullDataSource -> true); }); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/future/NetworkRequestTracker.java b/core/src/main/java/com/seibel/distanthorizons/core/network/future/NetworkRequestTracker.java index f04dc5d46..2ca57abb0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/future/NetworkRequestTracker.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/future/NetworkRequestTracker.java @@ -58,9 +58,8 @@ public class NetworkRequestTracker } private void completeAllExceptionally(Channel channel, Throwable cause) { - for (CompletableFuture responseFuture : pendingFutures.row(channel).values()) { + for (CompletableFuture responseFuture : pendingFutures.row(channel).values()) responseFuture.completeExceptionally(cause); - }; pendingFutures.row(channel).clear(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkRequestMessage.java index 72eb85870..8fe531253 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkRequestMessage.java @@ -1,6 +1,7 @@ package com.seibel.distanthorizons.core.network.messages; import com.seibel.distanthorizons.core.network.future.IFutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import com.seibel.distanthorizons.core.pos.DhSectionPos; import io.netty.buffer.ByteBuf; @@ -19,12 +20,12 @@ public class ChunkRequestMessage implements IFutureTrackableNetworkMessage messageClass) { return this.classToId.get(messageClass); } + public int getMessageId(Class messageClass) { + try + { + return this.classToId.get(messageClass); + } + catch (NullPointerException e) + { + throw new IllegalArgumentException("Message does not have ID assigned to it: "+messageClass.getSimpleName()); + } + } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java index b36702b75..db66267ab 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java @@ -66,7 +66,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld networkClient.registerAckHandler(RemotePlayerConfigMessage.class, ctx -> { // TODO Actually request chunks - ctx.writeAndFlush(new ChunkRequestMessage(new DhSectionPos(new DhBlockPos2D(0, 0)))); + // ctx.writeAndFlush(new ChunkRequestMessage(new DhSectionPos(new DhBlockPos2D(0, 0)))); }); } From d29ba9d423493324f8b7099ef37902fc29d9bac2 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 23 Jul 2023 14:39:29 +0500 Subject: [PATCH 007/205] Requests now work properly Merge request tracker into event source --- .../RemoteFullDataFileHandler.java | 8 +-- .../core/network/NetworkClient.java | 16 +++-- .../core/network/NetworkEventSource.java | 58 +++++++++++++-- .../core/network/NetworkServer.java | 10 +++ .../IFutureTrackableNetworkMessage.java | 8 --- .../network/future/NetworkRequestTracker.java | 72 ------------------- .../network/messages/ChunkRequestMessage.java | 10 ++- .../messages/ChunkResponseMessage.java | 18 ++--- .../FutureTrackableNetworkMessage.java | 30 ++++++++ .../core/pos/DhSectionPos.java | 14 +--- .../core/world/DhServerWorld.java | 3 +- 11 files changed, 114 insertions(+), 133 deletions(-) delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/future/IFutureTrackableNetworkMessage.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/future/NetworkRequestTracker.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java index 3526df785..e0d5bf6e8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java @@ -6,7 +6,6 @@ import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.network.ChildNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkClient; -import com.seibel.distanthorizons.core.network.future.NetworkRequestTracker; import com.seibel.distanthorizons.core.network.messages.ChunkRequestMessage; import com.seibel.distanthorizons.core.network.messages.ChunkResponseMessage; import com.seibel.distanthorizons.core.pos.DhSectionPos; @@ -20,24 +19,22 @@ public class RemoteFullDataFileHandler extends FullDataFileHandler protected static final Logger LOGGER = DhLoggerBuilder.getLogger(); private final NetworkClient networkClient; - private final NetworkRequestTracker chunkRequestTracker; public RemoteFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure, NetworkClient networkClient) { super(level, saveStructure); this.networkClient = networkClient; - this.chunkRequestTracker = new NetworkRequestTracker<>(networkClient, ChunkResponseMessage.class); } @Override public CompletableFuture read(DhSectionPos pos) { // TODO: LOD data file updating is probably incomplete return super.read(pos).thenCompose((fullDataSource) -> { - CompletableFuture responseFuture = chunkRequestTracker.sendRequest(networkClient.getChannel(), new ChunkRequestMessage(pos)) + CompletableFuture responseFuture = networkClient.sendRequest(new ChunkRequestMessage(pos)) .exceptionally(throwable -> { LOGGER.error(throwable); return null; }); - responseFuture.thenAccept(response -> LOGGER.info("ChunkResponseMessage "+response.dhSectionPos)); + responseFuture.thenAccept(response -> LOGGER.info("ChunkResponseMessage "+pos)); FullDataMetaFile metaFile = this.getLoadOrMakeFile(pos, true); return onDataFileUpdate(fullDataSource, metaFile, iFullDataSource -> {}, iFullDataSource -> true); @@ -47,6 +44,5 @@ public class RemoteFullDataFileHandler extends FullDataFileHandler @Override public void close() { super.close(); - chunkRequestTracker.close(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index 468da601b..635f2fa42 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -4,18 +4,17 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.network.messages.CloseMessage; import com.seibel.distanthorizons.core.network.messages.CloseReasonMessage; import com.seibel.distanthorizons.core.network.messages.HelloMessage; +import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.MessageHandler; import com.seibel.distanthorizons.core.network.protocol.NetworkChannelInitializer; import io.netty.bootstrap.Bootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelOption; -import io.netty.channel.EventLoopGroup; +import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; import org.apache.logging.log4j.Logger; import java.net.InetSocketAddress; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; public class NetworkClient extends NetworkEventSource implements AutoCloseable @@ -99,8 +98,10 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable }); this.channel = connectFuture.channel(); - this. channel.closeFuture().addListener((ChannelFuture channelFuture) -> + this.channel.closeFuture().addListener((ChannelFuture channelFuture) -> { + this.completeAllFuturesExceptionally(channelFuture.cause()); + switch (this.connectionState) { case OPEN: @@ -134,8 +135,9 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable this.channel.disconnect(); } - public Channel getChannel() { - return channel; + public final CompletableFuture sendRequest(FutureTrackableNetworkMessage msg) + { + return this.sendRequest(this.channel.pipeline().context(MessageHandler.class), msg); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index 1c88922fd..f87adafb1 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -1,12 +1,17 @@ package com.seibel.distanthorizons.core.network; +import com.google.common.collect.HashBasedTable; +import com.google.common.collect.Table; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.messages.AckMessage; import com.seibel.distanthorizons.core.network.protocol.INetworkMessage; +import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import org.apache.logging.log4j.Logger; import java.util.*; +import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -14,7 +19,7 @@ public abstract class NetworkEventSource { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); protected final Map, Set>> handlers = new HashMap<>(); - + private final Table> pendingFutures = HashBasedTable.create(); protected boolean hasHandler(Class handlerClass) { @@ -23,16 +28,32 @@ public abstract class NetworkEventSource protected void handleMessage(INetworkMessage message, ChannelHandlerContext channelContext) { + boolean handled = false; + Set> handlerList = this.handlers.get(message.getClass()); - if (handlerList == null || handlerList.isEmpty()) + if (handlerList != null) { - LOGGER.warn("Unhandled message type: " + message.getClass().getSimpleName()); - return; + for (BiConsumer handler : handlerList) + { + handled = true; + handler.accept(message, channelContext); + } } - for (BiConsumer handler : handlerList) + if (message instanceof FutureTrackableNetworkMessage) { - handler.accept(message, channelContext); + FutureTrackableNetworkMessage trackableMessage = (FutureTrackableNetworkMessage)message; + CompletableFuture future = pendingFutures.remove(channelContext, trackableMessage.futureId); + if (future != null) + { + handled = true; + future.complete(trackableMessage); + } + } + + if (!handled) + { + LOGGER.warn("Unhandled message type: " + message.getClass().getSimpleName()); } } @@ -59,8 +80,33 @@ public abstract class NetworkEventSource .remove(handlerImplementation); } + protected CompletableFuture sendRequest(ChannelHandlerContext ctx, FutureTrackableNetworkMessage msg) + { + CompletableFuture responseFuture = new CompletableFuture<>(); + pendingFutures.put(ctx, msg.futureId, (CompletableFuture) responseFuture); + + ctx.writeAndFlush(msg).addListener(writeFuture -> { + if (writeFuture.cause() != null) { + responseFuture.completeExceptionally(writeFuture.cause()); + } + }); + return responseFuture; + } + + protected void completeAllFuturesExceptionally(ChannelHandlerContext ctx, Throwable cause) { + for (CompletableFuture futureData : pendingFutures.row(ctx).values()) + futureData.completeExceptionally(cause); + pendingFutures.row(ctx).clear(); + } + + protected void completeAllFuturesExceptionally(Throwable cause) { + for (ChannelHandlerContext ctx : pendingFutures.rowKeySet()) + this.completeAllFuturesExceptionally(ctx, cause); + } + public void close() { this.handlers.clear(); + completeAllFuturesExceptionally(new Exception(this.getClass().getSimpleName()+" is closed.")); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java index 6e69459d2..68645ba69 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java @@ -5,6 +5,7 @@ import com.seibel.distanthorizons.core.network.messages.AckMessage; import com.seibel.distanthorizons.core.network.messages.CloseReasonMessage; import com.seibel.distanthorizons.core.network.messages.CloseMessage; import com.seibel.distanthorizons.core.network.messages.HelloMessage; +import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.MessageHandler; import com.seibel.distanthorizons.core.network.protocol.NetworkChannelInitializer; import com.seibel.distanthorizons.coreapi.ModInfo; @@ -16,6 +17,8 @@ import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import org.apache.logging.log4j.Logger; +import java.util.concurrent.CompletableFuture; + public class NetworkServer extends NetworkEventSource implements AutoCloseable { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); @@ -66,6 +69,7 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable this.registerHandler(CloseMessage.class, (closeMessage, channelContext) -> { LOGGER.info("Client disconnected: "+channelContext.channel().remoteAddress()); + this.completeAllFuturesExceptionally(channelContext, channelContext.channel().closeFuture().cause()); }); } @@ -99,6 +103,12 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable .addListener(ChannelFutureListener.CLOSE); } + @Override + public CompletableFuture sendRequest(ChannelHandlerContext ctx, FutureTrackableNetworkMessage msg) + { + return super.sendRequest(ctx, msg); + } + @Override public void close() { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/future/IFutureTrackableNetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/future/IFutureTrackableNetworkMessage.java deleted file mode 100644 index be3514034..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/future/IFutureTrackableNetworkMessage.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.seibel.distanthorizons.core.network.future; - -import com.seibel.distanthorizons.core.network.protocol.INetworkMessage; - -public interface IFutureTrackableNetworkMessage> extends INetworkMessage -{ - TKey getRequestKey(); -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/future/NetworkRequestTracker.java b/core/src/main/java/com/seibel/distanthorizons/core/network/future/NetworkRequestTracker.java deleted file mode 100644 index 2ca57abb0..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/future/NetworkRequestTracker.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.seibel.distanthorizons.core.network.future; - -import com.google.common.collect.HashBasedTable; -import com.google.common.collect.Table; -import com.seibel.distanthorizons.core.network.ChildNetworkEventSource; -import com.seibel.distanthorizons.core.network.NetworkEventSource; -import io.netty.channel.Channel; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOutboundInvoker; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CompletableFuture; - -public class NetworkRequestTracker - , TKey extends Comparable> - implements AutoCloseable -{ - private final ChildNetworkEventSource eventSource; - - private final Set knownChannels = new HashSet<>(); - private final Table> pendingFutures = HashBasedTable.create(); - - public NetworkRequestTracker( - NetworkEventSource eventSource, - Class responseClass) - { - this.eventSource = new ChildNetworkEventSource<>(eventSource); - registerNetworkHandlers(responseClass); - } - - private void registerNetworkHandlers(Class responseClass) - { - this.eventSource.registerHandler(responseClass, (msg, ctx) -> { - CompletableFuture future = pendingFutures.remove(ctx.channel(), msg.getRequestKey()); - if (future != null) { - future.complete(msg); - } - }); - } - - public CompletableFuture sendRequest(Channel channel, IFutureTrackableNetworkMessage msg) - { - if (knownChannels.add(channel)) - channel.closeFuture().addListener(closeFuture -> completeAllExceptionally(channel, closeFuture.cause())); - - CompletableFuture responseFuture = new CompletableFuture<>(); - pendingFutures.put(channel, msg.getRequestKey(), responseFuture); - - channel.writeAndFlush(msg).addListener(writeFuture -> { - if (writeFuture.cause() != null) { - responseFuture.completeExceptionally(writeFuture.cause()); - } - }); - return responseFuture; - } - - private void completeAllExceptionally(Channel channel, Throwable cause) { - for (CompletableFuture responseFuture : pendingFutures.row(channel).values()) - responseFuture.completeExceptionally(cause); - pendingFutures.row(channel).clear(); - } - - @Override public void close() - { - this.eventSource.close(); - for (Channel channel : pendingFutures.rowKeySet()) - this.completeAllExceptionally(channel, new Exception(this.getClass().getSimpleName()+" is closed.")); - } -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkRequestMessage.java index 8fe531253..0d9d96898 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkRequestMessage.java @@ -1,11 +1,11 @@ package com.seibel.distanthorizons.core.network.messages; -import com.seibel.distanthorizons.core.network.future.IFutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import com.seibel.distanthorizons.core.pos.DhSectionPos; import io.netty.buffer.ByteBuf; -public class ChunkRequestMessage implements IFutureTrackableNetworkMessage +public class ChunkRequestMessage extends FutureTrackableNetworkMessage { public DhSectionPos dhSectionPos; @@ -15,16 +15,14 @@ public class ChunkRequestMessage implements IFutureTrackableNetworkMessage +public class ChunkResponseMessage extends FutureTrackableNetworkMessage { - public DhSectionPos dhSectionPos; - public ChunkResponseMessage() {} - - public ChunkResponseMessage(DhSectionPos dhSectionPos) { - this.dhSectionPos = dhSectionPos; - } - - @Override public DhSectionPos getRequestKey() { return dhSectionPos; } @Override - public void encode(ByteBuf out) + public void encode0(ByteBuf out) { - dhSectionPos.encode(out); } @Override - public void decode(ByteBuf in) + public void decode0(ByteBuf in) { - dhSectionPos = INetworkObject.decode(new DhSectionPos((byte)0, 0, 0), in); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java new file mode 100644 index 000000000..96e5e326d --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java @@ -0,0 +1,30 @@ +package com.seibel.distanthorizons.core.network.protocol; + +import io.netty.buffer.ByteBuf; + +public abstract class FutureTrackableNetworkMessage implements INetworkMessage +{ + private static int lastId = 0; + public int futureId = lastId++; + + public static FutureTrackableNetworkMessage makeResponse(FutureTrackableNetworkMessage requestMessage, FutureTrackableNetworkMessage responseMessage) + { + responseMessage.futureId = requestMessage.futureId; + return responseMessage; + } + + @Override public final void encode(ByteBuf out) + { + out.writeInt(futureId); + this.encode0(out); + } + + @Override public final void decode(ByteBuf in) + { + futureId = in.readInt(); + this.decode0(in); + } + + protected abstract void encode0(ByteBuf out); + protected abstract void decode0(ByteBuf in); +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java index 19f86bd20..1a9ce9dcc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java @@ -21,7 +21,7 @@ import java.util.function.Consumer; * @author Leetom * @version 2022-11-6 */ -public class DhSectionPos implements Comparable, INetworkObject +public class DhSectionPos implements INetworkObject { /** * The lowest detail level a Section position can hold. @@ -241,18 +241,6 @@ public class DhSectionPos implements Comparable, INetworkObject this.sectionZ == that.sectionZ; } - @Override public int compareTo(@NotNull DhSectionPos o) - { - if (this.sectionDetailLevel != o.sectionDetailLevel) - return this.sectionDetailLevel - o.sectionDetailLevel; - if (this.sectionX != o.sectionX) - return this.sectionX - o.sectionX; - if (this.sectionZ != o.sectionZ) - return this.sectionZ - o.sectionZ; - - return 0; - } - @Override public int hashCode() { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java index fac91db65..088a814e7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java @@ -11,6 +11,7 @@ import com.seibel.distanthorizons.core.network.NetworkServer; import com.seibel.distanthorizons.core.network.messages.*; import com.seibel.distanthorizons.core.network.messages.ChunkRequestMessage; import com.seibel.distanthorizons.core.network.objects.RemotePlayer; +import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; @@ -108,7 +109,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld level.serverside.worldGenTick(new DhBlockPos2D(msg.dhSectionPos.sectionX, msg.dhSectionPos.sectionZ)); // Send chunk response message back - ctx.writeAndFlush(new ChunkResponseMessage(msg.dhSectionPos)); + ctx.writeAndFlush(FutureTrackableNetworkMessage.makeResponse(msg, new ChunkResponseMessage())); }); } From d7ef6c8a72f92eebd6a74c7b71778ffb26d5c95d Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 23 Jul 2023 20:34:40 +0500 Subject: [PATCH 008/205] Got chunks to generate on server --- .../fullData/FullDataPointIdMap.java | 15 ++++---- .../sources/CompleteFullDataSource.java | 9 ++--- .../HighDetailIncompleteFullDataSource.java | 9 ++--- .../LowDetailIncompleteFullDataSource.java | 9 ++--- .../interfaces/IStreamableFullDataSource.java | 9 ++--- .../transformers/ChunkToLodBuilder.java | 2 +- .../RemoteFullDataFileHandler.java | 8 ++--- .../messages/ChunkResponseMessage.java | 21 ------------ ...java => FullDataSourceRequestMessage.java} | 6 ++-- .../FullDataSourceResponseMessage.java | 34 +++++++++++++++++++ .../FutureTrackableNetworkMessage.java | 26 ++++++++++---- .../network/protocol/MessageRegistry.java | 4 +-- .../core/world/DhServerWorld.java | 16 ++++----- .../wrapperInterfaces/IWrapperFactory.java | 3 +- .../world/IBiomeWrapper.java | 2 +- 15 files changed, 101 insertions(+), 72 deletions(-) delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkResponseMessage.java rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ChunkRequestMessage.java => FullDataSourceRequestMessage.java} (76%) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java index 74d1e1645..589d327f1 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java @@ -7,6 +7,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrappe import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.core.util.objects.dataStreams.*; import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import java.io.*; import java.util.ArrayList; @@ -86,25 +87,25 @@ public class FullDataPointIdMap } /** Serializes all contained entries into the given stream, formatted in UTF */ - public void serialize(DhDataOutputStream outputStream) throws IOException + public void serialize(DhDataOutputStream outputStream, ILevelWrapper levelWrapper) throws IOException { lock.readLock().lock(); outputStream.writeInt(this.entries.size()); for (Entry entry : this.entries) { - outputStream.writeUTF(entry.serialize()); + outputStream.writeUTF(entry.serialize(levelWrapper)); } lock.readLock().unlock(); } /** Creates a new IdBiomeBlockStateMap from the given UTF formatted stream */ - public static FullDataPointIdMap deserialize(DhDataInputStream inputStream) throws IOException, InterruptedException + public static FullDataPointIdMap deserialize(DhDataInputStream inputStream, ILevelWrapper levelWrapper) throws IOException, InterruptedException { int entityCount = inputStream.readInt(); FullDataPointIdMap newMap = new FullDataPointIdMap(); for (int i = 0; i < entityCount; i++) { - newMap.entries.add(Entry.deserialize(inputStream.readUTF())); + newMap.entries.add(Entry.deserialize(inputStream.readUTF(), levelWrapper)); } return newMap; } @@ -160,9 +161,9 @@ public class FullDataPointIdMap } - public String serialize() { return this.biome.serialize() + SEPARATOR_STRING + this.blockState.serialize(); } + public String serialize(ILevelWrapper levelWrapper) { return this.biome.serialize(levelWrapper) + SEPARATOR_STRING + this.blockState.serialize(); } - public static Entry deserialize(String str) throws IOException, InterruptedException + public static Entry deserialize(String str, ILevelWrapper levelWrapper) throws IOException, InterruptedException { String[] stringArray = str.split(SEPARATOR_STRING); if (stringArray.length != 2) @@ -176,7 +177,7 @@ public class FullDataPointIdMap throw new InterruptedException(FullDataPointIdMap.class.getSimpleName()+" task interrupted."); } - IBiomeWrapper biome = WRAPPER_FACTORY.deserializeBiomeWrapper(stringArray[0]); + IBiomeWrapper biome = WRAPPER_FACTORY.deserializeBiomeWrapper(stringArray[0], levelWrapper); IBlockStateWrapper blockState = WRAPPER_FACTORY.deserializeBlockStateWrapper(stringArray[1]); return new Entry(biome, blockState); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java index 98b7bf8bf..4f34884df 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java @@ -17,6 +17,7 @@ import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream; import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import org.apache.logging.log4j.Logger; @@ -226,14 +227,14 @@ public class CompleteFullDataSource extends FullDataArrayAccessor implements IFu @Override - public void writeIdMappings(DhDataOutputStream outputStream) throws IOException + public void writeIdMappings(DhDataOutputStream outputStream, ILevelWrapper levelWrapper) throws IOException { outputStream.writeInt(IFullDataSource.DATA_GUARD_BYTE); - this.mapping.serialize(outputStream); + this.mapping.serialize(outputStream, levelWrapper); } @Override - public FullDataPointIdMap readIdMappings(long[][] dataPoints, DhDataInputStream inputStream) throws IOException, InterruptedException + public FullDataPointIdMap readIdMappings(long[][] dataPoints, DhDataInputStream inputStream, ILevelWrapper levelWrapper) throws IOException, InterruptedException { int guardByte = inputStream.readInt(); if (guardByte != IFullDataSource.DATA_GUARD_BYTE) @@ -241,7 +242,7 @@ public class CompleteFullDataSource extends FullDataArrayAccessor implements IFu throw new IOException("Invalid data content end guard for ID mapping"); } - return FullDataPointIdMap.deserialize(inputStream); + return FullDataPointIdMap.deserialize(inputStream, levelWrapper); } @Override public void setIdMapping(FullDataPointIdMap mappings) { this.mapping.mergeAndReturnRemappedEntityIds(mappings); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java index 6b67c65ad..a6fbe00f2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java @@ -17,6 +17,7 @@ import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream; import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import org.apache.logging.log4j.Logger; @@ -350,14 +351,14 @@ public class HighDetailIncompleteFullDataSource implements IIncompleteFullDataSo @Override - public void writeIdMappings(DhDataOutputStream dataOutputStream) throws IOException + public void writeIdMappings(DhDataOutputStream dataOutputStream, ILevelWrapper levelWrapper) throws IOException { dataOutputStream.writeInt(IFullDataSource.DATA_GUARD_BYTE); - this.mapping.serialize(dataOutputStream); + this.mapping.serialize(dataOutputStream, levelWrapper); } @Override - public FullDataPointIdMap readIdMappings(long[][][] dataPoints, DhDataInputStream inputStream) throws IOException, InterruptedException + public FullDataPointIdMap readIdMappings(long[][][] dataPoints, DhDataInputStream inputStream, ILevelWrapper levelWrapper) throws IOException, InterruptedException { // mark the start of the ID data int idMappingStartByte = inputStream.readInt(); @@ -368,7 +369,7 @@ public class HighDetailIncompleteFullDataSource implements IIncompleteFullDataSo } // deserialize the ID data - return FullDataPointIdMap.deserialize(inputStream); + return FullDataPointIdMap.deserialize(inputStream, levelWrapper); } @Override public void setIdMapping(FullDataPointIdMap mappings) { this.mapping.mergeAndReturnRemappedEntityIds(mappings); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java index 25de2c369..74ea29198 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java @@ -17,6 +17,7 @@ import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream; import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import org.apache.logging.log4j.Logger; @@ -237,14 +238,14 @@ public class LowDetailIncompleteFullDataSource extends FullDataArrayAccessor imp @Override - public void writeIdMappings(DhDataOutputStream outputStream) throws IOException + public void writeIdMappings(DhDataOutputStream outputStream, ILevelWrapper levelWrapper) throws IOException { outputStream.writeInt(IFullDataSource.DATA_GUARD_BYTE); - this.mapping.serialize(outputStream); + this.mapping.serialize(outputStream, levelWrapper); } @Override - public FullDataPointIdMap readIdMappings(StreamDataPointContainer streamDataPointContainer, DhDataInputStream inputStream) throws IOException, InterruptedException + public FullDataPointIdMap readIdMappings(StreamDataPointContainer streamDataPointContainer, DhDataInputStream inputStream, ILevelWrapper levelWrapper) throws IOException, InterruptedException { // Id mapping int dataPresentFlag = inputStream.readInt(); @@ -252,7 +253,7 @@ public class LowDetailIncompleteFullDataSource extends FullDataArrayAccessor imp { throw new IOException("invalid ID mapping end guard"); } - return FullDataPointIdMap.deserialize(inputStream); + return FullDataPointIdMap.deserialize(inputStream, levelWrapper); } @Override public void setIdMapping(FullDataPointIdMap mappings) { this.mapping.mergeAndReturnRemappedEntityIds(mappings); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IStreamableFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IStreamableFullDataSource.java index 92d9fc146..11c37fae8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IStreamableFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IStreamableFullDataSource.java @@ -8,6 +8,7 @@ import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream; import com.seibel.distanthorizons.core.util.objects.dataStreams.*; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import java.io.IOException; @@ -55,7 +56,7 @@ public interface IStreamableFullDataSource read(DhSectionPos pos) { // TODO: LOD data file updating is probably incomplete return super.read(pos).thenCompose((fullDataSource) -> { - CompletableFuture responseFuture = networkClient.sendRequest(new ChunkRequestMessage(pos)) + CompletableFuture responseFuture = networkClient.sendRequest(new FullDataSourceRequestMessage(pos)) .exceptionally(throwable -> { LOGGER.error(throwable); return null; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkResponseMessage.java deleted file mode 100644 index 14119a582..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkResponseMessage.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.seibel.distanthorizons.core.network.messages; - -import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; -import com.seibel.distanthorizons.core.network.protocol.INetworkObject; -import com.seibel.distanthorizons.core.pos.DhSectionPos; -import io.netty.buffer.ByteBuf; - -public class ChunkResponseMessage extends FutureTrackableNetworkMessage -{ - public ChunkResponseMessage() {} - - @Override - public void encode0(ByteBuf out) - { - } - - @Override - public void decode0(ByteBuf in) - { - } -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceRequestMessage.java similarity index 76% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkRequestMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceRequestMessage.java index 0d9d96898..3bef7e812 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ChunkRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceRequestMessage.java @@ -5,13 +5,13 @@ import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import com.seibel.distanthorizons.core.pos.DhSectionPos; import io.netty.buffer.ByteBuf; -public class ChunkRequestMessage extends FutureTrackableNetworkMessage +public class FullDataSourceRequestMessage extends FutureTrackableNetworkMessage { public DhSectionPos dhSectionPos; - public ChunkRequestMessage() {} + public FullDataSourceRequestMessage() {} - public ChunkRequestMessage(DhSectionPos dhSectionPos) { + public FullDataSourceRequestMessage(DhSectionPos dhSectionPos) { this.dhSectionPos = dhSectionPos; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java new file mode 100644 index 000000000..a35c09f5d --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java @@ -0,0 +1,34 @@ +package com.seibel.distanthorizons.core.network.messages; + +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; +import com.seibel.distanthorizons.core.level.DhServerLevel; +import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import io.netty.buffer.ByteBuf; + +import java.io.IOException; + +public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage +{ + public IFullDataSource fullDataSource; + public DhServerLevel level; + + public FullDataSourceResponseMessage() {} + public FullDataSourceResponseMessage(IFullDataSource fullDataSource, DhServerLevel level) + { + this.fullDataSource = fullDataSource; + this.level = level; + } + + @Override + public void encode0(ByteBuf out) throws IOException + { + //fullDataSource.writeToStream(new DhDataOutputStream(new ByteBufOutputStream(out)), level); + } + + @Override + public void decode0(ByteBuf in) + { + //DhSectionPos sectionPos = INetworkObject.decode(new DhSectionPos((byte) 0, (byte) 0, (byte) 0), in); + //fullDataSource = HighDetailIncompleteFullDataSource.createEmpty(sectionPos); + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java index 96e5e326d..dc6596950 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java @@ -15,16 +15,30 @@ public abstract class FutureTrackableNetworkMessage implements INetworkMessage @Override public final void encode(ByteBuf out) { - out.writeInt(futureId); - this.encode0(out); + try + { + out.writeInt(futureId); + this.encode0(out); + } + catch (Exception e) + { + throw new RuntimeException(e); + } } @Override public final void decode(ByteBuf in) { - futureId = in.readInt(); - this.decode0(in); + try + { + futureId = in.readInt(); + this.decode0(in); + } + catch (Exception e) + { + throw new RuntimeException(e); + } } - protected abstract void encode0(ByteBuf out); - protected abstract void decode0(ByteBuf in); + protected abstract void encode0(ByteBuf out) throws Exception; + protected abstract void decode0(ByteBuf in) throws Exception; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java index 63224344b..0d7d1d2a7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java @@ -29,8 +29,8 @@ public class MessageRegistry this.registerMessage(AckMessage.class, AckMessage::new); this.registerMessage(PlayerUUIDMessage.class, PlayerUUIDMessage::new); this.registerMessage(RemotePlayerConfigMessage.class, RemotePlayerConfigMessage::new); - this.registerMessage(ChunkRequestMessage.class, ChunkRequestMessage::new); - this.registerMessage(ChunkResponseMessage.class, ChunkResponseMessage::new); + this.registerMessage(FullDataSourceRequestMessage.class, FullDataSourceRequestMessage::new); + this.registerMessage(FullDataSourceResponseMessage.class, FullDataSourceResponseMessage::new); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java index 088a814e7..f8e1407ad 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java @@ -2,25 +2,21 @@ package com.seibel.distanthorizons.core.world; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; -import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile; -import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; import com.seibel.distanthorizons.core.file.structure.LocalSaveStructure; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.network.NetworkServer; import com.seibel.distanthorizons.core.network.messages.*; -import com.seibel.distanthorizons.core.network.messages.ChunkRequestMessage; import com.seibel.distanthorizons.core.network.objects.RemotePlayer; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import io.netty.channel.ChannelHandlerContext; import java.io.File; -import java.nio.file.FileAlreadyExistsException; import java.util.HashMap; import java.util.UUID; import java.util.concurrent.CompletableFuture; @@ -91,7 +87,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld channelContext.writeAndFlush(new AckMessage(RemotePlayerConfigMessage.class)); }); - this.networkServer.registerHandler(ChunkRequestMessage.class, (msg, ctx) -> + this.networkServer.registerHandler(FullDataSourceRequestMessage.class, (msg, ctx) -> { if (msg.dhSectionPos == null) { LOGGER.warn("RequestChunksMessage received with null msg.dhSectionPos"); @@ -107,9 +103,11 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld // TODO: Add level to packet level.serverside.worldGenTick(new DhBlockPos2D(msg.dhSectionPos.sectionX, msg.dhSectionPos.sectionZ)); - - // Send chunk response message back - ctx.writeAndFlush(FutureTrackableNetworkMessage.makeResponse(msg, new ChunkResponseMessage())); + + level.serverside.dataFileHandler.read(msg.dhSectionPos).thenAccept(fullDataSource -> { + // Send chunk response message back + ctx.writeAndFlush(FutureTrackableNetworkMessage.makeResponse(msg, new FullDataSourceResponseMessage(fullDataSource, level))); + }); }); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java index ef271da61..0eeaf4efd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java @@ -23,6 +23,7 @@ import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvironmentWrapper; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable; @@ -37,7 +38,7 @@ import java.io.IOException; public interface IWrapperFactory extends IBindable { AbstractBatchGenerationEnvironmentWrapper createBatchGenerator(IDhLevel targetLevel); - IBiomeWrapper deserializeBiomeWrapper(String str) throws IOException; + IBiomeWrapper deserializeBiomeWrapper(String str, ILevelWrapper levelWrapper) throws IOException; IBlockStateWrapper deserializeBlockStateWrapper(String str) throws IOException; IBlockStateWrapper getAirBlockStateWrapper(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IBiomeWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IBiomeWrapper.java index 69fabc533..e959de2e5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IBiomeWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IBiomeWrapper.java @@ -29,5 +29,5 @@ import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindab public interface IBiomeWrapper extends IDhApiBiomeWrapper, IBindable { String getName(); - String serialize(); + String serialize(ILevelWrapper levelWrapper); } From 10e28734972816f3d654b2197d58c1946a685199 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 23 Jul 2023 23:48:42 +0500 Subject: [PATCH 009/205] Downloads already visited chunks --- .../RemoteFullDataFileHandler.java | 37 ++++++++++++++----- .../FullDataSourceRequestMessage.java | 4 ++ .../FullDataSourceResponseMessage.java | 35 +++++++++++++++--- .../FutureTrackableNetworkMessage.java | 5 ++- .../protocol/NetworkChannelInitializer.java | 4 +- .../core/world/DhServerWorld.java | 8 ++-- 6 files changed, 71 insertions(+), 22 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java index 55580d415..22058c878 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java @@ -2,6 +2,7 @@ package com.seibel.distanthorizons.core.file.fullDatafile; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; +import com.seibel.distanthorizons.core.level.DhClientLevel; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.network.NetworkClient; @@ -25,17 +26,35 @@ public class RemoteFullDataFileHandler extends FullDataFileHandler @Override public CompletableFuture read(DhSectionPos pos) { - // TODO: LOD data file updating is probably incomplete - return super.read(pos).thenCompose((fullDataSource) -> { - CompletableFuture responseFuture = networkClient.sendRequest(new FullDataSourceRequestMessage(pos)) - .exceptionally(throwable -> { - LOGGER.error(throwable); - return null; - }); - responseFuture.thenAccept(response -> LOGGER.info("ChunkResponseMessage "+pos)); + return super.read(pos).thenCompose(fullDataSource -> { + if (fullDataSource == null) + return null; + + if (!fullDataSource.isEmpty()) + return CompletableFuture.completedFuture(fullDataSource); FullDataMetaFile metaFile = this.getLoadOrMakeFile(pos, true); - return onDataFileUpdate(fullDataSource, metaFile, iFullDataSource -> {}, iFullDataSource -> true); + return networkClient.sendRequest(new FullDataSourceRequestMessage(pos)) + .handle((response, throwable) -> { + try + { + if (throwable != null) + throw throwable; + + LOGGER.info("FullDataSourceResponseMessage " + pos); + return response.getFullDataSource(metaFile, pos, level); + } + catch (Exception e) + { + LOGGER.error(e); + return null; + } + catch (Throwable e) + { + LOGGER.error(e.toString()); + return null; + } + }); }); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceRequestMessage.java index 3bef7e812..1185f265e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceRequestMessage.java @@ -1,10 +1,14 @@ package com.seibel.distanthorizons.core.network.messages; +import com.google.common.collect.MapMaker; +import com.seibel.distanthorizons.core.level.DhClientLevel; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import com.seibel.distanthorizons.core.pos.DhSectionPos; import io.netty.buffer.ByteBuf; +import java.util.concurrent.ConcurrentMap; + public class FullDataSourceRequestMessage extends FutureTrackableNetworkMessage { public DhSectionPos dhSectionPos; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java index a35c09f5d..fb608fa7d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java @@ -1,16 +1,26 @@ package com.seibel.distanthorizons.core.network.messages; +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.HighDetailIncompleteFullDataSource; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; +import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile; import com.seibel.distanthorizons.core.level.DhServerLevel; +import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; +import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufInputStream; +import java.io.ByteArrayOutputStream; import java.io.IOException; public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage { - public IFullDataSource fullDataSource; - public DhServerLevel level; + private IFullDataSource fullDataSource; + private DhServerLevel level; + + private ByteBufInputStream inputStream; public FullDataSourceResponseMessage() {} public FullDataSourceResponseMessage(IFullDataSource fullDataSource, DhServerLevel level) @@ -22,13 +32,28 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage @Override public void encode0(ByteBuf out) throws IOException { - //fullDataSource.writeToStream(new DhDataOutputStream(new ByteBufOutputStream(out)), level); + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) + { + DhDataOutputStream dhOutputStream = new DhDataOutputStream(outputStream); + fullDataSource.writeToStream(dhOutputStream, level); + dhOutputStream.flush(); + + out.writeInt(outputStream.size()); + out.writeBytes(outputStream.toByteArray()); + } } @Override public void decode0(ByteBuf in) { - //DhSectionPos sectionPos = INetworkObject.decode(new DhSectionPos((byte) 0, (byte) 0, (byte) 0), in); - //fullDataSource = HighDetailIncompleteFullDataSource.createEmpty(sectionPos); + inputStream = new ByteBufInputStream(in.readBytes(in.readInt())); + } + + public IFullDataSource getFullDataSource(FullDataMetaFile metaFile, DhSectionPos pos, IDhLevel level) throws IOException, InterruptedException + { + IFullDataSource fullDataSource = HighDetailIncompleteFullDataSource.createEmpty(pos); + fullDataSource.populateFromStream(metaFile, new DhDataInputStream(inputStream), level); + inputStream.close(); + return fullDataSource; } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java index dc6596950..362232c59 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java @@ -1,16 +1,17 @@ package com.seibel.distanthorizons.core.network.protocol; import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; public abstract class FutureTrackableNetworkMessage implements INetworkMessage { private static int lastId = 0; public int futureId = lastId++; - public static FutureTrackableNetworkMessage makeResponse(FutureTrackableNetworkMessage requestMessage, FutureTrackableNetworkMessage responseMessage) + public static void sendResponse(ChannelHandlerContext ctx, FutureTrackableNetworkMessage requestMessage, FutureTrackableNetworkMessage responseMessage) { responseMessage.futureId = requestMessage.futureId; - return responseMessage; + ctx.writeAndFlush(responseMessage); } @Override public final void encode(ByteBuf out) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkChannelInitializer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkChannelInitializer.java index f021396b6..856f39d0b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkChannelInitializer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkChannelInitializer.java @@ -21,12 +21,12 @@ public class NetworkChannelInitializer extends ChannelInitializer ChannelPipeline pipeline = socketChannel.pipeline(); // Encoder - pipeline.addLast(new LengthFieldPrepender(Short.BYTES)); + pipeline.addLast(new LengthFieldPrepender(Integer.BYTES)); pipeline.addLast(new MessageEncoder()); pipeline.addLast(new NetworkOutboundExceptionRouter()); // Decoder - pipeline.addLast(new LengthFieldBasedFrameDecoder(Short.MAX_VALUE, 0, Short.BYTES, 0, Short.BYTES)); + pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, Integer.BYTES, 0, Integer.BYTES)); pipeline.addLast(new MessageDecoder()); // Handler diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java index f8e1407ad..71a49e53d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java @@ -90,11 +90,11 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld this.networkServer.registerHandler(FullDataSourceRequestMessage.class, (msg, ctx) -> { if (msg.dhSectionPos == null) { - LOGGER.warn("RequestChunksMessage received with null msg.dhSectionPos"); + LOGGER.warn("FullDataSourceRequestMessage received with null msg.dhSectionPos"); return; } - LOGGER.info("RequestChunksMessage received at pos ({}, {}) with detail level {}", msg.dhSectionPos.sectionX, msg.dhSectionPos.sectionZ, msg.dhSectionPos.sectionDetailLevel); + LOGGER.info("FullDataSourceRequestMessage received at pos ({}, {}) with detail level {}", msg.dhSectionPos.sectionX, msg.dhSectionPos.sectionZ, msg.dhSectionPos.sectionDetailLevel); // hasReceivedChunkRequest should be false somewhere ??? // to avoid sending updates until client says at least something about its state @@ -102,11 +102,11 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld DhServerLevel level = this.getLevel(playersByConnection.get(ctx).serverPlayer.getLevel()); // TODO: Add level to packet - level.serverside.worldGenTick(new DhBlockPos2D(msg.dhSectionPos.sectionX, msg.dhSectionPos.sectionZ)); + //level.serverside.worldGenTick(new DhBlockPos2D(msg.dhSectionPos.sectionX, msg.dhSectionPos.sectionZ)); level.serverside.dataFileHandler.read(msg.dhSectionPos).thenAccept(fullDataSource -> { // Send chunk response message back - ctx.writeAndFlush(FutureTrackableNetworkMessage.makeResponse(msg, new FullDataSourceResponseMessage(fullDataSource, level))); + FutureTrackableNetworkMessage.sendResponse(ctx, msg, new FullDataSourceResponseMessage(fullDataSource, level)); }); }); } From 4157a3958911750626524940e1993076d990bab4 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 24 Jul 2023 01:46:09 +0500 Subject: [PATCH 010/205] Somewhat working generation --- .../RemoteFullDataFileHandler.java | 8 +--- .../core/level/DhServerLevel.java | 3 ++ .../FullDataSourceResponseMessage.java | 3 +- .../core/world/DhServerWorld.java | 48 ++++++++++++------- 4 files changed, 37 insertions(+), 25 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java index 22058c878..c3cfcad43 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java @@ -2,7 +2,6 @@ package com.seibel.distanthorizons.core.file.fullDatafile; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; -import com.seibel.distanthorizons.core.level.DhClientLevel; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.network.NetworkClient; @@ -44,14 +43,9 @@ public class RemoteFullDataFileHandler extends FullDataFileHandler LOGGER.info("FullDataSourceResponseMessage " + pos); return response.getFullDataSource(metaFile, pos, level); } - catch (Exception e) - { - LOGGER.error(e); - return null; - } catch (Throwable e) { - LOGGER.error(e.toString()); + LOGGER.error(e); return null; } }); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 84826eb25..ce497272f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -4,6 +4,9 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedF import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; +import com.seibel.distanthorizons.core.network.messages.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.FullDataSourceResponseMessage; +import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhLodPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java index fb608fa7d..c021e92ba 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java @@ -1,5 +1,6 @@ package com.seibel.distanthorizons.core.network.messages; +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.HighDetailIncompleteFullDataSource; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile; @@ -51,7 +52,7 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage public IFullDataSource getFullDataSource(FullDataMetaFile metaFile, DhSectionPos pos, IDhLevel level) throws IOException, InterruptedException { - IFullDataSource fullDataSource = HighDetailIncompleteFullDataSource.createEmpty(pos); + IFullDataSource fullDataSource = CompleteFullDataSource.createEmpty(pos); fullDataSource.populateFromStream(metaFile, new DhDataInputStream(inputStream), level); inputStream.close(); return fullDataSource; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java index 71a49e53d..58245a679 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java @@ -2,6 +2,9 @@ package com.seibel.distanthorizons.core.world; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IIncompleteFullDataSource; +import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataFileHandler; import com.seibel.distanthorizons.core.file.structure.LocalSaveStructure; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.level.IDhLevel; @@ -10,6 +13,7 @@ import com.seibel.distanthorizons.core.network.messages.*; import com.seibel.distanthorizons.core.network.objects.RemotePlayer; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; +import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; @@ -86,27 +90,37 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld // TODO Take notice of received payload and possibly echo back a constrained version channelContext.writeAndFlush(new AckMessage(RemotePlayerConfigMessage.class)); }); - + + // This should be at DhServerLevel I guess this.networkServer.registerHandler(FullDataSourceRequestMessage.class, (msg, ctx) -> { - if (msg.dhSectionPos == null) { - LOGGER.warn("FullDataSourceRequestMessage received with null msg.dhSectionPos"); - return; - } - LOGGER.info("FullDataSourceRequestMessage received at pos ({}, {}) with detail level {}", msg.dhSectionPos.sectionX, msg.dhSectionPos.sectionZ, msg.dhSectionPos.sectionDetailLevel); - // hasReceivedChunkRequest should be false somewhere ??? - // to avoid sending updates until client says at least something about its state - - // TODO: Get level from the player that sent the packet - DhServerLevel level = this.getLevel(playersByConnection.get(ctx).serverPlayer.getLevel()); - - // TODO: Add level to packet - //level.serverside.worldGenTick(new DhBlockPos2D(msg.dhSectionPos.sectionX, msg.dhSectionPos.sectionZ)); - level.serverside.dataFileHandler.read(msg.dhSectionPos).thenAccept(fullDataSource -> { - // Send chunk response message back - FutureTrackableNetworkMessage.sendResponse(ctx, msg, new FullDataSourceResponseMessage(fullDataSource, level)); + DhServerLevel level = this.getLevel(playersByConnection.get(ctx).serverPlayer.getLevel()); + GeneratedFullDataFileHandler handler = level.serverside.dataFileHandler; + + handler.read(msg.dhSectionPos).thenAccept(fullDataSource -> { + if (fullDataSource instanceof CompleteFullDataSource) + { + // Send chunk response message back + FutureTrackableNetworkMessage.sendResponse(ctx, msg, new FullDataSourceResponseMessage(fullDataSource, level)); + return; + } + + // A pretty terrible trick but does the job at least somewhat + // (and seems to only reply with regions) + handler.addWorldGenCompleteListener(new GeneratedFullDataFileHandler.IOnWorldGenCompleteListener() + { + @Override public void onWorldGenTaskComplete(DhSectionPos pos) + { + if (pos != msg.dhSectionPos) return; + + handler.removeWorldGenCompleteListener(this); + + // Send chunk response message back + FutureTrackableNetworkMessage.sendResponse(ctx, msg, new FullDataSourceResponseMessage(fullDataSource, level)); + } + }); }); }); } From 133f007bde009a7dbc81342c30a0ee8f1f4d6bfe Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 24 Jul 2023 11:01:10 +0500 Subject: [PATCH 011/205] Fix mistake --- .../com/seibel/distanthorizons/core/world/DhClientWorld.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java index 6a6a6cc1f..3cd4ad498 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java @@ -62,7 +62,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld } private void registerNetworkHandlers() { - this.networkClient.registerAckHandler(HelloMessage.class, (msg, ctx) -> + this.networkClient.registerAckHandler(HelloMessage.class, ctx -> { ctx.writeAndFlush(new PlayerUUIDMessage(MC_CLIENT.getPlayerUUID())); }); From 3e9f741d62ff04ba2769283b833017d5e613764f Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 24 Jul 2023 14:15:03 +0500 Subject: [PATCH 012/205] Implement transferring of missing full data source types --- .../sources/CompleteFullDataSource.java | 3 ++ .../HighDetailIncompleteFullDataSource.java | 7 ++-- .../LowDetailIncompleteFullDataSource.java | 2 ++ .../sources/interfaces/IFullDataSource.java | 1 + .../RemoteFullDataFileHandler.java | 23 ++++++++++++- .../FullDataSourceResponseMessage.java | 32 ++++++++++++------- .../core/world/DhServerWorld.java | 23 ++----------- 7 files changed, 56 insertions(+), 35 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java index 4f34884df..d668c4fa5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java @@ -379,6 +379,9 @@ public class CompleteFullDataSource extends FullDataArrayAccessor implements IFu @Override public byte getDataDetailLevel() { return (byte) (this.sectionPos.sectionDetailLevel -SECTION_SIZE_OFFSET); } + @Override + public long getTypeId() { return TYPE_ID; } + @Override public byte getBinaryDataFormatVersion() { return DATA_FORMAT_VERSION; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java index a6fbe00f2..9af991742 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java @@ -404,8 +404,11 @@ public class HighDetailIncompleteFullDataSource implements IIncompleteFullDataSo public DhSectionPos getSectionPos() { return this.sectionPos; } @Override public byte getDataDetailLevel() { return (byte) (this.sectionPos.sectionDetailLevel - SECTION_SIZE_OFFSET); } - - @Override + + @Override + public long getTypeId() { return TYPE_ID; } + + @Override public byte getBinaryDataFormatVersion() { return DATA_FORMAT_VERSION; } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java index 74ea29198..b83bda615 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java @@ -278,6 +278,8 @@ public class LowDetailIncompleteFullDataSource extends FullDataArrayAccessor imp @Override public byte getDataDetailLevel() { return (byte) (this.sectionPos.sectionDetailLevel - SECTION_SIZE_OFFSET); } @Override + public long getTypeId() { return TYPE_ID; } + @Override public byte getBinaryDataFormatVersion() { return DATA_FORMAT_VERSION; } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IFullDataSource.java index 889b30726..39be296a1 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IFullDataSource.java @@ -43,6 +43,7 @@ public interface IFullDataSource /** Returns the detail level of the data contained by this {@link IFullDataSource}. */ byte getDataDetailLevel(); + long getTypeId(); byte getBinaryDataFormatVersion(); EDhApiWorldGenerationStep getWorldGenStep(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java index c3cfcad43..76b80dfa4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java @@ -4,12 +4,14 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.I import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.network.NetworkClient; import com.seibel.distanthorizons.core.network.messages.FullDataSourceRequestMessage; import com.seibel.distanthorizons.core.network.messages.FullDataSourceResponseMessage; import com.seibel.distanthorizons.core.pos.DhSectionPos; import org.apache.logging.log4j.Logger; +import java.util.ArrayList; import java.util.concurrent.CompletableFuture; public class RemoteFullDataFileHandler extends FullDataFileHandler @@ -18,9 +20,16 @@ public class RemoteFullDataFileHandler extends FullDataFileHandler private final NetworkClient networkClient; + private final F3Screen.NestedMessage f3Message; + private int finishedRequests = 0; + private int totalRequests = 0; + private int failedRequests = 0; + + public RemoteFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure, NetworkClient networkClient) { super(level, saveStructure); this.networkClient = networkClient; + this.f3Message = new F3Screen.NestedMessage(this::f3Log); } @Override @@ -33,27 +42,39 @@ public class RemoteFullDataFileHandler extends FullDataFileHandler return CompletableFuture.completedFuture(fullDataSource); FullDataMetaFile metaFile = this.getLoadOrMakeFile(pos, true); + totalRequests++; return networkClient.sendRequest(new FullDataSourceRequestMessage(pos)) .handle((response, throwable) -> { try { + finishedRequests++; if (throwable != null) throw throwable; LOGGER.info("FullDataSourceResponseMessage " + pos); - return response.getFullDataSource(metaFile, pos, level); + return response.getFullDataSource(metaFile, level); } catch (Throwable e) { + failedRequests++; LOGGER.error(e); return null; } }); }); } + + private String[] f3Log() + { + ArrayList lines = new ArrayList<>(); + lines.add("Remote Full Data File Handler ["+this.level.getLevelWrapper().getDimensionType().getDimensionName()+"]"); + lines.add(" Requests: "+this.finishedRequests +" / "+this.totalRequests +" (failed: "+ this.failedRequests+")"); + return lines.toArray(new String[0]); + } @Override public void close() { + f3Message.close(); super.close(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java index c021e92ba..5bea6199b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java @@ -1,13 +1,11 @@ package com.seibel.distanthorizons.core.network.messages; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.HighDetailIncompleteFullDataSource; +import com.seibel.distanthorizons.core.dataObjects.fullData.loader.AbstractFullDataSourceLoader; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; -import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream; import io.netty.buffer.ByteBuf; @@ -21,7 +19,8 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage private IFullDataSource fullDataSource; private DhServerLevel level; - private ByteBufInputStream inputStream; + private AbstractFullDataSourceLoader fullDataSourceLoader; + private ByteBuf dataBuffer; public FullDataSourceResponseMessage() {} public FullDataSourceResponseMessage(IFullDataSource fullDataSource, DhServerLevel level) @@ -39,22 +38,33 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage fullDataSource.writeToStream(dhOutputStream, level); dhOutputStream.flush(); + out.writeLong(fullDataSource.getTypeId()); + out.writeByte(fullDataSource.getBinaryDataFormatVersion()); out.writeInt(outputStream.size()); out.writeBytes(outputStream.toByteArray()); } } @Override - public void decode0(ByteBuf in) + public void decode0(ByteBuf in) throws IOException { - inputStream = new ByteBufInputStream(in.readBytes(in.readInt())); + long typeId = in.readLong(); + byte dataVersion = in.readByte(); + + this.fullDataSourceLoader = AbstractFullDataSourceLoader.getLoader(typeId, dataVersion); + if (this.fullDataSourceLoader == null) + { + throw new IOException("Invalid file: Data type loader not found: "+typeId+"(v"+dataVersion +")"); + } + + this.dataBuffer = in.readBytes(in.readInt()); } - public IFullDataSource getFullDataSource(FullDataMetaFile metaFile, DhSectionPos pos, IDhLevel level) throws IOException, InterruptedException + public IFullDataSource getFullDataSource(FullDataMetaFile metaFile, IDhLevel level) throws IOException, InterruptedException { - IFullDataSource fullDataSource = CompleteFullDataSource.createEmpty(pos); - fullDataSource.populateFromStream(metaFile, new DhDataInputStream(inputStream), level); - inputStream.close(); - return fullDataSource; + try (ByteBufInputStream inputStream = new ByteBufInputStream(dataBuffer)) + { + return fullDataSourceLoader.loadData(metaFile, new DhDataInputStream(inputStream), level); + } } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java index 58245a679..9ac154682 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java @@ -100,27 +100,8 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld GeneratedFullDataFileHandler handler = level.serverside.dataFileHandler; handler.read(msg.dhSectionPos).thenAccept(fullDataSource -> { - if (fullDataSource instanceof CompleteFullDataSource) - { - // Send chunk response message back - FutureTrackableNetworkMessage.sendResponse(ctx, msg, new FullDataSourceResponseMessage(fullDataSource, level)); - return; - } - - // A pretty terrible trick but does the job at least somewhat - // (and seems to only reply with regions) - handler.addWorldGenCompleteListener(new GeneratedFullDataFileHandler.IOnWorldGenCompleteListener() - { - @Override public void onWorldGenTaskComplete(DhSectionPos pos) - { - if (pos != msg.dhSectionPos) return; - - handler.removeWorldGenCompleteListener(this); - - // Send chunk response message back - FutureTrackableNetworkMessage.sendResponse(ctx, msg, new FullDataSourceResponseMessage(fullDataSource, level)); - } - }); + // Send chunk response message back + FutureTrackableNetworkMessage.sendResponse(ctx, msg, new FullDataSourceResponseMessage(fullDataSource, level)); }); }); } From 768ce5b8fd1da56ee96e94c43bce6215f663fd9f Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 24 Jul 2023 19:16:05 +0500 Subject: [PATCH 013/205] something --- .../sources/CompleteFullDataSource.java | 4 +- .../HighDetailIncompleteFullDataSource.java | 2 +- .../LowDetailIncompleteFullDataSource.java | 4 +- .../RemoteFullDataFileHandler.java | 65 +++++++++++-------- .../core/level/DhServerLevel.java | 8 ++- .../core/logging/ConfigBasedLogger.java | 2 +- .../core/world/DhServerWorld.java | 20 +++++- .../misc/IServerPlayerWrapper.java | 3 + 8 files changed, 70 insertions(+), 38 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java index d668c4fa5..8ac18dbcd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java @@ -88,9 +88,9 @@ public class CompleteFullDataSource extends FullDataArrayAccessor implements IFu public FullDataSourceSummaryData readSourceSummaryInfo(FullDataMetaFile dataFile, DhDataInputStream inputStream, IDhLevel level) throws IOException { int dataDetail = inputStream.readInt(); - if (dataDetail != dataFile.baseMetaData.dataLevel) + if (dataFile.baseMetaData != null && dataDetail != dataFile.baseMetaData.dataLevel) { - throw new IOException(LodUtil.formatLog("Data level mismatch: "+dataDetail+" != "+dataFile.baseMetaData.dataLevel)); + throw new IOException(LodUtil.formatLog("Data level mismatch: " + dataDetail + " != " + dataFile.baseMetaData.dataLevel)); } int width = inputStream.readInt(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java index 9af991742..535b10940 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java @@ -129,7 +129,7 @@ public class HighDetailIncompleteFullDataSource implements IIncompleteFullDataSo LodUtil.assertTrue(dataFile.pos.sectionDetailLevel <= MAX_SECTION_DETAIL); int dataDetail = inputStream.readShort(); - if(dataDetail != dataFile.baseMetaData.dataLevel) + if (dataFile.baseMetaData != null && dataDetail != dataFile.baseMetaData.dataLevel) { throw new IOException(LodUtil.formatLog("Data level mismatch: {} != {}", dataDetail, dataFile.baseMetaData.dataLevel)); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java index b83bda615..e6c0c7233 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java @@ -103,9 +103,9 @@ public class LowDetailIncompleteFullDataSource extends FullDataArrayAccessor imp public FullDataSourceSummaryData readSourceSummaryInfo(FullDataMetaFile dataFile, DhDataInputStream inputStream, IDhLevel level) throws IOException { int dataDetail = inputStream.readInt(); - if(dataDetail != dataFile.baseMetaData.dataLevel) + if (dataFile.baseMetaData != null && dataDetail != dataFile.baseMetaData.dataLevel) { - throw new IOException(LodUtil.formatLog("Data level mismatch: "+dataDetail+" != "+dataFile.baseMetaData.dataLevel)); + throw new IOException(LodUtil.formatLog("Data level mismatch: " + dataDetail + " != " + dataFile.baseMetaData.dataLevel)); } int width = inputStream.readInt(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java index 76b80dfa4..1bdbd7dae 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java @@ -10,6 +10,7 @@ import com.seibel.distanthorizons.core.network.messages.FullDataSourceRequestMes import com.seibel.distanthorizons.core.network.messages.FullDataSourceResponseMessage; import com.seibel.distanthorizons.core.pos.DhSectionPos; import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.concurrent.CompletableFuture; @@ -34,34 +35,42 @@ public class RemoteFullDataFileHandler extends FullDataFileHandler @Override public CompletableFuture read(DhSectionPos pos) { - return super.read(pos).thenCompose(fullDataSource -> { - if (fullDataSource == null) - return null; - - if (!fullDataSource.isEmpty()) - return CompletableFuture.completedFuture(fullDataSource); - - FullDataMetaFile metaFile = this.getLoadOrMakeFile(pos, true); - totalRequests++; - return networkClient.sendRequest(new FullDataSourceRequestMessage(pos)) - .handle((response, throwable) -> { - try - { - finishedRequests++; - if (throwable != null) - throw throwable; - - LOGGER.info("FullDataSourceResponseMessage " + pos); - return response.getFullDataSource(metaFile, level); - } - catch (Throwable e) - { - failedRequests++; - LOGGER.error(e); - return null; - } - }); - }); + FullDataMetaFile metaFile = this.getLoadOrMakeFile(pos, false); + if (metaFile != null) + { + return super.read(pos).thenCompose(fullDataSource -> requestFromServer(pos, fullDataSource)); + } + else + { + return requestFromServer(pos, null).thenCompose(fullDataSource -> fullDataSource != null + ? CompletableFuture.completedFuture(fullDataSource) + : super.read(pos)); + } + } + + @NotNull + private CompletableFuture requestFromServer(DhSectionPos pos, IFullDataSource fullDataSource) + { + totalRequests++; + return networkClient.sendRequest(new FullDataSourceRequestMessage(pos)) + .handle((response, throwable) -> { + try + { + finishedRequests++; + if (throwable != null) + throw throwable; + + LOGGER.info("FullDataSourceResponseMessage " + pos); + FullDataMetaFile metaFile = this.getLoadOrMakeFile(pos, true); + return response.getFullDataSource(metaFile, level); + } + catch (Throwable e) + { + failedRequests++; + LOGGER.error("Error while fetching full data source", e); + return fullDataSource; + } + }); } private String[] f3Log() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index ce497272f..6eb5cd1a0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -80,10 +80,14 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel // stop world gen serverside.stopWorldGen(); } - + } + + public void doWorldGen(DhBlockPos2D pos) { + this.doWorldGen(); + if (serverside.isWorldGenRunning()) { - serverside.worldGenTick(new DhBlockPos2D(0, 0)); // todo; + serverside.worldGenTick(pos); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/logging/ConfigBasedLogger.java b/core/src/main/java/com/seibel/distanthorizons/core/logging/ConfigBasedLogger.java index 8f52f8572..9e09e1825 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/logging/ConfigBasedLogger.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/logging/ConfigBasedLogger.java @@ -101,7 +101,7 @@ public class ConfigBasedLogger else logger.log(logLevel, msgStr); } - if (mode.levelForChat.isLessSpecificThan(level)) + if (MC != null && mode.levelForChat.isLessSpecificThan(level)) { if (param.length > 0 && param[param.length - 1] instanceof Throwable) MC.logToChat(level, msgStr + "\n" + diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java index 9ac154682..d4d0e025c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java @@ -24,6 +24,7 @@ import java.io.File; import java.util.HashMap; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentLinkedQueue; public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld { @@ -33,7 +34,8 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld private final NetworkServer networkServer; private final HashMap playersByUUID; private final BiMap playersByConnection; - + private final ConcurrentLinkedQueue worldGenLoopingQueue = new ConcurrentLinkedQueue<>(); + public DhServerWorld() @@ -109,9 +111,11 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld public void addPlayer(IServerPlayerWrapper serverPlayer) { this.playersByUUID.put(serverPlayer.getUUID(), new RemotePlayer(serverPlayer)); + this.worldGenLoopingQueue.add(serverPlayer); } public void removePlayer(IServerPlayerWrapper serverPlayer) { + this.worldGenLoopingQueue.remove(serverPlayer); RemotePlayer dhPlayer = this.playersByUUID.remove(serverPlayer.getUUID()); ChannelHandlerContext channelContext = this.playersByConnection.inverse().remove(dhPlayer); if (channelContext != null) @@ -167,7 +171,19 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld public void serverTick() { this.levels.values().forEach(DhServerLevel::serverTick); } - public void doWorldGen() { this.levels.values().forEach(DhServerLevel::doWorldGen); } + public void doWorldGen() { this.levels.values().forEach(level -> { + // TODO Deal with dimensions and dimension switches + + IServerPlayerWrapper firstPlayer = this.worldGenLoopingQueue.poll(); + if (firstPlayer == null) { + level.doWorldGen(); + return; + } + this.worldGenLoopingQueue.add(firstPlayer); + + com.seibel.distanthorizons.coreapi.util.math.Vec3d position = firstPlayer.getPosition(); + level.doWorldGen(new DhBlockPos2D((int) position.x, (int) position.z)); + }); } @Override public CompletableFuture saveAndFlush() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java index 6b08113e4..cd0a73448 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java @@ -2,6 +2,7 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.misc; import com.seibel.distanthorizons.api.interfaces.IDhApiUnsafeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; +import com.seibel.distanthorizons.coreapi.util.math.Vec3d; import java.util.UUID; @@ -10,4 +11,6 @@ public interface IServerPlayerWrapper extends IDhApiUnsafeWrapper UUID getUUID(); IServerLevelWrapper getLevel(); + + Vec3d getPosition(); } From d2f2a3b8aafdc646179dcda5d6e2446405116a5c Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 24 Jul 2023 19:59:16 +0500 Subject: [PATCH 014/205] Attempt to use GeneratedFullDataFileHandler --- .../core/file/fullDatafile/GeneratedFullDataFileHandler.java | 3 ++- .../core/file/fullDatafile/RemoteFullDataFileHandler.java | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java index 5e9c3d96e..01d926f04 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java @@ -9,6 +9,7 @@ import com.seibel.distanthorizons.core.generation.WorldGenerationQueue; import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker; import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult; import com.seibel.distanthorizons.core.level.DhLevel; +import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.level.IDhServerLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhLodPos; @@ -35,7 +36,7 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler // Use to hold onto incomplete data sources that are waiting for generation, so that they don't get GC'd before they are generated private final ConcurrentHashMap incompleteDataSources = new ConcurrentHashMap<>(); - public GeneratedFullDataFileHandler(IDhServerLevel level, AbstractSaveStructure saveStructure) { super(level, saveStructure); } + public GeneratedFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure) { super(level, saveStructure); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java index 1bdbd7dae..65a2bc3c6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java @@ -15,7 +15,7 @@ import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.concurrent.CompletableFuture; -public class RemoteFullDataFileHandler extends FullDataFileHandler +public class RemoteFullDataFileHandler extends GeneratedFullDataFileHandler { protected static final Logger LOGGER = DhLoggerBuilder.getLogger(); From 95d721e1a391da6c35ab5c9200f2675038408586 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 31 Jul 2023 15:23:56 +0500 Subject: [PATCH 015/205] World generation --- .../core/api/internal/ClientApi.java | 1 + .../core/api/internal/ServerApi.java | 7 +- .../core/api/internal/SharedApi.java | 11 ++ .../loader/CompleteFullDataSourceLoader.java | 8 + .../sources/CompleteFullDataSource.java | 5 +- .../GeneratedFullDataFileHandler.java | 11 +- .../RemoteFullDataFileHandler.java | 80 +-------- .../generation/IWorldGenerationQueue.java | 22 +++ .../core/generation/WorldGenerationQueue.java | 3 +- .../WorldRemoteGenerationQueue.java | 158 ++++++++++++++++++ .../core/level/ClientLevelModule.java | 10 +- .../core/level/DhClientLevel.java | 78 ++++++++- .../core/level/DhClientServerLevel.java | 10 +- .../core/level/DhServerLevel.java | 13 +- .../core/level/IDhClientLevel.java | 2 +- .../core/level/IDhServerLevel.java | 4 +- .../core/level/IDhWorldGenLevel.java | 9 + .../core/level/ServerLevelModule.java | 100 +---------- .../core/level/WorldGenModule.java | 125 ++++++++++++++ .../core/network/ChildNetworkEventSource.java | 43 ----- .../core/network/NetworkClient.java | 30 ++-- .../core/network/NetworkEventSource.java | 45 ++--- .../core/network/NetworkServer.java | 13 +- .../core/network/messages/AckMessage.java | 14 +- .../core/network/messages/CloseMessage.java | 4 +- .../network/messages/CloseReasonMessage.java | 4 +- .../FullDataSourceResponseMessage.java | 26 ++- .../core/network/messages/HelloMessage.java | 4 +- .../network/messages/PlayerUUIDMessage.java | 9 +- .../messages/RemotePlayerConfigMessage.java | 9 +- .../FutureTrackableNetworkMessage.java | 8 +- .../network/protocol/INetworkMessage.java | 8 - .../core/network/protocol/MessageDecoder.java | 2 +- .../core/network/protocol/MessageEncoder.java | 4 +- .../core/network/protocol/MessageHandler.java | 12 +- .../network/protocol/MessageRegistry.java | 14 +- .../core/network/protocol/NetworkMessage.java | 21 +++ .../core/world/DhClientWorld.java | 38 ++--- .../core/world/DhServerWorld.java | 104 ++++++++---- .../core/world/IDhClientWorld.java | 2 + 40 files changed, 639 insertions(+), 432 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/generation/IWorldGenerationQueue.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/level/IDhWorldGenLevel.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/ChildNetworkEventSource.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkMessage.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkMessage.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index 644d47172..55fe9ba6b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -271,6 +271,7 @@ public class ClientApi if (clientWorld != null) { clientWorld.clientTick(); + SharedApi.worldGenTick(clientWorld::doWorldGen); } profiler.pop(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java index 4a0379701..18c23a3e9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java @@ -64,12 +64,7 @@ public class ServerApi if (serverWorld != null) { serverWorld.serverTick(); - this.lastWorldGenTickDelta--; - if (this.lastWorldGenTickDelta <= 0) - { - serverWorld.doWorldGen(); - this.lastWorldGenTickDelta = 20; - } + SharedApi.worldGenTick(serverWorld::doWorldGen); } } catch (Exception e) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java index a5b785e4e..3d0f5eb19 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java @@ -12,6 +12,7 @@ import com.seibel.distanthorizons.core.world.*; public class SharedApi { private static AbstractDhWorld currentWorld; + private static int lastWorldGenTickDelta = 0; @@ -49,6 +50,16 @@ public class SharedApi } } + public static void worldGenTick(Runnable worldGenRunnable) + { + lastWorldGenTickDelta--; + if (lastWorldGenTickDelta <= 0) + { + worldGenRunnable.run(); + lastWorldGenTickDelta = 20; + } + } + public static AbstractDhWorld getAbstractDhWorld() { return currentWorld; } /** returns null if the {@link SharedApi#currentWorld} isn't a {@link DhClientServerWorld} */ public static DhClientServerWorld getDhClientServerWorld() { return (currentWorld != null && DhClientServerWorld.class.isInstance(currentWorld)) ? (DhClientServerWorld) currentWorld : null; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/loader/CompleteFullDataSourceLoader.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/loader/CompleteFullDataSourceLoader.java index 3c4d1903c..65adb662c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/loader/CompleteFullDataSourceLoader.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/loader/CompleteFullDataSourceLoader.java @@ -3,6 +3,7 @@ package com.seibel.distanthorizons.core.dataObjects.fullData.loader; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile; import com.seibel.distanthorizons.core.level.IDhLevel; +import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; @@ -23,4 +24,11 @@ public class CompleteFullDataSourceLoader extends AbstractFullDataSourceLoader return dataSource; } + /** Uses a given stream to create a temporary {@link CompleteFullDataSource}, which is not saved. */ + public CompleteFullDataSource loadData(DhSectionPos pos, DhDataInputStream inputStream, IDhLevel level) throws IOException, InterruptedException + { + CompleteFullDataSource dataSource = CompleteFullDataSource.createEmpty(pos); + dataSource.populateFromStream(null, inputStream, level); + return dataSource; + } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java index 8ac18dbcd..808dd93ed 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java @@ -21,6 +21,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import org.apache.logging.log4j.Logger; +import javax.annotation.CheckForNull; import java.io.*; /** @@ -85,10 +86,10 @@ public class CompleteFullDataSource extends FullDataArrayAccessor implements IFu } @Override - public FullDataSourceSummaryData readSourceSummaryInfo(FullDataMetaFile dataFile, DhDataInputStream inputStream, IDhLevel level) throws IOException + public FullDataSourceSummaryData readSourceSummaryInfo(@CheckForNull FullDataMetaFile dataFile, DhDataInputStream inputStream, IDhLevel level) throws IOException { int dataDetail = inputStream.readInt(); - if (dataFile.baseMetaData != null && dataDetail != dataFile.baseMetaData.dataLevel) + if (dataFile != null && dataFile.baseMetaData != null && dataDetail != dataFile.baseMetaData.dataLevel) { throw new IOException(LodUtil.formatLog("Data level mismatch: " + dataDetail + " != " + dataFile.baseMetaData.dataLevel)); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java index 01d926f04..ff65b0b21 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java @@ -5,6 +5,7 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFull import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IIncompleteFullDataSource; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; +import com.seibel.distanthorizons.core.generation.IWorldGenerationQueue; import com.seibel.distanthorizons.core.generation.WorldGenerationQueue; import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker; import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult; @@ -29,7 +30,7 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - private final AtomicReference worldGenQueueRef = new AtomicReference<>(null); + private final AtomicReference worldGenQueueRef = new AtomicReference<>(null); private final ArrayList onWorldGenTaskCompleteListeners = new ArrayList<>(); @@ -57,7 +58,7 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler //==================// /** Assumes there isn't a pre-existing queue. */ - public void setGenerationQueue(WorldGenerationQueue newWorldGenQueue) + public void setGenerationQueue(IWorldGenerationQueue newWorldGenQueue) { boolean oldQueueExists = this.worldGenQueueRef.compareAndSet(null, newWorldGenQueue); LodUtil.assertTrue(oldQueueExists, "previous world gen queue is still here!"); @@ -109,12 +110,12 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler @Nullable private CompletableFuture tryStartGenTask(FullDataMetaFile file, IIncompleteFullDataSource dataSource) { - WorldGenerationQueue worldGenQueue = this.worldGenQueueRef.get(); + IWorldGenerationQueue worldGenQueue = this.worldGenQueueRef.get(); // breaks down the missing positions into the desired detail level that the gen queue could accept if (worldGenQueue != null && !file.genQueueChecked) { DhSectionPos pos = file.pos; file.genQueueChecked = true; - byte maxSectDataDetailLevel = worldGenQueue.largestDataDetail; + byte maxSectDataDetailLevel = worldGenQueue.largestDataDetail(); byte targetDataDetailLevel = dataSource.getDataDetailLevel(); if (targetDataDetailLevel > maxSectDataDetailLevel) { @@ -210,7 +211,7 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler if (source instanceof IIncompleteFullDataSource && !file.genQueueChecked) { - WorldGenerationQueue worldGenQueue = this.worldGenQueueRef.get(); + IWorldGenerationQueue worldGenQueue = this.worldGenQueueRef.get(); if (worldGenQueue != null) { CompletableFuture future = this.updateFromExistingDataSources(file, (IIncompleteFullDataSource) source); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java index 65a2bc3c6..5cb0d8ee8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java @@ -1,89 +1,11 @@ package com.seibel.distanthorizons.core.file.fullDatafile; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.level.IDhLevel; -import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.logging.f3.F3Screen; -import com.seibel.distanthorizons.core.network.NetworkClient; -import com.seibel.distanthorizons.core.network.messages.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.FullDataSourceResponseMessage; -import com.seibel.distanthorizons.core.pos.DhSectionPos; -import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.concurrent.CompletableFuture; public class RemoteFullDataFileHandler extends GeneratedFullDataFileHandler { - protected static final Logger LOGGER = DhLoggerBuilder.getLogger(); - - private final NetworkClient networkClient; - - private final F3Screen.NestedMessage f3Message; - private int finishedRequests = 0; - private int totalRequests = 0; - private int failedRequests = 0; - - - public RemoteFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure, NetworkClient networkClient) { + public RemoteFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure) { super(level, saveStructure); - this.networkClient = networkClient; - this.f3Message = new F3Screen.NestedMessage(this::f3Log); - } - - @Override - public CompletableFuture read(DhSectionPos pos) { - FullDataMetaFile metaFile = this.getLoadOrMakeFile(pos, false); - if (metaFile != null) - { - return super.read(pos).thenCompose(fullDataSource -> requestFromServer(pos, fullDataSource)); - } - else - { - return requestFromServer(pos, null).thenCompose(fullDataSource -> fullDataSource != null - ? CompletableFuture.completedFuture(fullDataSource) - : super.read(pos)); - } - } - - @NotNull - private CompletableFuture requestFromServer(DhSectionPos pos, IFullDataSource fullDataSource) - { - totalRequests++; - return networkClient.sendRequest(new FullDataSourceRequestMessage(pos)) - .handle((response, throwable) -> { - try - { - finishedRequests++; - if (throwable != null) - throw throwable; - - LOGGER.info("FullDataSourceResponseMessage " + pos); - FullDataMetaFile metaFile = this.getLoadOrMakeFile(pos, true); - return response.getFullDataSource(metaFile, level); - } - catch (Throwable e) - { - failedRequests++; - LOGGER.error("Error while fetching full data source", e); - return fullDataSource; - } - }); - } - - private String[] f3Log() - { - ArrayList lines = new ArrayList<>(); - lines.add("Remote Full Data File Handler ["+this.level.getLevelWrapper().getDimensionType().getDimensionName()+"]"); - lines.add(" Requests: "+this.finishedRequests +" / "+this.totalRequests +" (failed: "+ this.failedRequests+")"); - return lines.toArray(new String[0]); - } - - @Override - public void close() { - f3Message.close(); - super.close(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/IWorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/IWorldGenerationQueue.java new file mode 100644 index 000000000..43b33ce12 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/IWorldGenerationQueue.java @@ -0,0 +1,22 @@ +package com.seibel.distanthorizons.core.generation; + +import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker; +import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult; +import com.seibel.distanthorizons.core.pos.DhBlockPos2D; +import com.seibel.distanthorizons.core.pos.DhLodPos; + +import java.io.Closeable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; + +public interface IWorldGenerationQueue extends Closeable +{ + byte largestDataDetail(); + + CompletableFuture submitGenTask(DhLodPos pos, byte requiredDataDetail, IWorldGenTaskTracker tracker); + void runCurrentGenTasksUntilBusy(DhBlockPos2D targetPos); + + CompletableFuture startClosing(boolean cancelCurrentGeneration, boolean alsoInterruptRunning); + + void close(); +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java index 228990d6c..e64eac970 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java @@ -29,7 +29,7 @@ import java.util.*; import java.util.concurrent.*; import java.util.function.Consumer; -public class WorldGenerationQueue implements Closeable, IDebugRenderable +public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRenderable { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); @@ -47,6 +47,7 @@ public class WorldGenerationQueue implements Closeable, IDebugRenderable /** largest numerical detail level allowed */ public final byte largestDataDetail; + @Override public byte largestDataDetail() { return this.largestDataDetail; } /** lowest numerical detail level allowed */ public final byte smallestDataDetail; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java new file mode 100644 index 000000000..69f9d1827 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -0,0 +1,158 @@ +package com.seibel.distanthorizons.core.generation; + +import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; +import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker; +import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult; +import com.seibel.distanthorizons.core.level.IDhClientLevel; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.logging.f3.F3Screen; +import com.seibel.distanthorizons.core.network.NetworkClient; +import com.seibel.distanthorizons.core.network.messages.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.FullDataSourceResponseMessage; +import com.seibel.distanthorizons.core.pos.DhBlockPos2D; +import com.seibel.distanthorizons.core.pos.DhChunkPos; +import com.seibel.distanthorizons.core.pos.DhLodPos; +import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; +import org.apache.logging.log4j.Logger; + +import javax.annotation.CheckForNull; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Semaphore; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; + +public class WorldRemoteGenerationQueue implements IWorldGenerationQueue +{ + private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + + private static final int MAX_CONCURRENT_REQUESTS = 5; + + private final NetworkClient networkClient; + private final IDhClientLevel level; + + private final ConcurrentMap waitingTasks = new ConcurrentHashMap<>(); + private final AtomicInteger pendingTasks = new AtomicInteger(); + + private final F3Screen.NestedMessage f3Message = new F3Screen.NestedMessage(this::f3Log); + private final AtomicInteger finishedRequests = new AtomicInteger(); + private final AtomicInteger totalRequests = new AtomicInteger(); + private final AtomicInteger failedRequests = new AtomicInteger(); + + public WorldRemoteGenerationQueue(NetworkClient networkClient, IDhClientLevel level) + { + this.networkClient = networkClient; + this.level = level; + } + + @Override public byte largestDataDetail() + { + return LodUtil.BLOCK_DETAIL_LEVEL; + } + + @Override public CompletableFuture submitGenTask(DhLodPos lodPos, byte requiredDataDetail, IWorldGenTaskTracker tracker) + { + LodUtil.assertTrue(lodPos.detailLevel == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL, "Only highest-detail sections are allowed."); + DhSectionPos sectionPos = new DhSectionPos(lodPos.detailLevel, lodPos); + + totalRequests.incrementAndGet(); + + WorldGenQueueEntry entry = new WorldGenQueueEntry(new CompletableFuture<>(), tracker); + waitingTasks.put(sectionPos, entry); + return entry.future; + } + + @Override public void runCurrentGenTasksUntilBusy(DhBlockPos2D targetPos) + { + if (pendingTasks.get() > MAX_CONCURRENT_REQUESTS || waitingTasks.isEmpty()) + return; + + DhSectionPos sectionPos = waitingTasks.keySet().stream().reduce(null, (a, b) + -> a != null + && a.getCenter().getCenterBlockPos().distSquared(targetPos) + < b.getCenter().getCenterBlockPos().distSquared(targetPos) + ? a : b); + + WorldGenQueueEntry entry = waitingTasks.remove(sectionPos); + pendingTasks.incrementAndGet(); + + networkClient.sendRequest(new FullDataSourceRequestMessage(sectionPos)) + .handle((response, throwable) -> { + pendingTasks.decrementAndGet(); + finishedRequests.incrementAndGet(); + + try + { + if (throwable != null) + throw throwable; + + LOGGER.info("FullDataSourceResponseMessage " + sectionPos); + CompleteFullDataSource fullDataSource = response.getFullDataSource(sectionPos, level); + + Consumer chunkDataConsumer = entry.tracker.getChunkDataConsumer(); + + sectionPos.forEachChildAtLevel(LodUtil.CHUNK_DETAIL_LEVEL, childPos -> { + ChunkSizedFullDataAccessor accessor = new ChunkSizedFullDataAccessor(new DhChunkPos(childPos.sectionX, childPos.sectionZ)); + + int detailLevelDifference = sectionPos.sectionDetailLevel - childPos.sectionDetailLevel; + int childRelativeX = childPos.sectionX - sectionPos.sectionX * BitShiftUtil.powerOfTwo(detailLevelDifference); + int childRelativeZ = childPos.sectionZ - sectionPos.sectionZ * BitShiftUtil.powerOfTwo(detailLevelDifference); + + fullDataSource.subView( + LodUtil.CHUNK_WIDTH, + childRelativeX * LodUtil.CHUNK_WIDTH, + childRelativeZ * LodUtil.CHUNK_WIDTH + ).shadowCopyTo(accessor); + + chunkDataConsumer.accept(accessor); + }); + + return entry.future.complete(WorldGenResult.CreateSuccess(sectionPos)); + } + catch (Throwable e) + { + failedRequests.incrementAndGet(); + LOGGER.error("Error while fetching full data source", e); + return entry.future.complete(WorldGenResult.CreateFail()); + } + }); + } + + private String[] f3Log() + { + ArrayList lines = new ArrayList<>(); + lines.add("World Remote Generation Queue ["+level.getClientLevelWrapper().getDimensionType().getDimensionName()+"]"); + lines.add(" Requests: "+this.finishedRequests +" / "+this.totalRequests +" (failed: "+ this.failedRequests+")"); + return lines.toArray(new String[0]); + } + + @Override public CompletableFuture startClosing(boolean cancelCurrentGeneration, boolean alsoInterruptRunning) + { + // TODO Cancel generation requests? + return CompletableFuture.completedFuture(null); + } + + @Override public void close() + { + f3Message.close(); + } + + private static class WorldGenQueueEntry + { + public CompletableFuture future; + public IWorldGenTaskTracker tracker; + + public WorldGenQueueEntry(CompletableFuture future, IWorldGenTaskTracker tracker) + { + this.future = future; + this.tracker = tracker; + } + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java b/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java index ba0499ccd..95e62063c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java @@ -1,14 +1,17 @@ package com.seibel.distanthorizons.core.level; import com.seibel.distanthorizons.api.enums.rendering.EDebugRendering; +import com.seibel.distanthorizons.core.config.AppliedConfigState; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; import com.seibel.distanthorizons.core.file.renderfile.RenderSourceFileHandler; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; +import com.seibel.distanthorizons.core.generation.WorldRemoteGenerationQueue; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.f3.F3Screen; +import com.seibel.distanthorizons.core.network.NetworkClient; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhLodPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; @@ -19,20 +22,21 @@ import com.seibel.distanthorizons.core.render.renderer.LodRenderer; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.coreapi.util.math.Mat4f; import org.apache.logging.log4j.Logger; -import java.io.Closeable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; -public class ClientLevelModule implements Closeable { +public class ClientLevelModule { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); private final IDhClientLevel parent; public final AtomicReference ClientRenderStateRef = new AtomicReference<>(); public final F3Screen.NestedMessage f3Message; + public ClientLevelModule(IDhClientLevel parent) { this.parent = parent; @@ -171,7 +175,7 @@ public class ClientLevelModule implements Closeable { return CompletableFuture.completedFuture(null); } } - + public void close() { // shutdown the renderer diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 42a9f8045..087a39974 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -1,14 +1,21 @@ package com.seibel.distanthorizons.core.level; +import com.seibel.distanthorizons.core.config.AppliedConfigState; +import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; import com.seibel.distanthorizons.core.file.fullDatafile.RemoteFullDataFileHandler; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; +import com.seibel.distanthorizons.core.generation.WorldRemoteGenerationQueue; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.network.ChildNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkClient; import com.seibel.distanthorizons.core.pos.DhBlockPos; +import com.seibel.distanthorizons.core.pos.DhBlockPos2D; +import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; @@ -16,17 +23,34 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.coreapi.util.math.Mat4f; import org.apache.logging.log4j.Logger; +import java.awt.*; import java.util.concurrent.CompletableFuture; /** The level used when connected to a server */ public class DhClientLevel extends DhLevel implements IDhClientLevel { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - + private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); + + private static class WorldGenState extends WorldGenModule.WorldGenState + { + WorldGenState(IDhClientLevel level, NetworkClient client) + { + this.worldGenerationQueue = new WorldRemoteGenerationQueue(client, level); + } + } + public final ClientLevelModule clientside; public final IClientLevelWrapper levelWrapper; public final AbstractSaveStructure saveStructure; public final RemoteFullDataFileHandler dataFileHandler; + + private final NetworkClient networkClient; + public final WorldGenModule worldGenModule; + // TODO maybe use some other value? + public final AppliedConfigState worldGeneratorEnabledConfig; + + //=============// // constructor // @@ -36,7 +60,12 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel { this.levelWrapper = clientLevelWrapper; this.saveStructure = saveStructure; - dataFileHandler = new RemoteFullDataFileHandler(this, saveStructure, networkClient); + this.dataFileHandler = new RemoteFullDataFileHandler(this, saveStructure); + + this.networkClient = networkClient; + this.worldGenModule = new WorldGenModule(dataFileHandler, this); + this.worldGeneratorEnabledConfig = new AppliedConfigState<>(Config.Client.Advanced.WorldGenerator.enableDistantGeneration); + clientside = new ClientLevelModule(this); clientside.startRenderer(); LOGGER.info("Started DHLevel for "+this.levelWrapper+" with saves at "+this.saveStructure); @@ -52,6 +81,33 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel chunkToLodBuilder.tick(); clientside.clientTick(); } + + public void doWorldGen() + { + worldGeneratorEnabledConfig.pollNewValue(); + boolean shouldDoWorldGen = worldGeneratorEnabledConfig.get() && clientside.isRendering(); + boolean isWorldGenRunning = worldGenModule.isWorldGenRunning(); + if (shouldDoWorldGen && !isWorldGenRunning) + { + // start world gen + worldGenModule.startWorldGen(this.dataFileHandler, new WorldGenState(this, this.networkClient)); + } + else if (!shouldDoWorldGen && isWorldGenRunning) + { + // stop world gen + worldGenModule.stopWorldGen(this.dataFileHandler); + } + + if (worldGenModule.isWorldGenRunning()) + { + ClientLevelModule.ClientRenderState renderState = clientside.ClientRenderStateRef.get(); + if (renderState != null && renderState.quadtree != null) + { + dataFileHandler.removeGenRequestIf(p -> !renderState.quadtree.isSectionPosInBounds(p)); + } + worldGenModule.worldGenTick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); + } + } @Override public void render(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IProfilerWrapper profiler) { @@ -92,6 +148,8 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel @Override public void close() { + if (worldGenModule != null) + worldGenModule.close(); clientside.close(); super.close(); dataFileHandler.close(); @@ -117,5 +175,17 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel public AbstractSaveStructure getSaveStructure() { return saveStructure; } - + + @Override + public void onWorldGenTaskComplete(DhSectionPos pos) + { + //if (pos.sectionDetailLevel == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL) + DebugRenderer.makeParticle( + new DebugRenderer.BoxParticle( + new DebugRenderer.Box(pos, 128f, 156f, 0.09f, Color.red.darker()), + 0.2, 32f + ) + ); + clientside.reloadPos(pos); + } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java index ec96d5d0c..9e752973f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java @@ -72,26 +72,26 @@ public class DhClientServerLevel extends DhLevel implements IDhClientLevel, IDhS { serverside.worldGeneratorEnabledConfig.pollNewValue(); boolean shouldDoWorldGen = serverside.worldGeneratorEnabledConfig.get() && clientside.isRendering(); - boolean isWorldGenRunning = serverside.isWorldGenRunning(); + boolean isWorldGenRunning = serverside.worldGenModule.isWorldGenRunning(); if (shouldDoWorldGen && !isWorldGenRunning) { // start world gen - serverside.startWorldGen(); + serverside.worldGenModule.startWorldGen(serverside.dataFileHandler, new ServerLevelModule.WorldGenState(this)); } else if (!shouldDoWorldGen && isWorldGenRunning) { // stop world gen - serverside.stopWorldGen(); + serverside.worldGenModule.stopWorldGen(serverside.dataFileHandler); } - if (serverside.isWorldGenRunning()) + if (serverside.worldGenModule.isWorldGenRunning()) { ClientLevelModule.ClientRenderState renderState = clientside.ClientRenderStateRef.get(); if (renderState != null && renderState.quadtree != null) { serverside.dataFileHandler.removeGenRequestIf(p -> !renderState.quadtree.isSectionPosInBounds(p)); } - serverside.worldGenTick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); + serverside.worldGenModule.worldGenTick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 6eb5cd1a0..e4be9bc7b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -4,9 +4,6 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedF import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; -import com.seibel.distanthorizons.core.network.messages.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.FullDataSourceResponseMessage; -import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhLodPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; @@ -69,25 +66,25 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel public void doWorldGen() { boolean shouldDoWorldGen = true; //todo; - boolean isWorldGenRunning = serverside.isWorldGenRunning(); + boolean isWorldGenRunning = serverside.worldGenModule.isWorldGenRunning(); if (shouldDoWorldGen && !isWorldGenRunning) { // start world gen - serverside.startWorldGen(); + serverside.worldGenModule.startWorldGen(serverside.dataFileHandler, new ServerLevelModule.WorldGenState(this)); } else if (!shouldDoWorldGen && isWorldGenRunning) { // stop world gen - serverside.stopWorldGen(); + serverside.worldGenModule.stopWorldGen(serverside.dataFileHandler); } } public void doWorldGen(DhBlockPos2D pos) { this.doWorldGen(); - if (serverside.isWorldGenRunning()) + if (serverside.worldGenModule.isWorldGenRunning()) { - serverside.worldGenTick(pos); + serverside.worldGenModule.worldGenTick(pos); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhClientLevel.java index 3a55bd252..6c8fa7d32 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhClientLevel.java @@ -7,7 +7,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrap import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; -public interface IDhClientLevel extends IDhLevel +public interface IDhClientLevel extends IDhWorldGenLevel { void clientTick(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhServerLevel.java index 500c29eec..f98798900 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhServerLevel.java @@ -3,11 +3,11 @@ package com.seibel.distanthorizons.core.level; import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataFileHandler; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; -public interface IDhServerLevel extends IDhLevel, GeneratedFullDataFileHandler.IOnWorldGenCompleteListener +public interface IDhServerLevel extends IDhWorldGenLevel, GeneratedFullDataFileHandler.IOnWorldGenCompleteListener { void serverTick(); - void doWorldGen(); IServerLevelWrapper getServerLevelWrapper(); } + diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhWorldGenLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhWorldGenLevel.java new file mode 100644 index 000000000..a97faaadd --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhWorldGenLevel.java @@ -0,0 +1,9 @@ +package com.seibel.distanthorizons.core.level; + +import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataFileHandler; + +public interface IDhWorldGenLevel extends IDhLevel, GeneratedFullDataFileHandler.IOnWorldGenCompleteListener +{ + void doWorldGen(); + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/ServerLevelModule.java b/core/src/main/java/com/seibel/distanthorizons/core/level/ServerLevelModule.java index 168b0772a..301eee809 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/ServerLevelModule.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/ServerLevelModule.java @@ -17,9 +17,8 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; public class ServerLevelModule { - private static class WorldGenState + public static class WorldGenState extends WorldGenModule.WorldGenState { - public final WorldGenerationQueue worldGenerationQueue; WorldGenState(IDhServerLevel level) { IDhApiWorldGenerator worldGenerator = WorldGeneratorInjector.INSTANCE.get(level.getLevelWrapper()); @@ -33,26 +32,6 @@ public class ServerLevelModule { } this.worldGenerationQueue = new WorldGenerationQueue(worldGenerator); } - - CompletableFuture closeAsync(boolean doInterrupt) - { - return this.worldGenerationQueue.startClosing(true, doInterrupt) - .exceptionally(ex -> - { - LOGGER.error("Error closing generation queue", ex); - return null; - } - ).thenRun(this.worldGenerationQueue::close) - .exceptionally(ex -> - { - LOGGER.error("Error closing world gen", ex); - return null; - }); - } - - public void tick(DhBlockPos2D targetPosForGeneration) { - worldGenerationQueue.runCurrentGenTasksUntilBusy(targetPosForGeneration); - } } private static final Logger LOGGER = DhLoggerBuilder.getLogger(); @@ -61,7 +40,7 @@ public class ServerLevelModule { public final AbstractSaveStructure saveStructure; public final GeneratedFullDataFileHandler dataFileHandler; public final AppliedConfigState worldGeneratorEnabledConfig; - private final AtomicReference worldGenStateRef = new AtomicReference<>(); + public final WorldGenModule worldGenModule; public ServerLevelModule(IDhServerLevel parent, IServerLevelWrapper levelWrapper, AbstractSaveStructure saveStructure) { @@ -70,62 +49,7 @@ public class ServerLevelModule { this.saveStructure = saveStructure; this.dataFileHandler = new GeneratedFullDataFileHandler(parent, saveStructure); this.worldGeneratorEnabledConfig = new AppliedConfigState<>(Config.Client.Advanced.WorldGenerator.enableDistantGeneration); - } - - //==============// - // tick methods // - //==============// - - - public void startWorldGen() - { - // create the new world generator - WorldGenState newWgs = new WorldGenState(parent); - if (!this.worldGenStateRef.compareAndSet(null, newWgs)) - { - LOGGER.warn("Failed to start world gen due to concurrency"); - newWgs.closeAsync(false); - } - dataFileHandler.addWorldGenCompleteListener(parent); - dataFileHandler.setGenerationQueue(newWgs.worldGenerationQueue); - } - - public void stopWorldGen() - { - WorldGenState worldGenState = this.worldGenStateRef.get(); - if (worldGenState == null) - { - LOGGER.warn("Attempted to stop world gen when it was not running"); - return; - } - - // shut down the world generator - while (!this.worldGenStateRef.compareAndSet(worldGenState, null)) - { - worldGenState = this.worldGenStateRef.get(); - if (worldGenState == null) - { - return; - } - } - dataFileHandler.clearGenerationQueue(); - worldGenState.closeAsync(true).join(); //TODO: Make it async. - dataFileHandler.removeWorldGenCompleteListener(parent); - } - - public boolean isWorldGenRunning() - { - return this.worldGenStateRef.get() != null; - } - - public void worldGenTick(DhBlockPos2D targetPosForGeneration) - { - WorldGenState worldGenState = this.worldGenStateRef.get(); - if (worldGenState != null) - { - // queue new world generation requests - worldGenState.tick(targetPosForGeneration); - } + this.worldGenModule = new WorldGenModule(this.dataFileHandler, this.parent); } //===============// @@ -134,23 +58,7 @@ public class ServerLevelModule { public void close() { // shutdown the world-gen - WorldGenState worldGenState = this.worldGenStateRef.get(); - if (worldGenState != null) - { - while (!this.worldGenStateRef.compareAndSet(worldGenState, null)) - { - worldGenState = this.worldGenStateRef.get(); - if (worldGenState == null) - { - break; - } - } - - if (worldGenState != null) - { - worldGenState.closeAsync(true).join(); //TODO: Make it async. - } - } + this.worldGenModule.close(); dataFileHandler.close(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java b/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java new file mode 100644 index 000000000..dfe30a940 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java @@ -0,0 +1,125 @@ +package com.seibel.distanthorizons.core.level; + +import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataFileHandler; +import com.seibel.distanthorizons.core.generation.IWorldGenerationQueue; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.pos.DhBlockPos2D; +import org.apache.logging.log4j.Logger; + +import java.io.Closeable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; + +public class WorldGenModule implements Closeable +{ + private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + + private final GeneratedFullDataFileHandler dataFileHandler; + private final GeneratedFullDataFileHandler.IOnWorldGenCompleteListener onWorldGenCompleteListener; + private final AtomicReference worldGenStateRef = new AtomicReference<>(); + + public static abstract class WorldGenState + { + public IWorldGenerationQueue worldGenerationQueue; + + CompletableFuture closeAsync(boolean doInterrupt) + { + return this.worldGenerationQueue.startClosing(true, doInterrupt) + .exceptionally(ex -> + { + LOGGER.error("Error closing generation queue", ex); + return null; + } + ).thenRun(this.worldGenerationQueue::close) + .exceptionally(ex -> + { + LOGGER.error("Error closing world gen", ex); + return null; + }); + } + + public void tick(DhBlockPos2D targetPosForGeneration) + { + worldGenerationQueue.runCurrentGenTasksUntilBusy(targetPosForGeneration); + } + } + + public WorldGenModule(GeneratedFullDataFileHandler dataFileHandler, GeneratedFullDataFileHandler.IOnWorldGenCompleteListener onWorldGenCompleteListener) + { + this.dataFileHandler = dataFileHandler; + this.onWorldGenCompleteListener = onWorldGenCompleteListener; + } + + public void startWorldGen(GeneratedFullDataFileHandler dataFileHandler, WorldGenState newWgs) + { + // create the new world generator + if (!this.worldGenStateRef.compareAndSet(null, newWgs)) + { + LOGGER.warn("Failed to start world gen due to concurrency"); + newWgs.closeAsync(false); + } + dataFileHandler.addWorldGenCompleteListener(onWorldGenCompleteListener); + dataFileHandler.setGenerationQueue(newWgs.worldGenerationQueue); + } + + public void stopWorldGen(GeneratedFullDataFileHandler dataFileHandler) + { + WorldGenState worldGenState = this.worldGenStateRef.get(); + if (worldGenState == null) + { + LOGGER.warn("Attempted to stop world gen when it was not running"); + return; + } + + // shut down the world generator + while (!this.worldGenStateRef.compareAndSet(worldGenState, null)) + { + worldGenState = this.worldGenStateRef.get(); + if (worldGenState == null) + { + return; + } + } + dataFileHandler.clearGenerationQueue(); + worldGenState.closeAsync(true).join(); //TODO: Make it async. + dataFileHandler.removeWorldGenCompleteListener(onWorldGenCompleteListener); + } + + public boolean isWorldGenRunning() + { + return this.worldGenStateRef.get() != null; + } + + public void worldGenTick(DhBlockPos2D targetPosForGeneration) + { + WorldGenState worldGenState = this.worldGenStateRef.get(); + if (worldGenState != null) + { + // queue new world generation requests + worldGenState.tick(targetPosForGeneration); + } + } + + public void close() + { + // shutdown the world-gen + WorldGenState worldGenState = this.worldGenStateRef.get(); + if (worldGenState != null) + { + while (!this.worldGenStateRef.compareAndSet(worldGenState, null)) + { + worldGenState = this.worldGenStateRef.get(); + if (worldGenState == null) + { + break; + } + } + + if (worldGenState != null) + { + worldGenState.closeAsync(true).join(); //TODO: Make it async. + } + } + dataFileHandler.close(); + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/ChildNetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/ChildNetworkEventSource.java deleted file mode 100644 index 9ccd8c0dd..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/ChildNetworkEventSource.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.seibel.distanthorizons.core.network; - -import com.seibel.distanthorizons.core.network.protocol.INetworkMessage; -import io.netty.channel.ChannelHandlerContext; - -import java.util.function.BiConsumer; - -/** Provides a way to register network message handlers which are expected to be removed later. */ -public final class ChildNetworkEventSource extends NetworkEventSource -{ - private final TParent parent; - private boolean isClosed = false; - - public ChildNetworkEventSource(TParent parent) - { - this.parent = parent; - } - public ChildNetworkEventSource(ChildNetworkEventSource child) - { - this(child.parent); - } - - @Override public void registerHandler(Class handlerClass, BiConsumer handlerImplementation) - { - if (isClosed) return; - - if (!this.hasHandler(handlerClass)) - { - parent.registerHandler(handlerClass, this::handleMessage); - } - - super.registerHandler(handlerClass, handlerImplementation); - } - - @Override public void close() - { - isClosed = true; - for (Class handlerClass : this.handlers.keySet()) - { - parent.removeHandler(handlerClass, this::handleMessage); - } - } -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index bd6073460..862286f2a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -1,12 +1,14 @@ package com.seibel.distanthorizons.core.network; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.network.messages.AckMessage; import com.seibel.distanthorizons.core.network.messages.CloseMessage; import com.seibel.distanthorizons.core.network.messages.CloseReasonMessage; import com.seibel.distanthorizons.core.network.messages.HelloMessage; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.MessageHandler; import com.seibel.distanthorizons.core.network.protocol.NetworkChannelInitializer; +import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; @@ -59,20 +61,15 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable private void registerHandlers() { - this.registerAckHandler(HelloMessage.class, channelContext -> - { - LOGGER.info("Connected to server: "+channelContext.channel().remoteAddress()); - }); - - this.registerHandler(CloseReasonMessage.class, (closeReasonMessage, channelContext) -> + this.registerHandler(CloseReasonMessage.class, closeReasonMessage -> { LOGGER.info(closeReasonMessage.reason); this.connectionState = EConnectionState.CLOSE_WAIT; }); - this.registerHandler(CloseMessage.class, (closeMessage, channelContext) -> + this.registerHandler(CloseMessage.class, closeMessage -> { - LOGGER.info("Disconnected from server: "+channelContext.channel().remoteAddress()); + LOGGER.info("Disconnected from server: "+closeMessage.getChannelContext().channel().remoteAddress()); if (this.connectionState == EConnectionState.CLOSE_WAIT) { this.close(); @@ -90,13 +87,13 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable ChannelFuture connectFuture = this.clientBootstrap.connect(this.address); connectFuture.addListener((ChannelFuture channelFuture) -> { - if (!channelFuture.isSuccess()) + if (!channelFuture.isSuccess()) { - LOGGER.warn("Connection failed: "+channelFuture.cause()); - return; - } + LOGGER.warn("Connection failed: "+channelFuture.cause()); + return; + } - this.channel.writeAndFlush(new HelloMessage()); + channel.writeAndFlush(new HelloMessage()); }); this.channel = connectFuture.channel(); @@ -129,13 +126,6 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable } }); } - - /** Kills the current connection, triggering auto-reconnection immediately. */ - public void reconnect() - { - this.connectionState = EConnectionState.RECONNECT_FORCE; - this.channel.disconnect(); - } public final CompletableFuture sendRequest(FutureTrackableNetworkMessage msg) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index f87adafb1..db9583423 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -5,38 +5,32 @@ import com.google.common.collect.Table; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.messages.AckMessage; -import com.seibel.distanthorizons.core.network.protocol.INetworkMessage; -import io.netty.channel.Channel; +import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import io.netty.channel.ChannelHandlerContext; import org.apache.logging.log4j.Logger; import java.util.*; import java.util.concurrent.CompletableFuture; -import java.util.function.BiConsumer; import java.util.function.Consumer; public abstract class NetworkEventSource { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - protected final Map, Set>> handlers = new HashMap<>(); + protected final Map, Set>> handlers = new HashMap<>(); private final Table> pendingFutures = HashBasedTable.create(); - protected boolean hasHandler(Class handlerClass) - { - return this.handlers.containsKey(handlerClass); - } - - protected void handleMessage(INetworkMessage message, ChannelHandlerContext channelContext) + protected final void handleMessage(NetworkMessage message, ChannelHandlerContext channelContext) { + message.setChannelContext(channelContext); boolean handled = false; - Set> handlerList = this.handlers.get(message.getClass()); + Set> handlerList = this.handlers.get(message.getClass()); if (handlerList != null) { - for (BiConsumer handler : handlerList) + for (Consumer handler : handlerList) { handled = true; - handler.accept(message, channelContext); + handler.accept(message); } } @@ -57,27 +51,10 @@ public abstract class NetworkEventSource } } - public void registerHandler(Class handlerClass, BiConsumer handlerImplementation) + public void registerHandler(Class handlerClass, Consumer handlerImplementation) { this.handlers.computeIfAbsent(handlerClass, missingHandlerClass -> new HashSet<>()) - .add((BiConsumer) handlerImplementation); - } - - public void registerAckHandler(Class clazz, Consumer handler) - { - this.registerHandler(AckMessage.class, (ackMessage, channelContext) -> - { - if (ackMessage.messageType == clazz) - { - handler.accept(channelContext); - } - }); - } - - protected void removeHandler(Class handlerClass, BiConsumer handlerImplementation) - { - this.handlers.computeIfAbsent(handlerClass, missingHandlerClass -> new HashSet<>()) - .remove(handlerImplementation); + .add((Consumer) handlerImplementation); } protected CompletableFuture sendRequest(ChannelHandlerContext ctx, FutureTrackableNetworkMessage msg) @@ -93,13 +70,13 @@ public abstract class NetworkEventSource return responseFuture; } - protected void completeAllFuturesExceptionally(ChannelHandlerContext ctx, Throwable cause) { + protected final void completeAllFuturesExceptionally(ChannelHandlerContext ctx, Throwable cause) { for (CompletableFuture futureData : pendingFutures.row(ctx).values()) futureData.completeExceptionally(cause); pendingFutures.row(ctx).clear(); } - protected void completeAllFuturesExceptionally(Throwable cause) { + protected final void completeAllFuturesExceptionally(Throwable cause) { for (ChannelHandlerContext ctx : pendingFutures.rowKeySet()) this.completeAllFuturesExceptionally(ctx, cause); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java index 68645ba69..31bb8fa0c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java @@ -44,8 +44,9 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable private void registerHandlers() { - this.registerHandler(HelloMessage.class, (helloMessage, channelContext) -> + this.registerHandler(HelloMessage.class, helloMessage -> { + ChannelHandlerContext channelContext = helloMessage.getChannelContext(); LOGGER.info("Client connected: "+channelContext.channel().remoteAddress()); if (helloMessage.version != ModInfo.PROTOCOL_VERSION) @@ -63,13 +64,15 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable return; } - channelContext.writeAndFlush(new AckMessage(HelloMessage.class)); + channelContext.writeAndFlush(new HelloMessage()); }); - this.registerHandler(CloseMessage.class, (closeMessage, channelContext) -> + this.registerHandler(CloseMessage.class, closeMessage -> { - LOGGER.info("Client disconnected: "+channelContext.channel().remoteAddress()); - this.completeAllFuturesExceptionally(channelContext, channelContext.channel().closeFuture().cause()); + Channel channel = closeMessage.getChannelContext().channel(); + LOGGER.info("Client disconnected: "+channel.remoteAddress()); + + this.completeAllFuturesExceptionally(closeMessage.getChannelContext(), channel.closeFuture().cause()); }); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/AckMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/AckMessage.java index a846352c7..6587ec517 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/AckMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/AckMessage.java @@ -1,6 +1,7 @@ package com.seibel.distanthorizons.core.network.messages; -import com.seibel.distanthorizons.core.network.protocol.INetworkMessage; +import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import com.seibel.distanthorizons.core.network.protocol.MessageRegistry; import io.netty.buffer.ByteBuf; @@ -8,19 +9,14 @@ import io.netty.buffer.ByteBuf; * Simple empty response message. * This message is not sent automatically. */ -public class AckMessage implements INetworkMessage +public class AckMessage extends FutureTrackableNetworkMessage { - public Class messageType; - - - public AckMessage() { } - public AckMessage(Class messageType) { this.messageType = messageType; } @Override - public void encode(ByteBuf out) { out.writeInt(MessageRegistry.INSTANCE.getMessageId(this.messageType)); } + public void encode0(ByteBuf out) { } @Override - public void decode(ByteBuf in) { this.messageType = MessageRegistry.INSTANCE.getMessageClassById(in.readInt()); } + public void decode0(ByteBuf in) { } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseMessage.java index f42aa3515..88b04cc7f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseMessage.java @@ -1,13 +1,13 @@ package com.seibel.distanthorizons.core.network.messages; -import com.seibel.distanthorizons.core.network.protocol.INetworkMessage; +import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import io.netty.buffer.ByteBuf; /** * This is not a "real" message, and only used to indicate a disconnection. * To send a "disconnect reason" message, use {@link CloseReasonMessage}. */ -public class CloseMessage implements INetworkMessage +public class CloseMessage extends NetworkMessage { @Override public void encode(ByteBuf out) { throw new UnsupportedOperationException("CloseMessage is not a real message, and must not be sent."); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseReasonMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseReasonMessage.java index 0671eb3c9..2a87bb4af 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseReasonMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseReasonMessage.java @@ -1,10 +1,10 @@ package com.seibel.distanthorizons.core.network.messages; -import com.seibel.distanthorizons.core.network.protocol.INetworkMessage; +import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import io.netty.buffer.ByteBuf; -public class CloseReasonMessage implements INetworkMessage +public class CloseReasonMessage extends NetworkMessage { public String reason; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java index 5bea6199b..6806a2364 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java @@ -1,11 +1,12 @@ package com.seibel.distanthorizons.core.network.messages; import com.seibel.distanthorizons.core.dataObjects.fullData.loader.AbstractFullDataSourceLoader; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; -import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile; +import com.seibel.distanthorizons.core.dataObjects.fullData.loader.CompleteFullDataSourceLoader; +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream; import io.netty.buffer.ByteBuf; @@ -16,14 +17,14 @@ import java.io.IOException; public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage { - private IFullDataSource fullDataSource; + private CompleteFullDataSource fullDataSource; private DhServerLevel level; - private AbstractFullDataSourceLoader fullDataSourceLoader; + private CompleteFullDataSourceLoader fullDataSourceLoader; private ByteBuf dataBuffer; public FullDataSourceResponseMessage() {} - public FullDataSourceResponseMessage(IFullDataSource fullDataSource, DhServerLevel level) + public FullDataSourceResponseMessage(CompleteFullDataSource fullDataSource, DhServerLevel level) { this.fullDataSource = fullDataSource; this.level = level; @@ -38,7 +39,6 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage fullDataSource.writeToStream(dhOutputStream, level); dhOutputStream.flush(); - out.writeLong(fullDataSource.getTypeId()); out.writeByte(fullDataSource.getBinaryDataFormatVersion()); out.writeInt(outputStream.size()); out.writeBytes(outputStream.toByteArray()); @@ -46,25 +46,21 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage } @Override - public void decode0(ByteBuf in) throws IOException + public void decode0(ByteBuf in) { - long typeId = in.readLong(); byte dataVersion = in.readByte(); - this.fullDataSourceLoader = AbstractFullDataSourceLoader.getLoader(typeId, dataVersion); - if (this.fullDataSourceLoader == null) - { - throw new IOException("Invalid file: Data type loader not found: "+typeId+"(v"+dataVersion +")"); - } + this.fullDataSourceLoader = (CompleteFullDataSourceLoader) AbstractFullDataSourceLoader.getLoader(CompleteFullDataSource.TYPE_ID, dataVersion); + assert this.fullDataSourceLoader != null; this.dataBuffer = in.readBytes(in.readInt()); } - public IFullDataSource getFullDataSource(FullDataMetaFile metaFile, IDhLevel level) throws IOException, InterruptedException + public CompleteFullDataSource getFullDataSource(DhSectionPos pos, IDhLevel level) throws IOException, InterruptedException { try (ByteBufInputStream inputStream = new ByteBufInputStream(dataBuffer)) { - return fullDataSourceLoader.loadData(metaFile, new DhDataInputStream(inputStream), level); + return fullDataSourceLoader.loadData(pos, new DhDataInputStream(inputStream), level); } } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/HelloMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/HelloMessage.java index 155f97dec..8c1ae2b7b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/HelloMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/HelloMessage.java @@ -1,10 +1,10 @@ package com.seibel.distanthorizons.core.network.messages; -import com.seibel.distanthorizons.core.network.protocol.INetworkMessage; +import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import com.seibel.distanthorizons.coreapi.ModInfo; import io.netty.buffer.ByteBuf; -public class HelloMessage implements INetworkMessage +public class HelloMessage extends NetworkMessage { public int version = ModInfo.PROTOCOL_VERSION; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PlayerUUIDMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PlayerUUIDMessage.java index 09f15fd2c..8632ca651 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PlayerUUIDMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PlayerUUIDMessage.java @@ -1,11 +1,12 @@ package com.seibel.distanthorizons.core.network.messages; -import com.seibel.distanthorizons.core.network.protocol.INetworkMessage; +import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import io.netty.buffer.ByteBuf; import java.util.UUID; -public class PlayerUUIDMessage implements INetworkMessage +public class PlayerUUIDMessage extends FutureTrackableNetworkMessage { public UUID playerUUID; @@ -15,13 +16,13 @@ public class PlayerUUIDMessage implements INetworkMessage public PlayerUUIDMessage(UUID playerUUID) { this.playerUUID = playerUUID; } @Override - public void encode(ByteBuf out) + public void encode0(ByteBuf out) { out.writeLong(this.playerUUID.getMostSignificantBits()); out.writeLong(this.playerUUID.getLeastSignificantBits()); } @Override - public void decode(ByteBuf in) { this.playerUUID = new UUID(in.readLong(), in.readLong()); } + public void decode0(ByteBuf in) { this.playerUUID = new UUID(in.readLong(), in.readLong()); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/RemotePlayerConfigMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/RemotePlayerConfigMessage.java index 8f0910801..21734c0a6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/RemotePlayerConfigMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/RemotePlayerConfigMessage.java @@ -1,11 +1,12 @@ package com.seibel.distanthorizons.core.network.messages; +import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; -import com.seibel.distanthorizons.core.network.protocol.INetworkMessage; +import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import com.seibel.distanthorizons.core.network.objects.RemotePlayer; import io.netty.buffer.ByteBuf; -public class RemotePlayerConfigMessage implements INetworkMessage +public class RemotePlayerConfigMessage extends FutureTrackableNetworkMessage { public RemotePlayer.Payload payload; @@ -15,9 +16,9 @@ public class RemotePlayerConfigMessage implements INetworkMessage public RemotePlayerConfigMessage(RemotePlayer.Payload payload) { this.payload = payload; } @Override - public void encode(ByteBuf out) { this.payload.encode(out); } + public void encode0(ByteBuf out) { this.payload.encode(out); } @Override - public void decode(ByteBuf in) { this.payload = INetworkObject.decode(new RemotePlayer.Payload(), in); } + public void decode0(ByteBuf in) { this.payload = INetworkObject.decode(new RemotePlayer.Payload(), in); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java index 362232c59..185ca037d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java @@ -3,15 +3,15 @@ package com.seibel.distanthorizons.core.network.protocol; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -public abstract class FutureTrackableNetworkMessage implements INetworkMessage +public abstract class FutureTrackableNetworkMessage extends NetworkMessage { private static int lastId = 0; public int futureId = lastId++; - public static void sendResponse(ChannelHandlerContext ctx, FutureTrackableNetworkMessage requestMessage, FutureTrackableNetworkMessage responseMessage) + public void sendResponse(FutureTrackableNetworkMessage responseMessage) { - responseMessage.futureId = requestMessage.futureId; - ctx.writeAndFlush(responseMessage); + responseMessage.futureId = futureId; + getChannelContext().writeAndFlush(responseMessage); } @Override public final void encode(ByteBuf out) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkMessage.java deleted file mode 100644 index 270baef88..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkMessage.java +++ /dev/null @@ -1,8 +0,0 @@ -package com.seibel.distanthorizons.core.network.protocol; - -/** For now this is only used for constraining listeners */ -public interface INetworkMessage extends INetworkObject -{ - -} - diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageDecoder.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageDecoder.java index 19eb98722..25521b413 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageDecoder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageDecoder.java @@ -11,7 +11,7 @@ public class MessageDecoder extends ByteToMessageDecoder @Override protected void decode(ChannelHandlerContext channelContext, ByteBuf inputByteBuf, List outputDecodedObjectList) { - INetworkMessage message = MessageRegistry.INSTANCE.createMessage(inputByteBuf.readShort()); + NetworkMessage message = MessageRegistry.INSTANCE.createMessage(inputByteBuf.readShort()); outputDecodedObjectList.add(INetworkObject.decode(message, inputByteBuf)); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageEncoder.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageEncoder.java index ddb7ed5f3..5f3c35f60 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageEncoder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageEncoder.java @@ -4,10 +4,10 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; -public class MessageEncoder extends MessageToByteEncoder +public class MessageEncoder extends MessageToByteEncoder { @Override - protected void encode(ChannelHandlerContext channelContext, INetworkMessage message, ByteBuf outputByteBuf) throws IllegalArgumentException + protected void encode(ChannelHandlerContext channelContext, NetworkMessage message, ByteBuf outputByteBuf) throws IllegalArgumentException { outputByteBuf.writeShort(MessageRegistry.INSTANCE.getMessageId(message)); message.encode(outputByteBuf); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java index 7a36c0196..8b5a7c2bd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java @@ -8,26 +8,22 @@ import io.netty.channel.SimpleChannelInboundHandler; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; import java.util.function.BiConsumer; @ChannelHandler.Sharable -public class MessageHandler extends SimpleChannelInboundHandler +public class MessageHandler extends SimpleChannelInboundHandler { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - private final BiConsumer messageConsumer; + private final BiConsumer messageConsumer; - public MessageHandler(BiConsumer messageConsumer) + public MessageHandler(BiConsumer messageConsumer) { this.messageConsumer = messageConsumer; } @Override - protected void channelRead0(ChannelHandlerContext channelContext, INetworkMessage message) + protected void channelRead0(ChannelHandlerContext channelContext, NetworkMessage message) { LOGGER.trace("Received message: " + message.getClass().getSimpleName()); this.messageConsumer.accept(message, channelContext); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java index 0d7d1d2a7..48eae6fe6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java @@ -12,8 +12,8 @@ public class MessageRegistry { public static final MessageRegistry INSTANCE = new MessageRegistry(); - private final Map> idToSupplier = new HashMap<>(); - private final BiMap, Integer> classToId = HashBiMap.create(); + private final Map> idToSupplier = new HashMap<>(); + private final BiMap, Integer> classToId = HashBiMap.create(); @@ -35,16 +35,16 @@ public class MessageRegistry - public void registerMessage(Class clazz, Supplier supplier) + public void registerMessage(Class clazz, Supplier supplier) { int id = this.idToSupplier.size() + 1; this.idToSupplier.put(id, supplier); this.classToId.put(clazz, id); } - public Class getMessageClassById(int messageId) { return this.classToId.inverse().get(messageId); } + public Class getMessageClassById(int messageId) { return this.classToId.inverse().get(messageId); } - public INetworkMessage createMessage(int messageId) throws IllegalArgumentException + public NetworkMessage createMessage(int messageId) throws IllegalArgumentException { try { @@ -56,9 +56,9 @@ public class MessageRegistry } } - public int getMessageId(INetworkMessage message) { return this.getMessageId(message.getClass()); } + public int getMessageId(NetworkMessage message) { return this.getMessageId(message.getClass()); } - public int getMessageId(Class messageClass) { + public int getMessageId(Class messageClass) { try { return this.classToId.get(messageClass); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkMessage.java new file mode 100644 index 000000000..e5a07187b --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkMessage.java @@ -0,0 +1,21 @@ +package com.seibel.distanthorizons.core.network.protocol; + +import io.netty.channel.ChannelHandlerContext; + +public abstract class NetworkMessage implements INetworkObject +{ + private ChannelHandlerContext channelContext = null; + + public ChannelHandlerContext getChannelContext() + { + return channelContext; + } + + public void setChannelContext(ChannelHandlerContext channelContext) + { + if (this.channelContext != null) + throw new IllegalStateException("Channel context cannot be changed after initial setting."); + this.channelContext = channelContext; + } +} + diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java index 3cd4ad498..ae69722f5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java @@ -4,8 +4,10 @@ import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure; import com.seibel.distanthorizons.core.level.DhClientLevel; +import com.seibel.distanthorizons.core.level.DhClientServerLevel; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.network.NetworkClient; +import com.seibel.distanthorizons.core.network.messages.AckMessage; import com.seibel.distanthorizons.core.network.messages.HelloMessage; import com.seibel.distanthorizons.core.network.messages.PlayerUUIDMessage; import com.seibel.distanthorizons.core.network.messages.RemotePlayerConfigMessage; @@ -62,26 +64,20 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld } private void registerNetworkHandlers() { - this.networkClient.registerAckHandler(HelloMessage.class, ctx -> - { - ctx.writeAndFlush(new PlayerUUIDMessage(MC_CLIENT.getPlayerUUID())); - }); - - // TODO Proper payload handling - this.networkClient.registerAckHandler(PlayerUUIDMessage.class, ctx -> - { - ctx.writeAndFlush(new RemotePlayerConfigMessage(new RemotePlayer.Payload())); - }); - this.networkClient.registerHandler(RemotePlayerConfigMessage.class, (msg, ctx) -> - { - - }); - - this.networkClient.registerAckHandler(RemotePlayerConfigMessage.class, ctx -> - { - // TODO Actually request chunks - // ctx.writeAndFlush(new ChunkRequestMessage(new DhSectionPos(new DhBlockPos2D(0, 0)))); - }); + this.networkClient.registerHandler(HelloMessage.class, helloMessage -> + { + LOGGER.info("Connected to server: "+helloMessage.getChannelContext().channel().remoteAddress()); + + this.networkClient.sendRequest(new PlayerUUIDMessage(MC_CLIENT.getPlayerUUID())) + .thenCompose(ack -> this.networkClient.sendRequest(new RemotePlayerConfigMessage(new RemotePlayer.Payload()))) + .thenAccept(responseConfigMsg -> { + // TODO do something with received config + }) + .exceptionally(throwable -> { + LOGGER.error("Error while fetching server's config", throwable); + return null; + }); + }); } @@ -146,6 +142,8 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld } public void clientTick() { this.eventLoop.tick(); } + + public void doWorldGen() { this.levels.values().forEach(DhClientLevel::doWorldGen); } @Override public CompletableFuture saveAndFlush() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java index d4d0e025c..ac61dc862 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java @@ -2,7 +2,10 @@ package com.seibel.distanthorizons.core.world; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; +import com.google.common.collect.Multimap; +import com.google.common.collect.MultimapBuilder; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IIncompleteFullDataSource; import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataFileHandler; import com.seibel.distanthorizons.core.file.structure.LocalSaveStructure; @@ -20,11 +23,10 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import io.netty.channel.ChannelHandlerContext; +import javax.annotation.CheckForNull; import java.io.File; -import java.util.HashMap; -import java.util.UUID; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.*; +import java.util.concurrent.*; public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld { @@ -34,7 +36,9 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld private final NetworkServer networkServer; private final HashMap playersByUUID; private final BiMap playersByConnection; + private final ConcurrentLinkedQueue worldGenLoopingQueue = new ConcurrentLinkedQueue<>(); + private ConcurrentMap incompleteDataSources = new ConcurrentHashMap<>(); @@ -56,17 +60,18 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld private void registerNetworkHandlers() { - this.networkServer.registerHandler(CloseMessage.class, (closeMessage, channelContext) -> + this.networkServer.registerHandler(CloseMessage.class, closeMessage -> { - RemotePlayer dhPlayer = this.playersByConnection.remove(channelContext); + RemotePlayer dhPlayer = this.playersByConnection.remove(closeMessage.getChannelContext()); if (dhPlayer != null) { dhPlayer.channelContext = null; } }); - this.networkServer.registerHandler(PlayerUUIDMessage.class, (playerUUIDMessage, channelContext) -> + this.networkServer.registerHandler(PlayerUUIDMessage.class, playerUUIDMessage -> { + ChannelHandlerContext channelContext = playerUUIDMessage.getChannelContext(); RemotePlayer dhPlayer = this.playersByUUID.get(playerUUIDMessage.playerUUID); if (dhPlayer == null) @@ -84,27 +89,30 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld dhPlayer.channelContext = channelContext; this.playersByConnection.put(channelContext, dhPlayer); - channelContext.writeAndFlush(new AckMessage(PlayerUUIDMessage.class)); + playerUUIDMessage.sendResponse(new AckMessage()); }); - this.networkServer.registerHandler(RemotePlayerConfigMessage.class, (dhRemotePlayerConfigMessage, channelContext) -> + this.networkServer.registerHandler(RemotePlayerConfigMessage.class, remotePlayerConfigMessage -> { - // TODO Take notice of received payload and possibly echo back a constrained version - channelContext.writeAndFlush(new AckMessage(RemotePlayerConfigMessage.class)); + // TODO Take notice of and/or constrain received payload + remotePlayerConfigMessage.sendResponse(remotePlayerConfigMessage); }); // This should be at DhServerLevel I guess - this.networkServer.registerHandler(FullDataSourceRequestMessage.class, (msg, ctx) -> + this.networkServer.registerHandler(FullDataSourceRequestMessage.class, msg -> { LOGGER.info("FullDataSourceRequestMessage received at pos ({}, {}) with detail level {}", msg.dhSectionPos.sectionX, msg.dhSectionPos.sectionZ, msg.dhSectionPos.sectionDetailLevel); - DhServerLevel level = this.getLevel(playersByConnection.get(ctx).serverPlayer.getLevel()); + DhServerLevel level = this.getLevel(playersByConnection.get(msg.getChannelContext()).serverPlayer.getLevel()); GeneratedFullDataFileHandler handler = level.serverside.dataFileHandler; - handler.read(msg.dhSectionPos).thenAccept(fullDataSource -> { - // Send chunk response message back - FutureTrackableNetworkMessage.sendResponse(ctx, msg, new FullDataSourceResponseMessage(fullDataSource, level)); - }); + incompleteDataSources.computeIfAbsent(msg.dhSectionPos, pos -> { + IncompleteDataSourceEntry entry = new IncompleteDataSourceEntry(); + handler.read(msg.dhSectionPos).thenAccept(fullDataSource -> { + entry.fullDataSource = fullDataSource; + }); + return entry; + }).requestMessages.add(msg); }); } @@ -169,21 +177,50 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld } } - public void serverTick() { this.levels.values().forEach(DhServerLevel::serverTick); } - - public void doWorldGen() { this.levels.values().forEach(level -> { - // TODO Deal with dimensions and dimension switches + public void serverTick() { + this.levels.values().forEach(DhServerLevel::serverTick); - IServerPlayerWrapper firstPlayer = this.worldGenLoopingQueue.poll(); - if (firstPlayer == null) { - level.doWorldGen(); - return; + for (Iterator it = incompleteDataSources.values().iterator(); it.hasNext(); ) + { + IncompleteDataSourceEntry entry = it.next(); + if (entry.fullDataSource instanceof IIncompleteFullDataSource) + { + IIncompleteFullDataSource incompleteSource = (IIncompleteFullDataSource) entry.fullDataSource; + if (!incompleteSource.hasBeenPromoted()) continue; + entry.fullDataSource = incompleteSource.tryPromotingToCompleteDataSource(); + } + + if (!(entry.fullDataSource instanceof CompleteFullDataSource)) + LodUtil.assertNotReach("Invalid full data source"); + + it.remove(); + CompleteFullDataSource completeSource = (CompleteFullDataSource) entry.fullDataSource; + + for (FullDataSourceRequestMessage msg : entry.requestMessages) + { + RemotePlayer remotePlayer = playersByConnection.get(msg.getChannelContext()); + if (remotePlayer == null) continue; + DhServerLevel level = this.getLevel(remotePlayer.serverPlayer.getLevel()); + msg.sendResponse(new FullDataSourceResponseMessage(completeSource, level)); + } } - this.worldGenLoopingQueue.add(firstPlayer); - - com.seibel.distanthorizons.coreapi.util.math.Vec3d position = firstPlayer.getPosition(); - level.doWorldGen(new DhBlockPos2D((int) position.x, (int) position.z)); - }); } + } + + public void doWorldGen() { + this.levels.values().forEach(level -> { + // TODO Deal with dimensions and dimension switches + + IServerPlayerWrapper firstPlayer = this.worldGenLoopingQueue.poll(); + if (firstPlayer == null) { + level.doWorldGen(); + return; + } + this.worldGenLoopingQueue.add(firstPlayer); + + com.seibel.distanthorizons.coreapi.util.math.Vec3d position = firstPlayer.getPosition(); + level.doWorldGen(new DhBlockPos2D((int) position.x, (int) position.z)); + }); + } @Override public CompletableFuture saveAndFlush() @@ -206,4 +243,11 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld LOGGER.info("Closed DhWorld of type "+this.environment); } + private static class IncompleteDataSourceEntry + { + @CheckForNull + public IFullDataSource fullDataSource; + public Set requestMessages = ConcurrentHashMap.newKeySet(); + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/IDhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/IDhClientWorld.java index ccac84a10..4d467a73f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/IDhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/IDhClientWorld.java @@ -7,6 +7,8 @@ public interface IDhClientWorld extends IDhWorld { void clientTick(); + void doWorldGen(); + default IDhClientLevel getOrLoadClientLevel(ILevelWrapper levelWrapper) { return (IDhClientLevel) this.getOrLoadLevel(levelWrapper); } } From 6d1f9803cee3fb0b0d874dd036d1bb1491195b3c Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 31 Jul 2023 17:42:14 +0500 Subject: [PATCH 016/205] Avoid losing requests due to concurrency --- .../core/world/DhServerWorld.java | 46 ++++++++++++------- 1 file changed, 29 insertions(+), 17 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java index ac61dc862..dd246a17c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java @@ -2,8 +2,6 @@ package com.seibel.distanthorizons.core.world; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; -import com.google.common.collect.Multimap; -import com.google.common.collect.MultimapBuilder; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IIncompleteFullDataSource; @@ -14,13 +12,13 @@ import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.network.NetworkServer; import com.seibel.distanthorizons.core.network.messages.*; import com.seibel.distanthorizons.core.network.objects.RemotePlayer; -import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; +import com.seibel.distanthorizons.coreapi.util.math.Vec3d; import io.netty.channel.ChannelHandlerContext; import javax.annotation.CheckForNull; @@ -38,7 +36,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld private final BiMap playersByConnection; private final ConcurrentLinkedQueue worldGenLoopingQueue = new ConcurrentLinkedQueue<>(); - private ConcurrentMap incompleteDataSources = new ConcurrentHashMap<>(); + private final ConcurrentMap incompleteDataSources = new ConcurrentHashMap<>(); @@ -106,13 +104,23 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld DhServerLevel level = this.getLevel(playersByConnection.get(msg.getChannelContext()).serverPlayer.getLevel()); GeneratedFullDataFileHandler handler = level.serverside.dataFileHandler; - incompleteDataSources.computeIfAbsent(msg.dhSectionPos, pos -> { - IncompleteDataSourceEntry entry = new IncompleteDataSourceEntry(); - handler.read(msg.dhSectionPos).thenAccept(fullDataSource -> { - entry.fullDataSource = fullDataSource; + while (true) + { + IncompleteDataSourceEntry entry = incompleteDataSources.computeIfAbsent(msg.dhSectionPos, pos -> { + IncompleteDataSourceEntry newEntry = new IncompleteDataSourceEntry(); + handler.read(msg.dhSectionPos).thenAccept(fullDataSource -> { + newEntry.fullDataSource = fullDataSource; + }); + return newEntry; }); - return entry; - }).requestMessages.add(msg); + // If this fails, current entry is being drained and need create another one + if (entry.requestCollectionSemaphore.tryAcquire()) + { + entry.requestMessages.add(msg); + entry.requestCollectionSemaphore.release(); + return; + } + } }); } @@ -183,6 +191,8 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld for (Iterator it = incompleteDataSources.values().iterator(); it.hasNext(); ) { IncompleteDataSourceEntry entry = it.next(); + if (entry.fullDataSource == null) continue; + if (entry.fullDataSource instanceof IIncompleteFullDataSource) { IIncompleteFullDataSource incompleteSource = (IIncompleteFullDataSource) entry.fullDataSource; @@ -190,12 +200,13 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld entry.fullDataSource = incompleteSource.tryPromotingToCompleteDataSource(); } - if (!(entry.fullDataSource instanceof CompleteFullDataSource)) - LodUtil.assertNotReach("Invalid full data source"); - - it.remove(); - CompleteFullDataSource completeSource = (CompleteFullDataSource) entry.fullDataSource; + LodUtil.assertTrue(entry.fullDataSource instanceof CompleteFullDataSource, "Invalid full data source"); + it.remove(); + // This semaphore is intentionally acquired forever + entry.requestCollectionSemaphore.acquireUninterruptibly(Short.MAX_VALUE); + + CompleteFullDataSource completeSource = (CompleteFullDataSource) entry.fullDataSource; for (FullDataSourceRequestMessage msg : entry.requestMessages) { RemotePlayer remotePlayer = playersByConnection.get(msg.getChannelContext()); @@ -217,7 +228,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld } this.worldGenLoopingQueue.add(firstPlayer); - com.seibel.distanthorizons.coreapi.util.math.Vec3d position = firstPlayer.getPosition(); + Vec3d position = firstPlayer.getPosition(); level.doWorldGen(new DhBlockPos2D((int) position.x, (int) position.z)); }); } @@ -247,7 +258,8 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld { @CheckForNull public IFullDataSource fullDataSource; - public Set requestMessages = ConcurrentHashMap.newKeySet(); + public final Set requestMessages = ConcurrentHashMap.newKeySet(); + public final Semaphore requestCollectionSemaphore = new Semaphore(Short.MAX_VALUE, true); } } From 781b5889801375b1d820d813e3b5ba3b6634a76f Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 1 Aug 2023 12:55:36 +0500 Subject: [PATCH 017/205] Implement server-side rate limiting --- .../distanthorizons/core/config/Config.java | 13 +++- .../WorldRemoteGenerationQueue.java | 68 +++++++++++++------ .../core/level/DhClientLevel.java | 8 ++- .../objects => multiplayer}/RemotePlayer.java | 21 ++++-- .../core/network/ChildNetworkEventSource.java | 40 +++++++++++ .../core/network/NetworkClient.java | 39 +++++++++-- .../core/network/NetworkEventSource.java | 31 +++++++-- .../exceptions/RateLimitedException.java | 9 +++ .../network/messages/ExceptionMessage.java | 39 +++++++++++ .../messages/RemotePlayerConfigMessage.java | 3 +- .../network/protocol/EMessageHandlerSide.java | 11 --- .../FutureTrackableNetworkMessage.java | 7 +- .../core/network/protocol/MessageHandler.java | 8 ++- .../network/protocol/MessageRegistry.java | 1 + .../core/world/DhClientWorld.java | 27 ++++---- .../core/world/DhServerWorld.java | 32 +++++++-- .../assets/distanthorizons/lang/en_us.json | 14 ++-- 17 files changed, 293 insertions(+), 78 deletions(-) rename core/src/main/java/com/seibel/distanthorizons/core/{network/objects => multiplayer}/RemotePlayer.java (54%) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/ChildNetworkEventSource.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/RateLimitedException.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/ExceptionMessage.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/protocol/EMessageHandlerSide.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 3180916c4..1670a7f58 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -747,7 +747,9 @@ public class Config public static ConfigEntry enableServerNetworking = new ConfigEntry.Builder() .set(false) .comment("" - + "Attention: this is only for developers and hasn't been implemented.\n" + + "Attention: \n" + + " 1. This feature is not fully implemented. \n" + + " 2. If you really want to use it, enable it only on trusted server/with trusted players. \n" + "\n" + "If true Distant Horizons will attempt to communicate with the connected \n" + "server in order to load LODs outside your vanilla render distance. \n" @@ -756,6 +758,15 @@ public class Config + "") .build(); + public static ConfigEntry serverNetworkingRateLimit = new ConfigEntry.Builder() + .setMinDefaultMax(1, 20, 100) + .comment("" + + "Limits the amount of sent/processed LOD requests concurrently. \n" + + "\n" + + "Note: Server can set its own rate limit. \n" + + "") + .build(); + } public static class MultiThreading diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 69f9d1827..2e0e9e527 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -1,5 +1,6 @@ package com.seibel.distanthorizons.core.generation; +import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker; @@ -7,20 +8,22 @@ import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult; import com.seibel.distanthorizons.core.level.IDhClientLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.f3.F3Screen; +import com.seibel.distanthorizons.core.network.ChildNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkClient; +import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.messages.FullDataSourceRequestMessage; import com.seibel.distanthorizons.core.network.messages.FullDataSourceResponseMessage; +import com.seibel.distanthorizons.core.network.messages.RemotePlayerConfigMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhLodPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; +import io.netty.channel.ChannelException; import org.apache.logging.log4j.Logger; -import javax.annotation.CheckForNull; import java.util.ArrayList; -import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; @@ -33,13 +36,13 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - private static final int MAX_CONCURRENT_REQUESTS = 5; - - private final NetworkClient networkClient; + private final ChildNetworkEventSource eventSource; private final IDhClientLevel level; private final ConcurrentMap waitingTasks = new ConcurrentHashMap<>(); - private final AtomicInteger pendingTasks = new AtomicInteger(); + private final Semaphore pendingTasksSemaphore = new Semaphore(Short.MAX_VALUE); + private int pendingTasks() { return Short.MAX_VALUE - pendingTasksSemaphore.availablePermits(); } + private int maxConcurrentRequests = 0; private final F3Screen.NestedMessage f3Message = new F3Screen.NestedMessage(this::f3Log); private final AtomicInteger finishedRequests = new AtomicInteger(); @@ -48,8 +51,12 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue public WorldRemoteGenerationQueue(NetworkClient networkClient, IDhClientLevel level) { - this.networkClient = networkClient; + this.eventSource = new ChildNetworkEventSource<>(networkClient); this.level = level; + + eventSource.registerHandler(RemotePlayerConfigMessage.class, msg -> { + maxConcurrentRequests = Math.min(msg.payload.fullDataRequestRateLimit, Config.Client.Advanced.Multiplayer.serverNetworkingRateLimit.get()); + }); } @Override public byte largestDataDetail() @@ -71,21 +78,24 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue @Override public void runCurrentGenTasksUntilBusy(DhBlockPos2D targetPos) { - if (pendingTasks.get() > MAX_CONCURRENT_REQUESTS || waitingTasks.isEmpty()) - return; - + while (eventSource.parent.isReady() && pendingTasks() < maxConcurrentRequests && !waitingTasks.isEmpty()) + sendNewRequest(targetPos); + } + + private void sendNewRequest(DhBlockPos2D targetPos) + { DhSectionPos sectionPos = waitingTasks.keySet().stream().reduce(null, (a, b) -> a != null && a.getCenter().getCenterBlockPos().distSquared(targetPos) - < b.getCenter().getCenterBlockPos().distSquared(targetPos) + < b.getCenter().getCenterBlockPos().distSquared(targetPos) ? a : b); WorldGenQueueEntry entry = waitingTasks.remove(sectionPos); - pendingTasks.incrementAndGet(); + pendingTasksSemaphore.acquireUninterruptibly(); - networkClient.sendRequest(new FullDataSourceRequestMessage(sectionPos)) + eventSource.parent.sendRequest(new FullDataSourceRequestMessage(sectionPos)) .handle((response, throwable) -> { - pendingTasks.decrementAndGet(); + pendingTasksSemaphore.release(); finishedRequests.incrementAndGet(); try @@ -114,14 +124,25 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue chunkDataConsumer.accept(accessor); }); - return entry.future.complete(WorldGenResult.CreateSuccess(sectionPos)); + entry.future.complete(WorldGenResult.CreateSuccess(sectionPos)); + } + catch (RateLimitedException e) + { + LOGGER.warn("Rate limited by server, re-queueing task: "+sectionPos, e); + finishedRequests.decrementAndGet(); + waitingTasks.put(sectionPos, entry); + } + catch (ChannelException e) { + finishedRequests.decrementAndGet(); + waitingTasks.put(sectionPos, entry); } catch (Throwable e) { - failedRequests.incrementAndGet(); LOGGER.error("Error while fetching full data source", e); - return entry.future.complete(WorldGenResult.CreateFail()); + failedRequests.incrementAndGet(); + entry.future.complete(WorldGenResult.CreateFail()); } + return null; }); } @@ -129,18 +150,27 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue { ArrayList lines = new ArrayList<>(); lines.add("World Remote Generation Queue ["+level.getClientLevelWrapper().getDimensionType().getDimensionName()+"]"); - lines.add(" Requests: "+this.finishedRequests +" / "+this.totalRequests +" (failed: "+ this.failedRequests+")"); + lines.add(" Requests: "+this.finishedRequests+" / "+this.totalRequests +" (failed: "+ this.failedRequests+")"); + lines.add(" Pending: "+this.pendingTasks()+" / "+this.maxConcurrentRequests); return lines.toArray(new String[0]); } @Override public CompletableFuture startClosing(boolean cancelCurrentGeneration, boolean alsoInterruptRunning) { - // TODO Cancel generation requests? + pendingTasksSemaphore.acquireUninterruptibly(Short.MAX_VALUE); + + if (cancelCurrentGeneration) + { + for (WorldGenQueueEntry entry : this.waitingTasks.values()) + entry.future.cancel(alsoInterruptRunning); + } + return CompletableFuture.completedFuture(null); } @Override public void close() { + eventSource.close(); f3Message.close(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 087a39974..282869454 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -22,7 +22,9 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapp import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.coreapi.util.math.Mat4f; import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.Nullable; +import javax.annotation.CheckForNull; import java.awt.*; import java.util.concurrent.CompletableFuture; @@ -45,6 +47,7 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel public final AbstractSaveStructure saveStructure; public final RemoteFullDataFileHandler dataFileHandler; + @CheckForNull private final NetworkClient networkClient; public final WorldGenModule worldGenModule; // TODO maybe use some other value? @@ -56,7 +59,7 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel // constructor // //=============// - public DhClientLevel(AbstractSaveStructure saveStructure, IClientLevelWrapper clientLevelWrapper, NetworkClient networkClient) + public DhClientLevel(AbstractSaveStructure saveStructure, IClientLevelWrapper clientLevelWrapper, @Nullable NetworkClient networkClient) { this.levelWrapper = clientLevelWrapper; this.saveStructure = saveStructure; @@ -85,7 +88,8 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel public void doWorldGen() { worldGeneratorEnabledConfig.pollNewValue(); - boolean shouldDoWorldGen = worldGeneratorEnabledConfig.get() && clientside.isRendering(); + boolean isClientWorking = networkClient != null && networkClient.isWorking(); + boolean shouldDoWorldGen = worldGeneratorEnabledConfig.get() && isClientWorking && clientside.isRendering(); boolean isWorldGenRunning = worldGenModule.isWorldGenRunning(); if (shouldDoWorldGen && !isWorldGenRunning) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/objects/RemotePlayer.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayer.java similarity index 54% rename from core/src/main/java/com/seibel/distanthorizons/core/network/objects/RemotePlayer.java rename to core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayer.java index 30f65397d..ac025ea61 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/objects/RemotePlayer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayer.java @@ -1,15 +1,18 @@ -package com.seibel.distanthorizons.core.network.objects; +package com.seibel.distanthorizons.core.multiplayer; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; +import java.util.concurrent.atomic.AtomicInteger; + public class RemotePlayer { public IServerPlayerWrapper serverPlayer; public Payload payload; public ChannelHandlerContext channelContext; + public final AtomicInteger pendingFullDataRequests = new AtomicInteger(); @@ -17,17 +20,23 @@ public class RemotePlayer public static class Payload implements INetworkObject { - // TODO Replace this example with useful fields, - // this should include any information the server needs to know about the connected client public int renderDistance; - + public int fullDataRequestRateLimit; @Override - public void encode(ByteBuf out) { out.writeInt(this.renderDistance); } + public void encode(ByteBuf out) + { + out.writeInt(this.renderDistance); + out.writeInt(this.fullDataRequestRateLimit); + } @Override - public void decode(ByteBuf in) { this.renderDistance = in.readInt(); } + public void decode(ByteBuf in) + { + this.renderDistance = in.readInt(); + this.fullDataRequestRateLimit = in.readInt(); + } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/ChildNetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/ChildNetworkEventSource.java new file mode 100644 index 000000000..c8535878f --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/ChildNetworkEventSource.java @@ -0,0 +1,40 @@ +package com.seibel.distanthorizons.core.network; + +import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; + +import java.util.function.Consumer; + +/** Provides a way to register network message handlers which are expected to be removed later. */ +public final class ChildNetworkEventSource extends NetworkEventSource +{ + public final TParent parent; + private boolean isClosed = false; + + public ChildNetworkEventSource(TParent parent) + { + this.parent = parent; + } + + @Override + public void registerHandler(Class handlerClass, Consumer handlerImplementation) + { + if (isClosed) return; + + if (!this.hasHandler(handlerClass)) + { + parent.registerHandler(handlerClass, this::handleMessage); + } + + super.registerHandler(handlerClass, handlerImplementation); + } + + @Override + public void close() + { + isClosed = true; + for (Class handlerClass : this.handlers.keySet()) + { + parent.removeHandler(handlerClass, this::handleMessage); + } + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index d4d45fa5c..68732d0f7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -1,7 +1,6 @@ package com.seibel.distanthorizons.core.network; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.network.messages.AckMessage; import com.seibel.distanthorizons.core.network.messages.CloseMessage; import com.seibel.distanthorizons.core.network.messages.CloseReasonMessage; import com.seibel.distanthorizons.core.network.messages.HelloMessage; @@ -16,6 +15,8 @@ import io.netty.channel.socket.nio.NioSocketChannel; import org.apache.logging.log4j.Logger; import java.net.InetSocketAddress; +import java.util.EnumSet; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; @@ -25,12 +26,22 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable private enum EConnectionState { + NOT_CONNECTING, OPEN, RECONNECT, RECONNECT_FORCE, CLOSE_WAIT, CLOSED } + private static final Set workingStates = EnumSet.of( + EConnectionState.OPEN, + EConnectionState.RECONNECT, + EConnectionState.RECONNECT_FORCE + ); + private static final Set closedStates = EnumSet.of( + EConnectionState.CLOSE_WAIT, + EConnectionState.CLOSED + ); private static final int FAILURE_RECONNECT_DELAY_SEC = 5; private static final int FAILURE_RECONNECT_ATTEMPTS = 3; @@ -38,6 +49,16 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable // TODO move to payload of some sort private final InetSocketAddress address; + /** Indicates whether the client is initialized and not started connecting yet. */ + public boolean isNotConnecting() { return this.connectionState == EConnectionState.NOT_CONNECTING; } + /** Indicates whether the client is working or in auto-recoverable state. */ + public boolean isWorking() { return workingStates.contains(this.connectionState); } + /** Indicates whether the client is closed(-ing) and should not be used. */ + public boolean isClosed() { return closedStates.contains(this.connectionState); } + private boolean isReady; + /** Indicates whether the connection is established and first message is sent. */ + public boolean isReady() { return isReady; } + private final EventLoopGroup workerGroup = new NioEventLoopGroup(); private final Bootstrap clientBootstrap = new Bootstrap() .group(this.workerGroup) @@ -45,7 +66,7 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable .option(ChannelOption.SO_KEEPALIVE, true) .handler(new NetworkChannelInitializer(new MessageHandler(this::handleMessage))); - private EConnectionState connectionState; + private EConnectionState connectionState = EConnectionState.NOT_CONNECTING; private Channel channel; private int reconnectAttempts = FAILURE_RECONNECT_ATTEMPTS; @@ -54,9 +75,7 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable public NetworkClient(String host, int port) { this.address = new InetSocketAddress(host, port); - this.registerHandlers(); - this.connect(); } private void registerHandlers() @@ -76,6 +95,12 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable } }); } + + public void startConnecting() + { + if (!isNotConnecting()) return; + this.connect(); + } private void connect() { @@ -94,12 +119,16 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable } channel.writeAndFlush(new HelloMessage()); + isReady = true; }); this.channel = connectFuture.channel(); this.channel.closeFuture().addListener((ChannelFuture channelFuture) -> { - this.completeAllFuturesExceptionally(channelFuture.cause()); + isReady = false; + this.completeAllFuturesExceptionally(channelFuture.cause() != null + ? channelFuture.cause() + : new ChannelException("Channel is closed.")); switch (this.connectionState) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index db9583423..ff7b5bdf8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -3,13 +3,16 @@ package com.seibel.distanthorizons.core.network; import com.google.common.collect.HashBasedTable; import com.google.common.collect.Table; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.network.messages.ExceptionMessage; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; -import com.seibel.distanthorizons.core.network.messages.AckMessage; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import io.netty.channel.ChannelHandlerContext; import org.apache.logging.log4j.Logger; -import java.util.*; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; @@ -19,9 +22,14 @@ public abstract class NetworkEventSource protected final Map, Set>> handlers = new HashMap<>(); private final Table> pendingFutures = HashBasedTable.create(); - protected final void handleMessage(NetworkMessage message, ChannelHandlerContext channelContext) + protected boolean hasHandler(Class handlerClass) + { + return this.handlers.containsKey(handlerClass); + } + + + protected void handleMessage(NetworkMessage message) { - message.setChannelContext(channelContext); boolean handled = false; Set> handlerList = this.handlers.get(message.getClass()); @@ -37,11 +45,15 @@ public abstract class NetworkEventSource if (message instanceof FutureTrackableNetworkMessage) { FutureTrackableNetworkMessage trackableMessage = (FutureTrackableNetworkMessage)message; - CompletableFuture future = pendingFutures.remove(channelContext, trackableMessage.futureId); + CompletableFuture future = pendingFutures.remove(message.getChannelContext(), trackableMessage.futureId); if (future != null) { handled = true; - future.complete(trackableMessage); + + if (message instanceof ExceptionMessage) + future.completeExceptionally(((ExceptionMessage) message).exception); + else + future.complete(trackableMessage); } } @@ -57,6 +69,13 @@ public abstract class NetworkEventSource .add((Consumer) handlerImplementation); } + protected void removeHandler(Class handlerClass, Consumer handlerImplementation) + { + this.handlers.computeIfAbsent(handlerClass, missingHandlerClass -> new HashSet<>()) + .remove(handlerImplementation); + } + + protected CompletableFuture sendRequest(ChannelHandlerContext ctx, FutureTrackableNetworkMessage msg) { CompletableFuture responseFuture = new CompletableFuture<>(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/RateLimitedException.java b/core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/RateLimitedException.java new file mode 100644 index 000000000..df9a1e4ed --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/RateLimitedException.java @@ -0,0 +1,9 @@ +package com.seibel.distanthorizons.core.network.exceptions; + +public class RateLimitedException extends Exception +{ + public RateLimitedException(String message) + { + super(message); + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ExceptionMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ExceptionMessage.java new file mode 100644 index 000000000..b27fba497 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ExceptionMessage.java @@ -0,0 +1,39 @@ +package com.seibel.distanthorizons.core.network.messages; + +import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; +import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.network.protocol.INetworkObject; +import io.netty.buffer.ByteBuf; + +import java.util.ArrayList; +import java.util.List; + +public class ExceptionMessage extends FutureTrackableNetworkMessage +{ + private static final List> exceptionMap = new ArrayList>() + {{ + // All exceptions here must include constructor: (String) + add(RateLimitedException.class); + }}; + + public Exception exception; + + public ExceptionMessage() { } + public ExceptionMessage(Exception exception) + { + this.exception = exception; + } + + @Override protected void encode0(ByteBuf out) + { + out.writeInt(exceptionMap.indexOf(exception.getClass())); + INetworkObject.encodeString(exception.getMessage(), out); + } + + @Override protected void decode0(ByteBuf in) throws Exception + { + int id = in.readInt(); + String message = INetworkObject.decodeString(in); + exception = exceptionMap.get(id).getDeclaredConstructor(String.class).newInstance(message); + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/RemotePlayerConfigMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/RemotePlayerConfigMessage.java index 21734c0a6..5b9616f3a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/RemotePlayerConfigMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/RemotePlayerConfigMessage.java @@ -2,8 +2,7 @@ package com.seibel.distanthorizons.core.network.messages; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; -import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; -import com.seibel.distanthorizons.core.network.objects.RemotePlayer; +import com.seibel.distanthorizons.core.multiplayer.RemotePlayer; import io.netty.buffer.ByteBuf; public class RemotePlayerConfigMessage extends FutureTrackableNetworkMessage diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/EMessageHandlerSide.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/EMessageHandlerSide.java deleted file mode 100644 index 6b8f6d760..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/EMessageHandlerSide.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.seibel.distanthorizons.core.network.protocol; - -/** - * CLIENT,
- * SERVER,
- */ -public enum EMessageHandlerSide -{ - CLIENT, - SERVER -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java index 185ca037d..6d68e1956 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java @@ -1,7 +1,7 @@ package com.seibel.distanthorizons.core.network.protocol; +import com.seibel.distanthorizons.core.network.messages.ExceptionMessage; import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; public abstract class FutureTrackableNetworkMessage extends NetworkMessage { @@ -14,6 +14,11 @@ public abstract class FutureTrackableNetworkMessage extends NetworkMessage getChannelContext().writeAndFlush(responseMessage); } + public void sendResponse(Exception e) + { + sendResponse(new ExceptionMessage(e)); + } + @Override public final void encode(ByteBuf out) { try diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java index 8b5a7c2bd..38aeec32d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java @@ -9,15 +9,16 @@ import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import java.util.function.BiConsumer; +import java.util.function.Consumer; @ChannelHandler.Sharable public class MessageHandler extends SimpleChannelInboundHandler { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - private final BiConsumer messageConsumer; + private final Consumer messageConsumer; - public MessageHandler(BiConsumer messageConsumer) + public MessageHandler(Consumer messageConsumer) { this.messageConsumer = messageConsumer; } @@ -26,7 +27,8 @@ public class MessageHandler extends SimpleChannelInboundHandler protected void channelRead0(ChannelHandlerContext channelContext, NetworkMessage message) { LOGGER.trace("Received message: " + message.getClass().getSimpleName()); - this.messageConsumer.accept(message, channelContext); + message.setChannelContext(channelContext); + this.messageConsumer.accept(message); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java index 48eae6fe6..0794a1267 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java @@ -27,6 +27,7 @@ public class MessageRegistry // Define your messages after this line this.registerMessage(AckMessage.class, AckMessage::new); + this.registerMessage(ExceptionMessage.class, ExceptionMessage::new); this.registerMessage(PlayerUUIDMessage.class, PlayerUUIDMessage::new); this.registerMessage(RemotePlayerConfigMessage.class, RemotePlayerConfigMessage::new); this.registerMessage(FullDataSourceRequestMessage.class, FullDataSourceRequestMessage::new); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java index 3bb43c030..053f96291 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java @@ -4,20 +4,17 @@ import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure; import com.seibel.distanthorizons.core.level.DhClientLevel; -import com.seibel.distanthorizons.core.level.DhClientServerLevel; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.network.NetworkClient; -import com.seibel.distanthorizons.core.network.messages.AckMessage; -import com.seibel.distanthorizons.core.network.messages.HelloMessage; -import com.seibel.distanthorizons.core.network.messages.PlayerUUIDMessage; -import com.seibel.distanthorizons.core.network.messages.RemotePlayerConfigMessage; -import com.seibel.distanthorizons.core.network.objects.RemotePlayer; +import com.seibel.distanthorizons.core.network.messages.*; +import com.seibel.distanthorizons.core.multiplayer.RemotePlayer; import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.util.objects.EventLoop; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; +import javax.annotation.CheckForNull; import java.io.File; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; @@ -30,6 +27,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld private final ConcurrentHashMap levels; public final ClientOnlySaveStructure saveStructure; + @CheckForNull private final NetworkClient networkClient; // TODO why does this executor have 2 threads? @@ -64,15 +62,16 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld } private void registerNetworkHandlers() { + assert this.networkClient != null; this.networkClient.registerHandler(HelloMessage.class, helloMessage -> { LOGGER.info("Connected to server: "+helloMessage.getChannelContext().channel().remoteAddress()); - this.networkClient.sendRequest(new PlayerUUIDMessage(MC_CLIENT.getPlayerUUID())) - .thenCompose(ack -> this.networkClient.sendRequest(new RemotePlayerConfigMessage(new RemotePlayer.Payload()))) - .thenAccept(responseConfigMsg -> { - // TODO do something with received config - }) + this.networkClient.sendRequest(new PlayerUUIDMessage(MC_CLIENT.getPlayerUUID())) + .thenCompose(ack -> this.networkClient.sendRequest(new RemotePlayerConfigMessage(new RemotePlayer.Payload() + {{ + fullDataRequestRateLimit = Config.Client.Advanced.Multiplayer.serverNetworkingRateLimit.get(); + }}))) .exceptionally(throwable -> { LOGGER.error("Error while fetching server's config", throwable); return null; @@ -143,7 +142,11 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld public void clientTick() { this.eventLoop.tick(); } - public void doWorldGen() { this.levels.values().forEach(DhClientLevel::doWorldGen); } + public void doWorldGen() { + if (networkClient != null && networkClient.isNotConnecting()) + networkClient.startConnecting(); + this.levels.values().forEach(DhClientLevel::doWorldGen); + } @Override public CompletableFuture saveAndFlush() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java index dd246a17c..16c6786db 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java @@ -2,6 +2,8 @@ package com.seibel.distanthorizons.core.world; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; +import com.seibel.distanthorizons.core.config.AppliedConfigState; +import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IIncompleteFullDataSource; @@ -10,8 +12,9 @@ import com.seibel.distanthorizons.core.file.structure.LocalSaveStructure; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.network.NetworkServer; +import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.messages.*; -import com.seibel.distanthorizons.core.network.objects.RemotePlayer; +import com.seibel.distanthorizons.core.multiplayer.RemotePlayer; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.LodUtil; @@ -37,7 +40,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld private final ConcurrentLinkedQueue worldGenLoopingQueue = new ConcurrentLinkedQueue<>(); private final ConcurrentMap incompleteDataSources = new ConcurrentHashMap<>(); - + private final AppliedConfigState rateLimitConfig = new AppliedConfigState<>(Config.Client.Advanced.Multiplayer.serverNetworkingRateLimit); public DhServerWorld() @@ -92,7 +95,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld this.networkServer.registerHandler(RemotePlayerConfigMessage.class, remotePlayerConfigMessage -> { - // TODO Take notice of and/or constrain received payload + remotePlayerConfigMessage.payload.fullDataRequestRateLimit = Math.min(rateLimitConfig.get(), remotePlayerConfigMessage.payload.fullDataRequestRateLimit); remotePlayerConfigMessage.sendResponse(remotePlayerConfigMessage); }); @@ -101,7 +104,15 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld { LOGGER.info("FullDataSourceRequestMessage received at pos ({}, {}) with detail level {}", msg.dhSectionPos.sectionX, msg.dhSectionPos.sectionZ, msg.dhSectionPos.sectionDetailLevel); - DhServerLevel level = this.getLevel(playersByConnection.get(msg.getChannelContext()).serverPlayer.getLevel()); + RemotePlayer remotePlayer = playersByConnection.get(msg.getChannelContext()); + if (remotePlayer.pendingFullDataRequests.incrementAndGet() > rateLimitConfig.get()) + { + remotePlayer.pendingFullDataRequests.decrementAndGet(); + msg.sendResponse(new RateLimitedException("Max concurrent requests: "+rateLimitConfig.get())); + return; + } + + DhServerLevel level = this.getLevel(remotePlayer.serverPlayer.getLevel()); GeneratedFullDataFileHandler handler = level.serverside.dataFileHandler; while (true) @@ -118,7 +129,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld { entry.requestMessages.add(msg); entry.requestCollectionSemaphore.release(); - return; + break; } } }); @@ -188,6 +199,15 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld public void serverTick() { this.levels.values().forEach(DhServerLevel::serverTick); + if (rateLimitConfig.pollNewValue()) + { + for (RemotePlayer remotePlayer : playersByConnection.values()) + { + remotePlayer.payload.fullDataRequestRateLimit = rateLimitConfig.get(); + remotePlayer.channelContext.writeAndFlush(new RemotePlayerConfigMessage(remotePlayer.payload)); + } + } + for (Iterator it = incompleteDataSources.values().iterator(); it.hasNext(); ) { IncompleteDataSourceEntry entry = it.next(); @@ -211,6 +231,8 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld { RemotePlayer remotePlayer = playersByConnection.get(msg.getChannelContext()); if (remotePlayer == null) continue; + remotePlayer.pendingFullDataRequests.decrementAndGet(); + DhServerLevel level = this.getLevel(remotePlayer.serverPlayer.getLevel()); msg.sendResponse(new FullDataSourceResponseMessage(completeSource, level)); } diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index 09091d2bc..762c0e159 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -305,12 +305,16 @@ "distanthorizons.config.client.advanced.multiplayer.enableMultiverseNetworking.@tooltip": "If true Distant Horizons will attempt to communicate with the connected \nserver in order to improve multiverse support.", "distanthorizons.config.client.advanced.multiplayer.enableServerNetworking": - "§4Unimplemented, Dev Use Only§r - Server Support", + "§4Partially implemented§r - Server Support", "distanthorizons.config.client.advanced.multiplayer.enableServerNetworking.@tooltip": - "§6Attention:§r this is only for developers and hasn't been implemented. \n\nIf true Distant Horizons will attempt to communicate with the connected \nserver in order to load LODs outside your vanilla render distance. \n\nNote: This requires DH to be installed on the server in order to function.", - - - + "§6Attention:§r this feature is not fully implemented. \n\nIf true Distant Horizons will attempt to communicate with the connected \nserver in order to load LODs outside your vanilla render distance. \n\nNote: This requires DH to be installed on the server in order to function.", + "distanthorizons.config.client.advanced.multiplayer.serverNetworkingRateLimit": + "Server Support - Request Rate Limit", + "distanthorizons.config.client.advanced.multiplayer.serverNetworkingRateLimit.@tooltip": + "Limits the amount of sent/processed LOD requests concurrently. \n\nNote: Server can set its own rate limit.", + + + "distanthorizons.config.client.advanced.multiThreading": "Threading", From 2251cd4c25984385a0bf1e00ddffbd91b2d842b7 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 1 Aug 2023 17:04:40 +0500 Subject: [PATCH 018/205] Attempt to fix dimension switching --- .../core/api/internal/ServerApi.java | 9 + .../WorldRemoteGenerationQueue.java | 17 +- .../core/level/DhClientLevel.java | 4 +- .../core/level/DhServerLevel.java | 143 +++++++++++++- .../RemotePlayerConnectionHandler.java | 89 +++++++++ .../core/network/ChildNetworkEventSource.java | 4 + .../core/network/NetworkClient.java | 16 +- .../core/world/DhClientWorld.java | 4 +- .../core/world/DhServerWorld.java | 176 +++--------------- 9 files changed, 277 insertions(+), 185 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java index 18c23a3e9..b88fd43b5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java @@ -171,5 +171,14 @@ public class ServerApi ((DhServerWorld) serverWorld).removePlayer(player); } } + public void serverPlayerLevelChangeEvent(IServerPlayerWrapper player, IServerLevelWrapper origin, IServerLevelWrapper dest) + { + IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); + if (serverWorld instanceof DhServerWorld) // TODO add support for DhClientServerWorld's (lan worlds) as well + { + LOGGER.debug("Player changed level: " + player.getUUID()); + ((DhServerWorld) serverWorld).changePlayerLevel(player, origin, dest); + } + } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 2e0e9e527..35146b1b1 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -24,7 +24,7 @@ import io.netty.channel.ChannelException; import org.apache.logging.log4j.Logger; import java.util.ArrayList; -import java.util.Map; +import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -84,17 +84,19 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue private void sendNewRequest(DhBlockPos2D targetPos) { - DhSectionPos sectionPos = waitingTasks.keySet().stream().reduce(null, (a, b) + if (!pendingTasksSemaphore.tryAcquire()) + return; + + DhSectionPos sectionPos = Objects.requireNonNull(waitingTasks.keySet().stream().reduce(null, (a, b) -> a != null && a.getCenter().getCenterBlockPos().distSquared(targetPos) < b.getCenter().getCenterBlockPos().distSquared(targetPos) - ? a : b); - + ? a : b)); WorldGenQueueEntry entry = waitingTasks.remove(sectionPos); - pendingTasksSemaphore.acquireUninterruptibly(); eventSource.parent.sendRequest(new FullDataSourceRequestMessage(sectionPos)) - .handle((response, throwable) -> { + .handle((response, throwable) -> + { pendingTasksSemaphore.release(); finishedRequests.incrementAndGet(); @@ -132,7 +134,8 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue finishedRequests.decrementAndGet(); waitingTasks.put(sectionPos, entry); } - catch (ChannelException e) { + catch (ChannelException e) + { finishedRequests.decrementAndGet(); waitingTasks.put(sectionPos, entry); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 282869454..634496e20 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -88,8 +88,8 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel public void doWorldGen() { worldGeneratorEnabledConfig.pollNewValue(); - boolean isClientWorking = networkClient != null && networkClient.isWorking(); - boolean shouldDoWorldGen = worldGeneratorEnabledConfig.get() && isClientWorking && clientside.isRendering(); + boolean isClientUsable = networkClient != null && !networkClient.isClosed(); + boolean shouldDoWorldGen = worldGeneratorEnabledConfig.get() && isClientUsable && clientside.isRendering(); boolean isWorldGenRunning = worldGenModule.isWorldGenRunning(); if (shouldDoWorldGen && !isWorldGenRunning) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index e4be9bc7b..0bb9f61ab 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -1,26 +1,53 @@ package com.seibel.distanthorizons.core.level; +import com.seibel.distanthorizons.core.config.AppliedConfigState; +import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IIncompleteFullDataSource; +import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataFileHandler; import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; +import com.seibel.distanthorizons.core.multiplayer.RemotePlayer; +import com.seibel.distanthorizons.core.multiplayer.RemotePlayerConnectionHandler; +import com.seibel.distanthorizons.core.network.ChildNetworkEventSource; +import com.seibel.distanthorizons.core.network.NetworkServer; +import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; +import com.seibel.distanthorizons.core.network.messages.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.FullDataSourceResponseMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhLodPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.world.DhServerWorld; +import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; +import com.seibel.distanthorizons.coreapi.util.math.Vec3d; import org.apache.logging.log4j.Logger; -import java.util.concurrent.CompletableFuture; +import javax.annotation.CheckForNull; +import java.util.Iterator; +import java.util.Set; +import java.util.concurrent.*; public class DhServerLevel extends DhLevel implements IDhServerLevel { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); public final ServerLevelModule serverside; private final IServerLevelWrapper serverLevelWrapper; - - public DhServerLevel(AbstractSaveStructure saveStructure, IServerLevelWrapper serverLevelWrapper) + + private final RemotePlayerConnectionHandler remotePlayerConnectionHandler; + private final ChildNetworkEventSource eventSource; + + private final ConcurrentLinkedQueue worldGenLoopingQueue = new ConcurrentLinkedQueue<>(); + private final ConcurrentMap incompleteDataSources = new ConcurrentHashMap<>(); + private final AppliedConfigState rateLimitConfig = new AppliedConfigState<>(Config.Client.Advanced.Multiplayer.serverNetworkingRateLimit); + + + public DhServerLevel(AbstractSaveStructure saveStructure, IServerLevelWrapper serverLevelWrapper, RemotePlayerConnectionHandler remotePlayerConnectionHandler) { if (saveStructure.getFullDataFolder(serverLevelWrapper).mkdirs()) { @@ -29,11 +56,97 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel this.serverLevelWrapper = serverLevelWrapper; serverside = new ServerLevelModule(this, serverLevelWrapper, saveStructure); LOGGER.info("Started DHLevel for {} with saves at {}", serverLevelWrapper, saveStructure); + + this.remotePlayerConnectionHandler = remotePlayerConnectionHandler; + this.eventSource = new ChildNetworkEventSource<>(remotePlayerConnectionHandler.eventSource); + this.registerNetworkHandlers(); + } + + private void registerNetworkHandlers() + { + this.eventSource.registerHandler(FullDataSourceRequestMessage.class, msg -> + { + RemotePlayer remotePlayer = remotePlayerConnectionHandler.getConnectedPlayer(msg); + if (remotePlayer.serverPlayer.getLevel() != this.serverLevelWrapper) + return; + + LOGGER.info("FullDataSourceRequestMessage received at pos ({}, {}) with detail level {}", msg.dhSectionPos.sectionX, msg.dhSectionPos.sectionZ, msg.dhSectionPos.sectionDetailLevel); + + if (remotePlayer.pendingFullDataRequests.incrementAndGet() > rateLimitConfig.get()) + { + remotePlayer.pendingFullDataRequests.decrementAndGet(); + msg.sendResponse(new RateLimitedException("Max concurrent requests: "+rateLimitConfig.get())); + return; + } + + while (true) + { + IncompleteDataSourceEntry entry = incompleteDataSources.computeIfAbsent(msg.dhSectionPos, pos -> { + IncompleteDataSourceEntry newEntry = new IncompleteDataSourceEntry(); + serverside.dataFileHandler.read(msg.dhSectionPos).thenAccept(fullDataSource -> { + newEntry.fullDataSource = fullDataSource; + }); + return newEntry; + }); + // If this fails, current entry is being drained and need create another one + if (entry.requestCollectionSemaphore.tryAcquire()) + { + entry.requestMessages.add(msg); + entry.requestCollectionSemaphore.release(); + break; + } + } + }); + } + + public void addPlayer(IServerPlayerWrapper serverPlayer) + { + synchronized (worldGenLoopingQueue) + { + this.worldGenLoopingQueue.add(serverPlayer); + } + } + + public void removePlayer(IServerPlayerWrapper serverPlayer) + { + synchronized (worldGenLoopingQueue) + { + this.worldGenLoopingQueue.remove(serverPlayer); + } } public void serverTick() { chunkToLodBuilder.tick(); + + for (Iterator it = incompleteDataSources.values().iterator(); it.hasNext(); ) + { + IncompleteDataSourceEntry entry = it.next(); + if (entry.fullDataSource == null) continue; + + if (entry.fullDataSource instanceof IIncompleteFullDataSource) + { + IIncompleteFullDataSource incompleteSource = (IIncompleteFullDataSource) entry.fullDataSource; + if (!incompleteSource.hasBeenPromoted()) continue; + entry.fullDataSource = incompleteSource.tryPromotingToCompleteDataSource(); + } + + LodUtil.assertTrue(entry.fullDataSource instanceof CompleteFullDataSource, "Invalid full data source"); + + it.remove(); + // This semaphore is intentionally acquired forever + entry.requestCollectionSemaphore.acquireUninterruptibly(Short.MAX_VALUE); + + CompleteFullDataSource completeSource = (CompleteFullDataSource) entry.fullDataSource; + for (FullDataSourceRequestMessage msg : entry.requestMessages) + { + RemotePlayer remotePlayer = remotePlayerConnectionHandler.getConnectedPlayer(msg); + if (remotePlayer == null) continue; + remotePlayer.pendingFullDataRequests.decrementAndGet(); + + msg.sendResponse(new FullDataSourceResponseMessage(completeSource, this)); + } + } } @Override @@ -77,14 +190,20 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel // stop world gen serverside.worldGenModule.stopWorldGen(serverside.dataFileHandler); } - } - - public void doWorldGen(DhBlockPos2D pos) { - this.doWorldGen(); if (serverside.worldGenModule.isWorldGenRunning()) { - serverside.worldGenModule.worldGenTick(pos); + IServerPlayerWrapper firstPlayer; + synchronized (worldGenLoopingQueue) + { + firstPlayer = this.worldGenLoopingQueue.poll(); + if (firstPlayer == null) + return; + this.worldGenLoopingQueue.add(firstPlayer); + } + + Vec3d position = firstPlayer.getPosition(); + serverside.worldGenModule.worldGenTick(new DhBlockPos2D((int) position.x, (int) position.z)); } } @@ -106,4 +225,12 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel public void onWorldGenTaskComplete(DhSectionPos pos) { //TODO: Send packet to client } + + private static class IncompleteDataSourceEntry + { + @CheckForNull + public IFullDataSource fullDataSource; + public final Set requestMessages = ConcurrentHashMap.newKeySet(); + public final Semaphore requestCollectionSemaphore = new Semaphore(Short.MAX_VALUE, true); + } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java new file mode 100644 index 000000000..ae626bb38 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java @@ -0,0 +1,89 @@ +package com.seibel.distanthorizons.core.multiplayer; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.seibel.distanthorizons.core.network.ChildNetworkEventSource; +import com.seibel.distanthorizons.core.network.NetworkServer; +import com.seibel.distanthorizons.core.network.messages.AckMessage; +import com.seibel.distanthorizons.core.network.messages.CloseMessage; +import com.seibel.distanthorizons.core.network.messages.PlayerUUIDMessage; +import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; +import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; +import io.netty.channel.ChannelHandlerContext; + +import java.util.HashMap; +import java.util.UUID; + +public class RemotePlayerConnectionHandler +{ + public final ChildNetworkEventSource eventSource; + private final HashMap playersByUUID = new HashMap<>(); + private final BiMap playersByConnection = HashBiMap.create(); + + public RemotePlayerConnectionHandler(NetworkServer networkServer) + { + this.eventSource = new ChildNetworkEventSource<>(networkServer); + this.registerNetworkHandlers(); + } + + private void registerNetworkHandlers() + { + this.eventSource.registerHandler(PlayerUUIDMessage.class, playerUUIDMessage -> + { + ChannelHandlerContext channelContext = playerUUIDMessage.getChannelContext(); + RemotePlayer dhPlayer = this.playersByUUID.get(playerUUIDMessage.playerUUID); + + if (dhPlayer == null) + { + this.eventSource.parent.disconnectClient(channelContext, "Player is not logged in."); + return; + } + + if (dhPlayer.channelContext != null) + { + this.eventSource.parent.disconnectClient(channelContext, "Another connection is already in use."); + return; + } + + dhPlayer.channelContext = channelContext; + this.playersByConnection.put(channelContext, dhPlayer); + + playerUUIDMessage.sendResponse(new AckMessage()); + }); + + this.eventSource.registerHandler(CloseMessage.class, closeMessage -> + { + RemotePlayer dhPlayer = this.playersByConnection.remove(closeMessage.getChannelContext()); + if (dhPlayer != null) + { + dhPlayer.channelContext = null; + } + }); + } + + public Iterable getConnectedPlayers() + { + return playersByConnection.values(); + } + + public RemotePlayer getConnectedPlayer(NetworkMessage msg) + { + return playersByConnection.get(msg.getChannelContext()); + } + + public void mcPlayerJoined(IServerPlayerWrapper serverPlayer) + { + this.playersByUUID.put(serverPlayer.getUUID(), new RemotePlayer(serverPlayer)); + } + + public void mcPlayerLeft(IServerPlayerWrapper serverPlayer) + { + RemotePlayer dhPlayer = this.playersByUUID.remove(serverPlayer.getUUID()); + ChannelHandlerContext channelContext = this.playersByConnection.inverse().remove(dhPlayer); + if (channelContext != null) + { + this.eventSource.parent.disconnectClient(channelContext, "You are being disconnected."); + } + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/ChildNetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/ChildNetworkEventSource.java index c8535878f..e7a1e4e39 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/ChildNetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/ChildNetworkEventSource.java @@ -14,6 +14,10 @@ public final class ChildNetworkEventSource e { this.parent = parent; } + public ChildNetworkEventSource(ChildNetworkEventSource child) + { + this.parent = child.parent; + } @Override public void registerHandler(Class handlerClass, Consumer handlerImplementation) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index 68732d0f7..f9ca5c9e4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -7,7 +7,6 @@ import com.seibel.distanthorizons.core.network.messages.HelloMessage; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.MessageHandler; import com.seibel.distanthorizons.core.network.protocol.NetworkChannelInitializer; -import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; @@ -26,18 +25,13 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable private enum EConnectionState { - NOT_CONNECTING, + INITIAL, OPEN, RECONNECT, RECONNECT_FORCE, CLOSE_WAIT, CLOSED } - private static final Set workingStates = EnumSet.of( - EConnectionState.OPEN, - EConnectionState.RECONNECT, - EConnectionState.RECONNECT_FORCE - ); private static final Set closedStates = EnumSet.of( EConnectionState.CLOSE_WAIT, EConnectionState.CLOSED @@ -50,9 +44,7 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable private final InetSocketAddress address; /** Indicates whether the client is initialized and not started connecting yet. */ - public boolean isNotConnecting() { return this.connectionState == EConnectionState.NOT_CONNECTING; } - /** Indicates whether the client is working or in auto-recoverable state. */ - public boolean isWorking() { return workingStates.contains(this.connectionState); } + public boolean isInitialState() { return this.connectionState == EConnectionState.INITIAL; } /** Indicates whether the client is closed(-ing) and should not be used. */ public boolean isClosed() { return closedStates.contains(this.connectionState); } private boolean isReady; @@ -66,7 +58,7 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable .option(ChannelOption.SO_KEEPALIVE, true) .handler(new NetworkChannelInitializer(new MessageHandler(this::handleMessage))); - private EConnectionState connectionState = EConnectionState.NOT_CONNECTING; + private EConnectionState connectionState = EConnectionState.INITIAL; private Channel channel; private int reconnectAttempts = FAILURE_RECONNECT_ATTEMPTS; @@ -98,7 +90,7 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable public void startConnecting() { - if (!isNotConnecting()) return; + if (!isInitialState()) return; this.connect(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java index 053f96291..2885b0e1f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java @@ -143,9 +143,9 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld public void clientTick() { this.eventLoop.tick(); } public void doWorldGen() { - if (networkClient != null && networkClient.isNotConnecting()) - networkClient.startConnecting(); this.levels.values().forEach(DhClientLevel::doWorldGen); + if (networkClient != null && networkClient.isInitialState()) + networkClient.startConnecting(); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java index 16c6786db..4eb0fa747 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java @@ -1,33 +1,22 @@ package com.seibel.distanthorizons.core.world; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; import com.seibel.distanthorizons.core.config.AppliedConfigState; import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IIncompleteFullDataSource; -import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataFileHandler; import com.seibel.distanthorizons.core.file.structure.LocalSaveStructure; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.level.IDhLevel; -import com.seibel.distanthorizons.core.network.NetworkServer; -import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; -import com.seibel.distanthorizons.core.network.messages.*; import com.seibel.distanthorizons.core.multiplayer.RemotePlayer; -import com.seibel.distanthorizons.core.pos.DhBlockPos2D; -import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.multiplayer.RemotePlayerConnectionHandler; +import com.seibel.distanthorizons.core.network.NetworkServer; +import com.seibel.distanthorizons.core.network.messages.RemotePlayerConfigMessage; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; -import com.seibel.distanthorizons.coreapi.util.math.Vec3d; -import io.netty.channel.ChannelHandlerContext; -import javax.annotation.CheckForNull; import java.io.File; -import java.util.*; -import java.util.concurrent.*; +import java.util.HashMap; +import java.util.concurrent.CompletableFuture; public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld { @@ -35,14 +24,11 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld public final LocalSaveStructure saveStructure; private final NetworkServer networkServer; - private final HashMap playersByUUID; - private final BiMap playersByConnection; - - private final ConcurrentLinkedQueue worldGenLoopingQueue = new ConcurrentLinkedQueue<>(); - private final ConcurrentMap incompleteDataSources = new ConcurrentHashMap<>(); + private final RemotePlayerConnectionHandler remotePlayerConnectionHandler; private final AppliedConfigState rateLimitConfig = new AppliedConfigState<>(Config.Client.Advanced.Multiplayer.serverNetworkingRateLimit); - - + + + public DhServerWorld() { super(EWorldEnvironment.Server_Only); @@ -52,103 +38,35 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld // TODO move to global payload once server specific configs are implemented this.networkServer = new NetworkServer(25049); - this.playersByUUID = new HashMap<>(); - this.playersByConnection = HashBiMap.create(); this.registerNetworkHandlers(); + this.remotePlayerConnectionHandler = new RemotePlayerConnectionHandler(networkServer); LOGGER.info("Started "+DhServerWorld.class.getSimpleName()+" of type "+this.environment); } private void registerNetworkHandlers() { - this.networkServer.registerHandler(CloseMessage.class, closeMessage -> - { - RemotePlayer dhPlayer = this.playersByConnection.remove(closeMessage.getChannelContext()); - if (dhPlayer != null) - { - dhPlayer.channelContext = null; - } - }); - - this.networkServer.registerHandler(PlayerUUIDMessage.class, playerUUIDMessage -> - { - ChannelHandlerContext channelContext = playerUUIDMessage.getChannelContext(); - RemotePlayer dhPlayer = this.playersByUUID.get(playerUUIDMessage.playerUUID); - - if (dhPlayer == null) - { - this.networkServer.disconnectClient(channelContext, "Player is not logged in."); - return; - } - - if (dhPlayer.channelContext != null) - { - this.networkServer.disconnectClient(channelContext, "Another connection is already in use."); - return; - } - - dhPlayer.channelContext = channelContext; - this.playersByConnection.put(channelContext, dhPlayer); - - playerUUIDMessage.sendResponse(new AckMessage()); - }); - this.networkServer.registerHandler(RemotePlayerConfigMessage.class, remotePlayerConfigMessage -> { remotePlayerConfigMessage.payload.fullDataRequestRateLimit = Math.min(rateLimitConfig.get(), remotePlayerConfigMessage.payload.fullDataRequestRateLimit); remotePlayerConfigMessage.sendResponse(remotePlayerConfigMessage); }); - - // This should be at DhServerLevel I guess - this.networkServer.registerHandler(FullDataSourceRequestMessage.class, msg -> - { - LOGGER.info("FullDataSourceRequestMessage received at pos ({}, {}) with detail level {}", msg.dhSectionPos.sectionX, msg.dhSectionPos.sectionZ, msg.dhSectionPos.sectionDetailLevel); - - RemotePlayer remotePlayer = playersByConnection.get(msg.getChannelContext()); - if (remotePlayer.pendingFullDataRequests.incrementAndGet() > rateLimitConfig.get()) - { - remotePlayer.pendingFullDataRequests.decrementAndGet(); - msg.sendResponse(new RateLimitedException("Max concurrent requests: "+rateLimitConfig.get())); - return; - } - - DhServerLevel level = this.getLevel(remotePlayer.serverPlayer.getLevel()); - GeneratedFullDataFileHandler handler = level.serverside.dataFileHandler; - - while (true) - { - IncompleteDataSourceEntry entry = incompleteDataSources.computeIfAbsent(msg.dhSectionPos, pos -> { - IncompleteDataSourceEntry newEntry = new IncompleteDataSourceEntry(); - handler.read(msg.dhSectionPos).thenAccept(fullDataSource -> { - newEntry.fullDataSource = fullDataSource; - }); - return newEntry; - }); - // If this fails, current entry is being drained and need create another one - if (entry.requestCollectionSemaphore.tryAcquire()) - { - entry.requestMessages.add(msg); - entry.requestCollectionSemaphore.release(); - break; - } - } - }); } public void addPlayer(IServerPlayerWrapper serverPlayer) { - this.playersByUUID.put(serverPlayer.getUUID(), new RemotePlayer(serverPlayer)); - this.worldGenLoopingQueue.add(serverPlayer); + this.remotePlayerConnectionHandler.mcPlayerJoined(serverPlayer); + this.getLevel(serverPlayer.getLevel()).addPlayer(serverPlayer); } public void removePlayer(IServerPlayerWrapper serverPlayer) { - this.worldGenLoopingQueue.remove(serverPlayer); - RemotePlayer dhPlayer = this.playersByUUID.remove(serverPlayer.getUUID()); - ChannelHandlerContext channelContext = this.playersByConnection.inverse().remove(dhPlayer); - if (channelContext != null) - { - this.networkServer.disconnectClient(channelContext, "You are being disconnected."); - } + this.getLevel(serverPlayer.getLevel()).removePlayer(serverPlayer); + this.remotePlayerConnectionHandler.mcPlayerLeft(serverPlayer); + } + public void changePlayerLevel(IServerPlayerWrapper player, IServerLevelWrapper origin, IServerLevelWrapper dest) + { + this.getLevel(origin).removePlayer(player); + this.getLevel(dest).addPlayer(player); } @Override @@ -163,7 +81,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld { File levelFile = this.saveStructure.getLevelFolder(wrapper); LodUtil.assertTrue(levelFile != null); - return new DhServerLevel(this.saveStructure, serverLevelWrapper); + return new DhServerLevel(this.saveStructure, serverLevelWrapper, this.remotePlayerConnectionHandler); }); } @@ -201,58 +119,16 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld if (rateLimitConfig.pollNewValue()) { - for (RemotePlayer remotePlayer : playersByConnection.values()) + for (RemotePlayer remotePlayer : this.remotePlayerConnectionHandler.getConnectedPlayers()) { remotePlayer.payload.fullDataRequestRateLimit = rateLimitConfig.get(); remotePlayer.channelContext.writeAndFlush(new RemotePlayerConfigMessage(remotePlayer.payload)); } } - - for (Iterator it = incompleteDataSources.values().iterator(); it.hasNext(); ) - { - IncompleteDataSourceEntry entry = it.next(); - if (entry.fullDataSource == null) continue; - - if (entry.fullDataSource instanceof IIncompleteFullDataSource) - { - IIncompleteFullDataSource incompleteSource = (IIncompleteFullDataSource) entry.fullDataSource; - if (!incompleteSource.hasBeenPromoted()) continue; - entry.fullDataSource = incompleteSource.tryPromotingToCompleteDataSource(); - } - - LodUtil.assertTrue(entry.fullDataSource instanceof CompleteFullDataSource, "Invalid full data source"); - - it.remove(); - // This semaphore is intentionally acquired forever - entry.requestCollectionSemaphore.acquireUninterruptibly(Short.MAX_VALUE); - - CompleteFullDataSource completeSource = (CompleteFullDataSource) entry.fullDataSource; - for (FullDataSourceRequestMessage msg : entry.requestMessages) - { - RemotePlayer remotePlayer = playersByConnection.get(msg.getChannelContext()); - if (remotePlayer == null) continue; - remotePlayer.pendingFullDataRequests.decrementAndGet(); - - DhServerLevel level = this.getLevel(remotePlayer.serverPlayer.getLevel()); - msg.sendResponse(new FullDataSourceResponseMessage(completeSource, level)); - } - } } public void doWorldGen() { - this.levels.values().forEach(level -> { - // TODO Deal with dimensions and dimension switches - - IServerPlayerWrapper firstPlayer = this.worldGenLoopingQueue.poll(); - if (firstPlayer == null) { - level.doWorldGen(); - return; - } - this.worldGenLoopingQueue.add(firstPlayer); - - Vec3d position = firstPlayer.getPosition(); - level.doWorldGen(new DhBlockPos2D((int) position.x, (int) position.z)); - }); + this.levels.values().forEach(DhServerLevel::doWorldGen); } @Override @@ -275,13 +151,5 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld this.levels.clear(); LOGGER.info("Closed DhWorld of type "+this.environment); } - - private static class IncompleteDataSourceEntry - { - @CheckForNull - public IFullDataSource fullDataSource; - public final Set requestMessages = ConcurrentHashMap.newKeySet(); - public final Semaphore requestCollectionSemaphore = new Semaphore(Short.MAX_VALUE, true); - } } From 1788c18d59e340cd73da1e0ae364899495c92b0d Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 1 Aug 2023 22:31:16 +0500 Subject: [PATCH 019/205] Everything I've done so far (not working/finished) --- .../WorldRemoteGenerationQueue.java | 60 ++++++++++--------- .../core/level/DhClientLevel.java | 8 +-- .../core/level/DhServerLevel.java | 16 ++++- .../core/multiplayer/ClientNetworkState.java | 32 ++++++++++ .../RemotePlayerConnectionHandler.java | 5 ++ .../core/network/NetworkClient.java | 8 +-- .../core/network/NetworkEventSource.java | 19 +++++- .../core/network/NetworkServer.java | 5 +- .../core/network/messages/CancelMessage.java | 19 ++++++ .../FutureTrackableNetworkMessage.java | 4 +- .../network/protocol/MessageRegistry.java | 1 + .../core/world/DhServerWorld.java | 5 ++ 12 files changed, 136 insertions(+), 46 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/CancelMessage.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 35146b1b1..338a0e707 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -1,6 +1,5 @@ package com.seibel.distanthorizons.core.generation; -import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker; @@ -8,12 +7,12 @@ import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult; import com.seibel.distanthorizons.core.level.IDhClientLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.f3.F3Screen; +import com.seibel.distanthorizons.core.multiplayer.ClientNetworkState; import com.seibel.distanthorizons.core.network.ChildNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkClient; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.messages.FullDataSourceRequestMessage; import com.seibel.distanthorizons.core.network.messages.FullDataSourceResponseMessage; -import com.seibel.distanthorizons.core.network.messages.RemotePlayerConfigMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhLodPos; @@ -25,6 +24,7 @@ import org.apache.logging.log4j.Logger; import java.util.ArrayList; import java.util.Objects; +import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -36,27 +36,25 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - private final ChildNetworkEventSource eventSource; + private final ClientNetworkState networkState; private final IDhClientLevel level; private final ConcurrentMap waitingTasks = new ConcurrentHashMap<>(); private final Semaphore pendingTasksSemaphore = new Semaphore(Short.MAX_VALUE); private int pendingTasks() { return Short.MAX_VALUE - pendingTasksSemaphore.availablePermits(); } - private int maxConcurrentRequests = 0; + private final Set> pendingRequests = ConcurrentHashMap.newKeySet(); + + private volatile CompletableFuture generatorClosingFuture = null; private final F3Screen.NestedMessage f3Message = new F3Screen.NestedMessage(this::f3Log); private final AtomicInteger finishedRequests = new AtomicInteger(); private final AtomicInteger totalRequests = new AtomicInteger(); private final AtomicInteger failedRequests = new AtomicInteger(); - public WorldRemoteGenerationQueue(NetworkClient networkClient, IDhClientLevel level) + public WorldRemoteGenerationQueue(ClientNetworkState networkState, IDhClientLevel level) { - this.eventSource = new ChildNetworkEventSource<>(networkClient); + this.networkState = networkState; this.level = level; - - eventSource.registerHandler(RemotePlayerConfigMessage.class, msg -> { - maxConcurrentRequests = Math.min(msg.payload.fullDataRequestRateLimit, Config.Client.Advanced.Multiplayer.serverNetworkingRateLimit.get()); - }); } @Override public byte largestDataDetail() @@ -78,15 +76,18 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue @Override public void runCurrentGenTasksUntilBusy(DhBlockPos2D targetPos) { - while (eventSource.parent.isReady() && pendingTasks() < maxConcurrentRequests && !waitingTasks.isEmpty()) + while (generatorClosingFuture == null + && networkState.eventSource.parent.isReady() + && !waitingTasks.isEmpty() + && pendingTasks() < this.networkState.config.fullDataRequestRateLimit + && pendingTasksSemaphore.tryAcquire()) + { sendNewRequest(targetPos); + } } private void sendNewRequest(DhBlockPos2D targetPos) { - if (!pendingTasksSemaphore.tryAcquire()) - return; - DhSectionPos sectionPos = Objects.requireNonNull(waitingTasks.keySet().stream().reduce(null, (a, b) -> a != null && a.getCenter().getCenterBlockPos().distSquared(targetPos) @@ -94,9 +95,11 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue ? a : b)); WorldGenQueueEntry entry = waitingTasks.remove(sectionPos); - eventSource.parent.sendRequest(new FullDataSourceRequestMessage(sectionPos)) - .handle((response, throwable) -> + CompletableFuture request = this.networkState.eventSource.parent.sendRequest(new FullDataSourceRequestMessage(sectionPos)); + pendingRequests.add(request); + request.handle((response, throwable) -> { + pendingRequests.remove(request); pendingTasksSemaphore.release(); finishedRequests.incrementAndGet(); @@ -154,26 +157,29 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue ArrayList lines = new ArrayList<>(); lines.add("World Remote Generation Queue ["+level.getClientLevelWrapper().getDimensionType().getDimensionName()+"]"); lines.add(" Requests: "+this.finishedRequests+" / "+this.totalRequests +" (failed: "+ this.failedRequests+")"); - lines.add(" Pending: "+this.pendingTasks()+" / "+this.maxConcurrentRequests); + lines.add(" Pending: "+this.pendingTasks()+" / "+this.networkState.config.fullDataRequestRateLimit); return lines.toArray(new String[0]); } @Override public CompletableFuture startClosing(boolean cancelCurrentGeneration, boolean alsoInterruptRunning) { - pendingTasksSemaphore.acquireUninterruptibly(Short.MAX_VALUE); - - if (cancelCurrentGeneration) - { - for (WorldGenQueueEntry entry : this.waitingTasks.values()) - entry.future.cancel(alsoInterruptRunning); - } - - return CompletableFuture.completedFuture(null); + return this.generatorClosingFuture = CompletableFuture.runAsync(() -> { + while (!pendingTasksSemaphore.tryAcquire(Short.MAX_VALUE)) + { + for (CompletableFuture request : pendingRequests) + request.cancel(false); + } + + if (cancelCurrentGeneration) + { + for (WorldGenQueueEntry entry : this.waitingTasks.values()) + entry.future.cancel(alsoInterruptRunning); + } + }); } @Override public void close() { - eventSource.close(); f3Message.close(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 634496e20..c4187e799 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -1,7 +1,5 @@ package com.seibel.distanthorizons.core.level; -import com.seibel.distanthorizons.core.config.AppliedConfigState; -import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; @@ -50,8 +48,6 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel @CheckForNull private final NetworkClient networkClient; public final WorldGenModule worldGenModule; - // TODO maybe use some other value? - public final AppliedConfigState worldGeneratorEnabledConfig; @@ -67,7 +63,6 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel this.networkClient = networkClient; this.worldGenModule = new WorldGenModule(dataFileHandler, this); - this.worldGeneratorEnabledConfig = new AppliedConfigState<>(Config.Client.Advanced.WorldGenerator.enableDistantGeneration); clientside = new ClientLevelModule(this); clientside.startRenderer(); @@ -87,9 +82,8 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel public void doWorldGen() { - worldGeneratorEnabledConfig.pollNewValue(); boolean isClientUsable = networkClient != null && !networkClient.isClosed(); - boolean shouldDoWorldGen = worldGeneratorEnabledConfig.get() && isClientUsable && clientside.isRendering(); + boolean shouldDoWorldGen = isClientUsable && clientside.isRendering(); boolean isWorldGenRunning = worldGenModule.isWorldGenRunning(); if (shouldDoWorldGen && !isWorldGenRunning) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 0bb9f61ab..cfe999295 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -14,6 +14,7 @@ import com.seibel.distanthorizons.core.multiplayer.RemotePlayerConnectionHandler import com.seibel.distanthorizons.core.network.ChildNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkServer; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; +import com.seibel.distanthorizons.core.network.messages.CancelMessage; import com.seibel.distanthorizons.core.network.messages.FullDataSourceRequestMessage; import com.seibel.distanthorizons.core.network.messages.FullDataSourceResponseMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; @@ -44,6 +45,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel private final ConcurrentLinkedQueue worldGenLoopingQueue = new ConcurrentLinkedQueue<>(); private final ConcurrentMap incompleteDataSources = new ConcurrentHashMap<>(); + private final ConcurrentMap fullDataRequests = new ConcurrentHashMap<>(); private final AppliedConfigState rateLimitConfig = new AppliedConfigState<>(Config.Client.Advanced.Multiplayer.serverNetworkingRateLimit); @@ -91,12 +93,20 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel // If this fails, current entry is being drained and need create another one if (entry.requestCollectionSemaphore.tryAcquire()) { + fullDataRequests.put(msg.futureId, msg); entry.requestMessages.add(msg); entry.requestCollectionSemaphore.release(); break; } } }); + + this.eventSource.registerHandler(CancelMessage.class, msg -> + { + this.fullDataRequests.remove(msg.futureId); + RemotePlayer remotePlayer = remotePlayerConnectionHandler.getConnectedPlayer(msg); + remotePlayer.pendingFullDataRequests.decrementAndGet(); + }); } public void addPlayer(IServerPlayerWrapper serverPlayer) @@ -142,8 +152,12 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel { RemotePlayer remotePlayer = remotePlayerConnectionHandler.getConnectedPlayer(msg); if (remotePlayer == null) continue; - remotePlayer.pendingFullDataRequests.decrementAndGet(); + // Check if cancelled + if (this.fullDataRequests.remove(msg.futureId) == null) + continue; + + remotePlayer.pendingFullDataRequests.decrementAndGet(); msg.sendResponse(new FullDataSourceResponseMessage(completeSource, this)); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java new file mode 100644 index 000000000..d988e8390 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java @@ -0,0 +1,32 @@ +package com.seibel.distanthorizons.core.multiplayer; + +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.network.ChildNetworkEventSource; +import com.seibel.distanthorizons.core.network.NetworkClient; +import com.seibel.distanthorizons.core.network.messages.RemotePlayerConfigMessage; + +import java.io.Closeable; + +public class ClientNetworkState implements Closeable +{ + public final ChildNetworkEventSource eventSource; + public RemotePlayer.Payload config = new RemotePlayer.Payload(); + + public ClientNetworkState(NetworkClient networkClient) + { + this.eventSource = new ChildNetworkEventSource<>(networkClient); + this.registerNetworkHandlers(); + } + + private void registerNetworkHandlers() + { + eventSource.registerHandler(RemotePlayerConfigMessage.class, msg -> { + this.config = msg.payload; + }); + } + + public void close() + { + this.eventSource.close(); + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java index ae626bb38..bedfb4480 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java @@ -71,6 +71,11 @@ public class RemotePlayerConnectionHandler return playersByConnection.get(msg.getChannelContext()); } + public RemotePlayer getPlayer(IServerPlayerWrapper serverPlayer) + { + return playersByUUID.get(serverPlayer.getUUID()); + } + public void mcPlayerJoined(IServerPlayerWrapper serverPlayer) { this.playersByUUID.put(serverPlayer.getUUID(), new RemotePlayer(serverPlayer)); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index f9ca5c9e4..5aaf89cb6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -47,9 +47,9 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable public boolean isInitialState() { return this.connectionState == EConnectionState.INITIAL; } /** Indicates whether the client is closed(-ing) and should not be used. */ public boolean isClosed() { return closedStates.contains(this.connectionState); } - private boolean isReady; + private boolean ready; /** Indicates whether the connection is established and first message is sent. */ - public boolean isReady() { return isReady; } + public boolean isReady() { return ready; } private final EventLoopGroup workerGroup = new NioEventLoopGroup(); private final Bootstrap clientBootstrap = new Bootstrap() @@ -111,13 +111,13 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable } channel.writeAndFlush(new HelloMessage()); - isReady = true; + ready = true; }); this.channel = connectFuture.channel(); this.channel.closeFuture().addListener((ChannelFuture channelFuture) -> { - isReady = false; + ready = false; this.completeAllFuturesExceptionally(channelFuture.cause() != null ? channelFuture.cause() : new ChannelException("Channel is closed.")); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index ff7b5bdf8..d91937ba7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -3,6 +3,7 @@ package com.seibel.distanthorizons.core.network; import com.google.common.collect.HashBasedTable; import com.google.common.collect.Table; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.network.messages.CancelMessage; import com.seibel.distanthorizons.core.network.messages.ExceptionMessage; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; @@ -13,6 +14,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; @@ -20,7 +22,7 @@ public abstract class NetworkEventSource { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); protected final Map, Set>> handlers = new HashMap<>(); - private final Table> pendingFutures = HashBasedTable.create(); + private final Table> pendingFutures = HashBasedTable.create(); protected boolean hasHandler(Class handlerClass) { @@ -32,6 +34,9 @@ public abstract class NetworkEventSource { boolean handled = false; + if (message instanceof FutureTrackableNetworkMessage) + ((FutureTrackableNetworkMessage) message).futureId |= (long) message.getChannelContext().hashCode() << 32; + Set> handlerList = this.handlers.get(message.getClass()); if (handlerList != null) { @@ -45,7 +50,7 @@ public abstract class NetworkEventSource if (message instanceof FutureTrackableNetworkMessage) { FutureTrackableNetworkMessage trackableMessage = (FutureTrackableNetworkMessage)message; - CompletableFuture future = pendingFutures.remove(message.getChannelContext(), trackableMessage.futureId); + CompletableFuture future = pendingFutures.get(message.getChannelContext(), trackableMessage.futureId); if (future != null) { handled = true; @@ -78,7 +83,17 @@ public abstract class NetworkEventSource protected CompletableFuture sendRequest(ChannelHandlerContext ctx, FutureTrackableNetworkMessage msg) { + msg.futureId |= (long) ctx.hashCode() << 32; + CompletableFuture responseFuture = new CompletableFuture<>(); + responseFuture.handle((response, throwable) -> { + pendingFutures.remove(ctx, msg.futureId); + + if (throwable instanceof CancellationException) + msg.sendResponse(new CancelMessage()); + + return null; + }); pendingFutures.put(ctx, msg.futureId, (CompletableFuture) responseFuture); ctx.writeAndFlush(msg).addListener(writeFuture -> { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java index 31bb8fa0c..eeef2d71a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java @@ -28,7 +28,6 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable private final EventLoopGroup bossGroup = new NioEventLoopGroup(1); private final EventLoopGroup workerGroup = new NioEventLoopGroup(); - private Channel channel; private boolean isClosed = false; @@ -95,8 +94,8 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable LOGGER.info("Server is started on port "+this.port); }); - this.channel = bindFuture.channel(); - this.channel.closeFuture().addListener(future -> this.close()); + Channel channel = bindFuture.channel(); + channel.closeFuture().addListener(future -> this.close()); } public void disconnectClient(ChannelHandlerContext ctx, String reason) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CancelMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CancelMessage.java new file mode 100644 index 000000000..649fcfd20 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CancelMessage.java @@ -0,0 +1,19 @@ +package com.seibel.distanthorizons.core.network.messages; + +import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import io.netty.buffer.ByteBuf; + +public class CancelMessage extends FutureTrackableNetworkMessage +{ + public CancelMessage() { } + + @Override + public void encode0(ByteBuf out) + { + } + + @Override + public void decode0(ByteBuf in) + { + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java index 6d68e1956..cca4a4676 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java @@ -6,7 +6,7 @@ import io.netty.buffer.ByteBuf; public abstract class FutureTrackableNetworkMessage extends NetworkMessage { private static int lastId = 0; - public int futureId = lastId++; + public long futureId = lastId++; public void sendResponse(FutureTrackableNetworkMessage responseMessage) { @@ -23,7 +23,7 @@ public abstract class FutureTrackableNetworkMessage extends NetworkMessage { try { - out.writeInt(futureId); + out.writeInt((int)futureId); this.encode0(out); } catch (Exception e) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java index 0794a1267..e1dfa41d2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java @@ -27,6 +27,7 @@ public class MessageRegistry // Define your messages after this line this.registerMessage(AckMessage.class, AckMessage::new); + this.registerMessage(CancelMessage.class, CancelMessage::new); this.registerMessage(ExceptionMessage.class, ExceptionMessage::new); this.registerMessage(PlayerUUIDMessage.class, PlayerUUIDMessage::new); this.registerMessage(RemotePlayerConfigMessage.class, RemotePlayerConfigMessage::new); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java index 4eb0fa747..5ca34183c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java @@ -48,6 +48,8 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld { this.networkServer.registerHandler(RemotePlayerConfigMessage.class, remotePlayerConfigMessage -> { + this.remotePlayerConnectionHandler.getConnectedPlayer(remotePlayerConfigMessage).payload = remotePlayerConfigMessage.payload; + remotePlayerConfigMessage.payload.fullDataRequestRateLimit = Math.min(rateLimitConfig.get(), remotePlayerConfigMessage.payload.fullDataRequestRateLimit); remotePlayerConfigMessage.sendResponse(remotePlayerConfigMessage); }); @@ -67,6 +69,9 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld { this.getLevel(origin).removePlayer(player); this.getLevel(dest).addPlayer(player); + + RemotePlayer remotePlayer = this.remotePlayerConnectionHandler.getPlayer(player); + remotePlayer.channelContext.writeAndFlush(new RemotePlayerConfigMessage(remotePlayer.payload)); } @Override From 76b226b865c7c44e5b2b950aa1892dfcba00819a Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 4 Aug 2023 13:06:56 +0500 Subject: [PATCH 020/205] Fix switching dimensions (request cancellation is broken) --- .../WorldRemoteGenerationQueue.java | 22 +++------- .../core/level/DhClientLevel.java | 15 ++++--- .../core/level/DhServerLevel.java | 28 ++++++------ .../core/multiplayer/ClientNetworkState.java | 42 +++++++++++++++--- .../core/multiplayer/MultiplayerConfig.java | 26 +++++++++++ .../core/multiplayer/RemotePlayer.java | 43 ------------------ .../RemotePlayerConnectionHandler.java | 44 ++++++++++++------- .../core/multiplayer/ServerPlayerState.java | 20 +++++++++ .../core/network/NetworkClient.java | 6 +-- .../core/network/NetworkEventSource.java | 26 +++++++---- .../core/network/NetworkServer.java | 9 ++-- ...rce.java => ScopedNetworkEventSource.java} | 8 +--- .../{CloseMessage.java => CloseEvent.java} | 2 +- .../messages/RemotePlayerConfigMessage.java | 8 ++-- .../FutureTrackableNetworkMessage.java | 1 + .../core/network/protocol/MessageHandler.java | 5 +-- .../core/world/DhClientWorld.java | 38 ++++------------ .../core/world/DhServerWorld.java | 26 +++++------ 18 files changed, 189 insertions(+), 180 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/multiplayer/MultiplayerConfig.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayer.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ServerPlayerState.java rename core/src/main/java/com/seibel/distanthorizons/core/network/{ChildNetworkEventSource.java => ScopedNetworkEventSource.java} (77%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{CloseMessage.java => CloseEvent.java} (92%) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 338a0e707..7669ff722 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -8,8 +8,6 @@ import com.seibel.distanthorizons.core.level.IDhClientLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.multiplayer.ClientNetworkState; -import com.seibel.distanthorizons.core.network.ChildNetworkEventSource; -import com.seibel.distanthorizons.core.network.NetworkClient; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.messages.FullDataSourceRequestMessage; import com.seibel.distanthorizons.core.network.messages.FullDataSourceResponseMessage; @@ -25,10 +23,7 @@ import org.apache.logging.log4j.Logger; import java.util.ArrayList; import java.util.Objects; import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.Semaphore; +import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; @@ -77,7 +72,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue @Override public void runCurrentGenTasksUntilBusy(DhBlockPos2D targetPos) { while (generatorClosingFuture == null - && networkState.eventSource.parent.isReady() + && networkState.client().isReady() && !waitingTasks.isEmpty() && pendingTasks() < this.networkState.config.fullDataRequestRateLimit && pendingTasksSemaphore.tryAcquire()) @@ -95,7 +90,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue ? a : b)); WorldGenQueueEntry entry = waitingTasks.remove(sectionPos); - CompletableFuture request = this.networkState.eventSource.parent.sendRequest(new FullDataSourceRequestMessage(sectionPos)); + CompletableFuture request = this.networkState.client().sendRequest(new FullDataSourceRequestMessage(sectionPos)); pendingRequests.add(request); request.handle((response, throwable) -> { @@ -131,14 +126,11 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue entry.future.complete(WorldGenResult.CreateSuccess(sectionPos)); } - catch (RateLimitedException e) - { - LOGGER.warn("Rate limited by server, re-queueing task: "+sectionPos, e); - finishedRequests.decrementAndGet(); - waitingTasks.put(sectionPos, entry); - } - catch (ChannelException e) + catch (CancellationException | ChannelException | RateLimitedException e) { + if (e instanceof RateLimitedException) + LOGGER.warn("Rate limited by server, re-queueing task ["+sectionPos+"]: "+e.getMessage()); + finishedRequests.decrementAndGet(); waitingTasks.put(sectionPos, entry); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index c4187e799..24fe4253d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -7,6 +7,7 @@ import com.seibel.distanthorizons.core.file.fullDatafile.RemoteFullDataFileHandl import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.generation.WorldRemoteGenerationQueue; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.multiplayer.ClientNetworkState; import com.seibel.distanthorizons.core.network.NetworkClient; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; @@ -34,9 +35,9 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel private static class WorldGenState extends WorldGenModule.WorldGenState { - WorldGenState(IDhClientLevel level, NetworkClient client) + WorldGenState(IDhClientLevel level, ClientNetworkState networkState) { - this.worldGenerationQueue = new WorldRemoteGenerationQueue(client, level); + this.worldGenerationQueue = new WorldRemoteGenerationQueue(networkState, level); } } @@ -46,7 +47,7 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel public final RemoteFullDataFileHandler dataFileHandler; @CheckForNull - private final NetworkClient networkClient; + private final ClientNetworkState networkState; public final WorldGenModule worldGenModule; @@ -55,13 +56,13 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel // constructor // //=============// - public DhClientLevel(AbstractSaveStructure saveStructure, IClientLevelWrapper clientLevelWrapper, @Nullable NetworkClient networkClient) + public DhClientLevel(AbstractSaveStructure saveStructure, IClientLevelWrapper clientLevelWrapper, @Nullable ClientNetworkState networkState) { this.levelWrapper = clientLevelWrapper; this.saveStructure = saveStructure; this.dataFileHandler = new RemoteFullDataFileHandler(this, saveStructure); - this.networkClient = networkClient; + this.networkState = networkState; this.worldGenModule = new WorldGenModule(dataFileHandler, this); clientside = new ClientLevelModule(this); @@ -82,13 +83,13 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel public void doWorldGen() { - boolean isClientUsable = networkClient != null && !networkClient.isClosed(); + boolean isClientUsable = networkState != null && !networkState.client().isClosed(); boolean shouldDoWorldGen = isClientUsable && clientside.isRendering(); boolean isWorldGenRunning = worldGenModule.isWorldGenRunning(); if (shouldDoWorldGen && !isWorldGenRunning) { // start world gen - worldGenModule.startWorldGen(this.dataFileHandler, new WorldGenState(this, this.networkClient)); + worldGenModule.startWorldGen(this.dataFileHandler, new WorldGenState(this, this.networkState)); } else if (!shouldDoWorldGen && isWorldGenRunning) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index cfe999295..710337b95 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -6,12 +6,11 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedF import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IIncompleteFullDataSource; -import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataFileHandler; import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; -import com.seibel.distanthorizons.core.multiplayer.RemotePlayer; +import com.seibel.distanthorizons.core.multiplayer.ServerPlayerState; import com.seibel.distanthorizons.core.multiplayer.RemotePlayerConnectionHandler; -import com.seibel.distanthorizons.core.network.ChildNetworkEventSource; +import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkServer; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.messages.CancelMessage; @@ -22,7 +21,6 @@ import com.seibel.distanthorizons.core.pos.DhLodPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.util.LodUtil; -import com.seibel.distanthorizons.core.world.DhServerWorld; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; @@ -41,7 +39,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel private final IServerLevelWrapper serverLevelWrapper; private final RemotePlayerConnectionHandler remotePlayerConnectionHandler; - private final ChildNetworkEventSource eventSource; + private final ScopedNetworkEventSource eventSource; private final ConcurrentLinkedQueue worldGenLoopingQueue = new ConcurrentLinkedQueue<>(); private final ConcurrentMap incompleteDataSources = new ConcurrentHashMap<>(); @@ -60,7 +58,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel LOGGER.info("Started DHLevel for {} with saves at {}", serverLevelWrapper, saveStructure); this.remotePlayerConnectionHandler = remotePlayerConnectionHandler; - this.eventSource = new ChildNetworkEventSource<>(remotePlayerConnectionHandler.eventSource); + this.eventSource = new ScopedNetworkEventSource<>(remotePlayerConnectionHandler.server()); this.registerNetworkHandlers(); } @@ -68,15 +66,15 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel { this.eventSource.registerHandler(FullDataSourceRequestMessage.class, msg -> { - RemotePlayer remotePlayer = remotePlayerConnectionHandler.getConnectedPlayer(msg); - if (remotePlayer.serverPlayer.getLevel() != this.serverLevelWrapper) + ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); + if (serverPlayerState.serverPlayer.getLevel() != this.serverLevelWrapper) return; LOGGER.info("FullDataSourceRequestMessage received at pos ({}, {}) with detail level {}", msg.dhSectionPos.sectionX, msg.dhSectionPos.sectionZ, msg.dhSectionPos.sectionDetailLevel); - if (remotePlayer.pendingFullDataRequests.incrementAndGet() > rateLimitConfig.get()) + if (serverPlayerState.pendingFullDataRequests.incrementAndGet() > rateLimitConfig.get()) { - remotePlayer.pendingFullDataRequests.decrementAndGet(); + serverPlayerState.pendingFullDataRequests.decrementAndGet(); msg.sendResponse(new RateLimitedException("Max concurrent requests: "+rateLimitConfig.get())); return; } @@ -104,8 +102,8 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel this.eventSource.registerHandler(CancelMessage.class, msg -> { this.fullDataRequests.remove(msg.futureId); - RemotePlayer remotePlayer = remotePlayerConnectionHandler.getConnectedPlayer(msg); - remotePlayer.pendingFullDataRequests.decrementAndGet(); + ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); + serverPlayerState.pendingFullDataRequests.decrementAndGet(); }); } @@ -150,14 +148,14 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel CompleteFullDataSource completeSource = (CompleteFullDataSource) entry.fullDataSource; for (FullDataSourceRequestMessage msg : entry.requestMessages) { - RemotePlayer remotePlayer = remotePlayerConnectionHandler.getConnectedPlayer(msg); - if (remotePlayer == null) continue; + ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); + if (serverPlayerState == null) continue; // Check if cancelled if (this.fullDataRequests.remove(msg.futureId) == null) continue; - remotePlayer.pendingFullDataRequests.decrementAndGet(); + serverPlayerState.pendingFullDataRequests.decrementAndGet(); msg.sendResponse(new FullDataSourceResponseMessage(completeSource, this)); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java index d988e8390..ce05ac121 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java @@ -1,32 +1,60 @@ package com.seibel.distanthorizons.core.multiplayer; import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.network.ChildNetworkEventSource; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkClient; +import com.seibel.distanthorizons.core.network.messages.AckMessage; +import com.seibel.distanthorizons.core.network.messages.HelloMessage; +import com.seibel.distanthorizons.core.network.messages.PlayerUUIDMessage; import com.seibel.distanthorizons.core.network.messages.RemotePlayerConfigMessage; +import org.apache.logging.log4j.Logger; import java.io.Closeable; +import java.util.UUID; public class ClientNetworkState implements Closeable { - public final ChildNetworkEventSource eventSource; - public RemotePlayer.Payload config = new RemotePlayer.Payload(); + protected static final Logger LOGGER = DhLoggerBuilder.getLogger(); - public ClientNetworkState(NetworkClient networkClient) + private final ScopedNetworkEventSource eventSource; + private final UUID playerUUID; + public MultiplayerConfig config = new MultiplayerConfig(); + + public NetworkClient client() { return this.eventSource.parent; } + + public ClientNetworkState(NetworkClient networkClient, UUID playerUUID) { - this.eventSource = new ChildNetworkEventSource<>(networkClient); + this.eventSource = new ScopedNetworkEventSource<>(networkClient); + this.playerUUID = playerUUID; this.registerNetworkHandlers(); + this.client().startConnecting(); } private void registerNetworkHandlers() { - eventSource.registerHandler(RemotePlayerConfigMessage.class, msg -> { - this.config = msg.payload; + this.client().registerHandler(HelloMessage.class, helloMessage -> + { + LOGGER.info("Connected to server: "+helloMessage.getChannelContext().channel().remoteAddress()); + + this.client().sendRequest(new PlayerUUIDMessage(playerUUID)) + .thenCompose(ack -> this.client().sendRequest(new RemotePlayerConfigMessage(new MultiplayerConfig() + {{ + fullDataRequestRateLimit = Config.Client.Advanced.Multiplayer.serverNetworkingRateLimit.get(); + }}))) + .thenAccept(msg -> { + this.config = msg.payload; + }) + .exceptionally(throwable -> { + LOGGER.error("Error while fetching server's config", throwable); + return null; + }); }); } public void close() { this.eventSource.close(); + this.client().close(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/MultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/MultiplayerConfig.java new file mode 100644 index 000000000..6b0fde3e0 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/MultiplayerConfig.java @@ -0,0 +1,26 @@ +package com.seibel.distanthorizons.core.multiplayer; + +import com.seibel.distanthorizons.core.network.protocol.INetworkObject; +import io.netty.buffer.ByteBuf; + +public class MultiplayerConfig implements INetworkObject +{ + public int renderDistance; + public int fullDataRequestRateLimit; + + + @Override + public void encode(ByteBuf out) + { + out.writeInt(this.renderDistance); + out.writeInt(this.fullDataRequestRateLimit); + } + + @Override + public void decode(ByteBuf in) + { + this.renderDistance = in.readInt(); + this.fullDataRequestRateLimit = in.readInt(); + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayer.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayer.java deleted file mode 100644 index ac025ea61..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayer.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.seibel.distanthorizons.core.multiplayer; - -import com.seibel.distanthorizons.core.network.protocol.INetworkObject; -import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; - -import java.util.concurrent.atomic.AtomicInteger; - -public class RemotePlayer -{ - public IServerPlayerWrapper serverPlayer; - public Payload payload; - public ChannelHandlerContext channelContext; - public final AtomicInteger pendingFullDataRequests = new AtomicInteger(); - - - - public RemotePlayer(IServerPlayerWrapper serverPlayer) { this.serverPlayer = serverPlayer; } - - public static class Payload implements INetworkObject - { - public int renderDistance; - public int fullDataRequestRateLimit; - - - @Override - public void encode(ByteBuf out) - { - out.writeInt(this.renderDistance); - out.writeInt(this.fullDataRequestRateLimit); - } - - @Override - public void decode(ByteBuf in) - { - this.renderDistance = in.readInt(); - this.fullDataRequestRateLimit = in.readInt(); - } - - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java index bedfb4480..8b0d83c79 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java @@ -2,27 +2,30 @@ package com.seibel.distanthorizons.core.multiplayer; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; -import com.seibel.distanthorizons.core.network.ChildNetworkEventSource; +import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkServer; import com.seibel.distanthorizons.core.network.messages.AckMessage; -import com.seibel.distanthorizons.core.network.messages.CloseMessage; +import com.seibel.distanthorizons.core.network.messages.CloseEvent; import com.seibel.distanthorizons.core.network.messages.PlayerUUIDMessage; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import io.netty.channel.ChannelHandlerContext; +import java.io.Closeable; import java.util.HashMap; import java.util.UUID; -public class RemotePlayerConnectionHandler +public class RemotePlayerConnectionHandler implements Closeable { - public final ChildNetworkEventSource eventSource; - private final HashMap playersByUUID = new HashMap<>(); - private final BiMap playersByConnection = HashBiMap.create(); + private final ScopedNetworkEventSource eventSource; + private final HashMap playersByUUID = new HashMap<>(); + private final BiMap playersByConnection = HashBiMap.create(); + + public NetworkServer server() { return this.eventSource.parent; } public RemotePlayerConnectionHandler(NetworkServer networkServer) { - this.eventSource = new ChildNetworkEventSource<>(networkServer); + this.eventSource = new ScopedNetworkEventSource<>(networkServer); this.registerNetworkHandlers(); } @@ -31,7 +34,7 @@ public class RemotePlayerConnectionHandler this.eventSource.registerHandler(PlayerUUIDMessage.class, playerUUIDMessage -> { ChannelHandlerContext channelContext = playerUUIDMessage.getChannelContext(); - RemotePlayer dhPlayer = this.playersByUUID.get(playerUUIDMessage.playerUUID); + ServerPlayerState dhPlayer = this.playersByUUID.get(playerUUIDMessage.playerUUID); if (dhPlayer == null) { @@ -51,9 +54,9 @@ public class RemotePlayerConnectionHandler playerUUIDMessage.sendResponse(new AckMessage()); }); - this.eventSource.registerHandler(CloseMessage.class, closeMessage -> + this.eventSource.registerHandler(CloseEvent.class, closeEvent -> { - RemotePlayer dhPlayer = this.playersByConnection.remove(closeMessage.getChannelContext()); + ServerPlayerState dhPlayer = this.playersByConnection.remove(closeEvent.getChannelContext()); if (dhPlayer != null) { dhPlayer.channelContext = null; @@ -61,29 +64,29 @@ public class RemotePlayerConnectionHandler }); } - public Iterable getConnectedPlayers() + public Iterable getConnectedPlayers() { return playersByConnection.values(); } - public RemotePlayer getConnectedPlayer(NetworkMessage msg) + public ServerPlayerState getConnectedPlayer(NetworkMessage msg) { return playersByConnection.get(msg.getChannelContext()); } - public RemotePlayer getPlayer(IServerPlayerWrapper serverPlayer) + public ServerPlayerState getPlayer(IServerPlayerWrapper serverPlayer) { return playersByUUID.get(serverPlayer.getUUID()); } - public void mcPlayerJoined(IServerPlayerWrapper serverPlayer) + public void registerJoinedPlayer(IServerPlayerWrapper serverPlayer) { - this.playersByUUID.put(serverPlayer.getUUID(), new RemotePlayer(serverPlayer)); + this.playersByUUID.put(serverPlayer.getUUID(), new ServerPlayerState(serverPlayer)); } - public void mcPlayerLeft(IServerPlayerWrapper serverPlayer) + public void unregisterLeftPlayer(IServerPlayerWrapper serverPlayer) { - RemotePlayer dhPlayer = this.playersByUUID.remove(serverPlayer.getUUID()); + ServerPlayerState dhPlayer = this.playersByUUID.remove(serverPlayer.getUUID()); ChannelHandlerContext channelContext = this.playersByConnection.inverse().remove(dhPlayer); if (channelContext != null) { @@ -91,4 +94,11 @@ public class RemotePlayerConnectionHandler } } + @Override + public void close() + { + this.eventSource.close(); + this.server().close(); + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ServerPlayerState.java new file mode 100644 index 000000000..bf7e83f7f --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ServerPlayerState.java @@ -0,0 +1,20 @@ +package com.seibel.distanthorizons.core.multiplayer; + +import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; +import io.netty.channel.ChannelHandlerContext; + +import java.util.concurrent.atomic.AtomicInteger; + +public class ServerPlayerState +{ + public IServerPlayerWrapper serverPlayer; + public MultiplayerConfig config; + public ChannelHandlerContext channelContext; + public final AtomicInteger pendingFullDataRequests = new AtomicInteger(); + + + + public ServerPlayerState(IServerPlayerWrapper serverPlayer) { this.serverPlayer = serverPlayer; } + +} + diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index 5aaf89cb6..b79d5997f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -1,7 +1,7 @@ package com.seibel.distanthorizons.core.network; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.network.messages.CloseMessage; +import com.seibel.distanthorizons.core.network.messages.CloseEvent; import com.seibel.distanthorizons.core.network.messages.CloseReasonMessage; import com.seibel.distanthorizons.core.network.messages.HelloMessage; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; @@ -78,9 +78,9 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable this.connectionState = EConnectionState.CLOSE_WAIT; }); - this.registerHandler(CloseMessage.class, closeMessage -> + this.registerHandler(CloseEvent.class, closeEvent -> { - LOGGER.info("Disconnected from server: "+closeMessage.getChannelContext().channel().remoteAddress()); + LOGGER.info("Disconnected from server: "+ closeEvent.getChannelContext().channel().remoteAddress()); if (this.connectionState == EConnectionState.CLOSE_WAIT) { this.close(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index d91937ba7..91179edbe 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -2,6 +2,7 @@ package com.seibel.distanthorizons.core.network; import com.google.common.collect.HashBasedTable; import com.google.common.collect.Table; +import com.google.common.collect.Tables; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.network.messages.CancelMessage; import com.seibel.distanthorizons.core.network.messages.ExceptionMessage; @@ -10,19 +11,19 @@ import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import io.netty.channel.ChannelHandlerContext; import org.apache.logging.log4j.Logger; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; import java.util.Set; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.function.Consumer; public abstract class NetworkEventSource { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - protected final Map, Set>> handlers = new HashMap<>(); - private final Table> pendingFutures = HashBasedTable.create(); + protected final ConcurrentMap, Set>> handlers = new ConcurrentHashMap<>(); + private final Table> pendingFutures = Tables.synchronizedTable(HashBasedTable.create()); protected boolean hasHandler(Class handlerClass) { @@ -84,6 +85,7 @@ public abstract class NetworkEventSource protected CompletableFuture sendRequest(ChannelHandlerContext ctx, FutureTrackableNetworkMessage msg) { msg.futureId |= (long) ctx.hashCode() << 32; + msg.setChannelContext(ctx); CompletableFuture responseFuture = new CompletableFuture<>(); responseFuture.handle((response, throwable) -> { @@ -105,14 +107,20 @@ public abstract class NetworkEventSource } protected final void completeAllFuturesExceptionally(ChannelHandlerContext ctx, Throwable cause) { - for (CompletableFuture futureData : pendingFutures.row(ctx).values()) - futureData.completeExceptionally(cause); - pendingFutures.row(ctx).clear(); + synchronized (pendingFutures) + { + for (CompletableFuture futureData : pendingFutures.row(ctx).values()) + futureData.completeExceptionally(cause); + pendingFutures.row(ctx).clear(); + } } protected final void completeAllFuturesExceptionally(Throwable cause) { - for (ChannelHandlerContext ctx : pendingFutures.rowKeySet()) - this.completeAllFuturesExceptionally(ctx, cause); + synchronized (pendingFutures) + { + for (ChannelHandlerContext ctx : pendingFutures.rowKeySet()) + this.completeAllFuturesExceptionally(ctx, cause); + } } public void close() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java index eeef2d71a..f110bc7a7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java @@ -1,9 +1,8 @@ package com.seibel.distanthorizons.core.network; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.network.messages.AckMessage; import com.seibel.distanthorizons.core.network.messages.CloseReasonMessage; -import com.seibel.distanthorizons.core.network.messages.CloseMessage; +import com.seibel.distanthorizons.core.network.messages.CloseEvent; import com.seibel.distanthorizons.core.network.messages.HelloMessage; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.MessageHandler; @@ -66,12 +65,12 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable channelContext.writeAndFlush(new HelloMessage()); }); - this.registerHandler(CloseMessage.class, closeMessage -> + this.registerHandler(CloseEvent.class, closeEvent -> { - Channel channel = closeMessage.getChannelContext().channel(); + Channel channel = closeEvent.getChannelContext().channel(); LOGGER.info("Client disconnected: "+channel.remoteAddress()); - this.completeAllFuturesExceptionally(closeMessage.getChannelContext(), channel.closeFuture().cause()); + this.completeAllFuturesExceptionally(closeEvent.getChannelContext(), channel.closeFuture().cause()); }); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/ChildNetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/ScopedNetworkEventSource.java similarity index 77% rename from core/src/main/java/com/seibel/distanthorizons/core/network/ChildNetworkEventSource.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/ScopedNetworkEventSource.java index e7a1e4e39..9f78171b2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/ChildNetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/ScopedNetworkEventSource.java @@ -5,19 +5,15 @@ import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import java.util.function.Consumer; /** Provides a way to register network message handlers which are expected to be removed later. */ -public final class ChildNetworkEventSource extends NetworkEventSource +public final class ScopedNetworkEventSource extends NetworkEventSource { public final TParent parent; private boolean isClosed = false; - public ChildNetworkEventSource(TParent parent) + public ScopedNetworkEventSource(TParent parent) { this.parent = parent; } - public ChildNetworkEventSource(ChildNetworkEventSource child) - { - this.parent = child.parent; - } @Override public void registerHandler(Class handlerClass, Consumer handlerImplementation) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseEvent.java similarity index 92% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseEvent.java index 88b04cc7f..a4a02bb44 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseEvent.java @@ -7,7 +7,7 @@ import io.netty.buffer.ByteBuf; * This is not a "real" message, and only used to indicate a disconnection. * To send a "disconnect reason" message, use {@link CloseReasonMessage}. */ -public class CloseMessage extends NetworkMessage +public class CloseEvent extends NetworkMessage { @Override public void encode(ByteBuf out) { throw new UnsupportedOperationException("CloseMessage is not a real message, and must not be sent."); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/RemotePlayerConfigMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/RemotePlayerConfigMessage.java index 5b9616f3a..dbc874f7c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/RemotePlayerConfigMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/RemotePlayerConfigMessage.java @@ -1,23 +1,23 @@ package com.seibel.distanthorizons.core.network.messages; +import com.seibel.distanthorizons.core.multiplayer.MultiplayerConfig; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; -import com.seibel.distanthorizons.core.multiplayer.RemotePlayer; import io.netty.buffer.ByteBuf; public class RemotePlayerConfigMessage extends FutureTrackableNetworkMessage { - public RemotePlayer.Payload payload; + public MultiplayerConfig payload; public RemotePlayerConfigMessage() { } - public RemotePlayerConfigMessage(RemotePlayer.Payload payload) { this.payload = payload; } + public RemotePlayerConfigMessage(MultiplayerConfig payload) { this.payload = payload; } @Override public void encode0(ByteBuf out) { this.payload.encode(out); } @Override - public void decode0(ByteBuf in) { this.payload = INetworkObject.decode(new RemotePlayer.Payload(), in); } + public void decode0(ByteBuf in) { this.payload = INetworkObject.decode(new MultiplayerConfig(), in); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java index cca4a4676..fb9a6d873 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java @@ -6,6 +6,7 @@ import io.netty.buffer.ByteBuf; public abstract class FutureTrackableNetworkMessage extends NetworkMessage { private static int lastId = 0; + // Only low 32 bits are sent (high bits are used for identifying a channel this request was sent from by remote peer) public long futureId = lastId++; public void sendResponse(FutureTrackableNetworkMessage responseMessage) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java index 38aeec32d..0a8a7e946 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java @@ -1,14 +1,13 @@ package com.seibel.distanthorizons.core.network.protocol; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.network.messages.CloseMessage; +import com.seibel.distanthorizons.core.network.messages.CloseEvent; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; -import java.util.function.BiConsumer; import java.util.function.Consumer; @ChannelHandler.Sharable @@ -34,7 +33,7 @@ public class MessageHandler extends SimpleChannelInboundHandler @Override public void channelInactive(@NotNull ChannelHandlerContext channelContext) { - this.channelRead0(channelContext, new CloseMessage()); + this.channelRead0(channelContext, new CloseEvent()); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java index 2885b0e1f..8ac38b330 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java @@ -5,9 +5,8 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure; import com.seibel.distanthorizons.core.level.DhClientLevel; import com.seibel.distanthorizons.core.level.IDhLevel; +import com.seibel.distanthorizons.core.multiplayer.ClientNetworkState; import com.seibel.distanthorizons.core.network.NetworkClient; -import com.seibel.distanthorizons.core.network.messages.*; -import com.seibel.distanthorizons.core.multiplayer.RemotePlayer; import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.util.objects.EventLoop; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; @@ -26,9 +25,8 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld private final ConcurrentHashMap levels; public final ClientOnlySaveStructure saveStructure; - @CheckForNull - private final NetworkClient networkClient; + private final ClientNetworkState networkState; // TODO why does this executor have 2 threads? public ExecutorService dhTickerThread = ThreadUtil.makeSingleThreadPool("DH Client World Ticker Thread", 2); @@ -50,34 +48,16 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld if (Config.Client.Advanced.Multiplayer.enableServerNetworking.get()) { // TODO server specific configs - this.networkClient = new NetworkClient(MC_CLIENT.getCurrentServerIp(), 25049); - this.registerNetworkHandlers(); + NetworkClient networkClient = new NetworkClient(MC_CLIENT.getCurrentServerIp(), 25049); + this.networkState = new ClientNetworkState(networkClient, MC_CLIENT.getPlayerUUID()); } else { - this.networkClient = null; + this.networkState = null; } LOGGER.info("Started DhWorld of type "+this.environment); } - - private void registerNetworkHandlers() { - assert this.networkClient != null; - this.networkClient.registerHandler(HelloMessage.class, helloMessage -> - { - LOGGER.info("Connected to server: "+helloMessage.getChannelContext().channel().remoteAddress()); - - this.networkClient.sendRequest(new PlayerUUIDMessage(MC_CLIENT.getPlayerUUID())) - .thenCompose(ack -> this.networkClient.sendRequest(new RemotePlayerConfigMessage(new RemotePlayer.Payload() - {{ - fullDataRequestRateLimit = Config.Client.Advanced.Multiplayer.serverNetworkingRateLimit.get(); - }}))) - .exceptionally(throwable -> { - LOGGER.error("Error while fetching server's config", throwable); - return null; - }); - }); - } @@ -102,7 +82,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld return null; } - return new DhClientLevel(this.saveStructure, clientLevelWrapper, networkClient); + return new DhClientLevel(this.saveStructure, clientLevelWrapper, networkState); }); } @@ -144,8 +124,6 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld public void doWorldGen() { this.levels.values().forEach(DhClientLevel::doWorldGen); - if (networkClient != null && networkClient.isInitialState()) - networkClient.startConnecting(); } @Override @@ -157,9 +135,9 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld @Override public void close() { - if (this.networkClient != null) + if (this.networkState != null) { - this.networkClient.close(); + this.networkState.close(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java index 5ca34183c..dd4ae4170 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java @@ -5,7 +5,7 @@ import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.file.structure.LocalSaveStructure; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.level.IDhLevel; -import com.seibel.distanthorizons.core.multiplayer.RemotePlayer; +import com.seibel.distanthorizons.core.multiplayer.ServerPlayerState; import com.seibel.distanthorizons.core.multiplayer.RemotePlayerConnectionHandler; import com.seibel.distanthorizons.core.network.NetworkServer; import com.seibel.distanthorizons.core.network.messages.RemotePlayerConfigMessage; @@ -23,7 +23,6 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld private final HashMap levels; public final LocalSaveStructure saveStructure; - private final NetworkServer networkServer; private final RemotePlayerConnectionHandler remotePlayerConnectionHandler; private final AppliedConfigState rateLimitConfig = new AppliedConfigState<>(Config.Client.Advanced.Multiplayer.serverNetworkingRateLimit); @@ -37,18 +36,18 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld this.levels = new HashMap<>(); // TODO move to global payload once server specific configs are implemented - this.networkServer = new NetworkServer(25049); - this.registerNetworkHandlers(); + NetworkServer networkServer = new NetworkServer(25049); this.remotePlayerConnectionHandler = new RemotePlayerConnectionHandler(networkServer); + this.registerNetworkHandlers(); LOGGER.info("Started "+DhServerWorld.class.getSimpleName()+" of type "+this.environment); } private void registerNetworkHandlers() { - this.networkServer.registerHandler(RemotePlayerConfigMessage.class, remotePlayerConfigMessage -> + this.remotePlayerConnectionHandler.server().registerHandler(RemotePlayerConfigMessage.class, remotePlayerConfigMessage -> { - this.remotePlayerConnectionHandler.getConnectedPlayer(remotePlayerConfigMessage).payload = remotePlayerConfigMessage.payload; + this.remotePlayerConnectionHandler.getConnectedPlayer(remotePlayerConfigMessage).config = remotePlayerConfigMessage.payload; remotePlayerConfigMessage.payload.fullDataRequestRateLimit = Math.min(rateLimitConfig.get(), remotePlayerConfigMessage.payload.fullDataRequestRateLimit); remotePlayerConfigMessage.sendResponse(remotePlayerConfigMessage); @@ -57,21 +56,18 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld public void addPlayer(IServerPlayerWrapper serverPlayer) { - this.remotePlayerConnectionHandler.mcPlayerJoined(serverPlayer); + this.remotePlayerConnectionHandler.registerJoinedPlayer(serverPlayer); this.getLevel(serverPlayer.getLevel()).addPlayer(serverPlayer); } public void removePlayer(IServerPlayerWrapper serverPlayer) { this.getLevel(serverPlayer.getLevel()).removePlayer(serverPlayer); - this.remotePlayerConnectionHandler.mcPlayerLeft(serverPlayer); + this.remotePlayerConnectionHandler.unregisterLeftPlayer(serverPlayer); } public void changePlayerLevel(IServerPlayerWrapper player, IServerLevelWrapper origin, IServerLevelWrapper dest) { this.getLevel(origin).removePlayer(player); this.getLevel(dest).addPlayer(player); - - RemotePlayer remotePlayer = this.remotePlayerConnectionHandler.getPlayer(player); - remotePlayer.channelContext.writeAndFlush(new RemotePlayerConfigMessage(remotePlayer.payload)); } @Override @@ -124,10 +120,10 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld if (rateLimitConfig.pollNewValue()) { - for (RemotePlayer remotePlayer : this.remotePlayerConnectionHandler.getConnectedPlayers()) + for (ServerPlayerState serverPlayerState : this.remotePlayerConnectionHandler.getConnectedPlayers()) { - remotePlayer.payload.fullDataRequestRateLimit = rateLimitConfig.get(); - remotePlayer.channelContext.writeAndFlush(new RemotePlayerConfigMessage(remotePlayer.payload)); + serverPlayerState.config.fullDataRequestRateLimit = rateLimitConfig.get(); + serverPlayerState.channelContext.writeAndFlush(new RemotePlayerConfigMessage(serverPlayerState.config)); } } } @@ -145,7 +141,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld @Override public void close() { - this.networkServer.close(); + this.remotePlayerConnectionHandler.close(); for (DhServerLevel level : this.levels.values()) { From 5767668efabcfaddd3461b998a9a9a9455f18690 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 4 Aug 2023 21:04:34 +0500 Subject: [PATCH 021/205] Somewhat proper request cancellation --- .../GeneratedFullDataFileHandler.java | 6 + .../generation/IWorldGenerationQueue.java | 5 +- .../core/generation/WorldGenerationQueue.java | 5 + .../WorldRemoteGenerationQueue.java | 105 ++++++++++++------ .../tasks/IWorldGenTaskTracker.java | 2 + .../core/level/DhServerLevel.java | 46 +++++--- .../core/network/NetworkEventSource.java | 6 +- .../FullDataSourceResponseMessage.java | 15 ++- 8 files changed, 131 insertions(+), 59 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java index ff65b0b21..2da61a3ad 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java @@ -80,12 +80,17 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler } public void removeGenRequestIf(Function removeIf) { + HashSet removedRequests = new HashSet<>(); + this.incompleteDataSources.forEach((pos, dataSource) -> { if (removeIf.apply(pos)) { this.incompleteDataSources.remove(pos); + removedRequests.add(pos); } }); + + this.worldGenQueueRef.get().cancelGenTasks(removedRequests); } //=================// @@ -332,6 +337,7 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler } @Override + @Nullable public Consumer getChunkDataConsumer() { if (this.loadedTargetFullDataSource == null) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/IWorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/IWorldGenerationQueue.java index 43b33ce12..f3d2da864 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/IWorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/IWorldGenerationQueue.java @@ -4,19 +4,20 @@ import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker; import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhLodPos; +import com.seibel.distanthorizons.core.pos.DhSectionPos; import java.io.Closeable; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; public interface IWorldGenerationQueue extends Closeable { byte largestDataDetail(); CompletableFuture submitGenTask(DhLodPos pos, byte requiredDataDetail, IWorldGenTaskTracker tracker); + void cancelGenTasks(Iterable positions); + void runCurrentGenTasksUntilBusy(DhBlockPos2D targetPos); CompletableFuture startClosing(boolean cancelCurrentGeneration, boolean alsoInterruptRunning); - void close(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java index e64eac970..d0211adc9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java @@ -152,6 +152,11 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender //} } + @Override + public void cancelGenTasks(Iterable positions) + { + // TODO Cancel gen tasks properly + } //===============// // running tasks // diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 7669ff722..3947965ae 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -20,9 +20,7 @@ import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import io.netty.channel.ChannelException; import org.apache.logging.log4j.Logger; -import java.util.ArrayList; -import java.util.Objects; -import java.util.Set; +import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; @@ -34,13 +32,12 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue private final ClientNetworkState networkState; private final IDhClientLevel level; - private final ConcurrentMap waitingTasks = new ConcurrentHashMap<>(); - private final Semaphore pendingTasksSemaphore = new Semaphore(Short.MAX_VALUE); - private int pendingTasks() { return Short.MAX_VALUE - pendingTasksSemaphore.availablePermits(); } - private final Set> pendingRequests = ConcurrentHashMap.newKeySet(); - private volatile CompletableFuture generatorClosingFuture = null; + private final ConcurrentMap waitingTasks = new ConcurrentHashMap<>(); + private final Semaphore pendingTasksSemaphore = new Semaphore(Short.MAX_VALUE, true); + private int pendingTasks() { return Short.MAX_VALUE - pendingTasksSemaphore.availablePermits(); } + private final F3Screen.NestedMessage f3Message = new F3Screen.NestedMessage(this::f3Log); private final AtomicInteger finishedRequests = new AtomicInteger(); private final AtomicInteger totalRequests = new AtomicInteger(); @@ -52,12 +49,14 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue this.level = level; } - @Override public byte largestDataDetail() + @Override + public byte largestDataDetail() { return LodUtil.BLOCK_DETAIL_LEVEL; } - @Override public CompletableFuture submitGenTask(DhLodPos lodPos, byte requiredDataDetail, IWorldGenTaskTracker tracker) + @Override + public CompletableFuture submitGenTask(DhLodPos lodPos, byte requiredDataDetail, IWorldGenTaskTracker tracker) { LodUtil.assertTrue(lodPos.detailLevel == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL, "Only highest-detail sections are allowed."); DhSectionPos sectionPos = new DhSectionPos(lodPos.detailLevel, lodPos); @@ -69,7 +68,8 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue return entry.future; } - @Override public void runCurrentGenTasksUntilBusy(DhBlockPos2D targetPos) + @Override + public void runCurrentGenTasksUntilBusy(DhBlockPos2D targetPos) { while (generatorClosingFuture == null && networkState.client().isReady() @@ -81,20 +81,42 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue } } + @Override + public void cancelGenTasks(Iterable positions) + { + for (DhSectionPos pos : positions) + { + WorldGenQueueEntry entry = waitingTasks.remove(pos); + if (entry != null) + { + if (entry.request != null) + entry.request.cancel(false); + entry.future.cancel(false); + } + } + } + private void sendNewRequest(DhBlockPos2D targetPos) { - DhSectionPos sectionPos = Objects.requireNonNull(waitingTasks.keySet().stream().reduce(null, (a, b) - -> a != null - && a.getCenter().getCenterBlockPos().distSquared(targetPos) - < b.getCenter().getCenterBlockPos().distSquared(targetPos) - ? a : b)); - WorldGenQueueEntry entry = waitingTasks.remove(sectionPos); + Map.Entry mapEntry = waitingTasks.entrySet().stream() + .filter(task -> task.getValue().request == null) + .reduce(null, (a, b) + -> a != null + && a.getKey().getCenter().getCenterBlockPos().distSquared(targetPos) + < b.getKey().getCenter().getCenterBlockPos().distSquared(targetPos) + ? a : b); + if (mapEntry == null) + { + pendingTasksSemaphore.release(); + return; + } - CompletableFuture request = this.networkState.client().sendRequest(new FullDataSourceRequestMessage(sectionPos)); - pendingRequests.add(request); - request.handle((response, throwable) -> + DhSectionPos sectionPos = mapEntry.getKey(); + WorldGenQueueEntry entry = mapEntry.getValue(); + + entry.request = this.networkState.client().sendRequest(new FullDataSourceRequestMessage(sectionPos)); + entry.request.handle((response, throwable) -> { - pendingRequests.remove(request); pendingTasksSemaphore.release(); finishedRequests.incrementAndGet(); @@ -103,11 +125,20 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue if (throwable != null) throw throwable; + waitingTasks.remove(sectionPos); LOGGER.info("FullDataSourceResponseMessage " + sectionPos); CompleteFullDataSource fullDataSource = response.getFullDataSource(sectionPos, level); + // Check is dimension has been switched - received data may no longer be relevant + if (fullDataSource == null) + throw new CancellationException(); + Consumer chunkDataConsumer = entry.tracker.getChunkDataConsumer(); + // FIXME Who decided it was a good idea to use weak references for cancellation purposes? + if (chunkDataConsumer == null) + return entry.future.cancel(false); + sectionPos.forEachChildAtLevel(LodUtil.CHUNK_DETAIL_LEVEL, childPos -> { ChunkSizedFullDataAccessor accessor = new ChunkSizedFullDataAccessor(new DhChunkPos(childPos.sectionX, childPos.sectionZ)); @@ -123,24 +154,27 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue chunkDataConsumer.accept(accessor); }); - - entry.future.complete(WorldGenResult.CreateSuccess(sectionPos)); } - catch (CancellationException | ChannelException | RateLimitedException e) + catch (ChannelException | RateLimitedException e) { if (e instanceof RateLimitedException) LOGGER.warn("Rate limited by server, re-queueing task ["+sectionPos+"]: "+e.getMessage()); finishedRequests.decrementAndGet(); - waitingTasks.put(sectionPos, entry); + } + catch (CancellationException ignored) + { + finishedRequests.decrementAndGet(); + totalRequests.decrementAndGet(); } catch (Throwable e) { LOGGER.error("Error while fetching full data source", e); failedRequests.incrementAndGet(); - entry.future.complete(WorldGenResult.CreateFail()); + return entry.future.complete(WorldGenResult.CreateFail()); } - return null; + + return entry.future.complete(WorldGenResult.CreateSuccess(sectionPos)); }); } @@ -153,24 +187,24 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue return lines.toArray(new String[0]); } - @Override public CompletableFuture startClosing(boolean cancelCurrentGeneration, boolean alsoInterruptRunning) + @Override + public CompletableFuture startClosing(boolean cancelCurrentGeneration, boolean alsoInterruptRunning) { return this.generatorClosingFuture = CompletableFuture.runAsync(() -> { while (!pendingTasksSemaphore.tryAcquire(Short.MAX_VALUE)) - { - for (CompletableFuture request : pendingRequests) - request.cancel(false); - } - - if (cancelCurrentGeneration) { for (WorldGenQueueEntry entry : this.waitingTasks.values()) + { + if (entry.request != null) + entry.request.cancel(alsoInterruptRunning); entry.future.cancel(alsoInterruptRunning); + } } }); } - @Override public void close() + @Override + public void close() { f3Message.close(); } @@ -179,6 +213,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue { public CompletableFuture future; public IWorldGenTaskTracker tracker; + public CompletableFuture request; public WorldGenQueueEntry(CompletableFuture future, IWorldGenTaskTracker tracker) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/IWorldGenTaskTracker.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/IWorldGenTaskTracker.java index d8704cc83..4f7e6bc4e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/IWorldGenTaskTracker.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/IWorldGenTaskTracker.java @@ -2,6 +2,7 @@ package com.seibel.distanthorizons.core.generation.tasks; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; +import javax.annotation.Nullable; import java.util.function.Consumer; /** @@ -13,5 +14,6 @@ public interface IWorldGenTaskTracker /** Returns true if the task hasn't been garbage collected. */ boolean isMemoryAddressValid(); + @Nullable Consumer getChunkDataConsumer(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 710337b95..c4b7c7240 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -29,6 +29,7 @@ import org.apache.logging.log4j.Logger; import javax.annotation.CheckForNull; import java.util.Iterator; +import java.util.Map; import java.util.Set; import java.util.concurrent.*; @@ -43,7 +44,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel private final ConcurrentLinkedQueue worldGenLoopingQueue = new ConcurrentLinkedQueue<>(); private final ConcurrentMap incompleteDataSources = new ConcurrentHashMap<>(); - private final ConcurrentMap fullDataRequests = new ConcurrentHashMap<>(); + private final ConcurrentMap fullDataRequests = new ConcurrentHashMap<>(); private final AppliedConfigState rateLimitConfig = new AppliedConfigState<>(Config.Client.Advanced.Multiplayer.serverNetworkingRateLimit); @@ -91,8 +92,8 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel // If this fails, current entry is being drained and need create another one if (entry.requestCollectionSemaphore.tryAcquire()) { - fullDataRequests.put(msg.futureId, msg); - entry.requestMessages.add(msg); + fullDataRequests.put(msg.futureId, entry); + entry.requestMessages.put(msg.futureId, msg); entry.requestCollectionSemaphore.release(); break; } @@ -101,9 +102,20 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel this.eventSource.registerHandler(CancelMessage.class, msg -> { - this.fullDataRequests.remove(msg.futureId); - ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); - serverPlayerState.pendingFullDataRequests.decrementAndGet(); + IncompleteDataSourceEntry entry = this.fullDataRequests.remove(msg.futureId); + if (entry == null) return; + FullDataSourceRequestMessage requestMessage = entry.requestMessages.remove(msg.futureId); + + remotePlayerConnectionHandler.getConnectedPlayer(msg).pendingFullDataRequests.decrementAndGet(); + + entry.requestCollectionSemaphore.acquireUninterruptibly(Short.MAX_VALUE); + if (entry.requestMessages.isEmpty()) + { + incompleteDataSources.remove(requestMessage.dhSectionPos); + serverside.dataFileHandler.removeGenRequestIf(pos -> pos == requestMessage.dhSectionPos); + } + + entry.requestCollectionSemaphore.release(Short.MAX_VALUE); }); } @@ -127,32 +139,32 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel { chunkToLodBuilder.tick(); - for (Iterator it = incompleteDataSources.values().iterator(); it.hasNext(); ) + for (Map.Entry mapEntry : incompleteDataSources.entrySet()) { - IncompleteDataSourceEntry entry = it.next(); - if (entry.fullDataSource == null) continue; + IncompleteDataSourceEntry entry = mapEntry.getValue(); + + if (entry.fullDataSource == null) + continue; if (entry.fullDataSource instanceof IIncompleteFullDataSource) { IIncompleteFullDataSource incompleteSource = (IIncompleteFullDataSource) entry.fullDataSource; - if (!incompleteSource.hasBeenPromoted()) continue; + if (!incompleteSource.hasBeenPromoted()) + continue; entry.fullDataSource = incompleteSource.tryPromotingToCompleteDataSource(); } LodUtil.assertTrue(entry.fullDataSource instanceof CompleteFullDataSource, "Invalid full data source"); + incompleteDataSources.remove(mapEntry.getKey()); - it.remove(); // This semaphore is intentionally acquired forever entry.requestCollectionSemaphore.acquireUninterruptibly(Short.MAX_VALUE); CompleteFullDataSource completeSource = (CompleteFullDataSource) entry.fullDataSource; - for (FullDataSourceRequestMessage msg : entry.requestMessages) + for (FullDataSourceRequestMessage msg : entry.requestMessages.values()) { ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); - if (serverPlayerState == null) continue; - - // Check if cancelled - if (this.fullDataRequests.remove(msg.futureId) == null) + if (serverPlayerState == null) continue; serverPlayerState.pendingFullDataRequests.decrementAndGet(); @@ -242,7 +254,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel { @CheckForNull public IFullDataSource fullDataSource; - public final Set requestMessages = ConcurrentHashMap.newKeySet(); + public final ConcurrentMap requestMessages = new ConcurrentHashMap<>(); public final Semaphore requestCollectionSemaphore = new Semaphore(Short.MAX_VALUE, true); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index 91179edbe..09f9ee576 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -8,6 +8,7 @@ import com.seibel.distanthorizons.core.network.messages.CancelMessage; import com.seibel.distanthorizons.core.network.messages.ExceptionMessage; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; +import io.netty.channel.ChannelException; import io.netty.channel.ChannelHandlerContext; import org.apache.logging.log4j.Logger; @@ -89,7 +90,8 @@ public abstract class NetworkEventSource CompletableFuture responseFuture = new CompletableFuture<>(); responseFuture.handle((response, throwable) -> { - pendingFutures.remove(ctx, msg.futureId); + if (!(throwable instanceof ChannelException)) + pendingFutures.remove(ctx, msg.futureId); if (throwable instanceof CancellationException) msg.sendResponse(new CancelMessage()); @@ -126,6 +128,6 @@ public abstract class NetworkEventSource public void close() { this.handlers.clear(); - completeAllFuturesExceptionally(new Exception(this.getClass().getSimpleName()+" is closed.")); + completeAllFuturesExceptionally(new ChannelException(this.getClass().getSimpleName()+" is closed.")); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java index 6806a2364..d8f550fe7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java @@ -12,6 +12,7 @@ import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStre import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufInputStream; +import javax.annotation.Nullable; import java.io.ByteArrayOutputStream; import java.io.IOException; @@ -20,6 +21,7 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage private CompleteFullDataSource fullDataSource; private DhServerLevel level; + private int levelHashCode; private CompleteFullDataSourceLoader fullDataSourceLoader; private ByteBuf dataBuffer; @@ -28,6 +30,9 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage { this.fullDataSource = fullDataSource; this.level = level; + + // TODO Multiverse support + this.levelHashCode = level.getLevelWrapper().getDimensionType().getDimensionName().hashCode(); } @Override @@ -39,6 +44,7 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage fullDataSource.writeToStream(dhOutputStream, level); dhOutputStream.flush(); + out.writeInt(levelHashCode); out.writeByte(fullDataSource.getBinaryDataFormatVersion()); out.writeInt(outputStream.size()); out.writeBytes(outputStream.toByteArray()); @@ -48,16 +54,19 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage @Override public void decode0(ByteBuf in) { + levelHashCode = in.readInt(); byte dataVersion = in.readByte(); - this.fullDataSourceLoader = (CompleteFullDataSourceLoader) AbstractFullDataSourceLoader.getLoader(CompleteFullDataSource.TYPE_ID, dataVersion); - assert this.fullDataSourceLoader != null; - this.dataBuffer = in.readBytes(in.readInt()); } + @Nullable public CompleteFullDataSource getFullDataSource(DhSectionPos pos, IDhLevel level) throws IOException, InterruptedException { + // TODO Multiverse support + if (levelHashCode != level.getLevelWrapper().getDimensionType().getDimensionName().hashCode()) + return null; + try (ByteBufInputStream inputStream = new ByteBufInputStream(dataBuffer)) { return fullDataSourceLoader.loadData(pos, new DhDataInputStream(inputStream), level); From 04fc4aa6765766440e387d08e8c964ade3674ec9 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 4 Aug 2023 21:51:53 +0500 Subject: [PATCH 022/205] Remove unnecessary logging --- .../core/generation/WorldRemoteGenerationQueue.java | 2 +- .../com/seibel/distanthorizons/core/level/DhServerLevel.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 3947965ae..0597ec47b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -126,7 +126,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue throw throwable; waitingTasks.remove(sectionPos); - LOGGER.info("FullDataSourceResponseMessage " + sectionPos); + LOGGER.debug("FullDataSourceResponseMessage " + sectionPos); CompleteFullDataSource fullDataSource = response.getFullDataSource(sectionPos, level); // Check is dimension has been switched - received data may no longer be relevant diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index c4b7c7240..c6c0f83b2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -71,7 +71,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel if (serverPlayerState.serverPlayer.getLevel() != this.serverLevelWrapper) return; - LOGGER.info("FullDataSourceRequestMessage received at pos ({}, {}) with detail level {}", msg.dhSectionPos.sectionX, msg.dhSectionPos.sectionZ, msg.dhSectionPos.sectionDetailLevel); + LOGGER.debug("FullDataSourceRequestMessage received at pos ({}, {}) with detail level {}", msg.dhSectionPos.sectionX, msg.dhSectionPos.sectionZ, msg.dhSectionPos.sectionDetailLevel); if (serverPlayerState.pendingFullDataRequests.incrementAndGet() > rateLimitConfig.get()) { From 9bd432ad7f2e8a52cf3806402dc90aff212a911e Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 4 Aug 2023 22:12:09 +0500 Subject: [PATCH 023/205] Fix memory leak related to requests --- .../com/seibel/distanthorizons/core/level/DhServerLevel.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index c6c0f83b2..06e47faa7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -163,6 +163,8 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel CompleteFullDataSource completeSource = (CompleteFullDataSource) entry.fullDataSource; for (FullDataSourceRequestMessage msg : entry.requestMessages.values()) { + this.fullDataRequests.remove(msg.futureId); + ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); if (serverPlayerState == null) continue; From 6bf32ff85ce65849481369d423815291130e1ac8 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 5 Aug 2023 22:30:33 +0500 Subject: [PATCH 024/205] Add debug renderer for remote generation queue --- .../WorldRemoteGenerationQueue.java | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 0597ec47b..0daaec15f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -15,17 +15,21 @@ import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhLodPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; +import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import io.netty.channel.ChannelException; import org.apache.logging.log4j.Logger; +import javax.annotation.CheckForNull; +import java.awt.*; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; -public class WorldRemoteGenerationQueue implements IWorldGenerationQueue +public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebugRenderable { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); @@ -47,6 +51,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue { this.networkState = networkState; this.level = level; + DebugRenderer.register(this); } @Override @@ -117,6 +122,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue entry.request = this.networkState.client().sendRequest(new FullDataSourceRequestMessage(sectionPos)); entry.request.handle((response, throwable) -> { + entry.request = null; pendingTasksSemaphore.release(); finishedRequests.incrementAndGet(); @@ -209,10 +215,23 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue f3Message.close(); } + @Override + public void debugRender(DebugRenderer r) { + for (Map.Entry mapEntry : waitingTasks.entrySet()) + { + r.renderBox(new DebugRenderer.Box(mapEntry.getKey(), -32f, 64f, 0.05f, + mapEntry.getValue().request != null + ? Color.red + : Color.blue + )); + } + } + private static class WorldGenQueueEntry { - public CompletableFuture future; - public IWorldGenTaskTracker tracker; + public final CompletableFuture future; + public final IWorldGenTaskTracker tracker; + @CheckForNull public CompletableFuture request; public WorldGenQueueEntry(CompletableFuture future, IWorldGenTaskTracker tracker) From 0c155ac8cde8800892ef1c3497e255ee045fa325 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 13 Aug 2023 18:59:08 +0500 Subject: [PATCH 025/205] Generation task prioritization (loaded > unloaded > ungenerated) --- .../fullData/FullDataPointIdMap.java | 4 +- .../fullDatafile/FullDataFileHandler.java | 12 ++ .../WorldRemoteGenerationQueue.java | 199 ++++++++++++------ .../core/level/DhClientLevel.java | 3 +- .../core/level/DhServerLevel.java | 20 +- .../core/multiplayer/ClientNetworkState.java | 29 ++- .../core/network/IClientRequestHandler.java | 19 ++ .../core/network/NetworkClient.java | 5 +- .../core/network/NetworkEventSource.java | 5 +- .../network/messages/CloseReasonMessage.java | 4 +- .../network/messages/ExceptionMessage.java | 4 +- .../FullDataSourceRequestMessage.java | 6 +- .../GenTaskPriorityRequestMessage.java | 31 +++ .../GenTaskPriorityResponseMessage.java | 32 +++ .../messages/RemotePlayerConfigMessage.java | 2 +- .../FutureTrackableNetworkMessage.java | 6 +- .../core/network/protocol/INetworkObject.java | 101 ++++++++- .../core/network/protocol/MessageDecoder.java | 2 +- .../network/protocol/MessageRegistry.java | 13 +- .../core/pos/DhSectionPos.java | 2 + .../wrapperInterfaces/IWrapperFactory.java | 2 +- .../block/IBlockStateWrapper.java | 3 +- 22 files changed, 395 insertions(+), 109 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/IClientRequestHandler.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/GenTaskPriorityRequestMessage.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/GenTaskPriorityResponseMessage.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java index 589d327f1..fa9a49b2a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java @@ -161,7 +161,7 @@ public class FullDataPointIdMap } - public String serialize(ILevelWrapper levelWrapper) { return this.biome.serialize(levelWrapper) + SEPARATOR_STRING + this.blockState.serialize(); } + public String serialize(ILevelWrapper levelWrapper) { return this.biome.serialize(levelWrapper) + SEPARATOR_STRING + this.blockState.serialize(levelWrapper); } public static Entry deserialize(String str, ILevelWrapper levelWrapper) throws IOException, InterruptedException { @@ -178,7 +178,7 @@ public class FullDataPointIdMap } IBiomeWrapper biome = WRAPPER_FACTORY.deserializeBiomeWrapper(stringArray[0], levelWrapper); - IBlockStateWrapper blockState = WRAPPER_FACTORY.deserializeBlockStateWrapper(stringArray[1]); + IBlockStateWrapper blockState = WRAPPER_FACTORY.deserializeBlockStateWrapper(stringArray[1], levelWrapper); return new Entry(biome, blockState); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java index b1ddbda49..348529f13 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java @@ -43,6 +43,18 @@ public class FullDataFileHandler implements IFullDataSourceProvider private final ConcurrentHashMap unloadedFiles = new ConcurrentHashMap<>(); private final ConcurrentHashMap fileBySectionPos = new ConcurrentHashMap<>(); public void ForEachFile(Consumer consumer) { this.fileBySectionPos.values().forEach(consumer); } + public Map getLoadStates(Iterable posList) + { + HashMap map = new HashMap<>(); + for (DhSectionPos pos : posList) + { + map.put(pos, + fileBySectionPos.containsKey(pos) ? 3 // Loaded + : unloadedFiles.containsKey(pos) ? 2 // Unloaded + : 1); // Not generated + } + return map; + } private LinkedList> onUpdatedListeners = new LinkedList<>(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 0daaec15f..c70a0b05f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -11,6 +11,8 @@ import com.seibel.distanthorizons.core.multiplayer.ClientNetworkState; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.messages.FullDataSourceRequestMessage; import com.seibel.distanthorizons.core.network.messages.FullDataSourceResponseMessage; +import com.seibel.distanthorizons.core.network.messages.GenTaskPriorityRequestMessage; +import com.seibel.distanthorizons.core.network.messages.GenTaskPriorityResponseMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhLodPos; @@ -25,9 +27,11 @@ import org.apache.logging.log4j.Logger; import javax.annotation.CheckForNull; import java.awt.*; import java.util.*; +import java.util.List; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; +import java.util.stream.Collectors; public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebugRenderable { @@ -42,6 +46,9 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug private final Semaphore pendingTasksSemaphore = new Semaphore(Short.MAX_VALUE, true); private int pendingTasks() { return Short.MAX_VALUE - pendingTasksSemaphore.availablePermits(); } + private CompletableFuture genTaskPriorityRequest = CompletableFuture.completedFuture(null); + private final Semaphore genTaskPriorityRequestSemaphore = new Semaphore(1, true); + private final F3Screen.NestedMessage f3Message = new F3Screen.NestedMessage(this::f3Log); private final AtomicInteger finishedRequests = new AtomicInteger(); private final AtomicInteger totalRequests = new AtomicInteger(); @@ -73,17 +80,62 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug return entry.future; } + private int posDistanceSquared(DhBlockPos2D targetPos, DhSectionPos pos) + { + return (int) pos.getCenter().getCenterBlockPos().distSquared(targetPos); + } + @Override public void runCurrentGenTasksUntilBusy(DhBlockPos2D targetPos) { - while (generatorClosingFuture == null - && networkState.client().isReady() - && !waitingTasks.isEmpty() + if (generatorClosingFuture != null || !networkState.getClient().isReady()) return; + + while (!waitingTasks.isEmpty() && pendingTasks() < this.networkState.config.fullDataRequestRateLimit && pendingTasksSemaphore.tryAcquire()) { sendNewRequest(targetPos); } + + if (genTaskPriorityRequestSemaphore.tryAcquire()) { + List posList = waitingTasks.entrySet().stream() + .filter(task -> task.getValue().request == null && task.getValue().priority == 0) + .sorted((x, y) -> posDistanceSquared(targetPos, x.getKey()) - posDistanceSquared(targetPos, y.getKey())) + .limit(this.networkState.config.fullDataRequestRateLimit) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + if (posList.isEmpty()) { + genTaskPriorityRequestSemaphore.release(); + return; + }; + + CompletableFuture request = this.networkState.getClient().sendRequest(new GenTaskPriorityRequestMessage(posList)); + genTaskPriorityRequest = request; + request.handleAsync((response, throwable) -> { + try + { + if (throwable != null) + throw throwable; + + for (Map.Entry mapEntry : response.posList.entrySet()) + { + WorldGenQueueEntry entry = waitingTasks.get(mapEntry.getKey()); + if (entry != null) + entry.priority = mapEntry.getValue(); + } + } + catch (ChannelException | CancellationException ignored) + { + } + catch (Throwable e) + { + LOGGER.error("Error while fetching gen task priorities", e); + } + + genTaskPriorityRequestSemaphore.release(); + return null; + }); + } } @Override @@ -106,10 +158,10 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug Map.Entry mapEntry = waitingTasks.entrySet().stream() .filter(task -> task.getValue().request == null) .reduce(null, (a, b) - -> a != null - && a.getKey().getCenter().getCenterBlockPos().distSquared(targetPos) - < b.getKey().getCenter().getCenterBlockPos().distSquared(targetPos) - ? a : b); + -> a == null + || b.getValue().priority > a.getValue().priority + || posDistanceSquared(targetPos, b.getKey()) < posDistanceSquared(targetPos, a.getKey()) + ? b : a); if (mapEntry == null) { pendingTasksSemaphore.release(); @@ -119,69 +171,71 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug DhSectionPos sectionPos = mapEntry.getKey(); WorldGenQueueEntry entry = mapEntry.getValue(); - entry.request = this.networkState.client().sendRequest(new FullDataSourceRequestMessage(sectionPos)); - entry.request.handle((response, throwable) -> - { - entry.request = null; - pendingTasksSemaphore.release(); - finishedRequests.incrementAndGet(); + CompletableFuture request = this.networkState.getClient().sendRequest(new FullDataSourceRequestMessage(sectionPos)); + entry.request = request; + request.handleAsync((response, throwable) -> + { + pendingTasksSemaphore.release(); + finishedRequests.incrementAndGet(); + + try + { + if (throwable != null) + throw throwable; + + waitingTasks.remove(sectionPos); + LOGGER.debug("FullDataSourceResponseMessage " + sectionPos); + CompleteFullDataSource fullDataSource = response.getFullDataSource(sectionPos, level); + + // FIXME Add dimension context to request instead + // Check is dimension has been switched - received data may no longer be relevant + if (fullDataSource == null) + throw new CancellationException(); + + Consumer chunkDataConsumer = entry.tracker.getChunkDataConsumer(); + + // FIXME Why keeping a reference in first place + if (chunkDataConsumer == null) + return entry.future.cancel(false); + + sectionPos.forEachChildAtLevel(LodUtil.CHUNK_DETAIL_LEVEL, childPos -> { + ChunkSizedFullDataAccessor accessor = new ChunkSizedFullDataAccessor(new DhChunkPos(childPos.sectionX, childPos.sectionZ)); - try - { - if (throwable != null) - throw throwable; - - waitingTasks.remove(sectionPos); - LOGGER.debug("FullDataSourceResponseMessage " + sectionPos); - CompleteFullDataSource fullDataSource = response.getFullDataSource(sectionPos, level); - - // Check is dimension has been switched - received data may no longer be relevant - if (fullDataSource == null) - throw new CancellationException(); - - Consumer chunkDataConsumer = entry.tracker.getChunkDataConsumer(); - - // FIXME Who decided it was a good idea to use weak references for cancellation purposes? - if (chunkDataConsumer == null) - return entry.future.cancel(false); - - sectionPos.forEachChildAtLevel(LodUtil.CHUNK_DETAIL_LEVEL, childPos -> { - ChunkSizedFullDataAccessor accessor = new ChunkSizedFullDataAccessor(new DhChunkPos(childPos.sectionX, childPos.sectionZ)); - - int detailLevelDifference = sectionPos.sectionDetailLevel - childPos.sectionDetailLevel; - int childRelativeX = childPos.sectionX - sectionPos.sectionX * BitShiftUtil.powerOfTwo(detailLevelDifference); - int childRelativeZ = childPos.sectionZ - sectionPos.sectionZ * BitShiftUtil.powerOfTwo(detailLevelDifference); - - fullDataSource.subView( - LodUtil.CHUNK_WIDTH, - childRelativeX * LodUtil.CHUNK_WIDTH, - childRelativeZ * LodUtil.CHUNK_WIDTH - ).shadowCopyTo(accessor); - - chunkDataConsumer.accept(accessor); - }); - } - catch (ChannelException | RateLimitedException e) - { - if (e instanceof RateLimitedException) - LOGGER.warn("Rate limited by server, re-queueing task ["+sectionPos+"]: "+e.getMessage()); - - finishedRequests.decrementAndGet(); - } - catch (CancellationException ignored) - { - finishedRequests.decrementAndGet(); - totalRequests.decrementAndGet(); - } - catch (Throwable e) - { - LOGGER.error("Error while fetching full data source", e); - failedRequests.incrementAndGet(); - return entry.future.complete(WorldGenResult.CreateFail()); - } + int detailLevelDifference = sectionPos.sectionDetailLevel - childPos.sectionDetailLevel; + int childRelativeX = childPos.sectionX - sectionPos.sectionX * BitShiftUtil.powerOfTwo(detailLevelDifference); + int childRelativeZ = childPos.sectionZ - sectionPos.sectionZ * BitShiftUtil.powerOfTwo(detailLevelDifference); - return entry.future.complete(WorldGenResult.CreateSuccess(sectionPos)); + fullDataSource.subView( + LodUtil.CHUNK_WIDTH, + childRelativeX * LodUtil.CHUNK_WIDTH, + childRelativeZ * LodUtil.CHUNK_WIDTH + ).shadowCopyTo(accessor); + + chunkDataConsumer.accept(accessor); }); + } + catch (ChannelException | RateLimitedException e) + { + if (e instanceof RateLimitedException) + LOGGER.warn("Rate limited by server, re-queueing task [" + sectionPos + "]: " + e.getMessage()); + + entry.request = null; + finishedRequests.decrementAndGet(); + } + catch (CancellationException ignored) + { + finishedRequests.decrementAndGet(); + totalRequests.decrementAndGet(); + } + catch (Throwable e) + { + LOGGER.error("Error while fetching full data source", e); + failedRequests.incrementAndGet(); + return entry.future.complete(WorldGenResult.CreateFail()); + } + + return entry.future.complete(WorldGenResult.CreateSuccess(sectionPos)); + }); } private String[] f3Log() @@ -197,6 +251,11 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug public CompletableFuture startClosing(boolean cancelCurrentGeneration, boolean alsoInterruptRunning) { return this.generatorClosingFuture = CompletableFuture.runAsync(() -> { + while (!genTaskPriorityRequestSemaphore.tryAcquire()) + { + genTaskPriorityRequest.cancel(false); + } + while (!pendingTasksSemaphore.tryAcquire(Short.MAX_VALUE)) { for (WorldGenQueueEntry entry : this.waitingTasks.values()) @@ -231,8 +290,12 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug { public final CompletableFuture future; public final IWorldGenTaskTracker tracker; + + // Higher value = higher priority. + // Priority of 0 is reserved for unassigned value + public int priority = 0; @CheckForNull - public CompletableFuture request; + public CompletableFuture request; public WorldGenQueueEntry(CompletableFuture future, IWorldGenTaskTracker tracker) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 24fe4253d..38055b266 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -8,7 +8,6 @@ import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.generation.WorldRemoteGenerationQueue; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.multiplayer.ClientNetworkState; -import com.seibel.distanthorizons.core.network.NetworkClient; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhSectionPos; @@ -83,7 +82,7 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel public void doWorldGen() { - boolean isClientUsable = networkState != null && !networkState.client().isClosed(); + boolean isClientUsable = networkState != null && !networkState.getClient().isClosed(); boolean shouldDoWorldGen = isClientUsable && clientside.isRendering(); boolean isWorldGenRunning = worldGenModule.isWorldGenRunning(); if (shouldDoWorldGen && !isWorldGenRunning) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 06e47faa7..cc98e20eb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -13,9 +13,7 @@ import com.seibel.distanthorizons.core.multiplayer.RemotePlayerConnectionHandler import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkServer; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; -import com.seibel.distanthorizons.core.network.messages.CancelMessage; -import com.seibel.distanthorizons.core.network.messages.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.FullDataSourceResponseMessage; +import com.seibel.distanthorizons.core.network.messages.*; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhLodPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; @@ -65,6 +63,12 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel private void registerNetworkHandlers() { + // TODO implement transparent message handling restriction by level + // workaround: +// ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); +// if (serverPlayerState.serverPlayer.getLevel() != this.serverLevelWrapper) +// return; + this.eventSource.registerHandler(FullDataSourceRequestMessage.class, msg -> { ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); @@ -100,6 +104,16 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel } }); + this.eventSource.registerHandler(GenTaskPriorityRequestMessage.class, msg -> { + ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); + if (serverPlayerState.serverPlayer.getLevel() != this.serverLevelWrapper) + return; + + msg.sendResponse(new GenTaskPriorityResponseMessage( + this.serverside.dataFileHandler.getLoadStates(msg.posList) + )); + }); + this.eventSource.registerHandler(CancelMessage.class, msg -> { IncompleteDataSourceEntry entry = this.fullDataRequests.remove(msg.futureId); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java index ce05ac121..41744ecfd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java @@ -2,6 +2,7 @@ package com.seibel.distanthorizons.core.multiplayer; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.network.IClientRequestHandler; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkClient; import com.seibel.distanthorizons.core.network.messages.AckMessage; @@ -17,29 +18,40 @@ public class ClientNetworkState implements Closeable { protected static final Logger LOGGER = DhLoggerBuilder.getLogger(); - private final ScopedNetworkEventSource eventSource; + private final NetworkClient client; private final UUID playerUUID; public MultiplayerConfig config = new MultiplayerConfig(); - public NetworkClient client() { return this.eventSource.parent; } + /** + * Returns the client used by this instance.

+ * If you need to subscribe to any packet events, create an instance of {@link ScopedNetworkEventSource} using the returned instance. + */ + public IClientRequestHandler getClient() { return this.client; } + /** + * Constructs a new instance. + * + * @param networkClient Client to use. It is assumed that this client will be at full control by this instance. + * @param playerUUID UUID of a player connected + */ public ClientNetworkState(NetworkClient networkClient, UUID playerUUID) { - this.eventSource = new ScopedNetworkEventSource<>(networkClient); + this.client = networkClient; this.playerUUID = playerUUID; this.registerNetworkHandlers(); - this.client().startConnecting(); + this.client.startConnecting(); } private void registerNetworkHandlers() { - this.client().registerHandler(HelloMessage.class, helloMessage -> + this.client.registerHandler(HelloMessage.class, helloMessage -> { LOGGER.info("Connected to server: "+helloMessage.getChannelContext().channel().remoteAddress()); - this.client().sendRequest(new PlayerUUIDMessage(playerUUID)) - .thenCompose(ack -> this.client().sendRequest(new RemotePlayerConfigMessage(new MultiplayerConfig() + this.getClient().sendRequest(new PlayerUUIDMessage(playerUUID)) + .thenCompose(ack -> this.getClient().sendRequest(new RemotePlayerConfigMessage(new MultiplayerConfig() {{ + renderDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistance.get(); fullDataRequestRateLimit = Config.Client.Advanced.Multiplayer.serverNetworkingRateLimit.get(); }}))) .thenAccept(msg -> { @@ -54,7 +66,6 @@ public class ClientNetworkState implements Closeable public void close() { - this.eventSource.close(); - this.client().close(); + this.client.close(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/IClientRequestHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/IClientRequestHandler.java new file mode 100644 index 000000000..a91b03798 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/IClientRequestHandler.java @@ -0,0 +1,19 @@ +package com.seibel.distanthorizons.core.network; + +import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; + +import java.util.concurrent.CompletableFuture; + +public interface IClientRequestHandler +{ + /** Indicates whether the client is initialized and not started connecting yet. */ + boolean isInitialState(); + /** Indicates whether the client is closed(-ing) and should not be used. */ + boolean isClosed(); + /** Indicates whether the connection is established and first message is sent. */ + boolean isReady(); + + /** Sends a new request. */ + CompletableFuture sendRequest(FutureTrackableNetworkMessage msg); +} + diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index b79d5997f..8eb4e5a19 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -19,7 +19,7 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; -public class NetworkClient extends NetworkEventSource implements AutoCloseable +public class NetworkClient extends NetworkEventSource implements IClientRequestHandler, AutoCloseable { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); @@ -102,6 +102,8 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable // FIXME sometimes this causes the MC connection to crash // this might happen if the URL can't be converted to a IP (IE UnknownHostException) ChannelFuture connectFuture = this.clientBootstrap.connect(this.address); + this.channel = connectFuture.channel(); + connectFuture.addListener((ChannelFuture channelFuture) -> { if (!channelFuture.isSuccess()) @@ -114,7 +116,6 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable ready = true; }); - this.channel = connectFuture.channel(); this.channel.closeFuture().addListener((ChannelFuture channelFuture) -> { ready = false; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index 09f9ee576..0d6daaa23 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -66,7 +66,10 @@ public abstract class NetworkEventSource if (!handled) { - LOGGER.warn("Unhandled message type: " + message.getClass().getSimpleName()); + String error = "Unhandled message type: " + message.getClass().getSimpleName(); + if (message instanceof FutureTrackableNetworkMessage) + error += ", future id: " + ((FutureTrackableNetworkMessage) message).futureId; + LOGGER.warn(error); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseReasonMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseReasonMessage.java index 2a87bb4af..0e7e9b640 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseReasonMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseReasonMessage.java @@ -14,9 +14,9 @@ public class CloseReasonMessage extends NetworkMessage public CloseReasonMessage(String reason) { this.reason = reason; } @Override - public void encode(ByteBuf out) { INetworkObject.encodeString(this.reason, out); } + public void encode(ByteBuf out) { encodeString(this.reason, out); } @Override - public void decode(ByteBuf in) { this.reason = INetworkObject.decodeString(in); } + public void decode(ByteBuf in) { this.reason = decodeString(in); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ExceptionMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ExceptionMessage.java index b27fba497..4496fd2df 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ExceptionMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ExceptionMessage.java @@ -27,13 +27,13 @@ public class ExceptionMessage extends FutureTrackableNetworkMessage @Override protected void encode0(ByteBuf out) { out.writeInt(exceptionMap.indexOf(exception.getClass())); - INetworkObject.encodeString(exception.getMessage(), out); + encodeString(exception.getMessage(), out); } @Override protected void decode0(ByteBuf in) throws Exception { int id = in.readInt(); - String message = INetworkObject.decodeString(in); + String message = decodeString(in); exception = exceptionMap.get(id).getDeclaredConstructor(String.class).newInstance(message); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceRequestMessage.java index 1185f265e..e05f518d5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceRequestMessage.java @@ -1,14 +1,10 @@ package com.seibel.distanthorizons.core.network.messages; -import com.google.common.collect.MapMaker; -import com.seibel.distanthorizons.core.level.DhClientLevel; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import com.seibel.distanthorizons.core.pos.DhSectionPos; import io.netty.buffer.ByteBuf; -import java.util.concurrent.ConcurrentMap; - public class FullDataSourceRequestMessage extends FutureTrackableNetworkMessage { public DhSectionPos dhSectionPos; @@ -28,6 +24,6 @@ public class FullDataSourceRequestMessage extends FutureTrackableNetworkMessage @Override public void decode0(ByteBuf in) { - dhSectionPos = INetworkObject.decode(new DhSectionPos((byte)0, 0, 0), in); + dhSectionPos = INetworkObject.decodeStatic(DhSectionPos.zero(), in); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/GenTaskPriorityRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/GenTaskPriorityRequestMessage.java new file mode 100644 index 000000000..45be3c077 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/GenTaskPriorityRequestMessage.java @@ -0,0 +1,31 @@ +package com.seibel.distanthorizons.core.network.messages; + +import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.pos.DhSectionPos; +import io.netty.buffer.ByteBuf; + +import java.util.ArrayList; +import java.util.List; + +public class GenTaskPriorityRequestMessage extends FutureTrackableNetworkMessage +{ + public List posList = new ArrayList<>(); + + public GenTaskPriorityRequestMessage() { } + public GenTaskPriorityRequestMessage(List posList) + { + this.posList = posList; + } + + @Override + protected void encode0(ByteBuf out) + { + encodeCollection(out, posList); + } + + @Override + protected void decode0(ByteBuf in) + { + decodeCollection(in, posList, DhSectionPos::zero); + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/GenTaskPriorityResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/GenTaskPriorityResponseMessage.java new file mode 100644 index 000000000..706309699 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/GenTaskPriorityResponseMessage.java @@ -0,0 +1,32 @@ +package com.seibel.distanthorizons.core.network.messages; + +import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.pos.DhSectionPos; +import io.netty.buffer.ByteBuf; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class GenTaskPriorityResponseMessage extends FutureTrackableNetworkMessage +{ + public Map posList = new HashMap<>(); + + public GenTaskPriorityResponseMessage() { } + public GenTaskPriorityResponseMessage(Map posList) + { + this.posList = posList; + } + + @Override + protected void encode0(ByteBuf out) + { + encodeCollection(out, posList.entrySet()); + } + + @Override + protected void decode0(ByteBuf in) + { + decodeMap(in, posList, DhSectionPos::zero, () -> 0); + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/RemotePlayerConfigMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/RemotePlayerConfigMessage.java index dbc874f7c..ace46ac98 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/RemotePlayerConfigMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/RemotePlayerConfigMessage.java @@ -18,6 +18,6 @@ public class RemotePlayerConfigMessage extends FutureTrackableNetworkMessage public void encode0(ByteBuf out) { this.payload.encode(out); } @Override - public void decode0(ByteBuf in) { this.payload = INetworkObject.decode(new MultiplayerConfig(), in); } + public void decode0(ByteBuf in) { this.payload = INetworkObject.decodeStatic(new MultiplayerConfig(), in); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java index fb9a6d873..6c3eb8d5e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java @@ -3,11 +3,13 @@ package com.seibel.distanthorizons.core.network.protocol; import com.seibel.distanthorizons.core.network.messages.ExceptionMessage; import io.netty.buffer.ByteBuf; +import java.util.concurrent.atomic.AtomicInteger; + public abstract class FutureTrackableNetworkMessage extends NetworkMessage { - private static int lastId = 0; + private static final AtomicInteger lastId = new AtomicInteger(); // Only low 32 bits are sent (high bits are used for identifying a channel this request was sent from by remote peer) - public long futureId = lastId++; + public long futureId = lastId.incrementAndGet(); public void sendResponse(FutureTrackableNetworkMessage responseMessage) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java index 087e05b75..f70d5f94a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java @@ -3,6 +3,10 @@ package com.seibel.distanthorizons.core.network.protocol; import io.netty.buffer.ByteBuf; import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.*; public interface INetworkObject { @@ -10,22 +14,109 @@ public interface INetworkObject void decode(ByteBuf in); - static T decode(T obj, ByteBuf inputByteBuf) + static T decodeStatic(T obj, ByteBuf inputByteBuf) { obj.decode(inputByteBuf); return obj; } - static void encodeString(String inputString, ByteBuf outputByteBuf) + default void encodeString(String inputString, ByteBuf outputByteBuf) { - outputByteBuf.writeShort(inputString.length()); - outputByteBuf.writeBytes(inputString.getBytes(StandardCharsets.UTF_8)); + byte[] bytes = inputString.getBytes(StandardCharsets.UTF_8); + outputByteBuf.writeShort(bytes.length); + outputByteBuf.writeBytes(bytes); } - static String decodeString(ByteBuf inputByteBuf) + default String decodeString(ByteBuf inputByteBuf) { int length = inputByteBuf.readShort(); return inputByteBuf.readBytes(length).toString(StandardCharsets.UTF_8); } + default void encodeCollection(ByteBuf outputByteBuf, Collection collection) + { + outputByteBuf.writeInt(collection.size()); + + Codec codec = null; + for (T item : collection) + { + if (codec == null) + codec = Codec.getCodec(item.getClass()); + codec.encode.accept(item, outputByteBuf); + } + } + + default void decodeCollection(ByteBuf inputByteBuf, Collection collection, Supplier innerValueConstructor) + { + int size = inputByteBuf.readInt(); + + Codec codec = null; + for (int i = 0; i < size; i++) + { + T item = innerValueConstructor.get(); + + if (codec == null) + codec = Codec.getCodec(item.getClass()); + item = (T) codec.decode.apply(item, inputByteBuf); + + collection.add(item); + } + } + + default void decodeMap(ByteBuf inputByteBuf, Map map, Supplier keySupplier, Supplier valueSupplier) + { + ArrayList> entryList = new ArrayList<>(); + + decodeCollection(inputByteBuf, entryList, () -> new AbstractMap.SimpleEntry<>(keySupplier.get(), valueSupplier.get())); + for (Map.Entry entry : entryList) + map.put(entry.getKey(), entry.getValue()); + } + + /** Should only be used for non-editable classes; + * otherwise, you may want to implement {@link INetworkObject} and use its method where applicable. */ + class Codec + { + private static final ConcurrentMap, Codec> codecMap = new ConcurrentHashMap, Codec>() + {{ + // Primitives must be added manually here + put(Integer.class, new Codec((obj, out) -> out.writeInt((int)obj), (obj, in) -> in.readInt())); + + put(INetworkObject.class, new Codec(INetworkObject::encode, INetworkObject::decodeStatic)); + put(Map.Entry.class, new Codec( + (obj, out) -> { + Map.Entry entry = (Entry) obj; + getCodec(entry.getKey().getClass()).encode.accept(entry.getKey(), out); + getCodec(entry.getValue().getClass()).encode.accept(entry.getValue(), out); + }, + (obj, in) -> { + Map.Entry entry = (Entry) obj; + return new SimpleEntry<>( + getCodec(entry.getKey().getClass()).decode.apply(entry.getKey(), in), + getCodec(entry.getValue().getClass()).decode.apply(entry.getValue(), in) + ); + } + )); + }}; + + public final BiConsumer encode; + public final BiFunction decode; + + public Codec(BiConsumer encode, BiFunction decode) + { + this.encode = (BiConsumer) encode; + this.decode = (BiFunction) decode; + } + + public static Codec getCodec(Class clazz) { + return codecMap.computeIfAbsent(clazz, ignored -> { + for (Map.Entry, Codec> entry : codecMap.entrySet()) + { + if (entry.getKey().isAssignableFrom(clazz)) + return entry.getValue(); + } + + throw new AssertionError("Class has no compatible codec: "+clazz.getSimpleName()); + }); + } + } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageDecoder.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageDecoder.java index 25521b413..114c745d4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageDecoder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageDecoder.java @@ -12,7 +12,7 @@ public class MessageDecoder extends ByteToMessageDecoder protected void decode(ChannelHandlerContext channelContext, ByteBuf inputByteBuf, List outputDecodedObjectList) { NetworkMessage message = MessageRegistry.INSTANCE.createMessage(inputByteBuf.readShort()); - outputDecodedObjectList.add(INetworkObject.decode(message, inputByteBuf)); + outputDecodedObjectList.add(INetworkObject.decodeStatic(message, inputByteBuf)); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java index e1dfa41d2..f34f093e4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java @@ -21,18 +21,27 @@ public class MessageRegistry { // Note: Messages must have parameterless constructors - // Keep messages below intact so client/server can disconnect if version does not match + // Opening & closing connection + // These messages should be compatible with any previous protocol versions this.registerMessage(HelloMessage.class, HelloMessage::new); this.registerMessage(CloseReasonMessage.class, CloseReasonMessage::new); - // Define your messages after this line + // Core this.registerMessage(AckMessage.class, AckMessage::new); this.registerMessage(CancelMessage.class, CancelMessage::new); this.registerMessage(ExceptionMessage.class, ExceptionMessage::new); + + // ID & config this.registerMessage(PlayerUUIDMessage.class, PlayerUUIDMessage::new); this.registerMessage(RemotePlayerConfigMessage.class, RemotePlayerConfigMessage::new); + + // Full data requests this.registerMessage(FullDataSourceRequestMessage.class, FullDataSourceRequestMessage::new); this.registerMessage(FullDataSourceResponseMessage.class, FullDataSourceResponseMessage::new); + + // Generation task prioritization + this.registerMessage(GenTaskPriorityRequestMessage.class, GenTaskPriorityRequestMessage::new); + this.registerMessage(GenTaskPriorityResponseMessage.class, GenTaskPriorityResponseMessage::new); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java index 28e395df6..cc467042c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java @@ -49,6 +49,8 @@ public class DhSectionPos implements INetworkObject + public static DhSectionPos zero() { return new DhSectionPos((byte) 0, 0, 0); }; + public DhSectionPos(byte sectionDetailLevel, int sectionX, int sectionZ) { this.sectionDetailLevel = sectionDetailLevel; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java index 0eeaf4efd..437d5b739 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java @@ -39,7 +39,7 @@ public interface IWrapperFactory extends IBindable { AbstractBatchGenerationEnvironmentWrapper createBatchGenerator(IDhLevel targetLevel); IBiomeWrapper deserializeBiomeWrapper(String str, ILevelWrapper levelWrapper) throws IOException; - IBlockStateWrapper deserializeBlockStateWrapper(String str) throws IOException; + IBlockStateWrapper deserializeBlockStateWrapper(String str, ILevelWrapper levelWrapper) throws IOException; IBlockStateWrapper getAirBlockStateWrapper(); /** diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java index 5df9357b4..d8ae596d4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java @@ -1,11 +1,12 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.block; import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; /** A Minecraft version independent way of handling Blocks. */ public interface IBlockStateWrapper extends IDhApiBlockStateWrapper { - String serialize(); + String serialize(ILevelWrapper levelWrapper); /** * Returning a value of 0 means the block is completely transparent. Date: Sun, 13 Aug 2023 23:18:55 +0200 Subject: [PATCH 026/205] refactor: Re-add old serialization method This is used by some override methods that don't have access to the level. These old serialization methods have `FIXME`s so they're easy to find in the future. --- .../fullData/FullDataPointIdMap.java | 19 +++++++++++++------ .../sources/CompleteFullDataSource.java | 3 +-- .../HighDetailIncompleteFullDataSource.java | 6 +++--- .../block/IBlockStateWrapper.java | 12 +++++++----- .../world/IBiomeWrapper.java | 2 ++ 5 files changed, 26 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java index 7e1bf376c..66190fb09 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java @@ -165,7 +165,7 @@ public class FullDataPointIdMap } if (dataPointEntryBySerialization.containsValue(entry)) { - LOGGER.error("Duplicate serialized entry found with value: " + entry.serialize()); + LOGGER.error("Duplicate serialized entry found with value: " + entry.serialize(levelWrapper)); } dataPointEntryBySerialization.put(entryString, entry); } @@ -187,7 +187,7 @@ public class FullDataPointIdMap for (int i = 0; i < entityCount; i++) { String entryString = inputStream.readUTF(); - Entry newEntry = Entry.deserialize(entryString); + Entry newEntry = Entry.deserialize(entryString, levelWrapper); newMap.entryList.add(newEntry); if (RUN_SERIALIZATION_DUPLICATE_VALIDATION) @@ -198,7 +198,7 @@ public class FullDataPointIdMap } if (dataPointEntryBySerialization.containsValue(newEntry)) { - LOGGER.error("Duplicate deserialized entry found with value: " + newEntry.serialize()); + LOGGER.error("Duplicate deserialized entry found with value: " + newEntry.serialize(levelWrapper)); } dataPointEntryBySerialization.put(entryString, newEntry); } @@ -278,10 +278,17 @@ public class FullDataPointIdMap } @Override - public String toString() { return this.serialize(); } - + public String toString() { + return this.serialize(); + } - public String serialize(ILevelWrapper levelWrapper) { return this.biome.serialize(levelWrapper) + SEPARATOR_STRING + this.blockState.serialize(levelWrapper); } + public String serialize(ILevelWrapper levelWrapper) { + return this.biome.serialize(levelWrapper) + BLOCK_STATE_SEPARATOR_STRING + this.blockState.serialize(levelWrapper); + } + + public String serialize() { + return this.biome.serialize() + BLOCK_STATE_SEPARATOR_STRING + this.blockState.serialize(); + } public static Entry deserialize(String str, ILevelWrapper levelWrapper) throws IOException, InterruptedException { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java index 9d15baf02..129e395a1 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java @@ -242,8 +242,7 @@ public class CompleteFullDataSource extends FullDataArrayAccessor implements IFu throw new IOException("Invalid data content end guard for ID mapping"); } - return FullDataPointIdMap.deserialize(inputStream, levelWrapper); - return FullDataPointIdMap.deserialize(inputStream, this.sectionPos); + return FullDataPointIdMap.deserialize(inputStream, this.sectionPos, levelWrapper); } @Override public void setIdMapping(FullDataPointIdMap mappings) { this.mapping.mergeAndReturnRemappedEntityIds(mappings); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java index 176d44fd6..eeff0ce17 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java @@ -365,10 +365,10 @@ public class HighDetailIncompleteFullDataSource implements IIncompleteFullDataSo } @Override - public void writeIdMappings(DhDataOutputStream dataOutputStream) throws IOException + public void writeIdMappings(DhDataOutputStream outputStream, ILevelWrapper levelWrapper) throws IOException { - dataOutputStream.writeInt(IFullDataSource.DATA_GUARD_BYTE); - this.mapping.serialize(dataOutputStream); + outputStream.writeInt(IFullDataSource.DATA_GUARD_BYTE); + this.mapping.serialize(outputStream, levelWrapper); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java index d8ae596d4..d4229b95a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java @@ -7,13 +7,15 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; public interface IBlockStateWrapper extends IDhApiBlockStateWrapper { String serialize(ILevelWrapper levelWrapper); - - /** - * Returning a value of 0 means the block is completely transparent. Date: Mon, 14 Aug 2023 19:23:39 +0200 Subject: [PATCH 027/205] fix: Fix server trying to access client instance --- .../fullData/FullDataPointIdMap.java | 18 +++++++----------- .../block/IBlockStateWrapper.java | 4 ++-- .../wrapperInterfaces/world/IBiomeWrapper.java | 3 +-- 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java index 66190fb09..65a951c57 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java @@ -4,14 +4,14 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream; +import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.io.*; +import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -257,7 +257,7 @@ public class FullDataPointIdMap // cache the hash code to improve speed if (this.hashCode == null) { - this.hashCode = this.serialize().hashCode(); + this.hashCode = this.serialize(this.biome.getLevelWrapper()).hashCode(); } return this.hashCode; @@ -273,21 +273,17 @@ public class FullDataPointIdMap return false; Entry other = (Entry) otherObj; - return other.biome.serialize().equals(this.biome.serialize()) - && other.blockState.serialize().equals(this.blockState.serialize()); + return other.biome.serialize(other.biome.getLevelWrapper()).equals(this.biome.serialize(this.biome.getLevelWrapper())) + && other.blockState.serialize(other.blockState.getLevelWrapper()).equals(this.blockState.serialize(this.blockState.getLevelWrapper())); } @Override public String toString() { - return this.serialize(); + return this.serialize(this.biome.getLevelWrapper()); } public String serialize(ILevelWrapper levelWrapper) { - return this.biome.serialize(levelWrapper) + BLOCK_STATE_SEPARATOR_STRING + this.blockState.serialize(levelWrapper); - } - - public String serialize() { - return this.biome.serialize() + BLOCK_STATE_SEPARATOR_STRING + this.blockState.serialize(); + return this.biome.serialize(this.biome.getLevelWrapper()) + BLOCK_STATE_SEPARATOR_STRING + this.blockState.serialize(this.biome.getLevelWrapper()); } public static Entry deserialize(String str, ILevelWrapper levelWrapper) throws IOException, InterruptedException diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java index d4229b95a..6b4b55944 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java @@ -7,8 +7,8 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; public interface IBlockStateWrapper extends IDhApiBlockStateWrapper { String serialize(ILevelWrapper levelWrapper); - // FIXME: Old code that might create a nullpointer exception - String serialize(); + + ILevelWrapper getLevelWrapper(); /** * Returning a value of 0 means the block is completely transparent. Date: Tue, 15 Aug 2023 00:03:13 +0200 Subject: [PATCH 028/205] fix: Fix `BlockStateWrapper` and `Biome` serializing to empty strings --- .../distanthorizons/core/config/Config.java | 2 +- .../fullData/FullDataPointIdMap.java | 18 ++++++++++++------ 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 6fad187f2..516927657 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -967,7 +967,7 @@ public class Config // TODO add change all option // TODO default to error chat and info file public static ConfigEntry logWorldGenEvent = new ConfigEntry.Builder() - .set(ELoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE) + .set(ELoggerMode.LOG_WARNING_TO_CHAT_AND_FILE) .comment("" + "If enabled, the mod will log information about the world generation process. \n" + "This can be useful for debugging.") diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java index 65a951c57..8921f8aac 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java @@ -1,5 +1,7 @@ package com.seibel.distanthorizons.core.dataObjects.fullData; +import com.seibel.distanthorizons.api.enums.config.ELoggerMode; +import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; @@ -8,11 +10,13 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; +import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -188,7 +192,10 @@ public class FullDataPointIdMap { String entryString = inputStream.readUTF(); Entry newEntry = Entry.deserialize(entryString, levelWrapper); - newMap.entryList.add(newEntry); + // Required check because of an underlying issue where the BiomeWrapper and BlockStateWrapper get null ILevelWrappers + if (newEntry != null) { + newMap.entryList.add(newEntry); + } if (RUN_SERIALIZATION_DUPLICATE_VALIDATION) { @@ -283,7 +290,7 @@ public class FullDataPointIdMap } public String serialize(ILevelWrapper levelWrapper) { - return this.biome.serialize(this.biome.getLevelWrapper()) + BLOCK_STATE_SEPARATOR_STRING + this.blockState.serialize(this.biome.getLevelWrapper()); + return this.biome.serialize(levelWrapper) + BLOCK_STATE_SEPARATOR_STRING + this.blockState.serialize(levelWrapper); } public static Entry deserialize(String str, ILevelWrapper levelWrapper) throws IOException, InterruptedException @@ -291,10 +298,12 @@ public class FullDataPointIdMap String[] stringArray = str.split(BLOCK_STATE_SEPARATOR_STRING); if (stringArray.length != 2) { + // This situation should never occur, both the biome and the blockstate in the entry will always have a value, even if it's default + // The default values will be handled by the biome and blockstate's deserialize functions throw new IOException("Failed to deserialize BiomeBlockStateEntry"); } - // necessary to prevent issues with deserializing objects after the level has been closed + // Necessary to prevent issues with deserializing objects after the level has been closed if (Thread.interrupted()) { throw new InterruptedException(FullDataPointIdMap.class.getSimpleName()+" task interrupted."); @@ -304,8 +313,5 @@ public class FullDataPointIdMap IBlockStateWrapper blockState = WRAPPER_FACTORY.deserializeBlockStateWrapper(stringArray[1], levelWrapper); return new Entry(biome, blockState); } - } - - } From 84c4ad6cdd772ffe7bace0fe56842fdbf3bcbeda Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Thu, 17 Aug 2023 22:39:01 +0500 Subject: [PATCH 029/205] Add missing level wrapper argument (and a bunch of null checks) --- .../fullData/FullDataPointIdMap.java | 21 ++++++++++--------- .../block/IBlockStateWrapper.java | 2 +- .../world/IBiomeWrapper.java | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java index 96f747b26..27c22b309 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java @@ -18,6 +18,7 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; +import java.util.Objects; import java.util.concurrent.locks.ReentrantReadWriteLock; /** @@ -36,7 +37,7 @@ public class FullDataPointIdMap * Has the system check if any duplicate Entries were read/written * when (de)serializing. */ - private static final boolean RUN_SERIALIZATION_DUPLICATE_VALIDATION = false; + private static final boolean RUN_SERIALIZATION_DUPLICATE_VALIDATION = true; /** Distant Horizons - Block State Wrapper */ private static final String BLOCK_STATE_SEPARATOR_STRING = "_DH-BSW_"; @@ -159,7 +160,7 @@ public class FullDataPointIdMap for (Entry entry : this.entryList) { - String entryString = entry.serialize(levelWrapper); + String entryString = entry.serialize(); outputStream.writeUTF(entryString); if (RUN_SERIALIZATION_DUPLICATE_VALIDATION) @@ -170,7 +171,7 @@ public class FullDataPointIdMap } if (dataPointEntryBySerialization.containsValue(entry)) { - LOGGER.error("Duplicate serialized entry found with value: " + entry.serialize(levelWrapper)); + LOGGER.error("Duplicate serialized entry found with value: " + entry.serialize()); } dataPointEntryBySerialization.put(entryString, entry); } @@ -206,7 +207,7 @@ public class FullDataPointIdMap } if (dataPointEntryBySerialization.containsValue(newEntry)) { - LOGGER.error("Duplicate deserialized entry found with value: " + newEntry.serialize(levelWrapper)); + LOGGER.error("Duplicate deserialized entry found with value: " + newEntry.serialize()); } dataPointEntryBySerialization.put(entryString, newEntry); } @@ -265,7 +266,7 @@ public class FullDataPointIdMap // cache the hash code to improve speed if (this.hashCode == null) { - this.hashCode = this.serialize(this.biome.getLevelWrapper()).hashCode(); + this.hashCode = this.serialize().hashCode(); } return this.hashCode; @@ -281,17 +282,17 @@ public class FullDataPointIdMap return false; Entry other = (Entry) otherObj; - return other.biome.serialize(other.biome.getLevelWrapper()).equals(this.biome.serialize(this.biome.getLevelWrapper())) - && other.blockState.serialize(other.blockState.getLevelWrapper()).equals(this.blockState.serialize(this.blockState.getLevelWrapper())); + return other.biome.serialize().equals(this.biome.serialize()) + && other.blockState.serialize().equals(this.blockState.serialize()); } @Override public String toString() { - return this.serialize(this.biome.getLevelWrapper()); + return this.serialize(); } - public String serialize(ILevelWrapper levelWrapper) { - return this.biome.serialize(levelWrapper) + BLOCK_STATE_SEPARATOR_STRING + this.blockState.serialize(levelWrapper); + public String serialize() { + return this.biome.serialize() + BLOCK_STATE_SEPARATOR_STRING + this.blockState.serialize(); } public static Entry deserialize(String str, ILevelWrapper levelWrapper) throws IOException, InterruptedException diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java index 6b4b55944..0e8f14f26 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java @@ -6,7 +6,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; /** A Minecraft version independent way of handling Blocks. */ public interface IBlockStateWrapper extends IDhApiBlockStateWrapper { - String serialize(ILevelWrapper levelWrapper); + String serialize(); ILevelWrapper getLevelWrapper(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IBiomeWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IBiomeWrapper.java index 1abfd7271..bb359c909 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IBiomeWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IBiomeWrapper.java @@ -29,6 +29,6 @@ import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindab public interface IBiomeWrapper extends IDhApiBiomeWrapper, IBindable { String getName(); - String serialize(ILevelWrapper levelWrapper); + String serialize(); ILevelWrapper getLevelWrapper(); } From bf58654a10065c88e23c8f7a2dbeadcad46b9db1 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 19 Aug 2023 21:22:51 +0500 Subject: [PATCH 030/205] Fix prioritization not working properly --- .../core/generation/WorldRemoteGenerationQueue.java | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index c70a0b05f..1a32278eb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -160,7 +160,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug .reduce(null, (a, b) -> a == null || b.getValue().priority > a.getValue().priority - || posDistanceSquared(targetPos, b.getKey()) < posDistanceSquared(targetPos, a.getKey()) + || (b.getValue().priority == a.getValue().priority && posDistanceSquared(targetPos, b.getKey()) < posDistanceSquared(targetPos, a.getKey())) ? b : a); if (mapEntry == null) { @@ -279,9 +279,11 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug for (Map.Entry mapEntry : waitingTasks.entrySet()) { r.renderBox(new DebugRenderer.Box(mapEntry.getKey(), -32f, 64f, 0.05f, - mapEntry.getValue().request != null - ? Color.red - : Color.blue + mapEntry.getValue().request != null ? Color.red + : mapEntry.getValue().priority == 3 ? Color.orange + : mapEntry.getValue().priority == 2 ? Color.cyan + : mapEntry.getValue().priority == 1 ? Color.blue + : Color.gray )); } } From 7568ab56f6f9f28838812764bcb6c001e5f96322 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 19 Aug 2023 22:21:01 +0500 Subject: [PATCH 031/205] Fix hang when near completion of loading from server Replace total value with sum of pending+finished requests --- .../core/generation/WorldRemoteGenerationQueue.java | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 1a32278eb..9ab3eab32 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -51,7 +51,6 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug private final F3Screen.NestedMessage f3Message = new F3Screen.NestedMessage(this::f3Log); private final AtomicInteger finishedRequests = new AtomicInteger(); - private final AtomicInteger totalRequests = new AtomicInteger(); private final AtomicInteger failedRequests = new AtomicInteger(); public WorldRemoteGenerationQueue(ClientNetworkState networkState, IDhClientLevel level) @@ -73,8 +72,6 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug LodUtil.assertTrue(lodPos.detailLevel == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL, "Only highest-detail sections are allowed."); DhSectionPos sectionPos = new DhSectionPos(lodPos.detailLevel, lodPos); - totalRequests.incrementAndGet(); - WorldGenQueueEntry entry = new WorldGenQueueEntry(new CompletableFuture<>(), tracker); waitingTasks.put(sectionPos, entry); return entry.future; @@ -90,7 +87,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug { if (generatorClosingFuture != null || !networkState.getClient().isReady()) return; - while (!waitingTasks.isEmpty() + while (waitingTasks.size() > pendingTasks() && pendingTasks() < this.networkState.config.fullDataRequestRateLimit && pendingTasksSemaphore.tryAcquire()) { @@ -146,9 +143,9 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug WorldGenQueueEntry entry = waitingTasks.remove(pos); if (entry != null) { + entry.future.cancel(false); if (entry.request != null) entry.request.cancel(false); - entry.future.cancel(false); } } } @@ -225,7 +222,6 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug catch (CancellationException ignored) { finishedRequests.decrementAndGet(); - totalRequests.decrementAndGet(); } catch (Throwable e) { @@ -242,7 +238,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug { ArrayList lines = new ArrayList<>(); lines.add("World Remote Generation Queue ["+level.getClientLevelWrapper().getDimensionType().getDimensionName()+"]"); - lines.add(" Requests: "+this.finishedRequests+" / "+this.totalRequests +" (failed: "+ this.failedRequests+")"); + lines.add(" Requests: "+this.finishedRequests+" / "+(this.waitingTasks.size() + this.finishedRequests.get())+" (failed: "+ this.failedRequests+")"); lines.add(" Pending: "+this.pendingTasks()+" / "+this.networkState.config.fullDataRequestRateLimit); return lines.toArray(new String[0]); } @@ -260,9 +256,9 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug { for (WorldGenQueueEntry entry : this.waitingTasks.values()) { + entry.future.cancel(alsoInterruptRunning); if (entry.request != null) entry.request.cancel(alsoInterruptRunning); - entry.future.cancel(alsoInterruptRunning); } } }); From d9283e938bd0dbe7f5c24f7fbda25a39e593f0b0 Mon Sep 17 00:00:00 2001 From: Steveplays28 Date: Mon, 21 Aug 2023 03:41:27 +0200 Subject: [PATCH 032/205] feat: Update rendering block ignores Barrier blocks, structure void blocks, light blocks, and air blocks now share 2 `HashMap`s that define blocks that should be ignored by the LOD builder. --- .../FullDataToRenderDataTransformer.java | 12 ++++++++---- .../core/wrapperInterfaces/IWrapperFactory.java | 2 ++ .../wrapperInterfaces/block/IBlockStateWrapper.java | 10 +++++----- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java index d344048c1..4381a9499 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java @@ -18,11 +18,13 @@ import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.FullDataPointUtil; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.RenderDataPointUtil; +import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; +import java.util.HashMap; + /** * Handles converting {@link ChunkSizedFullDataAccessor}, {@link IIncompleteFullDataSource}, * and {@link IFullDataSource}'s to {@link ColumnRenderSource}. @@ -30,7 +32,7 @@ import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; public class FullDataToRenderDataTransformer { private static final IWrapperFactory WRAPPER_FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class); - private static final IBlockStateWrapper AIR = WRAPPER_FACTORY.getAirBlockStateWrapper(); + private static final HashMap RENDERER_IGNORED_BLOCKS = WRAPPER_FACTORY.getRendererIgnoredBlocks(); @@ -305,9 +307,11 @@ public class FullDataToRenderDataTransformer int light = FullDataPointUtil.getLight(fullData); IBiomeWrapper biome = fullDataMapping.getBiomeWrapper(id); IBlockStateWrapper block = fullDataMapping.getBlockStateWrapper(id); - if (block.equals(AIR)) + + if (RENDERER_IGNORED_BLOCKS.containsValue(block)) { - // we don't render air + // Don't render a block that's marked as ignored by the renderer + // This map should include air (if it doesn't, the map either got refactored or something is wrong) continue; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java index 105e3bdd5..cc832130f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java @@ -28,6 +28,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.Abstrac import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable; import java.io.IOException; +import java.util.HashMap; /** * This handles creating abstract wrapper objects. @@ -41,6 +42,7 @@ public interface IWrapperFactory extends IBindable IBiomeWrapper deserializeBiomeWrapper(String str, ILevelWrapper levelWrapper) throws IOException; IBlockStateWrapper deserializeBlockStateWrapper(String str, ILevelWrapper levelWrapper) throws IOException; IBlockStateWrapper getAirBlockStateWrapper(); + HashMap getRendererIgnoredBlocks(); /** * Specifically designed to be used with the API. diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java index 0e8f14f26..a89495858 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java @@ -6,16 +6,16 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; /** A Minecraft version independent way of handling Blocks. */ public interface IBlockStateWrapper extends IDhApiBlockStateWrapper { - String serialize(); - + String serialize(); + ILevelWrapper getLevelWrapper(); - + /** * Returning a value of 0 means the block is completely transparent. Date: Mon, 21 Aug 2023 22:08:35 +0500 Subject: [PATCH 033/205] Incomplete --- .../AbstractPresetConfigEventHandler.java | 3 +- .../sources/CompleteFullDataSource.java | 23 +++++ .../transformers/ChunkToLodBuilder.java | 2 - .../WorldRemoteGenerationQueue.java | 16 +--- .../core/level/DhClientLevel.java | 40 +++++++++ .../distanthorizons/core/level/DhLevel.java | 3 +- .../core/level/DhServerLevel.java | 63 +++++++++++--- .../distanthorizons/core/level/IDhLevel.java | 5 +- .../core/multiplayer/ClientNetworkState.java | 4 +- .../RemotePlayerConnectionHandler.java | 3 + .../core/network/IClientRequestHandler.java | 19 ----- .../core/network/NetworkClient.java | 2 +- .../core/network/NetworkEventSource.java | 10 ++- .../FullDataSourceResponseMessage.java | 4 + .../messages/FullDataSourceUpdateMessage.java | 83 +++++++++++++++++++ .../network/protocol/MessageRegistry.java | 3 +- .../distanthorizons/core/pos/DhChunkPos.java | 11 +++ 17 files changed, 238 insertions(+), 56 deletions(-) delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/IClientRequestHandler.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceUpdateMessage.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java index 343af2b65..9375da336 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java @@ -35,7 +35,8 @@ public abstract class AbstractPresetConfigEventHandler this.onConfigUiClosed()); + if (configGui != null) + configGui.addOnScreenChangeListener(() -> this.onConfigUiClosed()); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java index 14fe953bb..99558e014 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java @@ -10,6 +10,7 @@ import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; +import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhLodPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.FullDataPointUtil; @@ -23,6 +24,7 @@ import org.apache.logging.log4j.Logger; import javax.annotation.CheckForNull; import java.io.*; +import java.util.function.Consumer; /** * This data source contains every datapoint over its given {@link DhSectionPos}. @@ -368,6 +370,27 @@ public class CompleteFullDataSource extends FullDataArrayAccessor implements IFu } } + public void splitIntoChunkSizedAccessors(Consumer consumer) + { + LodUtil.assertTrue(sectionPos.sectionDetailLevel == DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, "Data source detail level must be at block detail level."); + + sectionPos.forEachChildAtLevel(LodUtil.CHUNK_DETAIL_LEVEL, childPos -> { + ChunkSizedFullDataAccessor accessor = new ChunkSizedFullDataAccessor(new DhChunkPos(childPos.sectionX, childPos.sectionZ)); + + int detailLevelDifference = sectionPos.sectionDetailLevel - childPos.sectionDetailLevel; + int childRelativeX = childPos.sectionX - sectionPos.sectionX * BitShiftUtil.powerOfTwo(detailLevelDifference); + int childRelativeZ = childPos.sectionZ - sectionPos.sectionZ * BitShiftUtil.powerOfTwo(detailLevelDifference); + + subView( + LodUtil.CHUNK_WIDTH, + childRelativeX * LodUtil.CHUNK_WIDTH, + childRelativeZ * LodUtil.CHUNK_WIDTH + ).shadowCopyTo(accessor); + + consumer.accept(accessor); + }); + } + //=====================// diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/ChunkToLodBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/ChunkToLodBuilder.java index e49587a3d..aec487e74 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/ChunkToLodBuilder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/ChunkToLodBuilder.java @@ -82,8 +82,6 @@ public class ChunkToLodBuilder implements AutoCloseable } else if (MC != null && !MC.playerExists()) { - // TODO handle server side properly - // MC hasn't finished loading (or is currently unloaded) // can be uncommented if tasks aren't being cleared correctly diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 9ab3eab32..1fc7fd7a8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -195,21 +195,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug if (chunkDataConsumer == null) return entry.future.cancel(false); - sectionPos.forEachChildAtLevel(LodUtil.CHUNK_DETAIL_LEVEL, childPos -> { - ChunkSizedFullDataAccessor accessor = new ChunkSizedFullDataAccessor(new DhChunkPos(childPos.sectionX, childPos.sectionZ)); - - int detailLevelDifference = sectionPos.sectionDetailLevel - childPos.sectionDetailLevel; - int childRelativeX = childPos.sectionX - sectionPos.sectionX * BitShiftUtil.powerOfTwo(detailLevelDifference); - int childRelativeZ = childPos.sectionZ - sectionPos.sectionZ * BitShiftUtil.powerOfTwo(detailLevelDifference); - - fullDataSource.subView( - LodUtil.CHUNK_WIDTH, - childRelativeX * LodUtil.CHUNK_WIDTH, - childRelativeZ * LodUtil.CHUNK_WIDTH - ).shadowCopyTo(accessor); - - chunkDataConsumer.accept(accessor); - }); + fullDataSource.splitIntoChunkSizedAccessors(chunkDataConsumer); } catch (ChannelException | RateLimitedException e) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 1438f7108..a0605fdac 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -1,6 +1,7 @@ package com.seibel.distanthorizons.core.level; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; import com.seibel.distanthorizons.core.file.fullDatafile.RemoteFullDataFileHandler; @@ -8,22 +9,30 @@ import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.generation.WorldRemoteGenerationQueue; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.multiplayer.ClientNetworkState; +import com.seibel.distanthorizons.core.network.NetworkClient; +import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; +import com.seibel.distanthorizons.core.network.messages.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.FullDataSourceUpdateMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; +import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; +import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; +import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import com.seibel.distanthorizons.coreapi.util.math.Mat4f; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.Nullable; import javax.annotation.CheckForNull; import java.awt.*; +import java.io.IOException; import java.util.concurrent.CompletableFuture; /** The level used when connected to a server */ @@ -47,6 +56,8 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel @CheckForNull private final ClientNetworkState networkState; + @Nullable + private final ScopedNetworkEventSource eventSource; public final WorldGenModule worldGenModule; @@ -63,12 +74,41 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel this.networkState = networkState; this.worldGenModule = new WorldGenModule(dataFileHandler, this); + if (networkState != null) + { + this.eventSource = new ScopedNetworkEventSource<>(networkState.getClient()); + this.registerNetworkHandlers(); + } + else + { + this.eventSource = null; + } clientside = new ClientLevelModule(this); clientside.startRenderer(); LOGGER.info("Started DHLevel for " + this.levelWrapper + " with saves at " + this.saveStructure); } + private void registerNetworkHandlers() + { + assert this.eventSource != null; + + this.eventSource.registerHandler(FullDataSourceUpdateMessage.class, msg -> + { + try + { + CompleteFullDataSource fullDataSource = msg.getFullDataSource(this); + if (fullDataSource == null) return; + + fullDataSource.splitIntoChunkSizedAccessors(this::saveWrites); + } + catch (Exception e) + { + LOGGER.error("Error while updating full data source", e); + } + }); + } + //==============// // tick methods // //==============// diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhLevel.java index 08fd32fd8..5fb8d2d86 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhLevel.java @@ -25,7 +25,7 @@ public abstract class DhLevel implements IDhLevel } @Override - public void updateChunkAsync(IChunkWrapper chunk) + public CompletableFuture updateChunkAsync(IChunkWrapper chunk) { CompletableFuture future = this.chunkToLodBuilder.tryGenerateData(chunk); if (future != null) @@ -44,6 +44,7 @@ public abstract class DhLevel implements IDhLevel new DhApiChunkModifiedEvent.EventParam(this.getLevelWrapper(), chunk.getChunkPos().x, chunk.getChunkPos().z)); }); } + return future; } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 6cd032a10..d5e533e17 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -6,6 +6,7 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedF import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IIncompleteFullDataSource; +import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile; import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.multiplayer.ServerPlayerState; @@ -15,10 +16,12 @@ import com.seibel.distanthorizons.core.network.NetworkServer; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.messages.*; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; +import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhLodPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; @@ -26,9 +29,7 @@ import com.seibel.distanthorizons.coreapi.util.math.Vec3d; import org.apache.logging.log4j.Logger; import javax.annotation.CheckForNull; -import java.util.Iterator; import java.util.Map; -import java.util.Set; import java.util.concurrent.*; public class DhServerLevel extends DhLevel implements IDhServerLevel @@ -40,7 +41,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel private final RemotePlayerConnectionHandler remotePlayerConnectionHandler; private final ScopedNetworkEventSource eventSource; - private final ConcurrentLinkedQueue worldGenLoopingQueue = new ConcurrentLinkedQueue<>(); + private final LinkedBlockingQueue worldGenLoopingQueue = new LinkedBlockingQueue<>(); private final ConcurrentMap incompleteDataSources = new ConcurrentHashMap<>(); private final ConcurrentMap fullDataRequests = new ConcurrentHashMap<>(); private final AppliedConfigState rateLimitConfig = new AppliedConfigState<>(Config.Client.Advanced.Multiplayer.serverNetworkingRateLimit); @@ -66,12 +67,16 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel // TODO implement transparent message handling restriction by level // workaround: // ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); +// if (serverPlayerState == null) return; +// // if (serverPlayerState.serverPlayer.getLevel() != this.serverLevelWrapper) // return; this.eventSource.registerHandler(FullDataSourceRequestMessage.class, msg -> { ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); + if (serverPlayerState == null) return; + if (serverPlayerState.serverPlayer.getLevel() != this.serverLevelWrapper) return; @@ -106,6 +111,8 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel this.eventSource.registerHandler(GenTaskPriorityRequestMessage.class, msg -> { ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); + if (serverPlayerState == null) return; + if (serverPlayerState.serverPlayer.getLevel() != this.serverLevelWrapper) return; @@ -120,7 +127,9 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel if (entry == null) return; FullDataSourceRequestMessage requestMessage = entry.requestMessages.remove(msg.futureId); - remotePlayerConnectionHandler.getConnectedPlayer(msg).pendingFullDataRequests.decrementAndGet(); + ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); + if (serverPlayerState != null) + serverPlayerState.pendingFullDataRequests.decrementAndGet(); entry.requestCollectionSemaphore.acquireUninterruptibly(Short.MAX_VALUE); if (entry.requestMessages.isEmpty()) @@ -135,18 +144,12 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel public void addPlayer(IServerPlayerWrapper serverPlayer) { - synchronized (worldGenLoopingQueue) - { - this.worldGenLoopingQueue.add(serverPlayer); - } + this.worldGenLoopingQueue.add(serverPlayer); } public void removePlayer(IServerPlayerWrapper serverPlayer) { - synchronized (worldGenLoopingQueue) - { - this.worldGenLoopingQueue.remove(serverPlayer); - } + boolean ignored = this.worldGenLoopingQueue.remove(serverPlayer); } public void serverTick() @@ -189,6 +192,42 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel } } + @Override + public CompletableFuture updateChunkAsync(IChunkWrapper chunk) + { + DhSectionPos sectionPos = new DhSectionPos(chunk.getChunkPos()).convertToDetailLevel(DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); + FullDataMetaFile metaFile = this.serverside.dataFileHandler.getFileIfExist(sectionPos); + int prevChecksum = metaFile != null ? metaFile.baseMetaData.checksum : 0; + + CompletableFuture future = super.updateChunkAsync(chunk); + if (future == null) + return null; + + future.thenRun(() -> + { + if (metaFile == null || metaFile.baseMetaData.checksum == prevChecksum) + return; + + this.serverside.dataFileHandler.read(sectionPos).thenAccept(fullDataSource -> + { + if (!(fullDataSource instanceof CompleteFullDataSource)) + return; + CompleteFullDataSource completeSource = (CompleteFullDataSource) fullDataSource; + + for (IServerPlayerWrapper serverPlayer : worldGenLoopingQueue) + { + ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getPlayer(serverPlayer); + if (serverPlayerState == null) continue; + + if (chunk.getChunkPos().distance(new DhChunkPos(serverPlayer.getPosition())) <= serverPlayerState.config.renderDistance) + serverPlayerState.channelContext.writeAndFlush(new FullDataSourceUpdateMessage(completeSource, this)); + } + }); + }); + + return future; + } + @Override public void saveWrites(ChunkSizedFullDataAccessor data) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java index 688e7fe29..69ab18061 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java @@ -1,9 +1,11 @@ package com.seibel.distanthorizons.core.level; +import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; +import org.jetbrains.annotations.Nullable; import java.util.concurrent.CompletableFuture; @@ -20,7 +22,8 @@ public interface IDhLevel extends AutoCloseable */ ILevelWrapper getLevelWrapper(); - void updateChunkAsync(IChunkWrapper chunk); + @Nullable + CompletableFuture updateChunkAsync(IChunkWrapper chunk); IFullDataSourceProvider getFileHandler(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java index 41744ecfd..f1d4411b6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java @@ -2,7 +2,6 @@ package com.seibel.distanthorizons.core.multiplayer; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.network.IClientRequestHandler; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkClient; import com.seibel.distanthorizons.core.network.messages.AckMessage; @@ -10,6 +9,7 @@ import com.seibel.distanthorizons.core.network.messages.HelloMessage; import com.seibel.distanthorizons.core.network.messages.PlayerUUIDMessage; import com.seibel.distanthorizons.core.network.messages.RemotePlayerConfigMessage; import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; import java.io.Closeable; import java.util.UUID; @@ -26,7 +26,7 @@ public class ClientNetworkState implements Closeable * Returns the client used by this instance.

* If you need to subscribe to any packet events, create an instance of {@link ScopedNetworkEventSource} using the returned instance. */ - public IClientRequestHandler getClient() { return this.client; } + public NetworkClient getClient() { return this.client; } /** * Constructs a new instance. diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java index 8b0d83c79..52048f097 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java @@ -10,6 +10,7 @@ import com.seibel.distanthorizons.core.network.messages.PlayerUUIDMessage; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import io.netty.channel.ChannelHandlerContext; +import org.jetbrains.annotations.Nullable; import java.io.Closeable; import java.util.HashMap; @@ -69,11 +70,13 @@ public class RemotePlayerConnectionHandler implements Closeable return playersByConnection.values(); } + @Nullable public ServerPlayerState getConnectedPlayer(NetworkMessage msg) { return playersByConnection.get(msg.getChannelContext()); } + @Nullable public ServerPlayerState getPlayer(IServerPlayerWrapper serverPlayer) { return playersByUUID.get(serverPlayer.getUUID()); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/IClientRequestHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/IClientRequestHandler.java deleted file mode 100644 index a91b03798..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/IClientRequestHandler.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.seibel.distanthorizons.core.network; - -import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; - -import java.util.concurrent.CompletableFuture; - -public interface IClientRequestHandler -{ - /** Indicates whether the client is initialized and not started connecting yet. */ - boolean isInitialState(); - /** Indicates whether the client is closed(-ing) and should not be used. */ - boolean isClosed(); - /** Indicates whether the connection is established and first message is sent. */ - boolean isReady(); - - /** Sends a new request. */ - CompletableFuture sendRequest(FutureTrackableNetworkMessage msg); -} - diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index 8eb4e5a19..f59d9e751 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -19,7 +19,7 @@ import java.util.Set; import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; -public class NetworkClient extends NetworkEventSource implements IClientRequestHandler, AutoCloseable +public class NetworkClient extends NetworkEventSource implements AutoCloseable { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index 0d6daaa23..3e9837f56 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -5,8 +5,10 @@ import com.google.common.collect.Table; import com.google.common.collect.Tables; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.network.messages.CancelMessage; +import com.seibel.distanthorizons.core.network.messages.CloseEvent; import com.seibel.distanthorizons.core.network.messages.ExceptionMessage; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.network.protocol.MessageRegistry; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import io.netty.channel.ChannelException; import io.netty.channel.ChannelHandlerContext; @@ -75,7 +77,13 @@ public abstract class NetworkEventSource public void registerHandler(Class handlerClass, Consumer handlerImplementation) { - this.handlers.computeIfAbsent(handlerClass, missingHandlerClass -> new HashSet<>()) + this.handlers.computeIfAbsent(handlerClass, missingHandlerClass -> + { + // Will throw if the handler class is not found + if (handlerClass != CloseEvent.class) + MessageRegistry.INSTANCE.getMessageId(handlerClass); + return new HashSet<>(); + }) .add((Consumer) handlerImplementation); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java index d8f550fe7..4f3ca1716 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java @@ -71,5 +71,9 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage { return fullDataSourceLoader.loadData(pos, new DhDataInputStream(inputStream), level); } + finally + { + dataBuffer.release(); + } } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceUpdateMessage.java new file mode 100644 index 000000000..e03ef2e0f --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceUpdateMessage.java @@ -0,0 +1,83 @@ +package com.seibel.distanthorizons.core.network.messages; + +import com.seibel.distanthorizons.core.dataObjects.fullData.loader.AbstractFullDataSourceLoader; +import com.seibel.distanthorizons.core.dataObjects.fullData.loader.CompleteFullDataSourceLoader; +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; +import com.seibel.distanthorizons.core.level.DhServerLevel; +import com.seibel.distanthorizons.core.level.IDhLevel; +import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.network.protocol.INetworkObject; +import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; +import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufInputStream; + +import javax.annotation.Nullable; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +public class FullDataSourceUpdateMessage extends FutureTrackableNetworkMessage +{ + private CompleteFullDataSource fullDataSource; + private DhServerLevel level; + + private int levelHashCode; + private DhSectionPos sectionPos; + private CompleteFullDataSourceLoader fullDataSourceLoader; + private ByteBuf dataBuffer; + + public FullDataSourceUpdateMessage() {} + public FullDataSourceUpdateMessage(CompleteFullDataSource fullDataSource, DhServerLevel level) + { + this.fullDataSource = fullDataSource; + this.level = level; + + // TODO Multiverse support + this.levelHashCode = level.getLevelWrapper().getDimensionType().getDimensionName().hashCode(); + } + + @Override + public void encode0(ByteBuf out) throws IOException + { + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) + { + DhDataOutputStream dhOutputStream = new DhDataOutputStream(outputStream); + fullDataSource.writeToStream(dhOutputStream, level); + dhOutputStream.flush(); + + out.writeInt(levelHashCode); + fullDataSource.getSectionPos().encode(out); + out.writeByte(fullDataSource.getBinaryDataFormatVersion()); + out.writeInt(outputStream.size()); + out.writeBytes(outputStream.toByteArray()); + } + } + + @Override + public void decode0(ByteBuf in) + { + levelHashCode = in.readInt(); + sectionPos = INetworkObject.decodeStatic(DhSectionPos.zero(), in); + byte dataVersion = in.readByte(); + this.fullDataSourceLoader = (CompleteFullDataSourceLoader) AbstractFullDataSourceLoader.getLoader(CompleteFullDataSource.TYPE_ID, dataVersion); + this.dataBuffer = in.readBytes(in.readInt()); + } + + @Nullable + public CompleteFullDataSource getFullDataSource(IDhLevel level) throws IOException, InterruptedException + { + // TODO Multiverse support + if (levelHashCode != level.getLevelWrapper().getDimensionType().getDimensionName().hashCode()) + return null; + + try (ByteBufInputStream inputStream = new ByteBufInputStream(dataBuffer)) + { + return fullDataSourceLoader.loadData(sectionPos, new DhDataInputStream(inputStream), level); + } + finally + { + dataBuffer.release(); + } + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java index d8cf5974a..9cd94f53f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java @@ -35,9 +35,10 @@ public class MessageRegistry this.registerMessage(PlayerUUIDMessage.class, PlayerUUIDMessage::new); this.registerMessage(RemotePlayerConfigMessage.class, RemotePlayerConfigMessage::new); - // Full data requests + // Full data requests & updates this.registerMessage(FullDataSourceRequestMessage.class, FullDataSourceRequestMessage::new); this.registerMessage(FullDataSourceResponseMessage.class, FullDataSourceResponseMessage::new); + this.registerMessage(FullDataSourceUpdateMessage.class, FullDataSourceUpdateMessage::new); // Generation task prioritization this.registerMessage(GenTaskPriorityRequestMessage.class, GenTaskPriorityRequestMessage::new); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhChunkPos.java b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhChunkPos.java index b8d12ea55..ff7d77be8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhChunkPos.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhChunkPos.java @@ -19,6 +19,8 @@ package com.seibel.distanthorizons.core.pos; +import com.seibel.distanthorizons.coreapi.util.math.Vec3d; + import java.util.Objects; public class DhChunkPos @@ -48,6 +50,10 @@ public class DhChunkPos // >> 4 is the Same as div 16 this(blockPos.x >> 4, blockPos.z >> 4); } + public DhChunkPos(Vec3d pos) + { + this(((int)pos.x) >> 4, ((int)pos.z) >> 4); + } public DhChunkPos(long packed) { this(getX(packed), getZ(packed)); } @@ -72,6 +78,11 @@ public class DhChunkPos public long getLong() { return toLong(x, z); } + public double distance(DhChunkPos other) + { + return Math.sqrt(Math.pow(x - other.x, 2) + Math.pow(z - other.z, 2)); + } + @Override public boolean equals(Object obj) { From 3630dc724d758acf860129176d817fd2f43b41d5 Mon Sep 17 00:00:00 2001 From: Steveplays28 Date: Tue, 22 Aug 2023 10:52:19 +0200 Subject: [PATCH 034/205] fix: Add `null` check to config GUI when adding a screen change listener --- .../presets/AbstractPresetConfigEventHandler.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java index 343af2b65..a524a3e59 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java @@ -1,8 +1,10 @@ package com.seibel.distanthorizons.core.config.eventHandlers.presets; +import com.seibel.distanthorizons.core.api.internal.SharedApi; import com.seibel.distanthorizons.core.config.ConfigEntryWithPresetOptions; import com.seibel.distanthorizons.core.config.listeners.IConfigListener; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.world.EWorldEnvironment; import com.seibel.distanthorizons.core.wrapperInterfaces.config.IConfigGui; import com.seibel.distanthorizons.coreapi.interfaces.config.IConfigEntry; import com.seibel.distanthorizons.coreapi.util.StringUtil; @@ -35,7 +37,9 @@ public abstract class AbstractPresetConfigEventHandler this.onConfigUiClosed()); + if (configGui != null) { + configGui.addOnScreenChangeListener(this::onConfigUiClosed); + } } From 9db56bbf875fb6872eaf4986dface7bd95d66a98 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 22 Aug 2023 20:41:42 +0500 Subject: [PATCH 035/205] test --- .../core/level/DhServerLevel.java | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index d5e533e17..6cf5a62d8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -30,6 +30,7 @@ import org.apache.logging.log4j.Logger; import javax.annotation.CheckForNull; import java.util.Map; +import java.util.Objects; import java.util.concurrent.*; public class DhServerLevel extends DhLevel implements IDhServerLevel @@ -195,9 +196,16 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel @Override public CompletableFuture updateChunkAsync(IChunkWrapper chunk) { - DhSectionPos sectionPos = new DhSectionPos(chunk.getChunkPos()).convertToDetailLevel(DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); + DhSectionPos sectionPos = new DhSectionPos( + LodUtil.BLOCK_DETAIL_LEVEL, + chunk.getMinBlockX(), + chunk.getMinBlockZ() + ).convertToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); FullDataMetaFile metaFile = this.serverside.dataFileHandler.getFileIfExist(sectionPos); - int prevChecksum = metaFile != null ? metaFile.baseMetaData.checksum : 0; + + if (metaFile == null) + return super.updateChunkAsync(chunk); + int prevChecksum = metaFile.baseMetaData.checksum; CompletableFuture future = super.updateChunkAsync(chunk); if (future == null) @@ -205,24 +213,25 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel future.thenRun(() -> { - if (metaFile == null || metaFile.baseMetaData.checksum == prevChecksum) + FullDataMetaFile changedMetaFile = this.serverside.dataFileHandler.getFileIfExist(sectionPos); + Objects.requireNonNull(changedMetaFile, "Failed to get meta file for section pos " + sectionPos); + + if (changedMetaFile.baseMetaData.checksum == prevChecksum) return; - this.serverside.dataFileHandler.read(sectionPos).thenAccept(fullDataSource -> + IFullDataSource fullDataSource = changedMetaFile.getCachedDataSourceNowOrNull(); + if (!(fullDataSource instanceof CompleteFullDataSource)) + return; + CompleteFullDataSource completeSource = (CompleteFullDataSource) fullDataSource; + + for (IServerPlayerWrapper serverPlayer : worldGenLoopingQueue) { - if (!(fullDataSource instanceof CompleteFullDataSource)) - return; - CompleteFullDataSource completeSource = (CompleteFullDataSource) fullDataSource; + ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getPlayer(serverPlayer); + if (serverPlayerState == null) continue; - for (IServerPlayerWrapper serverPlayer : worldGenLoopingQueue) - { - ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getPlayer(serverPlayer); - if (serverPlayerState == null) continue; - - if (chunk.getChunkPos().distance(new DhChunkPos(serverPlayer.getPosition())) <= serverPlayerState.config.renderDistance) - serverPlayerState.channelContext.writeAndFlush(new FullDataSourceUpdateMessage(completeSource, this)); - } - }); + if (chunk.getChunkPos().distance(new DhChunkPos(serverPlayer.getPosition())) <= serverPlayerState.config.renderDistance) + serverPlayerState.channelContext.writeAndFlush(new FullDataSourceUpdateMessage(completeSource, this)); + } }); return future; From 2330377212986d1741c84e250c62ce9c48a216ba Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Wed, 23 Aug 2023 23:55:16 +0500 Subject: [PATCH 036/205] Send updates at chunk level instead of sections --- .../accessor/ChunkSizedFullDataAccessor.java | 171 +++++++++++++++++- .../WorldRemoteGenerationQueue.java | 7 +- .../core/level/DhClientLevel.java | 15 +- .../core/level/DhServerLevel.java | 28 +-- ...java => FullDataPartialUpdateMessage.java} | 40 ++-- .../network/protocol/MessageRegistry.java | 2 +- 6 files changed, 202 insertions(+), 61 deletions(-) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{FullDataSourceUpdateMessage.java => FullDataPartialUpdateMessage.java} (53%) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/accessor/ChunkSizedFullDataAccessor.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/accessor/ChunkSizedFullDataAccessor.java index faf82939b..51da8db2d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/accessor/ChunkSizedFullDataAccessor.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/accessor/ChunkSizedFullDataAccessor.java @@ -1,11 +1,21 @@ package com.seibel.distanthorizons.core.dataObjects.fullData.accessor; +import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap; +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IStreamableFullDataSource; +import com.seibel.distanthorizons.core.level.IDhLevel; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhLodPos; -import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.FullDataPointUtil; import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; +import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; /** * A more specific version of {@link FullDataArrayAccessor} @@ -15,6 +25,8 @@ import com.seibel.distanthorizons.core.util.LodUtil; */ public class ChunkSizedFullDataAccessor extends FullDataArrayAccessor { + private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + public final DhChunkPos pos; // TODO replace this var with LodUtil.BLOCK_DETAIL_LEVEL public final byte detailLevel = LodUtil.BLOCK_DETAIL_LEVEL; @@ -31,6 +43,163 @@ public class ChunkSizedFullDataAccessor extends FullDataArrayAccessor } + //=================// + // stream handling // + //=================// + + + public void writeSourceSummaryInfo(IDhLevel level, DhDataOutputStream outputStream) throws IOException + { + outputStream.writeInt(level.getMinY()); + } + + public void readSourceSummaryInfo(DhDataInputStream inputStream, IDhLevel level) throws IOException + { + int minY = inputStream.readInt(); + if (minY != level.getMinY()) + { + LOGGER.warn("Data minY mismatch: " + minY + " != " + level.getMinY() + ". Will ignore data's y level"); + } + } + + public boolean writeDataPoints(DhDataOutputStream outputStream) throws IOException + { + outputStream.writeInt(IFullDataSource.DATA_GUARD_BYTE); + + + + // Data array length + for (int x = 0; x < this.width; x++) + { + for (int z = 0; z < this.width; z++) + { + outputStream.writeInt(this.get(x, z).getSingleLength()); + } + } + + + + // Data array content (only on non-empty columns) + outputStream.writeInt(IFullDataSource.DATA_GUARD_BYTE); + for (int x = 0; x < this.width; x++) + { + for (int z = 0; z < this.width; z++) + { + SingleColumnFullDataAccessor columnAccessor = this.get(x, z); + if (columnAccessor.doesColumnExist()) + { + long[] dataPointArray = columnAccessor.getRaw(); + for (long dataPoint : dataPointArray) + { + outputStream.writeLong(dataPoint); + } + } + } + } + + + return true; + } + public long[][] readDataPoints(DhDataInputStream dataInputStream) throws IOException + { + // Data array length + int dataPresentFlag = dataInputStream.readInt(); + if (dataPresentFlag != IFullDataSource.DATA_GUARD_BYTE) + { + throw new IOException("Invalid file format. Data Points guard byte expected: (no data) [" + IFullDataSource.NO_DATA_FLAG_BYTE + "] or (data present) [" + IFullDataSource.DATA_GUARD_BYTE + "], but found [" + dataPresentFlag + "]."); + } + + + + long[][] dataPointArray = new long[width * width][]; + for (int x = 0; x < width; x++) + { + for (int z = 0; z < width; z++) + { + dataPointArray[x * width + z] = new long[dataInputStream.readInt()]; + } + } + + + + // check if the array start flag is present + int arrayStartFlag = dataInputStream.readInt(); + if (arrayStartFlag != IFullDataSource.DATA_GUARD_BYTE) + { + throw new IOException("invalid data length end guard"); + } + + for (int xz = 0; xz < dataPointArray.length; xz++) // x and z are combined + { + if (dataPointArray[xz].length != 0) + { + for (int y = 0; y < dataPointArray[xz].length; y++) + { + dataPointArray[xz][y] = dataInputStream.readLong(); + } + } + } + + + + return dataPointArray; + } + public void setDataPoints(long[][] dataPoints) + { + LodUtil.assertTrue(this.dataArrays.length == dataPoints.length, "Data point array length mismatch."); + + System.arraycopy(dataPoints, 0, this.dataArrays, 0, dataPoints.length); + } + + + public void writeIdMappings(DhDataOutputStream outputStream, ILevelWrapper levelWrapper) throws IOException + { + outputStream.writeInt(IFullDataSource.DATA_GUARD_BYTE); + this.mapping.serialize(outputStream, levelWrapper); + } + public FullDataPointIdMap readIdMappings(DhDataInputStream inputStream, ILevelWrapper levelWrapper) throws IOException, InterruptedException + { + int guardByte = inputStream.readInt(); + if (guardByte != IFullDataSource.DATA_GUARD_BYTE) + { + throw new IOException("Invalid data content end guard for ID mapping"); + } + + return FullDataPointIdMap.deserialize(inputStream, null, levelWrapper); + } + public void setIdMapping(FullDataPointIdMap mappings) { this.mapping.mergeAndReturnRemappedEntityIds(mappings); } + + + public void populateFromStream(DhDataInputStream inputStream, IDhLevel level) throws IOException, InterruptedException + { + this.readSourceSummaryInfo(inputStream, level); + + long[][] dataPoints = this.readDataPoints(inputStream); + if (dataPoints == null) + { + return; + } + this.setDataPoints(dataPoints); + + + FullDataPointIdMap mapping = this.readIdMappings(inputStream, level.getLevelWrapper()); + this.setIdMapping(mapping); + + } + + public void writeToStream(DhDataOutputStream outputStream, IDhLevel level) throws IOException + { + this.writeSourceSummaryInfo(level, outputStream); + + boolean hasData = this.writeDataPoints(outputStream); + if (!hasData) + { + return; + } + + this.writeIdMappings(outputStream, level.getLevelWrapper()); + } + public void setSingleColumn(long[] data, int xRelative, int zRelative) { this.dataArrays[xRelative * LodUtil.CHUNK_WIDTH + zRelative] = data; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 5183e8006..91410b3a4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -238,7 +238,8 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug return this.generatorClosingFuture = CompletableFuture.runAsync(() -> { while (!genTaskPriorityRequestSemaphore.tryAcquire()) { - genTaskPriorityRequest.cancel(false); + if (genTaskPriorityRequest.cancel(false)) + genTaskPriorityRequestSemaphore.release(); } while (!pendingTasksSemaphore.tryAcquire(Short.MAX_VALUE)) @@ -246,8 +247,8 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug for (WorldGenQueueEntry entry : this.waitingTasks.values()) { entry.future.cancel(alsoInterruptRunning); - if (entry.request != null) - entry.request.cancel(alsoInterruptRunning); + if (entry.request != null && entry.request.cancel(alsoInterruptRunning)) + pendingTasksSemaphore.release(); } } }); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index a0605fdac..bea8afdcf 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -11,28 +11,23 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.multiplayer.ClientNetworkState; import com.seibel.distanthorizons.core.network.NetworkClient; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; -import com.seibel.distanthorizons.core.network.messages.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.FullDataSourceUpdateMessage; +import com.seibel.distanthorizons.core.network.messages.FullDataPartialUpdateMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; -import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; -import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; -import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import com.seibel.distanthorizons.coreapi.util.math.Mat4f; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.Nullable; import javax.annotation.CheckForNull; import java.awt.*; -import java.io.IOException; import java.util.concurrent.CompletableFuture; /** The level used when connected to a server */ @@ -93,14 +88,14 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel { assert this.eventSource != null; - this.eventSource.registerHandler(FullDataSourceUpdateMessage.class, msg -> + this.eventSource.registerHandler(FullDataPartialUpdateMessage.class, msg -> { try { - CompleteFullDataSource fullDataSource = msg.getFullDataSource(this); - if (fullDataSource == null) return; + ChunkSizedFullDataAccessor fullDataAccessor = msg.getFullDataSource(this); + if (fullDataAccessor == null) return; - fullDataSource.splitIntoChunkSizedAccessors(this::saveWrites); + this.saveWrites(fullDataAccessor); } catch (Exception e) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 6cf5a62d8..ac1934c88 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -6,7 +6,6 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedF import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IIncompleteFullDataSource; -import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile; import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.multiplayer.ServerPlayerState; @@ -30,7 +29,6 @@ import org.apache.logging.log4j.Logger; import javax.annotation.CheckForNull; import java.util.Map; -import java.util.Objects; import java.util.concurrent.*; public class DhServerLevel extends DhLevel implements IDhServerLevel @@ -196,41 +194,19 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel @Override public CompletableFuture updateChunkAsync(IChunkWrapper chunk) { - DhSectionPos sectionPos = new DhSectionPos( - LodUtil.BLOCK_DETAIL_LEVEL, - chunk.getMinBlockX(), - chunk.getMinBlockZ() - ).convertToDetailLevel(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); - FullDataMetaFile metaFile = this.serverside.dataFileHandler.getFileIfExist(sectionPos); - - if (metaFile == null) - return super.updateChunkAsync(chunk); - int prevChecksum = metaFile.baseMetaData.checksum; - CompletableFuture future = super.updateChunkAsync(chunk); if (future == null) return null; - future.thenRun(() -> + future.thenAccept(chunkSizedFullDataAccessor -> { - FullDataMetaFile changedMetaFile = this.serverside.dataFileHandler.getFileIfExist(sectionPos); - Objects.requireNonNull(changedMetaFile, "Failed to get meta file for section pos " + sectionPos); - - if (changedMetaFile.baseMetaData.checksum == prevChecksum) - return; - - IFullDataSource fullDataSource = changedMetaFile.getCachedDataSourceNowOrNull(); - if (!(fullDataSource instanceof CompleteFullDataSource)) - return; - CompleteFullDataSource completeSource = (CompleteFullDataSource) fullDataSource; - for (IServerPlayerWrapper serverPlayer : worldGenLoopingQueue) { ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getPlayer(serverPlayer); if (serverPlayerState == null) continue; if (chunk.getChunkPos().distance(new DhChunkPos(serverPlayer.getPosition())) <= serverPlayerState.config.renderDistance) - serverPlayerState.channelContext.writeAndFlush(new FullDataSourceUpdateMessage(completeSource, this)); + serverPlayerState.channelContext.writeAndFlush(new FullDataPartialUpdateMessage(chunkSizedFullDataAccessor, this)); } }); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataPartialUpdateMessage.java similarity index 53% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceUpdateMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataPartialUpdateMessage.java index e03ef2e0f..03d0614db 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataPartialUpdateMessage.java @@ -1,13 +1,10 @@ package com.seibel.distanthorizons.core.network.messages; -import com.seibel.distanthorizons.core.dataObjects.fullData.loader.AbstractFullDataSourceLoader; -import com.seibel.distanthorizons.core.dataObjects.fullData.loader.CompleteFullDataSourceLoader; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; +import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; -import com.seibel.distanthorizons.core.network.protocol.INetworkObject; -import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream; import io.netty.buffer.ByteBuf; @@ -17,20 +14,19 @@ import javax.annotation.Nullable; import java.io.ByteArrayOutputStream; import java.io.IOException; -public class FullDataSourceUpdateMessage extends FutureTrackableNetworkMessage +public class FullDataPartialUpdateMessage extends FutureTrackableNetworkMessage { - private CompleteFullDataSource fullDataSource; + private ChunkSizedFullDataAccessor fullDataAccessor; private DhServerLevel level; private int levelHashCode; - private DhSectionPos sectionPos; - private CompleteFullDataSourceLoader fullDataSourceLoader; + private DhChunkPos chunkPos; private ByteBuf dataBuffer; - public FullDataSourceUpdateMessage() {} - public FullDataSourceUpdateMessage(CompleteFullDataSource fullDataSource, DhServerLevel level) + public FullDataPartialUpdateMessage() {} + public FullDataPartialUpdateMessage(ChunkSizedFullDataAccessor fullDataAccessor, DhServerLevel level) { - this.fullDataSource = fullDataSource; + this.fullDataAccessor = fullDataAccessor; this.level = level; // TODO Multiverse support @@ -43,12 +39,14 @@ public class FullDataSourceUpdateMessage extends FutureTrackableNetworkMessage try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { DhDataOutputStream dhOutputStream = new DhDataOutputStream(outputStream); - fullDataSource.writeToStream(dhOutputStream, level); + fullDataAccessor.writeToStream(dhOutputStream, level); dhOutputStream.flush(); out.writeInt(levelHashCode); - fullDataSource.getSectionPos().encode(out); - out.writeByte(fullDataSource.getBinaryDataFormatVersion()); + + out.writeInt(fullDataAccessor.pos.x); + out.writeInt(fullDataAccessor.pos.z); + out.writeInt(outputStream.size()); out.writeBytes(outputStream.toByteArray()); } @@ -58,14 +56,14 @@ public class FullDataSourceUpdateMessage extends FutureTrackableNetworkMessage public void decode0(ByteBuf in) { levelHashCode = in.readInt(); - sectionPos = INetworkObject.decodeStatic(DhSectionPos.zero(), in); - byte dataVersion = in.readByte(); - this.fullDataSourceLoader = (CompleteFullDataSourceLoader) AbstractFullDataSourceLoader.getLoader(CompleteFullDataSource.TYPE_ID, dataVersion); + + chunkPos = new DhChunkPos(in.readInt(), in.readInt()); + this.dataBuffer = in.readBytes(in.readInt()); } @Nullable - public CompleteFullDataSource getFullDataSource(IDhLevel level) throws IOException, InterruptedException + public ChunkSizedFullDataAccessor getFullDataSource(IDhLevel level) throws IOException, InterruptedException { // TODO Multiverse support if (levelHashCode != level.getLevelWrapper().getDimensionType().getDimensionName().hashCode()) @@ -73,7 +71,9 @@ public class FullDataSourceUpdateMessage extends FutureTrackableNetworkMessage try (ByteBufInputStream inputStream = new ByteBufInputStream(dataBuffer)) { - return fullDataSourceLoader.loadData(sectionPos, new DhDataInputStream(inputStream), level); + ChunkSizedFullDataAccessor result = new ChunkSizedFullDataAccessor(chunkPos); + result.populateFromStream(new DhDataInputStream(inputStream), level); + return result; } finally { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java index 9cd94f53f..77df81aa2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java @@ -38,7 +38,7 @@ public class MessageRegistry // Full data requests & updates this.registerMessage(FullDataSourceRequestMessage.class, FullDataSourceRequestMessage::new); this.registerMessage(FullDataSourceResponseMessage.class, FullDataSourceResponseMessage::new); - this.registerMessage(FullDataSourceUpdateMessage.class, FullDataSourceUpdateMessage::new); + this.registerMessage(FullDataPartialUpdateMessage.class, FullDataPartialUpdateMessage::new); // Generation task prioritization this.registerMessage(GenTaskPriorityRequestMessage.class, GenTaskPriorityRequestMessage::new); From cb95978502e078f20daacb6dca30b83ca8ada628 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 27 Aug 2023 19:56:12 +0500 Subject: [PATCH 037/205] Fix compilation --- .../core/generation/WorldRemoteGenerationQueue.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 91410b3a4..e7066f217 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -66,10 +66,9 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug } @Override - public CompletableFuture submitGenTask(DhLodPos lodPos, byte requiredDataDetail, IWorldGenTaskTracker tracker) + public CompletableFuture submitGenTask(DhSectionPos sectionPos, byte requiredDataDetail, IWorldGenTaskTracker tracker) { - LodUtil.assertTrue(lodPos.detailLevel == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL, "Only highest-detail sections are allowed."); - DhSectionPos sectionPos = new DhSectionPos(lodPos.detailLevel, lodPos); + LodUtil.assertTrue(sectionPos.sectionDetailLevel == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL, "Only highest-detail sections are allowed."); WorldGenQueueEntry entry = new WorldGenQueueEntry(new CompletableFuture<>(), tracker); waitingTasks.put(sectionPos, entry); From 415f16507ba605f09522f5bed6e349cc99593849 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 2 Sep 2023 01:23:56 +0500 Subject: [PATCH 038/205] Add post-rejoin updates --- .../fullDatafile/FullDataFileHandler.java | 3 +- .../GeneratedFullDataFileHandler.java | 2 +- .../RemoteFullDataFileHandler.java | 135 +++++++++++++++++- .../WorldRemoteGenerationQueue.java | 11 +- .../core/level/DhClientLevel.java | 5 +- .../core/level/DhServerLevel.java | 46 +++++- .../core/multiplayer/ClientNetworkState.java | 9 +- .../RemotePlayerConnectionHandler.java | 6 +- .../core/network/NetworkClient.java | 6 +- .../core/network/NetworkEventSource.java | 6 +- .../core/network/NetworkServer.java | 6 +- .../exceptions/InvalidLevelException.java | 9 ++ .../InvalidSectionPosException.java | 9 ++ .../messages/{ => base}/AckMessage.java | 2 +- .../messages/{ => base}/CancelMessage.java | 2 +- .../messages/{ => base}/CloseEvent.java | 2 +- .../{ => base}/CloseReasonMessage.java | 2 +- .../messages/{ => base}/ExceptionMessage.java | 7 +- .../messages/{ => base}/HelloMessage.java | 2 +- .../FullDataSourceRequestMessage.java | 7 +- .../FullDataSourceResponseMessage.java | 2 +- .../GenTaskPriorityRequestMessage.java | 2 +- .../GenTaskPriorityResponseMessage.java | 3 +- .../FullDataChangeSummaryRequestMessage.java | 45 ++++++ .../FullDataChangeSummaryResponseMessage.java | 35 +++++ .../FullDataPartialUpdateMessage.java | 2 +- .../{ => session}/PlayerUUIDMessage.java | 2 +- .../RemotePlayerConfigMessage.java | 2 +- .../FutureTrackableNetworkMessage.java | 2 +- .../core/network/protocol/INetworkObject.java | 18 +++ .../core/network/protocol/MessageHandler.java | 2 +- .../network/protocol/MessageRegistry.java | 15 +- .../core/world/DhServerWorld.java | 2 +- 33 files changed, 358 insertions(+), 51 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/InvalidLevelException.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/InvalidSectionPosException.java rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => base}/AckMessage.java (89%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => base}/CancelMessage.java (83%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => base}/CloseEvent.java (90%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => base}/CloseReasonMessage.java (89%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => base}/ExceptionMessage.java (77%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => base}/HelloMessage.java (87%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => fullData/generation}/FullDataSourceRequestMessage.java (78%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => fullData/generation}/FullDataSourceResponseMessage.java (97%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => fullData/generation/priority}/GenTaskPriorityRequestMessage.java (89%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => fullData/generation/priority}/GenTaskPriorityResponseMessage.java (89%) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryRequestMessage.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryResponseMessage.java rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => fullData/updates}/FullDataPartialUpdateMessage.java (97%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => session}/PlayerUUIDMessage.java (91%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => session}/RemotePlayerConfigMessage.java (91%) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java index 50e6a652c..d0592aaee 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java @@ -50,11 +50,12 @@ public class FullDataFileHandler implements IFullDataSourceProvider { map.put(pos, fileBySectionPos.containsKey(pos) ? 3 // Loaded - : unloadedFiles.containsKey(pos) ? 2 // Unloaded + : this.isFileUnloaded(pos) ? 2 // Unloaded : 1); // Not generated } return map; } + protected boolean isFileUnloaded(DhSectionPos pos) { return unloadedFiles.containsKey(pos); } private LinkedList> onUpdatedListeners = new LinkedList<>(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java index b87e664bd..c8e37ad51 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java @@ -28,7 +28,7 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - private final AtomicReference worldGenQueueRef = new AtomicReference<>(null); + protected final AtomicReference worldGenQueueRef = new AtomicReference<>(null); private final ArrayList onWorldGenTaskCompleteListeners = new ArrayList<>(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java index c714d0578..a5206b2c9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java @@ -1,13 +1,146 @@ package com.seibel.distanthorizons.core.file.fullDatafile; +import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; +import com.seibel.distanthorizons.core.generation.IWorldGenerationQueue; +import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker; import com.seibel.distanthorizons.core.level.IDhLevel; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.multiplayer.ClientNetworkState; +import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; +import com.seibel.distanthorizons.core.network.exceptions.InvalidSectionPosException; +import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataChangeSummaryRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataChangeSummaryResponseMessage; +import com.seibel.distanthorizons.core.pos.DhLodPos; +import com.seibel.distanthorizons.core.pos.DhSectionPos; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import javax.annotation.CheckForNull; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; +import java.util.stream.Collectors; public class RemoteFullDataFileHandler extends GeneratedFullDataFileHandler { - public RemoteFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure) + private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + + @CheckForNull + private final ClientNetworkState networkState; + + private final Set visitedSections = ConcurrentHashMap.newKeySet(); + private final ConcurrentMap sectionsToUpdate = new ConcurrentHashMap<>(); + private final AtomicBoolean isUpdating = new AtomicBoolean(false); + private boolean invalidSectionsFound = false; + + public RemoteFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure, @Nullable ClientNetworkState networkState) { super(level, saveStructure); + this.networkState = networkState; } + private void sendUpdateChecks() + { + assert this.networkState != null; + + if (this.invalidSectionsFound) + this.sectionsToUpdate.clear(); + + if (this.sectionsToUpdate.isEmpty()) + return; + + if (this.isUpdating.getAndSet(true)) + return; + + Map block = sectionsToUpdate.entrySet().stream() + .limit(20) + .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().baseMetaData.checksum)); + for (DhSectionPos pos : block.keySet()) + sectionsToUpdate.remove(pos); + + Consumer chunkDataConsumer = (ChunkSizedFullDataAccessor data) -> { + DhLodPos pos = data.getLodPos().convertToDetailLevel(CompleteFullDataSource.SECTION_SIZE_OFFSET); + this.write(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data); + }; + + this.networkState.getClient().sendRequest(new FullDataChangeSummaryRequestMessage(level.getLevelWrapper(), block)) + .handle((response, throwable) -> + { + try + { + if (throwable != null) + throw throwable; + + IWorldGenerationQueue queue = this.worldGenQueueRef.get(); + if (queue == null) + return null; + + for (DhSectionPos pos : response.changedPosList) + { + queue.submitGenTask(pos, pos.sectionDetailLevel, new IWorldGenTaskTracker() { + @Override + public boolean isMemoryAddressValid() + { + return true; + } + + @NotNull + @Override + public Consumer getChunkDataConsumer() + { + return chunkDataConsumer; + } + }); + } + } + catch (InvalidLevelException ignored) + { + // We're too late + } + catch (InvalidSectionPosException e) + { + LOGGER.error("Invalid sections found. Updating will not continue.", e); + invalidSectionsFound = true; + } + catch (Throwable e) + { + LOGGER.error("Error while checking section updates", e); + } + finally + { + this.isUpdating.set(false); + sendUpdateChecks(); + } + + return null; + }); + } + + @Override + public FullDataMetaFile getFileIfExist(DhSectionPos pos) + { + if (this.networkState == null || !this.isFileUnloaded(pos)) + return super.getFileIfExist(pos); + + FullDataMetaFile metaFile = super.getFileIfExist(pos); + if (metaFile == null) + return null; + + LOGGER.info("Checking server updates for section {}", pos); + pos.forEachChildAtLevel(DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL, childPos -> + { + FullDataMetaFile childMetaFile = super.getFileIfExist(childPos); + if (childMetaFile != null && visitedSections.add(childPos)) + sectionsToUpdate.put(childPos, childMetaFile); + }); + sendUpdateChecks(); + + return metaFile; + } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index e7066f217..c6290190b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -9,18 +9,15 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.multiplayer.ClientNetworkState; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; -import com.seibel.distanthorizons.core.network.messages.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.FullDataSourceResponseMessage; -import com.seibel.distanthorizons.core.network.messages.GenTaskPriorityRequestMessage; -import com.seibel.distanthorizons.core.network.messages.GenTaskPriorityResponseMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceResponseMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.priority.GenTaskPriorityRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.priority.GenTaskPriorityResponseMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; -import com.seibel.distanthorizons.core.pos.DhChunkPos; -import com.seibel.distanthorizons.core.pos.DhLodPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; import com.seibel.distanthorizons.core.util.LodUtil; -import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import io.netty.channel.ChannelException; import org.apache.logging.log4j.Logger; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 7a7a22d95..bdad12290 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -1,7 +1,6 @@ package com.seibel.distanthorizons.core.level; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; import com.seibel.distanthorizons.core.file.fullDatafile.RemoteFullDataFileHandler; @@ -11,7 +10,7 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.multiplayer.ClientNetworkState; import com.seibel.distanthorizons.core.network.NetworkClient; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; -import com.seibel.distanthorizons.core.network.messages.FullDataPartialUpdateMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataPartialUpdateMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhSectionPos; @@ -65,7 +64,7 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel { this.levelWrapper = clientLevelWrapper; this.saveStructure = saveStructure; - this.dataFileHandler = new RemoteFullDataFileHandler(this, saveStructure); + this.dataFileHandler = new RemoteFullDataFileHandler(this, saveStructure, networkState); this.networkState = networkState; this.worldGenModule = new WorldGenModule(dataFileHandler, this); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 46d8c41a4..b51fe9bad 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -6,14 +6,24 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedF import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IIncompleteFullDataSource; +import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile; import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.multiplayer.ServerPlayerState; import com.seibel.distanthorizons.core.multiplayer.RemotePlayerConnectionHandler; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkServer; +import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; +import com.seibel.distanthorizons.core.network.exceptions.InvalidSectionPosException; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; -import com.seibel.distanthorizons.core.network.messages.*; +import com.seibel.distanthorizons.core.network.messages.base.CancelMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceResponseMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.priority.GenTaskPriorityRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.priority.GenTaskPriorityResponseMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataChangeSummaryRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataChangeSummaryResponseMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataPartialUpdateMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhLodPos; @@ -28,7 +38,9 @@ import com.seibel.distanthorizons.coreapi.util.math.Vec3d; import org.apache.logging.log4j.Logger; import javax.annotation.CheckForNull; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import java.util.concurrent.*; public class DhServerLevel extends DhLevel implements IDhServerLevel @@ -120,6 +132,38 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel )); }); + this.eventSource.registerHandler(FullDataChangeSummaryRequestMessage.class, msg -> + { + ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); + if (serverPlayerState == null) return; + + if (serverPlayerState.serverPlayer.getLevel() != this.serverLevelWrapper) + return; + + if (!msg.isLevelValid(this.serverLevelWrapper)) + { + msg.sendResponse(new InvalidLevelException("Invalid level")); + return; + } + + // Load files and check checksums + HashSet changedPosList = new HashSet<>(); + for (Map.Entry entry : msg.checksums.entrySet()) + { + FullDataMetaFile metaFile = serverside.dataFileHandler.getFileIfExist(entry.getKey()); + if (metaFile == null) + { + msg.sendResponse(new InvalidSectionPosException("Not generated section pos: "+entry.getKey())); + return; + } + + if (entry.getValue() != metaFile.baseMetaData.checksum) + changedPosList.add(entry.getKey()); + } + + msg.sendResponse(new FullDataChangeSummaryResponseMessage(changedPosList)); + }); + this.eventSource.registerHandler(CancelMessage.class, msg -> { IncompleteDataSourceEntry entry = this.fullDataRequests.remove(msg.futureId); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java index f1d4411b6..c8699d6ab 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java @@ -4,12 +4,11 @@ import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkClient; -import com.seibel.distanthorizons.core.network.messages.AckMessage; -import com.seibel.distanthorizons.core.network.messages.HelloMessage; -import com.seibel.distanthorizons.core.network.messages.PlayerUUIDMessage; -import com.seibel.distanthorizons.core.network.messages.RemotePlayerConfigMessage; +import com.seibel.distanthorizons.core.network.messages.base.AckMessage; +import com.seibel.distanthorizons.core.network.messages.base.HelloMessage; +import com.seibel.distanthorizons.core.network.messages.session.PlayerUUIDMessage; +import com.seibel.distanthorizons.core.network.messages.session.RemotePlayerConfigMessage; import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.NotNull; import java.io.Closeable; import java.util.UUID; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java index 52048f097..858cdfab1 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java @@ -4,9 +4,9 @@ import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkServer; -import com.seibel.distanthorizons.core.network.messages.AckMessage; -import com.seibel.distanthorizons.core.network.messages.CloseEvent; -import com.seibel.distanthorizons.core.network.messages.PlayerUUIDMessage; +import com.seibel.distanthorizons.core.network.messages.base.AckMessage; +import com.seibel.distanthorizons.core.network.messages.base.CloseEvent; +import com.seibel.distanthorizons.core.network.messages.session.PlayerUUIDMessage; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import io.netty.channel.ChannelHandlerContext; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index f59d9e751..5dd6618da 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -1,9 +1,9 @@ package com.seibel.distanthorizons.core.network; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.network.messages.CloseEvent; -import com.seibel.distanthorizons.core.network.messages.CloseReasonMessage; -import com.seibel.distanthorizons.core.network.messages.HelloMessage; +import com.seibel.distanthorizons.core.network.messages.base.CloseEvent; +import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage; +import com.seibel.distanthorizons.core.network.messages.base.HelloMessage; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.MessageHandler; import com.seibel.distanthorizons.core.network.protocol.NetworkChannelInitializer; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index 3e9837f56..8e15adaca 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -4,9 +4,9 @@ import com.google.common.collect.HashBasedTable; import com.google.common.collect.Table; import com.google.common.collect.Tables; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.network.messages.CancelMessage; -import com.seibel.distanthorizons.core.network.messages.CloseEvent; -import com.seibel.distanthorizons.core.network.messages.ExceptionMessage; +import com.seibel.distanthorizons.core.network.messages.base.CancelMessage; +import com.seibel.distanthorizons.core.network.messages.base.CloseEvent; +import com.seibel.distanthorizons.core.network.messages.base.ExceptionMessage; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.MessageRegistry; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java index f110bc7a7..4177a23d4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java @@ -1,9 +1,9 @@ package com.seibel.distanthorizons.core.network; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.network.messages.CloseReasonMessage; -import com.seibel.distanthorizons.core.network.messages.CloseEvent; -import com.seibel.distanthorizons.core.network.messages.HelloMessage; +import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage; +import com.seibel.distanthorizons.core.network.messages.base.CloseEvent; +import com.seibel.distanthorizons.core.network.messages.base.HelloMessage; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.MessageHandler; import com.seibel.distanthorizons.core.network.protocol.NetworkChannelInitializer; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/InvalidLevelException.java b/core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/InvalidLevelException.java new file mode 100644 index 000000000..dca7295d8 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/InvalidLevelException.java @@ -0,0 +1,9 @@ +package com.seibel.distanthorizons.core.network.exceptions; + +public class InvalidLevelException extends Exception +{ + public InvalidLevelException(String message) + { + super(message); + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/InvalidSectionPosException.java b/core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/InvalidSectionPosException.java new file mode 100644 index 000000000..cd505c304 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/InvalidSectionPosException.java @@ -0,0 +1,9 @@ +package com.seibel.distanthorizons.core.network.exceptions; + +public class InvalidSectionPosException extends Exception +{ + public InvalidSectionPosException(String message) + { + super(message); + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/AckMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/AckMessage.java similarity index 89% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/AckMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/AckMessage.java index fc9c4ee4a..fd0684e65 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/AckMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/AckMessage.java @@ -1,4 +1,4 @@ -package com.seibel.distanthorizons.core.network.messages; +package com.seibel.distanthorizons.core.network.messages.base; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CancelMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CancelMessage.java similarity index 83% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/CancelMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CancelMessage.java index 649fcfd20..ee0118940 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CancelMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CancelMessage.java @@ -1,4 +1,4 @@ -package com.seibel.distanthorizons.core.network.messages; +package com.seibel.distanthorizons.core.network.messages.base; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import io.netty.buffer.ByteBuf; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseEvent.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CloseEvent.java similarity index 90% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseEvent.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CloseEvent.java index a4a02bb44..a25a47133 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseEvent.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CloseEvent.java @@ -1,4 +1,4 @@ -package com.seibel.distanthorizons.core.network.messages; +package com.seibel.distanthorizons.core.network.messages.base; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import io.netty.buffer.ByteBuf; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseReasonMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CloseReasonMessage.java similarity index 89% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseReasonMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CloseReasonMessage.java index 590678fc1..21c08989d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseReasonMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CloseReasonMessage.java @@ -1,4 +1,4 @@ -package com.seibel.distanthorizons.core.network.messages; +package com.seibel.distanthorizons.core.network.messages.base; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ExceptionMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ExceptionMessage.java similarity index 77% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/ExceptionMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ExceptionMessage.java index 4496fd2df..d4d4dc53b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ExceptionMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ExceptionMessage.java @@ -1,8 +1,9 @@ -package com.seibel.distanthorizons.core.network.messages; +package com.seibel.distanthorizons.core.network.messages.base; +import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; +import com.seibel.distanthorizons.core.network.exceptions.InvalidSectionPosException; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; -import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import io.netty.buffer.ByteBuf; import java.util.ArrayList; @@ -14,6 +15,8 @@ public class ExceptionMessage extends FutureTrackableNetworkMessage {{ // All exceptions here must include constructor: (String) add(RateLimitedException.class); + add(InvalidLevelException.class); + add(InvalidSectionPosException.class); }}; public Exception exception; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/HelloMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/HelloMessage.java similarity index 87% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/HelloMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/HelloMessage.java index 8c1ae2b7b..6d9689226 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/HelloMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/HelloMessage.java @@ -1,4 +1,4 @@ -package com.seibel.distanthorizons.core.network.messages; +package com.seibel.distanthorizons.core.network.messages.base; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import com.seibel.distanthorizons.coreapi.ModInfo; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceRequestMessage.java similarity index 78% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceRequestMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceRequestMessage.java index e05f518d5..0f6ff84b2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceRequestMessage.java @@ -1,17 +1,20 @@ -package com.seibel.distanthorizons.core.network.messages; +package com.seibel.distanthorizons.core.network.messages.fullData.generation; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import com.seibel.distanthorizons.core.pos.DhSectionPos; import io.netty.buffer.ByteBuf; +import javax.annotation.Nullable; + public class FullDataSourceRequestMessage extends FutureTrackableNetworkMessage { public DhSectionPos dhSectionPos; public FullDataSourceRequestMessage() {} - public FullDataSourceRequestMessage(DhSectionPos dhSectionPos) { + public FullDataSourceRequestMessage(DhSectionPos dhSectionPos) + { this.dhSectionPos = dhSectionPos; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java similarity index 97% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java index 4f3ca1716..025120380 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java @@ -1,4 +1,4 @@ -package com.seibel.distanthorizons.core.network.messages; +package com.seibel.distanthorizons.core.network.messages.fullData.generation; import com.seibel.distanthorizons.core.dataObjects.fullData.loader.AbstractFullDataSourceLoader; import com.seibel.distanthorizons.core.dataObjects.fullData.loader.CompleteFullDataSourceLoader; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/GenTaskPriorityRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/priority/GenTaskPriorityRequestMessage.java similarity index 89% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/GenTaskPriorityRequestMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/priority/GenTaskPriorityRequestMessage.java index 45be3c077..aad805ae3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/GenTaskPriorityRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/priority/GenTaskPriorityRequestMessage.java @@ -1,4 +1,4 @@ -package com.seibel.distanthorizons.core.network.messages; +package com.seibel.distanthorizons.core.network.messages.fullData.generation.priority; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.pos.DhSectionPos; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/GenTaskPriorityResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/priority/GenTaskPriorityResponseMessage.java similarity index 89% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/GenTaskPriorityResponseMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/priority/GenTaskPriorityResponseMessage.java index 706309699..81b95c064 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/GenTaskPriorityResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/priority/GenTaskPriorityResponseMessage.java @@ -1,11 +1,10 @@ -package com.seibel.distanthorizons.core.network.messages; +package com.seibel.distanthorizons.core.network.messages.fullData.generation.priority; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.pos.DhSectionPos; import io.netty.buffer.ByteBuf; import java.util.HashMap; -import java.util.List; import java.util.Map; public class GenTaskPriorityResponseMessage extends FutureTrackableNetworkMessage diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryRequestMessage.java new file mode 100644 index 000000000..7eefe78f0 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryRequestMessage.java @@ -0,0 +1,45 @@ +package com.seibel.distanthorizons.core.network.messages.fullData.updates; + +import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; +import io.netty.buffer.ByteBuf; + +import java.util.HashMap; +import java.util.Map; + +public class FullDataChangeSummaryRequestMessage extends FutureTrackableNetworkMessage +{ + public Map checksums = new HashMap<>(); + public int levelHashCode; + + public FullDataChangeSummaryRequestMessage() { } + + public FullDataChangeSummaryRequestMessage(ILevelWrapper levelWrapper, Map checksums) + { + // TODO Multiverse support + this.levelHashCode = levelWrapper.getDimensionType().getDimensionName().hashCode(); + this.checksums = checksums; + } + + @Override + public void encode0(ByteBuf out) + { + // TODO Multiverse support + out.writeInt(levelHashCode); + encodeCollection(out, checksums.entrySet()); + } + + @Override + public void decode0(ByteBuf in) + { + levelHashCode = in.readInt(); + decodeMap(in, checksums, DhSectionPos::zero, () -> 0); + } + + public boolean isLevelValid(ILevelWrapper levelWrapper) + { + return levelWrapper.getDimensionType().getDimensionName().hashCode() == levelHashCode; + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryResponseMessage.java new file mode 100644 index 000000000..24560cd59 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryResponseMessage.java @@ -0,0 +1,35 @@ +package com.seibel.distanthorizons.core.network.messages.fullData.updates; + +import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.pos.DhSectionPos; +import io.netty.buffer.ByteBuf; + +import java.util.HashSet; +import java.util.Set; + +public class FullDataChangeSummaryResponseMessage extends FutureTrackableNetworkMessage +{ + public final Set changedPosList; + + public FullDataChangeSummaryResponseMessage() + { + this.changedPosList = new HashSet<>(); + } + + public FullDataChangeSummaryResponseMessage(Set changedPosList) + { + this.changedPosList = changedPosList; + } + + @Override + public void encode0(ByteBuf out) + { + encodeCollection(out, changedPosList); + } + + @Override + public void decode0(ByteBuf in) + { + decodeCollection(in, changedPosList, DhSectionPos::zero); + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataPartialUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java similarity index 97% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataPartialUpdateMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java index 03d0614db..d1b66dbe8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/FullDataPartialUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java @@ -1,4 +1,4 @@ -package com.seibel.distanthorizons.core.network.messages; +package com.seibel.distanthorizons.core.network.messages.fullData.updates; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.level.DhServerLevel; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PlayerUUIDMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/session/PlayerUUIDMessage.java similarity index 91% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/PlayerUUIDMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/session/PlayerUUIDMessage.java index b7fd60bd1..4ae1980ba 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PlayerUUIDMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/session/PlayerUUIDMessage.java @@ -1,4 +1,4 @@ -package com.seibel.distanthorizons.core.network.messages; +package com.seibel.distanthorizons.core.network.messages.session; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/RemotePlayerConfigMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/session/RemotePlayerConfigMessage.java similarity index 91% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/RemotePlayerConfigMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/session/RemotePlayerConfigMessage.java index 1e235ef1b..9c7c82f0d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/RemotePlayerConfigMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/session/RemotePlayerConfigMessage.java @@ -1,4 +1,4 @@ -package com.seibel.distanthorizons.core.network.messages; +package com.seibel.distanthorizons.core.network.messages.session; import com.seibel.distanthorizons.core.multiplayer.MultiplayerConfig; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java index 6c3eb8d5e..16b60da99 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java @@ -1,6 +1,6 @@ package com.seibel.distanthorizons.core.network.protocol; -import com.seibel.distanthorizons.core.network.messages.ExceptionMessage; +import com.seibel.distanthorizons.core.network.messages.base.ExceptionMessage; import io.netty.buffer.ByteBuf; import java.util.concurrent.atomic.AtomicInteger; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java index f70d5f94a..6d2392325 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java @@ -1,7 +1,9 @@ package com.seibel.distanthorizons.core.network.protocol; import io.netty.buffer.ByteBuf; +import org.jetbrains.annotations.Contract; +import javax.annotation.Nullable; import java.nio.charset.StandardCharsets; import java.util.*; import java.util.concurrent.ConcurrentHashMap; @@ -20,6 +22,22 @@ public interface INetworkObject return obj; } + @Contract("_, null -> false; _, !null -> true") + default boolean encodeOptional(ByteBuf outputByteBuf, Object value) + { + boolean isNull = value != null; + outputByteBuf.writeBoolean(isNull); + return isNull; + } + + @Nullable + default T decodeOptional(ByteBuf inputByteBuf, Supplier decoder) + { + return inputByteBuf.readBoolean() + ? decoder.get() + : null; + } + default void encodeString(String inputString, ByteBuf outputByteBuf) { byte[] bytes = inputString.getBytes(StandardCharsets.UTF_8); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java index 0a8a7e946..1d4b59a95 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java @@ -1,7 +1,7 @@ package com.seibel.distanthorizons.core.network.protocol; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.network.messages.CloseEvent; +import com.seibel.distanthorizons.core.network.messages.base.CloseEvent; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java index 77df81aa2..cd2fd701e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java @@ -2,7 +2,16 @@ package com.seibel.distanthorizons.core.network.protocol; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; -import com.seibel.distanthorizons.core.network.messages.*; +import com.seibel.distanthorizons.core.network.messages.base.*; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceResponseMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.priority.GenTaskPriorityRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.priority.GenTaskPriorityResponseMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataChangeSummaryRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataChangeSummaryResponseMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataPartialUpdateMessage; +import com.seibel.distanthorizons.core.network.messages.session.PlayerUUIDMessage; +import com.seibel.distanthorizons.core.network.messages.session.RemotePlayerConfigMessage; import java.util.HashMap; import java.util.Map; @@ -43,6 +52,10 @@ public class MessageRegistry // Generation task prioritization this.registerMessage(GenTaskPriorityRequestMessage.class, GenTaskPriorityRequestMessage::new); this.registerMessage(GenTaskPriorityResponseMessage.class, GenTaskPriorityResponseMessage::new); + + // Post-rejoin updates + this.registerMessage(FullDataChangeSummaryRequestMessage.class, FullDataChangeSummaryRequestMessage::new); + this.registerMessage(FullDataChangeSummaryResponseMessage.class, FullDataChangeSummaryResponseMessage::new); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java index fdbca1b2b..3e30bec10 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java @@ -8,7 +8,7 @@ import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.multiplayer.ServerPlayerState; import com.seibel.distanthorizons.core.multiplayer.RemotePlayerConnectionHandler; import com.seibel.distanthorizons.core.network.NetworkServer; -import com.seibel.distanthorizons.core.network.messages.RemotePlayerConfigMessage; +import com.seibel.distanthorizons.core.network.messages.session.RemotePlayerConfigMessage; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; From e02eddc60eb41a533806e4ae037e7c301dd63a61 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Thu, 7 Sep 2023 21:07:16 +0500 Subject: [PATCH 039/205] Fix compiling --- .../core/file/fullDatafile/RemoteFullDataFileHandler.java | 2 +- .../com/seibel/distanthorizons/core/level/DhServerLevel.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java index 6798ffb7f..2528dfd87 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java @@ -85,7 +85,7 @@ public class RemoteFullDataFileHandler extends GeneratedFullDataFileHandler Consumer chunkDataConsumer = (ChunkSizedFullDataAccessor data) -> { DhLodPos pos = data.getLodPos().convertToDetailLevel(CompleteFullDataSource.SECTION_SIZE_OFFSET); - this.write(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data); + this.writeChunkDataToFile(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data); }; this.networkState.getClient().sendRequest(new FullDataChangeSummaryRequestMessage(level.getLevelWrapper(), block)) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index a8cb7b4e5..202502855 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -123,7 +123,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel { IncompleteDataSourceEntry entry = incompleteDataSources.computeIfAbsent(msg.dhSectionPos, pos -> { IncompleteDataSourceEntry newEntry = new IncompleteDataSourceEntry(); - serverside.dataFileHandler.read(msg.dhSectionPos).thenAccept(fullDataSource -> { + serverside.dataFileHandler.readAsync(msg.dhSectionPos).thenAccept(fullDataSource -> { newEntry.fullDataSource = fullDataSource; }); return newEntry; From 0e7a96d308815a4eec8d4e5652e8f532e0dbb3ed Mon Sep 17 00:00:00 2001 From: Steveplays28 Date: Sat, 9 Sep 2023 21:03:17 +0200 Subject: [PATCH 040/205] fix: Fix missing `IServerKeyedClientLevel` import --- .../com/seibel/distanthorizons/core/api/internal/ClientApi.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index 10e08bcef..8eeedd643 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -22,6 +22,7 @@ package com.seibel.distanthorizons.core.api.internal; import com.seibel.distanthorizons.api.methods.events.abstractEvents.*; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager; +import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.util.objects.Pair; import com.seibel.distanthorizons.core.world.*; From 6f4e105542faf024ccfaa34c868803e456dbffb5 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 18 Sep 2023 13:44:19 +0500 Subject: [PATCH 041/205] Fix compiling --- .../fullData/sources/CompleteFullDataSource.java | 10 +++++----- .../file/fullDatafile/RemoteFullDataFileHandler.java | 10 +++++++--- .../core/generation/WorldRemoteGenerationQueue.java | 4 ++-- .../distanthorizons/core/level/DhServerLevel.java | 2 +- .../fullData/updates/FullDataPartialUpdateMessage.java | 4 ++-- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java index fad706b66..edebb5f2d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java @@ -391,14 +391,14 @@ public class CompleteFullDataSource extends FullDataArrayAccessor implements IFu public void splitIntoChunkSizedAccessors(Consumer consumer) { - LodUtil.assertTrue(sectionPos.sectionDetailLevel == DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, "Data source detail level must be at block detail level."); + LodUtil.assertTrue(sectionPos.getDetailLevel() == DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, "Data source detail level must be at block detail level."); sectionPos.forEachChildAtLevel(LodUtil.CHUNK_DETAIL_LEVEL, childPos -> { - ChunkSizedFullDataAccessor accessor = new ChunkSizedFullDataAccessor(new DhChunkPos(childPos.sectionX, childPos.sectionZ)); + ChunkSizedFullDataAccessor accessor = new ChunkSizedFullDataAccessor(new DhChunkPos(childPos.getX(), childPos.getZ())); - int detailLevelDifference = sectionPos.sectionDetailLevel - childPos.sectionDetailLevel; - int childRelativeX = childPos.sectionX - sectionPos.sectionX * BitShiftUtil.powerOfTwo(detailLevelDifference); - int childRelativeZ = childPos.sectionZ - sectionPos.sectionZ * BitShiftUtil.powerOfTwo(detailLevelDifference); + int detailLevelDifference = sectionPos.getDetailLevel() - childPos.getDetailLevel(); + int childRelativeX = childPos.getX() - sectionPos.getX() * BitShiftUtil.powerOfTwo(detailLevelDifference); + int childRelativeZ = childPos.getZ() - sectionPos.getZ() * BitShiftUtil.powerOfTwo(detailLevelDifference); subView( LodUtil.CHUNK_WIDTH, diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java index 2528dfd87..111acdbab 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java @@ -84,8 +84,8 @@ public class RemoteFullDataFileHandler extends GeneratedFullDataFileHandler sectionsToUpdate.remove(pos); Consumer chunkDataConsumer = (ChunkSizedFullDataAccessor data) -> { - DhLodPos pos = data.getLodPos().convertToDetailLevel(CompleteFullDataSource.SECTION_SIZE_OFFSET); - this.writeChunkDataToFile(new DhSectionPos(pos.detailLevel, pos.x, pos.z), data); + DhSectionPos pos = data.getSectionPos().convertNewToDetailLevel(CompleteFullDataSource.SECTION_SIZE_OFFSET); + this.writeChunkDataToFile(new DhSectionPos(pos.getDetailLevel(), pos.getX(), pos.getZ()), data); }; this.networkState.getClient().sendRequest(new FullDataChangeSummaryRequestMessage(level.getLevelWrapper(), block)) @@ -102,7 +102,7 @@ public class RemoteFullDataFileHandler extends GeneratedFullDataFileHandler for (DhSectionPos pos : response.changedPosList) { - queue.submitGenTask(pos, pos.sectionDetailLevel, new IWorldGenTaskTracker() { + queue.submitGenTask(pos, pos.getDetailLevel(), new IWorldGenTaskTracker() { @Override public boolean isMemoryAddressValid() { @@ -144,6 +144,10 @@ public class RemoteFullDataFileHandler extends GeneratedFullDataFileHandler @Override public FullDataMetaFile getFileIfExist(DhSectionPos pos) { + // This feature is broken - same data may produce different hashes, apparently + if (true) + return super.getFileIfExist(pos); + if (this.networkState == null || !this.isFileUnloaded(pos)) return super.getFileIfExist(pos); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index c6290190b..704da2f47 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -65,7 +65,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug @Override public CompletableFuture submitGenTask(DhSectionPos sectionPos, byte requiredDataDetail, IWorldGenTaskTracker tracker) { - LodUtil.assertTrue(sectionPos.sectionDetailLevel == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL, "Only highest-detail sections are allowed."); + LodUtil.assertTrue(sectionPos.getDetailLevel() == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL, "Only highest-detail sections are allowed."); WorldGenQueueEntry entry = new WorldGenQueueEntry(new CompletableFuture<>(), tracker); waitingTasks.put(sectionPos, entry); @@ -74,7 +74,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug private int posDistanceSquared(DhBlockPos2D targetPos, DhSectionPos pos) { - return (int) pos.getCenter().getCenterBlockPos().distSquared(targetPos); + return (int) pos.getCenterBlockPos().distSquared(targetPos); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 36379395f..35d1947c0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -110,7 +110,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel if (serverPlayerState.serverPlayer.getLevel() != this.serverLevelWrapper) return; - LOGGER.debug("FullDataSourceRequestMessage received at pos ({}, {}) with detail level {}", msg.dhSectionPos.sectionX, msg.dhSectionPos.sectionZ, msg.dhSectionPos.sectionDetailLevel); + LOGGER.debug("FullDataSourceRequestMessage received at pos ({}, {}) with detail level {}", msg.dhSectionPos.getX(), msg.dhSectionPos.getZ(), msg.dhSectionPos.getDetailLevel()); if (serverPlayerState.pendingFullDataRequests.incrementAndGet() > rateLimitConfig.get()) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java index 601c5be7a..bf2687aa8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java @@ -63,8 +63,8 @@ public class FullDataPartialUpdateMessage extends FutureTrackableNetworkMessage out.writeInt(levelHashCode); - out.writeInt(fullDataAccessor.pos.x); - out.writeInt(fullDataAccessor.pos.z); + out.writeInt(fullDataAccessor.chunkPos.x); + out.writeInt(fullDataAccessor.chunkPos.z); out.writeInt(outputStream.size()); out.writeBytes(outputStream.toByteArray()); From 2cfc2c81c8cd8c8ef9801ee8b9bf3f6c6e83175b Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 19 Sep 2023 17:28:26 +0500 Subject: [PATCH 042/205] Fix future id collisions between c<->s (cause of occasional hangs on disconnection) Add packet trace logging --- .../core/multiplayer/MultiplayerConfig.java | 8 ++ .../core/network/NetworkClient.java | 2 +- .../core/network/NetworkEventSource.java | 81 ++++++++++++------- .../core/network/NetworkServer.java | 2 +- .../messages/base/CloseReasonMessage.java | 7 +- .../messages/base/ExceptionMessage.java | 6 ++ .../network/messages/base/HelloMessage.java | 5 ++ .../FullDataSourceRequestMessage.java | 7 ++ .../FullDataSourceResponseMessage.java | 9 +++ .../GenTaskPriorityRequestMessage.java | 6 ++ .../GenTaskPriorityResponseMessage.java | 6 ++ .../FullDataChangeSummaryRequestMessage.java | 8 ++ .../FullDataChangeSummaryResponseMessage.java | 6 ++ .../updates/FullDataPartialUpdateMessage.java | 23 ++++-- .../messages/session/PlayerUUIDMessage.java | 5 ++ .../session/RemotePlayerConfigMessage.java | 5 ++ .../FutureTrackableNetworkMessage.java | 40 ++++++++- .../core/network/protocol/MessageHandler.java | 20 +++-- .../core/network/protocol/NetworkMessage.java | 14 ++++ 19 files changed, 214 insertions(+), 46 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/MultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/MultiplayerConfig.java index 6b0fde3e0..f70cc6c0e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/MultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/MultiplayerConfig.java @@ -23,4 +23,12 @@ public class MultiplayerConfig implements INetworkObject this.fullDataRequestRateLimit = in.readInt(); } + @Override public String toString() + { + return "MultiplayerConfig{" + + "renderDistance=" + renderDistance + + ", fullDataRequestRateLimit=" + fullDataRequestRateLimit + + '}'; + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index 3382f2d0e..467166cd9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -75,7 +75,7 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable .group(this.workerGroup) .channel(NioSocketChannel.class) .option(ChannelOption.SO_KEEPALIVE, true) - .handler(new NetworkChannelInitializer(new MessageHandler(this::handleMessage))); + .handler(new NetworkChannelInitializer(new MessageHandler(this::handleMessage, this::addNewContext))); private EConnectionState connectionState = EConnectionState.INITIAL; private Channel channel; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index 594ab7437..d48d5296b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -45,7 +45,7 @@ public abstract class NetworkEventSource { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); protected final ConcurrentMap, Set>> handlers = new ConcurrentHashMap<>(); - private final Table> pendingFutures = Tables.synchronizedTable(HashBasedTable.create()); + private final ConcurrentMap>> pendingFutures = new ConcurrentHashMap<>(); protected boolean hasHandler(Class handlerClass) { @@ -57,9 +57,6 @@ public abstract class NetworkEventSource { boolean handled = false; - if (message instanceof FutureTrackableNetworkMessage) - ((FutureTrackableNetworkMessage) message).futureId |= (long) message.getChannelContext().hashCode() << 32; - Set> handlerList = this.handlers.get(message.getClass()); if (handlerList != null) { @@ -73,27 +70,34 @@ public abstract class NetworkEventSource if (message instanceof FutureTrackableNetworkMessage) { FutureTrackableNetworkMessage trackableMessage = (FutureTrackableNetworkMessage)message; - CompletableFuture future = pendingFutures.get(message.getChannelContext(), trackableMessage.futureId); - if (future != null) + ConcurrentMap> subMap = pendingFutures.get(message.getChannelContext()); + if (subMap != null) { - handled = true; - - if (message instanceof ExceptionMessage) - future.completeExceptionally(((ExceptionMessage) message).exception); - else - future.complete(trackableMessage); + CompletableFuture future = subMap.get(trackableMessage.futureId); + if (future != null) + { + handled = true; + + if (message instanceof ExceptionMessage) + future.completeExceptionally(((ExceptionMessage) message).exception); + else + future.complete(trackableMessage); + } } } if (!handled) { - String error = "Unhandled message type: " + message.getClass().getSimpleName(); - if (message instanceof FutureTrackableNetworkMessage) - error += ", future id: " + ((FutureTrackableNetworkMessage) message).futureId; + String error = "Unhandled message: " + message; LOGGER.warn(error); } } + protected void addNewContext(ChannelHandlerContext ctx) + { + this.pendingFutures.put(ctx, new ConcurrentHashMap<>()); + } + public void registerHandler(Class handlerClass, Consumer handlerImplementation) { this.handlers.computeIfAbsent(handlerClass, missingHandlerClass -> @@ -115,20 +119,37 @@ public abstract class NetworkEventSource protected CompletableFuture sendRequest(ChannelHandlerContext ctx, FutureTrackableNetworkMessage msg) { - msg.futureId |= (long) ctx.hashCode() << 32; msg.setChannelContext(ctx); CompletableFuture responseFuture = new CompletableFuture<>(); responseFuture.handle((response, throwable) -> { if (!(throwable instanceof ChannelException)) - pendingFutures.remove(ctx, msg.futureId); + { + ConcurrentMap> subMap = pendingFutures.get(ctx); + if (subMap != null) + subMap.remove(msg.futureId); + } if (throwable instanceof CancellationException) msg.sendResponse(new CancelMessage()); return null; }); - pendingFutures.put(ctx, msg.futureId, (CompletableFuture) responseFuture); + + ConcurrentMap> subMap = pendingFutures.get(ctx); + if (subMap == null) { + // Was deleted before adding + responseFuture.completeExceptionally(ctx.channel().closeFuture().cause()); + return responseFuture; + } + //noinspection unchecked + subMap.put(msg.futureId, (CompletableFuture) responseFuture); + if (!pendingFutures.containsKey(ctx)) { + // Was deleted while adding + responseFuture.completeExceptionally(ctx.channel().closeFuture().cause()); + return responseFuture; + } + // If passed until here, cancelling is up to the cleaning side ctx.writeAndFlush(msg).addListener(writeFuture -> { if (writeFuture.cause() != null) { @@ -138,21 +159,19 @@ public abstract class NetworkEventSource return responseFuture; } - protected final void completeAllFuturesExceptionally(ChannelHandlerContext ctx, Throwable cause) { - synchronized (pendingFutures) - { - for (CompletableFuture futureData : pendingFutures.row(ctx).values()) - futureData.completeExceptionally(cause); - pendingFutures.row(ctx).clear(); - } + protected final void completeAllFuturesExceptionally(ChannelHandlerContext ctx, Throwable cause) + { + ConcurrentMap> map = pendingFutures.remove(ctx); + if (map == null) return; + + for (CompletableFuture future : map.values()) + future.completeExceptionally(cause); } - protected final void completeAllFuturesExceptionally(Throwable cause) { - synchronized (pendingFutures) - { - for (ChannelHandlerContext ctx : pendingFutures.rowKeySet()) - this.completeAllFuturesExceptionally(ctx, cause); - } + protected final void completeAllFuturesExceptionally(Throwable cause) + { + for (ChannelHandlerContext ctx : pendingFutures.keySet()) + this.completeAllFuturesExceptionally(ctx, cause); } public void close() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java index f163b378d..bc65f040d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java @@ -99,7 +99,7 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable .group(this.bossGroup, this.workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.DEBUG)) - .childHandler(new NetworkChannelInitializer(new MessageHandler(this::handleMessage))); + .childHandler(new NetworkChannelInitializer(new MessageHandler(this::handleMessage, this::addNewContext))); ChannelFuture bindFuture = bootstrap.bind(this.port); bindFuture.addListener((ChannelFuture channelFuture) -> diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CloseReasonMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CloseReasonMessage.java index 1dec83a67..659af753a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CloseReasonMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CloseReasonMessage.java @@ -35,5 +35,10 @@ public class CloseReasonMessage extends NetworkMessage @Override public void decode(ByteBuf in) { this.reason = decodeString(in); } - + + @Override public String toString() + { + return super.toString("reason='" + reason + '\''); + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ExceptionMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ExceptionMessage.java index 3eb7091b3..ffac30807 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ExceptionMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ExceptionMessage.java @@ -58,4 +58,10 @@ public class ExceptionMessage extends FutureTrackableNetworkMessage String message = decodeString(in); exception = exceptionMap.get(id).getDeclaredConstructor(String.class).newInstance(message); } + + @Override public String toString() + { + return super.toString("exception=" + exception); + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/HelloMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/HelloMessage.java index 232de07ad..07c2f8869 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/HelloMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/HelloMessage.java @@ -35,4 +35,9 @@ public class HelloMessage extends NetworkMessage @Override public void decode(ByteBuf in) { this.version = in.readInt(); } + @Override public String toString() + { + return super.toString("version=" + version); + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceRequestMessage.java index 878471f36..65f88874a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceRequestMessage.java @@ -48,4 +48,11 @@ public class FullDataSourceRequestMessage extends FutureTrackableNetworkMessage { dhSectionPos = INetworkObject.decodeStatic(DhSectionPos.zero(), in); } + + @Override + public String toString() + { + return super.toString("dhSectionPos=" + dhSectionPos); + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java index f804619ec..3326cc16d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java @@ -95,4 +95,13 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage dataBuffer.release(); } } + + @Override public String toString() + { + return super.toString( + "levelHashCode=" + levelHashCode + + ", dataBuffer=" + dataBuffer + ); + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/priority/GenTaskPriorityRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/priority/GenTaskPriorityRequestMessage.java index cb5e4b657..b629d537a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/priority/GenTaskPriorityRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/priority/GenTaskPriorityRequestMessage.java @@ -47,4 +47,10 @@ public class GenTaskPriorityRequestMessage extends FutureTrackableNetworkMessage { decodeCollection(in, posList, DhSectionPos::zero); } + + @Override public String toString() + { + return super.toString("posList=" + posList); + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/priority/GenTaskPriorityResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/priority/GenTaskPriorityResponseMessage.java index 6b0e267b2..a9e4a137a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/priority/GenTaskPriorityResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/priority/GenTaskPriorityResponseMessage.java @@ -47,4 +47,10 @@ public class GenTaskPriorityResponseMessage extends FutureTrackableNetworkMessag { decodeMap(in, posList, DhSectionPos::zero, () -> 0); } + + @Override public String toString() + { + return super.toString("posList=" + posList); + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryRequestMessage.java index f60f3980a..090ec4fec 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryRequestMessage.java @@ -61,4 +61,12 @@ public class FullDataChangeSummaryRequestMessage extends FutureTrackableNetworkM return levelWrapper.getDimensionType().getDimensionName().hashCode() == levelHashCode; } + @Override public String toString() + { + return super.toString( + "checksums=" + checksums + + ", levelHashCode=" + levelHashCode + ); + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryResponseMessage.java index 8e5ab9bd5..fd2223fda 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryResponseMessage.java @@ -51,4 +51,10 @@ public class FullDataChangeSummaryResponseMessage extends FutureTrackableNetwork { decodeCollection(in, changedPosList, DhSectionPos::zero); } + + @Override public String toString() + { + return super.toString("changedPosList=" + changedPosList); + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java index bf2687aa8..3b51427f5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java @@ -23,6 +23,7 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedF import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream; @@ -33,7 +34,7 @@ import javax.annotation.Nullable; import java.io.ByteArrayOutputStream; import java.io.IOException; -public class FullDataPartialUpdateMessage extends FutureTrackableNetworkMessage +public class FullDataPartialUpdateMessage extends NetworkMessage { private ChunkSizedFullDataAccessor fullDataAccessor; private DhServerLevel level; @@ -52,8 +53,7 @@ public class FullDataPartialUpdateMessage extends FutureTrackableNetworkMessage this.levelHashCode = level.getLevelWrapper().getDimensionType().getDimensionName().hashCode(); } - @Override - public void encode0(ByteBuf out) throws IOException + public void encode(ByteBuf out) { try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { @@ -69,10 +69,13 @@ public class FullDataPartialUpdateMessage extends FutureTrackableNetworkMessage out.writeInt(outputStream.size()); out.writeBytes(outputStream.toByteArray()); } + catch (IOException e) + { + throw new RuntimeException(e); + } } - @Override - public void decode0(ByteBuf in) + public void decode(ByteBuf in) { levelHashCode = in.readInt(); @@ -99,4 +102,14 @@ public class FullDataPartialUpdateMessage extends FutureTrackableNetworkMessage dataBuffer.release(); } } + + @Override public String toString() + { + return super.toString( + "levelHashCode=" + levelHashCode + + ", chunkPos=" + chunkPos + + ", dataBuffer=" + dataBuffer + ); + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/session/PlayerUUIDMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/session/PlayerUUIDMessage.java index 185a7f383..4064142d6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/session/PlayerUUIDMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/session/PlayerUUIDMessage.java @@ -42,4 +42,9 @@ public class PlayerUUIDMessage extends FutureTrackableNetworkMessage @Override public void decode0(ByteBuf in) { this.playerUUID = new UUID(in.readLong(), in.readLong()); } + @Override public String toString() + { + return super.toString("playerUUID=" + playerUUID); + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/session/RemotePlayerConfigMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/session/RemotePlayerConfigMessage.java index 0b4bbebf5..dd70197bc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/session/RemotePlayerConfigMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/session/RemotePlayerConfigMessage.java @@ -37,4 +37,9 @@ public class RemotePlayerConfigMessage extends FutureTrackableNetworkMessage @Override public void decode0(ByteBuf in) { this.payload = INetworkObject.decodeStatic(new MultiplayerConfig(), in); } + @Override public String toString() + { + return super.toString("payload=" + payload); + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java index 881c8039f..f46bf36f6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java @@ -19,16 +19,30 @@ package com.seibel.distanthorizons.core.network.protocol; +import com.google.common.collect.MapMaker; +import com.seibel.distanthorizons.core.api.internal.SharedApi; import com.seibel.distanthorizons.core.network.messages.base.ExceptionMessage; +import com.seibel.distanthorizons.core.world.EWorldEnvironment; import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import javax.annotation.Nullable; +import java.util.Objects; +import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; public abstract class FutureTrackableNetworkMessage extends NetworkMessage { + private static final AtomicInteger lastId = new AtomicInteger(); - // Only low 32 bits are sent (high bits are used for identifying a channel this request was sent from by remote peer) - public long futureId = lastId.incrementAndGet(); + // 32 bits - Context ID (not transmitted) + // 1 bit - Requesting side (client - 0, server - 1) + // 31 bits - Request ID + public long futureId = lastId.incrementAndGet() + | ((Objects.requireNonNull(SharedApi.getEnvironment()) == EWorldEnvironment.Server_Only ? 1 : 0) << 31); + + private static final AtomicInteger lastContextId = new AtomicInteger(); + private static final ConcurrentMap contextIds = new MapMaker().weakKeys().makeMap(); public void sendResponse(FutureTrackableNetworkMessage responseMessage) { @@ -36,6 +50,13 @@ public abstract class FutureTrackableNetworkMessage extends NetworkMessage getChannelContext().writeAndFlush(responseMessage); } + @Override + public void setChannelContext(ChannelHandlerContext channelContext) + { + super.setChannelContext(channelContext); + this.futureId |= (long) contextIds.computeIfAbsent(channelContext, k -> lastContextId.incrementAndGet()) << 32; + } + public void sendResponse(Exception e) { sendResponse(new ExceptionMessage(e)); @@ -69,4 +90,19 @@ public abstract class FutureTrackableNetworkMessage extends NetworkMessage protected abstract void encode0(ByteBuf out) throws Exception; protected abstract void decode0(ByteBuf in) throws Exception; + + @Override + public String toString() + { + return toString(null); + } + + protected String toString(@Nullable String extraData) + { + return super.toString( + "futureId=" + futureId + + (extraData != null ? ", " + extraData : "") + ); + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java index 6803cc1a5..3967b86b6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java @@ -19,11 +19,11 @@ package com.seibel.distanthorizons.core.network.protocol; -import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.network.messages.base.CloseEvent; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; +import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; @@ -32,26 +32,36 @@ import java.util.function.Consumer; @ChannelHandler.Sharable public class MessageHandler extends SimpleChannelInboundHandler { - private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + private static final Logger LOGGER = LogManager.getLogger(); private final Consumer messageConsumer; + private final Consumer channelActiveConsumer; - public MessageHandler(Consumer messageConsumer) + public MessageHandler(Consumer messageConsumer, Consumer channelActiveConsumer) { this.messageConsumer = messageConsumer; + this.channelActiveConsumer = channelActiveConsumer; } @Override protected void channelRead0(ChannelHandlerContext channelContext, NetworkMessage message) { - LOGGER.trace("Received message: " + message.getClass().getSimpleName()); message.setChannelContext(channelContext); + LOGGER.trace("Received message: " + message); this.messageConsumer.accept(message); } @Override - public void channelInactive(@NotNull ChannelHandlerContext channelContext) + public void channelActive(@NotNull ChannelHandlerContext ctx) throws Exception { + super.channelActive(ctx); + this.channelActiveConsumer.accept(ctx); + } + + @Override + public void channelInactive(@NotNull ChannelHandlerContext channelContext) throws Exception + { + super.channelInactive(channelContext); this.channelRead0(channelContext, new CloseEvent()); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkMessage.java index fe06242da..2203b3398 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkMessage.java @@ -20,6 +20,9 @@ package com.seibel.distanthorizons.core.network.protocol; import io.netty.channel.ChannelHandlerContext; +import org.jetbrains.annotations.NotNull; + +import javax.annotation.Nullable; public abstract class NetworkMessage implements INetworkObject { @@ -36,5 +39,16 @@ public abstract class NetworkMessage implements INetworkObject throw new IllegalStateException("Channel context cannot be changed after initial setting."); this.channelContext = channelContext; } + + @Override public String toString() + { + return toString(""); + } + + protected String toString(@NotNull String extraData) + { + return this.getClass().getSimpleName() + "{" + extraData + '}'; + } + } From fccd197cd5ec714a47f17c498fe385343a49cd31 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 19 Sep 2023 18:53:10 +0500 Subject: [PATCH 043/205] Fix chunk updates sent to incomplete connections Hide useless warns, for cases when listener is not configured yet --- .../seibel/distanthorizons/core/level/DhServerLevel.java | 9 ++------- .../core/multiplayer/RemotePlayerConnectionHandler.java | 7 +++++-- .../distanthorizons/core/network/NetworkEventSource.java | 7 ++----- .../fullData/updates/FullDataPartialUpdateMessage.java | 3 +++ .../network/protocol/FutureTrackableNetworkMessage.java | 5 ++--- .../core/network/protocol/NetworkMessage.java | 4 ++-- 6 files changed, 16 insertions(+), 19 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 35d1947c0..eba0de788 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -45,7 +45,6 @@ import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDat import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataPartialUpdateMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhChunkPos; -import com.seibel.distanthorizons.core.pos.DhLodPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.util.LodUtil; @@ -59,7 +58,6 @@ import org.apache.logging.log4j.Logger; import javax.annotation.CheckForNull; import java.util.HashSet; import java.util.Map; -import java.util.Set; import java.util.concurrent.*; public class DhServerLevel extends DhLevel implements IDhServerLevel @@ -263,12 +261,9 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel future.thenAccept(chunkSizedFullDataAccessor -> { - for (IServerPlayerWrapper serverPlayer : worldGenLoopingQueue) + for (ServerPlayerState serverPlayerState : remotePlayerConnectionHandler.getConnectedPlayers()) { - ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getPlayer(serverPlayer); - if (serverPlayerState == null) continue; - - if (chunk.getChunkPos().distance(new DhChunkPos(serverPlayer.getPosition())) <= serverPlayerState.config.renderDistance) + if (chunk.getChunkPos().distance(new DhChunkPos(serverPlayerState.serverPlayer.getPosition())) <= serverPlayerState.config.renderDistance) serverPlayerState.channelContext.writeAndFlush(new FullDataPartialUpdateMessage(chunkSizedFullDataAccessor, this)); } }); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java index 858cdfab1..d5f0bc14d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java @@ -77,9 +77,12 @@ public class RemotePlayerConnectionHandler implements Closeable } @Nullable - public ServerPlayerState getPlayer(IServerPlayerWrapper serverPlayer) + public ServerPlayerState getConnectedPlayer(IServerPlayerWrapper serverPlayer) { - return playersByUUID.get(serverPlayer.getUUID()); + ServerPlayerState player = playersByUUID.get(serverPlayer.getUUID()); + if (player == null || player.channelContext == null) + return null; + return player; } public void registerJoinedPlayer(IServerPlayerWrapper serverPlayer) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index d48d5296b..fcfc18fed 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -86,11 +86,8 @@ public abstract class NetworkEventSource } } - if (!handled) - { - String error = "Unhandled message: " + message; - LOGGER.warn(error); - } + if (!handled && message.warnWhenUnhandled()) + LOGGER.warn("Unhandled message: " + message); } protected void addNewContext(ChannelHandlerContext ctx) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java index 3b51427f5..1524b3f26 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java @@ -43,6 +43,9 @@ public class FullDataPartialUpdateMessage extends NetworkMessage private DhChunkPos chunkPos; private ByteBuf dataBuffer; + @Override + public boolean warnWhenUnhandled() { return false; } + public FullDataPartialUpdateMessage() {} public FullDataPartialUpdateMessage(ChunkSizedFullDataAccessor fullDataAccessor, DhServerLevel level) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java index f46bf36f6..a6e437e2d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java @@ -33,12 +33,11 @@ import java.util.concurrent.atomic.AtomicInteger; public abstract class FutureTrackableNetworkMessage extends NetworkMessage { - private static final AtomicInteger lastId = new AtomicInteger(); // 32 bits - Context ID (not transmitted) // 1 bit - Requesting side (client - 0, server - 1) // 31 bits - Request ID - public long futureId = lastId.incrementAndGet() + public long futureId = lastId.getAndIncrement() | ((Objects.requireNonNull(SharedApi.getEnvironment()) == EWorldEnvironment.Server_Only ? 1 : 0) << 31); private static final AtomicInteger lastContextId = new AtomicInteger(); @@ -54,7 +53,7 @@ public abstract class FutureTrackableNetworkMessage extends NetworkMessage public void setChannelContext(ChannelHandlerContext channelContext) { super.setChannelContext(channelContext); - this.futureId |= (long) contextIds.computeIfAbsent(channelContext, k -> lastContextId.incrementAndGet()) << 32; + this.futureId |= (long) contextIds.computeIfAbsent(channelContext, k -> lastContextId.getAndIncrement()) << 32; } public void sendResponse(Exception e) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkMessage.java index 2203b3398..4c5fb44c3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkMessage.java @@ -22,12 +22,12 @@ package com.seibel.distanthorizons.core.network.protocol; import io.netty.channel.ChannelHandlerContext; import org.jetbrains.annotations.NotNull; -import javax.annotation.Nullable; - public abstract class NetworkMessage implements INetworkObject { private ChannelHandlerContext channelContext = null; + public boolean warnWhenUnhandled() { return true; } + public ChannelHandlerContext getChannelContext() { return channelContext; From a80bb082b3df4fdd8534d5b9431707a0f36941d6 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 19 Sep 2023 20:19:25 +0500 Subject: [PATCH 044/205] Validate response types --- .../RemoteFullDataFileHandler.java | 2 +- .../WorldRemoteGenerationQueue.java | 4 +- .../core/multiplayer/ClientNetworkState.java | 6 +-- .../core/network/NetworkClient.java | 4 +- .../core/network/NetworkEventSource.java | 46 ++++++++++++------- .../core/network/NetworkServer.java | 4 +- 6 files changed, 39 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java index 111acdbab..35a70c537 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java @@ -88,7 +88,7 @@ public class RemoteFullDataFileHandler extends GeneratedFullDataFileHandler this.writeChunkDataToFile(new DhSectionPos(pos.getDetailLevel(), pos.getX(), pos.getZ()), data); }; - this.networkState.getClient().sendRequest(new FullDataChangeSummaryRequestMessage(level.getLevelWrapper(), block)) + this.networkState.getClient().sendRequest(new FullDataChangeSummaryRequestMessage(level.getLevelWrapper(), block), FullDataChangeSummaryResponseMessage.class) .handle((response, throwable) -> { try diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 704da2f47..65b4311d2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -101,7 +101,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug return; }; - CompletableFuture request = this.networkState.getClient().sendRequest(new GenTaskPriorityRequestMessage(posList)); + CompletableFuture request = this.networkState.getClient().sendRequest(new GenTaskPriorityRequestMessage(posList), GenTaskPriorityResponseMessage.class); genTaskPriorityRequest = request; request.handleAsync((response, throwable) -> { try @@ -163,7 +163,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug DhSectionPos sectionPos = mapEntry.getKey(); WorldGenQueueEntry entry = mapEntry.getValue(); - CompletableFuture request = this.networkState.getClient().sendRequest(new FullDataSourceRequestMessage(sectionPos)); + CompletableFuture request = this.networkState.getClient().sendRequest(new FullDataSourceRequestMessage(sectionPos), FullDataSourceResponseMessage.class); entry.request = request; request.handleAsync((response, throwable) -> { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java index c8699d6ab..59acd6b8b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java @@ -47,12 +47,12 @@ public class ClientNetworkState implements Closeable { LOGGER.info("Connected to server: "+helloMessage.getChannelContext().channel().remoteAddress()); - this.getClient().sendRequest(new PlayerUUIDMessage(playerUUID)) - .thenCompose(ack -> this.getClient().sendRequest(new RemotePlayerConfigMessage(new MultiplayerConfig() + this.getClient().sendRequest(new PlayerUUIDMessage(playerUUID), AckMessage.class) + .thenCompose(ack -> this.getClient().sendRequest(new RemotePlayerConfigMessage(new MultiplayerConfig() {{ renderDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistance.get(); fullDataRequestRateLimit = Config.Client.Advanced.Multiplayer.serverNetworkingRateLimit.get(); - }}))) + }}), RemotePlayerConfigMessage.class)) .thenAccept(msg -> { this.config = msg.payload; }) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index 467166cd9..ecbb55955 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -168,9 +168,9 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable }); } - public final CompletableFuture sendRequest(FutureTrackableNetworkMessage msg) + public final CompletableFuture sendRequest(FutureTrackableNetworkMessage msg, Class responseClass) { - return this.sendRequest(this.channel.pipeline().context(MessageHandler.class), msg); + return this.sendRequest(this.channel.pipeline().context(MessageHandler.class), msg, responseClass); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index fcfc18fed..5b53ba7ff 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -19,9 +19,6 @@ package com.seibel.distanthorizons.core.network; -import com.google.common.collect.HashBasedTable; -import com.google.common.collect.Table; -import com.google.common.collect.Tables; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.network.messages.base.CancelMessage; import com.seibel.distanthorizons.core.network.messages.base.CloseEvent; @@ -33,6 +30,7 @@ import io.netty.channel.ChannelException; import io.netty.channel.ChannelHandlerContext; import org.apache.logging.log4j.Logger; +import java.io.InvalidClassException; import java.util.HashSet; import java.util.Set; import java.util.concurrent.CancellationException; @@ -45,7 +43,7 @@ public abstract class NetworkEventSource { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); protected final ConcurrentMap, Set>> handlers = new ConcurrentHashMap<>(); - private final ConcurrentMap>> pendingFutures = new ConcurrentHashMap<>(); + private final ConcurrentMap> pendingFutures = new ConcurrentHashMap<>(); protected boolean hasHandler(Class handlerClass) { @@ -70,18 +68,20 @@ public abstract class NetworkEventSource if (message instanceof FutureTrackableNetworkMessage) { FutureTrackableNetworkMessage trackableMessage = (FutureTrackableNetworkMessage)message; - ConcurrentMap> subMap = pendingFutures.get(message.getChannelContext()); + ConcurrentMap subMap = pendingFutures.get(message.getChannelContext()); if (subMap != null) { - CompletableFuture future = subMap.get(trackableMessage.futureId); - if (future != null) + FutureResponseData responseData = subMap.get(trackableMessage.futureId); + if (responseData != null) { handled = true; if (message instanceof ExceptionMessage) - future.completeExceptionally(((ExceptionMessage) message).exception); + responseData.future.completeExceptionally(((ExceptionMessage) message).exception); + else if (message.getClass() != responseData.responseClass) + responseData.future.completeExceptionally(new InvalidClassException("Response with invalid type: expected " + responseData.responseClass.getSimpleName() + ", got:" + message)); else - future.complete(trackableMessage); + responseData.future.complete(trackableMessage); } } } @@ -97,6 +97,7 @@ public abstract class NetworkEventSource public void registerHandler(Class handlerClass, Consumer handlerImplementation) { + //noinspection unchecked this.handlers.computeIfAbsent(handlerClass, missingHandlerClass -> { // Will throw if the handler class is not found @@ -114,7 +115,7 @@ public abstract class NetworkEventSource } - protected CompletableFuture sendRequest(ChannelHandlerContext ctx, FutureTrackableNetworkMessage msg) + protected CompletableFuture sendRequest(ChannelHandlerContext ctx, FutureTrackableNetworkMessage msg, Class responseClass) { msg.setChannelContext(ctx); @@ -122,7 +123,7 @@ public abstract class NetworkEventSource responseFuture.handle((response, throwable) -> { if (!(throwable instanceof ChannelException)) { - ConcurrentMap> subMap = pendingFutures.get(ctx); + ConcurrentMap subMap = pendingFutures.get(ctx); if (subMap != null) subMap.remove(msg.futureId); } @@ -133,14 +134,13 @@ public abstract class NetworkEventSource return null; }); - ConcurrentMap> subMap = pendingFutures.get(ctx); + ConcurrentMap subMap = pendingFutures.get(ctx); if (subMap == null) { // Was deleted before adding responseFuture.completeExceptionally(ctx.channel().closeFuture().cause()); return responseFuture; } - //noinspection unchecked - subMap.put(msg.futureId, (CompletableFuture) responseFuture); + subMap.put(msg.futureId, new FutureResponseData(responseClass, responseFuture)); if (!pendingFutures.containsKey(ctx)) { // Was deleted while adding responseFuture.completeExceptionally(ctx.channel().closeFuture().cause()); @@ -158,11 +158,11 @@ public abstract class NetworkEventSource protected final void completeAllFuturesExceptionally(ChannelHandlerContext ctx, Throwable cause) { - ConcurrentMap> map = pendingFutures.remove(ctx); + ConcurrentMap map = pendingFutures.remove(ctx); if (map == null) return; - for (CompletableFuture future : map.values()) - future.completeExceptionally(cause); + for (FutureResponseData responseData : map.values()) + responseData.future.completeExceptionally(cause); } protected final void completeAllFuturesExceptionally(Throwable cause) @@ -176,4 +176,16 @@ public abstract class NetworkEventSource this.handlers.clear(); completeAllFuturesExceptionally(new ChannelException(this.getClass().getSimpleName()+" is closed.")); } + + private static class FutureResponseData + { + public final Class responseClass; + public final CompletableFuture future; + + private FutureResponseData(Class responseClass, CompletableFuture future) { + this.responseClass = responseClass; + //noinspection unchecked + this.future = (CompletableFuture) future; + } + } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java index bc65f040d..003b192eb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java @@ -124,9 +124,9 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable } @Override - public CompletableFuture sendRequest(ChannelHandlerContext ctx, FutureTrackableNetworkMessage msg) + public CompletableFuture sendRequest(ChannelHandlerContext ctx, FutureTrackableNetworkMessage msg, Class responseClass) { - return super.sendRequest(ctx, msg); + return super.sendRequest(ctx, msg, responseClass); } @Override From 08704aad2ca56941fac98d1d4c7591744a6df598 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 19 Sep 2023 21:32:59 +0500 Subject: [PATCH 045/205] Allow selecting specific IDebugRenderable's for rendering --- .../config/client/DhApiDebuggingConfig.java | 2 +- .../distanthorizons/core/config/Config.java | 48 +++++++++++++++---- .../file/fullDatafile/FullDataMetaFile.java | 2 + .../file/renderfile/RenderDataMetaFile.java | 2 + .../core/generation/WorldGenerationQueue.java | 2 + .../WorldRemoteGenerationQueue.java | 6 ++- .../core/render/LodRenderSection.java | 3 ++ .../core/render/renderer/DebugRenderer.java | 4 +- .../core/render/renderer/LodRenderer.java | 2 +- 9 files changed, 57 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiDebuggingConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiDebuggingConfig.java index b9dab4d9d..5fc74c69f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiDebuggingConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiDebuggingConfig.java @@ -46,6 +46,6 @@ public class DhApiDebuggingConfig implements IDhApiDebuggingConfig { return new DhApiConfigValue(Config.Client.Advanced.Debugging.lodOnlyMode); } public IDhApiConfigValue debugWireframeRendering() - { return new DhApiConfigValue(Config.Client.Advanced.Debugging.debugWireframeRendering); } + { return new DhApiConfigValue(Config.Client.Advanced.Debugging.DebugWireframeRendering.enableRendering); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 3d1821bc1..c2741fab5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -1083,6 +1083,8 @@ public class Config public static class Debugging { + public static ConfigCategory debugWireframeRendering = new ConfigCategory.Builder().set(DebugWireframeRendering.class).build(); + public static ConfigEntry rendererMode = new ConfigEntry.Builder() .set(ERendererMode.DEFAULT) .comment("" @@ -1131,15 +1133,43 @@ public class Config + " Mod compatibility is not guaranteed.") .build(); - public static ConfigEntry debugWireframeRendering = new ConfigEntry.Builder() - .set(false) - .comment("" - + "If enabled, various wireframes for debugging internal functions will be drawn. \n" - + "\n" - + "NOTE: There WILL be performance hit! \n" - + " Additionally, only stuff that's loaded after you enable this \n" - + " will render their debug wireframes.") - .build(); + public static class DebugWireframeRendering + { + public static ConfigEntry enableRendering = new ConfigEntry.Builder() + .set(false) + .comment("" + + "If enabled, various wireframes for debugging internal functions will be drawn. \n" + + "\n" + + "NOTE: There WILL be performance hit! \n" + + " Additionally, only stuff that's loaded after you enable this \n" + + " will render their debug wireframes.") + .build(); + + public static ConfigEntry fullDataMetaFile = new ConfigEntry.Builder() + .set(false) + .comment("Class: FullDataMetaFile") + .build(); + + public static ConfigEntry lodRenderSection = new ConfigEntry.Builder() + .set(false) + .comment("Class: LodRenderSection") + .build(); + + public static ConfigEntry renderDataMetaFile = new ConfigEntry.Builder() + .set(false) + .comment("Class: RenderDataMetaFile") + .build(); + + public static ConfigEntry worldGenerationQueue = new ConfigEntry.Builder() + .set(false) + .comment("Class: WorldGenerationQueue") + .build(); + + public static ConfigEntry worldRemoteGenerationQueue = new ConfigEntry.Builder() + .set(false) + .comment("Class: WorldRemoteGenerationQueue") + .build(); + } public static ConfigEntry enableWhiteWorld = new ConfigEntry.Builder() .set(false) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java index 858f084e5..bfec48dbe 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java @@ -431,6 +431,8 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I @Override public void debugRender(DebugRenderer debugRenderer) { + if (!Config.Client.Advanced.Debugging.DebugWireframeRendering.fullDataMetaFile.get()) return; + if (this.pos.getDetailLevel() > DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL) { return; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderDataMetaFile.java b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderDataMetaFile.java index 60991802a..a9a07910f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderDataMetaFile.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/renderfile/RenderDataMetaFile.java @@ -438,6 +438,8 @@ public class RenderDataMetaFile extends AbstractMetaDataContainerFile implements @Override public void debugRender(DebugRenderer debugRenderer) { + if (!Config.Client.Advanced.Debugging.DebugWireframeRendering.renderDataMetaFile.get()) return; + Color color = Color.black; ColumnRenderSource cached = this.cachedRenderDataSource.get(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java index 3cf6f863b..d53aa5188 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java @@ -722,6 +722,8 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender @Override public void debugRender(DebugRenderer r) { + if (!Config.Client.Advanced.Debugging.DebugWireframeRendering.worldGenerationQueue.get()) return; + //if (true) return; waitingTasks.keySet().forEach((pos) -> { //DhLodPos pos = t.pos; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 65b4311d2..cb1fb2c55 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -1,5 +1,6 @@ package com.seibel.distanthorizons.core.generation; +import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker; @@ -257,7 +258,10 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug } @Override - public void debugRender(DebugRenderer r) { + public void debugRender(DebugRenderer r) + { + if (!Config.Client.Advanced.Debugging.DebugWireframeRendering.worldRemoteGenerationQueue.get()) return; + for (Map.Entry mapEntry : waitingTasks.entrySet()) { r.renderBox(new DebugRenderer.Box(mapEntry.getKey(), -32f, 64f, 0.05f, diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java index eaa32502e..ccf86b080 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.render; +import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource; import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.ColumnRenderBufferBuilder; import com.seibel.distanthorizons.core.enums.EDhDirection; @@ -99,6 +100,8 @@ public class LodRenderSection implements IDebugRenderable public void debugRender(DebugRenderer debugRenderer) { + if (!Config.Client.Advanced.Debugging.DebugWireframeRendering.lodRenderSection.get()) return; + Color color = Color.red; if (this.renderSourceProvider == null) color = Color.black; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java index 7ddfbd51e..afc2419c4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java @@ -258,7 +258,7 @@ public class DebugRenderer public static void makeParticle(BoxParticle particle) { if (INSTANCE == null) return; - if (!Config.Client.Advanced.Debugging.debugWireframeRendering.get()) return; + if (!Config.Client.Advanced.Debugging.DebugWireframeRendering.enableRendering.get()) return; INSTANCE.particles.add(particle); } @@ -321,7 +321,7 @@ public class DebugRenderer public void addRenderer(IDebugRenderable r) { - if (!Config.Client.Advanced.Debugging.debugWireframeRendering.get()) return; + if (!Config.Client.Advanced.Debugging.DebugWireframeRendering.enableRendering.get()) return; synchronized (renderers) { renderers.add(new WeakReference<>(r)); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java index 4a1c2da33..20720ba48 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java @@ -350,7 +350,7 @@ public class LodRenderer this.shaderProgram.unbind(); - if (Config.Client.Advanced.Debugging.debugWireframeRendering.get()) + if (Config.Client.Advanced.Debugging.DebugWireframeRendering.enableRendering.get()) { profiler.popPush("Debug wireframes"); // Note: this can be very slow if a lot of boxes are being rendered From d325a69e3f725b24d00ddffebf17c146877c424e Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Thu, 21 Sep 2023 10:16:34 +0500 Subject: [PATCH 046/205] Move context levels to requests --- .../WorldRemoteGenerationQueue.java | 2 +- .../core/level/DhServerLevel.java | 49 +++---------------- .../RemotePlayerConnectionHandler.java | 28 +++++++++++ .../messages/base/ILevelRelatedMessage.java | 41 ++++++++++++++++ .../FullDataSourceRequestMessage.java | 21 +++++--- .../FullDataSourceResponseMessage.java | 14 +----- .../FullDataChangeSummaryRequestMessage.java | 9 ++-- .../updates/FullDataPartialUpdateMessage.java | 10 ++-- 8 files changed, 102 insertions(+), 72 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ILevelRelatedMessage.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index cb1fb2c55..fdb7a3d12 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -164,7 +164,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug DhSectionPos sectionPos = mapEntry.getKey(); WorldGenQueueEntry entry = mapEntry.getValue(); - CompletableFuture request = this.networkState.getClient().sendRequest(new FullDataSourceRequestMessage(sectionPos), FullDataSourceResponseMessage.class); + CompletableFuture request = this.networkState.getClient().sendRequest(new FullDataSourceRequestMessage(level.getLevelWrapper(), sectionPos), FullDataSourceResponseMessage.class); entry.request = request; request.handleAsync((response, throwable) -> { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index eba0de788..bc75fdd5e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -92,28 +92,12 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel private void registerNetworkHandlers() { - // TODO implement transparent message handling restriction by level - // workaround: -// ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); -// if (serverPlayerState == null) return; -// -// if (serverPlayerState.serverPlayer.getLevel() != this.serverLevelWrapper) -// return; - - this.eventSource.registerHandler(FullDataSourceRequestMessage.class, msg -> + this.eventSource.registerHandler(FullDataSourceRequestMessage.class, remotePlayerConnectionHandler.currentLevelOnly(this, (msg, serverPlayerState) -> { - ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); - if (serverPlayerState == null) return; - - if (serverPlayerState.serverPlayer.getLevel() != this.serverLevelWrapper) - return; - - LOGGER.debug("FullDataSourceRequestMessage received at pos ({}, {}) with detail level {}", msg.dhSectionPos.getX(), msg.dhSectionPos.getZ(), msg.dhSectionPos.getDetailLevel()); - if (serverPlayerState.pendingFullDataRequests.incrementAndGet() > rateLimitConfig.get()) { serverPlayerState.pendingFullDataRequests.decrementAndGet(); - msg.sendResponse(new RateLimitedException("Max concurrent requests: "+rateLimitConfig.get())); + msg.sendResponse(new RateLimitedException("Max concurrent requests: " + rateLimitConfig.get())); return; } @@ -135,34 +119,17 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel break; } } - }); + })); - this.eventSource.registerHandler(GenTaskPriorityRequestMessage.class, msg -> { - ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); - if (serverPlayerState == null) return; - - if (serverPlayerState.serverPlayer.getLevel() != this.serverLevelWrapper) - return; - + this.eventSource.registerHandler(GenTaskPriorityRequestMessage.class, remotePlayerConnectionHandler.currentLevelOnly(this, (msg, serverPlayerState) -> + { msg.sendResponse(new GenTaskPriorityResponseMessage( this.serverside.dataFileHandler.getLoadStates(msg.posList) )); - }); + })); - this.eventSource.registerHandler(FullDataChangeSummaryRequestMessage.class, msg -> + this.eventSource.registerHandler(FullDataChangeSummaryRequestMessage.class, remotePlayerConnectionHandler.currentLevelOnly(this, (msg, serverPlayerState) -> { - ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); - if (serverPlayerState == null) return; - - if (serverPlayerState.serverPlayer.getLevel() != this.serverLevelWrapper) - return; - - if (!msg.isLevelValid(this.serverLevelWrapper)) - { - msg.sendResponse(new InvalidLevelException("Invalid level")); - return; - } - // Load files and check checksums HashSet changedPosList = new HashSet<>(); for (Map.Entry entry : msg.checksums.entrySet()) @@ -179,7 +146,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel } msg.sendResponse(new FullDataChangeSummaryResponseMessage(changedPosList)); - }); + })); this.eventSource.registerHandler(CancelMessage.class, msg -> { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java index d5f0bc14d..09a0b11db 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java @@ -2,8 +2,10 @@ package com.seibel.distanthorizons.core.multiplayer; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; +import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkServer; +import com.seibel.distanthorizons.core.network.messages.base.ILevelRelatedMessage; import com.seibel.distanthorizons.core.network.messages.base.AckMessage; import com.seibel.distanthorizons.core.network.messages.base.CloseEvent; import com.seibel.distanthorizons.core.network.messages.session.PlayerUUIDMessage; @@ -15,6 +17,8 @@ import org.jetbrains.annotations.Nullable; import java.io.Closeable; import java.util.HashMap; import java.util.UUID; +import java.util.function.BiConsumer; +import java.util.function.Consumer; public class RemotePlayerConnectionHandler implements Closeable { @@ -65,6 +69,30 @@ public class RemotePlayerConnectionHandler implements Closeable }); } + public Consumer connectedPlayersOnly(BiConsumer next) + { + return msg -> + { + ServerPlayerState serverPlayerState = getConnectedPlayer(msg); + if (serverPlayerState != null) + next.accept(msg, serverPlayerState); + }; + } + + public Consumer currentLevelOnly(DhServerLevel level, BiConsumer next) + { + return connectedPlayersOnly((msg, serverPlayerState) -> + { + if (serverPlayerState.serverPlayer.getLevel() != level.getLevelWrapper()) + return; + + if (msg instanceof ILevelRelatedMessage && ((ILevelRelatedMessage) msg).sendExceptionIfLevelInvalid(level.getLevelWrapper())) + return; + + next.accept(msg, serverPlayerState); + }); + } + public Iterable getConnectedPlayers() { return playersByConnection.values(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ILevelRelatedMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ILevelRelatedMessage.java new file mode 100644 index 000000000..8ba15e42d --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ILevelRelatedMessage.java @@ -0,0 +1,41 @@ +package com.seibel.distanthorizons.core.network.messages.base; + +import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; +import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; + +public interface ILevelRelatedMessage +{ + int getLevelHashCode(); + + /** + * Returns true if level does not match the given level. + * + * @param levelWrapper Level wrapper to check against. + * @return Whether the level is invalid. + */ + default boolean isLevelInvalid(ILevelWrapper levelWrapper) + { + return levelWrapper.getDimensionType().getDimensionName().hashCode() != getLevelHashCode(); + } + + /** + * Same as {@link #isLevelInvalid}. + * If current message implements {@link FutureTrackableNetworkMessage}, additionally sends an exception response if given wrapper does not match. + * + * @param levelWrapper Level wrapper to check against. + * @return Whether the level is invalid. + */ + default boolean sendExceptionIfLevelInvalid(ILevelWrapper levelWrapper) + { + if (isLevelInvalid(levelWrapper)) + { + if (this instanceof FutureTrackableNetworkMessage) + ((FutureTrackableNetworkMessage) this).sendResponse(new InvalidLevelException("Invalid level")); + return true; + } + + return false; + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceRequestMessage.java index 65f88874a..315981a5b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceRequestMessage.java @@ -19,40 +19,49 @@ package com.seibel.distanthorizons.core.network.messages.fullData.generation; +import com.seibel.distanthorizons.core.network.messages.base.ILevelRelatedMessage; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import io.netty.buffer.ByteBuf; -import javax.annotation.Nullable; - -public class FullDataSourceRequestMessage extends FutureTrackableNetworkMessage +public class FullDataSourceRequestMessage extends FutureTrackableNetworkMessage implements ILevelRelatedMessage { public DhSectionPos dhSectionPos; - + private int levelHashCode; + @Override public int getLevelHashCode() { return levelHashCode; } + public FullDataSourceRequestMessage() {} - public FullDataSourceRequestMessage(DhSectionPos dhSectionPos) + public FullDataSourceRequestMessage(ILevelWrapper levelWrapper, DhSectionPos dhSectionPos) { + // TODO Multiverse support + this.levelHashCode = levelWrapper.getDimensionType().getDimensionName().hashCode(); this.dhSectionPos = dhSectionPos; } @Override public void encode0(ByteBuf out) { + out.writeInt(levelHashCode); dhSectionPos.encode(out); } @Override public void decode0(ByteBuf in) { + levelHashCode = in.readInt(); dhSectionPos = INetworkObject.decodeStatic(DhSectionPos.zero(), in); } @Override public String toString() { - return super.toString("dhSectionPos=" + dhSectionPos); + return super.toString( + "dhSectionPos=" + dhSectionPos + + ", levelHashCode=" + levelHashCode + ); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java index 3326cc16d..930e1ea2d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java @@ -40,7 +40,6 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage private CompleteFullDataSource fullDataSource; private DhServerLevel level; - private int levelHashCode; private CompleteFullDataSourceLoader fullDataSourceLoader; private ByteBuf dataBuffer; @@ -49,9 +48,6 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage { this.fullDataSource = fullDataSource; this.level = level; - - // TODO Multiverse support - this.levelHashCode = level.getLevelWrapper().getDimensionType().getDimensionName().hashCode(); } @Override @@ -63,7 +59,6 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage fullDataSource.writeToStream(dhOutputStream, level); dhOutputStream.flush(); - out.writeInt(levelHashCode); out.writeByte(fullDataSource.getBinaryDataFormatVersion()); out.writeInt(outputStream.size()); out.writeBytes(outputStream.toByteArray()); @@ -73,7 +68,6 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage @Override public void decode0(ByteBuf in) { - levelHashCode = in.readInt(); byte dataVersion = in.readByte(); this.fullDataSourceLoader = (CompleteFullDataSourceLoader) AbstractFullDataSourceLoader.getLoader(CompleteFullDataSource.TYPE_ID, dataVersion); this.dataBuffer = in.readBytes(in.readInt()); @@ -82,9 +76,6 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage @Nullable public CompleteFullDataSource getFullDataSource(DhSectionPos pos, IDhLevel level) throws IOException, InterruptedException { - // TODO Multiverse support - if (levelHashCode != level.getLevelWrapper().getDimensionType().getDimensionName().hashCode()) - return null; try (ByteBufInputStream inputStream = new ByteBufInputStream(dataBuffer)) { @@ -98,10 +89,7 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage @Override public String toString() { - return super.toString( - "levelHashCode=" + levelHashCode + - ", dataBuffer=" + dataBuffer - ); + return super.toString("dataBuffer=" + dataBuffer); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryRequestMessage.java index 090ec4fec..9116525ca 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryRequestMessage.java @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.network.messages.fullData.updates; +import com.seibel.distanthorizons.core.network.messages.base.ILevelRelatedMessage; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; @@ -27,10 +28,11 @@ import io.netty.buffer.ByteBuf; import java.util.HashMap; import java.util.Map; -public class FullDataChangeSummaryRequestMessage extends FutureTrackableNetworkMessage +public class FullDataChangeSummaryRequestMessage extends FutureTrackableNetworkMessage implements ILevelRelatedMessage { public Map checksums = new HashMap<>(); public int levelHashCode; + @Override public int getLevelHashCode() { return levelHashCode; } public FullDataChangeSummaryRequestMessage() { } @@ -56,11 +58,6 @@ public class FullDataChangeSummaryRequestMessage extends FutureTrackableNetworkM decodeMap(in, checksums, DhSectionPos::zero, () -> 0); } - public boolean isLevelValid(ILevelWrapper levelWrapper) - { - return levelWrapper.getDimensionType().getDimensionName().hashCode() == levelHashCode; - } - @Override public String toString() { return super.toString( diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java index 1524b3f26..a2d70154e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java @@ -22,7 +22,7 @@ package com.seibel.distanthorizons.core.network.messages.fullData.updates; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.level.IDhLevel; -import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.network.messages.base.ILevelRelatedMessage; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; @@ -34,12 +34,14 @@ import javax.annotation.Nullable; import java.io.ByteArrayOutputStream; import java.io.IOException; -public class FullDataPartialUpdateMessage extends NetworkMessage +public class FullDataPartialUpdateMessage extends NetworkMessage implements ILevelRelatedMessage { private ChunkSizedFullDataAccessor fullDataAccessor; private DhServerLevel level; private int levelHashCode; + @Override public int getLevelHashCode() { return levelHashCode; } + private DhChunkPos chunkPos; private ByteBuf dataBuffer; @@ -90,9 +92,7 @@ public class FullDataPartialUpdateMessage extends NetworkMessage @Nullable public ChunkSizedFullDataAccessor getFullDataSource(IDhLevel level) throws IOException, InterruptedException { - // TODO Multiverse support - if (levelHashCode != level.getLevelWrapper().getDimensionType().getDimensionName().hashCode()) - return null; + if (isLevelInvalid(level.getLevelWrapper())) return null; try (ByteBufInputStream inputStream = new ByteBufInputStream(dataBuffer)) { From 877a89d17aa299b5ce075df9cda64161c6b33ca5 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Thu, 21 Sep 2023 21:00:23 +0500 Subject: [PATCH 047/205] Add config for a few slow features --- .../distanthorizons/core/config/Config.java | 66 ++++++++++++------- .../RemoteFullDataFileHandler.java | 10 +-- .../WorldRemoteGenerationQueue.java | 14 ++-- .../core/level/DhClientLevel.java | 2 +- .../core/level/DhServerLevel.java | 27 +++++--- .../core/multiplayer/MultiplayerConfig.java | 34 ---------- .../{ => client}/ClientNetworkState.java | 30 +++++---- .../config/AbstractMultiplayerConfig.java | 23 +++++++ .../multiplayer/config/MultiplayerConfig.java | 39 +++++++++++ .../MultiplayerConfigChangeListener.java | 33 ++++++++++ .../RemotePlayerConnectionHandler.java | 20 +++++- .../{ => server}/ServerPlayerState.java | 5 +- .../server/ServersideMultiplayerConfig.java | 44 +++++++++++++ .../core/network/NetworkClient.java | 8 ++- .../exceptions/RequestRejectedException.java | 10 +++ .../messages/base/ExceptionMessage.java | 2 + .../FullDataSourceResponseMessage.java | 2 - .../session/RemotePlayerConfigMessage.java | 15 +++-- .../core/world/DhClientWorld.java | 4 +- .../core/world/DhServerWorld.java | 32 +-------- 20 files changed, 285 insertions(+), 135 deletions(-) delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/multiplayer/MultiplayerConfig.java rename core/src/main/java/com/seibel/distanthorizons/core/multiplayer/{ => client}/ClientNetworkState.java (69%) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java rename core/src/main/java/com/seibel/distanthorizons/core/multiplayer/{ => server}/RemotePlayerConnectionHandler.java (80%) rename core/src/main/java/com/seibel/distanthorizons/core/multiplayer/{ => server}/ServerPlayerState.java (78%) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/RequestRejectedException.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index c2741fab5..1d4d01d22 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -755,6 +755,8 @@ public class Config public static class Multiplayer { + public static ConfigCategory serverNetworking = new ConfigCategory.Builder().set(ServerNetworking.class).build(); + public static ConfigEntry serverFolderNameMode = new ConfigEntry.Builder() .set(EServerFolderNameMode.NAME_ONLY) .comment("" @@ -800,29 +802,46 @@ public class Config + "") .build(); - // not currently implemented - public static ConfigEntry enableServerNetworking = new ConfigEntry.Builder() - .set(false) - .comment("" - + "Attention: \n" - + " 1. This feature is not fully implemented. \n" - + " 2. If you really want to use it, enable it only on trusted server/with trusted players. \n" - + "\n" - + "If true Distant Horizons will attempt to communicate with the connected \n" - + "server in order to load LODs outside your vanilla render distance. \n" - + "\n" - + "Note: This requires DH to be installed on the server in order to function. \n" - + "") - .build(); - - public static ConfigEntry serverNetworkingRateLimit = new ConfigEntry.Builder() - .setMinDefaultMax(1, 20, 100) - .comment("" - + "Limits the amount of sent/processed LOD requests concurrently. \n" - + "\n" - + "Note: Server can set its own rate limit. \n" - + "") - .build(); + // TODO Write strings + public static class ServerNetworking + { + public static ConfigEntry enableServerNetworking = new ConfigEntry.Builder() + .set(true) + .comment("" + + "Attention: \n" + + " 1. This feature is not fully implemented. \n" + + " 2. If you really want to use it, enable it only on trusted server/with trusted players. \n" + + "\n" + + "If true Distant Horizons will attempt to communicate with the connected \n" + + "server in order to load LODs outside your vanilla render distance. \n" + + "\n" + + "Note: This requires DH to be installed on the server in order to function. \n" + + "") + .build(); + + public static ConfigEntry requestRateLimit = new ConfigEntry.Builder() + .setMinDefaultMax(1, 20, 100) + .comment("" + + "Limits the amount of sent/processed LOD requests concurrently. \n" + + "\n" + + "Note: Server can set its own rate limit. \n" + + "") + .build(); + + public static ConfigEntry enableRealTimeUpdates = new ConfigEntry.Builder() + .set(false) + .comment("" + + "Enables real time updates from server." + + "") + .build(); + + public static ConfigEntry enablePostRelogUpdate = new ConfigEntry.Builder() + .set(false) + .comment("" + + "Enables updating of LODs after relog." + + "") + .build(); + } } @@ -1081,6 +1100,7 @@ public class Config } + // TODO write strings public static class Debugging { public static ConfigCategory debugWireframeRendering = new ConfigCategory.Builder().set(DebugWireframeRendering.class).build(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java index 35a70c537..544ac0e7b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.file.fullDatafile; +import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; @@ -26,12 +27,12 @@ import com.seibel.distanthorizons.core.generation.IWorldGenerationQueue; import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.multiplayer.ClientNetworkState; +import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; import com.seibel.distanthorizons.core.network.exceptions.InvalidSectionPosException; +import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataChangeSummaryRequestMessage; import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataChangeSummaryResponseMessage; -import com.seibel.distanthorizons.core.pos.DhLodPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; @@ -144,11 +145,10 @@ public class RemoteFullDataFileHandler extends GeneratedFullDataFileHandler @Override public FullDataMetaFile getFileIfExist(DhSectionPos pos) { - // This feature is broken - same data may produce different hashes, apparently - if (true) + if (this.networkState == null || !this.isFileUnloaded(pos)) return super.getFileIfExist(pos); - if (this.networkState == null || !this.isFileUnloaded(pos)) + if (!this.networkState.config.postRelogUpdateEnabled) return super.getFileIfExist(pos); FullDataMetaFile metaFile = super.getFileIfExist(pos); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index fdb7a3d12..b207eba97 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -8,7 +8,8 @@ import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult; import com.seibel.distanthorizons.core.level.IDhClientLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.f3.F3Screen; -import com.seibel.distanthorizons.core.multiplayer.ClientNetworkState; +import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; +import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceRequestMessage; import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceResponseMessage; @@ -178,13 +179,8 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug waitingTasks.remove(sectionPos); LOGGER.debug("FullDataSourceResponseMessage " + sectionPos); + CompleteFullDataSource fullDataSource = response.getFullDataSource(sectionPos, level); - - // FIXME Add dimension context to request instead - // Check is dimension has been switched - received data may no longer be relevant - if (fullDataSource == null) - throw new CancellationException(); - Consumer chunkDataConsumer = entry.tracker.getChunkDataConsumer(); // FIXME Why keeping a reference in first place @@ -193,6 +189,10 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug fullDataSource.splitIntoChunkSizedAccessors(chunkDataConsumer); } + catch (InvalidLevelException ignored) + { + // We're too late + } catch (ChannelException | RateLimitedException e) { if (e instanceof RateLimitedException) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 3b324752a..3ee003ff1 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -26,7 +26,7 @@ import com.seibel.distanthorizons.core.file.fullDatafile.RemoteFullDataFileHandl import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.generation.WorldRemoteGenerationQueue; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.multiplayer.ClientNetworkState; +import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; import com.seibel.distanthorizons.core.network.NetworkClient; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataPartialUpdateMessage; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index bc75fdd5e..8ae9c8ac0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -19,7 +19,6 @@ package com.seibel.distanthorizons.core.level; -import com.seibel.distanthorizons.core.config.AppliedConfigState; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; @@ -28,13 +27,13 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.I import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile; import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; -import com.seibel.distanthorizons.core.multiplayer.ServerPlayerState; -import com.seibel.distanthorizons.core.multiplayer.RemotePlayerConnectionHandler; +import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState; +import com.seibel.distanthorizons.core.multiplayer.server.RemotePlayerConnectionHandler; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkServer; -import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; import com.seibel.distanthorizons.core.network.exceptions.InvalidSectionPosException; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; +import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; import com.seibel.distanthorizons.core.network.messages.base.CancelMessage; import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceRequestMessage; import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceResponseMessage; @@ -72,7 +71,6 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel private final LinkedBlockingQueue worldGenLoopingQueue = new LinkedBlockingQueue<>(); private final ConcurrentMap incompleteDataSources = new ConcurrentHashMap<>(); private final ConcurrentMap fullDataRequests = new ConcurrentHashMap<>(); - private final AppliedConfigState rateLimitConfig = new AppliedConfigState<>(Config.Client.Advanced.Multiplayer.serverNetworkingRateLimit); public DhServerLevel(AbstractSaveStructure saveStructure, IServerLevelWrapper serverLevelWrapper, RemotePlayerConnectionHandler remotePlayerConnectionHandler) @@ -94,10 +92,10 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel { this.eventSource.registerHandler(FullDataSourceRequestMessage.class, remotePlayerConnectionHandler.currentLevelOnly(this, (msg, serverPlayerState) -> { - if (serverPlayerState.pendingFullDataRequests.incrementAndGet() > rateLimitConfig.get()) + if (serverPlayerState.pendingFullDataRequests.incrementAndGet() > serverPlayerState.config.getFullDataRequestRateLimit()) { serverPlayerState.pendingFullDataRequests.decrementAndGet(); - msg.sendResponse(new RateLimitedException("Max concurrent requests: " + rateLimitConfig.get())); + msg.sendResponse(new RateLimitedException("Max concurrent requests: " + serverPlayerState.config.getFullDataRequestRateLimit())); return; } @@ -110,7 +108,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel }); return newEntry; }); - // If this fails, current entry is being drained and need create another one + // If this fails, current entry is being drained and need to create another one if (entry.requestCollectionSemaphore.tryAcquire()) { fullDataRequests.put(msg.futureId, entry); @@ -130,6 +128,12 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel this.eventSource.registerHandler(FullDataChangeSummaryRequestMessage.class, remotePlayerConnectionHandler.currentLevelOnly(this, (msg, serverPlayerState) -> { + if (!Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate.get()) + { + msg.sendResponse(new RequestRejectedException("Operation is disabled from config.")); + return; + } + // Load files and check checksums HashSet changedPosList = new HashSet<>(); for (Map.Entry entry : msg.checksums.entrySet()) @@ -226,11 +230,16 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel if (future == null) return null; + if (!Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates.get()) + return future; + future.thenAccept(chunkSizedFullDataAccessor -> { for (ServerPlayerState serverPlayerState : remotePlayerConnectionHandler.getConnectedPlayers()) { - if (chunk.getChunkPos().distance(new DhChunkPos(serverPlayerState.serverPlayer.getPosition())) <= serverPlayerState.config.renderDistance) + if (serverPlayerState.config.isRealTimeUpdatesEnabled()) continue; + + if (chunk.getChunkPos().distance(new DhChunkPos(serverPlayerState.serverPlayer.getPosition())) <= serverPlayerState.config.getRenderDistance()) serverPlayerState.channelContext.writeAndFlush(new FullDataPartialUpdateMessage(chunkSizedFullDataAccessor, this)); } }); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/MultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/MultiplayerConfig.java deleted file mode 100644 index f70cc6c0e..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/MultiplayerConfig.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.seibel.distanthorizons.core.multiplayer; - -import com.seibel.distanthorizons.core.network.protocol.INetworkObject; -import io.netty.buffer.ByteBuf; - -public class MultiplayerConfig implements INetworkObject -{ - public int renderDistance; - public int fullDataRequestRateLimit; - - - @Override - public void encode(ByteBuf out) - { - out.writeInt(this.renderDistance); - out.writeInt(this.fullDataRequestRateLimit); - } - - @Override - public void decode(ByteBuf in) - { - this.renderDistance = in.readInt(); - this.fullDataRequestRateLimit = in.readInt(); - } - - @Override public String toString() - { - return "MultiplayerConfig{" + - "renderDistance=" + renderDistance + - ", fullDataRequestRateLimit=" + fullDataRequestRateLimit + - '}'; - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java similarity index 69% rename from core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java rename to core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index 59acd6b8b..4870863b5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -1,7 +1,8 @@ -package com.seibel.distanthorizons.core.multiplayer; +package com.seibel.distanthorizons.core.multiplayer.client; -import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; +import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkClient; import com.seibel.distanthorizons.core.network.messages.base.AckMessage; @@ -20,6 +21,7 @@ public class ClientNetworkState implements Closeable private final NetworkClient client; private final UUID playerUUID; public MultiplayerConfig config = new MultiplayerConfig(); + private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::onConfigChanged); /** * Returns the client used by this instance.

@@ -31,12 +33,13 @@ public class ClientNetworkState implements Closeable * Constructs a new instance. * * @param networkClient Client to use. It is assumed that this client will be at full control by this instance. - * @param playerUUID UUID of a player connected + * @param playerUUID UUID of a player connected. */ public ClientNetworkState(NetworkClient networkClient, UUID playerUUID) { this.client = networkClient; this.playerUUID = playerUUID; + this.registerNetworkHandlers(); this.client.startConnecting(); } @@ -48,23 +51,28 @@ public class ClientNetworkState implements Closeable LOGGER.info("Connected to server: "+helloMessage.getChannelContext().channel().remoteAddress()); this.getClient().sendRequest(new PlayerUUIDMessage(playerUUID), AckMessage.class) - .thenCompose(ack -> this.getClient().sendRequest(new RemotePlayerConfigMessage(new MultiplayerConfig() - {{ - renderDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistance.get(); - fullDataRequestRateLimit = Config.Client.Advanced.Multiplayer.serverNetworkingRateLimit.get(); - }}), RemotePlayerConfigMessage.class)) - .thenAccept(msg -> { - this.config = msg.payload; - }) + .thenAccept(ack -> this.getClient().sendMessage(new RemotePlayerConfigMessage(new MultiplayerConfig()))) .exceptionally(throwable -> { LOGGER.error("Error while fetching server's config", throwable); return null; }); }); + + this.client.registerHandler(RemotePlayerConfigMessage.class, msg -> + { + LOGGER.info("Connection config was changed: " + msg.payload); + this.config = (MultiplayerConfig) msg.payload; + }); + } + + private void onConfigChanged() + { + this.getClient().sendMessage(new RemotePlayerConfigMessage(new MultiplayerConfig())); } public void close() { + this.configChangeListener.close(); this.client.close(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java new file mode 100644 index 000000000..bfcd264b1 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java @@ -0,0 +1,23 @@ +package com.seibel.distanthorizons.core.multiplayer.config; + +import com.seibel.distanthorizons.core.network.protocol.INetworkObject; +import io.netty.buffer.ByteBuf; + +public abstract class AbstractMultiplayerConfig implements INetworkObject +{ + public abstract int getRenderDistance(); + public abstract int getFullDataRequestRateLimit(); + public abstract boolean isRealTimeUpdatesEnabled(); + public abstract boolean isPostRelogUpdateEnabled(); + + @Override + public void encode(ByteBuf out) + { + out.writeInt(this.getRenderDistance()); + out.writeInt(this.getFullDataRequestRateLimit()); + out.writeBoolean(this.isRealTimeUpdatesEnabled()); + out.writeBoolean(this.isPostRelogUpdateEnabled()); + } + + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java new file mode 100644 index 000000000..fae45c402 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java @@ -0,0 +1,39 @@ +package com.seibel.distanthorizons.core.multiplayer.config; + +import com.seibel.distanthorizons.core.config.Config; +import io.netty.buffer.ByteBuf; + +public class MultiplayerConfig extends AbstractMultiplayerConfig +{ + public int renderDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistance.get(); + @Override public int getRenderDistance() { return renderDistance; } + + public int fullDataRequestRateLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.requestRateLimit.get(); + @Override public int getFullDataRequestRateLimit() { return fullDataRequestRateLimit; } + + public boolean realTimeUpdatesEnabled = Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates.get(); + @Override public boolean isRealTimeUpdatesEnabled() { return realTimeUpdatesEnabled; } + + public boolean postRelogUpdateEnabled = Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate.get(); + @Override public boolean isPostRelogUpdateEnabled() { return postRelogUpdateEnabled; } + + @Override + public void decode(ByteBuf in) + { + this.renderDistance = in.readInt(); + this.fullDataRequestRateLimit = in.readInt(); + this.realTimeUpdatesEnabled = in.readBoolean(); + this.postRelogUpdateEnabled = in.readBoolean(); + } + + @Override public String toString() + { + return "MultiplayerConfig{" + + "renderDistance=" + renderDistance + + ", fullDataRequestRateLimit=" + fullDataRequestRateLimit + + ", realTimeUpdatesEnabled=" + realTimeUpdatesEnabled + + ", postRelogUpdatesEnabled=" + postRelogUpdateEnabled + + '}'; + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java new file mode 100644 index 000000000..286f7f976 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java @@ -0,0 +1,33 @@ +package com.seibel.distanthorizons.core.multiplayer.config; + +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener; +import com.seibel.distanthorizons.core.config.types.ConfigEntry; + +import java.io.Closeable; + +public class MultiplayerConfigChangeListener implements Closeable +{ + private final ConfigChangeListener renderDistance; + private final ConfigChangeListener rateLimit; + private final ConfigChangeListener enableRealTimeUpdates; + private final ConfigChangeListener enablePostRelogUpdate; + + public MultiplayerConfigChangeListener(Runnable runnable) + { + renderDistance = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistance, ignored -> runnable.run()); + rateLimit = new ConfigChangeListener<>(Config.Client.Advanced.Multiplayer.ServerNetworking.requestRateLimit, ignored -> runnable.run()); + enableRealTimeUpdates = new ConfigChangeListener<>(Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates, ignored -> runnable.run()); + enablePostRelogUpdate = new ConfigChangeListener<>(Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate, ignored -> runnable.run()); + } + + @Override + public void close() + { + renderDistance.close(); + rateLimit.close(); + enableRealTimeUpdates.close(); + enablePostRelogUpdate.close(); + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java similarity index 80% rename from core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java rename to core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java index 09a0b11db..e8828d75a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java @@ -1,14 +1,17 @@ -package com.seibel.distanthorizons.core.multiplayer; +package com.seibel.distanthorizons.core.multiplayer.server; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.seibel.distanthorizons.core.level.DhServerLevel; +import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; +import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkServer; import com.seibel.distanthorizons.core.network.messages.base.ILevelRelatedMessage; import com.seibel.distanthorizons.core.network.messages.base.AckMessage; import com.seibel.distanthorizons.core.network.messages.base.CloseEvent; import com.seibel.distanthorizons.core.network.messages.session.PlayerUUIDMessage; +import com.seibel.distanthorizons.core.network.messages.session.RemotePlayerConfigMessage; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import io.netty.channel.ChannelHandlerContext; @@ -26,6 +29,8 @@ public class RemotePlayerConnectionHandler implements Closeable private final HashMap playersByUUID = new HashMap<>(); private final BiMap playersByConnection = HashBiMap.create(); + private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::onConfigChanged); + public NetworkServer server() { return this.eventSource.parent; } public RemotePlayerConnectionHandler(NetworkServer networkServer) @@ -59,6 +64,12 @@ public class RemotePlayerConnectionHandler implements Closeable playerUUIDMessage.sendResponse(new AckMessage()); }); + this.eventSource.registerHandler(RemotePlayerConfigMessage.class, this.connectedPlayersOnly((remotePlayerConfigMessage, serverPlayerState) -> + { + serverPlayerState.config.clientConfig = (MultiplayerConfig) remotePlayerConfigMessage.payload; + serverPlayerState.channelContext.writeAndFlush(new RemotePlayerConfigMessage(serverPlayerState.config)); + })); + this.eventSource.registerHandler(CloseEvent.class, closeEvent -> { ServerPlayerState dhPlayer = this.playersByConnection.remove(closeEvent.getChannelContext()); @@ -69,6 +80,12 @@ public class RemotePlayerConnectionHandler implements Closeable }); } + private void onConfigChanged() + { + for (ServerPlayerState serverPlayerState : this.getConnectedPlayers()) + serverPlayerState.channelContext.writeAndFlush(new RemotePlayerConfigMessage(serverPlayerState.config)); + } + public Consumer connectedPlayersOnly(BiConsumer next) { return msg -> @@ -131,6 +148,7 @@ public class RemotePlayerConnectionHandler implements Closeable @Override public void close() { + this.configChangeListener.close(); this.eventSource.close(); this.server().close(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java similarity index 78% rename from core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ServerPlayerState.java rename to core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index bf7e83f7f..c2fde5ef5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -1,4 +1,4 @@ -package com.seibel.distanthorizons.core.multiplayer; +package com.seibel.distanthorizons.core.multiplayer.server; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import io.netty.channel.ChannelHandlerContext; @@ -8,8 +8,9 @@ import java.util.concurrent.atomic.AtomicInteger; public class ServerPlayerState { public IServerPlayerWrapper serverPlayer; - public MultiplayerConfig config; public ChannelHandlerContext channelContext; + + public ServersideMultiplayerConfig config = new ServersideMultiplayerConfig(); public final AtomicInteger pendingFullDataRequests = new AtomicInteger(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java new file mode 100644 index 000000000..0f6f36c13 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java @@ -0,0 +1,44 @@ +package com.seibel.distanthorizons.core.multiplayer.server; + +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.multiplayer.config.AbstractMultiplayerConfig; +import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; +import io.netty.buffer.ByteBuf; + +/** + * Used for constraining the client config to the server config. + */ +public class ServersideMultiplayerConfig extends AbstractMultiplayerConfig +{ + public MultiplayerConfig clientConfig = new MultiplayerConfig(); + + @Override + public int getRenderDistance() + { + return Math.min(clientConfig.renderDistance, Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistance.get()); + } + + @Override + public int getFullDataRequestRateLimit() + { + return Math.min(clientConfig.fullDataRequestRateLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.requestRateLimit.get()); + } + + @Override + public boolean isRealTimeUpdatesEnabled() + { + return clientConfig.realTimeUpdatesEnabled && Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates.get(); + } + + @Override + public boolean isPostRelogUpdateEnabled() { + return clientConfig.postRelogUpdateEnabled && Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate.get(); + } + + @Override + public void decode(ByteBuf in) + { + throw new UnsupportedOperationException("Decoding is not supported for server-only class."); + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index ecbb55955..986da58eb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -26,6 +26,7 @@ import com.seibel.distanthorizons.core.network.messages.base.HelloMessage; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.MessageHandler; import com.seibel.distanthorizons.core.network.protocol.NetworkChannelInitializer; +import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; @@ -131,7 +132,7 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable return; } - channel.writeAndFlush(new HelloMessage()); + sendMessage(new HelloMessage()); ready = true; }); @@ -168,6 +169,11 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable }); } + public final void sendMessage(NetworkMessage msg) + { + this.channel.writeAndFlush(msg); + } + public final CompletableFuture sendRequest(FutureTrackableNetworkMessage msg, Class responseClass) { return this.sendRequest(this.channel.pipeline().context(MessageHandler.class), msg, responseClass); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/RequestRejectedException.java b/core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/RequestRejectedException.java new file mode 100644 index 000000000..00f1ca574 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/RequestRejectedException.java @@ -0,0 +1,10 @@ +package com.seibel.distanthorizons.core.network.exceptions; + +public class RequestRejectedException extends Exception +{ + public RequestRejectedException(String message) + { + super(message); + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ExceptionMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ExceptionMessage.java index ffac30807..25f7b6ccd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ExceptionMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ExceptionMessage.java @@ -22,6 +22,7 @@ package com.seibel.distanthorizons.core.network.messages.base; import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; import com.seibel.distanthorizons.core.network.exceptions.InvalidSectionPosException; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; +import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import io.netty.buffer.ByteBuf; @@ -36,6 +37,7 @@ public class ExceptionMessage extends FutureTrackableNetworkMessage add(RateLimitedException.class); add(InvalidLevelException.class); add(InvalidSectionPosException.class); + add(RequestRejectedException.class); }}; public Exception exception; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java index 930e1ea2d..742188dde 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java @@ -73,10 +73,8 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage this.dataBuffer = in.readBytes(in.readInt()); } - @Nullable public CompleteFullDataSource getFullDataSource(DhSectionPos pos, IDhLevel level) throws IOException, InterruptedException { - try (ByteBufInputStream inputStream = new ByteBufInputStream(dataBuffer)) { return fullDataSourceLoader.loadData(pos, new DhDataInputStream(inputStream), level); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/session/RemotePlayerConfigMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/session/RemotePlayerConfigMessage.java index dd70197bc..d4b2f7b55 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/session/RemotePlayerConfigMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/session/RemotePlayerConfigMessage.java @@ -19,23 +19,24 @@ package com.seibel.distanthorizons.core.network.messages.session; -import com.seibel.distanthorizons.core.multiplayer.MultiplayerConfig; -import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.multiplayer.config.AbstractMultiplayerConfig; +import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; +import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import io.netty.buffer.ByteBuf; -public class RemotePlayerConfigMessage extends FutureTrackableNetworkMessage +public class RemotePlayerConfigMessage extends NetworkMessage { - public MultiplayerConfig payload; + public AbstractMultiplayerConfig payload; public RemotePlayerConfigMessage() { } - public RemotePlayerConfigMessage(MultiplayerConfig payload) { this.payload = payload; } + public RemotePlayerConfigMessage(AbstractMultiplayerConfig payload) { this.payload = payload; } @Override - public void encode0(ByteBuf out) { this.payload.encode(out); } + public void encode(ByteBuf out) { this.payload.encode(out); } @Override - public void decode0(ByteBuf in) { this.payload = INetworkObject.decodeStatic(new MultiplayerConfig(), in); } + public void decode(ByteBuf in) { this.payload = INetworkObject.decodeStatic(new MultiplayerConfig(), in); } @Override public String toString() { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java index aed0f25e0..c21bb7114 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java @@ -24,7 +24,7 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure; import com.seibel.distanthorizons.core.level.DhClientLevel; import com.seibel.distanthorizons.core.level.IDhLevel; -import com.seibel.distanthorizons.core.multiplayer.ClientNetworkState; +import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; import com.seibel.distanthorizons.core.network.NetworkClient; import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.util.objects.EventLoop; @@ -63,7 +63,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld this.saveStructure = new ClientOnlySaveStructure(); this.levels = new ConcurrentHashMap<>(); - if (Config.Client.Advanced.Multiplayer.enableServerNetworking.get()) + if (Config.Client.Advanced.Multiplayer.ServerNetworking.enableServerNetworking.get()) { // TODO server specific configs NetworkClient networkClient = new NetworkClient(MC_CLIENT.getCurrentServerIp(), 25049); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java index 2f8856f04..ae0ae691a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java @@ -19,15 +19,11 @@ package com.seibel.distanthorizons.core.world; -import com.seibel.distanthorizons.core.config.AppliedConfigState; -import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.file.structure.LocalSaveStructure; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.level.IDhLevel; -import com.seibel.distanthorizons.core.multiplayer.ServerPlayerState; -import com.seibel.distanthorizons.core.multiplayer.RemotePlayerConnectionHandler; +import com.seibel.distanthorizons.core.multiplayer.server.RemotePlayerConnectionHandler; import com.seibel.distanthorizons.core.network.NetworkServer; -import com.seibel.distanthorizons.core.network.messages.session.RemotePlayerConfigMessage; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; @@ -41,10 +37,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld { private final HashMap levels; public final LocalSaveStructure saveStructure; - private final RemotePlayerConnectionHandler remotePlayerConnectionHandler; - private final AppliedConfigState rateLimitConfig = new AppliedConfigState<>(Config.Client.Advanced.Multiplayer.serverNetworkingRateLimit); - //==============// @@ -61,21 +54,9 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld // TODO move to global payload once server specific configs are implemented NetworkServer networkServer = new NetworkServer(25049); this.remotePlayerConnectionHandler = new RemotePlayerConnectionHandler(networkServer); - this.registerNetworkHandlers(); LOGGER.info("Started "+DhServerWorld.class.getSimpleName()+" of type "+this.environment); } - - private void registerNetworkHandlers() - { - this.remotePlayerConnectionHandler.server().registerHandler(RemotePlayerConfigMessage.class, remotePlayerConfigMessage -> - { - this.remotePlayerConnectionHandler.getConnectedPlayer(remotePlayerConfigMessage).config = remotePlayerConfigMessage.payload; - - remotePlayerConfigMessage.payload.fullDataRequestRateLimit = Math.min(rateLimitConfig.get(), remotePlayerConfigMessage.payload.fullDataRequestRateLimit); - remotePlayerConfigMessage.sendResponse(remotePlayerConfigMessage); - }); - } @@ -95,8 +76,8 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld } public void changePlayerLevel(IServerPlayerWrapper player, IServerLevelWrapper origin, IServerLevelWrapper dest) { - this.getLevel(origin).removePlayer(player); this.getLevel(dest).addPlayer(player); + this.getLevel(origin).removePlayer(player); } @Override @@ -147,15 +128,6 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld public void serverTick() { this.levels.values().forEach(DhServerLevel::serverTick); - - if (rateLimitConfig.pollNewValue()) - { - for (ServerPlayerState serverPlayerState : this.remotePlayerConnectionHandler.getConnectedPlayers()) - { - serverPlayerState.config.fullDataRequestRateLimit = rateLimitConfig.get(); - serverPlayerState.channelContext.writeAndFlush(new RemotePlayerConfigMessage(serverPlayerState.config)); - } - } } public void doWorldGen() { From e94d67916d0dd56cd531497b278ff28df2d9ea6a Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 23 Sep 2023 15:32:24 +0500 Subject: [PATCH 048/205] Fix compiling Remove unused config category Use pooling when receiving sections Fix use of real time update config Fix debug renderer not unregistered on queue close --- .../distanthorizons/core/config/Config.java | 40 ------------------- .../loader/CompleteFullDataSourceLoader.java | 7 +++- .../interfaces/IStreamableFullDataSource.java | 16 ++++++++ .../fullDatafile/FullDataFileHandler.java | 1 + .../file/fullDatafile/FullDataMetaFile.java | 2 - .../WorldRemoteGenerationQueue.java | 6 +-- .../core/level/DhServerLevel.java | 2 +- .../FullDataSourceResponseMessage.java | 1 + 8 files changed, 27 insertions(+), 48 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 1da4d4813..c5f0bc8a2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -1103,8 +1103,6 @@ public class Config // TODO write strings public static class Debugging { - public static ConfigCategory debugWireframeRendering = new ConfigCategory.Builder().set(DebugWireframeRendering.class).build(); - public static ConfigEntry rendererMode = new ConfigEntry.Builder() .set(ERendererMode.DEFAULT) .comment("" @@ -1153,44 +1151,6 @@ public class Config + " Mod compatibility is not guaranteed.") .build(); - public static class DebugWireframeRendering - { - public static ConfigEntry enableRendering = new ConfigEntry.Builder() - .set(false) - .comment("" - + "If enabled, various wireframes for debugging internal functions will be drawn. \n" - + "\n" - + "NOTE: There WILL be performance hit! \n" - + " Additionally, only stuff that's loaded after you enable this \n" - + " will render their debug wireframes.") - .build(); - - public static ConfigEntry fullDataMetaFile = new ConfigEntry.Builder() - .set(false) - .comment("Class: FullDataMetaFile") - .build(); - - public static ConfigEntry lodRenderSection = new ConfigEntry.Builder() - .set(false) - .comment("Class: LodRenderSection") - .build(); - - public static ConfigEntry renderDataMetaFile = new ConfigEntry.Builder() - .set(false) - .comment("Class: RenderDataMetaFile") - .build(); - - public static ConfigEntry worldGenerationQueue = new ConfigEntry.Builder() - .set(false) - .comment("Class: WorldGenerationQueue") - .build(); - - public static ConfigEntry worldRemoteGenerationQueue = new ConfigEntry.Builder() - .set(false) - .comment("Class: WorldRemoteGenerationQueue") - .build(); - } - public static ConfigEntry enableWhiteWorld = new ConfigEntry.Builder() .set(false) .comment("" diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/loader/CompleteFullDataSourceLoader.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/loader/CompleteFullDataSourceLoader.java index 36d975894..0de9428cc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/loader/CompleteFullDataSourceLoader.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/loader/CompleteFullDataSourceLoader.java @@ -38,8 +38,11 @@ public class CompleteFullDataSourceLoader extends AbstractFullDataSourceLoader /** Uses a given stream to create a temporary {@link CompleteFullDataSource}, which is not saved. */ public CompleteFullDataSource loadData(DhSectionPos pos, DhDataInputStream inputStream, IDhLevel level) throws IOException, InterruptedException { - CompleteFullDataSource dataSource = CompleteFullDataSource.createEmpty(pos); - dataSource.populateFromStream(null, inputStream, level); + CompleteFullDataSource dataSource = (CompleteFullDataSource) this.tryGetPooledSource(); + if (dataSource == null) + dataSource = CompleteFullDataSource.createEmpty(pos); + + dataSource.repopulateFromStream(pos, inputStream, level); return dataSource; } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IStreamableFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IStreamableFullDataSource.java index 9a12568e7..42eeb670c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IStreamableFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IStreamableFullDataSource.java @@ -66,6 +66,22 @@ public interface IStreamableFullDataSource DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL) { return; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index b207eba97..c21ac1890 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -55,7 +55,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug { this.networkState = networkState; this.level = level; - DebugRenderer.register(this); + DebugRenderer.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showWorldGenQueue); } @Override @@ -188,6 +188,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug return entry.future.cancel(false); fullDataSource.splitIntoChunkSizedAccessors(chunkDataConsumer); + response.getFullDataSourceLoader().returnPooledDataSource(fullDataSource); } catch (InvalidLevelException ignored) { @@ -255,13 +256,12 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug public void close() { f3Message.close(); + DebugRenderer.unregister(this, Config.Client.Advanced.Debugging.DebugWireframe.showWorldGenQueue); } @Override public void debugRender(DebugRenderer r) { - if (!Config.Client.Advanced.Debugging.DebugWireframeRendering.worldRemoteGenerationQueue.get()) return; - for (Map.Entry mapEntry : waitingTasks.entrySet()) { r.renderBox(new DebugRenderer.Box(mapEntry.getKey(), -32f, 64f, 0.05f, diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 8ae9c8ac0..146511579 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -237,7 +237,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel { for (ServerPlayerState serverPlayerState : remotePlayerConnectionHandler.getConnectedPlayers()) { - if (serverPlayerState.config.isRealTimeUpdatesEnabled()) continue; + if (!serverPlayerState.config.isRealTimeUpdatesEnabled()) continue; if (chunk.getChunkPos().distance(new DhChunkPos(serverPlayerState.serverPlayer.getPosition())) <= serverPlayerState.config.getRenderDistance()) serverPlayerState.channelContext.writeAndFlush(new FullDataPartialUpdateMessage(chunkSizedFullDataAccessor, this)); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java index 742188dde..e29538f01 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java @@ -41,6 +41,7 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage private DhServerLevel level; private CompleteFullDataSourceLoader fullDataSourceLoader; + public CompleteFullDataSourceLoader getFullDataSourceLoader() { return fullDataSourceLoader; } private ByteBuf dataBuffer; public FullDataSourceResponseMessage() {} From 381cae7307bd1d346f0a26c31b2f302f3cac064b Mon Sep 17 00:00:00 2001 From: Steveplays28 Date: Sat, 23 Sep 2023 16:33:43 +0200 Subject: [PATCH 049/205] feat: Add config option for server port --- .../com/seibel/distanthorizons/core/config/Config.java | 7 +++++++ .../core/file/fullDatafile/FullDataFileHandler.java | 1 + .../core/generation/WorldRemoteGenerationQueue.java | 2 +- .../seibel/distanthorizons/core/world/DhClientWorld.java | 2 +- 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 1da4d4813..15d3c6359 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -841,6 +841,13 @@ public class Config + "Enables updating of LODs after relog." + "") .build(); + + public static ConfigEntry serverPort = new ConfigEntry.Builder() + .set(25049) + .comment("" + + "The port on the server that's used for sending LOD data." + + "") + .build(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java index b3b8c57f2..b1719b5cc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java @@ -38,6 +38,7 @@ import com.seibel.distanthorizons.core.util.FileUtil; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.ThreadUtil; import org.apache.logging.log4j.Logger; +import com.seibel.distanthorizons.core.dataObjects.fullData.loader.AbstractFullDataSourceLoader; import java.awt.*; import java.io.File; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index b207eba97..ccea477cc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -55,7 +55,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug { this.networkState = networkState; this.level = level; - DebugRenderer.register(this); + DebugRenderer.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showWorldGenQueue); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java index c21bb7114..d34091185 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java @@ -66,7 +66,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld if (Config.Client.Advanced.Multiplayer.ServerNetworking.enableServerNetworking.get()) { // TODO server specific configs - NetworkClient networkClient = new NetworkClient(MC_CLIENT.getCurrentServerIp(), 25049); + NetworkClient networkClient = new NetworkClient(MC_CLIENT.getCurrentServerIp().split(":")[0], Config.Client.Advanced.Multiplayer.ServerNetworking.serverPort.get()); this.networkState = new ClientNetworkState(networkClient, MC_CLIENT.getPlayerUUID()); } else From e4e6753a022176a7c74b0e8f5945f0a02c0b033f Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 24 Sep 2023 14:24:31 +0500 Subject: [PATCH 050/205] Add gen task rate display --- .../GeneratedFullDataFileHandler.java | 37 ++++++++++++++++++- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java index f4e5072e4..8360eb94f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java @@ -30,12 +30,14 @@ import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult; import com.seibel.distanthorizons.core.level.DhLevel; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.LodUtil; import org.apache.logging.log4j.Logger; import javax.annotation.Nullable; import java.lang.ref.WeakReference; +import java.text.DecimalFormat; import java.util.*; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicReference; @@ -50,12 +52,37 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler private final ArrayList onWorldGenTaskCompleteListeners = new ArrayList<>(); + private final ConcurrentSkipListSet taskCompletionTimes = new ConcurrentSkipListSet<>(); + private final F3Screen.DynamicMessage handlerF3Message; + // Use to hold onto incomplete data sources that are waiting for generation, so that they don't get GC'd before they are generated private final ConcurrentHashMap incompleteDataSources = new ConcurrentHashMap<>(); - public GeneratedFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure) { super(level, saveStructure); } - + public GeneratedFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure) { + super(level, saveStructure); + this.handlerF3Message = new F3Screen.DynamicMessage(() -> + { + // Keep only for last 30 seconds + taskCompletionTimes.removeIf(time -> time < System.currentTimeMillis() - 30000); + if (taskCompletionTimes.size() < 2) + return "Gen task completion time: No information yet"; + + double timePerCompletion = (double) (taskCompletionTimes.last() - taskCompletionTimes.first()) / taskCompletionTimes.size() / 1000; + if (timePerCompletion < 1) { + double completionRate = 1 / timePerCompletion; + return "Gen task completion rate: " + new DecimalFormat("#.00").format(completionRate) + " completions/sec"; + } else { + return "Gen task completion time: " + new DecimalFormat("#.00").format(timePerCompletion) + " seconds/completion"; + } + }); + } + @Override + public void close() + { + super.close(); + this.handlerF3Message.close(); + } //==================// // generation queue // @@ -256,6 +283,7 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler // generation completed, update the files and listener(s) this.flushAndSave(pos); //this.fireOnGenPosSuccessListeners(pos); + this.addTimestampToStatistics(); return; } else @@ -274,6 +302,11 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler genTask.releaseStrongReference(); } + private void addTimestampToStatistics() + { + taskCompletionTimes.add(System.currentTimeMillis()); + } + private void fireOnGenPosSuccessListeners(DhSectionPos pos) { if (true) return; From ed2fca240e45f46dcce0d0662487f14d1419327a Mon Sep 17 00:00:00 2001 From: Steveplays28 Date: Sun, 24 Sep 2023 11:33:49 +0200 Subject: [PATCH 051/205] fix: Fix compiling after merge --- .../core/file/fullDatafile/FullDataFileHandler.java | 4 ++-- .../core/generation/WorldRemoteGenerationQueue.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java index 1444d75fc..5dbdc63fd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java @@ -68,13 +68,13 @@ public class FullDataFileHandler implements IFullDataSourceProvider for (DhSectionPos pos : posList) { map.put(pos, - metaFileBySectionPos.containsKey(pos) ? 3 // Loaded + loadedMetaFileBySectionPos.containsKey(pos) ? 3 // Loaded : this.isFileUnloaded(pos) ? 2 // Unloaded : 1); // Not generated } return map; } - protected boolean isFileUnloaded(DhSectionPos pos) { return unloadedFileBySectionPos.containsKey(pos); } + protected boolean isFileUnloaded(DhSectionPos pos) { return !loadedMetaFileBySectionPos.containsKey(pos); } protected final IDhLevel level; protected final File saveDir; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index c21ac1890..707d11dd6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -80,7 +80,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug } @Override - public void runCurrentGenTasksUntilBusy(DhBlockPos2D targetPos) + public void startGenerationQueueAndSetTargetPos(DhBlockPos2D targetPos) { if (generatorClosingFuture != null || !networkState.getClient().isReady()) return; From 4fb9126d499e8635a6fba2c54b33a6d1e337c846 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 24 Sep 2023 18:48:14 +0500 Subject: [PATCH 052/205] Fix unloaded file check --- .../core/file/fullDatafile/FullDataFileHandler.java | 8 +++----- .../core/file/fullDatafile/RemoteFullDataFileHandler.java | 6 ++---- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java index 5dbdc63fd..539db7fa4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java @@ -38,13 +38,11 @@ import com.seibel.distanthorizons.core.util.FileUtil; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.ThreadUtil; import org.apache.logging.log4j.Logger; -import com.seibel.distanthorizons.core.dataObjects.fullData.loader.AbstractFullDataSourceLoader; import java.awt.*; import java.io.File; import java.io.IOException; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.concurrent.CompletableFuture; @@ -69,12 +67,12 @@ public class FullDataFileHandler implements IFullDataSourceProvider { map.put(pos, loadedMetaFileBySectionPos.containsKey(pos) ? 3 // Loaded - : this.isFileUnloaded(pos) ? 2 // Unloaded - : 1); // Not generated + : this.fileExists(pos) ? 2 // Unloaded + : 1); // Not generated } return map; } - protected boolean isFileUnloaded(DhSectionPos pos) { return !loadedMetaFileBySectionPos.containsKey(pos); } + protected boolean fileExists(DhSectionPos pos) { return this.computeDataFilePath(pos).exists(); } protected final IDhLevel level; protected final File saveDir; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java index 544ac0e7b..90b4098b3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java @@ -19,7 +19,6 @@ package com.seibel.distanthorizons.core.file.fullDatafile; -import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; @@ -30,7 +29,6 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; import com.seibel.distanthorizons.core.network.exceptions.InvalidSectionPosException; -import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataChangeSummaryRequestMessage; import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataChangeSummaryResponseMessage; import com.seibel.distanthorizons.core.pos.DhSectionPos; @@ -145,7 +143,7 @@ public class RemoteFullDataFileHandler extends GeneratedFullDataFileHandler @Override public FullDataMetaFile getFileIfExist(DhSectionPos pos) { - if (this.networkState == null || !this.isFileUnloaded(pos)) + if (this.networkState == null || !this.fileExists(pos)) return super.getFileIfExist(pos); if (!this.networkState.config.postRelogUpdateEnabled) @@ -155,7 +153,7 @@ public class RemoteFullDataFileHandler extends GeneratedFullDataFileHandler if (metaFile == null) return null; - LOGGER.info("Checking server updates for section {}", pos); + LOGGER.debug("Checking server updates for section {}", pos); pos.forEachChildAtLevel(DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL, childPos -> { FullDataMetaFile childMetaFile = super.getFileIfExist(childPos); From db31c543091a4686c36e2bdc451cc08f3fd1d8fa Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 26 Sep 2023 13:21:37 +0500 Subject: [PATCH 053/205] Fix Enable Distant Generation config being ignored --- .../seibel/distanthorizons/core/level/DhClientLevel.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 4cf798b73..8c0653503 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -19,6 +19,8 @@ package com.seibel.distanthorizons.core.level; +import com.seibel.distanthorizons.core.config.AppliedConfigState; +import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; @@ -72,6 +74,7 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel @Nullable private final ScopedNetworkEventSource eventSource; public final WorldGenModule worldGenModule; + public final AppliedConfigState worldGeneratorEnabledConfig; @@ -85,6 +88,7 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel this.saveStructure = saveStructure; this.dataFileHandler = new RemoteFullDataFileHandler(this, saveStructure, networkState); + this.worldGeneratorEnabledConfig = new AppliedConfigState<>(Config.Client.Advanced.WorldGenerator.enableDistantGeneration); this.networkState = networkState; this.worldGenModule = new WorldGenModule(dataFileHandler, this); if (networkState != null) @@ -135,8 +139,9 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel public void doWorldGen() { + this.worldGeneratorEnabledConfig.pollNewValue(); boolean isClientUsable = networkState != null && !networkState.getClient().isClosed(); - boolean shouldDoWorldGen = isClientUsable && clientside.isRendering(); + boolean shouldDoWorldGen = this.worldGeneratorEnabledConfig.get() && isClientUsable && clientside.isRendering(); boolean isWorldGenRunning = worldGenModule.isWorldGenRunning(); if (shouldDoWorldGen && !isWorldGenRunning) { From cc8b97f2fe00a8ae93d68ca86ade3bbeba8826e9 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 26 Sep 2023 22:03:04 +0500 Subject: [PATCH 054/205] Improve management of frequent real time updates --- .../core/level/DhServerLevel.java | 45 ++++++++++++++++--- .../misc/IServerPlayerWrapper.java | 2 + 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 575a21791..d623dd387 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -47,6 +47,7 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.util.objects.Pair; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; @@ -72,6 +73,11 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel private final ConcurrentMap incompleteDataSources = new ConcurrentHashMap<>(); private final ConcurrentMap fullDataRequests = new ConcurrentHashMap<>(); + // Used to manage frequent chunk updates. + // If a chunk is updated, sending will be delayed, waiting until other updates come around, + // i.e. if a chunk is constantly updated, it will be sent once it stops updating. + private final ConcurrentMap chunkUpdatesToSend = new ConcurrentHashMap<>(); + private static final int CHUNK_UPDATE_SEND_DELAY = 5000; public DhServerLevel(AbstractSaveStructure saveStructure, IServerLevelWrapper serverLevelWrapper, RemotePlayerConnectionHandler remotePlayerConnectionHandler) { @@ -187,6 +193,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel { chunkToLodBuilder.tick(); + // Send finished data source requests for (Map.Entry mapEntry : incompleteDataSources.entrySet()) { IncompleteDataSourceEntry entry = mapEntry.getValue(); @@ -221,6 +228,28 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel msg.sendResponse(new FullDataSourceResponseMessage(completeSource, this)); } } + + // Send updated chunks after delay + for (Map.Entry chunkUpdateEntry : chunkUpdatesToSend.entrySet()) + { + ChunkUpdateData chunkUpdateData = chunkUpdateEntry.getValue(); + + if (System.currentTimeMillis() < chunkUpdateData.time + CHUNK_UPDATE_SEND_DELAY) + continue; + + chunkUpdatesToSend.remove(chunkUpdateEntry.getKey()); + + for (ServerPlayerState serverPlayerState : remotePlayerConnectionHandler.getConnectedPlayers()) + { + if (!serverPlayerState.config.isRealTimeUpdatesEnabled()) continue; + + double distanceFromPlayer = chunkUpdateData.accessor.chunkPos.distance(new DhChunkPos(serverPlayerState.serverPlayer.getPosition())); + if (distanceFromPlayer < serverPlayerState.serverPlayer.getViewDistance() || + distanceFromPlayer > serverPlayerState.config.getRenderDistance()) return; + + serverPlayerState.channelContext.writeAndFlush(new FullDataPartialUpdateMessage(chunkUpdateData.accessor, this)); + } + } } @Override @@ -235,13 +264,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel future.thenAccept(chunkSizedFullDataAccessor -> { - for (ServerPlayerState serverPlayerState : remotePlayerConnectionHandler.getConnectedPlayers()) - { - if (!serverPlayerState.config.isRealTimeUpdatesEnabled()) continue; - - if (chunk.getChunkPos().distance(new DhChunkPos(serverPlayerState.serverPlayer.getPosition())) <= serverPlayerState.config.getRenderDistance()) - serverPlayerState.channelContext.writeAndFlush(new FullDataPartialUpdateMessage(chunkSizedFullDataAccessor, this)); - } + this.chunkUpdatesToSend.put(chunkSizedFullDataAccessor.chunkPos, new ChunkUpdateData(chunkSizedFullDataAccessor)); }); return future; @@ -329,4 +352,12 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel public final ConcurrentMap requestMessages = new ConcurrentHashMap<>(); public final Semaphore requestCollectionSemaphore = new Semaphore(Short.MAX_VALUE, true); } + + private static class ChunkUpdateData + { + public final ChunkSizedFullDataAccessor accessor; + public final long time = System.currentTimeMillis(); + + private ChunkUpdateData(ChunkSizedFullDataAccessor accessor) { this.accessor = accessor; } + } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java index 3b65332e5..d96380192 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java @@ -32,4 +32,6 @@ public interface IServerPlayerWrapper extends IDhApiUnsafeWrapper IServerLevelWrapper getLevel(); Vec3d getPosition(); + + int getViewDistance(); } From 8bda3351b858fbe377b7368389e61d30a6df0b1f Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Wed, 27 Sep 2023 21:40:59 +0500 Subject: [PATCH 055/205] Make worldGenLoopingQueue lock free --- .../core/level/DhServerLevel.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index d623dd387..790be8a29 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -47,7 +47,6 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.util.LodUtil; -import com.seibel.distanthorizons.core.util.objects.Pair; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; @@ -69,7 +68,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel private final RemotePlayerConnectionHandler remotePlayerConnectionHandler; private final ScopedNetworkEventSource eventSource; - private final LinkedBlockingQueue worldGenLoopingQueue = new LinkedBlockingQueue<>(); + private final ConcurrentLinkedQueue worldGenLoopingQueue = new ConcurrentLinkedQueue<>(); private final ConcurrentMap incompleteDataSources = new ConcurrentHashMap<>(); private final ConcurrentMap fullDataRequests = new ConcurrentHashMap<>(); @@ -186,7 +185,8 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel public void removePlayer(IServerPlayerWrapper serverPlayer) { - boolean ignored = this.worldGenLoopingQueue.remove(serverPlayer); + //noinspection ResultOfMethodCallIgnored + this.worldGenLoopingQueue.remove(serverPlayer); } public void serverTick() @@ -310,14 +310,14 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel if (serverside.worldGenModule.isWorldGenRunning()) { - IServerPlayerWrapper firstPlayer; - synchronized (worldGenLoopingQueue) - { - firstPlayer = this.worldGenLoopingQueue.poll(); - if (firstPlayer == null) - return; - this.worldGenLoopingQueue.add(firstPlayer); - } + IServerPlayerWrapper firstPlayer = this.worldGenLoopingQueue.peek(); + if (firstPlayer == null) + return; + + // Put first player in back before removing from front, so it can be removed by other thread without blocking + // - if it gets removed, remove() below will remove the item we just put instead + this.worldGenLoopingQueue.add(firstPlayer); + this.worldGenLoopingQueue.remove(firstPlayer); Vec3d position = firstPlayer.getPosition(); serverside.worldGenModule.worldGenTick(new DhBlockPos2D((int) position.x, (int) position.z)); From e9e7ac48b198ba01e95a2f509ca2f463243879b6 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Thu, 28 Sep 2023 23:28:18 +0500 Subject: [PATCH 056/205] Fix compilation --- .../core/generation/WorldRemoteGenerationQueue.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 707d11dd6..235c65a9d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -58,8 +58,14 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug DebugRenderer.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showWorldGenQueue); } + @Override - public byte largestDataDetail() + public byte lowestDataDetail() + { + return LodUtil.BLOCK_DETAIL_LEVEL; + } + @Override + public byte highestDataDetail() { return LodUtil.BLOCK_DETAIL_LEVEL; } From e3d7598501242f560c1c9f75e893af799e0a6537 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 14 Oct 2023 22:13:01 +0500 Subject: [PATCH 057/205] Fix compilation --- .../distanthorizons/core/api/internal/ClientApi.java | 8 ++++---- .../fullData/loader/CompleteFullDataSourceLoader.java | 4 ++++ .../fullData/sources/interfaces/IFullDataSource.java | 1 - .../core/file/fullDatafile/FullDataFileHandler.java | 2 +- .../file/fullDatafile/GeneratedFullDataFileHandler.java | 4 +++- .../core/file/fullDatafile/RemoteFullDataFileHandler.java | 8 +++++++- .../file/subDimMatching/SubDimensionLevelMatcher.java | 2 +- .../seibel/distanthorizons/core/level/DhClientLevel.java | 2 +- .../seibel/distanthorizons/core/level/DhServerLevel.java | 1 - .../generation/FullDataSourceResponseMessage.java | 2 +- 10 files changed, 22 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index eb7c95f5d..a123a3d0a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -372,7 +372,7 @@ public class ClientApi // Unload the current world, since it may be wrong. // A followup WorldChanged event should be received from the server soon after this. LOGGER.info("Unloading current client level so the server can define the correct multiverse level."); - this.clientLevelUnloadEvent((IClientLevelWrapper) MC.getWrappedClientWorld()); + this.clientLevelUnloadEvent(MC.getWrappedClientLevel()); }); break; @@ -396,11 +396,11 @@ public class ClientApi LOGGER.info("Server level change event received, changing the level to [" + levelKey + "]."); MC.executeOnRenderThread(() -> { - if (MC.getWrappedClientWorld() != null) + if (MC.getWrappedClientLevel() != null) { - this.clientLevelUnloadEvent((IClientLevelWrapper) MC.getWrappedClientWorld()); + this.clientLevelUnloadEvent(MC.getWrappedClientLevel()); } - IServerKeyedClientLevel clientLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel(MC.getWrappedClientWorld(), levelKey); + IServerKeyedClientLevel clientLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel(MC.getWrappedClientLevel(), levelKey); KEYED_CLIENT_LEVEL_MANAGER.setServerKeyedLevel(clientLevel); this.multiverseClientLevelLoadEvent(clientLevel); }); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/loader/CompleteFullDataSourceLoader.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/loader/CompleteFullDataSourceLoader.java index 1f38b0544..32341f5a0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/loader/CompleteFullDataSourceLoader.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/loader/CompleteFullDataSourceLoader.java @@ -20,8 +20,12 @@ package com.seibel.distanthorizons.core.dataObjects.fullData.loader; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; +import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; +import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; + +import java.io.IOException; public class CompleteFullDataSourceLoader extends AbstractFullDataSourceLoader { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IFullDataSource.java index ca8bd23ce..cbd21b50a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IFullDataSource.java @@ -63,7 +63,6 @@ public interface IFullDataSource /** Returns the detail level of the data contained by this {@link IFullDataSource}. */ byte getDataDetailLevel(); - long getTypeId(); byte getBinaryDataFormatVersion(); EDhApiWorldGenerationStep getWorldGenStep(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java index 3905e220b..55b27156c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java @@ -76,7 +76,7 @@ public class FullDataFileHandler implements IFullDataSourceProvider } return map; } - protected boolean fileExists(DhSectionPos pos) { return this.computeDataFilePath(pos).exists(); } + protected boolean fileExists(DhSectionPos pos) { return this.fullDataRepo.existsWithPrimaryKey(pos.serialize()); } protected final IDhLevel level; protected final File saveDir; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java index 417f0da0f..2f9348b90 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java @@ -36,6 +36,7 @@ import com.seibel.distanthorizons.core.util.LodUtil; import org.apache.logging.log4j.Logger; import javax.annotation.Nullable; +import java.io.File; import java.lang.ref.WeakReference; import java.util.*; import java.util.concurrent.*; @@ -47,7 +48,7 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - private final AtomicReference worldGenQueueRef = new AtomicReference<>(null); + protected final AtomicReference worldGenQueueRef = new AtomicReference<>(null); private final ArrayList onWorldGenTaskCompleteListeners = new ArrayList<>(); @@ -55,6 +56,7 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler private final ConcurrentHashMap generatingDataSourceByPos = new ConcurrentHashMap<>(); public GeneratedFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure) { super(level, saveStructure); } + public GeneratedFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure, @Nullable File saveDirOverride) { super(level, saveStructure, saveDirOverride); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java index f4468b897..bbe04f053 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java @@ -58,10 +58,16 @@ public class RemoteFullDataFileHandler extends GeneratedFullDataFileHandler private final AtomicBoolean isUpdating = new AtomicBoolean(false); private boolean invalidSectionsFound = false; - public RemoteFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure, @Nullable ClientNetworkState networkState) { + public RemoteFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure, @Nullable ClientNetworkState networkState) + { super(level, saveStructure); this.networkState = networkState; } + public RemoteFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure, @Nullable File saveDirOverride, @Nullable ClientNetworkState networkState) + { + super(level, saveStructure, saveDirOverride); + this.networkState = networkState; + } private void sendUpdateChecks() { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java index 276c48dd4..48bfd88b4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java @@ -218,7 +218,7 @@ public class SubDimensionLevelMatcher implements AutoCloseable try { // get the data source to compare against - IDhLevel tempLevel = new DhClientLevel(new ClientOnlySaveStructure(), this.currentClientLevel, testLevelFolder, false); + IDhLevel tempLevel = new DhClientLevel(new ClientOnlySaveStructure(), this.currentClientLevel, testLevelFolder, false, null); IFullDataSource testFullDataSource = tempLevel.getFileHandler().readAsync(new DhSectionPos(this.playerData.playerBlockPos)).join(); if (testFullDataSource == null) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 90730f1a0..4b14d4a2a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -88,7 +88,7 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel { this.levelWrapper = clientLevelWrapper; this.saveStructure = saveStructure; - this.dataFileHandler = new RemoteFullDataFileHandler(this, saveStructure, networkState); + this.dataFileHandler = new RemoteFullDataFileHandler(this, saveStructure, fullDataSaveDirOverride, networkState); this.worldGeneratorEnabledConfig = new AppliedConfigState<>(Config.Client.Advanced.WorldGenerator.enableDistantGeneration); this.networkState = networkState; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 72287d0fe..34278969d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -185,7 +185,6 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel public void removePlayer(IServerPlayerWrapper serverPlayer) { - //noinspection ResultOfMethodCallIgnored this.worldGenLoopingQueue.remove(serverPlayer); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java index e29538f01..bc72d6e77 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java @@ -70,7 +70,7 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage public void decode0(ByteBuf in) { byte dataVersion = in.readByte(); - this.fullDataSourceLoader = (CompleteFullDataSourceLoader) AbstractFullDataSourceLoader.getLoader(CompleteFullDataSource.TYPE_ID, dataVersion); + this.fullDataSourceLoader = (CompleteFullDataSourceLoader) AbstractFullDataSourceLoader.getLoader(CompleteFullDataSource.DATA_SOURCE_TYPE, dataVersion); this.dataBuffer = in.readBytes(in.readInt()); } From df3d20f94e2e692e1715a2d24ec5b458e752649d Mon Sep 17 00:00:00 2001 From: Steveplays28 Date: Fri, 20 Oct 2023 13:49:55 +0200 Subject: [PATCH 058/205] fix: Fix getting LOD render distance radius from the config This config option was renamed recently, to fix LODs not rendering out to the LOD render distance border. Also removed an unused import. --- .../core/multiplayer/config/MultiplayerConfig.java | 2 +- .../multiplayer/config/MultiplayerConfigChangeListener.java | 3 +-- .../core/multiplayer/server/ServersideMultiplayerConfig.java | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java index fae45c402..d93c41cff 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java @@ -5,7 +5,7 @@ import io.netty.buffer.ByteBuf; public class MultiplayerConfig extends AbstractMultiplayerConfig { - public int renderDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistance.get(); + public int renderDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get(); @Override public int getRenderDistance() { return renderDistance; } public int fullDataRequestRateLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.requestRateLimit.get(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java index 286f7f976..251e19d68 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java @@ -2,7 +2,6 @@ package com.seibel.distanthorizons.core.multiplayer.config; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener; -import com.seibel.distanthorizons.core.config.types.ConfigEntry; import java.io.Closeable; @@ -15,7 +14,7 @@ public class MultiplayerConfigChangeListener implements Closeable public MultiplayerConfigChangeListener(Runnable runnable) { - renderDistance = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistance, ignored -> runnable.run()); + renderDistance = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius, ignored -> runnable.run()); rateLimit = new ConfigChangeListener<>(Config.Client.Advanced.Multiplayer.ServerNetworking.requestRateLimit, ignored -> runnable.run()); enableRealTimeUpdates = new ConfigChangeListener<>(Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates, ignored -> runnable.run()); enablePostRelogUpdate = new ConfigChangeListener<>(Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate, ignored -> runnable.run()); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java index 0f6f36c13..e35843e60 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java @@ -15,7 +15,7 @@ public class ServersideMultiplayerConfig extends AbstractMultiplayerConfig @Override public int getRenderDistance() { - return Math.min(clientConfig.renderDistance, Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistance.get()); + return Math.min(clientConfig.renderDistance, Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get()); } @Override From a4df5a8ed888b2c773a6fe26c68b698152a23dcc Mon Sep 17 00:00:00 2001 From: Steveplays28 Date: Fri, 20 Oct 2023 14:03:19 +0200 Subject: [PATCH 059/205] style: Rename `renderDistance` to `renderDistanceRadius` to stay in sync with the main config --- .../distanthorizons/core/level/DhServerLevel.java | 2 +- .../config/AbstractMultiplayerConfig.java | 5 ++--- .../core/multiplayer/config/MultiplayerConfig.java | 8 ++++---- .../config/MultiplayerConfigChangeListener.java | 12 ++++++------ .../server/ServersideMultiplayerConfig.java | 4 ++-- 5 files changed, 15 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index bdc2f1326..bcf2bff5c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -244,7 +244,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel double distanceFromPlayer = chunkUpdateData.accessor.chunkPos.distance(new DhChunkPos(serverPlayerState.serverPlayer.getPosition())); if (distanceFromPlayer < serverPlayerState.serverPlayer.getViewDistance() || - distanceFromPlayer > serverPlayerState.config.getRenderDistance()) return; + distanceFromPlayer > serverPlayerState.config.getRenderDistanceRadius()) return; serverPlayerState.channelContext.writeAndFlush(new FullDataPartialUpdateMessage(chunkUpdateData.accessor, this)); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java index bfcd264b1..b55ffde1d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java @@ -5,7 +5,7 @@ import io.netty.buffer.ByteBuf; public abstract class AbstractMultiplayerConfig implements INetworkObject { - public abstract int getRenderDistance(); + public abstract int getRenderDistanceRadius(); public abstract int getFullDataRequestRateLimit(); public abstract boolean isRealTimeUpdatesEnabled(); public abstract boolean isPostRelogUpdateEnabled(); @@ -13,11 +13,10 @@ public abstract class AbstractMultiplayerConfig implements INetworkObject @Override public void encode(ByteBuf out) { - out.writeInt(this.getRenderDistance()); + out.writeInt(this.getRenderDistanceRadius()); out.writeInt(this.getFullDataRequestRateLimit()); out.writeBoolean(this.isRealTimeUpdatesEnabled()); out.writeBoolean(this.isPostRelogUpdateEnabled()); } - } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java index d93c41cff..3534eb10c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java @@ -5,8 +5,8 @@ import io.netty.buffer.ByteBuf; public class MultiplayerConfig extends AbstractMultiplayerConfig { - public int renderDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get(); - @Override public int getRenderDistance() { return renderDistance; } + public int renderDistanceRadius = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get(); + @Override public int getRenderDistanceRadius() { return renderDistanceRadius; } public int fullDataRequestRateLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.requestRateLimit.get(); @Override public int getFullDataRequestRateLimit() { return fullDataRequestRateLimit; } @@ -20,7 +20,7 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig @Override public void decode(ByteBuf in) { - this.renderDistance = in.readInt(); + this.renderDistanceRadius = in.readInt(); this.fullDataRequestRateLimit = in.readInt(); this.realTimeUpdatesEnabled = in.readBoolean(); this.postRelogUpdateEnabled = in.readBoolean(); @@ -29,7 +29,7 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig @Override public String toString() { return "MultiplayerConfig{" + - "renderDistance=" + renderDistance + + "renderDistance=" + renderDistanceRadius + ", fullDataRequestRateLimit=" + fullDataRequestRateLimit + ", realTimeUpdatesEnabled=" + realTimeUpdatesEnabled + ", postRelogUpdatesEnabled=" + postRelogUpdateEnabled + diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java index 251e19d68..b8a13d7ed 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java @@ -7,15 +7,15 @@ import java.io.Closeable; public class MultiplayerConfigChangeListener implements Closeable { - private final ConfigChangeListener renderDistance; - private final ConfigChangeListener rateLimit; + private final ConfigChangeListener renderDistanceRadius; + private final ConfigChangeListener requestRateLimit; private final ConfigChangeListener enableRealTimeUpdates; private final ConfigChangeListener enablePostRelogUpdate; public MultiplayerConfigChangeListener(Runnable runnable) { - renderDistance = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius, ignored -> runnable.run()); - rateLimit = new ConfigChangeListener<>(Config.Client.Advanced.Multiplayer.ServerNetworking.requestRateLimit, ignored -> runnable.run()); + renderDistanceRadius = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius, ignored -> runnable.run()); + requestRateLimit = new ConfigChangeListener<>(Config.Client.Advanced.Multiplayer.ServerNetworking.requestRateLimit, ignored -> runnable.run()); enableRealTimeUpdates = new ConfigChangeListener<>(Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates, ignored -> runnable.run()); enablePostRelogUpdate = new ConfigChangeListener<>(Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate, ignored -> runnable.run()); } @@ -23,8 +23,8 @@ public class MultiplayerConfigChangeListener implements Closeable @Override public void close() { - renderDistance.close(); - rateLimit.close(); + renderDistanceRadius.close(); + requestRateLimit.close(); enableRealTimeUpdates.close(); enablePostRelogUpdate.close(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java index e35843e60..93187ec72 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java @@ -13,9 +13,9 @@ public class ServersideMultiplayerConfig extends AbstractMultiplayerConfig public MultiplayerConfig clientConfig = new MultiplayerConfig(); @Override - public int getRenderDistance() + public int getRenderDistanceRadius() { - return Math.min(clientConfig.renderDistance, Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get()); + return Math.min(clientConfig.renderDistanceRadius, Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get()); } @Override From 9daf0c73170431c81d76ea5a31a4bb6d298c02dd Mon Sep 17 00:00:00 2001 From: Steveplays28 Date: Fri, 20 Oct 2023 14:07:42 +0200 Subject: [PATCH 060/205] chore: Add missing language entries for the server networking config --- .../main/resources/assets/distanthorizons/lang/en_us.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index 7bde8ae08..91f5bca46 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -331,6 +331,14 @@ "distanthorizons.config.client.advanced.multiplayer": "Multiplayer", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking": "Server Networking", + + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableServerNetworking": "Enable Server Networking", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.requestRateLimit": "Request rate limit", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableRealTimeUpdates": "Enable Real Time Updates", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enablePostRelogUpdates": "Enable Post Relog Updates", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.serverPort": "Server Port", + "distanthorizons.config.client.advanced.multiplayer.serverFolderNameMode": "Server Folder Mode", "distanthorizons.config.client.advanced.multiplayer.serverFolderNameMode.@tooltip": From 9640169be976ef499f26c0f8a6cf1962e34fd7af Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 23 Oct 2023 23:18:52 +0500 Subject: [PATCH 061/205] Make post-relog update independent from generation toggle --- .../RemoteFullDataFileHandler.java | 226 +++++++++++------- .../core/level/DhServerLevel.java | 78 ++++-- .../config/AbstractMultiplayerConfig.java | 2 + .../multiplayer/config/MultiplayerConfig.java | 7 + .../MultiplayerConfigChangeListener.java | 3 + .../server/ServersideMultiplayerConfig.java | 6 + .../FullDataSourceRequestMessage.java | 9 + 7 files changed, 222 insertions(+), 109 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java index bbe04f053..6d5c48750 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java @@ -19,34 +19,45 @@ package com.seibel.distanthorizons.core.file.fullDatafile; +import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.generation.IWorldGenerationQueue; -import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker; +import com.seibel.distanthorizons.core.level.DhLevel; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; import com.seibel.distanthorizons.core.network.exceptions.InvalidSectionPosException; +import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceResponseMessage; import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataChangeSummaryRequestMessage; import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataChangeSummaryResponseMessage; import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; +import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; +import io.netty.channel.ChannelException; import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import javax.annotation.CheckForNull; +import java.awt.*; import java.io.File; +import java.util.ArrayList; import java.util.Map; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; import java.util.stream.Collectors; -public class RemoteFullDataFileHandler extends GeneratedFullDataFileHandler +public class RemoteFullDataFileHandler extends GeneratedFullDataFileHandler implements IDebugRenderable { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); @@ -56,94 +67,16 @@ public class RemoteFullDataFileHandler extends GeneratedFullDataFileHandler private final Set visitedSections = ConcurrentHashMap.newKeySet(); private final ConcurrentMap sectionsToUpdate = new ConcurrentHashMap<>(); private final AtomicBoolean isUpdating = new AtomicBoolean(false); - private boolean invalidSectionsFound = false; - public RemoteFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure, @Nullable ClientNetworkState networkState) - { - super(level, saveStructure); - this.networkState = networkState; - } + private final F3Screen.NestedMessage f3Message = new F3Screen.NestedMessage(this::f3Log); + private final AtomicInteger finishedRequests = new AtomicInteger(); + private final AtomicInteger failedRequests = new AtomicInteger(); + public RemoteFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure, @Nullable File saveDirOverride, @Nullable ClientNetworkState networkState) { super(level, saveStructure, saveDirOverride); this.networkState = networkState; - } - - private void sendUpdateChecks() - { - assert this.networkState != null; - - if (this.invalidSectionsFound) - this.sectionsToUpdate.clear(); - - if (this.sectionsToUpdate.isEmpty()) - return; - - if (this.isUpdating.getAndSet(true)) - return; - - Map block = sectionsToUpdate.entrySet().stream() - .limit(20) - .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().baseMetaData.checksum)); - for (DhSectionPos pos : block.keySet()) - sectionsToUpdate.remove(pos); - - Consumer chunkDataConsumer = (ChunkSizedFullDataAccessor data) -> { - DhSectionPos pos = data.getSectionPos().convertNewToDetailLevel(CompleteFullDataSource.SECTION_SIZE_OFFSET); - this.writeChunkDataToFile(new DhSectionPos(pos.getDetailLevel(), pos.getX(), pos.getZ()), data); - }; - - this.networkState.getClient().sendRequest(new FullDataChangeSummaryRequestMessage(level.getLevelWrapper(), block), FullDataChangeSummaryResponseMessage.class) - .handle((response, throwable) -> - { - try - { - if (throwable != null) - throw throwable; - - IWorldGenerationQueue queue = this.worldGenQueueRef.get(); - if (queue == null) - return null; - - for (DhSectionPos pos : response.changedPosList) - { - queue.submitGenTask(pos, pos.getDetailLevel(), new IWorldGenTaskTracker() { - @Override - public boolean isMemoryAddressValid() - { - return true; - } - - @NotNull - @Override - public Consumer getChunkDataConsumer() - { - return chunkDataConsumer; - } - }); - } - } - catch (InvalidLevelException ignored) - { - // We're too late - } - catch (InvalidSectionPosException e) - { - LOGGER.error("Invalid sections found. Updating will not continue.", e); - invalidSectionsFound = true; - } - catch (Throwable e) - { - LOGGER.error("Error while checking section updates", e); - } - finally - { - this.isUpdating.set(false); - sendUpdateChecks(); - } - - return null; - }); + DebugRenderer.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showWorldGenQueue); } @Override @@ -163,11 +96,130 @@ public class RemoteFullDataFileHandler extends GeneratedFullDataFileHandler pos.forEachChildAtLevel(DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL, childPos -> { FullDataMetaFile childMetaFile = super.getFileIfExist(childPos); - if (childMetaFile != null && visitedSections.add(childPos)) + if (childMetaFile != null && childMetaFile.baseMetaData != null && visitedSections.add(childPos)) sectionsToUpdate.put(childPos, childMetaFile); }); sendUpdateChecks(); return metaFile; } + + private void sendUpdateChecks() + { + assert this.networkState != null; + + if (this.sectionsToUpdate.isEmpty()) + return; + + if (this.isUpdating.getAndSet(true)) + return; + + Map block = sectionsToUpdate.entrySet().stream() + .limit(20) + .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().baseMetaData.checksum)); + for (DhSectionPos pos : block.keySet()) + sectionsToUpdate.remove(pos); + + this.networkState.getClient().sendRequest(new FullDataChangeSummaryRequestMessage(level.getLevelWrapper(), block), FullDataChangeSummaryResponseMessage.class) + .handle((response, throwable) -> + { + try + { + if (throwable != null) + throw throwable; + + IWorldGenerationQueue queue = this.worldGenQueueRef.get(); + if (queue == null) + return null; + + for (DhSectionPos pos : response.changedPosList) + sendUpdateRequest(pos); + } + catch (InvalidLevelException ignored) + { + // We're too late + } + catch (InvalidSectionPosException ignored) + { + } + catch (Throwable e) + { + LOGGER.error("Error while checking section updates", e); + } + finally + { + this.isUpdating.set(false); + sendUpdateChecks(); + } + + return null; + }); + } + + public void sendUpdateRequest(DhSectionPos sectionPos) + { + assert this.networkState != null; + + this.networkState.getClient().sendRequest(new FullDataSourceRequestMessage(level.getLevelWrapper(), sectionPos, true), FullDataSourceResponseMessage.class) + .handleAsync((response, throwable) -> + { + try + { + if (throwable != null) + throw throwable; + + CompleteFullDataSource fullDataSource = response.getFullDataSource(sectionPos, level); + + fullDataSource.splitIntoChunkSizedAccessors(((DhLevel) level)::saveWrites); + response.getFullDataSourceLoader().returnPooledDataSource(fullDataSource); + } + catch (InvalidLevelException ignored) + { + // We're too late + } + catch (ChannelException | RateLimitedException ignored) + { + // Can't bother retrying + this.failedRequests.incrementAndGet(); + } + catch (Throwable e) + { + LOGGER.error("Error while fetching full data source", e); + this.failedRequests.incrementAndGet(); + } + + return null; + }); + } + + private String[] f3Log() + { + if (this.networkState == null || !this.networkState.config.postRelogUpdateEnabled) + return new String[0]; + + // These metrics are not precise; Updated sections[2] is within range of 1 rate limit or so + ArrayList lines = new ArrayList<>(); + lines.add("Post-relog update ["+level.getLevelWrapper().getDimensionType().getDimensionName()+"]"); + lines.add("Visited sections: "+visitedSections.size()); + lines.add("Updated sections: "+this.finishedRequests+" / "+(this.sectionsToUpdate.size() + this.finishedRequests.get())+" (failed: "+this.failedRequests+")"); + return lines.toArray(new String[0]); + } + + @Override + public void debugRender(DebugRenderer r) + { + for (Map.Entry mapEntry : sectionsToUpdate.entrySet()) + { + r.renderBox(new DebugRenderer.Box(mapEntry.getKey(), -32f, 64f, 0.05f, Color.pink)); + } + } + + @Override + public void close() + { + f3Message.close(); + DebugRenderer.unregister(this, Config.Client.Advanced.Debugging.DebugWireframe.showWorldGenQueue); + super.close(); + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index bcf2bff5c..b19d2fb09 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -97,29 +97,63 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel { this.eventSource.registerHandler(FullDataSourceRequestMessage.class, remotePlayerConnectionHandler.currentLevelOnly(this, (msg, serverPlayerState) -> { - if (serverPlayerState.pendingFullDataRequests.incrementAndGet() > serverPlayerState.config.getFullDataRequestRateLimit()) + if (msg.changedOnly) { - serverPlayerState.pendingFullDataRequests.decrementAndGet(); - msg.sendResponse(new RateLimitedException("Max concurrent requests: " + serverPlayerState.config.getFullDataRequestRateLimit())); - return; - } - - while (true) - { - IncompleteDataSourceEntry entry = incompleteDataSources.computeIfAbsent(msg.dhSectionPos, pos -> { - IncompleteDataSourceEntry newEntry = new IncompleteDataSourceEntry(); - serverside.dataFileHandler.readAsync(msg.dhSectionPos).thenAccept(fullDataSource -> { - newEntry.fullDataSource = fullDataSource; - }); - return newEntry; - }); - // If this fails, current entry is being drained and need to create another one - if (entry.requestCollectionSemaphore.tryAcquire()) + if (!serverPlayerState.config.isPostRelogUpdateEnabled()) { - fullDataRequests.put(msg.futureId, entry); - entry.requestMessages.put(msg.futureId, msg); - entry.requestCollectionSemaphore.release(); - break; + msg.sendResponse(new RequestRejectedException("Operation is disabled from config.")); + return; + } + + FullDataMetaFile metaFile = serverside.dataFileHandler.getFileIfExist(msg.dhSectionPos); + if (metaFile == null) + { + msg.sendResponse(new InvalidSectionPosException("Not generated section pos: "+msg.dhSectionPos)); + return; + } + + metaFile.getOrLoadCachedDataSourceAsync().thenAccept(source -> { + if (!(source instanceof CompleteFullDataSource)) + { + msg.sendResponse(new InvalidSectionPosException("Not generated section pos: "+msg.dhSectionPos)); + return; + } + + msg.sendResponse(new FullDataSourceResponseMessage((CompleteFullDataSource) source, this)); + }); + } + else + { + if (!serverPlayerState.config.isDistantGenerationEnabled()) + { + msg.sendResponse(new RequestRejectedException("Operation is disabled from config.")); + return; + } + + if (serverPlayerState.pendingFullDataRequests.incrementAndGet() > serverPlayerState.config.getFullDataRequestRateLimit()) + { + serverPlayerState.pendingFullDataRequests.decrementAndGet(); + msg.sendResponse(new RateLimitedException("Max concurrent requests: " + serverPlayerState.config.getFullDataRequestRateLimit())); + return; + } + + while (true) + { + IncompleteDataSourceEntry entry = incompleteDataSources.computeIfAbsent(msg.dhSectionPos, pos -> { + IncompleteDataSourceEntry newEntry = new IncompleteDataSourceEntry(); + serverside.dataFileHandler.readAsync(msg.dhSectionPos).thenAccept(fullDataSource -> { + newEntry.fullDataSource = fullDataSource; + }); + return newEntry; + }); + // If this fails, current entry is being drained and need to create another one + if (entry.requestCollectionSemaphore.tryAcquire()) + { + fullDataRequests.put(msg.futureId, entry); + entry.requestMessages.put(msg.futureId, msg); + entry.requestCollectionSemaphore.release(); + break; + } } } })); @@ -133,7 +167,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel this.eventSource.registerHandler(FullDataChangeSummaryRequestMessage.class, remotePlayerConnectionHandler.currentLevelOnly(this, (msg, serverPlayerState) -> { - if (!Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate.get()) + if (!serverPlayerState.config.isPostRelogUpdateEnabled()) { msg.sendResponse(new RequestRejectedException("Operation is disabled from config.")); return; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java index b55ffde1d..e686ad0ad 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java @@ -6,6 +6,7 @@ import io.netty.buffer.ByteBuf; public abstract class AbstractMultiplayerConfig implements INetworkObject { public abstract int getRenderDistanceRadius(); + public abstract boolean isDistantGenerationEnabled(); public abstract int getFullDataRequestRateLimit(); public abstract boolean isRealTimeUpdatesEnabled(); public abstract boolean isPostRelogUpdateEnabled(); @@ -14,6 +15,7 @@ public abstract class AbstractMultiplayerConfig implements INetworkObject public void encode(ByteBuf out) { out.writeInt(this.getRenderDistanceRadius()); + out.writeBoolean(this.isDistantGenerationEnabled()); out.writeInt(this.getFullDataRequestRateLimit()); out.writeBoolean(this.isRealTimeUpdatesEnabled()); out.writeBoolean(this.isPostRelogUpdateEnabled()); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java index 3534eb10c..406f892c6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java @@ -5,9 +5,14 @@ import io.netty.buffer.ByteBuf; public class MultiplayerConfig extends AbstractMultiplayerConfig { + // IMPORTANT: Once you added/removed config fields, modify MultiplayerConfigChangeListener accordingly. + public int renderDistanceRadius = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get(); @Override public int getRenderDistanceRadius() { return renderDistanceRadius; } + public boolean distantGenerationEnabled = Config.Client.Advanced.WorldGenerator.enableDistantGeneration.get(); + @Override public boolean isDistantGenerationEnabled() { return distantGenerationEnabled; } + public int fullDataRequestRateLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.requestRateLimit.get(); @Override public int getFullDataRequestRateLimit() { return fullDataRequestRateLimit; } @@ -21,6 +26,7 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig public void decode(ByteBuf in) { this.renderDistanceRadius = in.readInt(); + this.distantGenerationEnabled = in.readBoolean(); this.fullDataRequestRateLimit = in.readInt(); this.realTimeUpdatesEnabled = in.readBoolean(); this.postRelogUpdateEnabled = in.readBoolean(); @@ -30,6 +36,7 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig { return "MultiplayerConfig{" + "renderDistance=" + renderDistanceRadius + + ", distantGenerationEnabled=" + distantGenerationEnabled + ", fullDataRequestRateLimit=" + fullDataRequestRateLimit + ", realTimeUpdatesEnabled=" + realTimeUpdatesEnabled + ", postRelogUpdatesEnabled=" + postRelogUpdateEnabled + diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java index b8a13d7ed..4a5c4804f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java @@ -8,6 +8,7 @@ import java.io.Closeable; public class MultiplayerConfigChangeListener implements Closeable { private final ConfigChangeListener renderDistanceRadius; + private final ConfigChangeListener enableDistantGeneration; private final ConfigChangeListener requestRateLimit; private final ConfigChangeListener enableRealTimeUpdates; private final ConfigChangeListener enablePostRelogUpdate; @@ -15,6 +16,7 @@ public class MultiplayerConfigChangeListener implements Closeable public MultiplayerConfigChangeListener(Runnable runnable) { renderDistanceRadius = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius, ignored -> runnable.run()); + enableDistantGeneration = new ConfigChangeListener<>(Config.Client.Advanced.WorldGenerator.enableDistantGeneration, ignored -> runnable.run()); requestRateLimit = new ConfigChangeListener<>(Config.Client.Advanced.Multiplayer.ServerNetworking.requestRateLimit, ignored -> runnable.run()); enableRealTimeUpdates = new ConfigChangeListener<>(Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates, ignored -> runnable.run()); enablePostRelogUpdate = new ConfigChangeListener<>(Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate, ignored -> runnable.run()); @@ -24,6 +26,7 @@ public class MultiplayerConfigChangeListener implements Closeable public void close() { renderDistanceRadius.close(); + enableDistantGeneration.close(); requestRateLimit.close(); enableRealTimeUpdates.close(); enablePostRelogUpdate.close(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java index 93187ec72..217eab119 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java @@ -18,6 +18,12 @@ public class ServersideMultiplayerConfig extends AbstractMultiplayerConfig return Math.min(clientConfig.renderDistanceRadius, Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get()); } + @Override + public boolean isDistantGenerationEnabled() + { + return clientConfig.distantGenerationEnabled && Config.Client.Advanced.WorldGenerator.enableDistantGeneration.get(); + } + @Override public int getFullDataRequestRateLimit() { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceRequestMessage.java index 315981a5b..b253f151a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceRequestMessage.java @@ -31,6 +31,7 @@ public class FullDataSourceRequestMessage extends FutureTrackableNetworkMessage public DhSectionPos dhSectionPos; private int levelHashCode; @Override public int getLevelHashCode() { return levelHashCode; } + public boolean changedOnly; public FullDataSourceRequestMessage() {} @@ -40,12 +41,19 @@ public class FullDataSourceRequestMessage extends FutureTrackableNetworkMessage this.levelHashCode = levelWrapper.getDimensionType().getDimensionName().hashCode(); this.dhSectionPos = dhSectionPos; } + + public FullDataSourceRequestMessage(ILevelWrapper levelWrapper, DhSectionPos dhSectionPos, boolean changedOnly) + { + this(levelWrapper, dhSectionPos); + this.changedOnly = true; + } @Override public void encode0(ByteBuf out) { out.writeInt(levelHashCode); dhSectionPos.encode(out); + out.writeBoolean(changedOnly); } @Override @@ -53,6 +61,7 @@ public class FullDataSourceRequestMessage extends FutureTrackableNetworkMessage { levelHashCode = in.readInt(); dhSectionPos = INetworkObject.decodeStatic(DhSectionPos.zero(), in); + changedOnly = in.readBoolean(); } @Override From 84d36df3888cbe3e66e099859010bfcfe7c2250e Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 4 Nov 2023 16:56:15 +0500 Subject: [PATCH 062/205] Do not clean up rendering on a dedicated server --- .../distanthorizons/core/api/internal/SharedApi.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java index 9691c82a4..420eef015 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java @@ -79,6 +79,7 @@ public class SharedApi public static void setDhWorld(AbstractDhWorld newWorld) { + AbstractDhWorld prevWorld = currentWorld; currentWorld = newWorld; // starting and stopping the DataRenderTransformer is necessary to prevent attempting to @@ -90,8 +91,12 @@ public class SharedApi else { ThreadPools.shutdownThreadPools(); - DebugRenderer.clearRenderables(); - MC_RENDER.clearTargetFrameBuffer(); + + if (prevWorld != null && prevWorld.environment != EWorldEnvironment.Server_Only) + { + DebugRenderer.clearRenderables(); + MC_RENDER.clearTargetFrameBuffer(); + } // recommend that the garbage collector cleans up any objects from the old world and thread pools System.gc(); From d2f5c0223861b8a44acf636fe8da8942dee40fab Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 12 Nov 2023 17:17:15 +0500 Subject: [PATCH 063/205] Bandaid fix to prevent duplicate section requests --- .../core/generation/WorldRemoteGenerationQueue.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 235c65a9d..e06204bfc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -51,6 +51,8 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug private final AtomicInteger finishedRequests = new AtomicInteger(); private final AtomicInteger failedRequests = new AtomicInteger(); + private final Set alreadyGeneratedPosHashSet = ConcurrentHashMap.newKeySet(); + public WorldRemoteGenerationQueue(ClientNetworkState networkState, IDhClientLevel level) { this.networkState = networkState; @@ -75,6 +77,15 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug { LodUtil.assertTrue(sectionPos.getDetailLevel() == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL, "Only highest-detail sections are allowed."); + // check if this is a duplicate generation task + if (this.alreadyGeneratedPosHashSet.contains(sectionPos)) + { + // temporary solution to prevent generating the same section multiple times + LOGGER.trace("Duplicate generation section " + sectionPos + ". Skipping..."); + return CompletableFuture.completedFuture(WorldGenResult.CreateFail()); + } + this.alreadyGeneratedPosHashSet.add(sectionPos); + WorldGenQueueEntry entry = new WorldGenQueueEntry(new CompletableFuture<>(), tracker); waitingTasks.put(sectionPos, entry); return entry.future; @@ -149,6 +160,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug entry.future.cancel(false); if (entry.request != null) entry.request.cancel(false); + alreadyGeneratedPosHashSet.remove(pos); } } } From b791a185a249f32b6340290355d1413898a0c38a Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 20 Nov 2023 16:54:17 +0500 Subject: [PATCH 064/205] Deduplicate code in MultiplayerConfigChangeListener --- .../MultiplayerConfigChangeListener.java | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java index 4a5c4804f..f9e096a6b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java @@ -1,35 +1,37 @@ package com.seibel.distanthorizons.core.multiplayer.config; import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.config.types.ConfigEntry; import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener; import java.io.Closeable; +import java.util.ArrayList; +@SuppressWarnings({"rawtypes", "unchecked"}) public class MultiplayerConfigChangeListener implements Closeable { - private final ConfigChangeListener renderDistanceRadius; - private final ConfigChangeListener enableDistantGeneration; - private final ConfigChangeListener requestRateLimit; - private final ConfigChangeListener enableRealTimeUpdates; - private final ConfigChangeListener enablePostRelogUpdate; + private static final ConfigEntry[] CONFIG_ENTRIES = new ConfigEntry[] { + Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius, + Config.Client.Advanced.WorldGenerator.enableDistantGeneration, + Config.Client.Advanced.Multiplayer.ServerNetworking.requestRateLimit, + Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates, + Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate + }; + + private final ArrayList changeListeners = new ArrayList<>(); public MultiplayerConfigChangeListener(Runnable runnable) { - renderDistanceRadius = new ConfigChangeListener<>(Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius, ignored -> runnable.run()); - enableDistantGeneration = new ConfigChangeListener<>(Config.Client.Advanced.WorldGenerator.enableDistantGeneration, ignored -> runnable.run()); - requestRateLimit = new ConfigChangeListener<>(Config.Client.Advanced.Multiplayer.ServerNetworking.requestRateLimit, ignored -> runnable.run()); - enableRealTimeUpdates = new ConfigChangeListener<>(Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates, ignored -> runnable.run()); - enablePostRelogUpdate = new ConfigChangeListener<>(Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate, ignored -> runnable.run()); + for (ConfigEntry entry : CONFIG_ENTRIES) + changeListeners.add(new ConfigChangeListener(entry, ignored -> runnable.run())); } @Override public void close() { - renderDistanceRadius.close(); - enableDistantGeneration.close(); - requestRateLimit.close(); - enableRealTimeUpdates.close(); - enablePostRelogUpdate.close(); + for (ConfigChangeListener changeListener : changeListeners) + changeListener.close(); + changeListeners.clear(); } } From 77bd333fffe32fdc106e4d53388bc92eed8334ab Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 20 Nov 2023 17:18:05 +0500 Subject: [PATCH 065/205] Fix RejectedExecutionException when leaving server --- .../core/network/NetworkClient.java | 45 +++++++------------ 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index 986da58eb..5891b24bd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -48,12 +48,11 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable INITIAL, OPEN, RECONNECT, - RECONNECT_FORCE, - CLOSE_WAIT, + CLOSING, CLOSED } private static final Set closedStates = EnumSet.of( - EConnectionState.CLOSE_WAIT, + EConnectionState.CLOSING, EConnectionState.CLOSED ); @@ -95,13 +94,13 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable this.registerHandler(CloseReasonMessage.class, closeReasonMessage -> { LOGGER.info(closeReasonMessage.reason); - this.connectionState = EConnectionState.CLOSE_WAIT; + this.connectionState = EConnectionState.CLOSING; }); this.registerHandler(CloseEvent.class, closeEvent -> { LOGGER.info("Disconnected from server: "+ closeEvent.getChannelContext().channel().remoteAddress()); - if (this.connectionState == EConnectionState.CLOSE_WAIT) + if (this.connectionState == EConnectionState.CLOSING) { this.close(); } @@ -143,30 +142,20 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable ? channelFuture.cause() : new ChannelException("Channel is closed.")); - switch (this.connectionState) + if (this.connectionState != EConnectionState.OPEN) + return; + + this.reconnectAttempts--; + LOGGER.info("Reconnection attempts left: [" + this.reconnectAttempts + "] of [" + FAILURE_RECONNECT_ATTEMPTS + "]."); + if (this.reconnectAttempts == 0) { - case OPEN: - this.reconnectAttempts--; - LOGGER.info("Reconnection attempts left: ["+this.reconnectAttempts+"] of ["+FAILURE_RECONNECT_ATTEMPTS+"]."); - if (this.reconnectAttempts == 0) - { - this.connectionState = EConnectionState.CLOSE_WAIT; - return; - } - - this.connectionState = EConnectionState.RECONNECT; - this.workerGroup.schedule(this::connect, FAILURE_RECONNECT_DELAY_SEC, TimeUnit.SECONDS); - break; - - case RECONNECT_FORCE: - LOGGER.info("Reconnecting forcefully."); - this.reconnectAttempts = FAILURE_RECONNECT_ATTEMPTS; - - this.connectionState = EConnectionState.RECONNECT; - this.workerGroup.schedule(this::connect, 0, TimeUnit.SECONDS); - break; + this.connectionState = EConnectionState.CLOSING; + return; } - }); + + this.connectionState = EConnectionState.RECONNECT; + this.workerGroup.schedule(this::connect, FAILURE_RECONNECT_DELAY_SEC, TimeUnit.SECONDS); + }); } public final void sendMessage(NetworkMessage msg) @@ -188,8 +177,8 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable } this.connectionState = EConnectionState.CLOSED; - this.workerGroup.shutdownGracefully().syncUninterruptibly(); this.channel.close().syncUninterruptibly(); + this.workerGroup.shutdownGracefully().syncUninterruptibly(); super.close(); } From e98cf1f2b5cf924bb9268cec7d1df12282256632 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Thu, 23 Nov 2023 20:58:09 +0500 Subject: [PATCH 066/205] Hide all unrelated config entries --- .../distanthorizons/core/config/Config.java | 10 ++++++++++ .../core/config/file/ConfigFileHandling.java | 5 +++++ .../core/config/types/ConfigEntry.java | 15 +++++++++++++-- .../distanthorizons/core/world/DhClientWorld.java | 2 +- .../distanthorizons/core/world/DhServerWorld.java | 4 ++-- 5 files changed, 31 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index e6eea5f1c..ef7e0a69e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -155,6 +155,7 @@ public class Config .build(); public static ConfigEntry lodChunkRenderDistanceRadius = new ConfigEntry.Builder() + .enableOnServer() .setMinDefaultMax(32, 128, 4096) .comment("The radius of the mod's render distance. (measured in chunks)") .setPerformance(EConfigEntryPerformance.HIGH) @@ -660,6 +661,7 @@ public class Config public static class WorldGenerator { public static ConfigEntry enableDistantGeneration = new ConfigEntry.Builder() + .enableOnServer() .set(true) .comment("" + " Should Distant Horizons slowly generate LODs \n" @@ -670,6 +672,7 @@ public class Config .build(); public static ConfigEntry distantGeneratorMode = new ConfigEntry.Builder() + .enableOnServer() .set(EDhApiDistantGeneratorMode.FEATURES) .comment("" + "How detailed should LODs be generated outside the vanilla render distance? \n" @@ -711,6 +714,7 @@ public class Config .build(); public static ConfigEntry worldGenerationTimeoutLengthInSeconds = new ConfigEntry.Builder() + .enableOnServer() .setMinDefaultMax(5, 60, 60 * 10/*10 minutes*/ ) .comment("" + "How long should a world generator thread run for before timing out? \n" @@ -754,6 +758,7 @@ public class Config public static class LodBuilding { public static ConfigEntry minTimeBetweenChunkUpdatesInSeconds = new ConfigEntry.Builder() + .enableOnServer() .setMinDefaultMax(0, 1, 60) .comment("" + "Determines how long must pass between LOD chunk updates before another. \n" @@ -819,6 +824,7 @@ public class Config public static class ServerNetworking { public static ConfigEntry enableServerNetworking = new ConfigEntry.Builder() + .enableOnServer() .set(true) .comment("" + "Attention: \n" @@ -833,6 +839,7 @@ public class Config .build(); public static ConfigEntry requestRateLimit = new ConfigEntry.Builder() + .enableOnServer() .setMinDefaultMax(1, 20, 100) .comment("" + "Limits the amount of sent/processed LOD requests concurrently. \n" @@ -842,6 +849,7 @@ public class Config .build(); public static ConfigEntry enableRealTimeUpdates = new ConfigEntry.Builder() + .enableOnServer() .set(false) .comment("" + "Enables real time updates from server." @@ -849,6 +857,7 @@ public class Config .build(); public static ConfigEntry enablePostRelogUpdate = new ConfigEntry.Builder() + .enableOnServer() .set(false) .comment("" + "Enables updating of LODs after relog." @@ -856,6 +865,7 @@ public class Config .build(); public static ConfigEntry serverPort = new ConfigEntry.Builder() + .enableOnServer() .set(25049) .comment("" + "The port on the server that's used for sending LOD data." diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigFileHandling.java b/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigFileHandling.java index b63d5cae1..a465908ff 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigFileHandling.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigFileHandling.java @@ -192,6 +192,8 @@ public class ConfigFileHandling public void saveEntry(ConfigEntry entry, CommentedFileConfig workConfig) { if (!entry.getAppearance().showInFile) return; + if (SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class).isDedicatedServer() && !entry.isEnabledOnServer()) + return; if (entry.getTrueValue() == null) throw new IllegalArgumentException("Entry [" + entry.getNameWCategory() + "] is null, this may be a problem with [" + configBase.modName + "]. Please contact the authors"); @@ -254,6 +256,9 @@ public class ConfigFileHandling ) return; + if (SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class).isDedicatedServer() && !entry.isEnabledOnServer()) + return; + nightConfig.setComment(entry.getNameWCategory(), " " + entry.getComment().replaceAll("\n", "\n ") + "\n "); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java index 12bb99e36..b15e5a14e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java @@ -44,6 +44,7 @@ public class ConfigEntry extends AbstractConfigType> implem private T min; private T max; private final ArrayList listenerList; + private final boolean enabledOnServer; // API control // /** @@ -57,13 +58,14 @@ public class ConfigEntry extends AbstractConfigType> implem /** Creates the entry */ - private ConfigEntry(EConfigEntryAppearance appearance, T value, String comment, T min, T max, boolean allowApiOverride, EConfigEntryPerformance performance, ArrayList listenerList) + private ConfigEntry(EConfigEntryAppearance appearance, T value, String comment, T min, T max, boolean enabledOnServer, boolean allowApiOverride, EConfigEntryPerformance performance, ArrayList listenerList) { super(appearance, value); this.comment = comment; this.min = min; this.max = max; + this.enabledOnServer = enabledOnServer; this.allowApiOverride = allowApiOverride; this.performance = performance; this.listenerList = listenerList; @@ -179,6 +181,8 @@ public class ConfigEntry extends AbstractConfigType> implem if (validness == 1) this.value = (T) NumberUtil.getMaximum(this.value.getClass()); } + public boolean isEnabledOnServer() { return this.enabledOnServer; } + @Override public String getComment() { return this.comment; } @Override @@ -297,6 +301,7 @@ public class ConfigEntry extends AbstractConfigType> implem private String tmpComment = null; private T tmpMin = null; private T tmpMax = null; + protected boolean tmpEnabledOnServer = false; private boolean tmpUseApiOverwrite = true; private EConfigEntryPerformance tmpPerformance = EConfigEntryPerformance.DONT_SHOW; protected ArrayList tmpIConfigListener = new ArrayList<>(); @@ -334,6 +339,12 @@ public class ConfigEntry extends AbstractConfigType> implem return this; } + public Builder enableOnServer() + { + this.tmpEnabledOnServer = true; + return this; + } + public Builder setUseApiOverwrite(boolean newUseApiOverwrite) { this.tmpUseApiOverwrite = newUseApiOverwrite; @@ -376,7 +387,7 @@ public class ConfigEntry extends AbstractConfigType> implem public ConfigEntry build() { - return new ConfigEntry<>(this.tmpAppearance, this.tmpValue, this.tmpComment, this.tmpMin, this.tmpMax, this.tmpUseApiOverwrite, this.tmpPerformance, this.tmpIConfigListener); + return new ConfigEntry<>(this.tmpAppearance, this.tmpValue, this.tmpComment, this.tmpMin, this.tmpMax, this.tmpEnabledOnServer, this.tmpUseApiOverwrite, this.tmpPerformance, this.tmpIConfigListener); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java index 9a4261939..de242b2e0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java @@ -65,7 +65,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld if (Config.Client.Advanced.Multiplayer.ServerNetworking.enableServerNetworking.get()) { - // TODO server specific configs + // TODO per server configs NetworkClient networkClient = new NetworkClient(MC_CLIENT.getCurrentServerIp().split(":")[0], Config.Client.Advanced.Multiplayer.ServerNetworking.serverPort.get()); this.networkState = new ClientNetworkState(networkClient, MC_CLIENT.getPlayerUUID()); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java index ae0ae691a..b7134364f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.world; +import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.file.structure.LocalSaveStructure; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.level.IDhLevel; @@ -51,8 +52,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld this.saveStructure = new LocalSaveStructure(); this.levels = new HashMap<>(); - // TODO move to global payload once server specific configs are implemented - NetworkServer networkServer = new NetworkServer(25049); + NetworkServer networkServer = new NetworkServer(Config.Client.Advanced.Multiplayer.ServerNetworking.serverPort.get()); this.remotePlayerConnectionHandler = new RemotePlayerConnectionHandler(networkServer); LOGGER.info("Started "+DhServerWorld.class.getSimpleName()+" of type "+this.environment); From 82d66ca392b8e7df30123d31e2e410e63a4897b2 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 25 Nov 2023 17:44:51 +0500 Subject: [PATCH 067/205] Basic chat commands --- .../distanthorizons/core/config/Config.java | 22 +++++++++---------- .../core/config/file/ConfigFileHandling.java | 4 ++-- .../core/config/types/ConfigEntry.java | 16 +++++++------- 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index ef7e0a69e..15d254785 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -155,7 +155,7 @@ public class Config .build(); public static ConfigEntry lodChunkRenderDistanceRadius = new ConfigEntry.Builder() - .enableOnServer() + .setServersideShortName("renderDistanceRadius") .setMinDefaultMax(32, 128, 4096) .comment("The radius of the mod's render distance. (measured in chunks)") .setPerformance(EConfigEntryPerformance.HIGH) @@ -661,7 +661,7 @@ public class Config public static class WorldGenerator { public static ConfigEntry enableDistantGeneration = new ConfigEntry.Builder() - .enableOnServer() + .setServersideShortName("enableDistantGeneration") .set(true) .comment("" + " Should Distant Horizons slowly generate LODs \n" @@ -672,7 +672,7 @@ public class Config .build(); public static ConfigEntry distantGeneratorMode = new ConfigEntry.Builder() - .enableOnServer() + .setServersideShortName("distantGeneratorMode") .set(EDhApiDistantGeneratorMode.FEATURES) .comment("" + "How detailed should LODs be generated outside the vanilla render distance? \n" @@ -714,7 +714,7 @@ public class Config .build(); public static ConfigEntry worldGenerationTimeoutLengthInSeconds = new ConfigEntry.Builder() - .enableOnServer() + .setServersideShortName("worldGenerationTimeout") .setMinDefaultMax(5, 60, 60 * 10/*10 minutes*/ ) .comment("" + "How long should a world generator thread run for before timing out? \n" @@ -758,7 +758,7 @@ public class Config public static class LodBuilding { public static ConfigEntry minTimeBetweenChunkUpdatesInSeconds = new ConfigEntry.Builder() - .enableOnServer() + .setServersideShortName("minTimeBetweenChunkUpdates") .setMinDefaultMax(0, 1, 60) .comment("" + "Determines how long must pass between LOD chunk updates before another. \n" @@ -824,7 +824,7 @@ public class Config public static class ServerNetworking { public static ConfigEntry enableServerNetworking = new ConfigEntry.Builder() - .enableOnServer() + .setServersideShortName("enableServerNetworking") .set(true) .comment("" + "Attention: \n" @@ -839,7 +839,7 @@ public class Config .build(); public static ConfigEntry requestRateLimit = new ConfigEntry.Builder() - .enableOnServer() + .setServersideShortName("requestRateLimit") .setMinDefaultMax(1, 20, 100) .comment("" + "Limits the amount of sent/processed LOD requests concurrently. \n" @@ -849,7 +849,7 @@ public class Config .build(); public static ConfigEntry enableRealTimeUpdates = new ConfigEntry.Builder() - .enableOnServer() + .setServersideShortName("enableRealTimeUpdates") .set(false) .comment("" + "Enables real time updates from server." @@ -857,7 +857,7 @@ public class Config .build(); public static ConfigEntry enablePostRelogUpdate = new ConfigEntry.Builder() - .enableOnServer() + .setServersideShortName("enablePostRelogUpdate") .set(false) .comment("" + "Enables updating of LODs after relog." @@ -865,8 +865,8 @@ public class Config .build(); public static ConfigEntry serverPort = new ConfigEntry.Builder() - .enableOnServer() - .set(25049) + .setServersideShortName("serverPort") + .setMinDefaultMax(1, 25049, 65535) .comment("" + "The port on the server that's used for sending LOD data." + "") diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigFileHandling.java b/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigFileHandling.java index a465908ff..8459ce9ef 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigFileHandling.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigFileHandling.java @@ -192,7 +192,7 @@ public class ConfigFileHandling public void saveEntry(ConfigEntry entry, CommentedFileConfig workConfig) { if (!entry.getAppearance().showInFile) return; - if (SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class).isDedicatedServer() && !entry.isEnabledOnServer()) + if (SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class).isDedicatedServer() && entry.getServersideShortName() == null) return; if (entry.getTrueValue() == null) throw new IllegalArgumentException("Entry [" + entry.getNameWCategory() + "] is null, this may be a problem with [" + configBase.modName + "]. Please contact the authors"); @@ -256,7 +256,7 @@ public class ConfigFileHandling ) return; - if (SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class).isDedicatedServer() && !entry.isEnabledOnServer()) + if (SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class).isDedicatedServer() && entry.getServersideShortName() == null) return; nightConfig.setComment(entry.getNameWCategory(), " " + entry.getComment().replaceAll("\n", "\n ") + "\n "); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java index b15e5a14e..5a877f9f7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java @@ -44,7 +44,7 @@ public class ConfigEntry extends AbstractConfigType> implem private T min; private T max; private final ArrayList listenerList; - private final boolean enabledOnServer; + private final String serversideShortName; // API control // /** @@ -58,14 +58,14 @@ public class ConfigEntry extends AbstractConfigType> implem /** Creates the entry */ - private ConfigEntry(EConfigEntryAppearance appearance, T value, String comment, T min, T max, boolean enabledOnServer, boolean allowApiOverride, EConfigEntryPerformance performance, ArrayList listenerList) + private ConfigEntry(EConfigEntryAppearance appearance, T value, String comment, T min, T max, String serversideShortName, boolean allowApiOverride, EConfigEntryPerformance performance, ArrayList listenerList) { super(appearance, value); this.comment = comment; this.min = min; this.max = max; - this.enabledOnServer = enabledOnServer; + this.serversideShortName = serversideShortName; this.allowApiOverride = allowApiOverride; this.performance = performance; this.listenerList = listenerList; @@ -181,7 +181,7 @@ public class ConfigEntry extends AbstractConfigType> implem if (validness == 1) this.value = (T) NumberUtil.getMaximum(this.value.getClass()); } - public boolean isEnabledOnServer() { return this.enabledOnServer; } + public String getServersideShortName() { return this.serversideShortName; } @Override public String getComment() { return this.comment; } @@ -301,7 +301,7 @@ public class ConfigEntry extends AbstractConfigType> implem private String tmpComment = null; private T tmpMin = null; private T tmpMax = null; - protected boolean tmpEnabledOnServer = false; + protected String tmpServersideShortName = null; private boolean tmpUseApiOverwrite = true; private EConfigEntryPerformance tmpPerformance = EConfigEntryPerformance.DONT_SHOW; protected ArrayList tmpIConfigListener = new ArrayList<>(); @@ -339,9 +339,9 @@ public class ConfigEntry extends AbstractConfigType> implem return this; } - public Builder enableOnServer() + public Builder setServersideShortName(String name) { - this.tmpEnabledOnServer = true; + this.tmpServersideShortName = name; return this; } @@ -387,7 +387,7 @@ public class ConfigEntry extends AbstractConfigType> implem public ConfigEntry build() { - return new ConfigEntry<>(this.tmpAppearance, this.tmpValue, this.tmpComment, this.tmpMin, this.tmpMax, this.tmpEnabledOnServer, this.tmpUseApiOverwrite, this.tmpPerformance, this.tmpIConfigListener); + return new ConfigEntry<>(this.tmpAppearance, this.tmpValue, this.tmpComment, this.tmpMin, this.tmpMax, this.tmpServersideShortName, this.tmpUseApiOverwrite, this.tmpPerformance, this.tmpIConfigListener); } } From 6da1e75dad51fd9bf9257ac9099de19c8e755a70 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 25 Nov 2023 18:04:25 +0500 Subject: [PATCH 068/205] Fix server side generation setting being ignored --- .../com/seibel/distanthorizons/core/level/DhClientLevel.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 9bc021572..f19c3ba5a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -144,9 +144,8 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel public void doWorldGen() { - this.worldGeneratorEnabledConfig.pollNewValue(); boolean isClientUsable = networkState != null && !networkState.getClient().isClosed(); - boolean shouldDoWorldGen = this.worldGeneratorEnabledConfig.get() && isClientUsable && clientside.isRendering(); + boolean shouldDoWorldGen = isClientUsable && this.networkState.config.distantGenerationEnabled && clientside.isRendering(); boolean isWorldGenRunning = worldGenModule.isWorldGenRunning(); if (shouldDoWorldGen && !isWorldGenRunning) { From f36bffa4b799fabade4a6be2f2652f0442826a53 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 26 Nov 2023 21:32:50 +0500 Subject: [PATCH 069/205] Add missing config entries in chat commands --- .../distanthorizons/core/config/Config.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 15d254785..c9fa0536c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -582,7 +582,7 @@ public class Config .comment("" + "How bright LOD colors are. \n" + "\n" - + "0 = black \n" + + "0 = black \n"f + "1 = normal \n" + "2 = near white") .build(); @@ -893,6 +893,7 @@ public class Config public static final ConfigEntry numberOfWorldGenerationThreads = new ConfigEntry.Builder() + .setServersideShortName("numberOfWorldGenerationThreads") .setMinDefaultMax(1, ThreadPresetConfigEventHandler.getWorldGenDefaultThreadCount(), Runtime.getRuntime().availableProcessors()) @@ -908,11 +909,13 @@ public class Config + THREAD_NOTE) .build(); public static final ConfigEntry runTimeRatioForWorldGenerationThreads = new ConfigEntry.Builder() + .setServersideShortName("runTimeRatioForWorldGenerationThreads") .setMinDefaultMax(0.01, ThreadPresetConfigEventHandler.getWorldGenDefaultRunTimeRatio(), 1.0) .comment(THREAD_RUN_TIME_RATIO_NOTE) .build(); public static final ConfigEntry numberOfFileHandlerThreads = new ConfigEntry.Builder() + .setServersideShortName("numberOfFileHandlerThreads") .setMinDefaultMax(1, ThreadPresetConfigEventHandler.getFileHandlerDefaultThreadCount(), Runtime.getRuntime().availableProcessors()) @@ -926,11 +929,13 @@ public class Config + THREAD_NOTE) .build(); public static final ConfigEntry runTimeRatioForFileHandlerThreads = new ConfigEntry.Builder() + .setServersideShortName("runTimeRatioForFileHandlerThreads") .setMinDefaultMax(0.01, ThreadPresetConfigEventHandler.getFileHandlerDefaultRunTimeRatio(), 1.0) .comment(THREAD_RUN_TIME_RATIO_NOTE) .build(); public static final ConfigEntry numberOfLodBuilderThreads = new ConfigEntry.Builder() + .setServersideShortName("numberOfLodBuilderThreads") .setMinDefaultMax(1, ThreadPresetConfigEventHandler.getLodBuilderDefaultThreadCount(), Runtime.getRuntime().availableProcessors()) @@ -943,10 +948,12 @@ public class Config + THREAD_NOTE) .build(); public static final ConfigEntry runTimeRatioForLodBuilderThreads = new ConfigEntry.Builder() + .setServersideShortName("runTimeRatioForLodBuilderThreads") .setMinDefaultMax(0.01, ThreadPresetConfigEventHandler.getLodBuilderDefaultRunTimeRatio(), 1.0) .comment(THREAD_RUN_TIME_RATIO_NOTE) .build(); public static final ConfigEntry enableLodBuilderThreadLimiting = new ConfigEntry.Builder() + .setServersideShortName("enableLodBuilderThreadLimiting") .set(true) .comment("" + "Should only be disabled if deadlock occurs and LODs refuse to update. \n" @@ -1054,6 +1061,7 @@ public class Config // TODO add change all option // TODO default to error chat and info file public static ConfigEntry logWorldGenEvent = new ConfigEntry.Builder() + .setServersideShortName("logWorldGenEvent") .set(ELoggerMode.LOG_WARNING_TO_CHAT_AND_FILE) .comment("" + "If enabled, the mod will log information about the world generation process. \n" @@ -1061,6 +1069,7 @@ public class Config .build(); public static ConfigEntry logWorldGenPerformance = new ConfigEntry.Builder() + .setServersideShortName("logWorldGenPerformance") .set(ELoggerMode.LOG_WARNING_TO_CHAT_AND_FILE) .comment("" + "If enabled, the mod will log performance about the world generation process. \n" @@ -1068,6 +1077,7 @@ public class Config .build(); public static ConfigEntry logWorldGenLoadEvent = new ConfigEntry.Builder() + .setServersideShortName("logWorldGenPerformance") .set(ELoggerMode.LOG_WARNING_TO_CHAT_AND_FILE) .comment("" + "If enabled, the mod will log information about the world generation process. \n" @@ -1075,6 +1085,7 @@ public class Config .build(); public static ConfigEntry logLodBuilderEvent = new ConfigEntry.Builder() + .setServersideShortName("logLodBuilderEvent") .set(ELoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE) .comment("" + "If enabled, the mod will log information about the LOD generation process. \n" @@ -1096,6 +1107,7 @@ public class Config .build(); public static ConfigEntry logFileReadWriteEvent = new ConfigEntry.Builder() + .setServersideShortName("logFileReadWriteEvent") .set(ELoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE) .comment("" + "If enabled, the mod will log information about file read/write operations. \n" @@ -1103,6 +1115,7 @@ public class Config .build(); public static ConfigEntry logFileSubDimEvent = new ConfigEntry.Builder() + .setServersideShortName("logFileSubDimEvent") .set(ELoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE) .comment("" + "If enabled, the mod will log information about file sub-dimension operations. \n" @@ -1110,6 +1123,7 @@ public class Config .build(); public static ConfigEntry logNetworkEvent = new ConfigEntry.Builder() + .setServersideShortName("logNetworkEvent") .set(ELoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE) .comment("" + "If enabled, the mod will log information about network operations. \n" From 166cc55e3c870a7c125fa2334255948a7c69a48c Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 27 Nov 2023 23:46:05 +0500 Subject: [PATCH 070/205] Abstract away ChannelHandlerContext from user code --- .../distanthorizons/core/config/Config.java | 2 +- .../core/level/DhServerLevel.java | 2 +- .../client/ClientNetworkState.java | 2 +- .../server/RemotePlayerConnectionHandler.java | 51 +++++-------- .../multiplayer/server/ServerPlayerState.java | 4 +- .../core/network/IConnection.java | 58 ++++++++++++++ .../core/network/NetworkClient.java | 28 ++++--- .../core/network/NetworkEventSource.java | 48 ++++++------ .../core/network/NetworkServer.java | 76 +++++++++++-------- .../FutureTrackableNetworkMessage.java | 12 +-- .../core/network/protocol/INetworkObject.java | 6 +- .../core/network/protocol/MessageHandler.java | 8 +- .../core/network/protocol/NetworkMessage.java | 22 +++--- 13 files changed, 195 insertions(+), 124 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index c9fa0536c..b5711adb6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -582,7 +582,7 @@ public class Config .comment("" + "How bright LOD colors are. \n" + "\n" - + "0 = black \n"f + + "0 = black \n" + "1 = normal \n" + "2 = near white") .build(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index b19d2fb09..bd0066c86 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -280,7 +280,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel if (distanceFromPlayer < serverPlayerState.serverPlayer.getViewDistance() || distanceFromPlayer > serverPlayerState.config.getRenderDistanceRadius()) return; - serverPlayerState.channelContext.writeAndFlush(new FullDataPartialUpdateMessage(chunkUpdateData.accessor, this)); + serverPlayerState.connection.sendMessage(new FullDataPartialUpdateMessage(chunkUpdateData.accessor, this)); } } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index 4870863b5..9883b393a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -48,7 +48,7 @@ public class ClientNetworkState implements Closeable { this.client.registerHandler(HelloMessage.class, helloMessage -> { - LOGGER.info("Connected to server: "+helloMessage.getChannelContext().channel().remoteAddress()); + LOGGER.info("Connected to server: "+helloMessage.getConnection().getRemoteAddress()); this.getClient().sendRequest(new PlayerUUIDMessage(playerUUID), AckMessage.class) .thenAccept(ack -> this.getClient().sendMessage(new RemotePlayerConfigMessage(new MultiplayerConfig()))) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java index e8828d75a..56f5870d6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java @@ -5,16 +5,16 @@ import com.google.common.collect.HashBiMap; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; -import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; +import com.seibel.distanthorizons.core.network.IConnection; import com.seibel.distanthorizons.core.network.NetworkServer; -import com.seibel.distanthorizons.core.network.messages.base.ILevelRelatedMessage; +import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.messages.base.AckMessage; import com.seibel.distanthorizons.core.network.messages.base.CloseEvent; +import com.seibel.distanthorizons.core.network.messages.base.ILevelRelatedMessage; import com.seibel.distanthorizons.core.network.messages.session.PlayerUUIDMessage; import com.seibel.distanthorizons.core.network.messages.session.RemotePlayerConfigMessage; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; -import io.netty.channel.ChannelHandlerContext; import org.jetbrains.annotations.Nullable; import java.io.Closeable; @@ -27,7 +27,7 @@ public class RemotePlayerConnectionHandler implements Closeable { private final ScopedNetworkEventSource eventSource; private final HashMap playersByUUID = new HashMap<>(); - private final BiMap playersByConnection = HashBiMap.create(); + private final BiMap playersByConnection = HashBiMap.create(); private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::onConfigChanged); @@ -43,23 +43,23 @@ public class RemotePlayerConnectionHandler implements Closeable { this.eventSource.registerHandler(PlayerUUIDMessage.class, playerUUIDMessage -> { - ChannelHandlerContext channelContext = playerUUIDMessage.getChannelContext(); - ServerPlayerState dhPlayer = this.playersByUUID.get(playerUUIDMessage.playerUUID); + IConnection connection = playerUUIDMessage.getConnection(); + ServerPlayerState serverPlayerState = this.playersByUUID.get(playerUUIDMessage.playerUUID); - if (dhPlayer == null) + if (serverPlayerState == null) { - this.eventSource.parent.disconnectClient(channelContext, "Player is not logged in."); + connection.disconnect("Player is not logged in."); return; } - if (dhPlayer.channelContext != null) + if (serverPlayerState.connection != null) { - this.eventSource.parent.disconnectClient(channelContext, "Another connection is already in use."); + connection.disconnect("Another connection is already in use."); return; } - dhPlayer.channelContext = channelContext; - this.playersByConnection.put(channelContext, dhPlayer); + serverPlayerState.connection = connection; + this.playersByConnection.put(connection, serverPlayerState); playerUUIDMessage.sendResponse(new AckMessage()); }); @@ -67,15 +67,15 @@ public class RemotePlayerConnectionHandler implements Closeable this.eventSource.registerHandler(RemotePlayerConfigMessage.class, this.connectedPlayersOnly((remotePlayerConfigMessage, serverPlayerState) -> { serverPlayerState.config.clientConfig = (MultiplayerConfig) remotePlayerConfigMessage.payload; - serverPlayerState.channelContext.writeAndFlush(new RemotePlayerConfigMessage(serverPlayerState.config)); + serverPlayerState.connection.sendMessage(new RemotePlayerConfigMessage(serverPlayerState.config)); })); this.eventSource.registerHandler(CloseEvent.class, closeEvent -> { - ServerPlayerState dhPlayer = this.playersByConnection.remove(closeEvent.getChannelContext()); + ServerPlayerState dhPlayer = this.playersByConnection.remove(closeEvent.getConnection()); if (dhPlayer != null) { - dhPlayer.channelContext = null; + dhPlayer.connection = null; } }); } @@ -83,7 +83,7 @@ public class RemotePlayerConnectionHandler implements Closeable private void onConfigChanged() { for (ServerPlayerState serverPlayerState : this.getConnectedPlayers()) - serverPlayerState.channelContext.writeAndFlush(new RemotePlayerConfigMessage(serverPlayerState.config)); + serverPlayerState.connection.sendMessage(new RemotePlayerConfigMessage(serverPlayerState.config)); } public Consumer connectedPlayersOnly(BiConsumer next) @@ -118,16 +118,7 @@ public class RemotePlayerConnectionHandler implements Closeable @Nullable public ServerPlayerState getConnectedPlayer(NetworkMessage msg) { - return playersByConnection.get(msg.getChannelContext()); - } - - @Nullable - public ServerPlayerState getConnectedPlayer(IServerPlayerWrapper serverPlayer) - { - ServerPlayerState player = playersByUUID.get(serverPlayer.getUUID()); - if (player == null || player.channelContext == null) - return null; - return player; + return playersByConnection.get(msg.getConnection()); } public void registerJoinedPlayer(IServerPlayerWrapper serverPlayer) @@ -138,11 +129,9 @@ public class RemotePlayerConnectionHandler implements Closeable public void unregisterLeftPlayer(IServerPlayerWrapper serverPlayer) { ServerPlayerState dhPlayer = this.playersByUUID.remove(serverPlayer.getUUID()); - ChannelHandlerContext channelContext = this.playersByConnection.inverse().remove(dhPlayer); - if (channelContext != null) - { - this.eventSource.parent.disconnectClient(channelContext, "You are being disconnected."); - } + IConnection connection = this.playersByConnection.inverse().remove(dhPlayer); + if (connection != null) + connection.disconnect("You are being disconnected."); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index c2fde5ef5..3429107a2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -1,14 +1,14 @@ package com.seibel.distanthorizons.core.multiplayer.server; +import com.seibel.distanthorizons.core.network.IConnection; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; -import io.netty.channel.ChannelHandlerContext; import java.util.concurrent.atomic.AtomicInteger; public class ServerPlayerState { public IServerPlayerWrapper serverPlayer; - public ChannelHandlerContext channelContext; + public IConnection connection; public ServersideMultiplayerConfig config = new ServersideMultiplayerConfig(); public final AtomicInteger pendingFullDataRequests = new AtomicInteger(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java b/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java new file mode 100644 index 000000000..bc94845dc --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java @@ -0,0 +1,58 @@ +package com.seibel.distanthorizons.core.network; + +import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage; +import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; + +import java.net.SocketAddress; +import java.util.concurrent.CompletableFuture; + +public interface IConnection +{ + ChannelHandlerContext getChannelContext(); + NetworkEventSource getRequestHandler(); + + default SocketAddress getRemoteAddress() + { + return this.getChannelContext().channel().remoteAddress(); + } + + default CompletableFuture sendMessage(NetworkMessage message) + { + CompletableFuture future = new CompletableFuture<>(); + + this.getChannelContext().writeAndFlush(message).addListener(writeFuture -> + { + if (writeFuture.cause() != null) + { + future.completeExceptionally(writeFuture.cause()); + } + else + { + future.complete(null); + } + }); + + return future; + } + + default CompletableFuture sendRequest(FutureTrackableNetworkMessage msg, Class responseClass) + { + return this.getRequestHandler().sendRequest(this, msg, responseClass); + } + + default void disconnect(String reason) + { + this.getChannelContext().channel().config().setAutoRead(false); + this.getChannelContext().writeAndFlush(new CloseReasonMessage(reason)) + .addListener(ChannelFutureListener.CLOSE); + } + + default Throwable getCloseReason() + { + return this.getChannelContext().channel().closeFuture().cause(); + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index 5891b24bd..813ab7ca0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -23,7 +23,6 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.network.messages.base.CloseEvent; import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage; import com.seibel.distanthorizons.core.network.messages.base.HelloMessage; -import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.MessageHandler; import com.seibel.distanthorizons.core.network.protocol.NetworkChannelInitializer; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; @@ -36,10 +35,9 @@ import org.apache.logging.log4j.Logger; import java.net.InetSocketAddress; import java.util.EnumSet; import java.util.Set; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; -public class NetworkClient extends NetworkEventSource implements AutoCloseable +public class NetworkClient extends NetworkEventSource implements IConnection, AutoCloseable { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); @@ -75,7 +73,13 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable .group(this.workerGroup) .channel(NioSocketChannel.class) .option(ChannelOption.SO_KEEPALIVE, true) - .handler(new NetworkChannelInitializer(new MessageHandler(this::handleMessage, this::addNewContext))); + .handler(new NetworkChannelInitializer(new MessageHandler( + (ctx, msg) -> { + msg.setConnection(this); + this.handleMessage(msg); + }, + ctx -> this.addNewConnection(this) + ))); private EConnectionState connectionState = EConnectionState.INITIAL; private Channel channel; @@ -99,7 +103,7 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable this.registerHandler(CloseEvent.class, closeEvent -> { - LOGGER.info("Disconnected from server: "+ closeEvent.getChannelContext().channel().remoteAddress()); + LOGGER.info("Disconnected from server: "+this.getRemoteAddress()); if (this.connectionState == EConnectionState.CLOSING) { this.close(); @@ -158,17 +162,19 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable }); } - public final void sendMessage(NetworkMessage msg) + @Override + public ChannelHandlerContext getChannelContext() { - this.channel.writeAndFlush(msg); + return this.channel.pipeline().context(MessageHandler.class); } - public final CompletableFuture sendRequest(FutureTrackableNetworkMessage msg, Class responseClass) + @Override + public NetworkEventSource getRequestHandler() { - return this.sendRequest(this.channel.pipeline().context(MessageHandler.class), msg, responseClass); + return this; } - @Override + @Override public void close() { if (this.connectionState == EConnectionState.CLOSED) @@ -178,7 +184,7 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable this.connectionState = EConnectionState.CLOSED; this.channel.close().syncUninterruptibly(); - this.workerGroup.shutdownGracefully().syncUninterruptibly(); + this.workerGroup.shutdownGracefully(); super.close(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index 5b53ba7ff..77d3834fd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -27,7 +27,6 @@ import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMe import com.seibel.distanthorizons.core.network.protocol.MessageRegistry; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import io.netty.channel.ChannelException; -import io.netty.channel.ChannelHandlerContext; import org.apache.logging.log4j.Logger; import java.io.InvalidClassException; @@ -43,7 +42,7 @@ public abstract class NetworkEventSource { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); protected final ConcurrentMap, Set>> handlers = new ConcurrentHashMap<>(); - private final ConcurrentMap> pendingFutures = new ConcurrentHashMap<>(); + private final ConcurrentMap> pendingFutures = new ConcurrentHashMap<>(); protected boolean hasHandler(Class handlerClass) { @@ -68,7 +67,7 @@ public abstract class NetworkEventSource if (message instanceof FutureTrackableNetworkMessage) { FutureTrackableNetworkMessage trackableMessage = (FutureTrackableNetworkMessage)message; - ConcurrentMap subMap = pendingFutures.get(message.getChannelContext()); + ConcurrentMap subMap = pendingFutures.get(message.getConnection()); if (subMap != null) { FutureResponseData responseData = subMap.get(trackableMessage.futureId); @@ -90,9 +89,9 @@ public abstract class NetworkEventSource LOGGER.warn("Unhandled message: " + message); } - protected void addNewContext(ChannelHandlerContext ctx) + protected void addNewConnection(IConnection connection) { - this.pendingFutures.put(ctx, new ConcurrentHashMap<>()); + this.pendingFutures.put(connection, new ConcurrentHashMap<>()); } public void registerHandler(Class handlerClass, Consumer handlerImplementation) @@ -115,50 +114,51 @@ public abstract class NetworkEventSource } - protected CompletableFuture sendRequest(ChannelHandlerContext ctx, FutureTrackableNetworkMessage msg, Class responseClass) + protected CompletableFuture sendRequest(IConnection connection, FutureTrackableNetworkMessage msg, Class responseClass) { - msg.setChannelContext(ctx); + msg.setConnection(connection); CompletableFuture responseFuture = new CompletableFuture<>(); - responseFuture.handle((response, throwable) -> { + responseFuture.whenComplete((response, throwable) -> + { if (!(throwable instanceof ChannelException)) { - ConcurrentMap subMap = pendingFutures.get(ctx); + ConcurrentMap subMap = pendingFutures.get(connection); if (subMap != null) subMap.remove(msg.futureId); } if (throwable instanceof CancellationException) msg.sendResponse(new CancelMessage()); - - return null; }); - ConcurrentMap subMap = pendingFutures.get(ctx); - if (subMap == null) { + ConcurrentMap subMap = pendingFutures.get(connection); + if (subMap == null) + { // Was deleted before adding - responseFuture.completeExceptionally(ctx.channel().closeFuture().cause()); + responseFuture.completeExceptionally(connection.getCloseReason()); return responseFuture; } subMap.put(msg.futureId, new FutureResponseData(responseClass, responseFuture)); - if (!pendingFutures.containsKey(ctx)) { + if (!pendingFutures.containsKey(connection)) + { // Was deleted while adding - responseFuture.completeExceptionally(ctx.channel().closeFuture().cause()); + responseFuture.completeExceptionally(connection.getCloseReason()); return responseFuture; } // If passed until here, cancelling is up to the cleaning side - ctx.writeAndFlush(msg).addListener(writeFuture -> { - if (writeFuture.cause() != null) { - responseFuture.completeExceptionally(writeFuture.cause()); - } + connection.sendMessage(msg).whenComplete((ignored, throwable) -> + { + if (throwable != null) + responseFuture.completeExceptionally(throwable); }); return responseFuture; } - protected final void completeAllFuturesExceptionally(ChannelHandlerContext ctx, Throwable cause) + protected final void completeAllFuturesExceptionally(IConnection connection, Throwable cause) { - ConcurrentMap map = pendingFutures.remove(ctx); + ConcurrentMap map = pendingFutures.remove(connection); if (map == null) return; for (FutureResponseData responseData : map.values()) @@ -167,8 +167,8 @@ public abstract class NetworkEventSource protected final void completeAllFuturesExceptionally(Throwable cause) { - for (ChannelHandlerContext ctx : pendingFutures.keySet()) - this.completeAllFuturesExceptionally(ctx, cause); + for (IConnection connection : pendingFutures.keySet()) + this.completeAllFuturesExceptionally(connection, cause); } public void close() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java index 003b192eb..ab24e7516 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java @@ -19,13 +19,13 @@ package com.seibel.distanthorizons.core.network; +import com.google.common.collect.MapMaker; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage; import com.seibel.distanthorizons.core.network.messages.base.CloseEvent; import com.seibel.distanthorizons.core.network.messages.base.HelloMessage; -import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.MessageHandler; import com.seibel.distanthorizons.core.network.protocol.NetworkChannelInitializer; +import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import com.seibel.distanthorizons.coreapi.ModInfo; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.*; @@ -35,7 +35,8 @@ import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import org.apache.logging.log4j.Logger; -import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.atomic.AtomicBoolean; public class NetworkServer extends NetworkEventSource implements AutoCloseable { @@ -46,7 +47,9 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable private final EventLoopGroup bossGroup = new NioEventLoopGroup(1); private final EventLoopGroup workerGroup = new NioEventLoopGroup(); - private boolean isClosed = false; + private final AtomicBoolean isClosed = new AtomicBoolean(); + + private final ConcurrentMap connections = new MapMaker().weakKeys().weakValues().makeMap(); @@ -63,16 +66,16 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable { this.registerHandler(HelloMessage.class, helloMessage -> { - ChannelHandlerContext channelContext = helloMessage.getChannelContext(); - LOGGER.info("Client connected: "+channelContext.channel().remoteAddress()); + IConnection connection = helloMessage.getConnection(); + LOGGER.info("Client connected: "+connection.getRemoteAddress()); if (helloMessage.version != ModInfo.PROTOCOL_VERSION) { try { String disconnectReason = "Version mismatch. Server version: ["+ModInfo.PROTOCOL_VERSION+"], client version: ["+helloMessage.version+"]."; - LOGGER.info("Disconnecting the client ["+channelContext.name()+"]: "+disconnectReason); - this.disconnectClient(channelContext, disconnectReason); + LOGGER.info("Disconnecting the client ["+connection.getRemoteAddress()+"]: "+disconnectReason); + connection.disconnect(disconnectReason); } catch (Exception e) { @@ -81,15 +84,14 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable return; } - channelContext.writeAndFlush(new HelloMessage()); + connection.sendMessage(new HelloMessage()); }); this.registerHandler(CloseEvent.class, closeEvent -> { - Channel channel = closeEvent.getChannelContext().channel(); - LOGGER.info("Client disconnected: "+channel.remoteAddress()); - - this.completeAllFuturesExceptionally(closeEvent.getChannelContext(), channel.closeFuture().cause()); + IConnection connection = closeEvent.getConnection(); + LOGGER.info("Client disconnected: "+connection.getRemoteAddress()); + this.completeAllFuturesExceptionally(closeEvent.getConnection(), connection.getCloseReason()); }); } @@ -99,7 +101,13 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable .group(this.bossGroup, this.workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.DEBUG)) - .childHandler(new NetworkChannelInitializer(new MessageHandler(this::handleMessage, this::addNewContext))); + .childHandler(new NetworkChannelInitializer(new MessageHandler( + (ctx, msg) -> { + msg.setConnection(this.connections.computeIfAbsent(ctx, Connection::new)); + this.handleMessage(msg); + }, + ctx -> this.addNewConnection(this.connections.computeIfAbsent(ctx, Connection::new)) + ))); ChannelFuture bindFuture = bootstrap.bind(this.port); bindFuture.addListener((ChannelFuture channelFuture) -> @@ -116,27 +124,11 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable channel.closeFuture().addListener(future -> this.close()); } - public void disconnectClient(ChannelHandlerContext ctx, String reason) - { - ctx.channel().config().setAutoRead(false); - ctx.writeAndFlush(new CloseReasonMessage(reason)) - .addListener(ChannelFutureListener.CLOSE); - } - - @Override - public CompletableFuture sendRequest(ChannelHandlerContext ctx, FutureTrackableNetworkMessage msg, Class responseClass) - { - return super.sendRequest(ctx, msg, responseClass); - } - @Override public void close() { - if (this.isClosed) - { + if (!this.isClosed.compareAndSet(false, true)) return; - } - this.isClosed = true; LOGGER.info("Shutting down the network server."); this.workerGroup.shutdownGracefully().syncUninterruptibly(); @@ -146,4 +138,26 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable super.close(); } + public class Connection implements IConnection + { + private final ChannelHandlerContext channelContext; + + public Connection(ChannelHandlerContext channelContext) + { + this.channelContext = channelContext; + } + + @Override + public ChannelHandlerContext getChannelContext() + { + return this.channelContext; + } + + @Override + public NetworkEventSource getRequestHandler() + { + return NetworkServer.this; + } + + } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java index a6e437e2d..ce267f67e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java @@ -21,10 +21,10 @@ package com.seibel.distanthorizons.core.network.protocol; import com.google.common.collect.MapMaker; import com.seibel.distanthorizons.core.api.internal.SharedApi; +import com.seibel.distanthorizons.core.network.IConnection; import com.seibel.distanthorizons.core.network.messages.base.ExceptionMessage; import com.seibel.distanthorizons.core.world.EWorldEnvironment; import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; import javax.annotation.Nullable; import java.util.Objects; @@ -41,19 +41,19 @@ public abstract class FutureTrackableNetworkMessage extends NetworkMessage | ((Objects.requireNonNull(SharedApi.getEnvironment()) == EWorldEnvironment.Server_Only ? 1 : 0) << 31); private static final AtomicInteger lastContextId = new AtomicInteger(); - private static final ConcurrentMap contextIds = new MapMaker().weakKeys().makeMap(); + private static final ConcurrentMap connectionToIdMap = new MapMaker().weakKeys().makeMap(); public void sendResponse(FutureTrackableNetworkMessage responseMessage) { responseMessage.futureId = futureId; - getChannelContext().writeAndFlush(responseMessage); + this.getConnection().sendMessage(responseMessage); } @Override - public void setChannelContext(ChannelHandlerContext channelContext) + public void setConnection(IConnection connection) { - super.setChannelContext(channelContext); - this.futureId |= (long) contextIds.computeIfAbsent(channelContext, k -> lastContextId.getAndIncrement()) << 32; + super.setConnection(connection); + this.futureId |= (long) connectionToIdMap.computeIfAbsent(connection, k -> lastContextId.getAndIncrement()) << 32; } public void sendResponse(Exception e) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java index eb4e2e38f..63a2cc476 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java @@ -109,8 +109,10 @@ public interface INetworkObject map.put(entry.getKey(), entry.getValue()); } - /** Should only be used for non-editable classes; - * otherwise, you may want to implement {@link INetworkObject} and use its method where applicable. */ + /** + * Should only be used for non-editable classes; + * otherwise, you may want to implement {@link INetworkObject} and use its method where applicable. + */ class Codec { private static final ConcurrentMap, Codec> codecMap = new ConcurrentHashMap, Codec>() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java index 3967b86b6..55122118d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java @@ -27,6 +27,7 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; +import java.util.function.BiConsumer; import java.util.function.Consumer; @ChannelHandler.Sharable @@ -34,10 +35,10 @@ public class MessageHandler extends SimpleChannelInboundHandler { private static final Logger LOGGER = LogManager.getLogger(); - private final Consumer messageConsumer; + private final BiConsumer messageConsumer; private final Consumer channelActiveConsumer; - public MessageHandler(Consumer messageConsumer, Consumer channelActiveConsumer) + public MessageHandler(BiConsumer messageConsumer, Consumer channelActiveConsumer) { this.messageConsumer = messageConsumer; this.channelActiveConsumer = channelActiveConsumer; @@ -46,9 +47,8 @@ public class MessageHandler extends SimpleChannelInboundHandler @Override protected void channelRead0(ChannelHandlerContext channelContext, NetworkMessage message) { - message.setChannelContext(channelContext); LOGGER.trace("Received message: " + message); - this.messageConsumer.accept(message); + this.messageConsumer.accept(channelContext, message); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkMessage.java index 4c5fb44c3..0c51455ee 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkMessage.java @@ -19,33 +19,35 @@ package com.seibel.distanthorizons.core.network.protocol; -import io.netty.channel.ChannelHandlerContext; -import org.jetbrains.annotations.NotNull; +import com.seibel.distanthorizons.core.network.IConnection; + +import javax.annotation.Nullable; public abstract class NetworkMessage implements INetworkObject { - private ChannelHandlerContext channelContext = null; + private IConnection connection = null; public boolean warnWhenUnhandled() { return true; } - public ChannelHandlerContext getChannelContext() + public IConnection getConnection() { - return channelContext; + return connection; } - public void setChannelContext(ChannelHandlerContext channelContext) + public void setConnection(IConnection connection) { - if (this.channelContext != null) + if (this.connection != null) throw new IllegalStateException("Channel context cannot be changed after initial setting."); - this.channelContext = channelContext; + this.connection = connection; } - @Override public String toString() + @Override + public String toString() { return toString(""); } - protected String toString(@NotNull String extraData) + protected String toString(@Nullable String extraData) { return this.getClass().getSimpleName() + "{" + extraData + '}'; } From cb95c45d76feba6dab1129c5faf6fca21f7c2232 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Wed, 6 Dec 2023 22:06:37 +0500 Subject: [PATCH 071/205] Fix compilation --- .../distanthorizons/core/config/file/ConfigFileHandling.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigFileHandling.java b/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigFileHandling.java index ff18ad659..8e3e516d5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigFileHandling.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigFileHandling.java @@ -25,6 +25,7 @@ import com.seibel.distanthorizons.core.config.ConfigBase; import com.seibel.distanthorizons.core.config.types.AbstractConfigType; import com.seibel.distanthorizons.core.config.types.ConfigEntry; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; From 04e43ebec8de30546f5638b87648c2ae5f7c6906 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 18 Dec 2023 19:13:57 +0500 Subject: [PATCH 072/205] Remove current post-relog update feature --- .../distanthorizons/core/config/Config.java | 3 +- .../RemoteFullDataFileHandler.java | 194 +----------------- .../core/level/DhClientLevel.java | 2 +- .../multiplayer/config/MultiplayerConfig.java | 2 +- .../MultiplayerConfigChangeListener.java | 2 +- .../server/ServersideMultiplayerConfig.java | 2 +- 6 files changed, 8 insertions(+), 197 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 38e6044d0..04e487749 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -881,7 +881,8 @@ public class Config + "") .build(); - public static ConfigEntry enablePostRelogUpdate = new ConfigEntry.Builder() + // Disabled, previous implementation is too terrible to continue using it + private static ConfigEntry enablePostRelogUpdate = new ConfigEntry.Builder() .setServersideShortName("enablePostRelogUpdate") .set(false) .comment("" diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java index 6d5c48750..a4fdc563d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java @@ -19,207 +19,17 @@ package com.seibel.distanthorizons.core.file.fullDatafile; -import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; -import com.seibel.distanthorizons.core.generation.IWorldGenerationQueue; -import com.seibel.distanthorizons.core.level.DhLevel; import com.seibel.distanthorizons.core.level.IDhLevel; -import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.logging.f3.F3Screen; -import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; -import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; -import com.seibel.distanthorizons.core.network.exceptions.InvalidSectionPosException; -import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceResponseMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataChangeSummaryRequestMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataChangeSummaryResponseMessage; -import com.seibel.distanthorizons.core.pos.DhSectionPos; -import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; -import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; -import io.netty.channel.ChannelException; -import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.Nullable; -import javax.annotation.CheckForNull; -import java.awt.*; import java.io.File; -import java.util.ArrayList; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; -import java.util.stream.Collectors; -public class RemoteFullDataFileHandler extends GeneratedFullDataFileHandler implements IDebugRenderable +public class RemoteFullDataFileHandler extends GeneratedFullDataFileHandler { - private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - - @CheckForNull - private final ClientNetworkState networkState; - - private final Set visitedSections = ConcurrentHashMap.newKeySet(); - private final ConcurrentMap sectionsToUpdate = new ConcurrentHashMap<>(); - private final AtomicBoolean isUpdating = new AtomicBoolean(false); - - private final F3Screen.NestedMessage f3Message = new F3Screen.NestedMessage(this::f3Log); - private final AtomicInteger finishedRequests = new AtomicInteger(); - private final AtomicInteger failedRequests = new AtomicInteger(); - - public RemoteFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure, @Nullable File saveDirOverride, @Nullable ClientNetworkState networkState) + public RemoteFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure, @Nullable File saveDirOverride) { super(level, saveStructure, saveDirOverride); - this.networkState = networkState; - DebugRenderer.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showWorldGenQueue); - } - - @Override - public FullDataMetaFile getFileIfExist(DhSectionPos pos) - { - if (this.networkState == null || !this.fileExists(pos)) - return super.getFileIfExist(pos); - - if (!this.networkState.config.postRelogUpdateEnabled) - return super.getFileIfExist(pos); - - FullDataMetaFile metaFile = super.getFileIfExist(pos); - if (metaFile == null) - return null; - - LOGGER.debug("Checking server updates for section {}", pos); - pos.forEachChildAtLevel(DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL, childPos -> - { - FullDataMetaFile childMetaFile = super.getFileIfExist(childPos); - if (childMetaFile != null && childMetaFile.baseMetaData != null && visitedSections.add(childPos)) - sectionsToUpdate.put(childPos, childMetaFile); - }); - sendUpdateChecks(); - - return metaFile; - } - - private void sendUpdateChecks() - { - assert this.networkState != null; - - if (this.sectionsToUpdate.isEmpty()) - return; - - if (this.isUpdating.getAndSet(true)) - return; - - Map block = sectionsToUpdate.entrySet().stream() - .limit(20) - .collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue().baseMetaData.checksum)); - for (DhSectionPos pos : block.keySet()) - sectionsToUpdate.remove(pos); - - this.networkState.getClient().sendRequest(new FullDataChangeSummaryRequestMessage(level.getLevelWrapper(), block), FullDataChangeSummaryResponseMessage.class) - .handle((response, throwable) -> - { - try - { - if (throwable != null) - throw throwable; - - IWorldGenerationQueue queue = this.worldGenQueueRef.get(); - if (queue == null) - return null; - - for (DhSectionPos pos : response.changedPosList) - sendUpdateRequest(pos); - } - catch (InvalidLevelException ignored) - { - // We're too late - } - catch (InvalidSectionPosException ignored) - { - } - catch (Throwable e) - { - LOGGER.error("Error while checking section updates", e); - } - finally - { - this.isUpdating.set(false); - sendUpdateChecks(); - } - - return null; - }); - } - - public void sendUpdateRequest(DhSectionPos sectionPos) - { - assert this.networkState != null; - - this.networkState.getClient().sendRequest(new FullDataSourceRequestMessage(level.getLevelWrapper(), sectionPos, true), FullDataSourceResponseMessage.class) - .handleAsync((response, throwable) -> - { - try - { - if (throwable != null) - throw throwable; - - CompleteFullDataSource fullDataSource = response.getFullDataSource(sectionPos, level); - - fullDataSource.splitIntoChunkSizedAccessors(((DhLevel) level)::saveWrites); - response.getFullDataSourceLoader().returnPooledDataSource(fullDataSource); - } - catch (InvalidLevelException ignored) - { - // We're too late - } - catch (ChannelException | RateLimitedException ignored) - { - // Can't bother retrying - this.failedRequests.incrementAndGet(); - } - catch (Throwable e) - { - LOGGER.error("Error while fetching full data source", e); - this.failedRequests.incrementAndGet(); - } - - return null; - }); - } - - private String[] f3Log() - { - if (this.networkState == null || !this.networkState.config.postRelogUpdateEnabled) - return new String[0]; - - // These metrics are not precise; Updated sections[2] is within range of 1 rate limit or so - ArrayList lines = new ArrayList<>(); - lines.add("Post-relog update ["+level.getLevelWrapper().getDimensionType().getDimensionName()+"]"); - lines.add("Visited sections: "+visitedSections.size()); - lines.add("Updated sections: "+this.finishedRequests+" / "+(this.sectionsToUpdate.size() + this.finishedRequests.get())+" (failed: "+this.failedRequests+")"); - return lines.toArray(new String[0]); - } - - @Override - public void debugRender(DebugRenderer r) - { - for (Map.Entry mapEntry : sectionsToUpdate.entrySet()) - { - r.renderBox(new DebugRenderer.Box(mapEntry.getKey(), -32f, 64f, 0.05f, Color.pink)); - } - } - - @Override - public void close() - { - f3Message.close(); - DebugRenderer.unregister(this, Config.Client.Advanced.Debugging.DebugWireframe.showWorldGenQueue); - super.close(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 561695128..b00ecf146 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -88,7 +88,7 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel { this.levelWrapper = clientLevelWrapper; this.saveStructure = saveStructure; - this.dataFileHandler = new RemoteFullDataFileHandler(this, saveStructure, fullDataSaveDirOverride, networkState); + this.dataFileHandler = new RemoteFullDataFileHandler(this, saveStructure, fullDataSaveDirOverride); this.worldGeneratorEnabledConfig = new AppliedConfigState<>(Config.Client.Advanced.WorldGenerator.enableDistantGeneration); this.networkState = networkState; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java index 406f892c6..787639f18 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java @@ -19,7 +19,7 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig public boolean realTimeUpdatesEnabled = Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates.get(); @Override public boolean isRealTimeUpdatesEnabled() { return realTimeUpdatesEnabled; } - public boolean postRelogUpdateEnabled = Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate.get(); + public boolean postRelogUpdateEnabled = false; // Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate.get(); @Override public boolean isPostRelogUpdateEnabled() { return postRelogUpdateEnabled; } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java index f9e096a6b..6dec4fd2a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java @@ -15,7 +15,7 @@ public class MultiplayerConfigChangeListener implements Closeable Config.Client.Advanced.WorldGenerator.enableDistantGeneration, Config.Client.Advanced.Multiplayer.ServerNetworking.requestRateLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates, - Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate + //Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate }; private final ArrayList changeListeners = new ArrayList<>(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java index 217eab119..5ae7472cf 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java @@ -38,7 +38,7 @@ public class ServersideMultiplayerConfig extends AbstractMultiplayerConfig @Override public boolean isPostRelogUpdateEnabled() { - return clientConfig.postRelogUpdateEnabled && Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate.get(); + return false; // clientConfig.postRelogUpdateEnabled && Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate.get(); } @Override From a2cc1c291391482e3a6d93e491d753399ecea342 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 18 Dec 2023 22:15:34 +0500 Subject: [PATCH 073/205] Revert "Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core" This reverts commit 522a7995166e987059a17904899d091b0071becc, reversing changes made to 04e43ebec8de30546f5638b87648c2ae5f7c6906. --- .../com/seibel/distanthorizons/api/DhApi.java | 17 +- .../distanthorizons/coreapi/ModInfo.java | 2 - .../core/api/internal/SharedApi.java | 76 +++----- .../interfaces/IIncompleteFullDataSource.java | 4 +- .../fullDatafile/FullDataFileHandler.java | 11 +- .../file/fullDatafile/FullDataMetaFile.java | 162 ++++++++---------- .../distanthorizons/core/util/ThreadUtil.java | 4 +- 7 files changed, 97 insertions(+), 179 deletions(-) diff --git a/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java b/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java index a069e5da3..3cabd3b6b 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java @@ -52,7 +52,7 @@ public class DhApi { /** * If you can see this Java Doc, this can be ignored.
- * This is just to let you know that Javadocs are available and that you should use the API jar + * This is just to you know that Javadocs are available and that you should use the API jar * instead of the full mod jar.

* * Note: Don't use this string in your code. It may change and is only for reference. @@ -118,11 +118,7 @@ public class DhApi - //==================// // always available // - //==================// - - // interfaces // /** * Used to bind/unbind Distant Horizons Api events. @@ -143,8 +139,6 @@ public class DhApi public static final IOverrideInjector overrides = OverrideInjector.INSTANCE; - // getters // - /** * This version should only be updated when breaking changes are introduced to the Distant Horizons API. * @since API 1.0.0 @@ -180,13 +174,4 @@ public class DhApi */ public static int getNetworkProtocolVersion() { return ModInfo.PROTOCOL_VERSION; } - - // methods // - - /** - * Returns true if the thread this method was called from is owned by Distant Horizons. - * @since API 1.1.0 - */ - public static boolean isDhThread() { return Thread.currentThread().getName().startsWith(ModInfo.THREAD_NAME_PREFIX); } - } diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java index 8e35bebcd..9283d700a 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java @@ -48,7 +48,5 @@ public final class ModInfo public static final String NETWORKING_RESOURCE_NAMESPACE = "distant_horizons"; public static final String MULTIVERSE_PLUGIN_NAMESPACE = "world_control"; - /** All DH owned threads should start with this string to allow for easier debugging and profiling. */ - public static String THREAD_NAME_PREFIX = "DH-"; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java index b77a95be6..fcc4f5f47 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java @@ -25,10 +25,8 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.generation.DhLightingEngine; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; -import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.objects.Pair; import com.seibel.distanthorizons.core.util.threading.ThreadPools; import com.seibel.distanthorizons.core.world.*; @@ -54,18 +52,14 @@ public class SharedApi private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); private static final Set UPDATING_CHUNK_SET = ConcurrentHashMap.newKeySet(); - /** how many chunks can be queued for updating per thread, used to prevent updates from infinitely pilling up if the user flys around extremely fast */ - private static final int MAX_UPDATING_CHUNK_COUNT_PER_THREAD = 500; - private static final int MIN_MS_BETWEEN_OVERLOADED_LOG_MESSAGE = 5_000; + private static final int MAX_UPDATING_CHUNK_COUNT_PER_THREAD = 100; - private static final Timer CHUNK_UPDATE_TIMER = new Timer(); private static AbstractDhWorld currentWorld; private static int lastWorldGenTickDelta = 0; - private static long lastOverloadedLogMessageMsTime = 0; - public F3Screen.DynamicMessage f3Message; + private static final Timer CHUNK_UPDATE_TIMER = new Timer(); @@ -73,14 +67,7 @@ public class SharedApi // constructor // //=============// - private SharedApi() - { - this.f3Message = new F3Screen.DynamicMessage(() -> - { - int maxUpdateCount = MAX_UPDATING_CHUNK_COUNT_PER_THREAD * Config.Client.Advanced.MultiThreading.numberOfLodBuilderThreads.get(); - return LodUtil.formatLog("Queued chunk updates: " + UPDATING_CHUNK_SET.size() + " / " + maxUpdateCount); - }); - } + private SharedApi() { } public static void init() { Initializer.init(); } @@ -161,10 +148,6 @@ public class SharedApi public void applyChunkUpdate(IChunkWrapper chunkWrapper, ILevelWrapper level, boolean updateNeighborChunks) { - //========================// - // world and level checks // - //========================// - if (chunkWrapper == null) { // shouldn't happen, but just in case @@ -200,42 +183,7 @@ public class SharedApi } - - //=====================// - // task limiting check // - //=====================// - - int currentQueueCount = UPDATING_CHUNK_SET.size(); - int maxQueueCount = MAX_UPDATING_CHUNK_COUNT_PER_THREAD * Config.Client.Advanced.MultiThreading.numberOfLodBuilderThreads.get(); - if (currentQueueCount >= maxQueueCount) - { - // The maximum number of chunks are already queued, don't add more. - // This is done to prevent overloading the system if the user fly's extremely fast and queues too many chunks - - long msBetweenLastLog = System.currentTimeMillis() - lastOverloadedLogMessageMsTime; - if (msBetweenLastLog >= MIN_MS_BETWEEN_OVERLOADED_LOG_MESSAGE) - { - lastOverloadedLogMessageMsTime = System.currentTimeMillis(); - LOGGER.warn("Too many chunks queued for updating, max queue count ["+maxQueueCount+"] (["+MAX_UPDATING_CHUNK_COUNT_PER_THREAD+"] per thread). Some LODs may not be updated or may be missing. Please move through the world slower, decrease your vanilla render distance, or increase the CPU load config."); - } - - return; - } - - // prevent duplicate update requests - if (UPDATING_CHUNK_SET.contains(chunkWrapper.getChunkPos())) - { - // this chunk is already being updated - return; - } - UPDATING_CHUNK_SET.add(chunkWrapper.getChunkPos()); - - - - //===============================// - // update the necessary chunk(s) // - //===============================// - + // update the necessary chunk(s) if (!updateNeighborChunks) { // only update the center chunk @@ -280,6 +228,22 @@ public class SharedApi } private static void bakeChunkLightingAndSendToLevelAsync(IChunkWrapper chunkWrapper, @Nullable ArrayList neighbourChunkList, IDhLevel dhLevel) { + if (UPDATING_CHUNK_SET.size() >= MAX_UPDATING_CHUNK_COUNT_PER_THREAD * Config.Client.Advanced.MultiThreading.numberOfLodBuilderThreads.get()) + { + // The maximum number of chunks are already queued, don't add more. + // This is done to prevent overloading the system if the user flys extremely fast and queues too many chunks + return; + } + + // prevent duplicate update requests + if (UPDATING_CHUNK_SET.contains(chunkWrapper.getChunkPos())) + { + // this chunk is already being updated + return; + } + UPDATING_CHUNK_SET.add(chunkWrapper.getChunkPos()); + + // lighting the chunk needs to be done on a separate thread to prevent lagging any of the event threads ThreadPoolExecutor executor = ThreadPools.getLightPopulatorExecutor(); if (executor == null) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IIncompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IIncompleteFullDataSource.java index b48bf785b..50f16bd4b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IIncompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IIncompleteFullDataSource.java @@ -36,8 +36,8 @@ public interface IIncompleteFullDataSource extends IFullDataSource { DhSectionPos inputPos = inputSource.getSectionPos(); DhSectionPos thisPos = this.getSectionPos(); - LodUtil.assertTrue(inputPos.getDetailLevel() < thisPos.getDetailLevel(), "input data source at pos: ["+inputPos+"] has a lower detail level than this: ["+thisPos+"]."); - LodUtil.assertTrue(inputPos.overlapsExactly(this.getSectionPos()), "input source at pos: ["+inputPos+"] (converted to ["+inputPos.convertNewToDetailLevel(thisPos.getDetailLevel())+"]) doesn't overlap with this source's pos: ["+thisPos+"]."); + LodUtil.assertTrue(inputPos.getDetailLevel() < thisPos.getDetailLevel()); + LodUtil.assertTrue(inputPos.overlapsExactly(this.getSectionPos()), "input source at pos: "+inputPos+" doesn't overlap with this source's pos: "+thisPos); if (inputSource.isEmpty()) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java index 819e37ecb..e2c73584d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java @@ -412,10 +412,6 @@ public class FullDataFileHandler implements IFullDataSourceProvider return; } - // can happen if data source caching isn't working correctly - LodUtil.assertTrue(existingFile.pos.equals(existingFullDataSource.getSectionPos()), "Data source returned the wrong position, pooled data source: ["+usePooledDataSources+"]. Expected: ["+existingFile.pos+"] actual: ["+existingFullDataSource.getSectionPos()+"]."); - - if (showFullDataFileSampling) { DebugRenderer.makeParticle(new DebugRenderer.BoxParticle( @@ -433,11 +429,10 @@ public class FullDataFileHandler implements IFullDataSourceProvider //throw e; } - - // return the pooled data source if necessary - if (usePooledDataSources) + // pooling temporary data sources massively reduces garbage collector overhead when just sampling (going from ~8 GB/sec to ~90 MB/sec) + if (usePooledDataSources && !existingFile.cacheLoadingDataSource) { - // pooling temporary data sources massively reduces garbage collector overhead when just sampling (going from ~8 GB/sec to ~90 MB/sec) + existingFile.clearCachedDataSource(); // get the data loader AbstractFullDataSourceLoader dataSourceLoader; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java index 9a2c9f74a..3c09bfa5b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java @@ -45,6 +45,7 @@ import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; import com.seibel.distanthorizons.core.sql.MetaDataDto; import com.seibel.distanthorizons.core.util.AtomicsUtil; import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; import com.seibel.distanthorizons.core.util.threading.ThreadPools; @@ -53,6 +54,8 @@ import org.apache.logging.log4j.Logger; /** Represents a File that contains a {@link IFullDataSource}. */ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements IDebugRenderable { + public static final String FILE_SUFFIX = ".lod"; + private static final Logger LOGGER = DhLoggerBuilder.getLogger(FullDataMetaFile.class.getSimpleName()); // === Object lifetime tracking === @@ -86,11 +89,8 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I * This makes null checks simpler. */ private DataSourceReferenceTracker.FullDataSourceSoftRef cachedFullDataSourceRef = new DataSourceReferenceTracker.FullDataSourceSoftRef(this,null); - // two different load futures are used to - // prevent accidentally returning a pooled (non-cached) data source - private final AtomicReference> cachedDataSourceLoadFutureRef = new AtomicReference<>(null); - private final AtomicReference> pooledDataSourceLoadFutureRef = new AtomicReference<>(null); - + private final AtomicReference> dataSourceLoadFutureRef = new AtomicReference<>(null); + public volatile Boolean cacheLoadingDataSource = null; // === Concurrent Write tracking === private final AtomicReference writeQueueRef = new AtomicReference<>(new GuardedMultiAppendQueue()); @@ -171,6 +171,7 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I { this.cachedFullDataSourceRef.close(); this.cachedFullDataSourceRef.clear(); + this.cacheLoadingDataSource = null; } return dataExists; @@ -180,44 +181,18 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I public CompletableFuture getDataSourceWithoutCachingAsync() { return this.getOrLoadCachedDataSourceAsync(false); } public CompletableFuture getOrLoadCachedDataSourceAsync() { return this.getOrLoadCachedDataSourceAsync(true); } - /** - * Synchronized to help prevent issues where multiple threads try to read as cached and un-cached at the same time. - * Hopefully isn't necessary and could potentially be removed in the future. - */ - private synchronized CompletableFuture getOrLoadCachedDataSourceAsync(boolean cacheLoadingSource) + private CompletableFuture getOrLoadCachedDataSourceAsync(boolean cacheLoadingSource) { checkAndLogPhantomDataSourceLifeCycles(); - AtomicReference> dataSourceLoadFutureRef = cacheLoadingSource ? this.cachedDataSourceLoadFutureRef : this.pooledDataSourceLoadFutureRef; - - - - //========================// - // use the pre-existing // - // load future if present // - //========================// - - CompletableFuture preExistingLoadFuture = dataSourceLoadFutureRef.get(); - if (preExistingLoadFuture != null) - { - return preExistingLoadFuture; - } - - - - //========================// - // attempt to get the // - // cached data if present // - //========================// - - CompletableFuture potentialLoadFuture = null; - if (cacheLoadingSource) - { - potentialLoadFuture = this.getCachedDataSourceAndUpdateIfNeededAsync(); - } - + CompletableFuture potentialLoadFuture = this.getCachedDataSourceAsync(); if (potentialLoadFuture != null) { + if (cacheLoadingSource) + { + this.cacheLoadingDataSource = true; + } + // return the in-process future return potentialLoadFuture; } @@ -225,14 +200,14 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I { // there is no cached data, we'll have to load it - // create a new load future if necessary potentialLoadFuture = new CompletableFuture<>(); - if (!dataSourceLoadFutureRef.compareAndSet(null, potentialLoadFuture)) + if (!this.dataSourceLoadFutureRef.compareAndSet(null, potentialLoadFuture)) { // two threads attempted to start this job at the same time, only use the first future - // (shouldn't happen since this method is synchronized, but just in case) - potentialLoadFuture = dataSourceLoadFutureRef.get(); + potentialLoadFuture = this.dataSourceLoadFutureRef.get(); } + + this.cacheLoadingDataSource = cacheLoadingSource; } @@ -240,10 +215,7 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I final CompletableFuture dataSourceLoadFuture = potentialLoadFuture; if (!this.doesDtoExist) { - //==================// - // create a new DTO // - // and data source // - //==================// + // create a new DTO and data source this.fullDataSourceProvider.onDataFileCreatedAsync(this) .thenApply((fullDataSource) -> @@ -257,18 +229,16 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I return fullDataSource; }) - .thenCompose((fullDataSource) -> this.applyWriteQueueAndSaveAsync(fullDataSource, cacheLoadingSource)) + .thenCompose((fullDataSource) -> this.applyWriteQueueAndSaveAsync(fullDataSource)) .thenAccept((fullDataSource) -> { dataSourceLoadFuture.complete(fullDataSource); - dataSourceLoadFutureRef.set(null); + this.dataSourceLoadFutureRef.set(null); }); } else { - //=========================// - // load the data from file // - //=========================// + // load the existing Meta file and data source if (this.baseMetaData == null) { @@ -282,54 +252,65 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I // load the data source CompletableFuture.supplyAsync(() -> - { - // Load the file. - IFullDataSource fullDataSource; - try (InputStream inputStream = this.getInputStream(); - DhDataInputStream compressedStream = new DhDataInputStream(inputStream)) { - if (cacheLoadingSource) + // Load the file. + IFullDataSource fullDataSource; + try (InputStream inputStream = this.getInputStream(); + DhDataInputStream compressedStream = new DhDataInputStream(inputStream)) { - fullDataSource = this.fullDataSourceLoader.loadDataSource(this, compressedStream, this.level); + if (cacheLoadingSource) + { + fullDataSource = this.fullDataSourceLoader.loadDataSource(this, compressedStream, this.level); + } + else + { + fullDataSource = this.fullDataSourceLoader.loadTemporaryDataSource(this, compressedStream, this.level); + } } - else + catch (Exception ex) { - fullDataSource = this.fullDataSourceLoader.loadTemporaryDataSource(this, compressedStream, this.level); + LOGGER.error("Full Data Load error: "+ ex.getMessage(), ex); + + dataSourceLoadFuture.completeExceptionally(ex); + this.dataSourceLoadFutureRef.set(null); + + // can happen if there is a missing file or the file was incorrectly formatted, or terminated early + throw new CompletionException(ex); } - } - catch (Exception ex) + return fullDataSource; + }, executor) + .thenCompose((fullDataSource) -> this.applyWriteQueueAndSaveAsync(fullDataSource)) + .thenAccept((fullDataSource) -> { - LOGGER.error("Full Data Load error: "+ ex.getMessage(), ex); - - dataSourceLoadFuture.completeExceptionally(ex); - dataSourceLoadFutureRef.set(null); - - // can happen if there is a missing file or the file was incorrectly formatted, or terminated early - throw new CompletionException(ex); - } - return fullDataSource; - }, executor) - .thenCompose((fullDataSource) -> this.applyWriteQueueAndSaveAsync(fullDataSource, cacheLoadingSource)) - .thenAccept((fullDataSource) -> - { - dataSourceLoadFuture.complete(fullDataSource); - dataSourceLoadFutureRef.set(null); - }); + dataSourceLoadFuture.complete(fullDataSource); + this.dataSourceLoadFutureRef.set(null); + }); } else { // don't load anything if the provider has been shut down dataSourceLoadFuture.complete(null); - dataSourceLoadFutureRef.set(null); + this.dataSourceLoadFutureRef.set(null); return dataSourceLoadFuture; } } return dataSourceLoadFuture; } + + + /** @return returns null if {@link FullDataMetaFile#cachedFullDataSourceRef} is empty and no cached {@link IFullDataSource} exists. */ - private CompletableFuture getCachedDataSourceAndUpdateIfNeededAsync() + private CompletableFuture getCachedDataSourceAsync() { + // this data source is being written to, use the existing future + CompletableFuture dataSourceLoadFuture = this.dataSourceLoadFutureRef.get(); + if (dataSourceLoadFuture != null) + { + return dataSourceLoadFuture; + } + + // attempt to get the cached data source IFullDataSource cachedFullDataSource = this.cachedFullDataSourceRef.get(); if (cachedFullDataSource == null) @@ -353,7 +334,7 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I // Create a new future if one doesn't already exist CompletableFuture newFuture = new CompletableFuture<>(); - CompletableFuture oldFuture = AtomicsUtil.compareAndExchange(this.cachedDataSourceLoadFutureRef, null, newFuture); + CompletableFuture oldFuture = AtomicsUtil.compareAndExchange(this.dataSourceLoadFutureRef, null, newFuture); if (oldFuture != null) { @@ -368,17 +349,17 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I // wait for the update to finish before returning the data source CompletableFuture.supplyAsync(() -> cachedFullDataSource, executor) - .thenCompose((fullDataSource) -> this.applyWriteQueueAndSaveAsync(fullDataSource, true)) + .thenCompose((fullDataSource) -> this.applyWriteQueueAndSaveAsync(fullDataSource)) .thenAccept((fullDataSource) -> { newFuture.complete(fullDataSource); - this.cachedDataSourceLoadFutureRef.set(null); + this.dataSourceLoadFutureRef.set(null); }); } else { // don't update anything if the provider has been shut down - this.cachedDataSourceLoadFutureRef.set(null); + this.dataSourceLoadFutureRef.set(null); newFuture.complete(null); } @@ -518,7 +499,7 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I color = Color.YELLOW; } } - else if (this.cachedDataSourceLoadFutureRef.get() != null) + else if (this.dataSourceLoadFutureRef.get() != null) { color = Color.BLUE; } @@ -553,7 +534,7 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I * and stores the result in {@link FullDataMetaFile#cachedFullDataSourceRef}. */ @SuppressWarnings("resource") // due to DataObjTracker and DataObjSoftTracker being created outside a try-catch block - private CompletableFuture applyWriteQueueAndSaveAsync(IFullDataSource fullDataSourceToUpdate, boolean cacheLoadingSource) + private CompletableFuture applyWriteQueueAndSaveAsync(IFullDataSource fullDataSourceToUpdate) { CompletableFuture completionFuture = new CompletableFuture<>(); @@ -607,13 +588,8 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I } - if (cacheLoadingSource) + if (this.cacheLoadingDataSource) { - if (fullDataSource != null) - { - LodUtil.assertTrue(this.pos.equals(fullDataSource.getSectionPos()), "Attempting to cache a datasource with the wrong position. Meta file pos: [" + this.pos + "], data source pos: [" + fullDataSource.getSectionPos() + "]."); - } - // save the updated data source this.cachedFullDataSourceRef = new DataSourceReferenceTracker.FullDataSourceSoftRef(this, fullDataSource); } @@ -625,7 +601,7 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I if (this.needsUpdate) { // another update was requested while this update was being processed - if (cacheLoadingSource) + if (this.cacheLoadingDataSource) { this.getOrLoadCachedDataSourceAsync(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/ThreadUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/ThreadUtil.java index eacd25811..63a102c64 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/ThreadUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/ThreadUtil.java @@ -24,7 +24,6 @@ import com.seibel.distanthorizons.core.config.types.ConfigEntry; import com.seibel.distanthorizons.core.util.threading.DhThreadFactory; import com.seibel.distanthorizons.core.util.threading.RateLimitedThreadPoolExecutor; import com.seibel.distanthorizons.core.util.threading.ThreadPools; -import com.seibel.distanthorizons.coreapi.ModInfo; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -39,7 +38,8 @@ public class ThreadUtil { private static final Logger LOGGER = LogManager.getLogger(); - public static String THREAD_NAME_PREFIX = ModInfo.THREAD_NAME_PREFIX; + /** The prefix isn't strictly required, but makes debugging and profiling much easier. */ + public static String THREAD_NAME_PREFIX = "DH-"; public static int MINIMUM_RELATIVE_PRIORITY = -4; public static int DEFAULT_RELATIVE_PRIORITY = 0; From b4ea632b9316dd4ddc73fd705150c9e6d22d812a Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 18 Dec 2023 23:10:39 +0500 Subject: [PATCH 074/205] Add kick trigger on repeated rate limit hitting Remove some unused code --- .../distanthorizons/core/config/Config.java | 43 +++++-- .../types/enums/EConfigEntryAppearance.java | 2 +- .../WorldRemoteGenerationQueue.java | 6 +- .../core/level/DhServerLevel.java | 105 ++++-------------- .../config/AbstractMultiplayerConfig.java | 4 +- .../multiplayer/config/MultiplayerConfig.java | 8 +- .../MultiplayerConfigChangeListener.java | 2 +- .../multiplayer/server/ServerPlayerState.java | 25 ++++- .../server/ServersideMultiplayerConfig.java | 4 +- .../core/network/IConnection.java | 8 +- .../core/network/NetworkEventSource.java | 7 +- .../SupplierBasedConcurrencyLimiter.java | 41 +++++++ .../SupplierBasedRateLimiter.java | 44 ++++++++ .../assets/distanthorizons/lang/en_us.json | 2 +- 14 files changed, 184 insertions(+), 117 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedConcurrencyLimiter.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedRateLimiter.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 04e487749..bae7689e6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -863,16 +863,6 @@ public class Config + "") .build(); - public static ConfigEntry requestRateLimit = new ConfigEntry.Builder() - .setServersideShortName("requestRateLimit") - .setMinDefaultMax(1, 20, 100) - .comment("" - + "Limits the amount of sent/processed LOD requests concurrently. \n" - + "\n" - + "Note: Server can set its own rate limit. \n" - + "") - .build(); - public static ConfigEntry enableRealTimeUpdates = new ConfigEntry.Builder() .setServersideShortName("enableRealTimeUpdates") .set(false) @@ -881,7 +871,7 @@ public class Config + "") .build(); - // Disabled, previous implementation is too terrible to continue using it + /** Disabled, previous implementation is too terrible to continue using it. */ private static ConfigEntry enablePostRelogUpdate = new ConfigEntry.Builder() .setServersideShortName("enablePostRelogUpdate") .set(false) @@ -897,6 +887,37 @@ public class Config + "The port on the server that's used for sending LOD data." + "") .build(); + + public static ConfigEntry rateLimitHitTolerance = new ConfigEntry.Builder() + .setServersideShortName("rateLimitHitTolerance") + .setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) + .setMinDefaultMax(1, 10, 100) + .comment("" + + "Amount of rate/concurrency limit hits in one second before disconnecting the offending clients. \n" + + "Warning: too low values can cause slower clients to disconnect prematurely.\n" + + "This setting is server-only; it does not have effect on client.\n" + + "") + .build(); + + public static ConfigEntry fullDataRequestConcurrencyLimit = new ConfigEntry.Builder() + .setServersideShortName("fullDataRequestConcurrencyLimit") + .setMinDefaultMax(1, 20, 100) + .comment("" + + "Limits the amount of sent/processed LOD requests concurrently on server, per player. \n" + + "") + .build(); + + /** + * Intentionally disabled. + * @see #enablePostRelogUpdate + */ + private static ConfigEntry fullDataChangeSummaryRequestRateLimit = new ConfigEntry.Builder() + .setServersideShortName("fullDataChangeSummaryRequestRateLimit") + .setMinDefaultMax(1, 20, 100) + .comment("" + + "Limits the amount of LOD updates the client can check within a second. \n" + + "") + .build(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/types/enums/EConfigEntryAppearance.java b/core/src/main/java/com/seibel/distanthorizons/core/config/types/enums/EConfigEntryAppearance.java index 24dd3fd51..6366f57b0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/types/enums/EConfigEntryAppearance.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/types/enums/EConfigEntryAppearance.java @@ -32,7 +32,7 @@ public enum EConfigEntryAppearance /** Will only show the option in the UI. The option will be reverted on game restart */ ONLY_IN_GUI(true, false), /** Only show the option in the file. There would be no way to access it using the UI */ - ONLY_IN_FILE(true, false), + ONLY_IN_FILE(false, true), /** The option is only available via code. Generally this is only used for deprecated options. */ ONLY_IN_API(false, false); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index e06204bfc..0fafb7e49 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -102,7 +102,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug if (generatorClosingFuture != null || !networkState.getClient().isReady()) return; while (getWaitingTaskCount() > getInProgressTaskCount() - && getInProgressTaskCount() < this.networkState.config.fullDataRequestRateLimit + && getInProgressTaskCount() < this.networkState.config.fullDataRequestConcurrencyLimit && pendingTasksSemaphore.tryAcquire()) { sendNewRequest(targetPos); @@ -112,7 +112,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug List posList = waitingTasks.entrySet().stream() .filter(task -> task.getValue().request == null && task.getValue().priority == 0) .sorted((x, y) -> posDistanceSquared(targetPos, x.getKey()) - posDistanceSquared(targetPos, y.getKey())) - .limit(this.networkState.config.fullDataRequestRateLimit) + .limit(this.networkState.config.fullDataRequestConcurrencyLimit) .map(Map.Entry::getKey) .collect(Collectors.toList()); if (posList.isEmpty()) { @@ -239,7 +239,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug { ArrayList lines = new ArrayList<>(); lines.add("World Remote Generation Queue ["+level.getClientLevelWrapper().getDimensionType().getDimensionName()+"]"); - lines.add("Requests: "+this.finishedRequests+" / "+(this.getWaitingTaskCount() + this.finishedRequests.get())+" (failed: "+ this.failedRequests+", rate limit: "+this.networkState.config.fullDataRequestRateLimit+")"); + lines.add("Requests: "+this.finishedRequests+" / "+(this.getWaitingTaskCount() + this.finishedRequests.get())+" (failed: "+ this.failedRequests+", rate limit: "+this.networkState.config.fullDataRequestConcurrencyLimit +")"); return lines.toArray(new String[0]); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index bd0066c86..60b45d10e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -32,7 +32,6 @@ import com.seibel.distanthorizons.core.multiplayer.server.RemotePlayerConnection import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkServer; import com.seibel.distanthorizons.core.network.exceptions.InvalidSectionPosException; -import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; import com.seibel.distanthorizons.core.network.messages.base.CancelMessage; import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceRequestMessage; @@ -97,63 +96,31 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel { this.eventSource.registerHandler(FullDataSourceRequestMessage.class, remotePlayerConnectionHandler.currentLevelOnly(this, (msg, serverPlayerState) -> { - if (msg.changedOnly) + if (!serverPlayerState.config.isDistantGenerationEnabled()) { - if (!serverPlayerState.config.isPostRelogUpdateEnabled()) - { - msg.sendResponse(new RequestRejectedException("Operation is disabled from config.")); - return; - } - - FullDataMetaFile metaFile = serverside.dataFileHandler.getFileIfExist(msg.dhSectionPos); - if (metaFile == null) - { - msg.sendResponse(new InvalidSectionPosException("Not generated section pos: "+msg.dhSectionPos)); - return; - } - - metaFile.getOrLoadCachedDataSourceAsync().thenAccept(source -> { - if (!(source instanceof CompleteFullDataSource)) - { - msg.sendResponse(new InvalidSectionPosException("Not generated section pos: "+msg.dhSectionPos)); - return; - } - - msg.sendResponse(new FullDataSourceResponseMessage((CompleteFullDataSource) source, this)); - }); + msg.sendResponse(new RequestRejectedException("Operation is disabled from config.")); + return; } - else + + if (!serverPlayerState.fullDataRequestConcurrencyLimiter.tryAcquire(msg)) + return; + + while (true) { - if (!serverPlayerState.config.isDistantGenerationEnabled()) - { - msg.sendResponse(new RequestRejectedException("Operation is disabled from config.")); - return; - } - - if (serverPlayerState.pendingFullDataRequests.incrementAndGet() > serverPlayerState.config.getFullDataRequestRateLimit()) - { - serverPlayerState.pendingFullDataRequests.decrementAndGet(); - msg.sendResponse(new RateLimitedException("Max concurrent requests: " + serverPlayerState.config.getFullDataRequestRateLimit())); - return; - } - - while (true) - { - IncompleteDataSourceEntry entry = incompleteDataSources.computeIfAbsent(msg.dhSectionPos, pos -> { - IncompleteDataSourceEntry newEntry = new IncompleteDataSourceEntry(); - serverside.dataFileHandler.readAsync(msg.dhSectionPos).thenAccept(fullDataSource -> { - newEntry.fullDataSource = fullDataSource; - }); - return newEntry; + IncompleteDataSourceEntry entry = incompleteDataSources.computeIfAbsent(msg.dhSectionPos, pos -> { + IncompleteDataSourceEntry newEntry = new IncompleteDataSourceEntry(); + serverside.dataFileHandler.readAsync(msg.dhSectionPos).thenAccept(fullDataSource -> { + newEntry.fullDataSource = fullDataSource; }); - // If this fails, current entry is being drained and need to create another one - if (entry.requestCollectionSemaphore.tryAcquire()) - { - fullDataRequests.put(msg.futureId, entry); - entry.requestMessages.put(msg.futureId, msg); - entry.requestCollectionSemaphore.release(); - break; - } + return newEntry; + }); + // If this fails, current entry is being drained and need to create another one + if (entry.requestCollectionSemaphore.tryAcquire()) + { + fullDataRequests.put(msg.futureId, entry); + entry.requestMessages.put(msg.futureId, msg); + entry.requestCollectionSemaphore.release(); + break; } } })); @@ -165,32 +132,6 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel )); })); - this.eventSource.registerHandler(FullDataChangeSummaryRequestMessage.class, remotePlayerConnectionHandler.currentLevelOnly(this, (msg, serverPlayerState) -> - { - if (!serverPlayerState.config.isPostRelogUpdateEnabled()) - { - msg.sendResponse(new RequestRejectedException("Operation is disabled from config.")); - return; - } - - // Load files and check checksums - HashSet changedPosList = new HashSet<>(); - for (Map.Entry entry : msg.checksums.entrySet()) - { - FullDataMetaFile metaFile = serverside.dataFileHandler.getFileIfExist(entry.getKey()); - if (metaFile == null) - { - msg.sendResponse(new InvalidSectionPosException("Not generated section pos: "+entry.getKey())); - return; - } - - if (entry.getValue() != metaFile.baseMetaData.checksum) - changedPosList.add(entry.getKey()); - } - - msg.sendResponse(new FullDataChangeSummaryResponseMessage(changedPosList)); - })); - this.eventSource.registerHandler(CancelMessage.class, msg -> { IncompleteDataSourceEntry entry = this.fullDataRequests.remove(msg.futureId); @@ -199,7 +140,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); if (serverPlayerState != null) - serverPlayerState.pendingFullDataRequests.decrementAndGet(); + serverPlayerState.fullDataRequestConcurrencyLimiter.release(); entry.requestCollectionSemaphore.acquireUninterruptibly(Short.MAX_VALUE); if (entry.requestMessages.isEmpty()) @@ -257,7 +198,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel if (serverPlayerState == null) continue; - serverPlayerState.pendingFullDataRequests.decrementAndGet(); + serverPlayerState.fullDataRequestConcurrencyLimiter.release(); msg.sendResponse(new FullDataSourceResponseMessage(completeSource, this)); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java index e686ad0ad..006e90e2b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java @@ -7,7 +7,7 @@ public abstract class AbstractMultiplayerConfig implements INetworkObject { public abstract int getRenderDistanceRadius(); public abstract boolean isDistantGenerationEnabled(); - public abstract int getFullDataRequestRateLimit(); + public abstract int getFullDataRequestConcurrencyLimit(); public abstract boolean isRealTimeUpdatesEnabled(); public abstract boolean isPostRelogUpdateEnabled(); @@ -16,7 +16,7 @@ public abstract class AbstractMultiplayerConfig implements INetworkObject { out.writeInt(this.getRenderDistanceRadius()); out.writeBoolean(this.isDistantGenerationEnabled()); - out.writeInt(this.getFullDataRequestRateLimit()); + out.writeInt(this.getFullDataRequestConcurrencyLimit()); out.writeBoolean(this.isRealTimeUpdatesEnabled()); out.writeBoolean(this.isPostRelogUpdateEnabled()); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java index 787639f18..9b7d60cac 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java @@ -13,8 +13,8 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig public boolean distantGenerationEnabled = Config.Client.Advanced.WorldGenerator.enableDistantGeneration.get(); @Override public boolean isDistantGenerationEnabled() { return distantGenerationEnabled; } - public int fullDataRequestRateLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.requestRateLimit.get(); - @Override public int getFullDataRequestRateLimit() { return fullDataRequestRateLimit; } + public int fullDataRequestConcurrencyLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.fullDataRequestConcurrencyLimit.get(); + @Override public int getFullDataRequestConcurrencyLimit() { return fullDataRequestConcurrencyLimit; } public boolean realTimeUpdatesEnabled = Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates.get(); @Override public boolean isRealTimeUpdatesEnabled() { return realTimeUpdatesEnabled; } @@ -27,7 +27,7 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig { this.renderDistanceRadius = in.readInt(); this.distantGenerationEnabled = in.readBoolean(); - this.fullDataRequestRateLimit = in.readInt(); + this.fullDataRequestConcurrencyLimit = in.readInt(); this.realTimeUpdatesEnabled = in.readBoolean(); this.postRelogUpdateEnabled = in.readBoolean(); } @@ -37,7 +37,7 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig return "MultiplayerConfig{" + "renderDistance=" + renderDistanceRadius + ", distantGenerationEnabled=" + distantGenerationEnabled + - ", fullDataRequestRateLimit=" + fullDataRequestRateLimit + + ", fullDataRequestConcurrencyLimit=" + fullDataRequestConcurrencyLimit + ", realTimeUpdatesEnabled=" + realTimeUpdatesEnabled + ", postRelogUpdatesEnabled=" + postRelogUpdateEnabled + '}'; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java index 6dec4fd2a..1dc0dfe2c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java @@ -13,7 +13,7 @@ public class MultiplayerConfigChangeListener implements Closeable private static final ConfigEntry[] CONFIG_ENTRIES = new ConfigEntry[] { Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius, Config.Client.Advanced.WorldGenerator.enableDistantGeneration, - Config.Client.Advanced.Multiplayer.ServerNetworking.requestRateLimit, + Config.Client.Advanced.Multiplayer.ServerNetworking.fullDataRequestConcurrencyLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates, //Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate }; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index 3429107a2..f22a15567 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -1,17 +1,36 @@ package com.seibel.distanthorizons.core.multiplayer.server; import com.seibel.distanthorizons.core.network.IConnection; +import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataChangeSummaryRequestMessage; +import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedConcurrencyLimiter; +import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateLimiter; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; +import org.jetbrains.annotations.NotNull; -import java.util.concurrent.atomic.AtomicInteger; +import static com.seibel.distanthorizons.core.config.Config.Client.Advanced.Multiplayer.ServerNetworking; public class ServerPlayerState -{ +{ public IServerPlayerWrapper serverPlayer; public IConnection connection; + @NotNull public ServersideMultiplayerConfig config = new ServersideMultiplayerConfig(); - public final AtomicInteger pendingFullDataRequests = new AtomicInteger(); + + public final SupplierBasedRateLimiter rateLimitKickTrigger = new SupplierBasedRateLimiter<>( + () -> ServerNetworking.rateLimitHitTolerance.get(), + ignored -> this.connection.disconnect("You have been repeatedly exceeding rate/concurrency limits.") + ); + + public final SupplierBasedConcurrencyLimiter fullDataRequestConcurrencyLimiter = new SupplierBasedConcurrencyLimiter<>( + () -> ServerNetworking.fullDataRequestConcurrencyLimit.get(), + msg -> { + msg.sendResponse(new RateLimitedException("Max concurrent requests: " + config.getFullDataRequestConcurrencyLimit())); + this.rateLimitKickTrigger.tryAcquire(null); + } + ); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java index 5ae7472cf..4f9cb47d8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java @@ -25,9 +25,9 @@ public class ServersideMultiplayerConfig extends AbstractMultiplayerConfig } @Override - public int getFullDataRequestRateLimit() + public int getFullDataRequestConcurrencyLimit() { - return Math.min(clientConfig.fullDataRequestRateLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.requestRateLimit.get()); + return Math.min(clientConfig.fullDataRequestConcurrencyLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.fullDataRequestConcurrencyLimit.get()); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java b/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java index bc94845dc..c05dccd86 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java @@ -40,7 +40,13 @@ public interface IConnection default CompletableFuture sendRequest(FutureTrackableNetworkMessage msg, Class responseClass) { - return this.getRequestHandler().sendRequest(this, msg, responseClass); + CompletableFuture responseFuture = this.getRequestHandler().createRequest(this, msg, responseClass); + this.sendMessage(msg).whenComplete((ignored, throwable) -> + { + if (throwable != null) + responseFuture.completeExceptionally(throwable); + }); + return responseFuture; } default void disconnect(String reason) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index 77d3834fd..cd860c6ca 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -114,7 +114,7 @@ public abstract class NetworkEventSource } - protected CompletableFuture sendRequest(IConnection connection, FutureTrackableNetworkMessage msg, Class responseClass) + protected CompletableFuture createRequest(IConnection connection, FutureTrackableNetworkMessage msg, Class responseClass) { msg.setConnection(connection); @@ -148,11 +148,6 @@ public abstract class NetworkEventSource } // If passed until here, cancelling is up to the cleaning side - connection.sendMessage(msg).whenComplete((ignored, throwable) -> - { - if (throwable != null) - responseFuture.completeExceptionally(throwable); - }); return responseFuture; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedConcurrencyLimiter.java b/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedConcurrencyLimiter.java new file mode 100644 index 000000000..27e2316c0 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedConcurrencyLimiter.java @@ -0,0 +1,41 @@ +package com.seibel.distanthorizons.core.util.ratelimiting; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * Limits concurrent tasks based on given current limit supplier.
+ * If limit of concurrent tasks was exceeded, acquisitions will fail and the provided failure handler will be called instead. + * @param Type of the object used as context for failure handler. + */ +public class SupplierBasedConcurrencyLimiter +{ + private final Supplier maxConcurrentTasksSupplier; + private final Consumer onFailureConsumer; + + private final AtomicInteger pendingTasks = new AtomicInteger(); + + public SupplierBasedConcurrencyLimiter(Supplier maxConcurrentTasksSupplier, Consumer onFailureConsumer) + { + this.maxConcurrentTasksSupplier = maxConcurrentTasksSupplier; + this.onFailureConsumer = onFailureConsumer; + } + + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + public boolean tryAcquire(T context) + { + if (this.pendingTasks.incrementAndGet() > this.maxConcurrentTasksSupplier.get()) + { + this.onFailureConsumer.accept(context); + return false; + } + + return true; + } + + public void release() + { + this.pendingTasks.decrementAndGet(); + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedRateLimiter.java b/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedRateLimiter.java new file mode 100644 index 000000000..efa3813f2 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedRateLimiter.java @@ -0,0 +1,44 @@ +package com.seibel.distanthorizons.core.util.ratelimiting; + +import com.google.common.util.concurrent.RateLimiter; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +/** + * Limits rate of tasks based on given current limit supplier.
+ * If rate limit was exceeded, acquisitions will fail and the provided failure handler will be called instead. + * @param Type of the object used as context for failure handler. + */ +@SuppressWarnings("UnstableApiUsage") +public class SupplierBasedRateLimiter +{ + private final Supplier maxRateSupplier; + private final Consumer onFailureConsumer; + + private final RateLimiter rateLimiter = RateLimiter.create(1); + + public SupplierBasedRateLimiter(Supplier maxRateSupplier, Consumer onFailureConsumer) + { + this.maxRateSupplier = maxRateSupplier; + this.onFailureConsumer = onFailureConsumer; + } + + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + public boolean tryAcquire(T context) + { + return tryAcquire(1, context); + } + + public boolean tryAcquire(int permits, T context) + { + rateLimiter.setRate(maxRateSupplier.get()); + if (!rateLimiter.tryAcquire(permits)) + { + this.onFailureConsumer.accept(context); + return false; + } + + return true; + } +} diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index c51bfc844..8e553e697 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -347,7 +347,7 @@ "distanthorizons.config.client.advanced.multiplayer.serverNetworking": "Server Networking", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableServerNetworking": "Enable Server Networking", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.requestRateLimit": "Request rate limit", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.fullDataRequestConcurrencyLimit": "Full data request concurrency limit", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableRealTimeUpdates": "Enable Real Time Updates", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enablePostRelogUpdates": "Enable Post Relog Updates", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.serverPort": "Server Port", From 02aca6f044b67793dcfb65ba3164081e21cc2f09 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 19 Dec 2023 22:52:19 +0500 Subject: [PATCH 075/205] Allow section state requests to be controlled separately --- .../distanthorizons/core/config/Config.java | 8 +++++++ .../WorldRemoteGenerationQueue.java | 4 ++-- .../core/level/DhServerLevel.java | 9 +++----- .../config/AbstractMultiplayerConfig.java | 2 ++ .../multiplayer/config/MultiplayerConfig.java | 5 +++++ .../MultiplayerConfigChangeListener.java | 1 + .../multiplayer/server/ServerPlayerState.java | 13 ++++++++++-- .../server/ServersideMultiplayerConfig.java | 6 ++++++ .../protocol/NetworkChannelInitializer.java | 2 +- .../SupplierBasedRateLimiter.java | 21 +++++++++++++++++-- .../assets/distanthorizons/lang/en_us.json | 3 ++- 11 files changed, 60 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index bae7689e6..d661ac736 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -907,6 +907,14 @@ public class Config + "") .build(); + public static ConfigEntry genTaskPriorityRequestRateLimit = new ConfigEntry.Builder() + .setServersideShortName("genTaskPriorityRequestRateLimit") + .setMinDefaultMax(1, 50, 200) + .comment("" + + "Limits the amount of LOD sections that the client can request states for, per second. \n" + + "") + .build(); + /** * Intentionally disabled. * @see #enablePostRelogUpdate diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 0fafb7e49..f6ccd4056 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -112,7 +112,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug List posList = waitingTasks.entrySet().stream() .filter(task -> task.getValue().request == null && task.getValue().priority == 0) .sorted((x, y) -> posDistanceSquared(targetPos, x.getKey()) - posDistanceSquared(targetPos, y.getKey())) - .limit(this.networkState.config.fullDataRequestConcurrencyLimit) + .limit(this.networkState.config.genTaskPriorityRequestRateLimit) .map(Map.Entry::getKey) .collect(Collectors.toList()); if (posList.isEmpty()) { @@ -135,7 +135,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug entry.priority = mapEntry.getValue(); } } - catch (ChannelException | CancellationException ignored) + catch (ChannelException | CancellationException | RateLimitedException ignored) { } catch (Throwable e) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 60b45d10e..f7d135db4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -24,22 +24,18 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedF import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IIncompleteFullDataSource; -import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile; import com.seibel.distanthorizons.core.file.fullDatafile.IFullDataSourceProvider; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState; import com.seibel.distanthorizons.core.multiplayer.server.RemotePlayerConnectionHandler; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkServer; -import com.seibel.distanthorizons.core.network.exceptions.InvalidSectionPosException; import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; import com.seibel.distanthorizons.core.network.messages.base.CancelMessage; import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceRequestMessage; import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceResponseMessage; import com.seibel.distanthorizons.core.network.messages.fullData.generation.priority.GenTaskPriorityRequestMessage; import com.seibel.distanthorizons.core.network.messages.fullData.generation.priority.GenTaskPriorityResponseMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataChangeSummaryRequestMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataChangeSummaryResponseMessage; import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataPartialUpdateMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhChunkPos; @@ -54,7 +50,6 @@ import com.seibel.distanthorizons.coreapi.util.math.Vec3d; import org.apache.logging.log4j.Logger; import javax.annotation.CheckForNull; -import java.util.HashSet; import java.util.Map; import java.util.concurrent.*; @@ -128,7 +123,9 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel this.eventSource.registerHandler(GenTaskPriorityRequestMessage.class, remotePlayerConnectionHandler.currentLevelOnly(this, (msg, serverPlayerState) -> { msg.sendResponse(new GenTaskPriorityResponseMessage( - this.serverside.dataFileHandler.getLoadStates(msg.posList) + this.serverside.dataFileHandler.getLoadStates(msg.posList.stream() + .limit(serverPlayerState.genTaskPriorityRequestRateLimiter.acquireOrDrain(msg.posList.size())) + ::iterator) )); })); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java index 006e90e2b..f1ad36d78 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java @@ -8,6 +8,7 @@ public abstract class AbstractMultiplayerConfig implements INetworkObject public abstract int getRenderDistanceRadius(); public abstract boolean isDistantGenerationEnabled(); public abstract int getFullDataRequestConcurrencyLimit(); + public abstract int getGenTaskPriorityRequestRateLimit(); public abstract boolean isRealTimeUpdatesEnabled(); public abstract boolean isPostRelogUpdateEnabled(); @@ -17,6 +18,7 @@ public abstract class AbstractMultiplayerConfig implements INetworkObject out.writeInt(this.getRenderDistanceRadius()); out.writeBoolean(this.isDistantGenerationEnabled()); out.writeInt(this.getFullDataRequestConcurrencyLimit()); + out.writeInt(this.getGenTaskPriorityRequestRateLimit()); out.writeBoolean(this.isRealTimeUpdatesEnabled()); out.writeBoolean(this.isPostRelogUpdateEnabled()); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java index 9b7d60cac..631a3ce03 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java @@ -16,6 +16,9 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig public int fullDataRequestConcurrencyLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.fullDataRequestConcurrencyLimit.get(); @Override public int getFullDataRequestConcurrencyLimit() { return fullDataRequestConcurrencyLimit; } + public int genTaskPriorityRequestRateLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.genTaskPriorityRequestRateLimit.get(); + @Override public int getGenTaskPriorityRequestRateLimit() { return genTaskPriorityRequestRateLimit; } + public boolean realTimeUpdatesEnabled = Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates.get(); @Override public boolean isRealTimeUpdatesEnabled() { return realTimeUpdatesEnabled; } @@ -28,6 +31,7 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig this.renderDistanceRadius = in.readInt(); this.distantGenerationEnabled = in.readBoolean(); this.fullDataRequestConcurrencyLimit = in.readInt(); + this.genTaskPriorityRequestRateLimit = in.readInt(); this.realTimeUpdatesEnabled = in.readBoolean(); this.postRelogUpdateEnabled = in.readBoolean(); } @@ -38,6 +42,7 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig "renderDistance=" + renderDistanceRadius + ", distantGenerationEnabled=" + distantGenerationEnabled + ", fullDataRequestConcurrencyLimit=" + fullDataRequestConcurrencyLimit + + ", genTaskPriorityRequestRateLimit=" + genTaskPriorityRequestRateLimit + ", realTimeUpdatesEnabled=" + realTimeUpdatesEnabled + ", postRelogUpdatesEnabled=" + postRelogUpdateEnabled + '}'; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java index 1dc0dfe2c..3a4b8ce50 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java @@ -14,6 +14,7 @@ public class MultiplayerConfigChangeListener implements Closeable Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius, Config.Client.Advanced.WorldGenerator.enableDistantGeneration, Config.Client.Advanced.Multiplayer.ServerNetworking.fullDataRequestConcurrencyLimit, + Config.Client.Advanced.Multiplayer.ServerNetworking.genTaskPriorityRequestRateLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates, //Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate }; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index f22a15567..f8e1ac49b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -3,7 +3,7 @@ package com.seibel.distanthorizons.core.multiplayer.server; import com.seibel.distanthorizons.core.network.IConnection; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataChangeSummaryRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.priority.GenTaskPriorityRequestMessage; import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedConcurrencyLimiter; import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateLimiter; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; @@ -27,11 +27,20 @@ public class ServerPlayerState public final SupplierBasedConcurrencyLimiter fullDataRequestConcurrencyLimiter = new SupplierBasedConcurrencyLimiter<>( () -> ServerNetworking.fullDataRequestConcurrencyLimit.get(), msg -> { - msg.sendResponse(new RateLimitedException("Max concurrent requests: " + config.getFullDataRequestConcurrencyLimit())); + msg.sendResponse(new RateLimitedException("Max concurrent full data requests: " + config.getFullDataRequestConcurrencyLimit())); this.rateLimitKickTrigger.tryAcquire(null); } ); + public final SupplierBasedRateLimiter genTaskPriorityRequestRateLimiter = new SupplierBasedRateLimiter<>( + () -> ServerNetworking.genTaskPriorityRequestRateLimit.get(), + msg -> { + // Shouldn't be called, but it's here just in case + msg.sendResponse(new RateLimitedException("Max section checks per second: " + config.getFullDataRequestConcurrencyLimit())); + this.rateLimitKickTrigger.tryAcquire(null); + } + ); + public ServerPlayerState(IServerPlayerWrapper serverPlayer) { this.serverPlayer = serverPlayer; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java index 4f9cb47d8..03a3547a8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java @@ -30,6 +30,12 @@ public class ServersideMultiplayerConfig extends AbstractMultiplayerConfig return Math.min(clientConfig.fullDataRequestConcurrencyLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.fullDataRequestConcurrencyLimit.get()); } + @Override + public int getGenTaskPriorityRequestRateLimit() + { + return Math.min(clientConfig.genTaskPriorityRequestRateLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.genTaskPriorityRequestRateLimit.get()); + } + @Override public boolean isRealTimeUpdatesEnabled() { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkChannelInitializer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkChannelInitializer.java index c72d8a1d4..d17209a7e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkChannelInitializer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkChannelInitializer.java @@ -43,7 +43,7 @@ public class NetworkChannelInitializer extends ChannelInitializer pipeline.addLast(new NetworkOutboundExceptionRouter()); // Decoder - pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, Integer.BYTES, 0, Integer.BYTES)); + pipeline.addLast(new LengthFieldBasedFrameDecoder(4194304 /* 4 MiB */, 0, Integer.BYTES, 0, Integer.BYTES)); pipeline.addLast(new MessageDecoder()); // Handler diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedRateLimiter.java b/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedRateLimiter.java index efa3813f2..99ec05b58 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedRateLimiter.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedRateLimiter.java @@ -27,12 +27,29 @@ public class SupplierBasedRateLimiter @SuppressWarnings("BooleanMethodIsAlwaysInverted") public boolean tryAcquire(T context) { - return tryAcquire(1, context); + return tryAcquire(context, 1); } - public boolean tryAcquire(int permits, T context) + public int acquireOrDrain(int permits) { rateLimiter.setRate(maxRateSupplier.get()); + + if (rateLimiter.tryAcquire(permits)) + return permits; + + int acquired = 0; + while ((permits /= 2) > 0) + { + if (rateLimiter.tryAcquire(permits)) + acquired += permits; + } + return acquired; + } + + public boolean tryAcquire(T context, int permits) + { + rateLimiter.setRate(maxRateSupplier.get()); + if (!rateLimiter.tryAcquire(permits)) { this.onFailureConsumer.accept(context); diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index 8e553e697..bf73ce835 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -347,7 +347,8 @@ "distanthorizons.config.client.advanced.multiplayer.serverNetworking": "Server Networking", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableServerNetworking": "Enable Server Networking", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.fullDataRequestConcurrencyLimit": "Full data request concurrency limit", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.fullDataRequestConcurrencyLimit": "Sections - request concurrency limit", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.genTaskPriorityRequestRateLimit": "Generation task priority - request rate limit", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableRealTimeUpdates": "Enable Real Time Updates", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enablePostRelogUpdates": "Enable Post Relog Updates", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.serverPort": "Server Port", From 904c1a7d71d86bc5df08d1c195f6ca3c9e8d5ebe Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 19 Dec 2023 23:20:23 +0500 Subject: [PATCH 076/205] Add protection against hang on shutdown --- .../generation/WorldRemoteGenerationQueue.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index f6ccd4056..cf18f1795 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -1,5 +1,6 @@ package com.seibel.distanthorizons.core.generation; +import com.google.common.base.Stopwatch; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; @@ -36,6 +37,8 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + private static final long SHUTDOWN_TIMEOUT_SECONDS = 5; + private final ClientNetworkState networkState; private final IDhClientLevel level; @@ -252,13 +255,16 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug public CompletableFuture startClosing(boolean cancelCurrentGeneration, boolean alsoInterruptRunning) { return this.generatorClosingFuture = CompletableFuture.runAsync(() -> { - while (!genTaskPriorityRequestSemaphore.tryAcquire()) + Stopwatch stopwatch = Stopwatch.createStarted(); + + do { if (genTaskPriorityRequest.cancel(false)) genTaskPriorityRequestSemaphore.release(); } - - while (!pendingTasksSemaphore.tryAcquire(Short.MAX_VALUE)) + while (!genTaskPriorityRequestSemaphore.tryAcquire() && stopwatch.elapsed(TimeUnit.SECONDS) < SHUTDOWN_TIMEOUT_SECONDS); + + do { for (WorldGenQueueEntry entry : this.waitingTasks.values()) { @@ -267,6 +273,10 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug pendingTasksSemaphore.release(); } } + while (!pendingTasksSemaphore.tryAcquire(Short.MAX_VALUE) && stopwatch.elapsed(TimeUnit.SECONDS) < SHUTDOWN_TIMEOUT_SECONDS); + + if (stopwatch.elapsed(TimeUnit.SECONDS) >= SHUTDOWN_TIMEOUT_SECONDS) + LOGGER.warn("Generation queue for " + level.getLevelWrapper() + " did not shutdown in " + SHUTDOWN_TIMEOUT_SECONDS + " seconds! Some unfinished tasks might be left hanging."); }); } From ff8b1f24d0ce48b0d45c4be91430b0aace0cbc36 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 18 Dec 2023 19:17:14 +0500 Subject: [PATCH 077/205] Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core --- .../com/seibel/distanthorizons/api/DhApi.java | 17 +- .../distanthorizons/coreapi/ModInfo.java | 2 + .../core/api/internal/SharedApi.java | 76 +++++--- .../interfaces/IIncompleteFullDataSource.java | 4 +- .../fullDatafile/FullDataFileHandler.java | 11 +- .../file/fullDatafile/FullDataMetaFile.java | 162 ++++++++++-------- .../distanthorizons/core/util/ThreadUtil.java | 4 +- 7 files changed, 179 insertions(+), 97 deletions(-) diff --git a/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java b/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java index 3cabd3b6b..a069e5da3 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java @@ -52,7 +52,7 @@ public class DhApi { /** * If you can see this Java Doc, this can be ignored.
- * This is just to you know that Javadocs are available and that you should use the API jar + * This is just to let you know that Javadocs are available and that you should use the API jar * instead of the full mod jar.

* * Note: Don't use this string in your code. It may change and is only for reference. @@ -118,7 +118,11 @@ public class DhApi + //==================// // always available // + //==================// + + // interfaces // /** * Used to bind/unbind Distant Horizons Api events. @@ -139,6 +143,8 @@ public class DhApi public static final IOverrideInjector overrides = OverrideInjector.INSTANCE; + // getters // + /** * This version should only be updated when breaking changes are introduced to the Distant Horizons API. * @since API 1.0.0 @@ -174,4 +180,13 @@ public class DhApi */ public static int getNetworkProtocolVersion() { return ModInfo.PROTOCOL_VERSION; } + + // methods // + + /** + * Returns true if the thread this method was called from is owned by Distant Horizons. + * @since API 1.1.0 + */ + public static boolean isDhThread() { return Thread.currentThread().getName().startsWith(ModInfo.THREAD_NAME_PREFIX); } + } diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java index 9283d700a..8e35bebcd 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java @@ -48,5 +48,7 @@ public final class ModInfo public static final String NETWORKING_RESOURCE_NAMESPACE = "distant_horizons"; public static final String MULTIVERSE_PLUGIN_NAMESPACE = "world_control"; + /** All DH owned threads should start with this string to allow for easier debugging and profiling. */ + public static String THREAD_NAME_PREFIX = "DH-"; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java index fcc4f5f47..b77a95be6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java @@ -25,8 +25,10 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.generation.DhLightingEngine; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; +import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.objects.Pair; import com.seibel.distanthorizons.core.util.threading.ThreadPools; import com.seibel.distanthorizons.core.world.*; @@ -52,14 +54,18 @@ public class SharedApi private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); private static final Set UPDATING_CHUNK_SET = ConcurrentHashMap.newKeySet(); - private static final int MAX_UPDATING_CHUNK_COUNT_PER_THREAD = 100; + /** how many chunks can be queued for updating per thread, used to prevent updates from infinitely pilling up if the user flys around extremely fast */ + private static final int MAX_UPDATING_CHUNK_COUNT_PER_THREAD = 500; + private static final int MIN_MS_BETWEEN_OVERLOADED_LOG_MESSAGE = 5_000; + private static final Timer CHUNK_UPDATE_TIMER = new Timer(); private static AbstractDhWorld currentWorld; private static int lastWorldGenTickDelta = 0; + private static long lastOverloadedLogMessageMsTime = 0; - private static final Timer CHUNK_UPDATE_TIMER = new Timer(); + public F3Screen.DynamicMessage f3Message; @@ -67,7 +73,14 @@ public class SharedApi // constructor // //=============// - private SharedApi() { } + private SharedApi() + { + this.f3Message = new F3Screen.DynamicMessage(() -> + { + int maxUpdateCount = MAX_UPDATING_CHUNK_COUNT_PER_THREAD * Config.Client.Advanced.MultiThreading.numberOfLodBuilderThreads.get(); + return LodUtil.formatLog("Queued chunk updates: " + UPDATING_CHUNK_SET.size() + " / " + maxUpdateCount); + }); + } public static void init() { Initializer.init(); } @@ -148,6 +161,10 @@ public class SharedApi public void applyChunkUpdate(IChunkWrapper chunkWrapper, ILevelWrapper level, boolean updateNeighborChunks) { + //========================// + // world and level checks // + //========================// + if (chunkWrapper == null) { // shouldn't happen, but just in case @@ -183,7 +200,42 @@ public class SharedApi } - // update the necessary chunk(s) + + //=====================// + // task limiting check // + //=====================// + + int currentQueueCount = UPDATING_CHUNK_SET.size(); + int maxQueueCount = MAX_UPDATING_CHUNK_COUNT_PER_THREAD * Config.Client.Advanced.MultiThreading.numberOfLodBuilderThreads.get(); + if (currentQueueCount >= maxQueueCount) + { + // The maximum number of chunks are already queued, don't add more. + // This is done to prevent overloading the system if the user fly's extremely fast and queues too many chunks + + long msBetweenLastLog = System.currentTimeMillis() - lastOverloadedLogMessageMsTime; + if (msBetweenLastLog >= MIN_MS_BETWEEN_OVERLOADED_LOG_MESSAGE) + { + lastOverloadedLogMessageMsTime = System.currentTimeMillis(); + LOGGER.warn("Too many chunks queued for updating, max queue count ["+maxQueueCount+"] (["+MAX_UPDATING_CHUNK_COUNT_PER_THREAD+"] per thread). Some LODs may not be updated or may be missing. Please move through the world slower, decrease your vanilla render distance, or increase the CPU load config."); + } + + return; + } + + // prevent duplicate update requests + if (UPDATING_CHUNK_SET.contains(chunkWrapper.getChunkPos())) + { + // this chunk is already being updated + return; + } + UPDATING_CHUNK_SET.add(chunkWrapper.getChunkPos()); + + + + //===============================// + // update the necessary chunk(s) // + //===============================// + if (!updateNeighborChunks) { // only update the center chunk @@ -228,22 +280,6 @@ public class SharedApi } private static void bakeChunkLightingAndSendToLevelAsync(IChunkWrapper chunkWrapper, @Nullable ArrayList neighbourChunkList, IDhLevel dhLevel) { - if (UPDATING_CHUNK_SET.size() >= MAX_UPDATING_CHUNK_COUNT_PER_THREAD * Config.Client.Advanced.MultiThreading.numberOfLodBuilderThreads.get()) - { - // The maximum number of chunks are already queued, don't add more. - // This is done to prevent overloading the system if the user flys extremely fast and queues too many chunks - return; - } - - // prevent duplicate update requests - if (UPDATING_CHUNK_SET.contains(chunkWrapper.getChunkPos())) - { - // this chunk is already being updated - return; - } - UPDATING_CHUNK_SET.add(chunkWrapper.getChunkPos()); - - // lighting the chunk needs to be done on a separate thread to prevent lagging any of the event threads ThreadPoolExecutor executor = ThreadPools.getLightPopulatorExecutor(); if (executor == null) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IIncompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IIncompleteFullDataSource.java index 50f16bd4b..b48bf785b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IIncompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IIncompleteFullDataSource.java @@ -36,8 +36,8 @@ public interface IIncompleteFullDataSource extends IFullDataSource { DhSectionPos inputPos = inputSource.getSectionPos(); DhSectionPos thisPos = this.getSectionPos(); - LodUtil.assertTrue(inputPos.getDetailLevel() < thisPos.getDetailLevel()); - LodUtil.assertTrue(inputPos.overlapsExactly(this.getSectionPos()), "input source at pos: "+inputPos+" doesn't overlap with this source's pos: "+thisPos); + LodUtil.assertTrue(inputPos.getDetailLevel() < thisPos.getDetailLevel(), "input data source at pos: ["+inputPos+"] has a lower detail level than this: ["+thisPos+"]."); + LodUtil.assertTrue(inputPos.overlapsExactly(this.getSectionPos()), "input source at pos: ["+inputPos+"] (converted to ["+inputPos.convertNewToDetailLevel(thisPos.getDetailLevel())+"]) doesn't overlap with this source's pos: ["+thisPos+"]."); if (inputSource.isEmpty()) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java index e2c73584d..819e37ecb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java @@ -412,6 +412,10 @@ public class FullDataFileHandler implements IFullDataSourceProvider return; } + // can happen if data source caching isn't working correctly + LodUtil.assertTrue(existingFile.pos.equals(existingFullDataSource.getSectionPos()), "Data source returned the wrong position, pooled data source: ["+usePooledDataSources+"]. Expected: ["+existingFile.pos+"] actual: ["+existingFullDataSource.getSectionPos()+"]."); + + if (showFullDataFileSampling) { DebugRenderer.makeParticle(new DebugRenderer.BoxParticle( @@ -429,10 +433,11 @@ public class FullDataFileHandler implements IFullDataSourceProvider //throw e; } - // pooling temporary data sources massively reduces garbage collector overhead when just sampling (going from ~8 GB/sec to ~90 MB/sec) - if (usePooledDataSources && !existingFile.cacheLoadingDataSource) + + // return the pooled data source if necessary + if (usePooledDataSources) { - existingFile.clearCachedDataSource(); + // pooling temporary data sources massively reduces garbage collector overhead when just sampling (going from ~8 GB/sec to ~90 MB/sec) // get the data loader AbstractFullDataSourceLoader dataSourceLoader; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java index 3c09bfa5b..9a2c9f74a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataMetaFile.java @@ -45,7 +45,6 @@ import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; import com.seibel.distanthorizons.core.sql.MetaDataDto; import com.seibel.distanthorizons.core.util.AtomicsUtil; import com.seibel.distanthorizons.core.util.LodUtil; -import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; import com.seibel.distanthorizons.core.util.threading.ThreadPools; @@ -54,8 +53,6 @@ import org.apache.logging.log4j.Logger; /** Represents a File that contains a {@link IFullDataSource}. */ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements IDebugRenderable { - public static final String FILE_SUFFIX = ".lod"; - private static final Logger LOGGER = DhLoggerBuilder.getLogger(FullDataMetaFile.class.getSimpleName()); // === Object lifetime tracking === @@ -89,8 +86,11 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I * This makes null checks simpler. */ private DataSourceReferenceTracker.FullDataSourceSoftRef cachedFullDataSourceRef = new DataSourceReferenceTracker.FullDataSourceSoftRef(this,null); - private final AtomicReference> dataSourceLoadFutureRef = new AtomicReference<>(null); - public volatile Boolean cacheLoadingDataSource = null; + // two different load futures are used to + // prevent accidentally returning a pooled (non-cached) data source + private final AtomicReference> cachedDataSourceLoadFutureRef = new AtomicReference<>(null); + private final AtomicReference> pooledDataSourceLoadFutureRef = new AtomicReference<>(null); + // === Concurrent Write tracking === private final AtomicReference writeQueueRef = new AtomicReference<>(new GuardedMultiAppendQueue()); @@ -171,7 +171,6 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I { this.cachedFullDataSourceRef.close(); this.cachedFullDataSourceRef.clear(); - this.cacheLoadingDataSource = null; } return dataExists; @@ -181,18 +180,44 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I public CompletableFuture getDataSourceWithoutCachingAsync() { return this.getOrLoadCachedDataSourceAsync(false); } public CompletableFuture getOrLoadCachedDataSourceAsync() { return this.getOrLoadCachedDataSourceAsync(true); } - private CompletableFuture getOrLoadCachedDataSourceAsync(boolean cacheLoadingSource) + /** + * Synchronized to help prevent issues where multiple threads try to read as cached and un-cached at the same time. + * Hopefully isn't necessary and could potentially be removed in the future. + */ + private synchronized CompletableFuture getOrLoadCachedDataSourceAsync(boolean cacheLoadingSource) { checkAndLogPhantomDataSourceLifeCycles(); - CompletableFuture potentialLoadFuture = this.getCachedDataSourceAsync(); + AtomicReference> dataSourceLoadFutureRef = cacheLoadingSource ? this.cachedDataSourceLoadFutureRef : this.pooledDataSourceLoadFutureRef; + + + + //========================// + // use the pre-existing // + // load future if present // + //========================// + + CompletableFuture preExistingLoadFuture = dataSourceLoadFutureRef.get(); + if (preExistingLoadFuture != null) + { + return preExistingLoadFuture; + } + + + + //========================// + // attempt to get the // + // cached data if present // + //========================// + + CompletableFuture potentialLoadFuture = null; + if (cacheLoadingSource) + { + potentialLoadFuture = this.getCachedDataSourceAndUpdateIfNeededAsync(); + } + if (potentialLoadFuture != null) { - if (cacheLoadingSource) - { - this.cacheLoadingDataSource = true; - } - // return the in-process future return potentialLoadFuture; } @@ -200,14 +225,14 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I { // there is no cached data, we'll have to load it + // create a new load future if necessary potentialLoadFuture = new CompletableFuture<>(); - if (!this.dataSourceLoadFutureRef.compareAndSet(null, potentialLoadFuture)) + if (!dataSourceLoadFutureRef.compareAndSet(null, potentialLoadFuture)) { // two threads attempted to start this job at the same time, only use the first future - potentialLoadFuture = this.dataSourceLoadFutureRef.get(); + // (shouldn't happen since this method is synchronized, but just in case) + potentialLoadFuture = dataSourceLoadFutureRef.get(); } - - this.cacheLoadingDataSource = cacheLoadingSource; } @@ -215,7 +240,10 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I final CompletableFuture dataSourceLoadFuture = potentialLoadFuture; if (!this.doesDtoExist) { - // create a new DTO and data source + //==================// + // create a new DTO // + // and data source // + //==================// this.fullDataSourceProvider.onDataFileCreatedAsync(this) .thenApply((fullDataSource) -> @@ -229,16 +257,18 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I return fullDataSource; }) - .thenCompose((fullDataSource) -> this.applyWriteQueueAndSaveAsync(fullDataSource)) + .thenCompose((fullDataSource) -> this.applyWriteQueueAndSaveAsync(fullDataSource, cacheLoadingSource)) .thenAccept((fullDataSource) -> { dataSourceLoadFuture.complete(fullDataSource); - this.dataSourceLoadFutureRef.set(null); + dataSourceLoadFutureRef.set(null); }); } else { - // load the existing Meta file and data source + //=========================// + // load the data from file // + //=========================// if (this.baseMetaData == null) { @@ -252,65 +282,54 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I // load the data source CompletableFuture.supplyAsync(() -> + { + // Load the file. + IFullDataSource fullDataSource; + try (InputStream inputStream = this.getInputStream(); + DhDataInputStream compressedStream = new DhDataInputStream(inputStream)) { - // Load the file. - IFullDataSource fullDataSource; - try (InputStream inputStream = this.getInputStream(); - DhDataInputStream compressedStream = new DhDataInputStream(inputStream)) + if (cacheLoadingSource) { - if (cacheLoadingSource) - { - fullDataSource = this.fullDataSourceLoader.loadDataSource(this, compressedStream, this.level); - } - else - { - fullDataSource = this.fullDataSourceLoader.loadTemporaryDataSource(this, compressedStream, this.level); - } + fullDataSource = this.fullDataSourceLoader.loadDataSource(this, compressedStream, this.level); } - catch (Exception ex) + else { - LOGGER.error("Full Data Load error: "+ ex.getMessage(), ex); - - dataSourceLoadFuture.completeExceptionally(ex); - this.dataSourceLoadFutureRef.set(null); - - // can happen if there is a missing file or the file was incorrectly formatted, or terminated early - throw new CompletionException(ex); + fullDataSource = this.fullDataSourceLoader.loadTemporaryDataSource(this, compressedStream, this.level); } - return fullDataSource; - }, executor) - .thenCompose((fullDataSource) -> this.applyWriteQueueAndSaveAsync(fullDataSource)) - .thenAccept((fullDataSource) -> + } + catch (Exception ex) { - dataSourceLoadFuture.complete(fullDataSource); - this.dataSourceLoadFutureRef.set(null); - }); + LOGGER.error("Full Data Load error: "+ ex.getMessage(), ex); + + dataSourceLoadFuture.completeExceptionally(ex); + dataSourceLoadFutureRef.set(null); + + // can happen if there is a missing file or the file was incorrectly formatted, or terminated early + throw new CompletionException(ex); + } + return fullDataSource; + }, executor) + .thenCompose((fullDataSource) -> this.applyWriteQueueAndSaveAsync(fullDataSource, cacheLoadingSource)) + .thenAccept((fullDataSource) -> + { + dataSourceLoadFuture.complete(fullDataSource); + dataSourceLoadFutureRef.set(null); + }); } else { // don't load anything if the provider has been shut down dataSourceLoadFuture.complete(null); - this.dataSourceLoadFutureRef.set(null); + dataSourceLoadFutureRef.set(null); return dataSourceLoadFuture; } } return dataSourceLoadFuture; } - - - /** @return returns null if {@link FullDataMetaFile#cachedFullDataSourceRef} is empty and no cached {@link IFullDataSource} exists. */ - private CompletableFuture getCachedDataSourceAsync() + private CompletableFuture getCachedDataSourceAndUpdateIfNeededAsync() { - // this data source is being written to, use the existing future - CompletableFuture dataSourceLoadFuture = this.dataSourceLoadFutureRef.get(); - if (dataSourceLoadFuture != null) - { - return dataSourceLoadFuture; - } - - // attempt to get the cached data source IFullDataSource cachedFullDataSource = this.cachedFullDataSourceRef.get(); if (cachedFullDataSource == null) @@ -334,7 +353,7 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I // Create a new future if one doesn't already exist CompletableFuture newFuture = new CompletableFuture<>(); - CompletableFuture oldFuture = AtomicsUtil.compareAndExchange(this.dataSourceLoadFutureRef, null, newFuture); + CompletableFuture oldFuture = AtomicsUtil.compareAndExchange(this.cachedDataSourceLoadFutureRef, null, newFuture); if (oldFuture != null) { @@ -349,17 +368,17 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I // wait for the update to finish before returning the data source CompletableFuture.supplyAsync(() -> cachedFullDataSource, executor) - .thenCompose((fullDataSource) -> this.applyWriteQueueAndSaveAsync(fullDataSource)) + .thenCompose((fullDataSource) -> this.applyWriteQueueAndSaveAsync(fullDataSource, true)) .thenAccept((fullDataSource) -> { newFuture.complete(fullDataSource); - this.dataSourceLoadFutureRef.set(null); + this.cachedDataSourceLoadFutureRef.set(null); }); } else { // don't update anything if the provider has been shut down - this.dataSourceLoadFutureRef.set(null); + this.cachedDataSourceLoadFutureRef.set(null); newFuture.complete(null); } @@ -499,7 +518,7 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I color = Color.YELLOW; } } - else if (this.dataSourceLoadFutureRef.get() != null) + else if (this.cachedDataSourceLoadFutureRef.get() != null) { color = Color.BLUE; } @@ -534,7 +553,7 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I * and stores the result in {@link FullDataMetaFile#cachedFullDataSourceRef}. */ @SuppressWarnings("resource") // due to DataObjTracker and DataObjSoftTracker being created outside a try-catch block - private CompletableFuture applyWriteQueueAndSaveAsync(IFullDataSource fullDataSourceToUpdate) + private CompletableFuture applyWriteQueueAndSaveAsync(IFullDataSource fullDataSourceToUpdate, boolean cacheLoadingSource) { CompletableFuture completionFuture = new CompletableFuture<>(); @@ -588,8 +607,13 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I } - if (this.cacheLoadingDataSource) + if (cacheLoadingSource) { + if (fullDataSource != null) + { + LodUtil.assertTrue(this.pos.equals(fullDataSource.getSectionPos()), "Attempting to cache a datasource with the wrong position. Meta file pos: [" + this.pos + "], data source pos: [" + fullDataSource.getSectionPos() + "]."); + } + // save the updated data source this.cachedFullDataSourceRef = new DataSourceReferenceTracker.FullDataSourceSoftRef(this, fullDataSource); } @@ -601,7 +625,7 @@ public class FullDataMetaFile extends AbstractMetaDataContainerFile implements I if (this.needsUpdate) { // another update was requested while this update was being processed - if (this.cacheLoadingDataSource) + if (cacheLoadingSource) { this.getOrLoadCachedDataSourceAsync(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/ThreadUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/ThreadUtil.java index 63a102c64..eacd25811 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/ThreadUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/ThreadUtil.java @@ -24,6 +24,7 @@ import com.seibel.distanthorizons.core.config.types.ConfigEntry; import com.seibel.distanthorizons.core.util.threading.DhThreadFactory; import com.seibel.distanthorizons.core.util.threading.RateLimitedThreadPoolExecutor; import com.seibel.distanthorizons.core.util.threading.ThreadPools; +import com.seibel.distanthorizons.coreapi.ModInfo; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -38,8 +39,7 @@ public class ThreadUtil { private static final Logger LOGGER = LogManager.getLogger(); - /** The prefix isn't strictly required, but makes debugging and profiling much easier. */ - public static String THREAD_NAME_PREFIX = "DH-"; + public static String THREAD_NAME_PREFIX = ModInfo.THREAD_NAME_PREFIX; public static int MINIMUM_RELATIVE_PRIORITY = -4; public static int DEFAULT_RELATIVE_PRIORITY = 0; From 502aaf6a8ab9f836eba921024f81ec565a40e78d Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Thu, 28 Dec 2023 18:58:50 +0500 Subject: [PATCH 078/205] Add a config to delay generation requests, for cases when gen tasks are not filled properly yet --- .../com/seibel/distanthorizons/core/config/Config.java | 8 ++++++++ .../core/generation/WorldRemoteGenerationQueue.java | 8 ++++++++ .../main/resources/assets/distanthorizons/lang/en_us.json | 3 ++- 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index d661ac736..00274d35c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -915,6 +915,14 @@ public class Config + "") .build(); + public static ConfigEntry fullDataRequestBeginDelay = new ConfigEntry.Builder() + .setMinDefaultMax(0, 3, 10) + .comment("" + + "Adds a delay in seconds before sending LOD requests, when generation is enabled. \n" + + "Increase this value if initial generation starts too far away. \n" + + "") + .build(); + /** * Intentionally disabled. * @see #enablePostRelogUpdate diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index cf18f1795..97d143c60 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -2,6 +2,7 @@ package com.seibel.distanthorizons.core.generation; import com.google.common.base.Stopwatch; import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.config.types.ConfigEntry; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker; @@ -26,6 +27,7 @@ import org.apache.logging.log4j.Logger; import javax.annotation.CheckForNull; import java.awt.*; +import java.time.Duration; import java.util.*; import java.util.List; import java.util.concurrent.*; @@ -42,6 +44,11 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug private final ClientNetworkState networkState; private final IDhClientLevel level; + // Used to prevent requests for section very far away, as result of request list not completely filled. + // Kinda a hack, since queue is not notified when file handler is done with feeding sections to generate + private static final ConfigEntry REQUEST_BEGIN_DELAY = Config.Client.Advanced.Multiplayer.ServerNetworking.fullDataRequestBeginDelay; + private final Stopwatch requestBeginStopwatch = Stopwatch.createStarted(); + private volatile CompletableFuture generatorClosingFuture = null; private final ConcurrentMap waitingTasks = new ConcurrentHashMap<>(); @@ -103,6 +110,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug public void startGenerationQueueAndSetTargetPos(DhBlockPos2D targetPos) { if (generatorClosingFuture != null || !networkState.getClient().isReady()) return; + if (requestBeginStopwatch.elapsed(TimeUnit.SECONDS) < REQUEST_BEGIN_DELAY.get()) return; while (getWaitingTaskCount() > getInProgressTaskCount() && getInProgressTaskCount() < this.networkState.config.fullDataRequestConcurrencyLimit diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index bf73ce835..391e77989 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -348,7 +348,8 @@ "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableServerNetworking": "Enable Server Networking", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.fullDataRequestConcurrencyLimit": "Sections - request concurrency limit", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.genTaskPriorityRequestRateLimit": "Generation task priority - request rate limit", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.genTaskPriorityRequestRateLimit": "Generation task priority - request rate limit", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.fullDataRequestBeginDelay": "Generation request begin delay", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableRealTimeUpdates": "Enable Real Time Updates", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enablePostRelogUpdates": "Enable Post Relog Updates", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.serverPort": "Server Port", From 8b0f6a44145935fbbe2dedefa4aa4a22fbfcf339 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 5 Jan 2024 22:08:16 +0500 Subject: [PATCH 079/205] Daemonize timers --- .../com/seibel/distanthorizons/core/api/internal/SharedApi.java | 2 +- .../eventHandlers/presets/AbstractPresetConfigEventHandler.java | 2 +- .../core/file/fullDatafile/GeneratedFullDataFileHandler.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java index b77a95be6..7b0d50288 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java @@ -58,7 +58,7 @@ public class SharedApi private static final int MAX_UPDATING_CHUNK_COUNT_PER_THREAD = 500; private static final int MIN_MS_BETWEEN_OVERLOADED_LOG_MESSAGE = 5_000; - private static final Timer CHUNK_UPDATE_TIMER = new Timer(); + private static final Timer CHUNK_UPDATE_TIMER = new Timer("DH-ChunkUpdateTimer", true); private static AbstractDhWorld currentWorld; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java index 6069072ab..5501cce56 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java @@ -119,7 +119,7 @@ public abstract class AbstractPresetConfigEventHandler worldGenQueueRef = new AtomicReference<>(null); From dbc1ad4cb7e512b62144d8170f9bc11a2f250178 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 5 Jan 2024 22:08:37 +0500 Subject: [PATCH 080/205] Add names to network threads --- .../seibel/distanthorizons/core/network/NetworkClient.java | 3 ++- .../seibel/distanthorizons/core/network/NetworkServer.java | 5 +++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index 813ab7ca0..3db6f26dc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -30,6 +30,7 @@ import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.util.concurrent.DefaultThreadFactory; import org.apache.logging.log4j.Logger; import java.net.InetSocketAddress; @@ -68,7 +69,7 @@ public class NetworkClient extends NetworkEventSource implements IConnection, Au /** Indicates whether the connection is established and first message is sent. */ public boolean isReady() { return ready; } - private final EventLoopGroup workerGroup = new NioEventLoopGroup(); + private final EventLoopGroup workerGroup = new NioEventLoopGroup(new DefaultThreadFactory("DH-Network - Client Thread")); private final Bootstrap clientBootstrap = new Bootstrap() .group(this.workerGroup) .channel(NioSocketChannel.class) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java index ab24e7516..a9a77fd55 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java @@ -33,6 +33,7 @@ import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; +import io.netty.util.concurrent.DefaultThreadFactory; import org.apache.logging.log4j.Logger; import java.util.concurrent.ConcurrentMap; @@ -45,8 +46,8 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable // TODO move to the config private final int port; - private final EventLoopGroup bossGroup = new NioEventLoopGroup(1); - private final EventLoopGroup workerGroup = new NioEventLoopGroup(); + private final EventLoopGroup bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("DH-Network - Server Boss Thread")); + private final EventLoopGroup workerGroup = new NioEventLoopGroup(new DefaultThreadFactory("DH-Network - Server Worker Thread")); private final AtomicBoolean isClosed = new AtomicBoolean(); private final ConcurrentMap connections = new MapMaker().weakKeys().weakValues().makeMap(); From 962f523af9eba5b5cc67fffa91320ee394291d0b Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 5 Jan 2024 22:37:27 +0500 Subject: [PATCH 081/205] Fix 1.16.5 and 1.17.1 builds --- .../com/seibel/distanthorizons/core/network/NetworkClient.java | 2 +- .../com/seibel/distanthorizons/core/network/NetworkServer.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index 3db6f26dc..be323d3d3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -69,7 +69,7 @@ public class NetworkClient extends NetworkEventSource implements IConnection, Au /** Indicates whether the connection is established and first message is sent. */ public boolean isReady() { return ready; } - private final EventLoopGroup workerGroup = new NioEventLoopGroup(new DefaultThreadFactory("DH-Network - Client Thread")); + private final EventLoopGroup workerGroup = new NioEventLoopGroup(0, new DefaultThreadFactory("DH-Network - Client Thread")); private final Bootstrap clientBootstrap = new Bootstrap() .group(this.workerGroup) .channel(NioSocketChannel.class) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java index a9a77fd55..d62a26876 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java @@ -47,7 +47,7 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable private final int port; private final EventLoopGroup bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("DH-Network - Server Boss Thread")); - private final EventLoopGroup workerGroup = new NioEventLoopGroup(new DefaultThreadFactory("DH-Network - Server Worker Thread")); + private final EventLoopGroup workerGroup = new NioEventLoopGroup(0, new DefaultThreadFactory("DH-Network - Server Worker Thread")); private final AtomicBoolean isClosed = new AtomicBoolean(); private final ConcurrentMap connections = new MapMaker().weakKeys().weakValues().makeMap(); From 8a309b56d919a61e18b21895cfc901b24c1057f1 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 13 Jan 2024 15:46:38 +0500 Subject: [PATCH 082/205] Fix crash when changing config while disconnected --- .../distanthorizons/core/network/IConnection.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java b/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java index c05dccd86..8a74d5c37 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java @@ -3,6 +3,7 @@ package com.seibel.distanthorizons.core.network; import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; +import io.netty.channel.ChannelException; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; @@ -23,7 +24,14 @@ public interface IConnection { CompletableFuture future = new CompletableFuture<>(); - this.getChannelContext().writeAndFlush(message).addListener(writeFuture -> + ChannelHandlerContext ctx = this.getChannelContext(); + if (ctx == null) + { + future.completeExceptionally(new ChannelException("Channel is closed.")); + return future; + } + + ctx.writeAndFlush(message).addListener(writeFuture -> { if (writeFuture.cause() != null) { From 573a284580acacbf12c347d3fc716537cc67182f Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 13 Jan 2024 19:44:48 +0500 Subject: [PATCH 083/205] Fix generation --- .../core/level/DhClientLevel.java | 4 +++ .../core/level/DhServerLevel.java | 29 ++++++++++--------- 2 files changed, 20 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 0150fb821..3ec690fba 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -85,6 +85,10 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel public DhClientLevel(AbstractSaveStructure saveStructure, IClientLevelWrapper clientLevelWrapper, @Nullable ClientNetworkState networkState) { this(saveStructure, clientLevelWrapper, null, true, networkState); } public DhClientLevel(AbstractSaveStructure saveStructure, IClientLevelWrapper clientLevelWrapper, @Nullable File fullDataSaveDirOverride, boolean enableRendering, @Nullable ClientNetworkState networkState) { + if (saveStructure.getFullDataFolder(clientLevelWrapper).mkdirs()) + { + LOGGER.warn("unable to create data folder."); + } this.levelWrapper = clientLevelWrapper; this.saveStructure = saveStructure; this.dataFileHandler = new RemoteFullDataFileHandler(this, saveStructure, fullDataSaveDirOverride); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 2a01dbcc6..0ecb29c05 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -104,9 +104,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel { IncompleteDataSourceEntry entry = incompleteDataSources.computeIfAbsent(msg.dhSectionPos, pos -> { IncompleteDataSourceEntry newEntry = new IncompleteDataSourceEntry(); - serverside.dataFileHandler.getAsync(msg.dhSectionPos).thenAccept(fullDataSource -> { - newEntry.fullDataSource = fullDataSource; - }); + this.trySetGeneratedDataSourceToEntry(newEntry, pos); return newEntry; }); // If this fails, current entry is being drained and need to create another one @@ -169,17 +167,9 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel { IncompleteDataSourceEntry entry = mapEntry.getValue(); - if (entry.fullDataSource == null) + if (entry.fullDataSource == null || entry.fullDataSource instanceof IIncompleteFullDataSource) continue; - if (entry.fullDataSource instanceof IIncompleteFullDataSource) - { - IIncompleteFullDataSource incompleteSource = (IIncompleteFullDataSource) entry.fullDataSource; - if (!incompleteSource.hasBeenPromoted()) - continue; - entry.fullDataSource = incompleteSource.tryPromotingToCompleteDataSource(); - } - LodUtil.assertTrue(entry.fullDataSource instanceof CompleteFullDataSource, "Invalid full data source"); incompleteDataSources.remove(mapEntry.getKey()); @@ -310,10 +300,23 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel @Override public boolean hasSkyLight() { return this.serverLevelWrapper.hasSkyLight(); } + private void trySetGeneratedDataSourceToEntry(IncompleteDataSourceEntry entry, DhSectionPos pos) + { + this.serverside.dataFileHandler.getAsync(pos).thenAccept(fullDataSource -> { + if (fullDataSource instanceof IIncompleteFullDataSource) + fullDataSource = ((IIncompleteFullDataSource) fullDataSource).tryPromotingToCompleteDataSource(); + if (fullDataSource instanceof CompleteFullDataSource) + entry.fullDataSource = fullDataSource; + }); + } + @Override public void onWorldGenTaskComplete(DhSectionPos pos) { - //TODO: Send packet to client + IncompleteDataSourceEntry entry = this.incompleteDataSources.get(pos); + if (entry == null) return; + + this.trySetGeneratedDataSourceToEntry(entry, pos); } private static class IncompleteDataSourceEntry From 61c516df1d9260ae4b725cfe259723f5f67e7cda Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 27 Jan 2024 19:45:30 +0500 Subject: [PATCH 084/205] Post-relog updates --- .../distanthorizons/core/config/Config.java | 19 +- .../fullDatafile/FullDataFileHandler.java | 4 +- .../RemoteFullDataFileHandler.java | 40 ++- .../WorldRemoteGenerationQueue.java | 253 +++------------ .../core/level/DhClientLevel.java | 75 +++-- .../core/level/DhServerLevel.java | 185 +++++++---- .../distanthorizons/core/level/IDhLevel.java | 2 + .../client/AbstractFullDataRequestQueue.java | 289 ++++++++++++++++++ .../client/FullDataRefreshQueue.java | 30 ++ .../config/AbstractMultiplayerConfig.java | 2 + .../multiplayer/config/MultiplayerConfig.java | 31 +- .../MultiplayerConfigChangeListener.java | 13 +- .../multiplayer/server/ServerPlayerState.java | 15 +- .../server/ServersideMultiplayerConfig.java | 18 +- .../core/network/IConnection.java | 7 + .../core/network/NetworkServer.java | 3 +- .../FullDataSourceRequestMessage.java | 46 +-- .../FullDataSourceResponseMessage.java | 66 ++-- .../core/sql/AbstractDataSourceRepo.java | 18 ++ .../SupplierBasedConcurrencyLimiter.java | 1 + 20 files changed, 746 insertions(+), 371 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/FullDataRefreshQueue.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 00274d35c..211508450 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -737,7 +737,7 @@ public class Config // deprecated and not implemented, can be made public if we ever re-implement it @Deprecated - private static ConfigEntry generationPriority = new ConfigEntry.Builder() + private static final ConfigEntry generationPriority = new ConfigEntry.Builder() .set(EGenerationPriority.NEAR_FIRST) .comment("" + "In what priority should fake chunks be generated outside the vanilla render distance? \n" @@ -871,8 +871,7 @@ public class Config + "") .build(); - /** Disabled, previous implementation is too terrible to continue using it. */ - private static ConfigEntry enablePostRelogUpdate = new ConfigEntry.Builder() + public static ConfigEntry enablePostRelogUpdate = new ConfigEntry.Builder() .setServersideShortName("enablePostRelogUpdate") .set(false) .comment("" @@ -903,7 +902,7 @@ public class Config .setServersideShortName("fullDataRequestConcurrencyLimit") .setMinDefaultMax(1, 20, 100) .comment("" - + "Limits the amount of sent/processed LOD requests concurrently on server, per player. \n" + + "Limits the amount of sent/processed LOD *generation* requests concurrently on server, per player. \n" + "") .build(); @@ -923,11 +922,19 @@ public class Config + "") .build(); + public static ConfigEntry postRelogUpdateConcurrencyLimit = new ConfigEntry.Builder() + .setServersideShortName("postRelogUpdateConcurrencyLimit") + .setMinDefaultMax(1, 50, 100) + .comment("" + + "Limits the amount of sent/processed LOD *update* requests concurrently on server, per player. \n" + + "") + .build(); + /** * Intentionally disabled. * @see #enablePostRelogUpdate */ - private static ConfigEntry fullDataChangeSummaryRequestRateLimit = new ConfigEntry.Builder() + private static final ConfigEntry fullDataChangeSummaryRequestRateLimit = new ConfigEntry.Builder() .setServersideShortName("fullDataChangeSummaryRequestRateLimit") .setMinDefaultMax(1, 20, 100) .comment("" @@ -1085,7 +1092,7 @@ public class Config // deprecated and not implemented, can be made public if we ever re-implement it @Deprecated - private static ConfigEntry rebuildTimes = new ConfigEntry.Builder() + private static final ConfigEntry rebuildTimes = new ConfigEntry.Builder() .set(EBufferRebuildTimes.NORMAL) .comment("" + "How frequently should vertex buffers (geometry) be rebuilt and sent to the GPU? \n" diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java index 5535568a0..09b848987 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataFileHandler.java @@ -57,13 +57,13 @@ public class FullDataFileHandler extends AbstractDataSourceHandler { + int checksum = Objects.requireNonNull(this.repo.getChecksumForSection(childPos)); + this.dataRefreshQueue.submitRequest(childPos, this.level::updateDataSourcesWithChunkData, checksum); + }); + + return fullDataSource; + } + + @Override + public void close() + { + if (this.dataRefreshQueue != null) + { + this.dataRefreshQueue.close(); + } + super.close(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 97d143c60..ee6766f38 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -3,71 +3,50 @@ package com.seibel.distanthorizons.core.generation; import com.google.common.base.Stopwatch; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.types.ConfigEntry; -import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker; import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult; import com.seibel.distanthorizons.core.level.IDhClientLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.logging.f3.F3Screen; +import com.seibel.distanthorizons.core.multiplayer.client.AbstractFullDataRequestQueue; import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; -import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceResponseMessage; import com.seibel.distanthorizons.core.network.messages.fullData.generation.priority.GenTaskPriorityRequestMessage; import com.seibel.distanthorizons.core.network.messages.fullData.generation.priority.GenTaskPriorityResponseMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhSectionPos; -import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; import com.seibel.distanthorizons.core.util.LodUtil; import io.netty.channel.ChannelException; import org.apache.logging.log4j.Logger; -import javax.annotation.CheckForNull; -import java.awt.*; -import java.time.Duration; import java.util.*; import java.util.List; import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; import java.util.stream.Collectors; -public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebugRenderable +public class WorldRemoteGenerationQueue extends AbstractFullDataRequestQueue implements IWorldGenerationQueue, IDebugRenderable { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - private static final long SHUTDOWN_TIMEOUT_SECONDS = 5; - - private final ClientNetworkState networkState; - private final IDhClientLevel level; - // Used to prevent requests for section very far away, as result of request list not completely filled. // Kinda a hack, since queue is not notified when file handler is done with feeding sections to generate private static final ConfigEntry REQUEST_BEGIN_DELAY = Config.Client.Advanced.Multiplayer.ServerNetworking.fullDataRequestBeginDelay; private final Stopwatch requestBeginStopwatch = Stopwatch.createStarted(); - private volatile CompletableFuture generatorClosingFuture = null; - - private final ConcurrentMap waitingTasks = new ConcurrentHashMap<>(); - private final Semaphore pendingTasksSemaphore = new Semaphore(Short.MAX_VALUE, true); - private CompletableFuture genTaskPriorityRequest = CompletableFuture.completedFuture(null); private final Semaphore genTaskPriorityRequestSemaphore = new Semaphore(1, true); - private final F3Screen.NestedMessage f3Message = new F3Screen.NestedMessage(this::f3Log); - private final AtomicInteger finishedRequests = new AtomicInteger(); - private final AtomicInteger failedRequests = new AtomicInteger(); - private final Set alreadyGeneratedPosHashSet = ConcurrentHashMap.newKeySet(); + @Override + protected int getRequestConcurrencyLimit() { return this.networkState.config.fullDataRequestConcurrencyLimit; } + + @Override + protected String getQueueName() { return "World Remote Generation Queue"; } + public WorldRemoteGenerationQueue(ClientNetworkState networkState, IDhClientLevel level) { - this.networkState = networkState; - this.level = level; - DebugRenderer.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showWorldGenQueue); + super(networkState, level, false, Config.Client.Advanced.Debugging.DebugWireframe.showWorldGenQueue); } @@ -85,65 +64,53 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug @Override public CompletableFuture submitGenTask(DhSectionPos sectionPos, byte requiredDataDetail, IWorldGenTaskTracker tracker) { - LodUtil.assertTrue(sectionPos.getDetailLevel() == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL, "Only highest-detail sections are allowed."); - - // check if this is a duplicate generation task - if (this.alreadyGeneratedPosHashSet.contains(sectionPos)) - { - // temporary solution to prevent generating the same section multiple times - LOGGER.trace("Duplicate generation section " + sectionPos + ". Skipping..."); - return CompletableFuture.completedFuture(WorldGenResult.CreateFail()); - } - this.alreadyGeneratedPosHashSet.add(sectionPos); - - WorldGenQueueEntry entry = new WorldGenQueueEntry(new CompletableFuture<>(), tracker); - waitingTasks.put(sectionPos, entry); - return entry.future; - } - - private int posDistanceSquared(DhBlockPos2D targetPos, DhSectionPos pos) - { - return (int) pos.getCenterBlockPos().distSquared(targetPos); + return super.submitRequest(sectionPos, tracker.getChunkDataConsumer()) + .thenApply(result -> result + ? WorldGenResult.CreateSuccess(sectionPos) + : WorldGenResult.CreateFail()); } @Override public void startGenerationQueueAndSetTargetPos(DhBlockPos2D targetPos) { - if (generatorClosingFuture != null || !networkState.getClient().isReady()) return; - if (requestBeginStopwatch.elapsed(TimeUnit.SECONDS) < REQUEST_BEGIN_DELAY.get()) return; - - while (getWaitingTaskCount() > getInProgressTaskCount() - && getInProgressTaskCount() < this.networkState.config.fullDataRequestConcurrencyLimit - && pendingTasksSemaphore.tryAcquire()) + if (this.requestBeginStopwatch.elapsed(TimeUnit.SECONDS) < REQUEST_BEGIN_DELAY.get()) { - sendNewRequest(targetPos); + return; + } + if (!super.tick(targetPos)) + { + return; } - if (genTaskPriorityRequestSemaphore.tryAcquire()) { - List posList = waitingTasks.entrySet().stream() + if (this.genTaskPriorityRequestSemaphore.tryAcquire()) { + List posList = this.waitingTasks.entrySet().stream() .filter(task -> task.getValue().request == null && task.getValue().priority == 0) - .sorted((x, y) -> posDistanceSquared(targetPos, x.getKey()) - posDistanceSquared(targetPos, y.getKey())) + .sorted((x, y) -> this.posDistanceSquared(targetPos, x.getKey()) - this.posDistanceSquared(targetPos, y.getKey())) .limit(this.networkState.config.genTaskPriorityRequestRateLimit) .map(Map.Entry::getKey) .collect(Collectors.toList()); if (posList.isEmpty()) { - genTaskPriorityRequestSemaphore.release(); + this.genTaskPriorityRequestSemaphore.release(); return; }; CompletableFuture request = this.networkState.getClient().sendRequest(new GenTaskPriorityRequestMessage(posList), GenTaskPriorityResponseMessage.class); - genTaskPriorityRequest = request; + this.genTaskPriorityRequest = request; request.handleAsync((response, throwable) -> { try { if (throwable != null) + { throw throwable; + } for (Map.Entry mapEntry : response.posList.entrySet()) { - WorldGenQueueEntry entry = waitingTasks.get(mapEntry.getKey()); + RequestQueueEntry entry = this.waitingTasks.get(mapEntry.getKey()); if (entry != null) + { entry.priority = mapEntry.getValue(); + } } } catch (ChannelException | CancellationException | RateLimitedException ignored) @@ -154,7 +121,7 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug LOGGER.error("Error while fetching gen task priorities", e); } - genTaskPriorityRequestSemaphore.release(); + this.genTaskPriorityRequestSemaphore.release(); return null; }); } @@ -163,168 +130,28 @@ public class WorldRemoteGenerationQueue implements IWorldGenerationQueue, IDebug @Override public void cancelGenTasks(Iterable positions) { - for (DhSectionPos pos : positions) - { - WorldGenQueueEntry entry = waitingTasks.remove(pos); - if (entry != null) - { - entry.future.cancel(false); - if (entry.request != null) - entry.request.cancel(false); - alreadyGeneratedPosHashSet.remove(pos); - } - } + super.cancelRequests(positions); } - private void sendNewRequest(DhBlockPos2D targetPos) - { - Map.Entry mapEntry = waitingTasks.entrySet().stream() - .filter(task -> task.getValue().request == null) - .reduce(null, (a, b) - -> a == null - || b.getValue().priority > a.getValue().priority - || (b.getValue().priority == a.getValue().priority && posDistanceSquared(targetPos, b.getKey()) < posDistanceSquared(targetPos, a.getKey())) - ? b : a); - if (mapEntry == null) - { - pendingTasksSemaphore.release(); - return; - } - - DhSectionPos sectionPos = mapEntry.getKey(); - WorldGenQueueEntry entry = mapEntry.getValue(); - - CompletableFuture request = this.networkState.getClient().sendRequest(new FullDataSourceRequestMessage(level.getLevelWrapper(), sectionPos), FullDataSourceResponseMessage.class); - entry.request = request; - request.handleAsync((response, throwable) -> - { - pendingTasksSemaphore.release(); - finishedRequests.incrementAndGet(); - - try - { - if (throwable != null) - throw throwable; - - waitingTasks.remove(sectionPos); - LOGGER.debug("FullDataSourceResponseMessage " + sectionPos); - - CompleteFullDataSource fullDataSource = response.getFullDataSource(sectionPos, level); - Consumer chunkDataConsumer = entry.tracker.getChunkDataConsumer(); - - // FIXME Why keeping a reference in first place - if (chunkDataConsumer == null) - return entry.future.cancel(false); - - fullDataSource.splitIntoChunkSizedAccessors(chunkDataConsumer); - response.getFullDataSourceLoader().returnPooledDataSource(fullDataSource); - } - catch (InvalidLevelException ignored) - { - // We're too late - } - catch (ChannelException | RateLimitedException e) - { - if (e instanceof RateLimitedException) - LOGGER.warn("Rate limited by server, re-queueing task [" + sectionPos + "]: " + e.getMessage()); - - entry.request = null; - finishedRequests.decrementAndGet(); - } - catch (CancellationException ignored) - { - finishedRequests.decrementAndGet(); - } - catch (Throwable e) - { - LOGGER.error("Error while fetching full data source", e); - failedRequests.incrementAndGet(); - return entry.future.complete(WorldGenResult.CreateFail()); - } - - return entry.future.complete(WorldGenResult.CreateSuccess(sectionPos)); - }); - } - - private String[] f3Log() - { - ArrayList lines = new ArrayList<>(); - lines.add("World Remote Generation Queue ["+level.getClientLevelWrapper().getDimensionType().getDimensionName()+"]"); - lines.add("Requests: "+this.finishedRequests+" / "+(this.getWaitingTaskCount() + this.finishedRequests.get())+" (failed: "+ this.failedRequests+", rate limit: "+this.networkState.config.fullDataRequestConcurrencyLimit +")"); - return lines.toArray(new String[0]); - } - - @Override - public int getWaitingTaskCount() { return this.waitingTasks.size(); } - @Override - public int getInProgressTaskCount() { return Short.MAX_VALUE - pendingTasksSemaphore.availablePermits(); } - @Override public CompletableFuture startClosing(boolean cancelCurrentGeneration, boolean alsoInterruptRunning) { - return this.generatorClosingFuture = CompletableFuture.runAsync(() -> { + return CompletableFuture.allOf(super.startClosing(alsoInterruptRunning), CompletableFuture.runAsync(() -> { Stopwatch stopwatch = Stopwatch.createStarted(); do { - if (genTaskPriorityRequest.cancel(false)) - genTaskPriorityRequestSemaphore.release(); - } - while (!genTaskPriorityRequestSemaphore.tryAcquire() && stopwatch.elapsed(TimeUnit.SECONDS) < SHUTDOWN_TIMEOUT_SECONDS); - - do - { - for (WorldGenQueueEntry entry : this.waitingTasks.values()) + if (this.genTaskPriorityRequest.cancel(false)) { - entry.future.cancel(alsoInterruptRunning); - if (entry.request != null && entry.request.cancel(alsoInterruptRunning)) - pendingTasksSemaphore.release(); + this.genTaskPriorityRequestSemaphore.release(); } } - while (!pendingTasksSemaphore.tryAcquire(Short.MAX_VALUE) && stopwatch.elapsed(TimeUnit.SECONDS) < SHUTDOWN_TIMEOUT_SECONDS); - + while (!this.genTaskPriorityRequestSemaphore.tryAcquire() && stopwatch.elapsed(TimeUnit.SECONDS) < SHUTDOWN_TIMEOUT_SECONDS); + if (stopwatch.elapsed(TimeUnit.SECONDS) >= SHUTDOWN_TIMEOUT_SECONDS) - LOGGER.warn("Generation queue for " + level.getLevelWrapper() + " did not shutdown in " + SHUTDOWN_TIMEOUT_SECONDS + " seconds! Some unfinished tasks might be left hanging."); - }); - } - - @Override - public void close() - { - f3Message.close(); - DebugRenderer.unregister(this, Config.Client.Advanced.Debugging.DebugWireframe.showWorldGenQueue); - } - - @Override - public void debugRender(DebugRenderer r) - { - for (Map.Entry mapEntry : waitingTasks.entrySet()) - { - r.renderBox(new DebugRenderer.Box(mapEntry.getKey(), -32f, 64f, 0.05f, - mapEntry.getValue().request != null ? Color.red - : mapEntry.getValue().priority == 3 ? Color.orange - : mapEntry.getValue().priority == 2 ? Color.cyan - : mapEntry.getValue().priority == 1 ? Color.blue - : Color.gray - )); - } - } - - private static class WorldGenQueueEntry - { - public final CompletableFuture future; - public final IWorldGenTaskTracker tracker; - - // Higher value = higher priority. - // Priority of 0 is reserved for unassigned value - public int priority = 0; - @CheckForNull - public CompletableFuture request; - - public WorldGenQueueEntry(CompletableFuture future, IWorldGenTaskTracker tracker) - { - this.future = future; - this.tracker = tracker; - } + { + LOGGER.warn("Priority request queue for " + this.level.getLevelWrapper() + " did not shutdown in " + SHUTDOWN_TIMEOUT_SECONDS + " seconds! It might be left hanging."); + } + })); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 3ec690fba..d69addbae 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -29,6 +29,7 @@ import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.generation.WorldRemoteGenerationQueue; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; +import com.seibel.distanthorizons.core.multiplayer.client.FullDataRefreshQueue; import com.seibel.distanthorizons.core.network.NetworkClient; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataPartialUpdateMessage; @@ -73,11 +74,15 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel private final ClientNetworkState networkState; @Nullable private final ScopedNetworkEventSource eventSource; + public final WorldGenModule worldGenModule; public final AppliedConfigState worldGeneratorEnabledConfig; + @Nullable + private final FullDataRefreshQueue dataRefreshQueue; + + - //=============// // constructor // //=============// @@ -91,21 +96,24 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel } this.levelWrapper = clientLevelWrapper; this.saveStructure = saveStructure; - this.dataFileHandler = new RemoteFullDataFileHandler(this, saveStructure, fullDataSaveDirOverride); - this.worldGeneratorEnabledConfig = new AppliedConfigState<>(Config.Client.Advanced.WorldGenerator.enableDistantGeneration); this.networkState = networkState; - this.worldGenModule = new WorldGenModule(dataFileHandler, this); if (networkState != null) { this.eventSource = new ScopedNetworkEventSource<>(networkState.getClient()); + this.dataRefreshQueue = new FullDataRefreshQueue(this, networkState); this.registerNetworkHandlers(); } else { this.eventSource = null; + this.dataRefreshQueue = null; } + this.dataFileHandler = new RemoteFullDataFileHandler(this, saveStructure, fullDataSaveDirOverride, this.dataRefreshQueue); + this.worldGeneratorEnabledConfig = new AppliedConfigState<>(Config.Client.Advanced.WorldGenerator.enableDistantGeneration); + this.worldGenModule = new WorldGenModule(this.dataFileHandler, this); + this.clientside = new ClientLevelModule(this); if (enableRendering) { @@ -123,7 +131,10 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel try { ChunkSizedFullDataAccessor fullDataAccessor = msg.getFullDataSource(this); - if (fullDataAccessor == null) return; + if (fullDataAccessor == null) + { + return; + } this.updateDataSourcesWithChunkData(fullDataAccessor); } @@ -145,6 +156,11 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel { this.chunkToLodBuilder.tick(); this.clientside.clientTick(); + + if (this.dataRefreshQueue != null) + { + this.dataRefreshQueue.tick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); + } } catch (Exception e) { @@ -152,37 +168,38 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel } } + @Override public void doWorldGen() { - boolean isClientUsable = networkState != null && !networkState.getClient().isClosed(); - boolean shouldDoWorldGen = isClientUsable && this.networkState.config.distantGenerationEnabled && clientside.isRendering(); - boolean isWorldGenRunning = worldGenModule.isWorldGenRunning(); + boolean isClientUsable = this.networkState != null && !this.networkState.getClient().isClosed(); + boolean shouldDoWorldGen = isClientUsable && this.networkState.config.distantGenerationEnabled && this.clientside.isRendering(); + boolean isWorldGenRunning = this.worldGenModule.isWorldGenRunning(); if (shouldDoWorldGen && !isWorldGenRunning) { // start world gen - worldGenModule.startWorldGen(this.dataFileHandler, new WorldGenState(this, this.networkState)); + this.worldGenModule.startWorldGen(this.dataFileHandler, new WorldGenState(this, this.networkState)); } else if (!shouldDoWorldGen && isWorldGenRunning) { // stop world gen - worldGenModule.stopWorldGen(this.dataFileHandler); + this.worldGenModule.stopWorldGen(this.dataFileHandler); } - if (worldGenModule.isWorldGenRunning()) + if (this.worldGenModule.isWorldGenRunning()) { - ClientLevelModule.ClientRenderState renderState = clientside.ClientRenderStateRef.get(); + ClientLevelModule.ClientRenderState renderState = this.clientside.ClientRenderStateRef.get(); if (renderState != null && renderState.quadtree != null) { - dataFileHandler.removeGenRequestIf(p -> !renderState.quadtree.isSectionPosInBounds(p)); + this.dataFileHandler.removeGenRequestIf(p -> !renderState.quadtree.isSectionPosInBounds(p)); } - worldGenModule.worldGenTick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); + this.worldGenModule.worldGenTick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); } } @Override public void render(Mat4f mcModelViewMatrix, Mat4f mcProjectionMatrix, float partialTicks, IProfilerWrapper profiler) { - clientside.render(mcModelViewMatrix, mcProjectionMatrix, partialTicks, profiler); + this.clientside.render(mcModelViewMatrix, mcProjectionMatrix, partialTicks, profiler); } @@ -192,35 +209,37 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel //================// @Override - public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper block) { return levelWrapper.computeBaseColor(pos, biome, block); } + public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper block) { return this.levelWrapper.computeBaseColor(pos, biome, block); } @Override - public IClientLevelWrapper getClientLevelWrapper() { return levelWrapper; } + public IClientLevelWrapper getClientLevelWrapper() { return this.levelWrapper; } @Override public void clearRenderCache() { - clientside.clearRenderCache(); + this.clientside.clearRenderCache(); } @Override - public ILevelWrapper getLevelWrapper() { return levelWrapper; } + public ILevelWrapper getLevelWrapper() { return this.levelWrapper; } @Override public void updateDataSourcesWithChunkData(ChunkSizedFullDataAccessor data) { this.clientside.updateDataSourcesWithChunkData(data); } @Override - public int getMinY() { return levelWrapper.getMinHeight(); } + public int getMinY() { return this.levelWrapper.getMinHeight(); } @Override public void close() { - if (worldGenModule != null) - worldGenModule.close(); - clientside.close(); + if (this.worldGenModule != null) + { + this.worldGenModule.close(); + } + this.clientside.close(); super.close(); - dataFileHandler.close(); - LOGGER.info("Closed " + DhClientLevel.class.getSimpleName() + " for " + levelWrapper); + this.dataFileHandler.close(); + LOGGER.info("Closed " + DhClientLevel.class.getSimpleName() + " for " + this.levelWrapper); } //=======================// @@ -230,13 +249,13 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel @Override public IFullDataSourceProvider getFileHandler() { - return dataFileHandler; + return this.dataFileHandler; } @Override public AbstractSaveStructure getSaveStructure() { - return saveStructure; + return this.saveStructure; } @Override @@ -253,6 +272,6 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel 0.2, 32f ) ); - clientside.reloadPos(pos); + this.clientside.reloadPos(pos); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index d5dc85ba0..be4003021 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -56,6 +56,7 @@ import java.util.concurrent.*; public class DhServerLevel extends DhLevel implements IDhServerLevel { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + public final ServerLevelModule serverside; private final IServerLevelWrapper serverLevelWrapper; @@ -89,36 +90,73 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel private void registerNetworkHandlers() { - this.eventSource.registerHandler(FullDataSourceRequestMessage.class, remotePlayerConnectionHandler.currentLevelOnly(this, (msg, serverPlayerState) -> + this.eventSource.registerHandler(FullDataSourceRequestMessage.class, this.remotePlayerConnectionHandler.currentLevelOnly(this, (msg, serverPlayerState) -> { - if (!serverPlayerState.config.isDistantGenerationEnabled()) + if (msg.checksum == null) { - msg.sendResponse(new RequestRejectedException("Operation is disabled from config.")); - return; - } - - if (!serverPlayerState.fullDataRequestConcurrencyLimiter.tryAcquire(msg)) - return; - - while (true) - { - IncompleteDataSourceEntry entry = incompleteDataSources.computeIfAbsent(msg.dhSectionPos, pos -> { - IncompleteDataSourceEntry newEntry = new IncompleteDataSourceEntry(); - this.trySetGeneratedDataSourceToEntry(newEntry, pos); - return newEntry; - }); - // If this fails, current entry is being drained and need to create another one - if (entry.requestCollectionSemaphore.tryAcquire()) + // Normal generation + + if (!serverPlayerState.config.isDistantGenerationEnabled()) { - fullDataRequests.put(msg.futureId, entry); - entry.requestMessages.put(msg.futureId, msg); - entry.requestCollectionSemaphore.release(); - break; + msg.sendResponse(new RequestRejectedException("Operation is disabled from config.")); + return; } + + if (!serverPlayerState.fullDataRequestConcurrencyLimiter.tryAcquire(msg)) + { + return; + } + + while (true) + { + IncompleteDataSourceEntry entry = this.incompleteDataSources.computeIfAbsent(msg.sectionPos, pos -> + { + IncompleteDataSourceEntry newEntry = new IncompleteDataSourceEntry(); + this.trySetGeneratedDataSourceToEntry(newEntry, pos); + return newEntry; + }); + // If this fails, current entry is being drained and need to create another one + if (entry.requestCollectionSemaphore.tryAcquire()) + { + this.fullDataRequests.put(msg.futureId, entry); + entry.requestMessages.put(msg.futureId, msg); + entry.requestCollectionSemaphore.release(); + break; + } + } + } + else + { + // Post-relog update + + if (!serverPlayerState.config.isPostRelogUpdateEnabled()) + { + msg.sendResponse(new RequestRejectedException("Operation is disabled from config.")); + return; + } + + if (!serverPlayerState.postRelogUpdateRequestConcurrencyLimiter.tryAcquire(msg)) + { + return; + } + + Integer serverChecksum = this.serverside.dataFileHandler.repo.getChecksumForSection(msg.sectionPos); + if (serverChecksum == null || serverChecksum.equals(msg.checksum)) + { + serverPlayerState.postRelogUpdateRequestConcurrencyLimiter.release(); + msg.sendResponse(new FullDataSourceResponseMessage(null, this)); + return; + } + + this.serverside.dataFileHandler.getAsync(msg.sectionPos).thenAccept(fullDataSource -> + { + serverPlayerState.postRelogUpdateRequestConcurrencyLimiter.release(); + msg.sendResponse(new FullDataSourceResponseMessage((CompleteFullDataSource) fullDataSource, this)); + }); } })); - this.eventSource.registerHandler(GenTaskPriorityRequestMessage.class, remotePlayerConnectionHandler.currentLevelOnly(this, (msg, serverPlayerState) -> + this.eventSource.registerHandler(GenTaskPriorityRequestMessage.class, this.remotePlayerConnectionHandler.currentLevelOnly(this, (msg, serverPlayerState) -> { msg.sendResponse(new GenTaskPriorityResponseMessage( this.serverside.dataFileHandler.getLoadStates(msg.posList.stream() @@ -130,18 +168,23 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel this.eventSource.registerHandler(CancelMessage.class, msg -> { IncompleteDataSourceEntry entry = this.fullDataRequests.remove(msg.futureId); - if (entry == null) return; + if (entry == null) + { + return; + } FullDataSourceRequestMessage requestMessage = entry.requestMessages.remove(msg.futureId); - ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); + ServerPlayerState serverPlayerState = this.remotePlayerConnectionHandler.getConnectedPlayer(msg); if (serverPlayerState != null) + { serverPlayerState.fullDataRequestConcurrencyLimiter.release(); + } entry.requestCollectionSemaphore.acquireUninterruptibly(Short.MAX_VALUE); if (entry.requestMessages.isEmpty()) { - incompleteDataSources.remove(requestMessage.dhSectionPos); - serverside.dataFileHandler.removeGenRequestIf(pos -> pos == requestMessage.dhSectionPos); + this.incompleteDataSources.remove(requestMessage.sectionPos); + this.serverside.dataFileHandler.removeGenRequestIf(pos -> pos == requestMessage.sectionPos); } entry.requestCollectionSemaphore.release(Short.MAX_VALUE); @@ -158,20 +201,23 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel this.worldGenLoopingQueue.remove(serverPlayer); } + @Override public void serverTick() { - chunkToLodBuilder.tick(); + this.chunkToLodBuilder.tick(); // Send finished data source requests - for (Map.Entry mapEntry : incompleteDataSources.entrySet()) + for (Map.Entry mapEntry : this.incompleteDataSources.entrySet()) { IncompleteDataSourceEntry entry = mapEntry.getValue(); if (entry.fullDataSource == null || entry.fullDataSource instanceof IIncompleteFullDataSource) + { continue; + } LodUtil.assertTrue(entry.fullDataSource instanceof CompleteFullDataSource, "Invalid full data source"); - incompleteDataSources.remove(mapEntry.getKey()); + this.incompleteDataSources.remove(mapEntry.getKey()); // This semaphore is intentionally acquired forever entry.requestCollectionSemaphore.acquireUninterruptibly(Short.MAX_VALUE); @@ -181,9 +227,11 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel { this.fullDataRequests.remove(msg.futureId); - ServerPlayerState serverPlayerState = remotePlayerConnectionHandler.getConnectedPlayer(msg); + ServerPlayerState serverPlayerState = this.remotePlayerConnectionHandler.getConnectedPlayer(msg); if (serverPlayerState == null) + { continue; + } serverPlayerState.fullDataRequestConcurrencyLimiter.release(); msg.sendResponse(new FullDataSourceResponseMessage(completeSource, this)); @@ -191,22 +239,30 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel } // Send updated chunks after delay - for (Map.Entry chunkUpdateEntry : chunkUpdatesToSend.entrySet()) + for (Map.Entry chunkUpdateEntry : this.chunkUpdatesToSend.entrySet()) { ChunkUpdateData chunkUpdateData = chunkUpdateEntry.getValue(); if (System.currentTimeMillis() < chunkUpdateData.time + CHUNK_UPDATE_SEND_DELAY) - continue; - - chunkUpdatesToSend.remove(chunkUpdateEntry.getKey()); - - for (ServerPlayerState serverPlayerState : remotePlayerConnectionHandler.getConnectedPlayers()) { - if (!serverPlayerState.config.isRealTimeUpdatesEnabled()) continue; + continue; + } + + this.chunkUpdatesToSend.remove(chunkUpdateEntry.getKey()); + + for (ServerPlayerState serverPlayerState : this.remotePlayerConnectionHandler.getConnectedPlayers()) + { + if (!serverPlayerState.config.isRealTimeUpdatesEnabled()) + { + continue; + } double distanceFromPlayer = chunkUpdateData.accessor.chunkPos.distance(new DhChunkPos(serverPlayerState.serverPlayer.getPosition())); if (distanceFromPlayer < serverPlayerState.serverPlayer.getViewDistance() || - distanceFromPlayer > serverPlayerState.config.getRenderDistanceRadius()) return; + distanceFromPlayer > serverPlayerState.config.getRenderDistanceRadius()) + { + return; + } serverPlayerState.connection.sendMessage(new FullDataPartialUpdateMessage(chunkUpdateData.accessor, this)); } @@ -218,10 +274,14 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel { CompletableFuture future = super.updateChunkAsync(chunk); if (future == null) + { return null; + } if (!Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates.get()) + { return future; + } future.thenAccept(chunkSizedFullDataAccessor -> { @@ -240,37 +300,42 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel } @Override - public int getMinY() { return getLevelWrapper().getMinHeight(); } + public int getMinY() + { + return this.getLevelWrapper().getMinHeight(); + } @Override public void close() { super.close(); - serverside.close(); - LOGGER.info("Closed DHLevel for {}", getLevelWrapper()); + this.serverside.close(); + LOGGER.info("Closed DHLevel for {}", this.getLevelWrapper()); } @Override public void doWorldGen() { boolean shouldDoWorldGen = true; //todo; - boolean isWorldGenRunning = serverside.worldGenModule.isWorldGenRunning(); + boolean isWorldGenRunning = this.serverside.worldGenModule.isWorldGenRunning(); if (shouldDoWorldGen && !isWorldGenRunning) { // start world gen - serverside.worldGenModule.startWorldGen(serverside.dataFileHandler, new ServerLevelModule.WorldGenState(this)); + this.serverside.worldGenModule.startWorldGen(this.serverside.dataFileHandler, new ServerLevelModule.WorldGenState(this)); } else if (!shouldDoWorldGen && isWorldGenRunning) { // stop world gen - serverside.worldGenModule.stopWorldGen(serverside.dataFileHandler); + this.serverside.worldGenModule.stopWorldGen(this.serverside.dataFileHandler); } - if (serverside.worldGenModule.isWorldGenRunning()) + if (this.serverside.worldGenModule.isWorldGenRunning()) { IServerPlayerWrapper firstPlayer = this.worldGenLoopingQueue.peek(); if (firstPlayer == null) + { return; + } // Put first player in back before removing from front, so it can be removed by other thread without blocking // - if it gets removed, remove() below will remove the item we just put instead @@ -278,23 +343,32 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel this.worldGenLoopingQueue.remove(firstPlayer); Vec3d position = firstPlayer.getPosition(); - serverside.worldGenModule.worldGenTick(new DhBlockPos2D((int) position.x, (int) position.z)); + this.serverside.worldGenModule.worldGenTick(new DhBlockPos2D((int) position.x, (int) position.z)); } } @Override - public IServerLevelWrapper getServerLevelWrapper() { return serverLevelWrapper; } + public IServerLevelWrapper getServerLevelWrapper() + { + return this.serverLevelWrapper; + } @Override - public ILevelWrapper getLevelWrapper() { return getServerLevelWrapper(); } + public ILevelWrapper getLevelWrapper() + { + return this.getServerLevelWrapper(); + } @Override - public IFullDataSourceProvider getFileHandler() { return serverside.dataFileHandler; } + public IFullDataSourceProvider getFileHandler() + { + return this.serverside.dataFileHandler; + } @Override public AbstractSaveStructure getSaveStructure() { - return serverside.saveStructure; + return this.serverside.saveStructure; } @Override @@ -304,9 +378,13 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel { this.serverside.dataFileHandler.getAsync(pos).thenAccept(fullDataSource -> { if (fullDataSource instanceof IIncompleteFullDataSource) + { fullDataSource = ((IIncompleteFullDataSource) fullDataSource).tryPromotingToCompleteDataSource(); + } if (fullDataSource instanceof CompleteFullDataSource) + { entry.fullDataSource = fullDataSource; + } }); } @@ -314,7 +392,10 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel public void onWorldGenTaskComplete(DhSectionPos pos) { IncompleteDataSourceEntry entry = this.incompleteDataSources.get(pos); - if (entry == null) return; + if (entry == null) + { + return; + } this.trySetGeneratedDataSourceToEntry(entry, pos); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java index b707c7166..c97be2d74 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java @@ -38,6 +38,8 @@ public interface IDhLevel extends AutoCloseable */ ILevelWrapper getLevelWrapper(); + void updateDataSourcesWithChunkData(ChunkSizedFullDataAccessor data); + @Nullable CompletableFuture updateChunkAsync(IChunkWrapper chunk); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java new file mode 100644 index 000000000..67755ff15 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -0,0 +1,289 @@ +package com.seibel.distanthorizons.core.multiplayer.client; + +import com.google.common.base.Stopwatch; +import com.seibel.distanthorizons.core.config.types.ConfigEntry; +import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; +import com.seibel.distanthorizons.core.level.IDhClientLevel; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.logging.f3.F3Screen; +import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; +import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceResponseMessage; +import com.seibel.distanthorizons.core.pos.DhBlockPos2D; +import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; +import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; +import com.seibel.distanthorizons.core.util.LodUtil; +import io.netty.channel.ChannelException; +import org.apache.logging.log4j.Logger; + +import javax.annotation.CheckForNull; +import javax.annotation.Nullable; +import java.awt.*; +import java.util.ArrayList; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.*; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; + +public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, AutoCloseable +{ + private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + + protected static final long SHUTDOWN_TIMEOUT_SECONDS = 5; + + public final ClientNetworkState networkState; + protected final IDhClientLevel level; + private final boolean changedOnly; + + private volatile CompletableFuture closingFuture = null; + + protected final ConcurrentMap waitingTasks = new ConcurrentHashMap<>(); + private final Semaphore pendingTasksSemaphore = new Semaphore(Short.MAX_VALUE, true); + + private final F3Screen.NestedMessage f3Message = new F3Screen.NestedMessage(this::f3Log); + private final AtomicInteger finishedRequests = new AtomicInteger(); + private final AtomicInteger failedRequests = new AtomicInteger(); + private final ConfigEntry showDebugWireframeConfig; + + private final Set alreadyRequestedPositions = ConcurrentHashMap.newKeySet(); + + + protected abstract int getRequestConcurrencyLimit(); + + protected abstract String getQueueName(); + + + public AbstractFullDataRequestQueue(ClientNetworkState networkState, IDhClientLevel level, boolean changedOnly, ConfigEntry showDebugWireframeConfig) + { + this.networkState = networkState; + this.level = level; + this.changedOnly = changedOnly; + this.showDebugWireframeConfig = showDebugWireframeConfig; + DebugRenderer.register(this, this.showDebugWireframeConfig); + } + + public CompletableFuture submitRequest(DhSectionPos sectionPos, Consumer chunkDataConsumer) + { + return this.submitRequest(sectionPos, chunkDataConsumer, null); + } + public CompletableFuture submitRequest(DhSectionPos sectionPos, Consumer chunkDataConsumer, @Nullable Integer currentChecksum) + { + LodUtil.assertTrue(sectionPos.getDetailLevel() == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL, "Only highest-detail sections are allowed."); + + // check if this is a duplicate task + if (this.alreadyRequestedPositions.contains(sectionPos)) + { + // temporary solution to prevent requesting the same section multiple times + LOGGER.trace("Duplicate section " + sectionPos + ". Skipping..."); + return CompletableFuture.completedFuture(false); + } + this.alreadyRequestedPositions.add(sectionPos); + + RequestQueueEntry entry = new RequestQueueEntry(chunkDataConsumer, currentChecksum); + this.waitingTasks.put(sectionPos, entry); + return entry.future; + } + + protected int posDistanceSquared(DhBlockPos2D targetPos, DhSectionPos pos) + { + return (int) pos.getCenterBlockPos().distSquared(targetPos); + } + + public synchronized boolean tick(DhBlockPos2D targetPos) + { + if (this.closingFuture != null || !this.networkState.getClient().isReady()) + { + return false; + } + + while (this.getWaitingTaskCount() > this.getInProgressTaskCount() + && this.getInProgressTaskCount() < this.getRequestConcurrencyLimit() + && this.pendingTasksSemaphore.tryAcquire()) + { + this.sendNewRequest(targetPos); + } + + return true; + } + + public void cancelRequests(Iterable positions) + { + for (DhSectionPos pos : positions) + { + RequestQueueEntry entry = this.waitingTasks.remove(pos); + if (entry != null) + { + entry.future.cancel(false); + if (entry.request != null) + { + entry.request.cancel(false); + } + this.alreadyRequestedPositions.remove(pos); + } + } + } + + private void sendNewRequest(DhBlockPos2D targetPos) + { + Map.Entry mapEntry = this.waitingTasks.entrySet().stream() + .filter(task -> task.getValue().request == null) + .reduce(null, (a, b) + -> a == null + || b.getValue().priority > a.getValue().priority + || (b.getValue().priority == a.getValue().priority && this.posDistanceSquared(targetPos, b.getKey()) < this.posDistanceSquared(targetPos, a.getKey())) + ? b : a); + if (mapEntry == null) + { + this.pendingTasksSemaphore.release(); + return; + } + + DhSectionPos sectionPos = mapEntry.getKey(); + RequestQueueEntry entry = mapEntry.getValue(); + + CompletableFuture request = this.networkState.getClient().sendRequest(new FullDataSourceRequestMessage(this.level.getLevelWrapper(), sectionPos, entry.currentChecksum), FullDataSourceResponseMessage.class); + entry.request = request; + request.handleAsync((response, throwable) -> + { + this.pendingTasksSemaphore.release(); + this.finishedRequests.incrementAndGet(); + + try + { + if (throwable != null) + { + throw throwable; + } + + this.waitingTasks.remove(sectionPos); + LOGGER.debug("FullDataSourceResponseMessage " + sectionPos); + + CompleteFullDataSource fullDataSource = response.getFullDataSource(sectionPos, this.level); + + if (fullDataSource != null) + { + fullDataSource.splitIntoChunkSizedAccessors(entry.chunkDataConsumer); + response.getFullDataSourceLoader().returnPooledDataSource(fullDataSource); + } + else + { + LodUtil.assertTrue(this.changedOnly, "Received empty data source response for not changed-only request"); + } + } + catch (InvalidLevelException ignored) + { + // We're too late + } + catch (ChannelException | RateLimitedException e) + { + if (e instanceof RateLimitedException) + { + LOGGER.warn("Rate limited by server, re-queueing task [" + sectionPos + "]: " + e.getMessage()); + } + + entry.request = null; + this.finishedRequests.decrementAndGet(); + } + catch (CancellationException ignored) + { + this.finishedRequests.decrementAndGet(); + } + catch (Throwable e) + { + LOGGER.error("Error while fetching full data source", e); + this.failedRequests.incrementAndGet(); + return entry.future.complete(false); + } + + return entry.future.complete(true); + }); + } + + private String[] f3Log() + { + ArrayList lines = new ArrayList<>(); + lines.add(this.getQueueName() + " [" + this.level.getClientLevelWrapper().getDimensionType().getDimensionName() + "]"); + lines.add("Requests: " + this.finishedRequests + " / " + (this.getWaitingTaskCount() + this.finishedRequests.get()) + " (failed: " + this.failedRequests + ", rate limit: " + this.getRequestConcurrencyLimit() + ")"); + return lines.toArray(new String[0]); + } + + public int getWaitingTaskCount() { return this.waitingTasks.size(); } + + public int getInProgressTaskCount() { return Short.MAX_VALUE - this.pendingTasksSemaphore.availablePermits(); } + + public CompletableFuture startClosing(boolean alsoInterruptRunning) + { + return this.closingFuture = CompletableFuture.runAsync(() -> { + Stopwatch stopwatch = Stopwatch.createStarted(); + + do + { + for (RequestQueueEntry entry : this.waitingTasks.values()) + { + entry.future.cancel(alsoInterruptRunning); + if (entry.request != null && entry.request.cancel(alsoInterruptRunning)) + { + this.pendingTasksSemaphore.release(); + } + } + } + while (!this.pendingTasksSemaphore.tryAcquire(Short.MAX_VALUE) && stopwatch.elapsed(TimeUnit.SECONDS) < SHUTDOWN_TIMEOUT_SECONDS); + + if (stopwatch.elapsed(TimeUnit.SECONDS) >= SHUTDOWN_TIMEOUT_SECONDS) + { + LOGGER.warn(this.getQueueName() + " for " + this.level.getLevelWrapper() + " did not shutdown in " + SHUTDOWN_TIMEOUT_SECONDS + " seconds! Some unfinished tasks might be left hanging."); + } + }); + } + + @Override + public void close() + { + this.f3Message.close(); + DebugRenderer.unregister(this, this.showDebugWireframeConfig); + } + + @Override + public void debugRender(DebugRenderer r) + { + for (Map.Entry mapEntry : this.waitingTasks.entrySet()) + { + r.renderBox(new DebugRenderer.Box(mapEntry.getKey(), -32f, 64f, 0.05f, + mapEntry.getValue().request != null ? Color.red + : mapEntry.getValue().priority == 3 ? Color.orange + : mapEntry.getValue().priority == 2 ? Color.cyan + : mapEntry.getValue().priority == 1 ? Color.blue + : Color.gray + )); + } + } + + protected static class RequestQueueEntry + { + public final CompletableFuture future = new CompletableFuture<>(); + public final Consumer chunkDataConsumer; + @Nullable + public final Integer currentChecksum; + + // Higher value = higher priority. + // Priority of 0 is reserved for unassigned value + public int priority = 0; + @CheckForNull + public CompletableFuture request; + + public RequestQueueEntry( + Consumer chunkDataConsumer, + @Nullable + Integer currentChecksum) + { + this.chunkDataConsumer = chunkDataConsumer; + this.currentChecksum = currentChecksum; + } + + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/FullDataRefreshQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/FullDataRefreshQueue.java new file mode 100644 index 000000000..8e92afd4c --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/FullDataRefreshQueue.java @@ -0,0 +1,30 @@ +package com.seibel.distanthorizons.core.multiplayer.client; + +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.level.IDhClientLevel; +import com.seibel.distanthorizons.core.pos.DhBlockPos2D; + +public class FullDataRefreshQueue extends AbstractFullDataRequestQueue +{ + public FullDataRefreshQueue(IDhClientLevel level, ClientNetworkState networkState) + { + super(networkState, level, true, Config.Client.Advanced.Debugging.DebugWireframe.showWorldGenQueue); + } + + @Override + protected int getRequestConcurrencyLimit() { return this.networkState.config.postRelogUpdateConcurrencyLimit; } + + @Override + protected String getQueueName() { return "Data Refresh Queue"; } + + @Override + public boolean tick(DhBlockPos2D targetPos) + { + if (!this.networkState.config.postRelogUpdateEnabled) + { + return false; + } + return super.tick(targetPos); + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java index f1ad36d78..7086c7ac3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java @@ -11,6 +11,7 @@ public abstract class AbstractMultiplayerConfig implements INetworkObject public abstract int getGenTaskPriorityRequestRateLimit(); public abstract boolean isRealTimeUpdatesEnabled(); public abstract boolean isPostRelogUpdateEnabled(); + public abstract int getPostRelogUpdateConcurrencyLimit(); @Override public void encode(ByteBuf out) @@ -21,6 +22,7 @@ public abstract class AbstractMultiplayerConfig implements INetworkObject out.writeInt(this.getGenTaskPriorityRequestRateLimit()); out.writeBoolean(this.isRealTimeUpdatesEnabled()); out.writeBoolean(this.isPostRelogUpdateEnabled()); + out.writeInt(this.getPostRelogUpdateConcurrencyLimit()); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java index 631a3ce03..578e84fac 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java @@ -8,22 +8,25 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig // IMPORTANT: Once you added/removed config fields, modify MultiplayerConfigChangeListener accordingly. public int renderDistanceRadius = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get(); - @Override public int getRenderDistanceRadius() { return renderDistanceRadius; } + @Override public int getRenderDistanceRadius() { return this.renderDistanceRadius; } public boolean distantGenerationEnabled = Config.Client.Advanced.WorldGenerator.enableDistantGeneration.get(); - @Override public boolean isDistantGenerationEnabled() { return distantGenerationEnabled; } + @Override public boolean isDistantGenerationEnabled() { return this.distantGenerationEnabled; } public int fullDataRequestConcurrencyLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.fullDataRequestConcurrencyLimit.get(); - @Override public int getFullDataRequestConcurrencyLimit() { return fullDataRequestConcurrencyLimit; } + @Override public int getFullDataRequestConcurrencyLimit() { return this.fullDataRequestConcurrencyLimit; } public int genTaskPriorityRequestRateLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.genTaskPriorityRequestRateLimit.get(); - @Override public int getGenTaskPriorityRequestRateLimit() { return genTaskPriorityRequestRateLimit; } + @Override public int getGenTaskPriorityRequestRateLimit() { return this.genTaskPriorityRequestRateLimit; } public boolean realTimeUpdatesEnabled = Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates.get(); - @Override public boolean isRealTimeUpdatesEnabled() { return realTimeUpdatesEnabled; } + @Override public boolean isRealTimeUpdatesEnabled() { return this.realTimeUpdatesEnabled; } - public boolean postRelogUpdateEnabled = false; // Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate.get(); - @Override public boolean isPostRelogUpdateEnabled() { return postRelogUpdateEnabled; } + public boolean postRelogUpdateEnabled = Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate.get(); + @Override public boolean isPostRelogUpdateEnabled() { return this.postRelogUpdateEnabled; } + + public int postRelogUpdateConcurrencyLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.postRelogUpdateConcurrencyLimit.get(); + @Override public int getPostRelogUpdateConcurrencyLimit() { return this.postRelogUpdateConcurrencyLimit; } @Override public void decode(ByteBuf in) @@ -34,17 +37,19 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig this.genTaskPriorityRequestRateLimit = in.readInt(); this.realTimeUpdatesEnabled = in.readBoolean(); this.postRelogUpdateEnabled = in.readBoolean(); + this.postRelogUpdateConcurrencyLimit = in.readInt(); } @Override public String toString() { return "MultiplayerConfig{" + - "renderDistance=" + renderDistanceRadius + - ", distantGenerationEnabled=" + distantGenerationEnabled + - ", fullDataRequestConcurrencyLimit=" + fullDataRequestConcurrencyLimit + - ", genTaskPriorityRequestRateLimit=" + genTaskPriorityRequestRateLimit + - ", realTimeUpdatesEnabled=" + realTimeUpdatesEnabled + - ", postRelogUpdatesEnabled=" + postRelogUpdateEnabled + + "renderDistance=" + this.renderDistanceRadius + + ", distantGenerationEnabled=" + this.distantGenerationEnabled + + ", fullDataRequestConcurrencyLimit=" + this.fullDataRequestConcurrencyLimit + + ", genTaskPriorityRequestRateLimit=" + this.genTaskPriorityRequestRateLimit + + ", realTimeUpdatesEnabled=" + this.realTimeUpdatesEnabled + + ", postRelogUpdatesEnabled=" + this.postRelogUpdateEnabled + + ", postRelogUpdateConcurrencyLimit=" + this.postRelogUpdateConcurrencyLimit + '}'; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java index 3a4b8ce50..76ba40c7a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java @@ -16,7 +16,8 @@ public class MultiplayerConfigChangeListener implements Closeable Config.Client.Advanced.Multiplayer.ServerNetworking.fullDataRequestConcurrencyLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.genTaskPriorityRequestRateLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates, - //Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate + Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate, + Config.Client.Advanced.Multiplayer.ServerNetworking.postRelogUpdateConcurrencyLimit, }; private final ArrayList changeListeners = new ArrayList<>(); @@ -24,15 +25,19 @@ public class MultiplayerConfigChangeListener implements Closeable public MultiplayerConfigChangeListener(Runnable runnable) { for (ConfigEntry entry : CONFIG_ENTRIES) - changeListeners.add(new ConfigChangeListener(entry, ignored -> runnable.run())); + { + this.changeListeners.add(new ConfigChangeListener(entry, ignored -> runnable.run())); + } } @Override public void close() { - for (ConfigChangeListener changeListener : changeListeners) + for (ConfigChangeListener changeListener : this.changeListeners) + { changeListener.close(); - changeListeners.clear(); + } + this.changeListeners.clear(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index f8e1ac49b..3ab07416f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -27,7 +27,7 @@ public class ServerPlayerState public final SupplierBasedConcurrencyLimiter fullDataRequestConcurrencyLimiter = new SupplierBasedConcurrencyLimiter<>( () -> ServerNetworking.fullDataRequestConcurrencyLimit.get(), msg -> { - msg.sendResponse(new RateLimitedException("Max concurrent full data requests: " + config.getFullDataRequestConcurrencyLimit())); + msg.sendResponse(new RateLimitedException("Max concurrent full data requests: " + this.config.getFullDataRequestConcurrencyLimit())); this.rateLimitKickTrigger.tryAcquire(null); } ); @@ -35,15 +35,22 @@ public class ServerPlayerState public final SupplierBasedRateLimiter genTaskPriorityRequestRateLimiter = new SupplierBasedRateLimiter<>( () -> ServerNetworking.genTaskPriorityRequestRateLimit.get(), msg -> { - // Shouldn't be called, but it's here just in case - msg.sendResponse(new RateLimitedException("Max section checks per second: " + config.getFullDataRequestConcurrencyLimit())); + msg.sendResponse(new RateLimitedException("Max section checks per second: " + this.config.getFullDataRequestConcurrencyLimit())); + this.rateLimitKickTrigger.tryAcquire(null); + } + ); + + public final SupplierBasedConcurrencyLimiter postRelogUpdateRequestConcurrencyLimiter = new SupplierBasedConcurrencyLimiter<>( + () -> ServerNetworking.postRelogUpdateConcurrencyLimit.get(), + msg -> { + msg.sendResponse(new RateLimitedException("Max concurrent post-relog update requests: " + this.config.getPostRelogUpdateConcurrencyLimit())); this.rateLimitKickTrigger.tryAcquire(null); } ); - public ServerPlayerState(IServerPlayerWrapper serverPlayer) { this.serverPlayer = serverPlayer; } + public ServerPlayerState(IServerPlayerWrapper serverPlayer) { this.serverPlayer = serverPlayer; } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java index 03a3547a8..e16ccac79 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java @@ -15,36 +15,42 @@ public class ServersideMultiplayerConfig extends AbstractMultiplayerConfig @Override public int getRenderDistanceRadius() { - return Math.min(clientConfig.renderDistanceRadius, Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get()); + return Math.min(this.clientConfig.renderDistanceRadius, Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get()); } @Override public boolean isDistantGenerationEnabled() { - return clientConfig.distantGenerationEnabled && Config.Client.Advanced.WorldGenerator.enableDistantGeneration.get(); + return this.clientConfig.distantGenerationEnabled && Config.Client.Advanced.WorldGenerator.enableDistantGeneration.get(); } @Override public int getFullDataRequestConcurrencyLimit() { - return Math.min(clientConfig.fullDataRequestConcurrencyLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.fullDataRequestConcurrencyLimit.get()); + return Math.min(this.clientConfig.fullDataRequestConcurrencyLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.fullDataRequestConcurrencyLimit.get()); } @Override public int getGenTaskPriorityRequestRateLimit() { - return Math.min(clientConfig.genTaskPriorityRequestRateLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.genTaskPriorityRequestRateLimit.get()); + return Math.min(this.clientConfig.genTaskPriorityRequestRateLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.genTaskPriorityRequestRateLimit.get()); } @Override public boolean isRealTimeUpdatesEnabled() { - return clientConfig.realTimeUpdatesEnabled && Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates.get(); + return this.clientConfig.realTimeUpdatesEnabled && Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates.get(); } @Override public boolean isPostRelogUpdateEnabled() { - return false; // clientConfig.postRelogUpdateEnabled && Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate.get(); + return this.clientConfig.postRelogUpdateEnabled && Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate.get(); + } + + @Override + public int getPostRelogUpdateConcurrencyLimit() + { + return Math.min(this.clientConfig.postRelogUpdateConcurrencyLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.postRelogUpdateConcurrencyLimit.get()); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java b/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java index 8a74d5c37..4cb494688 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java @@ -6,12 +6,16 @@ import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import io.netty.channel.ChannelException; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import java.net.SocketAddress; import java.util.concurrent.CompletableFuture; public interface IConnection { + Logger LOGGER = LogManager.getLogger(); + ChannelHandlerContext getChannelContext(); NetworkEventSource getRequestHandler(); @@ -22,6 +26,7 @@ public interface IConnection default CompletableFuture sendMessage(NetworkMessage message) { + LOGGER.trace("Sending message: " + message); CompletableFuture future = new CompletableFuture<>(); ChannelHandlerContext ctx = this.getChannelContext(); @@ -52,7 +57,9 @@ public interface IConnection this.sendMessage(msg).whenComplete((ignored, throwable) -> { if (throwable != null) + { responseFuture.completeExceptionally(throwable); + } }); return responseFuture; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java index d62a26876..c3d0df0d3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java @@ -43,7 +43,6 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - // TODO move to the config private final int port; private final EventLoopGroup bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("DH-Network - Server Boss Thread")); @@ -129,7 +128,9 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable public void close() { if (!this.isClosed.compareAndSet(false, true)) + { return; + } LOGGER.info("Shutting down the network server."); this.workerGroup.shutdownGracefully().syncUninterruptibly(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceRequestMessage.java index b253f151a..658dce6cc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceRequestMessage.java @@ -26,50 +26,56 @@ import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import io.netty.buffer.ByteBuf; +import javax.annotation.Nullable; + public class FullDataSourceRequestMessage extends FutureTrackableNetworkMessage implements ILevelRelatedMessage { - public DhSectionPos dhSectionPos; private int levelHashCode; - @Override public int getLevelHashCode() { return levelHashCode; } - public boolean changedOnly; + + public DhSectionPos sectionPos; + + /** Only present when requesting for changes. */ + @Nullable + public Integer checksum; + + @Override + public int getLevelHashCode() { return this.levelHashCode; } public FullDataSourceRequestMessage() {} - - public FullDataSourceRequestMessage(ILevelWrapper levelWrapper, DhSectionPos dhSectionPos) + public FullDataSourceRequestMessage(ILevelWrapper levelWrapper, DhSectionPos sectionPos, @Nullable Integer checksum) { // TODO Multiverse support this.levelHashCode = levelWrapper.getDimensionType().getDimensionName().hashCode(); - this.dhSectionPos = dhSectionPos; - } - - public FullDataSourceRequestMessage(ILevelWrapper levelWrapper, DhSectionPos dhSectionPos, boolean changedOnly) - { - this(levelWrapper, dhSectionPos); - this.changedOnly = true; + this.sectionPos = sectionPos; + this.checksum = checksum; } @Override public void encode0(ByteBuf out) { - out.writeInt(levelHashCode); - dhSectionPos.encode(out); - out.writeBoolean(changedOnly); + out.writeInt(this.levelHashCode); + this.sectionPos.encode(out); + if (this.encodeOptional(out, this.checksum)) + { + out.writeInt(this.checksum); + } } @Override public void decode0(ByteBuf in) { - levelHashCode = in.readInt(); - dhSectionPos = INetworkObject.decodeStatic(DhSectionPos.zero(), in); - changedOnly = in.readBoolean(); + this.levelHashCode = in.readInt(); + this.sectionPos = INetworkObject.decodeStatic(DhSectionPos.zero(), in); + this.checksum = this.decodeOptional(in, in::readInt); } @Override public String toString() { return super.toString( - "dhSectionPos=" + dhSectionPos + - ", levelHashCode=" + levelHashCode + "dhSectionPos=" + this.sectionPos + + ", levelHashCode=" + this.levelHashCode + + ", checksum=" + this.checksum ); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java index 15f9c392e..52b2ef71c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java @@ -31,20 +31,33 @@ import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStre import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufInputStream; +import javax.annotation.Nullable; import java.io.ByteArrayOutputStream; import java.io.IOException; +/** + * Response message, containing the requested full data source, + * or nothing if requested in updates-only mode and the data was not updated.
+ * Decoded full data source is not cached, since it's intended for a single use. + */ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage { + // Transmitted data + @Nullable + private ByteBuf dataBuffer; + + // Used only when encoding + @Nullable private CompleteFullDataSource fullDataSource; private DhServerLevel level; + // Used only when decoding private CompleteFullDataSourceLoader fullDataSourceLoader; - public CompleteFullDataSourceLoader getFullDataSourceLoader() { return fullDataSourceLoader; } - private ByteBuf dataBuffer; + public CompleteFullDataSourceLoader getFullDataSourceLoader() { return this.fullDataSourceLoader; } + public FullDataSourceResponseMessage() {} - public FullDataSourceResponseMessage(CompleteFullDataSource fullDataSource, DhServerLevel level) + public FullDataSourceResponseMessage(@Nullable CompleteFullDataSource fullDataSource, DhServerLevel level) { this.fullDataSource = fullDataSource; this.level = level; @@ -53,41 +66,54 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage @Override public void encode0(ByteBuf out) throws IOException { - try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) + if (this.encodeOptional(out, this.fullDataSource)) { - DhDataOutputStream dhOutputStream = new DhDataOutputStream(outputStream); - fullDataSource.writeToStream(dhOutputStream, level); - dhOutputStream.flush(); - - out.writeByte(fullDataSource.getDataFormatVersion()); - out.writeInt(outputStream.size()); - out.writeBytes(outputStream.toByteArray()); + try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) + { + DhDataOutputStream dhOutputStream = new DhDataOutputStream(outputStream); + this.fullDataSource.writeToStream(dhOutputStream, this.level); + dhOutputStream.flush(); + + out.writeByte(this.fullDataSource.getDataFormatVersion()); + out.writeInt(outputStream.size()); + out.writeBytes(outputStream.toByteArray()); + } } } @Override public void decode0(ByteBuf in) { - byte dataVersion = in.readByte(); - this.fullDataSourceLoader = (CompleteFullDataSourceLoader) AbstractFullDataSourceLoader.getLoader(CompleteFullDataSource.DATA_TYPE_NAME, dataVersion); - this.dataBuffer = in.readBytes(in.readInt()); + this.dataBuffer = this.decodeOptional(in, () -> + { + byte dataVersion = in.readByte(); + this.fullDataSourceLoader = (CompleteFullDataSourceLoader) AbstractFullDataSourceLoader.getLoader(CompleteFullDataSource.DATA_TYPE_NAME, dataVersion); + return in.readBytes(in.readInt()); + }); } - public CompleteFullDataSource getFullDataSource(DhSectionPos pos, IDhLevel level) throws IOException, InterruptedException + @Nullable + public synchronized CompleteFullDataSource getFullDataSource(DhSectionPos pos, IDhLevel level) throws IOException, InterruptedException { - try (ByteBufInputStream inputStream = new ByteBufInputStream(dataBuffer)) + if (this.dataBuffer == null) { - return fullDataSourceLoader.loadData(pos, new DhDataInputStream(inputStream), level); + return null; + } + + try (ByteBufInputStream inputStream = new ByteBufInputStream(this.dataBuffer)) + { + return this.fullDataSourceLoader.loadData(pos, new DhDataInputStream(inputStream), level); } finally { - dataBuffer.release(); + this.dataBuffer.release(); } } - @Override public String toString() + @Override + public String toString() { - return super.toString("dataBuffer=" + dataBuffer); + return super.toString("dataBuffer=" + this.dataBuffer); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/AbstractDataSourceRepo.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/AbstractDataSourceRepo.java index 479da8595..05232d9f8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/sql/AbstractDataSourceRepo.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/AbstractDataSourceRepo.java @@ -22,6 +22,7 @@ package com.seibel.distanthorizons.core.sql; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep; import com.seibel.distanthorizons.core.pos.DhSectionPos; +import javax.annotation.Nullable; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Map; @@ -164,5 +165,22 @@ public abstract class AbstractDataSourceRepo extends AbstractDhRepo resultMap = this.queryDictionaryFirst("SELECT Checksum FROM DhFullData WHERE DhSectionPos = '" + pos.serialize() + "';"); + if (resultMap == null || resultMap.get("Checksum") == null) + { + return null; + } + else + { + return (int) resultMap.get("Checksum"); + } + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedConcurrencyLimiter.java b/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedConcurrencyLimiter.java index 27e2316c0..6326c1608 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedConcurrencyLimiter.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedConcurrencyLimiter.java @@ -27,6 +27,7 @@ public class SupplierBasedConcurrencyLimiter { if (this.pendingTasks.incrementAndGet() > this.maxConcurrentTasksSupplier.get()) { + this.pendingTasks.decrementAndGet(); this.onFailureConsumer.accept(context); return false; } From c9d426e581b37dee626056fa2b1b271ba5139835 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 28 Jan 2024 22:37:50 +0500 Subject: [PATCH 085/205] Refactor ILevelRelatedMessage and RemotePlayerConnectionHandler --- .../server/RemotePlayerConnectionHandler.java | 44 +++++++++++++++---- .../messages/base/ILevelRelatedMessage.java | 31 ++----------- .../updates/FullDataPartialUpdateMessage.java | 36 ++++++++------- 3 files changed, 60 insertions(+), 51 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java index 56f5870d6..2dc603dd5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java @@ -2,32 +2,36 @@ package com.seibel.distanthorizons.core.multiplayer.server; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; +import com.google.common.collect.Maps; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; import com.seibel.distanthorizons.core.network.IConnection; import com.seibel.distanthorizons.core.network.NetworkServer; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; +import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; import com.seibel.distanthorizons.core.network.messages.base.AckMessage; import com.seibel.distanthorizons.core.network.messages.base.CloseEvent; import com.seibel.distanthorizons.core.network.messages.base.ILevelRelatedMessage; import com.seibel.distanthorizons.core.network.messages.session.PlayerUUIDMessage; import com.seibel.distanthorizons.core.network.messages.session.RemotePlayerConfigMessage; +import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import org.jetbrains.annotations.Nullable; import java.io.Closeable; -import java.util.HashMap; +import java.util.ArrayList; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; import java.util.function.Consumer; public class RemotePlayerConnectionHandler implements Closeable { private final ScopedNetworkEventSource eventSource; - private final HashMap playersByUUID = new HashMap<>(); - private final BiMap playersByConnection = HashBiMap.create(); + private final ConcurrentHashMap playersByUUID = new ConcurrentHashMap<>(); + private final BiMap playersByConnection = Maps.synchronizedBiMap(HashBiMap.create()); private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::onConfigChanged); @@ -83,28 +87,45 @@ public class RemotePlayerConnectionHandler implements Closeable private void onConfigChanged() { for (ServerPlayerState serverPlayerState : this.getConnectedPlayers()) + { serverPlayerState.connection.sendMessage(new RemotePlayerConfigMessage(serverPlayerState.config)); + } } public Consumer connectedPlayersOnly(BiConsumer next) { return msg -> { - ServerPlayerState serverPlayerState = getConnectedPlayer(msg); + ServerPlayerState serverPlayerState = this.getConnectedPlayer(msg); if (serverPlayerState != null) + { next.accept(msg, serverPlayerState); + } }; } public Consumer currentLevelOnly(DhServerLevel level, BiConsumer next) { - return connectedPlayersOnly((msg, serverPlayerState) -> + return this.connectedPlayersOnly((msg, serverPlayerState) -> { if (serverPlayerState.serverPlayer.getLevel() != level.getLevelWrapper()) + { return; + } - if (msg instanceof ILevelRelatedMessage && ((ILevelRelatedMessage) msg).sendExceptionIfLevelInvalid(level.getLevelWrapper())) - return; + if (msg instanceof ILevelRelatedMessage) + { + if (!((ILevelRelatedMessage) msg).isLevelValid(level.getLevelWrapper())) + { + if (msg instanceof FutureTrackableNetworkMessage) + { + ((FutureTrackableNetworkMessage) msg).sendResponse(new InvalidLevelException("Invalid level")); + } + + return; + } + + } next.accept(msg, serverPlayerState); }); @@ -112,13 +133,16 @@ public class RemotePlayerConnectionHandler implements Closeable public Iterable getConnectedPlayers() { - return playersByConnection.values(); + synchronized (this.playersByConnection) + { + return new ArrayList<>(this.playersByConnection.values()); + } } @Nullable public ServerPlayerState getConnectedPlayer(NetworkMessage msg) { - return playersByConnection.get(msg.getConnection()); + return this.playersByConnection.get(msg.getConnection()); } public void registerJoinedPlayer(IServerPlayerWrapper serverPlayer) @@ -131,7 +155,9 @@ public class RemotePlayerConnectionHandler implements Closeable ServerPlayerState dhPlayer = this.playersByUUID.remove(serverPlayer.getUUID()); IConnection connection = this.playersByConnection.inverse().remove(dhPlayer); if (connection != null) + { connection.disconnect("You are being disconnected."); + } } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ILevelRelatedMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ILevelRelatedMessage.java index 8ba15e42d..30a72094d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ILevelRelatedMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ILevelRelatedMessage.java @@ -1,7 +1,5 @@ package com.seibel.distanthorizons.core.network.messages.base; -import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; -import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; public interface ILevelRelatedMessage @@ -9,33 +7,12 @@ public interface ILevelRelatedMessage int getLevelHashCode(); /** - * Returns true if level does not match the given level. - * - * @param levelWrapper Level wrapper to check against. - * @return Whether the level is invalid. + * Checks whether the message's level matches the given level. */ - default boolean isLevelInvalid(ILevelWrapper levelWrapper) + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + default boolean isLevelValid(ILevelWrapper levelWrapper) { - return levelWrapper.getDimensionType().getDimensionName().hashCode() != getLevelHashCode(); - } - - /** - * Same as {@link #isLevelInvalid}. - * If current message implements {@link FutureTrackableNetworkMessage}, additionally sends an exception response if given wrapper does not match. - * - * @param levelWrapper Level wrapper to check against. - * @return Whether the level is invalid. - */ - default boolean sendExceptionIfLevelInvalid(ILevelWrapper levelWrapper) - { - if (isLevelInvalid(levelWrapper)) - { - if (this instanceof FutureTrackableNetworkMessage) - ((FutureTrackableNetworkMessage) this).sendResponse(new InvalidLevelException("Invalid level")); - return true; - } - - return false; + return levelWrapper.getDimensionType().getDimensionName().hashCode() == this.getLevelHashCode(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java index a2d70154e..4edbe02e2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java @@ -40,7 +40,8 @@ public class FullDataPartialUpdateMessage extends NetworkMessage implements ILev private DhServerLevel level; private int levelHashCode; - @Override public int getLevelHashCode() { return levelHashCode; } + @Override + public int getLevelHashCode() { return this.levelHashCode; } private DhChunkPos chunkPos; private ByteBuf dataBuffer; @@ -58,18 +59,19 @@ public class FullDataPartialUpdateMessage extends NetworkMessage implements ILev this.levelHashCode = level.getLevelWrapper().getDimensionType().getDimensionName().hashCode(); } + @Override public void encode(ByteBuf out) { try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { DhDataOutputStream dhOutputStream = new DhDataOutputStream(outputStream); - fullDataAccessor.writeToStream(dhOutputStream, level); + this.fullDataAccessor.writeToStream(dhOutputStream, this.level); dhOutputStream.flush(); - out.writeInt(levelHashCode); + out.writeInt(this.levelHashCode); - out.writeInt(fullDataAccessor.chunkPos.x); - out.writeInt(fullDataAccessor.chunkPos.z); + out.writeInt(this.fullDataAccessor.chunkPos.x); + out.writeInt(this.fullDataAccessor.chunkPos.z); out.writeInt(outputStream.size()); out.writeBytes(outputStream.toByteArray()); @@ -80,11 +82,12 @@ public class FullDataPartialUpdateMessage extends NetworkMessage implements ILev } } + @Override public void decode(ByteBuf in) { - levelHashCode = in.readInt(); + this.levelHashCode = in.readInt(); - chunkPos = new DhChunkPos(in.readInt(), in.readInt()); + this.chunkPos = new DhChunkPos(in.readInt(), in.readInt()); this.dataBuffer = in.readBytes(in.readInt()); } @@ -92,26 +95,29 @@ public class FullDataPartialUpdateMessage extends NetworkMessage implements ILev @Nullable public ChunkSizedFullDataAccessor getFullDataSource(IDhLevel level) throws IOException, InterruptedException { - if (isLevelInvalid(level.getLevelWrapper())) return null; - - try (ByteBufInputStream inputStream = new ByteBufInputStream(dataBuffer)) + if (!this.isLevelValid(level.getLevelWrapper())) { - ChunkSizedFullDataAccessor result = new ChunkSizedFullDataAccessor(chunkPos); + return null; + } + + try (ByteBufInputStream inputStream = new ByteBufInputStream(this.dataBuffer)) + { + ChunkSizedFullDataAccessor result = new ChunkSizedFullDataAccessor(this.chunkPos); result.populateFromStream(new DhDataInputStream(inputStream), level); return result; } finally { - dataBuffer.release(); + this.dataBuffer.release(); } } @Override public String toString() { return super.toString( - "levelHashCode=" + levelHashCode + - ", chunkPos=" + chunkPos + - ", dataBuffer=" + dataBuffer + "levelHashCode=" + this.levelHashCode + + ", chunkPos=" + this.chunkPos + + ", dataBuffer=" + this.dataBuffer ); } From 3932ea21c28365ebae04deda60f6efcc971d4f96 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 29 Jan 2024 22:36:13 +0500 Subject: [PATCH 086/205] Limit rate+concurrency instead of only concurrency Rename post-relog update to Login sync --- .editorconfig | 2 +- .../distanthorizons/core/config/Config.java | 62 ++++++++----------- .../WorldRemoteGenerationQueue.java | 2 +- .../core/level/DhServerLevel.java | 10 +-- .../client/AbstractFullDataRequestQueue.java | 12 +++- .../client/FullDataRefreshQueue.java | 4 +- .../config/AbstractMultiplayerConfig.java | 8 +-- .../multiplayer/config/MultiplayerConfig.java | 20 +++--- .../MultiplayerConfigChangeListener.java | 6 +- .../multiplayer/server/ServerPlayerState.java | 16 ++--- .../server/ServersideMultiplayerConfig.java | 11 ++-- ...upplierBasedRateAndConcurrencyLimiter.java | 39 ++++++++++++ .../SupplierBasedRateLimiter.java | 23 +++++-- .../assets/distanthorizons/lang/en_us.json | 31 +++++----- 14 files changed, 146 insertions(+), 100 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedRateAndConcurrencyLimiter.java diff --git a/.editorconfig b/.editorconfig index 2228be96c..4661e28bb 100644 --- a/.editorconfig +++ b/.editorconfig @@ -537,7 +537,7 @@ ij_groovy_wrap_chain_calls_after_dot = false ij_groovy_wrap_long_lines = false [{*.har,*.json,*.png.mcmeta,mcmod.info,pack.mcmeta}] -indent_size = 4 +indent_size = 2 ij_json_array_wrapping = split_into_lines ij_json_keep_blank_lines_in_code = 0 ij_json_keep_indents_on_empty_lines = false diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 211508450..62c339f4c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -845,7 +845,6 @@ public class Config + "") .build(); - // TODO Write strings public static class ServerNetworking { public static ConfigEntry enableServerNetworking = new ConfigEntry.Builder() @@ -862,23 +861,6 @@ public class Config + "Note: This requires DH to be installed on the server in order to function. \n" + "") .build(); - - public static ConfigEntry enableRealTimeUpdates = new ConfigEntry.Builder() - .setServersideShortName("enableRealTimeUpdates") - .set(false) - .comment("" - + "Enables real time updates from server." - + "") - .build(); - - public static ConfigEntry enablePostRelogUpdate = new ConfigEntry.Builder() - .setServersideShortName("enablePostRelogUpdate") - .set(false) - .comment("" - + "Enables updating of LODs after relog." - + "") - .build(); - public static ConfigEntry serverPort = new ConfigEntry.Builder() .setServersideShortName("serverPort") .setMinDefaultMax(1, 25049, 65535) @@ -893,16 +875,16 @@ public class Config .setMinDefaultMax(1, 10, 100) .comment("" + "Amount of rate/concurrency limit hits in one second before disconnecting the offending clients. \n" - + "Warning: too low values can cause slower clients to disconnect prematurely.\n" - + "This setting is server-only; it does not have effect on client.\n" + + "This setting is server-only; it does not have effect on the client.\n" + "") .build(); - public static ConfigEntry fullDataRequestConcurrencyLimit = new ConfigEntry.Builder() - .setServersideShortName("fullDataRequestConcurrencyLimit") + + public static ConfigEntry generationRequestRCLimit = new ConfigEntry.Builder() + .setServersideShortName("generationRequestRCLimit") .setMinDefaultMax(1, 20, 100) .comment("" - + "Limits the amount of sent/processed LOD *generation* requests concurrently on server, per player. \n" + + "Limits the amount of generation requests sent by client and processed by server. \n" + "") .build(); @@ -914,7 +896,7 @@ public class Config + "") .build(); - public static ConfigEntry fullDataRequestBeginDelay = new ConfigEntry.Builder() + public static ConfigEntry generationRequestBeginDelay = new ConfigEntry.Builder() .setMinDefaultMax(0, 3, 10) .comment("" + "Adds a delay in seconds before sending LOD requests, when generation is enabled. \n" @@ -922,25 +904,31 @@ public class Config + "") .build(); - public static ConfigEntry postRelogUpdateConcurrencyLimit = new ConfigEntry.Builder() - .setServersideShortName("postRelogUpdateConcurrencyLimit") - .setMinDefaultMax(1, 50, 100) + + public static ConfigEntry enableRealTimeUpdates = new ConfigEntry.Builder() + .setServersideShortName("enableRealTimeUpdates") + .set(false) .comment("" - + "Limits the amount of sent/processed LOD *update* requests concurrently on server, per player. \n" + + "Enables real time updates from server." + "") .build(); - /** - * Intentionally disabled. - * @see #enablePostRelogUpdate - */ - private static final ConfigEntry fullDataChangeSummaryRequestRateLimit = new ConfigEntry.Builder() - .setServersideShortName("fullDataChangeSummaryRequestRateLimit") - .setMinDefaultMax(1, 20, 100) + + public static ConfigEntry enableLoginDataSync = new ConfigEntry.Builder() + .setServersideShortName("enableLoginDataSync") + .set(false) .comment("" - + "Limits the amount of LOD updates the client can check within a second. \n" + + "Enables updating of saved LODs after login." + "") - .build(); + .build(); + + public static ConfigEntry loginDataSyncRCLimit = new ConfigEntry.Builder() + .setServersideShortName("loginDataSyncRCLimit") + .setMinDefaultMax(1, 50, 100) + .comment("" + + "Limits the amount of sent/processed LOD *update* requests concurrently, per player. \n" + + "") + .build(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index ee6766f38..197c5e0c5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -30,7 +30,7 @@ public class WorldRemoteGenerationQueue extends AbstractFullDataRequestQueue imp // Used to prevent requests for section very far away, as result of request list not completely filled. // Kinda a hack, since queue is not notified when file handler is done with feeding sections to generate - private static final ConfigEntry REQUEST_BEGIN_DELAY = Config.Client.Advanced.Multiplayer.ServerNetworking.fullDataRequestBeginDelay; + private static final ConfigEntry REQUEST_BEGIN_DELAY = Config.Client.Advanced.Multiplayer.ServerNetworking.generationRequestBeginDelay; private final Stopwatch requestBeginStopwatch = Stopwatch.createStarted(); private CompletableFuture genTaskPriorityRequest = CompletableFuture.completedFuture(null); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index be4003021..2d868fb06 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -127,15 +127,15 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel } else { - // Post-relog update + // Sync only - if (!serverPlayerState.config.isPostRelogUpdateEnabled()) + if (!serverPlayerState.config.isLoginDataSyncEnabled()) { msg.sendResponse(new RequestRejectedException("Operation is disabled from config.")); return; } - if (!serverPlayerState.postRelogUpdateRequestConcurrencyLimiter.tryAcquire(msg)) + if (!serverPlayerState.loginDataSyncRCLimiter.tryAcquire(msg)) { return; } @@ -143,14 +143,14 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel Integer serverChecksum = this.serverside.dataFileHandler.repo.getChecksumForSection(msg.sectionPos); if (serverChecksum == null || serverChecksum.equals(msg.checksum)) { - serverPlayerState.postRelogUpdateRequestConcurrencyLimiter.release(); + serverPlayerState.loginDataSyncRCLimiter.release(); msg.sendResponse(new FullDataSourceResponseMessage(null, this)); return; } this.serverside.dataFileHandler.getAsync(msg.sectionPos).thenAccept(fullDataSource -> { - serverPlayerState.postRelogUpdateRequestConcurrencyLimiter.release(); + serverPlayerState.loginDataSyncRCLimiter.release(); msg.sendResponse(new FullDataSourceResponseMessage((CompleteFullDataSource) fullDataSource, this)); }); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index 67755ff15..b0e9d1b77 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -16,6 +16,7 @@ import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateLimiter; import io.netty.channel.ChannelException; import org.apache.logging.log4j.Logger; @@ -51,6 +52,8 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, private final Set alreadyRequestedPositions = ConcurrentHashMap.newKeySet(); + private final SupplierBasedRateLimiter rateLimiter = new SupplierBasedRateLimiter<>(this::getRequestConcurrencyLimit); + protected abstract int getRequestConcurrencyLimit(); @@ -104,6 +107,12 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, && this.getInProgressTaskCount() < this.getRequestConcurrencyLimit() && this.pendingTasksSemaphore.tryAcquire()) { + if (!this.rateLimiter.tryAcquire()) + { + this.pendingTasksSemaphore.release(); + break; + } + this.sendNewRequest(targetPos); } @@ -277,8 +286,7 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, public RequestQueueEntry( Consumer chunkDataConsumer, - @Nullable - Integer currentChecksum) + @Nullable Integer currentChecksum) { this.chunkDataConsumer = chunkDataConsumer; this.currentChecksum = currentChecksum; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/FullDataRefreshQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/FullDataRefreshQueue.java index 8e92afd4c..862ababaf 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/FullDataRefreshQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/FullDataRefreshQueue.java @@ -12,7 +12,7 @@ public class FullDataRefreshQueue extends AbstractFullDataRequestQueue } @Override - protected int getRequestConcurrencyLimit() { return this.networkState.config.postRelogUpdateConcurrencyLimit; } + protected int getRequestConcurrencyLimit() { return this.networkState.config.loginDataSyncRCLimit; } @Override protected String getQueueName() { return "Data Refresh Queue"; } @@ -20,7 +20,7 @@ public class FullDataRefreshQueue extends AbstractFullDataRequestQueue @Override public boolean tick(DhBlockPos2D targetPos) { - if (!this.networkState.config.postRelogUpdateEnabled) + if (!this.networkState.config.loginDataSyncEnabled) { return false; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java index 7086c7ac3..5c62b7187 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java @@ -10,8 +10,8 @@ public abstract class AbstractMultiplayerConfig implements INetworkObject public abstract int getFullDataRequestConcurrencyLimit(); public abstract int getGenTaskPriorityRequestRateLimit(); public abstract boolean isRealTimeUpdatesEnabled(); - public abstract boolean isPostRelogUpdateEnabled(); - public abstract int getPostRelogUpdateConcurrencyLimit(); + public abstract boolean isLoginDataSyncEnabled(); + public abstract int getLoginDataSyncRCLimit(); @Override public void encode(ByteBuf out) @@ -21,8 +21,8 @@ public abstract class AbstractMultiplayerConfig implements INetworkObject out.writeInt(this.getFullDataRequestConcurrencyLimit()); out.writeInt(this.getGenTaskPriorityRequestRateLimit()); out.writeBoolean(this.isRealTimeUpdatesEnabled()); - out.writeBoolean(this.isPostRelogUpdateEnabled()); - out.writeInt(this.getPostRelogUpdateConcurrencyLimit()); + out.writeBoolean(this.isLoginDataSyncEnabled()); + out.writeInt(this.getLoginDataSyncRCLimit()); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java index 578e84fac..680ef3ef2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java @@ -13,7 +13,7 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig public boolean distantGenerationEnabled = Config.Client.Advanced.WorldGenerator.enableDistantGeneration.get(); @Override public boolean isDistantGenerationEnabled() { return this.distantGenerationEnabled; } - public int fullDataRequestConcurrencyLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.fullDataRequestConcurrencyLimit.get(); + public int fullDataRequestConcurrencyLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.generationRequestRCLimit.get(); @Override public int getFullDataRequestConcurrencyLimit() { return this.fullDataRequestConcurrencyLimit; } public int genTaskPriorityRequestRateLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.genTaskPriorityRequestRateLimit.get(); @@ -22,11 +22,11 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig public boolean realTimeUpdatesEnabled = Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates.get(); @Override public boolean isRealTimeUpdatesEnabled() { return this.realTimeUpdatesEnabled; } - public boolean postRelogUpdateEnabled = Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate.get(); - @Override public boolean isPostRelogUpdateEnabled() { return this.postRelogUpdateEnabled; } + public boolean loginDataSyncEnabled = Config.Client.Advanced.Multiplayer.ServerNetworking.enableLoginDataSync.get(); + @Override public boolean isLoginDataSyncEnabled() { return this.loginDataSyncEnabled; } - public int postRelogUpdateConcurrencyLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.postRelogUpdateConcurrencyLimit.get(); - @Override public int getPostRelogUpdateConcurrencyLimit() { return this.postRelogUpdateConcurrencyLimit; } + public int loginDataSyncRCLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.loginDataSyncRCLimit.get(); + @Override public int getLoginDataSyncRCLimit() { return this.loginDataSyncRCLimit; } @Override public void decode(ByteBuf in) @@ -36,20 +36,20 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig this.fullDataRequestConcurrencyLimit = in.readInt(); this.genTaskPriorityRequestRateLimit = in.readInt(); this.realTimeUpdatesEnabled = in.readBoolean(); - this.postRelogUpdateEnabled = in.readBoolean(); - this.postRelogUpdateConcurrencyLimit = in.readInt(); + this.loginDataSyncEnabled = in.readBoolean(); + this.loginDataSyncRCLimit = in.readInt(); } @Override public String toString() { return "MultiplayerConfig{" + - "renderDistance=" + this.renderDistanceRadius + + "renderDistanceRadius=" + this.renderDistanceRadius + ", distantGenerationEnabled=" + this.distantGenerationEnabled + ", fullDataRequestConcurrencyLimit=" + this.fullDataRequestConcurrencyLimit + ", genTaskPriorityRequestRateLimit=" + this.genTaskPriorityRequestRateLimit + ", realTimeUpdatesEnabled=" + this.realTimeUpdatesEnabled + - ", postRelogUpdatesEnabled=" + this.postRelogUpdateEnabled + - ", postRelogUpdateConcurrencyLimit=" + this.postRelogUpdateConcurrencyLimit + + ", loginDataSyncEnabled=" + this.loginDataSyncEnabled + + ", loginDataSyncRCLimit=" + this.loginDataSyncRCLimit + '}'; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java index 76ba40c7a..3624c2ed4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java @@ -13,11 +13,11 @@ public class MultiplayerConfigChangeListener implements Closeable private static final ConfigEntry[] CONFIG_ENTRIES = new ConfigEntry[] { Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius, Config.Client.Advanced.WorldGenerator.enableDistantGeneration, - Config.Client.Advanced.Multiplayer.ServerNetworking.fullDataRequestConcurrencyLimit, + Config.Client.Advanced.Multiplayer.ServerNetworking.generationRequestRCLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.genTaskPriorityRequestRateLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates, - Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate, - Config.Client.Advanced.Multiplayer.ServerNetworking.postRelogUpdateConcurrencyLimit, + Config.Client.Advanced.Multiplayer.ServerNetworking.enableLoginDataSync, + Config.Client.Advanced.Multiplayer.ServerNetworking.loginDataSyncRCLimit, }; private final ArrayList changeListeners = new ArrayList<>(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index 3ab07416f..97c75df92 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -4,7 +4,7 @@ import com.seibel.distanthorizons.core.network.IConnection; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceRequestMessage; import com.seibel.distanthorizons.core.network.messages.fullData.generation.priority.GenTaskPriorityRequestMessage; -import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedConcurrencyLimiter; +import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateAndConcurrencyLimiter; import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateLimiter; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import org.jetbrains.annotations.NotNull; @@ -24,10 +24,10 @@ public class ServerPlayerState ignored -> this.connection.disconnect("You have been repeatedly exceeding rate/concurrency limits.") ); - public final SupplierBasedConcurrencyLimiter fullDataRequestConcurrencyLimiter = new SupplierBasedConcurrencyLimiter<>( - () -> ServerNetworking.fullDataRequestConcurrencyLimit.get(), + public final SupplierBasedRateAndConcurrencyLimiter fullDataRequestConcurrencyLimiter = new SupplierBasedRateAndConcurrencyLimiter<>( + () -> ServerNetworking.generationRequestRCLimit.get(), msg -> { - msg.sendResponse(new RateLimitedException("Max concurrent full data requests: " + this.config.getFullDataRequestConcurrencyLimit())); + msg.sendResponse(new RateLimitedException("Full data request rate/concurrency limit: " + this.config.getFullDataRequestConcurrencyLimit())); this.rateLimitKickTrigger.tryAcquire(null); } ); @@ -35,15 +35,15 @@ public class ServerPlayerState public final SupplierBasedRateLimiter genTaskPriorityRequestRateLimiter = new SupplierBasedRateLimiter<>( () -> ServerNetworking.genTaskPriorityRequestRateLimit.get(), msg -> { - msg.sendResponse(new RateLimitedException("Max section checks per second: " + this.config.getFullDataRequestConcurrencyLimit())); + msg.sendResponse(new RateLimitedException("Generation task priority check rate limit: " + this.config.getFullDataRequestConcurrencyLimit())); this.rateLimitKickTrigger.tryAcquire(null); } ); - public final SupplierBasedConcurrencyLimiter postRelogUpdateRequestConcurrencyLimiter = new SupplierBasedConcurrencyLimiter<>( - () -> ServerNetworking.postRelogUpdateConcurrencyLimit.get(), + public final SupplierBasedRateAndConcurrencyLimiter loginDataSyncRCLimiter = new SupplierBasedRateAndConcurrencyLimiter<>( + () -> ServerNetworking.loginDataSyncRCLimit.get(), msg -> { - msg.sendResponse(new RateLimitedException("Max concurrent post-relog update requests: " + this.config.getPostRelogUpdateConcurrencyLimit())); + msg.sendResponse(new RateLimitedException("Data sync rate/concurrency limit: " + this.config.getLoginDataSyncRCLimit())); this.rateLimitKickTrigger.tryAcquire(null); } ); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java index e16ccac79..58a9b8f64 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java @@ -27,7 +27,7 @@ public class ServersideMultiplayerConfig extends AbstractMultiplayerConfig @Override public int getFullDataRequestConcurrencyLimit() { - return Math.min(this.clientConfig.fullDataRequestConcurrencyLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.fullDataRequestConcurrencyLimit.get()); + return Math.min(this.clientConfig.fullDataRequestConcurrencyLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.generationRequestRCLimit.get()); } @Override @@ -43,14 +43,15 @@ public class ServersideMultiplayerConfig extends AbstractMultiplayerConfig } @Override - public boolean isPostRelogUpdateEnabled() { - return this.clientConfig.postRelogUpdateEnabled && Config.Client.Advanced.Multiplayer.ServerNetworking.enablePostRelogUpdate.get(); + public boolean isLoginDataSyncEnabled() + { + return this.clientConfig.loginDataSyncEnabled && Config.Client.Advanced.Multiplayer.ServerNetworking.enableLoginDataSync.get(); } @Override - public int getPostRelogUpdateConcurrencyLimit() + public int getLoginDataSyncRCLimit() { - return Math.min(this.clientConfig.postRelogUpdateConcurrencyLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.postRelogUpdateConcurrencyLimit.get()); + return Math.min(this.clientConfig.loginDataSyncRCLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.loginDataSyncRCLimit.get()); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedRateAndConcurrencyLimiter.java b/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedRateAndConcurrencyLimiter.java new file mode 100644 index 000000000..e41b28f92 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedRateAndConcurrencyLimiter.java @@ -0,0 +1,39 @@ +package com.seibel.distanthorizons.core.util.ratelimiting; + +import java.util.function.Consumer; +import java.util.function.Supplier; + +public class SupplierBasedRateAndConcurrencyLimiter +{ + private final SupplierBasedRateLimiter rateLimiter; + private final SupplierBasedConcurrencyLimiter concurrencyLimiter; + + public SupplierBasedRateAndConcurrencyLimiter(Supplier maxRateSupplier, Consumer onFailureConsumer) + { + this.rateLimiter = new SupplierBasedRateLimiter<>(maxRateSupplier, onFailureConsumer); + this.concurrencyLimiter = new SupplierBasedConcurrencyLimiter<>(maxRateSupplier, onFailureConsumer); + } + + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + public boolean tryAcquire(T context) + { + if (!this.concurrencyLimiter.tryAcquire(context)) + { + return false; + } + + if (!this.rateLimiter.tryAcquire(context)) + { + this.concurrencyLimiter.release(); + return false; + } + + return true; + } + + public void release() + { + this.concurrencyLimiter.release(); + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedRateLimiter.java b/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedRateLimiter.java index 99ec05b58..bc7e2ff05 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedRateLimiter.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedRateLimiter.java @@ -18,6 +18,7 @@ public class SupplierBasedRateLimiter private final RateLimiter rateLimiter = RateLimiter.create(1); + public SupplierBasedRateLimiter(Supplier maxRateSupplier) { this(maxRateSupplier, ignored -> { }); } public SupplierBasedRateLimiter(Supplier maxRateSupplier, Consumer onFailureConsumer) { this.maxRateSupplier = maxRateSupplier; @@ -27,30 +28,40 @@ public class SupplierBasedRateLimiter @SuppressWarnings("BooleanMethodIsAlwaysInverted") public boolean tryAcquire(T context) { - return tryAcquire(context, 1); + return this.tryAcquire(context, 1); + } + + public boolean tryAcquire() + { + return this.tryAcquire(null, 1); } public int acquireOrDrain(int permits) { - rateLimiter.setRate(maxRateSupplier.get()); + this.rateLimiter.setRate(this.maxRateSupplier.get()); - if (rateLimiter.tryAcquire(permits)) + if (this.rateLimiter.tryAcquire(permits)) + { return permits; + } int acquired = 0; while ((permits /= 2) > 0) { - if (rateLimiter.tryAcquire(permits)) + if (this.rateLimiter.tryAcquire(permits)) + { acquired += permits; + } } + return acquired; } public boolean tryAcquire(T context, int permits) { - rateLimiter.setRate(maxRateSupplier.get()); + this.rateLimiter.setRate(this.maxRateSupplier.get()); - if (!rateLimiter.tryAcquire(permits)) + if (!this.rateLimiter.tryAcquire(permits)) { this.onFailureConsumer.accept(context); return false; diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index 391e77989..7365919ba 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -345,15 +345,23 @@ "Multiplayer", "distanthorizons.config.client.advanced.multiplayer.serverNetworking": "Server Networking", - + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableServerNetworking": "Enable Server Networking", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.fullDataRequestConcurrencyLimit": "Sections - request concurrency limit", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.genTaskPriorityRequestRateLimit": "Generation task priority - request rate limit", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.fullDataRequestBeginDelay": "Generation request begin delay", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableRealTimeUpdates": "Enable Real Time Updates", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enablePostRelogUpdates": "Enable Post Relog Updates", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableServerNetworking.@tooltip": "§6Attention:§r this feature is not fully implemented. \n\nIf true Distant Horizons will attempt to communicate with the connected \nserver in order to load LODs outside your vanilla render distance. \n\nNote: This requires DH to be installed on the server in order to function.", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.serverPort": "Server Port", - + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.serverPort.@tooltip": "The port on the server that's used for sending LOD data.", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestRCLimit": "Gen task rate/concurrency limit", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestRCLimit.@tooltip": "Limits the amount of generation requests sent by client and processed by server.", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.genTaskPriorityRequestRateLimit": "Gen task priority check rate limit", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.genTaskPriorityRequestRateLimit.@tooltip": "Limits the amount of LOD sections that the client can request states for, per second.", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestBeginDelay": "Generation request begin delay", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestBeginDelay.@tooltip": "Adds a delay in seconds before sending LOD requests, when generation is enabled. \nIncrease this value if initial generation starts too far away.", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableRealTimeUpdates": "Enable Real Time Updates", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableRealTimeUpdates.@tooltip": "Enables real time updates from server.", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableLoginDataSync": "Synchronize LODs on Login", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableLoginDataSync.@tooltip": "Enables updating of saved LODs after login.", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.loginDataSyncRCLimit": "Login sync rate/concurrency limit", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.loginDataSyncRCLimit.@tooltip": "Limits the amount of sent/processed LOD *update* requests concurrently, per player.", "distanthorizons.config.client.advanced.multiplayer.serverFolderNameMode": "Server Folder Mode", "distanthorizons.config.client.advanced.multiplayer.serverFolderNameMode.@tooltip": @@ -366,15 +374,6 @@ "Networked Multiverse Support", "distanthorizons.config.client.advanced.multiplayer.enableMultiverseNetworking.@tooltip": "If true Distant Horizons will attempt to communicate with the connected \nserver in order to improve multiverse support.", - "distanthorizons.config.client.advanced.multiplayer.enableServerNetworking": - "§4Partially implemented§r - Server Support", - "distanthorizons.config.client.advanced.multiplayer.enableServerNetworking.@tooltip": - "§6Attention:§r this feature is not fully implemented. \n\nIf true Distant Horizons will attempt to communicate with the connected \nserver in order to load LODs outside your vanilla render distance. \n\nNote: This requires DH to be installed on the server in order to function.", - "distanthorizons.config.client.advanced.multiplayer.serverNetworkingRateLimit": - "Server Support - Request Rate Limit", - "distanthorizons.config.client.advanced.multiplayer.serverNetworkingRateLimit.@tooltip": - "Limits the amount of sent/processed LOD requests concurrently. \n\nNote: Server can set its own rate limit.", - "distanthorizons.config.client.advanced.multiThreading": From 3f823c58cce80998a91858f8875f1df81d479a76 Mon Sep 17 00:00:00 2001 From: s809 <11816467-s809@users.noreply.gitlab.com> Date: Fri, 2 Feb 2024 05:48:36 +0000 Subject: [PATCH 087/205] Fix exception while saving new data --- .../core/file/fullDatafile/RemoteFullDataFileHandler.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java index 68385438f..2b53bbe30 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataFileHandler.java @@ -51,7 +51,12 @@ public class RemoteFullDataFileHandler extends GeneratedFullDataFileHandler } pos.forEachChildAtLevel(DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL, childPos -> { - int checksum = Objects.requireNonNull(this.repo.getChecksumForSection(childPos)); + Integer checksum = this.repo.getChecksumForSection(childPos); + if (checksum == null) + { + return; + } + this.dataRefreshQueue.submitRequest(childPos, this.level::updateDataSourcesWithChunkData, checksum); }); From 7d72e823253d443065df576db94ea443d76591af Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 3 Feb 2024 20:17:21 +0500 Subject: [PATCH 088/205] Change a comment in Config --- .../seibel/distanthorizons/core/config/Config.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 62c339f4c..016de628c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -157,7 +157,14 @@ public class Config public static ConfigEntry lodChunkRenderDistanceRadius = new ConfigEntry.Builder() .setServersideShortName("renderDistanceRadius") .setMinDefaultMax(32, 128, 4096) - .comment("The radius of the mod's render distance. (measured in chunks)") + .comment("" + + "The radius of the mod's render distance. (measured in chunks)\n" + + "On server changes the distance players will receive real time updates for, if enabled." + + "\n" + + "Note for servers:\n" + + "This setting does not prevent players from generating farther out.\n" + + "If you want to limit performance impact, change rate/concurrency (RC) limits\n" + + "and thread count/runtime ratio settings instead.") .setPerformance(EConfigEntryPerformance.HIGH) .build(); @@ -677,10 +684,7 @@ public class Config .set(true) .comment("" + " Should Distant Horizons slowly generate LODs \n" - + " outside the vanilla render distance?\n" - + "\n" - + " Note: when on a server, distant generation isn't supported \n" - + " and will always be disabled.") + + " outside the vanilla render distance?") .build(); public static ConfigEntry distantGeneratorMode = new ConfigEntry.Builder() From 0cfbe0955890ac57e6907c6598b8be8766ad2121 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 3 Feb 2024 22:24:34 +0500 Subject: [PATCH 089/205] Use ConfigBasedLogger Add sub-sectioning to server networking section --- .../distanthorizons/core/config/Config.java | 8 +++- .../core/network/IConnection.java | 8 ++-- .../core/network/NetworkClient.java | 30 ++++++------ .../core/network/NetworkEventSource.java | 46 ++++++++++++++----- .../core/network/NetworkServer.java | 14 ++++-- .../core/network/protocol/MessageHandler.java | 8 ++-- .../protocol/NetworkExceptionHandler.java | 8 ++-- .../assets/distanthorizons/lang/en_us.json | 5 +- 8 files changed, 86 insertions(+), 41 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 016de628c..582f334b9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -802,8 +802,6 @@ public class Config public static class Multiplayer { - public static ConfigCategory serverNetworking = new ConfigCategory.Builder().set(ServerNetworking.class).build(); - public static ConfigEntry serverFolderNameMode = new ConfigEntry.Builder() .set(EServerFolderNameMode.NAME_ONLY) .comment("" @@ -849,8 +847,11 @@ public class Config + "") .build(); + public static ConfigCategory serverNetworking = new ConfigCategory.Builder().set(ServerNetworking.class).build(); + public static class ServerNetworking { + public static ConfigUIComment generalSectionNote = new ConfigUIComment(); public static ConfigEntry enableServerNetworking = new ConfigEntry.Builder() .setServersideShortName("enableServerNetworking") .set(true) @@ -884,6 +885,7 @@ public class Config .build(); + public static ConfigUIComment generationSectionNote = new ConfigUIComment(); public static ConfigEntry generationRequestRCLimit = new ConfigEntry.Builder() .setServersideShortName("generationRequestRCLimit") .setMinDefaultMax(1, 20, 100) @@ -909,6 +911,7 @@ public class Config .build(); + public static ConfigUIComment realTimeUpdatesSectionNote = new ConfigUIComment(); public static ConfigEntry enableRealTimeUpdates = new ConfigEntry.Builder() .setServersideShortName("enableRealTimeUpdates") .set(false) @@ -918,6 +921,7 @@ public class Config .build(); + public static ConfigUIComment loginDataSyncSectionNote = new ConfigUIComment(); public static ConfigEntry enableLoginDataSync = new ConfigEntry.Builder() .setServersideShortName("enableLoginDataSync") .set(false) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java b/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java index 4cb494688..8399cf9a4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java @@ -1,5 +1,7 @@ package com.seibel.distanthorizons.core.network; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; @@ -7,14 +9,14 @@ import io.netty.channel.ChannelException; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import java.net.SocketAddress; import java.util.concurrent.CompletableFuture; public interface IConnection { - Logger LOGGER = LogManager.getLogger(); + ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), + () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); ChannelHandlerContext getChannelContext(); NetworkEventSource getRequestHandler(); @@ -26,7 +28,7 @@ public interface IConnection default CompletableFuture sendMessage(NetworkMessage message) { - LOGGER.trace("Sending message: " + message); + LOGGER.debug("Sending message: " + message); CompletableFuture future = new CompletableFuture<>(); ChannelHandlerContext ctx = this.getChannelContext(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index be323d3d3..f6b2656d4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -19,19 +19,19 @@ package com.seibel.distanthorizons.core.network; -import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.network.messages.base.CloseEvent; import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage; import com.seibel.distanthorizons.core.network.messages.base.HelloMessage; import com.seibel.distanthorizons.core.network.protocol.MessageHandler; import com.seibel.distanthorizons.core.network.protocol.NetworkChannelInitializer; -import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.util.concurrent.DefaultThreadFactory; -import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; import java.net.InetSocketAddress; import java.util.EnumSet; @@ -40,7 +40,8 @@ import java.util.concurrent.TimeUnit; public class NetworkClient extends NetworkEventSource implements IConnection, AutoCloseable { - private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), + () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); private enum EConnectionState { @@ -67,7 +68,7 @@ public class NetworkClient extends NetworkEventSource implements IConnection, Au public boolean isClosed() { return closedStates.contains(this.connectionState); } private boolean ready; /** Indicates whether the connection is established and first message is sent. */ - public boolean isReady() { return ready; } + public boolean isReady() { return this.ready; } private final EventLoopGroup workerGroup = new NioEventLoopGroup(0, new DefaultThreadFactory("DH-Network - Client Thread")); private final Bootstrap clientBootstrap = new Bootstrap() @@ -98,7 +99,7 @@ public class NetworkClient extends NetworkEventSource implements IConnection, Au { this.registerHandler(CloseReasonMessage.class, closeReasonMessage -> { - LOGGER.info(closeReasonMessage.reason); + LOGGER.warn(closeReasonMessage.reason); this.connectionState = EConnectionState.CLOSING; }); @@ -114,7 +115,10 @@ public class NetworkClient extends NetworkEventSource implements IConnection, Au public void startConnecting() { - if (!isInitialState()) return; + if (!this.isInitialState()) + { + return; + } this.connect(); } @@ -123,8 +127,6 @@ public class NetworkClient extends NetworkEventSource implements IConnection, Au LOGGER.info("Connecting to server: "+this.address); this.connectionState = EConnectionState.OPEN; - // FIXME sometimes this causes the MC connection to crash - // this might happen if the URL can't be converted to a IP (IE UnknownHostException) ChannelFuture connectFuture = this.clientBootstrap.connect(this.address); this.channel = connectFuture.channel(); @@ -132,23 +134,25 @@ public class NetworkClient extends NetworkEventSource implements IConnection, Au { if (!channelFuture.isSuccess()) { - LOGGER.warn("Connection failed: "+channelFuture.cause()); + LOGGER.info("Connection failed: " + channelFuture.cause()); return; } - sendMessage(new HelloMessage()); - ready = true; + this.sendMessage(new HelloMessage()); + this.ready = true; }); this.channel.closeFuture().addListener((ChannelFuture channelFuture) -> { - ready = false; + this.ready = false; this.completeAllFuturesExceptionally(channelFuture.cause() != null ? channelFuture.cause() : new ChannelException("Channel is closed.")); if (this.connectionState != EConnectionState.OPEN) + { return; + } this.reconnectAttempts--; LOGGER.info("Reconnection attempts left: [" + this.reconnectAttempts + "] of [" + FAILURE_RECONNECT_ATTEMPTS + "]."); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index cd860c6ca..3fb3a3482 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -19,7 +19,8 @@ package com.seibel.distanthorizons.core.network; -import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.network.messages.base.CancelMessage; import com.seibel.distanthorizons.core.network.messages.base.CloseEvent; import com.seibel.distanthorizons.core.network.messages.base.ExceptionMessage; @@ -27,7 +28,7 @@ import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMe import com.seibel.distanthorizons.core.network.protocol.MessageRegistry; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import io.netty.channel.ChannelException; -import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; import java.io.InvalidClassException; import java.util.HashSet; @@ -40,7 +41,8 @@ import java.util.function.Consumer; public abstract class NetworkEventSource { - private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), + () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); protected final ConcurrentMap, Set>> handlers = new ConcurrentHashMap<>(); private final ConcurrentMap> pendingFutures = new ConcurrentHashMap<>(); @@ -67,7 +69,7 @@ public abstract class NetworkEventSource if (message instanceof FutureTrackableNetworkMessage) { FutureTrackableNetworkMessage trackableMessage = (FutureTrackableNetworkMessage)message; - ConcurrentMap subMap = pendingFutures.get(message.getConnection()); + ConcurrentMap subMap = this.pendingFutures.get(message.getConnection()); if (subMap != null) { FutureResponseData responseData = subMap.get(trackableMessage.futureId); @@ -76,17 +78,25 @@ public abstract class NetworkEventSource handled = true; if (message instanceof ExceptionMessage) + { responseData.future.completeExceptionally(((ExceptionMessage) message).exception); + } else if (message.getClass() != responseData.responseClass) + { responseData.future.completeExceptionally(new InvalidClassException("Response with invalid type: expected " + responseData.responseClass.getSimpleName() + ", got:" + message)); + } else + { responseData.future.complete(trackableMessage); + } } } } if (!handled && message.warnWhenUnhandled()) + { LOGGER.warn("Unhandled message: " + message); + } } protected void addNewConnection(IConnection connection) @@ -101,7 +111,9 @@ public abstract class NetworkEventSource { // Will throw if the handler class is not found if (handlerClass != CloseEvent.class) + { MessageRegistry.INSTANCE.getMessageId(handlerClass); + } return new HashSet<>(); }) .add((Consumer) handlerImplementation); @@ -123,16 +135,20 @@ public abstract class NetworkEventSource { if (!(throwable instanceof ChannelException)) { - ConcurrentMap subMap = pendingFutures.get(connection); + ConcurrentMap subMap = this.pendingFutures.get(connection); if (subMap != null) + { subMap.remove(msg.futureId); + } } if (throwable instanceof CancellationException) + { msg.sendResponse(new CancelMessage()); + } }); - ConcurrentMap subMap = pendingFutures.get(connection); + ConcurrentMap subMap = this.pendingFutures.get(connection); if (subMap == null) { // Was deleted before adding @@ -140,9 +156,10 @@ public abstract class NetworkEventSource return responseFuture; } subMap.put(msg.futureId, new FutureResponseData(responseClass, responseFuture)); - if (!pendingFutures.containsKey(connection)) + if (!this.pendingFutures.containsKey(connection)) { // Was deleted while adding + // Note: removal from subMap will happen in whenComplete above responseFuture.completeExceptionally(connection.getCloseReason()); return responseFuture; } @@ -153,23 +170,30 @@ public abstract class NetworkEventSource protected final void completeAllFuturesExceptionally(IConnection connection, Throwable cause) { - ConcurrentMap map = pendingFutures.remove(connection); - if (map == null) return; + ConcurrentMap map = this.pendingFutures.remove(connection); + if (map == null) + { + return; + } for (FutureResponseData responseData : map.values()) + { responseData.future.completeExceptionally(cause); + } } protected final void completeAllFuturesExceptionally(Throwable cause) { - for (IConnection connection : pendingFutures.keySet()) + for (IConnection connection : this.pendingFutures.keySet()) + { this.completeAllFuturesExceptionally(connection, cause); + } } public void close() { this.handlers.clear(); - completeAllFuturesExceptionally(new ChannelException(this.getClass().getSimpleName()+" is closed.")); + this.completeAllFuturesExceptionally(new ChannelException(this.getClass().getSimpleName() + " is closed.")); } private static class FutureResponseData diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java index c3d0df0d3..2eff15ac0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java @@ -20,28 +20,32 @@ package com.seibel.distanthorizons.core.network; import com.google.common.collect.MapMaker; -import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.network.messages.base.CloseEvent; import com.seibel.distanthorizons.core.network.messages.base.HelloMessage; import com.seibel.distanthorizons.core.network.protocol.MessageHandler; import com.seibel.distanthorizons.core.network.protocol.NetworkChannelInitializer; -import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; import com.seibel.distanthorizons.coreapi.ModInfo; import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.*; +import io.netty.channel.Channel; +import io.netty.channel.ChannelFuture; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.EventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; import io.netty.util.concurrent.DefaultThreadFactory; -import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicBoolean; public class NetworkServer extends NetworkEventSource implements AutoCloseable { - private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), + () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); private final int port; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java index 55122118d..dd6df7fc9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java @@ -19,12 +19,13 @@ package com.seibel.distanthorizons.core.network.protocol; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.network.messages.base.CloseEvent; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import java.util.function.BiConsumer; @@ -33,7 +34,8 @@ import java.util.function.Consumer; @ChannelHandler.Sharable public class MessageHandler extends SimpleChannelInboundHandler { - private static final Logger LOGGER = LogManager.getLogger(); + private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), + () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); private final BiConsumer messageConsumer; private final Consumer channelActiveConsumer; @@ -47,7 +49,7 @@ public class MessageHandler extends SimpleChannelInboundHandler @Override protected void channelRead0(ChannelHandlerContext channelContext, NetworkMessage message) { - LOGGER.trace("Received message: " + message); + LOGGER.debug("Received message: " + message); this.messageConsumer.accept(channelContext, message); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkExceptionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkExceptionHandler.java index 3de51d35f..ad0ee862b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkExceptionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkExceptionHandler.java @@ -19,14 +19,16 @@ package com.seibel.distanthorizons.core.network.protocol; -import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; -import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; public class NetworkExceptionHandler extends ChannelInboundHandlerAdapter { - private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), + () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); @Override public void exceptionCaught(ChannelHandlerContext channelContext, Throwable cause) diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index 7365919ba..4a67e3f3e 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -345,19 +345,22 @@ "Multiplayer", "distanthorizons.config.client.advanced.multiplayer.serverNetworking": "Server Networking", - + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generalSectionNote": " \u25cf General", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableServerNetworking": "Enable Server Networking", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableServerNetworking.@tooltip": "§6Attention:§r this feature is not fully implemented. \n\nIf true Distant Horizons will attempt to communicate with the connected \nserver in order to load LODs outside your vanilla render distance. \n\nNote: This requires DH to be installed on the server in order to function.", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.serverPort": "Server Port", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.serverPort.@tooltip": "The port on the server that's used for sending LOD data.", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationSectionNote": " \u25cf Generation", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestRCLimit": "Gen task rate/concurrency limit", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestRCLimit.@tooltip": "Limits the amount of generation requests sent by client and processed by server.", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.genTaskPriorityRequestRateLimit": "Gen task priority check rate limit", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.genTaskPriorityRequestRateLimit.@tooltip": "Limits the amount of LOD sections that the client can request states for, per second.", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestBeginDelay": "Generation request begin delay", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestBeginDelay.@tooltip": "Adds a delay in seconds before sending LOD requests, when generation is enabled. \nIncrease this value if initial generation starts too far away.", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.realTimeUpdatesSectionNote": " \u25cf Real Time Updates", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableRealTimeUpdates": "Enable Real Time Updates", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableRealTimeUpdates.@tooltip": "Enables real time updates from server.", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.loginDataSyncSectionNote": " \u25cf Login Data Sync", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableLoginDataSync": "Synchronize LODs on Login", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableLoginDataSync.@tooltip": "Enables updating of saved LODs after login.", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.loginDataSyncRCLimit": "Login sync rate/concurrency limit", From e5e45510381e3054d4d7d18aabde442f86ae6d53 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 3 Feb 2024 23:02:31 +0500 Subject: [PATCH 090/205] Some logging changes --- .../client/AbstractFullDataRequestQueue.java | 16 +++++++++------- .../multiplayer/client/ClientNetworkState.java | 13 ++++++++----- .../core/network/NetworkEventSource.java | 3 ++- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index b0e9d1b77..c90a36715 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -1,14 +1,16 @@ package com.seibel.distanthorizons.core.multiplayer.client; import com.google.common.base.Stopwatch; +import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.types.ConfigEntry; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.level.IDhClientLevel; -import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.logging.ConfigBasedSpamLogger; import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; +import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceRequestMessage; import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceResponseMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; @@ -18,7 +20,7 @@ import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateLimiter; import io.netty.channel.ChannelException; -import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; import javax.annotation.CheckForNull; import javax.annotation.Nullable; @@ -32,7 +34,8 @@ import java.util.function.Consumer; public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, AutoCloseable { - private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + private static final ConfigBasedSpamLogger LOGGER = new ConfigBasedSpamLogger(LogManager.getLogger(), + () -> Config.Client.Advanced.Logging.logNetworkEvent.get(), 3); protected static final long SHUTDOWN_TIMEOUT_SECONDS = 5; @@ -81,7 +84,7 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, if (this.alreadyRequestedPositions.contains(sectionPos)) { // temporary solution to prevent requesting the same section multiple times - LOGGER.trace("Duplicate section " + sectionPos + ". Skipping..."); + LOGGER.debug("Duplicate section " + sectionPos + ". Skipping..."); return CompletableFuture.completedFuture(false); } this.alreadyRequestedPositions.add(sectionPos); @@ -169,7 +172,6 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, } this.waitingTasks.remove(sectionPos); - LOGGER.debug("FullDataSourceResponseMessage " + sectionPos); CompleteFullDataSource fullDataSource = response.getFullDataSource(sectionPos, this.level); @@ -183,9 +185,9 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, LodUtil.assertTrue(this.changedOnly, "Received empty data source response for not changed-only request"); } } - catch (InvalidLevelException ignored) + catch (InvalidLevelException | RequestRejectedException ignored) { - // We're too late + // We're too late / some cases might trigger a bunch of expected rejections } catch (ChannelException | RateLimitedException e) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index 9883b393a..cb40882e3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -1,22 +1,24 @@ package com.seibel.distanthorizons.core.multiplayer.client; -import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; -import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkClient; +import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.messages.base.AckMessage; import com.seibel.distanthorizons.core.network.messages.base.HelloMessage; import com.seibel.distanthorizons.core.network.messages.session.PlayerUUIDMessage; import com.seibel.distanthorizons.core.network.messages.session.RemotePlayerConfigMessage; -import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; import java.io.Closeable; import java.util.UUID; public class ClientNetworkState implements Closeable { - protected static final Logger LOGGER = DhLoggerBuilder.getLogger(); + protected static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), + () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); private final NetworkClient client; private final UUID playerUUID; @@ -50,7 +52,7 @@ public class ClientNetworkState implements Closeable { LOGGER.info("Connected to server: "+helloMessage.getConnection().getRemoteAddress()); - this.getClient().sendRequest(new PlayerUUIDMessage(playerUUID), AckMessage.class) + this.getClient().sendRequest(new PlayerUUIDMessage(this.playerUUID), AckMessage.class) .thenAccept(ack -> this.getClient().sendMessage(new RemotePlayerConfigMessage(new MultiplayerConfig()))) .exceptionally(throwable -> { LOGGER.error("Error while fetching server's config", throwable); @@ -70,6 +72,7 @@ public class ClientNetworkState implements Closeable this.getClient().sendMessage(new RemotePlayerConfigMessage(new MultiplayerConfig())); } + @Override public void close() { this.configChangeListener.close(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index 3fb3a3482..8ef52c513 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -27,6 +27,7 @@ import com.seibel.distanthorizons.core.network.messages.base.ExceptionMessage; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.MessageRegistry; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; +import com.seibel.distanthorizons.coreapi.ModInfo; import io.netty.channel.ChannelException; import org.apache.logging.log4j.LogManager; @@ -93,7 +94,7 @@ public abstract class NetworkEventSource } } - if (!handled && message.warnWhenUnhandled()) + if (!handled && ModInfo.IS_DEV_BUILD && message.warnWhenUnhandled()) { LOGGER.warn("Unhandled message: " + message); } From 1baa666d23dffa78e576d2a7f6400f0a68672767 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 4 Feb 2024 02:34:53 +0500 Subject: [PATCH 091/205] Add priority distance limiting by ratio --- .../distanthorizons/core/config/Config.java | 25 ++++++++++++------ .../WorldRemoteGenerationQueue.java | 6 +++++ .../client/AbstractFullDataRequestQueue.java | 26 +++++++++++++++---- .../assets/distanthorizons/lang/en_us.json | 6 +++-- 4 files changed, 48 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 582f334b9..e6f625885 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -894,14 +894,6 @@ public class Config + "") .build(); - public static ConfigEntry genTaskPriorityRequestRateLimit = new ConfigEntry.Builder() - .setServersideShortName("genTaskPriorityRequestRateLimit") - .setMinDefaultMax(1, 50, 200) - .comment("" - + "Limits the amount of LOD sections that the client can request states for, per second. \n" - + "") - .build(); - public static ConfigEntry generationRequestBeginDelay = new ConfigEntry.Builder() .setMinDefaultMax(0, 3, 10) .comment("" @@ -910,6 +902,23 @@ public class Config + "") .build(); + public static ConfigEntry genTaskPriorityDistanceRatio = new ConfigEntry.Builder() + .setServersideShortName("genTaskPriorityDistanceRatio") + .setMinDefaultMax(1d, 3d, 10d) + .comment("" + + "Controls the max ratio between distances of nearest unloaded sections of each priority. \n" + + "For example, value of 2 means that the nearest lower priority section will be allowed to stay \n" + + "unloaded only if it's at most 2x closer than one of a higher priority. \n" + + "") + .build(); + public static ConfigEntry genTaskPriorityRequestRateLimit = new ConfigEntry.Builder() + .setServersideShortName("genTaskPriorityRequestRateLimit") + .setMinDefaultMax(1, 50, 200) + .comment("" + + "Limits the amount of LOD sections that the client can request states for, per second. \n" + + "") + .build(); + public static ConfigUIComment realTimeUpdatesSectionNote = new ConfigUIComment(); public static ConfigEntry enableRealTimeUpdates = new ConfigEntry.Builder() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 197c5e0c5..c5f6eb659 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -61,6 +61,12 @@ public class WorldRemoteGenerationQueue extends AbstractFullDataRequestQueue imp return LodUtil.BLOCK_DETAIL_LEVEL; } + @Override + protected double getPriorityDistanceRatio() + { + return Config.Client.Advanced.Multiplayer.ServerNetworking.genTaskPriorityDistanceRatio.get(); + } + @Override public CompletableFuture submitGenTask(DhSectionPos sectionPos, byte requiredDataDetail, IWorldGenTaskTracker tracker) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index c90a36715..0d8a0158e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -62,6 +62,8 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, protected abstract String getQueueName(); + protected double getPriorityDistanceRatio() { return 1; } + public AbstractFullDataRequestQueue(ClientNetworkState networkState, IDhClientLevel level, boolean changedOnly, ConfigEntry showDebugWireframeConfig) { @@ -143,11 +145,25 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, { Map.Entry mapEntry = this.waitingTasks.entrySet().stream() .filter(task -> task.getValue().request == null) - .reduce(null, (a, b) - -> a == null - || b.getValue().priority > a.getValue().priority - || (b.getValue().priority == a.getValue().priority && this.posDistanceSquared(targetPos, b.getKey()) < this.posDistanceSquared(targetPos, a.getKey())) - ? b : a); + .reduce(null, (a, b) -> { + if (a == null) + { + return b; + } + + if (b.getValue().priority < a.getValue().priority) + { + Map.Entry temp = b; + b = a; + a = temp; + } + + double distanceRatio = Math.sqrt(this.posDistanceSquared(targetPos, b.getKey())) / Math.sqrt(this.posDistanceSquared(targetPos, a.getKey())); + double maxDistanceRatioScaled = Math.pow(this.getPriorityDistanceRatio(), b.getValue().priority - a.getValue().priority); + + return distanceRatio < maxDistanceRatioScaled ? b : a; + }); + if (mapEntry == null) { this.pendingTasksSemaphore.release(); diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index 4a67e3f3e..b96bf70bd 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -353,10 +353,12 @@ "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationSectionNote": " \u25cf Generation", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestRCLimit": "Gen task rate/concurrency limit", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestRCLimit.@tooltip": "Limits the amount of generation requests sent by client and processed by server.", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.genTaskPriorityRequestRateLimit": "Gen task priority check rate limit", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.genTaskPriorityRequestRateLimit.@tooltip": "Limits the amount of LOD sections that the client can request states for, per second.", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestBeginDelay": "Generation request begin delay", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestBeginDelay.@tooltip": "Adds a delay in seconds before sending LOD requests, when generation is enabled. \nIncrease this value if initial generation starts too far away.", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.genTaskPriorityDistanceRatio": "Gen task priority distance max ratio", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.genTaskPriorityDistanceRatio.@tooltip": "Controls the max ratio between distances of nearest unloaded sections of each priority. \nFor example, a value of 2 means that the nearest lower priority section will be allowed to stay \nunloaded only if it's at most 2x closer than one of a higher priority.", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.genTaskPriorityRequestRateLimit": "Gen task priority check rate limit", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.genTaskPriorityRequestRateLimit.@tooltip": "Limits the amount of LOD sections that the client can request states for, per second.", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.realTimeUpdatesSectionNote": " \u25cf Real Time Updates", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableRealTimeUpdates": "Enable Real Time Updates", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableRealTimeUpdates.@tooltip": "Enables real time updates from server.", From df6ac6d74046f055b2be89217e16896c1010dd55 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 4 Feb 2024 20:20:57 +0500 Subject: [PATCH 092/205] Add connection state on F3 screen Hide refresh queue from debug when disabled --- .../client/AbstractFullDataRequestQueue.java | 21 ++++++++++--- .../client/ClientNetworkState.java | 25 ++++++++++++++++ .../client/FullDataRefreshQueue.java | 3 ++ .../core/network/IConnection.java | 30 ++++++++++++++++--- .../core/network/NetworkClient.java | 6 ++-- .../protocol/NetworkExceptionHandler.java | 11 ++++++- 6 files changed, 85 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index 0d8a0158e..16517db6a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -58,6 +58,9 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, private final SupplierBasedRateLimiter rateLimiter = new SupplierBasedRateLimiter<>(this::getRequestConcurrencyLimit); + @SuppressWarnings("BooleanMethodIsAlwaysInverted") + protected boolean showInDebug() { return true; } + protected abstract int getRequestConcurrencyLimit(); protected abstract String getQueueName(); @@ -232,10 +235,15 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, private String[] f3Log() { - ArrayList lines = new ArrayList<>(); - lines.add(this.getQueueName() + " [" + this.level.getClientLevelWrapper().getDimensionType().getDimensionName() + "]"); - lines.add("Requests: " + this.finishedRequests + " / " + (this.getWaitingTaskCount() + this.finishedRequests.get()) + " (failed: " + this.failedRequests + ", rate limit: " + this.getRequestConcurrencyLimit() + ")"); - return lines.toArray(new String[0]); + if (!this.showInDebug()) + { + return new String[0]; + } + + return new String[]{ + this.getQueueName() + " [" + this.level.getClientLevelWrapper().getDimensionType().getDimensionName() + "]", + "Requests: " + this.finishedRequests + " / " + (this.getWaitingTaskCount() + this.finishedRequests.get()) + " (failed: " + this.failedRequests + ", rate limit: " + this.getRequestConcurrencyLimit() + ")" + }; } public int getWaitingTaskCount() { return this.waitingTasks.size(); } @@ -277,6 +285,11 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, @Override public void debugRender(DebugRenderer r) { + if (!this.showInDebug()) + { + return; + } + for (Map.Entry mapEntry : this.waitingTasks.entrySet()) { r.renderBox(new DebugRenderer.Box(mapEntry.getKey(), -32f, 64f, 0.05f, diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index cb40882e3..d0200c949 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -2,6 +2,7 @@ package com.seibel.distanthorizons.core.multiplayer.client; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; +import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; import com.seibel.distanthorizons.core.network.NetworkClient; @@ -13,6 +14,7 @@ import com.seibel.distanthorizons.core.network.messages.session.RemotePlayerConf import org.apache.logging.log4j.LogManager; import java.io.Closeable; +import java.text.MessageFormat; import java.util.UUID; public class ClientNetworkState implements Closeable @@ -25,6 +27,8 @@ public class ClientNetworkState implements Closeable public MultiplayerConfig config = new MultiplayerConfig(); private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::onConfigChanged); + private final F3Screen.NestedMessage f3Message = new F3Screen.NestedMessage(this::f3Log); + /** * Returns the client used by this instance.

* If you need to subscribe to any packet events, create an instance of {@link ScopedNetworkEventSource} using the returned instance. @@ -72,9 +76,30 @@ public class ClientNetworkState implements Closeable this.getClient().sendMessage(new RemotePlayerConfigMessage(new MultiplayerConfig())); } + private String[] f3Log() + { + if (!this.client.isClosed()) + { + return new String[]{ + this.client.getRemoteAddress() != null + ? "Connected, ready: " + this.client.isReady() + : MessageFormat.format("Disconnected, attempts left: {0} / {1}", this.client.getReconnectAttempts(), NetworkClient.FAILURE_RECONNECT_ATTEMPTS) + }; + } + else + { + return new String[]{ + this.client.getCloseReason() != null + ? "Disconnected: " + this.client.getCloseReason().getMessage() + : "Disconnected (check logs for more information)" + }; + } + } + @Override public void close() { + this.f3Message.close(); this.configChangeListener.close(); this.client.close(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/FullDataRefreshQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/FullDataRefreshQueue.java index 862ababaf..58bc21d9a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/FullDataRefreshQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/FullDataRefreshQueue.java @@ -11,6 +11,9 @@ public class FullDataRefreshQueue extends AbstractFullDataRequestQueue super(networkState, level, true, Config.Client.Advanced.Debugging.DebugWireframe.showWorldGenQueue); } + @Override + protected boolean showInDebug() { return this.networkState.config.loginDataSyncEnabled; } + @Override protected int getRequestConcurrencyLimit() { return this.networkState.config.loginDataSyncRCLimit; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java b/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java index 8399cf9a4..bc605a52b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java @@ -10,6 +10,7 @@ import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import org.apache.logging.log4j.LogManager; +import javax.annotation.Nullable; import java.net.SocketAddress; import java.util.concurrent.CompletableFuture; @@ -18,12 +19,20 @@ public interface IConnection ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); + @Nullable ChannelHandlerContext getChannelContext(); NetworkEventSource getRequestHandler(); + @Nullable default SocketAddress getRemoteAddress() { - return this.getChannelContext().channel().remoteAddress(); + ChannelHandlerContext ctx = this.getChannelContext(); + if (ctx == null) + { + return null; + } + + return ctx.channel().remoteAddress(); } default CompletableFuture sendMessage(NetworkMessage message) @@ -68,14 +77,27 @@ public interface IConnection default void disconnect(String reason) { - this.getChannelContext().channel().config().setAutoRead(false); - this.getChannelContext().writeAndFlush(new CloseReasonMessage(reason)) + ChannelHandlerContext ctx = this.getChannelContext(); + if (ctx == null) + { + return; + } + + ctx.channel().config().setAutoRead(false); + ctx.writeAndFlush(new CloseReasonMessage(reason)) .addListener(ChannelFutureListener.CLOSE); } + @Nullable default Throwable getCloseReason() { - return this.getChannelContext().channel().closeFuture().cause(); + ChannelHandlerContext ctx = this.getChannelContext(); + if (ctx == null) + { + return null; + } + + return ctx.channel().closeFuture().cause(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index f6b2656d4..7a9fd8f9c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -57,7 +57,7 @@ public class NetworkClient extends NetworkEventSource implements IConnection, Au ); private static final int FAILURE_RECONNECT_DELAY_SEC = 5; - private static final int FAILURE_RECONNECT_ATTEMPTS = 3; + public static final int FAILURE_RECONNECT_ATTEMPTS = 3; // TODO move to payload of some sort private final InetSocketAddress address; @@ -85,8 +85,10 @@ public class NetworkClient extends NetworkEventSource implements IConnection, Au private EConnectionState connectionState = EConnectionState.INITIAL; private Channel channel; - private int reconnectAttempts = FAILURE_RECONNECT_ATTEMPTS; + private int reconnectAttempts = FAILURE_RECONNECT_ATTEMPTS; + /** Returns the amount of reconnections the client will attempt to perform before giving up. */ + public int getReconnectAttempts() { return this.reconnectAttempts; } public NetworkClient(String host, int port) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkExceptionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkExceptionHandler.java index ad0ee862b..905512b2f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkExceptionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkExceptionHandler.java @@ -25,6 +25,8 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; import org.apache.logging.log4j.LogManager; +import java.net.SocketException; + public class NetworkExceptionHandler extends ChannelInboundHandlerAdapter { private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), @@ -33,7 +35,14 @@ public class NetworkExceptionHandler extends ChannelInboundHandlerAdapter @Override public void exceptionCaught(ChannelHandlerContext channelContext, Throwable cause) { - LOGGER.error("Exception caught in channel: [" + channelContext.name() + "].", cause); + if (cause instanceof SocketException) + { + LOGGER.info("Exception caught in channel: [" + channelContext.name() + "]: " + cause.getMessage()); + } + else + { + LOGGER.error("Exception caught in channel: [" + channelContext.name() + "].", cause); + } channelContext.close(); } From 44527630da6bcabf687a1cd85b1ad29dbba662fb Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 4 Feb 2024 21:00:02 +0500 Subject: [PATCH 093/205] Fix queue not filled when generation is toggled --- .../seibel/distanthorizons/core/level/DhClientLevel.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index d69addbae..2c0fccdee 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -178,6 +178,12 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel { // start world gen this.worldGenModule.startWorldGen(this.dataFileHandler, new WorldGenState(this, this.networkState)); + + // populate the queue based on the current rendering tree + ClientLevelModule.ClientRenderState renderState = this.clientside.ClientRenderStateRef.get(); + renderState.quadtree.leafNodeIterator().forEachRemaining(node -> { + this.dataFileHandler.getAsync(node.sectionPos); + }); } else if (!shouldDoWorldGen && isWorldGenRunning) { From d347dbd2228c865dae581d39446979a214497e38 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 4 Feb 2024 21:29:21 +0500 Subject: [PATCH 094/205] Fix typo --- .../core/network/protocol/INetworkObject.java | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java index 63a2cc476..94599b857 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java @@ -78,7 +78,9 @@ public interface INetworkObject for (T item : collection) { if (codec == null) + { codec = Codec.getCodec(item.getClass()); + } codec.encode.accept(item, outputByteBuf); } } @@ -93,7 +95,9 @@ public interface INetworkObject T item = innerValueConstructor.get(); if (codec == null) + { codec = Codec.getCodec(item.getClass()); + } item = (T) codec.decode.apply(item, inputByteBuf); collection.add(item); @@ -104,24 +108,26 @@ public interface INetworkObject { ArrayList> entryList = new ArrayList<>(); - decodeCollection(inputByteBuf, entryList, () -> new AbstractMap.SimpleEntry<>(keySupplier.get(), valueSupplier.get())); + this.decodeCollection(inputByteBuf, entryList, () -> new AbstractMap.SimpleEntry<>(keySupplier.get(), valueSupplier.get())); for (Map.Entry entry : entryList) + { map.put(entry.getKey(), entry.getValue()); + } } /** * Should only be used for non-editable classes; - * otherwise, you may want to implement {@link INetworkObject} and use its method where applicable. + * otherwise, you may want to implement {@link INetworkObject} and use its methods where applicable. */ class Codec { private static final ConcurrentMap, Codec> codecMap = new ConcurrentHashMap, Codec>() {{ // Primitives must be added manually here - put(Integer.class, new Codec((obj, out) -> out.writeInt((int)obj), (obj, in) -> in.readInt())); + this.put(Integer.class, new Codec((obj, out) -> out.writeInt((int)obj), (obj, in) -> in.readInt())); - put(INetworkObject.class, new Codec(INetworkObject::encode, INetworkObject::decodeStatic)); - put(Map.Entry.class, new Codec( + this.put(INetworkObject.class, new Codec(INetworkObject::encode, INetworkObject::decodeStatic)); + this.put(Map.Entry.class, new Codec( (obj, out) -> { Map.Entry entry = (Entry) obj; getCodec(entry.getKey().getClass()).encode.accept(entry.getKey(), out); @@ -151,7 +157,9 @@ public interface INetworkObject for (Map.Entry, Codec> entry : codecMap.entrySet()) { if (entry.getKey().isAssignableFrom(clazz)) + { return entry.getValue(); + } } throw new AssertionError("Class has no compatible codec: "+clazz.getSimpleName()); From b1b907bfe57b6133abb00fe343882ee91a5b521d Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 4 Feb 2024 21:33:21 +0500 Subject: [PATCH 095/205] Silence a warning --- .../distanthorizons/core/network/protocol/INetworkObject.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java index 94599b857..57a11e198 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java @@ -98,6 +98,7 @@ public interface INetworkObject { codec = Codec.getCodec(item.getClass()); } + //noinspection unchecked item = (T) codec.decode.apply(item, inputByteBuf); collection.add(item); @@ -146,6 +147,7 @@ public interface INetworkObject public final BiConsumer encode; public final BiFunction decode; + @SuppressWarnings("unchecked") public Codec(BiConsumer encode, BiFunction decode) { this.encode = (BiConsumer) encode; From 3e37e9ee0838cdbee5d24a327412cd3228a3b671 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 4 Feb 2024 21:57:57 +0500 Subject: [PATCH 096/205] Remove unused packets --- .../WorldRemoteGenerationQueue.java | 4 +- .../core/level/DhClientLevel.java | 2 +- .../core/level/DhServerLevel.java | 10 +-- .../client/AbstractFullDataRequestQueue.java | 5 +- .../multiplayer/server/ServerPlayerState.java | 4 +- .../FullDataPartialUpdateMessage.java | 2 +- .../FullDataSourceRequestMessage.java | 2 +- .../FullDataSourceResponseMessage.java | 2 +- .../GenTaskPriorityRequestMessage.java | 8 +-- .../GenTaskPriorityResponseMessage.java | 8 +-- .../FullDataChangeSummaryRequestMessage.java | 69 ------------------- .../FullDataChangeSummaryResponseMessage.java | 60 ---------------- .../network/protocol/MessageRegistry.java | 16 ++--- 13 files changed, 28 insertions(+), 164 deletions(-) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/{updates => }/FullDataPartialUpdateMessage.java (99%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/{generation => }/FullDataSourceRequestMessage.java (99%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/{generation => }/FullDataSourceResponseMessage.java (99%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/{priority => }/GenTaskPriorityRequestMessage.java (89%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/{priority => }/GenTaskPriorityResponseMessage.java (89%) delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryRequestMessage.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryResponseMessage.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index c5f6eb659..34c5da6e8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -10,8 +10,8 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.multiplayer.client.AbstractFullDataRequestQueue; import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.priority.GenTaskPriorityRequestMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.priority.GenTaskPriorityResponseMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.GenTaskPriorityRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.GenTaskPriorityResponseMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 2c0fccdee..8a9fff9d2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -32,7 +32,7 @@ import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; import com.seibel.distanthorizons.core.multiplayer.client.FullDataRefreshQueue; import com.seibel.distanthorizons.core.network.NetworkClient; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; -import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataPartialUpdateMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhSectionPos; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 2d868fb06..d1248abc0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -32,11 +32,11 @@ import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.NetworkServer; import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; import com.seibel.distanthorizons.core.network.messages.base.CancelMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceResponseMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.priority.GenTaskPriorityRequestMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.priority.GenTaskPriorityResponseMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataPartialUpdateMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceResponseMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.GenTaskPriorityRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.GenTaskPriorityResponseMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index 16517db6a..59dce8385 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -11,8 +11,8 @@ import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceResponseMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceResponseMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; @@ -25,7 +25,6 @@ import org.apache.logging.log4j.LogManager; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import java.awt.*; -import java.util.ArrayList; import java.util.Map; import java.util.Set; import java.util.concurrent.*; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index 97c75df92..9a6be0f74 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -2,8 +2,8 @@ package com.seibel.distanthorizons.core.multiplayer.server; import com.seibel.distanthorizons.core.network.IConnection; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.priority.GenTaskPriorityRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.GenTaskPriorityRequestMessage; import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateAndConcurrencyLimiter; import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateLimiter; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java similarity index 99% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java index 4edbe02e2..5fa967d17 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataPartialUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.fullData.updates; +package com.seibel.distanthorizons.core.network.messages.fullData; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.level.DhServerLevel; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceRequestMessage.java similarity index 99% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceRequestMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceRequestMessage.java index 658dce6cc..2e78c31ec 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceRequestMessage.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.fullData.generation; +package com.seibel.distanthorizons.core.network.messages.fullData; import com.seibel.distanthorizons.core.network.messages.base.ILevelRelatedMessage; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java similarity index 99% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java index 52b2ef71c..283da878b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.fullData.generation; +package com.seibel.distanthorizons.core.network.messages.fullData; import com.seibel.distanthorizons.core.dataObjects.fullData.loader.AbstractFullDataSourceLoader; import com.seibel.distanthorizons.core.dataObjects.fullData.loader.CompleteFullDataSourceLoader; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/priority/GenTaskPriorityRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/GenTaskPriorityRequestMessage.java similarity index 89% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/priority/GenTaskPriorityRequestMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/GenTaskPriorityRequestMessage.java index b629d537a..e0eff26b5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/priority/GenTaskPriorityRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/GenTaskPriorityRequestMessage.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.fullData.generation.priority; +package com.seibel.distanthorizons.core.network.messages.fullData.generation; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.pos.DhSectionPos; @@ -39,18 +39,18 @@ public class GenTaskPriorityRequestMessage extends FutureTrackableNetworkMessage @Override protected void encode0(ByteBuf out) { - encodeCollection(out, posList); + this.encodeCollection(out, this.posList); } @Override protected void decode0(ByteBuf in) { - decodeCollection(in, posList, DhSectionPos::zero); + this.decodeCollection(in, this.posList, DhSectionPos::zero); } @Override public String toString() { - return super.toString("posList=" + posList); + return super.toString("posList=" + this.posList); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/priority/GenTaskPriorityResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/GenTaskPriorityResponseMessage.java similarity index 89% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/priority/GenTaskPriorityResponseMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/GenTaskPriorityResponseMessage.java index a9e4a137a..5eaa2278d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/priority/GenTaskPriorityResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/GenTaskPriorityResponseMessage.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.fullData.generation.priority; +package com.seibel.distanthorizons.core.network.messages.fullData.generation; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.pos.DhSectionPos; @@ -39,18 +39,18 @@ public class GenTaskPriorityResponseMessage extends FutureTrackableNetworkMessag @Override protected void encode0(ByteBuf out) { - encodeCollection(out, posList.entrySet()); + this.encodeCollection(out, this.posList.entrySet()); } @Override protected void decode0(ByteBuf in) { - decodeMap(in, posList, DhSectionPos::zero, () -> 0); + this.decodeMap(in, this.posList, DhSectionPos::zero, () -> 0); } @Override public String toString() { - return super.toString("posList=" + posList); + return super.toString("posList=" + this.posList); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryRequestMessage.java deleted file mode 100644 index 9116525ca..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryRequestMessage.java +++ /dev/null @@ -1,69 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.network.messages.fullData.updates; - -import com.seibel.distanthorizons.core.network.messages.base.ILevelRelatedMessage; -import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; -import com.seibel.distanthorizons.core.pos.DhSectionPos; -import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; -import io.netty.buffer.ByteBuf; - -import java.util.HashMap; -import java.util.Map; - -public class FullDataChangeSummaryRequestMessage extends FutureTrackableNetworkMessage implements ILevelRelatedMessage -{ - public Map checksums = new HashMap<>(); - public int levelHashCode; - @Override public int getLevelHashCode() { return levelHashCode; } - - public FullDataChangeSummaryRequestMessage() { } - - public FullDataChangeSummaryRequestMessage(ILevelWrapper levelWrapper, Map checksums) - { - // TODO Multiverse support - this.levelHashCode = levelWrapper.getDimensionType().getDimensionName().hashCode(); - this.checksums = checksums; - } - - @Override - public void encode0(ByteBuf out) - { - // TODO Multiverse support - out.writeInt(levelHashCode); - encodeCollection(out, checksums.entrySet()); - } - - @Override - public void decode0(ByteBuf in) - { - levelHashCode = in.readInt(); - decodeMap(in, checksums, DhSectionPos::zero, () -> 0); - } - - @Override public String toString() - { - return super.toString( - "checksums=" + checksums + - ", levelHashCode=" + levelHashCode - ); - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryResponseMessage.java deleted file mode 100644 index fd2223fda..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/updates/FullDataChangeSummaryResponseMessage.java +++ /dev/null @@ -1,60 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.network.messages.fullData.updates; - -import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; -import com.seibel.distanthorizons.core.pos.DhSectionPos; -import io.netty.buffer.ByteBuf; - -import java.util.HashSet; -import java.util.Set; - -public class FullDataChangeSummaryResponseMessage extends FutureTrackableNetworkMessage -{ - public final Set changedPosList; - - public FullDataChangeSummaryResponseMessage() - { - this.changedPosList = new HashSet<>(); - } - - public FullDataChangeSummaryResponseMessage(Set changedPosList) - { - this.changedPosList = changedPosList; - } - - @Override - public void encode0(ByteBuf out) - { - encodeCollection(out, changedPosList); - } - - @Override - public void decode0(ByteBuf in) - { - decodeCollection(in, changedPosList, DhSectionPos::zero); - } - - @Override public String toString() - { - return super.toString("changedPosList=" + changedPosList); - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java index c126ad808..94287aab0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java @@ -22,13 +22,11 @@ package com.seibel.distanthorizons.core.network.protocol; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.seibel.distanthorizons.core.network.messages.base.*; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.FullDataSourceResponseMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.priority.GenTaskPriorityRequestMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.priority.GenTaskPriorityResponseMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataChangeSummaryRequestMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataChangeSummaryResponseMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.updates.FullDataPartialUpdateMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceResponseMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.GenTaskPriorityRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.generation.GenTaskPriorityResponseMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage; import com.seibel.distanthorizons.core.network.messages.session.PlayerUUIDMessage; import com.seibel.distanthorizons.core.network.messages.session.RemotePlayerConfigMessage; @@ -71,10 +69,6 @@ public class MessageRegistry // Generation task prioritization this.registerMessage(GenTaskPriorityRequestMessage.class, GenTaskPriorityRequestMessage::new); this.registerMessage(GenTaskPriorityResponseMessage.class, GenTaskPriorityResponseMessage::new); - - // Post-rejoin updates - this.registerMessage(FullDataChangeSummaryRequestMessage.class, FullDataChangeSummaryRequestMessage::new); - this.registerMessage(FullDataChangeSummaryResponseMessage.class, FullDataChangeSummaryResponseMessage::new); } From 2353bd7545e654820160f0c8e099e084435b7eeb Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Wed, 7 Feb 2024 21:03:37 +0500 Subject: [PATCH 097/205] Fix compilation --- .../com/seibel/distanthorizons/core/api/internal/ClientApi.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index 7b4e82a61..b66f67faa 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -57,6 +57,7 @@ import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; import org.lwjgl.opengl.GL32; +import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.HashSet; import java.util.concurrent.TimeUnit; @@ -333,7 +334,6 @@ public class ClientApi // networking // //============// - /** @param byteBuf is Netty's {@link ByteBuffer} wrapper. */ public void serverMessageReceived(ByteBuf byteBuf) { if (!Config.Client.Advanced.Multiplayer.enableMultiverseNetworking.get()) From 7bf9ce141a839ba928f244af3afd794049578f7d Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 10 Feb 2024 17:06:49 +0500 Subject: [PATCH 098/205] Avoid doing requests on join --- .../client/AbstractFullDataRequestQueue.java | 2 +- .../client/ClientNetworkState.java | 18 +++- ...java => ConstrainedMultiplayerConfig.java} | 2 +- .../server/RemotePlayerConnectionHandler.java | 2 +- .../multiplayer/server/ServerPlayerState.java | 2 +- .../core/network/NetworkClient.java | 85 ++++++++----------- 6 files changed, 55 insertions(+), 56 deletions(-) rename core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/{ServersideMultiplayerConfig.java => ConstrainedMultiplayerConfig.java} (96%) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index 59dce8385..974a4829f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -105,7 +105,7 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, public synchronized boolean tick(DhBlockPos2D targetPos) { - if (this.closingFuture != null || !this.networkState.getClient().isReady()) + if (this.closingFuture != null || !this.networkState.isReady()) { return false; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index d0200c949..34ba3eea3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -8,6 +8,7 @@ import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChang import com.seibel.distanthorizons.core.network.NetworkClient; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.messages.base.AckMessage; +import com.seibel.distanthorizons.core.network.messages.base.CloseEvent; import com.seibel.distanthorizons.core.network.messages.base.HelloMessage; import com.seibel.distanthorizons.core.network.messages.session.PlayerUUIDMessage; import com.seibel.distanthorizons.core.network.messages.session.RemotePlayerConfigMessage; @@ -24,8 +25,11 @@ public class ClientNetworkState implements Closeable private final NetworkClient client; private final UUID playerUUID; + public MultiplayerConfig config = new MultiplayerConfig(); + private volatile boolean configReceived = false; private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::onConfigChanged); + public boolean isReady() { return this.configReceived; } private final F3Screen.NestedMessage f3Message = new F3Screen.NestedMessage(this::f3Log); @@ -47,7 +51,7 @@ public class ClientNetworkState implements Closeable this.playerUUID = playerUUID; this.registerNetworkHandlers(); - this.client.startConnecting(); + this.client.connect(); } private void registerNetworkHandlers() @@ -66,8 +70,14 @@ public class ClientNetworkState implements Closeable this.client.registerHandler(RemotePlayerConfigMessage.class, msg -> { - LOGGER.info("Connection config was changed: " + msg.payload); + LOGGER.info("Connection config has been changed: " + msg.payload); this.config = (MultiplayerConfig) msg.payload; + this.configReceived = true; + }); + + this.client.registerHandler(CloseEvent.class, msg -> + { + this.configReceived = false; }); } @@ -82,8 +92,8 @@ public class ClientNetworkState implements Closeable { return new String[]{ this.client.getRemoteAddress() != null - ? "Connected, ready: " + this.client.isReady() - : MessageFormat.format("Disconnected, attempts left: {0} / {1}", this.client.getReconnectAttempts(), NetworkClient.FAILURE_RECONNECT_ATTEMPTS) + ? "Connected, ready: " + this.isReady() + : MessageFormat.format("Disconnected, attempts left: {0} / {1}", this.client.getReconnectionAttemptsLeft(), NetworkClient.RECONNECTION_ATTEMPTS) }; } else diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ConstrainedMultiplayerConfig.java similarity index 96% rename from core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java rename to core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ConstrainedMultiplayerConfig.java index 58a9b8f64..c998d496b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServersideMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ConstrainedMultiplayerConfig.java @@ -8,7 +8,7 @@ import io.netty.buffer.ByteBuf; /** * Used for constraining the client config to the server config. */ -public class ServersideMultiplayerConfig extends AbstractMultiplayerConfig +public class ConstrainedMultiplayerConfig extends AbstractMultiplayerConfig { public MultiplayerConfig clientConfig = new MultiplayerConfig(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java index 2dc603dd5..207374835 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java @@ -156,7 +156,7 @@ public class RemotePlayerConnectionHandler implements Closeable IConnection connection = this.playersByConnection.inverse().remove(dhPlayer); if (connection != null) { - connection.disconnect("You are being disconnected."); + connection.disconnect("You have been disconnected."); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index 9a6be0f74..53cfe1a95 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -17,7 +17,7 @@ public class ServerPlayerState public IConnection connection; @NotNull - public ServersideMultiplayerConfig config = new ServersideMultiplayerConfig(); + public ConstrainedMultiplayerConfig config = new ConstrainedMultiplayerConfig(); public final SupplierBasedRateLimiter rateLimitKickTrigger = new SupplierBasedRateLimiter<>( () -> ServerNetworking.rateLimitHitTolerance.get(), diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index 7a9fd8f9c..e038c6930 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -37,6 +37,7 @@ import java.net.InetSocketAddress; import java.util.EnumSet; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicReference; public class NetworkClient extends NetworkEventSource implements IConnection, AutoCloseable { @@ -47,28 +48,22 @@ public class NetworkClient extends NetworkEventSource implements IConnection, Au { INITIAL, OPEN, - RECONNECT, - CLOSING, + RECONNECTING, + GOT_CLOSE_REASON, CLOSED } private static final Set closedStates = EnumSet.of( - EConnectionState.CLOSING, + EConnectionState.GOT_CLOSE_REASON, EConnectionState.CLOSED ); - - private static final int FAILURE_RECONNECT_DELAY_SEC = 5; - public static final int FAILURE_RECONNECT_ATTEMPTS = 3; - - // TODO move to payload of some sort - private final InetSocketAddress address; - - /** Indicates whether the client is initialized and not started connecting yet. */ - public boolean isInitialState() { return this.connectionState == EConnectionState.INITIAL; } + private final AtomicReference connectionState = new AtomicReference<>(EConnectionState.INITIAL); /** Indicates whether the client is closed(-ing) and should not be used. */ - public boolean isClosed() { return closedStates.contains(this.connectionState); } - private boolean ready; - /** Indicates whether the connection is established and first message is sent. */ - public boolean isReady() { return this.ready; } + public boolean isClosed() { return closedStates.contains(this.connectionState.get()); } + + private static final int RECONNECTION_DELAY_SEC = 5; + public static final int RECONNECTION_ATTEMPTS = 3; + + private final InetSocketAddress address; private final EventLoopGroup workerGroup = new NioEventLoopGroup(0, new DefaultThreadFactory("DH-Network - Client Thread")); private final Bootstrap clientBootstrap = new Bootstrap() @@ -83,12 +78,11 @@ public class NetworkClient extends NetworkEventSource implements IConnection, Au ctx -> this.addNewConnection(this) ))); - private EConnectionState connectionState = EConnectionState.INITIAL; private Channel channel; - private int reconnectAttempts = FAILURE_RECONNECT_ATTEMPTS; + private int reconnectionAttemptsLeft = RECONNECTION_ATTEMPTS; /** Returns the amount of reconnections the client will attempt to perform before giving up. */ - public int getReconnectAttempts() { return this.reconnectAttempts; } + public int getReconnectionAttemptsLeft() { return this.reconnectionAttemptsLeft; } public NetworkClient(String host, int port) @@ -102,33 +96,27 @@ public class NetworkClient extends NetworkEventSource implements IConnection, Au this.registerHandler(CloseReasonMessage.class, closeReasonMessage -> { LOGGER.warn(closeReasonMessage.reason); - this.connectionState = EConnectionState.CLOSING; + this.connectionState.set(EConnectionState.GOT_CLOSE_REASON); }); this.registerHandler(CloseEvent.class, closeEvent -> { LOGGER.info("Disconnected from server: "+this.getRemoteAddress()); - if (this.connectionState == EConnectionState.CLOSING) + if (this.connectionState.get() == EConnectionState.GOT_CLOSE_REASON) { this.close(); } }); } - public void startConnecting() + public void connect() { - if (!this.isInitialState()) + if (!this.connectionState.compareAndSet(EConnectionState.INITIAL, EConnectionState.OPEN)) { return; } - this.connect(); - } - - private void connect() - { - LOGGER.info("Connecting to server: "+this.address); - this.connectionState = EConnectionState.OPEN; - + + LOGGER.info("Connecting to server: " + this.address); ChannelFuture connectFuture = this.clientBootstrap.connect(this.address); this.channel = connectFuture.channel(); @@ -141,31 +129,33 @@ public class NetworkClient extends NetworkEventSource implements IConnection, Au } this.sendMessage(new HelloMessage()); - this.ready = true; }); this.channel.closeFuture().addListener((ChannelFuture channelFuture) -> { - this.ready = false; this.completeAllFuturesExceptionally(channelFuture.cause() != null ? channelFuture.cause() : new ChannelException("Channel is closed.")); - if (this.connectionState != EConnectionState.OPEN) + if (this.connectionState.get() == EConnectionState.OPEN) { - return; + this.reconnectionAttemptsLeft--; + LOGGER.info("Reconnection attempts left: [" + this.reconnectionAttemptsLeft + "] of [" + RECONNECTION_ATTEMPTS + "]."); + + if (this.reconnectionAttemptsLeft != 0) + { + this.connectionState.set(EConnectionState.RECONNECTING); + this.workerGroup.schedule(() -> + { + this.connectionState.set(EConnectionState.INITIAL); + this.connect(); + }, RECONNECTION_DELAY_SEC, TimeUnit.SECONDS); + } + else + { + this.connectionState.set(EConnectionState.GOT_CLOSE_REASON); + } } - - this.reconnectAttempts--; - LOGGER.info("Reconnection attempts left: [" + this.reconnectAttempts + "] of [" + FAILURE_RECONNECT_ATTEMPTS + "]."); - if (this.reconnectAttempts == 0) - { - this.connectionState = EConnectionState.CLOSING; - return; - } - - this.connectionState = EConnectionState.RECONNECT; - this.workerGroup.schedule(this::connect, FAILURE_RECONNECT_DELAY_SEC, TimeUnit.SECONDS); }); } @@ -184,12 +174,11 @@ public class NetworkClient extends NetworkEventSource implements IConnection, Au @Override public void close() { - if (this.connectionState == EConnectionState.CLOSED) + if (this.connectionState.getAndSet(EConnectionState.CLOSED) == EConnectionState.CLOSED) { return; } - this.connectionState = EConnectionState.CLOSED; this.channel.close().syncUninterruptibly(); this.workerGroup.shutdownGracefully(); From 48212ba746494e5812d1e03f2686eb887fe7d414 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 11 Feb 2024 19:39:35 +0500 Subject: [PATCH 099/205] Add another config option Fix rate limiting issues (kinda) --- .../distanthorizons/core/config/Config.java | 13 ++++ .../core/level/DhClientLevel.java | 36 +++++++---- .../core/level/DhServerLevel.java | 16 +++-- .../client/AbstractFullDataRequestQueue.java | 9 +++ .../config/AbstractMultiplayerConfig.java | 2 + .../multiplayer/config/MultiplayerConfig.java | 6 ++ .../MultiplayerConfigChangeListener.java | 4 +- .../server/ConstrainedMultiplayerConfig.java | 6 ++ .../server/RemotePlayerConnectionHandler.java | 6 +- .../multiplayer/server/ServerPlayerState.java | 64 +++++++++++-------- .../messages/base/ILevelRelatedMessage.java | 2 +- .../FullDataPartialUpdateMessage.java | 2 +- .../SupplierBasedRateLimiter.java | 2 +- .../assets/distanthorizons/lang/en_us.json | 13 ++-- 14 files changed, 126 insertions(+), 55 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 601f1f555..c524a1fae 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -946,6 +946,19 @@ public class Config + "Limits the amount of sent/processed LOD *update* requests concurrently, per player. \n" + "") .build(); + + public static ConfigUIComment immersivePortalsSectionNote = new ConfigUIComment(); + public static ConfigEntry generateMultipleDimensions = new ConfigEntry.Builder() + .setServersideShortName("generateMultipleDimensions") + .set(false) + .comment("" + + "Controls whether the players should be allowed to generate in multiple dimensions at once. \n" + + "" + + "For server owners: \n" + + "All dimensions share the same thread pool. This means that empty dimensions (i.e. without players in them) \n" + + "may slow down the generation of non-empty ones." + + "") + .build(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index affa5afde..6052e8f07 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -171,13 +171,25 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel @Override public void doWorldGen() { - boolean isClientUsable = this.networkState != null && !this.networkState.getClient().isClosed(); - boolean shouldDoWorldGen = isClientUsable && this.networkState.config.distantGenerationEnabled && this.clientside.isRendering(); + ClientNetworkState networkState = this.networkState; + + boolean isClientUsable = false, isAllowedDimension = false; + if (networkState != null) + { + isClientUsable = !networkState.getClient().isClosed(); + isAllowedDimension = MC_CLIENT.getWrappedClientLevel() == this.levelWrapper || networkState.config.generateMultipleDimensions; + } + + boolean shouldDoWorldGen = isClientUsable + && networkState.config.distantGenerationEnabled + && isAllowedDimension + && this.clientside.isRendering(); + boolean isWorldGenRunning = this.worldGenModule.isWorldGenRunning(); if (shouldDoWorldGen && !isWorldGenRunning) { // start world gen - this.worldGenModule.startWorldGen(this.dataFileHandler, new WorldGenState(this, this.networkState)); + this.worldGenModule.startWorldGen(this.dataFileHandler, new WorldGenState(this, networkState)); // populate the queue based on the current rendering tree ClientLevelModule.ClientRenderState renderState = this.clientside.ClientRenderStateRef.get(); @@ -217,25 +229,25 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel //================// @Override - public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper block) { return levelWrapper.computeBaseColor(pos, biome, block); } + public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper block) { return this.levelWrapper.computeBaseColor(pos, biome, block); } @Override - public IClientLevelWrapper getClientLevelWrapper() { return levelWrapper; } + public IClientLevelWrapper getClientLevelWrapper() { return this.levelWrapper; } @Override public void clearRenderCache() { - clientside.clearRenderCache(); + this.clientside.clearRenderCache(); } @Override - public ILevelWrapper getLevelWrapper() { return levelWrapper; } + public ILevelWrapper getLevelWrapper() { return this.levelWrapper; } @Override public void updateDataSourcesWithChunkData(ChunkSizedFullDataAccessor data) { this.clientside.updateDataSourcesWithChunkData(data); } @Override - public int getMinY() { return levelWrapper.getMinHeight(); } + public int getMinY() { return this.levelWrapper.getMinHeight(); } @Override public void close() @@ -246,8 +258,8 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel } this.clientside.close(); super.close(); - dataFileHandler.close(); - LOGGER.info("Closed " + DhClientLevel.class.getSimpleName() + " for " + levelWrapper); + this.dataFileHandler.close(); + LOGGER.info("Closed " + DhClientLevel.class.getSimpleName() + " for " + this.levelWrapper); } //=======================// @@ -257,13 +269,13 @@ public class DhClientLevel extends DhLevel implements IDhClientLevel @Override public IFullDataSourceProvider getFileHandler() { - return dataFileHandler; + return this.dataFileHandler; } @Override public AbstractSaveStructure getSaveStructure() { - return saveStructure; + return this.saveStructure; } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index d1248abc0..8212b8a21 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -92,6 +92,8 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel { this.eventSource.registerHandler(FullDataSourceRequestMessage.class, this.remotePlayerConnectionHandler.currentLevelOnly(this, (msg, serverPlayerState) -> { + ServerPlayerState.RateLimiterSet rateLimiterSet = serverPlayerState.getRateLimiterSet(this); + if (msg.checksum == null) { // Normal generation @@ -102,7 +104,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel return; } - if (!serverPlayerState.fullDataRequestConcurrencyLimiter.tryAcquire(msg)) + if (!rateLimiterSet.fullDataRequestConcurrencyLimiter.tryAcquire(msg)) { return; } @@ -135,7 +137,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel return; } - if (!serverPlayerState.loginDataSyncRCLimiter.tryAcquire(msg)) + if (!rateLimiterSet.loginDataSyncRCLimiter.tryAcquire(msg)) { return; } @@ -143,14 +145,14 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel Integer serverChecksum = this.serverside.dataFileHandler.repo.getChecksumForSection(msg.sectionPos); if (serverChecksum == null || serverChecksum.equals(msg.checksum)) { - serverPlayerState.loginDataSyncRCLimiter.release(); + rateLimiterSet.loginDataSyncRCLimiter.release(); msg.sendResponse(new FullDataSourceResponseMessage(null, this)); return; } this.serverside.dataFileHandler.getAsync(msg.sectionPos).thenAccept(fullDataSource -> { - serverPlayerState.loginDataSyncRCLimiter.release(); + rateLimiterSet.loginDataSyncRCLimiter.release(); msg.sendResponse(new FullDataSourceResponseMessage((CompleteFullDataSource) fullDataSource, this)); }); } @@ -160,7 +162,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel { msg.sendResponse(new GenTaskPriorityResponseMessage( this.serverside.dataFileHandler.getLoadStates(msg.posList.stream() - .limit(serverPlayerState.genTaskPriorityRequestRateLimiter.acquireOrDrain(msg.posList.size())) + .limit(serverPlayerState.getRateLimiterSet(this).genTaskPriorityRequestRateLimiter.acquireOrDrain(msg.posList.size())) ::iterator) )); })); @@ -177,7 +179,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel ServerPlayerState serverPlayerState = this.remotePlayerConnectionHandler.getConnectedPlayer(msg); if (serverPlayerState != null) { - serverPlayerState.fullDataRequestConcurrencyLimiter.release(); + serverPlayerState.getRateLimiterSet(this).fullDataRequestConcurrencyLimiter.release(); } entry.requestCollectionSemaphore.acquireUninterruptibly(Short.MAX_VALUE); @@ -233,7 +235,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel continue; } - serverPlayerState.fullDataRequestConcurrencyLimiter.release(); + serverPlayerState.getRateLimiterSet(this).fullDataRequestConcurrencyLimiter.release(); msg.sendResponse(new FullDataSourceResponseMessage(completeSource, this)); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index 974a4829f..a0c7e1b55 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -5,6 +5,7 @@ import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.types.ConfigEntry; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.level.IDhClientLevel; import com.seibel.distanthorizons.core.logging.ConfigBasedSpamLogger; import com.seibel.distanthorizons.core.logging.f3.F3Screen; @@ -19,6 +20,7 @@ import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateLimiter; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import io.netty.channel.ChannelException; import org.apache.logging.log4j.LogManager; @@ -36,6 +38,8 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, private static final ConfigBasedSpamLogger LOGGER = new ConfigBasedSpamLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get(), 3); + private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); + protected static final long SHUTDOWN_TIMEOUT_SECONDS = 5; public final ClientNetworkState networkState; @@ -289,6 +293,11 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, return; } + if (MC_CLIENT.getWrappedClientLevel() != this.level.getClientLevelWrapper()) + { + return; + } + for (Map.Entry mapEntry : this.waitingTasks.entrySet()) { r.renderBox(new DebugRenderer.Box(mapEntry.getKey(), -32f, 64f, 0.05f, diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java index 5c62b7187..f070a3dd2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java @@ -12,6 +12,7 @@ public abstract class AbstractMultiplayerConfig implements INetworkObject public abstract boolean isRealTimeUpdatesEnabled(); public abstract boolean isLoginDataSyncEnabled(); public abstract int getLoginDataSyncRCLimit(); + public abstract boolean getGenerateMultipleDimensions(); @Override public void encode(ByteBuf out) @@ -23,6 +24,7 @@ public abstract class AbstractMultiplayerConfig implements INetworkObject out.writeBoolean(this.isRealTimeUpdatesEnabled()); out.writeBoolean(this.isLoginDataSyncEnabled()); out.writeInt(this.getLoginDataSyncRCLimit()); + out.writeBoolean(this.getGenerateMultipleDimensions()); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java index 680ef3ef2..9f2ef5974 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java @@ -28,6 +28,10 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig public int loginDataSyncRCLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.loginDataSyncRCLimit.get(); @Override public int getLoginDataSyncRCLimit() { return this.loginDataSyncRCLimit; } + public boolean generateMultipleDimensions = Config.Client.Advanced.Multiplayer.ServerNetworking.generateMultipleDimensions.get(); + @Override public boolean getGenerateMultipleDimensions() { return this.generateMultipleDimensions; } + + @Override public void decode(ByteBuf in) { @@ -38,6 +42,7 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig this.realTimeUpdatesEnabled = in.readBoolean(); this.loginDataSyncEnabled = in.readBoolean(); this.loginDataSyncRCLimit = in.readInt(); + this.generateMultipleDimensions = in.readBoolean(); } @Override public String toString() @@ -50,6 +55,7 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig ", realTimeUpdatesEnabled=" + this.realTimeUpdatesEnabled + ", loginDataSyncEnabled=" + this.loginDataSyncEnabled + ", loginDataSyncRCLimit=" + this.loginDataSyncRCLimit + + ", generateMultipleDimensions=" + this.generateMultipleDimensions + '}'; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java index 3624c2ed4..08651b052 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java @@ -18,12 +18,14 @@ public class MultiplayerConfigChangeListener implements Closeable Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates, Config.Client.Advanced.Multiplayer.ServerNetworking.enableLoginDataSync, Config.Client.Advanced.Multiplayer.ServerNetworking.loginDataSyncRCLimit, + Config.Client.Advanced.Multiplayer.ServerNetworking.generateMultipleDimensions, }; - private final ArrayList changeListeners = new ArrayList<>(); + private final ArrayList changeListeners; public MultiplayerConfigChangeListener(Runnable runnable) { + this.changeListeners = new ArrayList<>(CONFIG_ENTRIES.length); for (ConfigEntry entry : CONFIG_ENTRIES) { this.changeListeners.add(new ConfigChangeListener(entry, ignored -> runnable.run())); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ConstrainedMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ConstrainedMultiplayerConfig.java index c998d496b..9e136c94d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ConstrainedMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ConstrainedMultiplayerConfig.java @@ -54,6 +54,12 @@ public class ConstrainedMultiplayerConfig extends AbstractMultiplayerConfig return Math.min(this.clientConfig.loginDataSyncRCLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.loginDataSyncRCLimit.get()); } + @Override + public boolean getGenerateMultipleDimensions() + { + return this.clientConfig.generateMultipleDimensions && Config.Client.Advanced.Multiplayer.ServerNetworking.generateMultipleDimensions.get(); + } + @Override public void decode(ByteBuf in) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java index 207374835..cd047edcc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java @@ -3,6 +3,8 @@ package com.seibel.distanthorizons.core.multiplayer.server; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.Maps; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.config.types.ConfigEntry; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; @@ -29,6 +31,8 @@ import java.util.function.Consumer; public class RemotePlayerConnectionHandler implements Closeable { + private static final ConfigEntry GENERATE_MULTIPLE_DIMENSIONS_CONFIG = Config.Client.Advanced.Multiplayer.ServerNetworking.generateMultipleDimensions; + private final ScopedNetworkEventSource eventSource; private final ConcurrentHashMap playersByUUID = new ConcurrentHashMap<>(); private final BiMap playersByConnection = Maps.synchronizedBiMap(HashBiMap.create()); @@ -115,7 +119,7 @@ public class RemotePlayerConnectionHandler implements Closeable if (msg instanceof ILevelRelatedMessage) { - if (!((ILevelRelatedMessage) msg).isLevelValid(level.getLevelWrapper())) + if (!GENERATE_MULTIPLE_DIMENSIONS_CONFIG.get() && !((ILevelRelatedMessage) msg).isSameLevelAs(level.getLevelWrapper())) { if (msg instanceof FutureTrackableNetworkMessage) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index 53cfe1a95..6ef80d445 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -1,5 +1,6 @@ package com.seibel.distanthorizons.core.multiplayer.server; +import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.network.IConnection; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage; @@ -9,6 +10,8 @@ import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateLimite import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import org.jetbrains.annotations.NotNull; +import java.util.concurrent.ConcurrentHashMap; + import static com.seibel.distanthorizons.core.config.Config.Client.Advanced.Multiplayer.ServerNetworking; public class ServerPlayerState @@ -24,33 +27,42 @@ public class ServerPlayerState ignored -> this.connection.disconnect("You have been repeatedly exceeding rate/concurrency limits.") ); - public final SupplierBasedRateAndConcurrencyLimiter fullDataRequestConcurrencyLimiter = new SupplierBasedRateAndConcurrencyLimiter<>( - () -> ServerNetworking.generationRequestRCLimit.get(), - msg -> { - msg.sendResponse(new RateLimitedException("Full data request rate/concurrency limit: " + this.config.getFullDataRequestConcurrencyLimit())); - this.rateLimitKickTrigger.tryAcquire(null); - } - ); - - public final SupplierBasedRateLimiter genTaskPriorityRequestRateLimiter = new SupplierBasedRateLimiter<>( - () -> ServerNetworking.genTaskPriorityRequestRateLimit.get(), - msg -> { - msg.sendResponse(new RateLimitedException("Generation task priority check rate limit: " + this.config.getFullDataRequestConcurrencyLimit())); - this.rateLimitKickTrigger.tryAcquire(null); - } - ); - - public final SupplierBasedRateAndConcurrencyLimiter loginDataSyncRCLimiter = new SupplierBasedRateAndConcurrencyLimiter<>( - () -> ServerNetworking.loginDataSyncRCLimit.get(), - msg -> { - msg.sendResponse(new RateLimitedException("Data sync rate/concurrency limit: " + this.config.getLoginDataSyncRCLimit())); - this.rateLimitKickTrigger.tryAcquire(null); - } - ); - - + private final ConcurrentHashMap rateLimiterSets = new ConcurrentHashMap<>(); + public RateLimiterSet getRateLimiterSet(DhServerLevel level) + { + return this.rateLimiterSets.computeIfAbsent(level, ignored -> new RateLimiterSet()); + } public ServerPlayerState(IServerPlayerWrapper serverPlayer) { this.serverPlayer = serverPlayer; } - + + + public class RateLimiterSet + { + public final SupplierBasedRateAndConcurrencyLimiter fullDataRequestConcurrencyLimiter = new SupplierBasedRateAndConcurrencyLimiter<>( + () -> ServerNetworking.generationRequestRCLimit.get(), + msg -> { + msg.sendResponse(new RateLimitedException("Full data request rate/concurrency limit: " + ServerPlayerState.this.config.getFullDataRequestConcurrencyLimit())); + ServerPlayerState.this.rateLimitKickTrigger.tryAcquire(null); + } + ); + + public final SupplierBasedRateLimiter genTaskPriorityRequestRateLimiter = new SupplierBasedRateLimiter<>( + () -> ServerNetworking.genTaskPriorityRequestRateLimit.get(), + msg -> { + msg.sendResponse(new RateLimitedException("Generation task priority check rate limit: " + ServerPlayerState.this.config.getFullDataRequestConcurrencyLimit())); + ServerPlayerState.this.rateLimitKickTrigger.tryAcquire(null); + } + ); + + public final SupplierBasedRateAndConcurrencyLimiter loginDataSyncRCLimiter = new SupplierBasedRateAndConcurrencyLimiter<>( + () -> ServerNetworking.loginDataSyncRCLimit.get(), + msg -> { + msg.sendResponse(new RateLimitedException("Data sync rate/concurrency limit: " + ServerPlayerState.this.config.getLoginDataSyncRCLimit())); + ServerPlayerState.this.rateLimitKickTrigger.tryAcquire(null); + } + ); + + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ILevelRelatedMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ILevelRelatedMessage.java index 30a72094d..8cd0bab67 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ILevelRelatedMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ILevelRelatedMessage.java @@ -10,7 +10,7 @@ public interface ILevelRelatedMessage * Checks whether the message's level matches the given level. */ @SuppressWarnings("BooleanMethodIsAlwaysInverted") - default boolean isLevelValid(ILevelWrapper levelWrapper) + default boolean isSameLevelAs(ILevelWrapper levelWrapper) { return levelWrapper.getDimensionType().getDimensionName().hashCode() == this.getLevelHashCode(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java index 5fa967d17..795232ec2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java @@ -95,7 +95,7 @@ public class FullDataPartialUpdateMessage extends NetworkMessage implements ILev @Nullable public ChunkSizedFullDataAccessor getFullDataSource(IDhLevel level) throws IOException, InterruptedException { - if (!this.isLevelValid(level.getLevelWrapper())) + if (!this.isSameLevelAs(level.getLevelWrapper())) { return null; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedRateLimiter.java b/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedRateLimiter.java index bc7e2ff05..f0c98fdde 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedRateLimiter.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedRateLimiter.java @@ -16,7 +16,7 @@ public class SupplierBasedRateLimiter private final Supplier maxRateSupplier; private final Consumer onFailureConsumer; - private final RateLimiter rateLimiter = RateLimiter.create(1); + private final RateLimiter rateLimiter = RateLimiter.create(Double.POSITIVE_INFINITY); public SupplierBasedRateLimiter(Supplier maxRateSupplier) { this(maxRateSupplier, ignored -> { }); } public SupplierBasedRateLimiter(Supplier maxRateSupplier, Consumer onFailureConsumer) diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index 5ea9d2b72..5545e9bd0 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -355,13 +355,13 @@ "distanthorizons.config.client.advanced.multiplayer.serverNetworking.serverPort": "Server Port", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.serverPort.@tooltip": "The port on the server that's used for sending LOD data.", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationSectionNote": " \u25cf Generation", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestRCLimit": "Gen task rate/concurrency limit", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestRCLimit": "Gen Task Rate/Concurrency Limit", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestRCLimit.@tooltip": "Limits the amount of generation requests sent by client and processed by server.", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestBeginDelay": "Generation request begin delay", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestBeginDelay": "Generation Request Begin Delay", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestBeginDelay.@tooltip": "Adds a delay in seconds before sending LOD requests, when generation is enabled. \nIncrease this value if initial generation starts too far away.", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.genTaskPriorityDistanceRatio": "Gen task priority distance max ratio", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.genTaskPriorityDistanceRatio": "Gen Task Priority Distance Max Ratio", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.genTaskPriorityDistanceRatio.@tooltip": "Controls the max ratio between distances of nearest unloaded sections of each priority. \nFor example, a value of 2 means that the nearest lower priority section will be allowed to stay \nunloaded only if it's at most 2x closer than one of a higher priority.", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.genTaskPriorityRequestRateLimit": "Gen task priority check rate limit", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.genTaskPriorityRequestRateLimit": "Gen Task Priority Check Rate Limit", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.genTaskPriorityRequestRateLimit.@tooltip": "Limits the amount of LOD sections that the client can request states for, per second.", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.realTimeUpdatesSectionNote": " \u25cf Real Time Updates", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableRealTimeUpdates": "Enable Real Time Updates", @@ -369,8 +369,11 @@ "distanthorizons.config.client.advanced.multiplayer.serverNetworking.loginDataSyncSectionNote": " \u25cf Login Data Sync", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableLoginDataSync": "Synchronize LODs on Login", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableLoginDataSync.@tooltip": "Enables updating of saved LODs after login.", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.loginDataSyncRCLimit": "Login sync rate/concurrency limit", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.loginDataSyncRCLimit": "Login Sync Rate/Concurrency Limit", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.loginDataSyncRCLimit.@tooltip": "Limits the amount of sent/processed LOD *update* requests concurrently, per player.", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.immersivePortalsSectionNote": " \u25cf Immersive Portals", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generateMultipleDimensions": "Generate Multiple Dimensions", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generateMultipleDimensions.@tooltip": "Controls whether the players should be allowed to generate in multiple dimensions at once. \n\nFor server owners: \nAll dimensions share the same thread pool. This means that empty dimensions (i.e. without players in them) \nmay slow down the generation of non-empty ones.", "distanthorizons.config.client.advanced.multiplayer.serverFolderNameMode": "Server Folder Mode", "distanthorizons.config.client.advanced.multiplayer.serverFolderNameMode.@tooltip": From 7bb0ec314877dfc08dcd9eb684b1281a65058304 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 12 Feb 2024 21:39:30 +0500 Subject: [PATCH 100/205] Fix compilation --- .../java/com/seibel/distanthorizons/core/level/IDhLevel.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java index ed82b95ae..e5c9c02a5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java @@ -38,8 +38,6 @@ public interface IDhLevel extends AutoCloseable */ ILevelWrapper getLevelWrapper(); - void updateDataSourcesWithChunkData(ChunkSizedFullDataAccessor data); - @Nullable CompletableFuture updateChunkAsync(IChunkWrapper chunk); From 15fe39c1bb876cbf417ce5810881ac28f7bbb1f3 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 13 Feb 2024 22:03:55 +0500 Subject: [PATCH 101/205] Fix connection exception handling --- .../core/network/IConnection.java | 14 +----- .../core/network/NetworkClient.java | 12 ++++- .../core/network/NetworkServer.java | 10 +++- .../core/network/protocol/MessageHandler.java | 25 +++++++++- .../protocol/NetworkChannelInitializer.java | 11 +++-- .../protocol/NetworkExceptionHandler.java | 49 ------------------- 6 files changed, 53 insertions(+), 68 deletions(-) delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkExceptionHandler.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java b/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java index bc605a52b..4039d07cb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java @@ -22,6 +22,8 @@ public interface IConnection @Nullable ChannelHandlerContext getChannelContext(); NetworkEventSource getRequestHandler(); + @Nullable + Throwable getCloseReason(); @Nullable default SocketAddress getRemoteAddress() @@ -88,16 +90,4 @@ public interface IConnection .addListener(ChannelFutureListener.CLOSE); } - @Nullable - default Throwable getCloseReason() - { - ChannelHandlerContext ctx = this.getChannelContext(); - if (ctx == null) - { - return null; - } - - return ctx.channel().closeFuture().cause(); - } - } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index e038c6930..74c55661f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -33,6 +33,7 @@ import io.netty.channel.socket.nio.NioSocketChannel; import io.netty.util.concurrent.DefaultThreadFactory; import org.apache.logging.log4j.LogManager; +import javax.annotation.Nullable; import java.net.InetSocketAddress; import java.util.EnumSet; import java.util.Set; @@ -65,7 +66,13 @@ public class NetworkClient extends NetworkEventSource implements IConnection, Au private final InetSocketAddress address; - private final EventLoopGroup workerGroup = new NioEventLoopGroup(0, new DefaultThreadFactory("DH-Network - Client Thread")); + @Nullable + private Throwable closeReason; + @Override + @Nullable + public Throwable getCloseReason() { return this.closeReason; } + + private final EventLoopGroup workerGroup = new NioEventLoopGroup(0, new DefaultThreadFactory("DH-Network - Client Thread")); private final Bootstrap clientBootstrap = new Bootstrap() .group(this.workerGroup) .channel(NioSocketChannel.class) @@ -75,7 +82,8 @@ public class NetworkClient extends NetworkEventSource implements IConnection, Au msg.setConnection(this); this.handleMessage(msg); }, - ctx -> this.addNewConnection(this) + ctx -> this.addNewConnection(this), + (ctx, closeReason) -> this.closeReason = closeReason ))); private Channel channel; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java index 2eff15ac0..62f98d35f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java @@ -39,6 +39,7 @@ import io.netty.handler.logging.LoggingHandler; import io.netty.util.concurrent.DefaultThreadFactory; import org.apache.logging.log4j.LogManager; +import javax.annotation.Nullable; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicBoolean; @@ -110,7 +111,8 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable msg.setConnection(this.connections.computeIfAbsent(ctx, Connection::new)); this.handleMessage(msg); }, - ctx -> this.addNewConnection(this.connections.computeIfAbsent(ctx, Connection::new)) + ctx -> this.addNewConnection(this.connections.computeIfAbsent(ctx, Connection::new)), + (ctx, closeReason) -> ((Connection) this.connections.computeIfAbsent(ctx, Connection::new)).closeReason = closeReason ))); ChannelFuture bindFuture = bootstrap.bind(this.port); @@ -148,6 +150,12 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable { private final ChannelHandlerContext channelContext; + @Nullable + private Throwable closeReason; + @Override + @Nullable + public Throwable getCloseReason() { return this.closeReason; } + public Connection(ChannelHandlerContext channelContext) { this.channelContext = channelContext; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java index dd6df7fc9..1d4fd375c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java @@ -28,6 +28,7 @@ import io.netty.channel.SimpleChannelInboundHandler; import org.apache.logging.log4j.LogManager; import org.jetbrains.annotations.NotNull; +import java.net.SocketException; import java.util.function.BiConsumer; import java.util.function.Consumer; @@ -39,11 +40,16 @@ public class MessageHandler extends SimpleChannelInboundHandler private final BiConsumer messageConsumer; private final Consumer channelActiveConsumer; + private final BiConsumer closeReasonConsumer; - public MessageHandler(BiConsumer messageConsumer, Consumer channelActiveConsumer) + public MessageHandler( + BiConsumer messageConsumer, + Consumer channelActiveConsumer, + BiConsumer closeReasonConsumer) { this.messageConsumer = messageConsumer; this.channelActiveConsumer = channelActiveConsumer; + this.closeReasonConsumer = closeReasonConsumer; } @Override @@ -67,4 +73,21 @@ public class MessageHandler extends SimpleChannelInboundHandler this.channelRead0(channelContext, new CloseEvent()); } + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception + { + super.exceptionCaught(ctx, cause); + this.closeReasonConsumer.accept(ctx, cause); + ctx.close(); + + if (cause instanceof SocketException) + { + LOGGER.info("Exception caught in channel: [" + ctx.name() + "]: " + cause.getMessage()); + } + else + { + LOGGER.error("Exception caught in channel: [" + ctx.name() + "].", cause); + } + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkChannelInitializer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkChannelInitializer.java index d17209a7e..898798c88 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkChannelInitializer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkChannelInitializer.java @@ -25,9 +25,15 @@ import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.LengthFieldPrepender; import org.jetbrains.annotations.NotNull; -/** used when creating a network channel */ +/** Used when creating a network channel */ public class NetworkChannelInitializer extends ChannelInitializer { + /** + * 4 MiB should be enough for any transferred data.
+ * Currently largest transferred data is DH full data sections, which usually don't exceed 1-2 MiB in size. + */ + private static final int MAX_MESSAGE_LENGTH = 4194304; + private final MessageHandler messageHandler; public NetworkChannelInitializer(MessageHandler messageHandler) { this.messageHandler = messageHandler; } @@ -43,12 +49,11 @@ public class NetworkChannelInitializer extends ChannelInitializer pipeline.addLast(new NetworkOutboundExceptionRouter()); // Decoder - pipeline.addLast(new LengthFieldBasedFrameDecoder(4194304 /* 4 MiB */, 0, Integer.BYTES, 0, Integer.BYTES)); + pipeline.addLast(new LengthFieldBasedFrameDecoder(MAX_MESSAGE_LENGTH, 0, Integer.BYTES, 0, Integer.BYTES)); pipeline.addLast(new MessageDecoder()); // Handler pipeline.addLast(this.messageHandler); - pipeline.addLast(new NetworkExceptionHandler()); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkExceptionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkExceptionHandler.java deleted file mode 100644 index 905512b2f..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkExceptionHandler.java +++ /dev/null @@ -1,49 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.network.protocol; - -import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelInboundHandlerAdapter; -import org.apache.logging.log4j.LogManager; - -import java.net.SocketException; - -public class NetworkExceptionHandler extends ChannelInboundHandlerAdapter -{ - private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), - () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); - - @Override - public void exceptionCaught(ChannelHandlerContext channelContext, Throwable cause) - { - if (cause instanceof SocketException) - { - LOGGER.info("Exception caught in channel: [" + channelContext.name() + "]: " + cause.getMessage()); - } - else - { - LOGGER.error("Exception caught in channel: [" + channelContext.name() + "].", cause); - } - channelContext.close(); - } - -} From ebb47990f41e16ad8090ebd2b079b37e375c5153 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Thu, 15 Feb 2024 23:15:01 +0500 Subject: [PATCH 102/205] Fix immersive portals --- .../distanthorizons/coreapi/ModInfo.java | 4 ++-- .../WorldRemoteGenerationQueue.java | 2 +- .../core/level/DhClientLevel.java | 6 +++++- .../server/RemotePlayerConnectionHandler.java | 21 +++++++++++-------- .../FullDataPartialUpdateMessage.java | 2 -- .../GenTaskPriorityRequestMessage.java | 16 ++++++++++++-- .../core/network/protocol/MessageHandler.java | 3 +-- .../core/pos/DhBlockPos2D.java | 2 ++ .../world/IDimensionTypeWrapper.java | 2 ++ 9 files changed, 39 insertions(+), 19 deletions(-) diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java index 6665b77be..377805e01 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java @@ -26,8 +26,8 @@ package com.seibel.distanthorizons.coreapi; public final class ModInfo { public static final String ID = "distanthorizons"; - /** The internal protocol version used for networking */ - public static final int PROTOCOL_VERSION = 1; + /** The internal protocol version used for networking. Incremented every time any packets are added, changed or removed. */ + public static final int PROTOCOL_VERSION = 2; /** The protocol version used for multiverse networking */ public static final int MULTIVERSE_PLUGIN_PROTOCOL_VERSION = 1; /** The internal mod name */ diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 34c5da6e8..c98ff2197 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -100,7 +100,7 @@ public class WorldRemoteGenerationQueue extends AbstractFullDataRequestQueue imp return; }; - CompletableFuture request = this.networkState.getClient().sendRequest(new GenTaskPriorityRequestMessage(posList), GenTaskPriorityResponseMessage.class); + CompletableFuture request = this.networkState.getClient().sendRequest(new GenTaskPriorityRequestMessage(posList, this.level), GenTaskPriorityResponseMessage.class); this.genTaskPriorityRequest = request; request.handleAsync((response, throwable) -> { try diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index ed7e2f014..f468076b7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -210,7 +210,11 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel { this.dataFileHandler.removeGenRequestIf(p -> !renderState.quadtree.isSectionPosInBounds(p)); } - this.worldGenModule.worldGenTick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); + + this.worldGenModule.worldGenTick( + new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos()) + .scale(MC_CLIENT.getWrappedClientLevel().getDimensionType().getTeleportationScale(this.getLevelWrapper().getDimensionType())) + ); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java index cd047edcc..6e17a05bf 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java @@ -19,6 +19,7 @@ import com.seibel.distanthorizons.core.network.messages.session.PlayerUUIDMessag import com.seibel.distanthorizons.core.network.messages.session.RemotePlayerConfigMessage; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; +import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import org.jetbrains.annotations.Nullable; @@ -112,23 +113,25 @@ public class RemotePlayerConnectionHandler implements Closeable { return this.connectedPlayersOnly((msg, serverPlayerState) -> { - if (serverPlayerState.serverPlayer.getLevel() != level.getLevelWrapper()) + LodUtil.assertTrue(msg instanceof ILevelRelatedMessage, "Received message does not implement " + ILevelRelatedMessage.class.getSimpleName() + ": " + msg.getClass().getSimpleName()); + + // Handle only in requested dimension + if (!((ILevelRelatedMessage) msg).isSameLevelAs(level.getLevelWrapper())) { return; } - if (msg instanceof ILevelRelatedMessage) + // If player is not in this dimension and handling multiple dimensions at once is not allowed + if (serverPlayerState.serverPlayer.getLevel() != level.getLevelWrapper() + && !GENERATE_MULTIPLE_DIMENSIONS_CONFIG.get()) { - if (!GENERATE_MULTIPLE_DIMENSIONS_CONFIG.get() && !((ILevelRelatedMessage) msg).isSameLevelAs(level.getLevelWrapper())) + // If the message can be replied to - reply with error, otherwise just ignore + if (msg instanceof FutureTrackableNetworkMessage) { - if (msg instanceof FutureTrackableNetworkMessage) - { - ((FutureTrackableNetworkMessage) msg).sendResponse(new InvalidLevelException("Invalid level")); - } - - return; + ((FutureTrackableNetworkMessage) msg).sendResponse(new InvalidLevelException("Invalid level")); } + return; } next.accept(msg, serverPlayerState); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java index 795232ec2..652aa0e86 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java @@ -86,9 +86,7 @@ public class FullDataPartialUpdateMessage extends NetworkMessage implements ILev public void decode(ByteBuf in) { this.levelHashCode = in.readInt(); - this.chunkPos = new DhChunkPos(in.readInt(), in.readInt()); - this.dataBuffer = in.readBytes(in.readInt()); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/GenTaskPriorityRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/GenTaskPriorityRequestMessage.java index e0eff26b5..4d5dfa414 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/GenTaskPriorityRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/GenTaskPriorityRequestMessage.java @@ -19,6 +19,8 @@ package com.seibel.distanthorizons.core.network.messages.fullData.generation; +import com.seibel.distanthorizons.core.level.IDhLevel; +import com.seibel.distanthorizons.core.network.messages.base.ILevelRelatedMessage; import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; import com.seibel.distanthorizons.core.pos.DhSectionPos; import io.netty.buffer.ByteBuf; @@ -26,25 +28,35 @@ import io.netty.buffer.ByteBuf; import java.util.ArrayList; import java.util.List; -public class GenTaskPriorityRequestMessage extends FutureTrackableNetworkMessage +public class GenTaskPriorityRequestMessage extends FutureTrackableNetworkMessage implements ILevelRelatedMessage { public List posList = new ArrayList<>(); + private int levelHashCode; + @Override + public int getLevelHashCode() { return this.levelHashCode; } + + public GenTaskPriorityRequestMessage() { } - public GenTaskPriorityRequestMessage(List posList) + public GenTaskPriorityRequestMessage(List posList, IDhLevel level) { this.posList = posList; + + // TODO Multiverse support + this.levelHashCode = level.getLevelWrapper().getDimensionType().getDimensionName().hashCode(); } @Override protected void encode0(ByteBuf out) { + out.writeInt(this.levelHashCode); this.encodeCollection(out, this.posList); } @Override protected void decode0(ByteBuf in) { + this.levelHashCode = in.readInt(); this.decodeCollection(in, this.posList, DhSectionPos::zero); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java index 1d4fd375c..eb2c00d63 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java @@ -74,9 +74,8 @@ public class MessageHandler extends SimpleChannelInboundHandler } @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - super.exceptionCaught(ctx, cause); this.closeReasonConsumer.accept(ctx, cause); ctx.close(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhBlockPos2D.java b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhBlockPos2D.java index c6f8c1906..60bb331c4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhBlockPos2D.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhBlockPos2D.java @@ -59,6 +59,8 @@ public class DhBlockPos2D public DhBlockPos2D subtract(DhBlockPos2D other) { return new DhBlockPos2D(this.x - other.x, this.z - other.z); } + public DhBlockPos2D scale(double scale) { return new DhBlockPos2D((int) (this.x * scale), (int) (this.z * scale)); } + public Pos2D toPos2D() { return new Pos2D(this.x, this.z); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IDimensionTypeWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IDimensionTypeWrapper.java index 0422e0ba9..d33fc77fa 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IDimensionTypeWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IDimensionTypeWrapper.java @@ -37,4 +37,6 @@ public interface IDimensionTypeWrapper extends IDhApiDimensionTypeWrapper, IBind @Override boolean hasSkyLight(); + double getTeleportationScale(IDimensionTypeWrapper to); + } From d966697ecb7d62f6a3f5757ffa1c53e9c480a9b0 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 24 Feb 2024 21:37:54 +0500 Subject: [PATCH 103/205] Change some wording --- .../multiplayer/server/RemotePlayerConnectionHandler.java | 2 +- .../seibel/distanthorizons/core/network/NetworkClient.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java index 6e17a05bf..243d0abb8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java @@ -163,7 +163,7 @@ public class RemotePlayerConnectionHandler implements Closeable IConnection connection = this.playersByConnection.inverse().remove(dhPlayer); if (connection != null) { - connection.disconnect("You have been disconnected."); + connection.disconnect("You have logged out."); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index 74c55661f..e138509ae 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -99,11 +99,11 @@ public class NetworkClient extends NetworkEventSource implements IConnection, Au this.registerHandlers(); } - private void registerHandlers() + private void registerHandlers() { this.registerHandler(CloseReasonMessage.class, closeReasonMessage -> { - LOGGER.warn(closeReasonMessage.reason); + LOGGER.warn("Received disconnect reason: " + closeReasonMessage.reason); this.connectionState.set(EConnectionState.GOT_CLOSE_REASON); }); From 1fd85f22499a2daade8a12e25f2b159a867ecdfd Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 24 Feb 2024 22:48:24 +0500 Subject: [PATCH 104/205] Show server side messages in F3 on disconnect --- .../core/network/NetworkClient.java | 4 ++- .../core/network/protocol/MessageHandler.java | 26 +++++++++---------- 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java index e138509ae..45c5873b8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -103,7 +103,9 @@ public class NetworkClient extends NetworkEventSource implements IConnection, Au { this.registerHandler(CloseReasonMessage.class, closeReasonMessage -> { - LOGGER.warn("Received disconnect reason: " + closeReasonMessage.reason); + String fullCloseText = "[Server] " + closeReasonMessage.reason; + LOGGER.warn(fullCloseText); + this.closeReason = new Exception(fullCloseText); this.connectionState.set(EConnectionState.GOT_CLOSE_REASON); }); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java index eb2c00d63..6ff466ef7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java @@ -40,16 +40,16 @@ public class MessageHandler extends SimpleChannelInboundHandler private final BiConsumer messageConsumer; private final Consumer channelActiveConsumer; - private final BiConsumer closeReasonConsumer; + private final BiConsumer exceptionConsumer; public MessageHandler( BiConsumer messageConsumer, Consumer channelActiveConsumer, - BiConsumer closeReasonConsumer) + BiConsumer exceptionConsumer) { this.messageConsumer = messageConsumer; this.channelActiveConsumer = channelActiveConsumer; - this.closeReasonConsumer = closeReasonConsumer; + this.exceptionConsumer = exceptionConsumer; } @Override @@ -66,19 +66,9 @@ public class MessageHandler extends SimpleChannelInboundHandler this.channelActiveConsumer.accept(ctx); } - @Override - public void channelInactive(@NotNull ChannelHandlerContext channelContext) throws Exception - { - super.channelInactive(channelContext); - this.channelRead0(channelContext, new CloseEvent()); - } - @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { - this.closeReasonConsumer.accept(ctx, cause); - ctx.close(); - if (cause instanceof SocketException) { LOGGER.info("Exception caught in channel: [" + ctx.name() + "]: " + cause.getMessage()); @@ -86,7 +76,17 @@ public class MessageHandler extends SimpleChannelInboundHandler else { LOGGER.error("Exception caught in channel: [" + ctx.name() + "].", cause); + this.exceptionConsumer.accept(ctx, cause); } + + ctx.close(); + } + + @Override + public void channelInactive(@NotNull ChannelHandlerContext channelContext) throws Exception + { + super.channelInactive(channelContext); + this.channelRead0(channelContext, new CloseEvent()); } } From 2d4f0338917607ddc4460aa213632ed350765799 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 25 Feb 2024 18:05:45 +0500 Subject: [PATCH 105/205] Do not wait for world gen tasks to stop while shutting down queue --- .../GeneratedFullDataFileHandler.java | 6 ++- .../core/generation/WorldGenerationQueue.java | 53 +++++++------------ .../tasks/InProgressWorldGenTaskGroup.java | 5 +- 3 files changed, 27 insertions(+), 37 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java index a92de2c44..61355ad58 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java @@ -123,7 +123,11 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler } }); - this.worldGenQueueRef.get().cancelGenTasks(removedRequests); + IWorldGenerationQueue worldGenQueue = this.worldGenQueueRef.get(); + if (worldGenQueue != null) + { + worldGenQueue.cancelGenTasks(removedRequests); + } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java index 00fd702f0..fa505b1d7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java @@ -171,6 +171,7 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender // running tasks // //===============// + @Override public void startGenerationQueueAndSetTargetPos(DhBlockPos2D targetPos) { // update the target pos @@ -271,8 +272,7 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender if (!this.inProgressGenTasksByLodPos.containsKey(closestTask.pos)) { // no task exists for this position, start one - InProgressWorldGenTaskGroup newTaskGroup = new InProgressWorldGenTaskGroup(closestTaskGroup); - boolean taskStarted = this.tryStartingWorldGenTaskGroup(newTaskGroup); + boolean taskStarted = this.tryStartingWorldGenTaskGroup(closestTaskGroup); if (!taskStarted) { LOGGER.trace("Unable to start task: "+closestTask.pos+", skipping. Task position may have already been generated."); @@ -317,11 +317,12 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender return true; } } + /** @return true if the task was started, false otherwise */ - private boolean tryStartingWorldGenTaskGroup(InProgressWorldGenTaskGroup newTaskGroup) + private boolean tryStartingWorldGenTaskGroup(WorldGenTaskGroup group) { - byte taskDetailLevel = newTaskGroup.group.dataDetail; - DhSectionPos taskPos = newTaskGroup.group.pos; + byte taskDetailLevel = group.dataDetail; + DhSectionPos taskPos = group.pos; byte granularity = (byte) (taskPos.getDetailLevel() - taskDetailLevel); LodUtil.assertTrue(granularity >= this.minGranularity && granularity <= this.maxGranularity); LodUtil.assertTrue(taskDetailLevel >= this.highestDataDetail && taskDetailLevel <= this.lowestDataDetail); @@ -329,17 +330,17 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender DhChunkPos chunkPosMin = new DhChunkPos(taskPos.getSectionBBoxPos().getCornerBlockPos()); // check if this is a duplicate generation task - if (this.alreadyGeneratedPosHashSet.containsKey(newTaskGroup.group.pos)) + if (this.alreadyGeneratedPosHashSet.containsKey(group.pos)) { // temporary solution to prevent generating the same section multiple times LOGGER.trace("Duplicate generation section " + taskPos + " with granularity [" + granularity + "] at " + chunkPosMin + ". Skipping..."); // sending a success result is necessary to make sure the render sections are reloaded correctly - newTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateSuccess(new DhSectionPos(granularity, taskPos.getX(), taskPos.getZ())))); + group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateSuccess(new DhSectionPos(granularity, taskPos.getX(), taskPos.getZ())))); return false; } - this.alreadyGeneratedPosHashSet.put(newTaskGroup.group.pos, Thread.currentThread().getStackTrace()); - this.alreadyGeneratedPosQueue.add(newTaskGroup.group.pos); + this.alreadyGeneratedPosHashSet.put(group.pos, Thread.currentThread().getStackTrace()); + this.alreadyGeneratedPosQueue.add(group.pos); // remove extra tracked duplicate positions while (this.alreadyGeneratedPosQueue.size() > MAX_ALREADY_GENERATED_COUNT) @@ -352,8 +353,8 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender //LOGGER.info("Generating section "+taskPos+" with granularity "+granularity+" at "+chunkPosMin); this.numberOfTasksQueued++; - newTaskGroup.genFuture = this.startGenerationEvent(chunkPosMin, granularity, taskDetailLevel, newTaskGroup.group::consumeChunkData); - LodUtil.assertTrue(newTaskGroup.genFuture != null); + CompletableFuture genFuture = this.startGenerationEvent(chunkPosMin, granularity, taskDetailLevel, group::consumeChunkData); + InProgressWorldGenTaskGroup newTaskGroup = new InProgressWorldGenTaskGroup(group, genFuture); newTaskGroup.genFuture.whenComplete((voidObj, exception) -> { @@ -484,14 +485,15 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender // getters // //=========// - public int getWaitingTaskCount() { return this.waitingTasks.size(); } - public int getInProgressTaskCount() { return this.inProgressGenTasksByLodPos.size(); } + @Override public int getWaitingTaskCount() { return this.waitingTasks.size(); } + @Override public int getInProgressTaskCount() { return this.inProgressGenTasksByLodPos.size(); } //==========// // shutdown // //==========// + @Override public CompletableFuture startClosing(boolean cancelCurrentGeneration, boolean alsoInterruptRunning) { LOGGER.info("Closing world gen queue"); @@ -503,13 +505,6 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender this.inProgressGenTasksByLodPos.values().forEach(runningTaskGroup -> { CompletableFuture genFuture = runningTaskGroup.genFuture; // Do this to prevent it getting swapped out - if (genFuture == null) - { - // genFuture's shouldn't be null, but sometimes they are... - LOGGER.info("Null gen future: "+runningTaskGroup.group.pos); - return; - } - if (cancelCurrentGeneration) { @@ -550,20 +545,6 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender - LOGGER.info("Awaiting world generator thread pool termination..."); - try - { - int waitTimeInSeconds = 3; - ThreadPoolExecutor executor = ThreadPools.getWorldGenExecutor(); - if (executor != null && !executor.awaitTermination(waitTimeInSeconds, TimeUnit.SECONDS)) - { - LOGGER.warn("World generator thread pool shutdown didn't complete after [" + waitTimeInSeconds + "] seconds. Some world generator requests may still be running."); - } - } - catch (InterruptedException e) - { - LOGGER.warn("World generator thread pool shutdown interrupted! Ignoring child threads...", e); - } @@ -621,13 +602,17 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender { index = 4 * X * X - X - Y; if (X < Y) + { index = index - 2 * (X - Y); + } } else { index = 4 * Y * Y - X - Y; if (X < Y) + { index = index + 2 * (X - Y); + } } return index; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/InProgressWorldGenTaskGroup.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/InProgressWorldGenTaskGroup.java index 5f3fe4cae..43f716fc6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/InProgressWorldGenTaskGroup.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/tasks/InProgressWorldGenTaskGroup.java @@ -28,12 +28,13 @@ import java.util.concurrent.CompletableFuture; public final class InProgressWorldGenTaskGroup { public final WorldGenTaskGroup group; - public CompletableFuture genFuture = null; + public final CompletableFuture genFuture; - public InProgressWorldGenTaskGroup(WorldGenTaskGroup group) + public InProgressWorldGenTaskGroup(WorldGenTaskGroup group, CompletableFuture genFuture) { this.group = group; + this.genFuture = genFuture; } } From 278ae04532075810e0d3076b9895d3f63976eb3e Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 5 Mar 2024 22:08:54 +0500 Subject: [PATCH 106/205] Initial buggy plugin channel support --- .../distanthorizons/coreapi/ModInfo.java | 19 +- .../core/api/internal/ClientApi.java | 204 ++++-------------- .../api/internal/ClientPluginChannelApi.java | 98 +++++++++ .../WorldRemoteGenerationQueue.java | 4 +- .../core/level/DhClientLevel.java | 7 +- .../core/level/DhServerLevel.java | 17 +- .../core/level/IKeyedClientLevelManager.java | 15 +- .../client/AbstractFullDataRequestQueue.java | 4 +- .../client/ClientNetworkState.java | 26 +-- .../server/RemotePlayerConnectionHandler.java | 46 ++-- .../multiplayer/server/ServerPlayerState.java | 8 +- .../core/network/NetworkEventSource.java | 162 +++----------- .../network/ScopedNetworkEventSource.java | 20 +- .../messages/AbstractMessageRegistry.java | 75 +++++++ .../core/network/messages/ICloseEvent.java | 14 ++ .../{base => netty}/ILevelRelatedMessage.java | 2 +- .../messages/netty/NettyMessageRegistry.java | 46 ++++ .../messages/{ => netty}/base/AckMessage.java | 8 +- .../{ => netty}/base/CancelMessage.java | 6 +- .../{ => netty}/base/CloseReasonMessage.java | 16 +- .../{ => netty}/base/ExceptionMessage.java | 24 +-- .../{ => netty}/base/HelloMessage.java | 8 +- .../base/NettyCloseEvent.java} | 14 +- .../FullDataPartialUpdateMessage.java | 8 +- .../FullDataSourceRequestMessage.java | 14 +- .../FullDataSourceResponseMessage.java | 10 +- .../GenTaskPriorityRequestMessage.java | 12 +- .../GenTaskPriorityResponseMessage.java | 10 +- .../session/PlayerUUIDMessage.java | 9 +- .../session/RemotePlayerConfigMessage.java | 10 +- .../plugin/CurrentLevelKeyMessage.java | 32 +++ .../messages/plugin/PluginCloseEvent.java | 11 + .../plugin/PluginMessageRegistry.java | 19 ++ .../plugin/ServerConnectInfoMessage.java | 32 +++ .../INettyConnection.java} | 14 +- .../NettyClient.java} | 24 +-- .../core/network/netty/NettyEventSource.java | 163 ++++++++++++++ .../NettyMessage.java} | 19 +- .../NettyServer.java} | 30 +-- .../TrackableNettyMessage.java} | 33 +-- .../network/plugin/PluginChannelHandler.java | 74 +++++++ .../network/plugin/PluginChannelMessage.java | 8 + .../core/network/protocol/INetworkObject.java | 20 +- .../core/network/protocol/MessageDecoder.java | 20 +- .../core/network/protocol/MessageEncoder.java | 17 +- .../network/protocol/MessageRegistry.java | 110 ---------- .../NettyChannelInitializer.java} | 20 +- .../NettyMessageHandler.java} | 17 +- .../NettyOutboundExceptionRouter.java} | 4 +- .../protocol/plugin/PluginMessageDecoder.java | 33 +++ .../protocol/plugin/PluginMessageEncoder.java | 22 ++ .../distanthorizons/core/util/RenderUtil.java | 2 +- .../core/world/DhClientWorld.java | 15 +- .../core/world/DhServerWorld.java | 19 +- .../misc/IPluginPacketSender.java | 11 + .../world/IClientLevelWrapper.java | 2 +- .../world/ILevelWrapper.java | 2 +- 57 files changed, 1011 insertions(+), 678 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/AbstractMessageRegistry.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/ICloseEvent.java rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{base => netty}/ILevelRelatedMessage.java (87%) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/NettyMessageRegistry.java rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => netty}/base/AckMessage.java (74%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => netty}/base/CancelMessage.java (81%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => netty}/base/CloseReasonMessage.java (69%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => netty}/base/ExceptionMessage.java (71%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => netty}/base/HelloMessage.java (82%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{base/CloseEvent.java => netty/base/NettyCloseEvent.java} (64%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => netty}/fullData/FullDataPartialUpdateMessage.java (91%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => netty}/fullData/FullDataSourceRequestMessage.java (80%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => netty}/fullData/FullDataSourceResponseMessage.java (91%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => netty}/fullData/generation/GenTaskPriorityRequestMessage.java (78%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => netty}/fullData/generation/GenTaskPriorityResponseMessage.java (78%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => netty}/session/PlayerUUIDMessage.java (79%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{ => netty}/session/RemotePlayerConfigMessage.java (82%) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/CurrentLevelKeyMessage.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginCloseEvent.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginMessageRegistry.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/ServerConnectInfoMessage.java rename core/src/main/java/com/seibel/distanthorizons/core/network/{IConnection.java => netty/INettyConnection.java} (76%) rename core/src/main/java/com/seibel/distanthorizons/core/network/{NetworkClient.java => netty/NettyClient.java} (86%) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyEventSource.java rename core/src/main/java/com/seibel/distanthorizons/core/network/{protocol/NetworkMessage.java => netty/NettyMessage.java} (72%) rename core/src/main/java/com/seibel/distanthorizons/core/network/{NetworkServer.java => netty/NettyServer.java} (83%) rename core/src/main/java/com/seibel/distanthorizons/core/network/{protocol/FutureTrackableNetworkMessage.java => netty/TrackableNettyMessage.java} (73%) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelHandler.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelMessage.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java rename core/src/main/java/com/seibel/distanthorizons/core/network/protocol/{NetworkChannelInitializer.java => netty/NettyChannelInitializer.java} (67%) rename core/src/main/java/com/seibel/distanthorizons/core/network/protocol/{MessageHandler.java => netty/NettyMessageHandler.java} (83%) rename core/src/main/java/com/seibel/distanthorizons/core/network/protocol/{NetworkOutboundExceptionRouter.java => netty/NettyOutboundExceptionRouter.java} (89%) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/protocol/plugin/PluginMessageDecoder.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/protocol/plugin/PluginMessageEncoder.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IPluginPacketSender.java diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java index 377805e01..e9cc8829a 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java @@ -26,10 +26,20 @@ package com.seibel.distanthorizons.coreapi; public final class ModInfo { public static final String ID = "distanthorizons"; - /** The internal protocol version used for networking. Incremented every time any packets are added, changed or removed. */ + + public static final String RESOURCE_NAMESPACE = "distant_horizons"; + public static final String DEDICATED_SERVER_INITIAL_PATH = "dedicated_server_initial"; + + // region Protocol versions + // Incremented every time any packets are added, changed or removed, with a few exceptions. + /** Netty protocol version. */ public static final int PROTOCOL_VERSION = 2; - /** The protocol version used for multiverse networking */ - public static final int MULTIVERSE_PLUGIN_PROTOCOL_VERSION = 1; + /** Plugin channel protocol version. */ + public static final int PLUGIN_PROTOCOL_VERSION = 1; + public static final String PLUGIN_CHANNEL_PATH = "plugin_channel"; + // endregion + + /** The internal mod name */ public static final String NAME = "DistantHorizons"; /** Human-readable version of NAME */ @@ -45,9 +55,6 @@ public final class ModInfo /** This version should be updated whenever non-breaking fixes are added to the DH API */ public static final int API_PATH_VERSION = 0; - public static final String NETWORKING_RESOURCE_NAMESPACE = "distant_horizons"; - public static final String MULTIVERSE_PLUGIN_NAMESPACE = "world_control"; - /** All DH owned threads should start with this string to allow for easier debugging and profiling. */ public static final String THREAD_NAME_PREFIX = "DH-"; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index b66f67faa..631784a29 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -20,44 +20,40 @@ package com.seibel.distanthorizons.core.api.internal; import com.seibel.distanthorizons.api.DhApi; +import com.seibel.distanthorizons.api.enums.rendering.EDebugRendering; import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass; -import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiFramebuffer; +import com.seibel.distanthorizons.api.enums.rendering.ERendererMode; import com.seibel.distanthorizons.api.methods.events.abstractEvents.*; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; -import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager; -import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel; -import com.seibel.distanthorizons.core.pos.DhChunkPos; -import com.seibel.distanthorizons.core.render.DhApiRenderProxy; -import com.seibel.distanthorizons.core.util.objects.Pair; -import com.seibel.distanthorizons.core.world.*; -import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; -import com.seibel.distanthorizons.core.level.IDhClientLevel; import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.coreapi.DependencyInjection.OverrideInjector; -import com.seibel.distanthorizons.coreapi.ModInfo; -import com.seibel.distanthorizons.api.enums.rendering.EDebugRendering; -import com.seibel.distanthorizons.api.enums.rendering.ERendererMode; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.level.IDhClientLevel; +import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.logging.ConfigBasedSpamLogger; import com.seibel.distanthorizons.core.logging.SpamReducedLogger; -import com.seibel.distanthorizons.coreapi.util.math.Mat4f; +import com.seibel.distanthorizons.core.pos.DhChunkPos; +import com.seibel.distanthorizons.core.render.DhApiRenderProxy; import com.seibel.distanthorizons.core.render.glObject.GLProxy; import com.seibel.distanthorizons.core.render.renderer.TestRenderer; import com.seibel.distanthorizons.core.util.RenderUtil; +import com.seibel.distanthorizons.core.util.objects.Pair; +import com.seibel.distanthorizons.core.world.AbstractDhWorld; +import com.seibel.distanthorizons.core.world.DhClientServerWorld; +import com.seibel.distanthorizons.core.world.DhClientWorld; +import com.seibel.distanthorizons.core.world.IDhClientWorld; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; +import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; +import com.seibel.distanthorizons.coreapi.ModInfo; +import com.seibel.distanthorizons.coreapi.util.math.Mat4f; import io.netty.buffer.ByteBuf; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; -import org.lwjgl.opengl.GL32; -import java.nio.charset.StandardCharsets; import java.util.HashMap; import java.util.HashSet; import java.util.concurrent.TimeUnit; @@ -77,8 +73,6 @@ public class ClientApi public static TestRenderer testRenderer = new TestRenderer(); private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); - private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); - private static final IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class); public static final long SPAM_LOGGER_FLUSH_NS = TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS); @@ -87,10 +81,8 @@ public class ClientApi private long lastFlushNanoTime = 0; - private boolean isServerCommunicationEnabled = false; + private ClientPluginChannelApi pluginChannelApi = new ClientPluginChannelApi(this::clientLevelLoadEvent, this::clientLevelUnloadEvent); - /** set to true if any unexpected responses are received from the server */ - private boolean serverNetworkingIsMalformed = false; /** Holds any levels that were loaded before the {@link ClientApi#onClientOnlyConnected} was fired. */ public final HashSet waitingClientLevels = new HashSet<>(); @@ -156,10 +148,8 @@ public class ClientApi } // clear the previous server's information - this.isServerCommunicationEnabled = false; - this.serverNetworkingIsMalformed = false; - KEYED_CLIENT_LEVEL_MANAGER.setUseOverrideWrapper(false); - KEYED_CLIENT_LEVEL_MANAGER.setServerKeyedLevel(null); + this.pluginChannelApi.close(); + this.pluginChannelApi = new ClientPluginChannelApi(this::clientLevelLoadEvent, this::clientLevelUnloadEvent); // remove any waiting items this.waitingChunkByClientLevelAndPos.clear(); @@ -172,17 +162,22 @@ public class ClientApi // level events // //==============// - public void clientLevelUnloadEvent(@Nullable IClientLevelWrapper level) + public void clientLevelUnloadEvent(IClientLevelWrapper level) + { + this.clientLevelUnloadEvent(level, false); + } + + public void clientLevelUnloadEvent(IClientLevelWrapper level, boolean respawn) { try { - if (level == null) - { - // can happen on certain multiverse servers - return; - } LOGGER.info("Unloading client level [" + level + "]."); + if (level instanceof IServerKeyedClientLevel && !respawn) + { + this.pluginChannelApi.onClientLevelUnload(); + } + AbstractDhWorld world = SharedApi.getAbstractDhWorld(); if (world != null) { @@ -201,26 +196,17 @@ public class ClientApi } } - public void clientLevelLoadEvent(@Nullable IClientLevelWrapper level) { this.clientLevelLoadEvent(level, false); } - public void multiverseClientLevelLoadEvent(@Nullable IClientLevelWrapper level) { this.clientLevelLoadEvent(level, true); } - private void clientLevelLoadEvent(@Nullable IClientLevelWrapper level, boolean isServerCommunication) + public void clientLevelLoadEvent(IClientLevelWrapper level) { try { - if (this.isServerCommunicationEnabled && !isServerCommunication) + if (!this.pluginChannelApi.allowLoadingLevel()) { - LOGGER.info("Server supports communication, deferring loading."); - return; - } - if (level == null) - { - // can happen on certain multiverse servers + LOGGER.info("Levels in this connection are managed by the server and loading is not allowed, ignoring auto-load."); return; } - - - LOGGER.info("Loading " + (isServerCommunication ? "Multiverse" : "") + " client level [" + level + "]."); + LOGGER.info("Loading client level [" + level + "]."); AbstractDhWorld world = SharedApi.getAbstractDhWorld(); if (world != null) @@ -241,6 +227,7 @@ public class ClientApi LOGGER.error("Unexpected error in ClientApi.clientLevelLoadEvent(), error: "+e.getMessage(), e); } } + private void loadWaitingChunksForLevel(IClientLevelWrapper level) { HashSet> keysToRemove = new HashSet<>(); @@ -333,122 +320,10 @@ public class ClientApi //============// // networking // //============// - - public void serverMessageReceived(ByteBuf byteBuf) + + public void pluginMessageReceived(ByteBuf byteBuf) { - if (!Config.Client.Advanced.Multiplayer.enableMultiverseNetworking.get()) - { - // multiverse networking disabled, ignore anything sent from the server - return; - } - - - - // either value can be set to true to debug the received byte stream - boolean stopAndDisplayInputAsByteArray = false; - boolean stopAndDisplayInputAsString = false; - if (stopAndDisplayInputAsByteArray || stopAndDisplayInputAsString) - { - String messageString = ""; - if (stopAndDisplayInputAsByteArray) - { - int byteCount = byteBuf.readableBytes(); - byte[] arr = new byte[byteCount]; - StringBuilder stringBuilder = new StringBuilder("Server message received: ["); - for (int i = 0; i < byteCount; i++) - { - arr[i] = byteBuf.readByte(); - stringBuilder.append(arr[i]); - } - stringBuilder.append("]"); - - messageString = stringBuilder.toString(); - } - else if (stopAndDisplayInputAsString) - { - messageString = byteBuf.toString(StandardCharsets.UTF_8); - } - - // this is logged as an error so it is easier to see in an Intellij log - LOGGER.error(messageString); - return; - } - - - - - // It is important to ensure malicious server input is ignored. - if (this.serverNetworkingIsMalformed) - { - return; - } - - // check that the incoming message is within the expected size - short commandLength = byteBuf.readShort(); - if (commandLength < 1 || commandLength > 32) - { - LOGGER.error("Server command length [" + commandLength + "] outside the expected range of 1 to 32 (inclusive)."); - ClientApi.INSTANCE.serverNetworkingIsMalformed = true; - return; - } - - // parse the command - String eventType; - try - { - eventType = byteBuf.readCharSequence(commandLength, StandardCharsets.UTF_8).toString(); - } - catch (Exception e) - { - LOGGER.error("Server sent un-parsable command. Error: " + e.getMessage()); - return; - } - - switch (eventType) - { - case "ServerCommsEnabled": - LOGGER.info("Server supports DH multiverse protocol."); - ClientApi.INSTANCE.isServerCommunicationEnabled = true; - KEYED_CLIENT_LEVEL_MANAGER.setUseOverrideWrapper(true); - MC.executeOnRenderThread(() -> - { - // Unload the current world, since it may be wrong. - // A followup WorldChanged event should be received from the server soon after this. - LOGGER.info("Unloading current client level so the server can define the correct multiverse level."); - this.clientLevelUnloadEvent(MC.getWrappedClientLevel()); - }); - break; - - case "LevelChanged": - short levelKeyLength = byteBuf.readShort(); - if (levelKeyLength < 1 || levelKeyLength > 128) // TODO 128 should be put into a constant somewhere - { - LOGGER.error("Server [LevelChanged] command length [" + commandLength + "] outside the expected range of 1 to 128 (inclusive)."); - this.serverNetworkingIsMalformed = true; - return; - } - - String levelKey = byteBuf.readCharSequence(levelKeyLength, StandardCharsets.UTF_8).toString(); - if (!levelKey.matches("[a-zA-Z0-9_]+")) - { - LOGGER.error("Server sent invalid world key name, and is being ignored."); - this.isServerCommunicationEnabled = false; - this.serverNetworkingIsMalformed = true; - return; - } - - LOGGER.info("Server level change event received, changing the level to [" + levelKey + "]."); - MC.executeOnRenderThread(() -> { - if (MC.getWrappedClientLevel() != null) - { - this.clientLevelUnloadEvent(MC.getWrappedClientLevel()); - } - IServerKeyedClientLevel clientLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel(MC.getWrappedClientLevel(), levelKey); - KEYED_CLIENT_LEVEL_MANAGER.setServerKeyedLevel(clientLevel); - this.multiverseClientLevelLoadEvent(clientLevel); - }); - break; - } + this.pluginChannelApi.handlePacket(byteBuf); } @@ -535,7 +410,12 @@ public class ClientApi { return; } - IDhClientLevel level = dhClientWorld.getOrLoadClientLevel(levelWrapper); + + IDhClientLevel level = (IDhClientLevel) dhClientWorld.getLevel(levelWrapper); + if (level == null) + { + return; + } if (this.rendererDisabledBecauseOfExceptions) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java new file mode 100644 index 000000000..ea07c609f --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -0,0 +1,98 @@ +package com.seibel.distanthorizons.core.api.internal; + +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager; +import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel; +import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; +import com.seibel.distanthorizons.core.network.messages.plugin.PluginCloseEvent; +import com.seibel.distanthorizons.core.network.messages.plugin.CurrentLevelKeyMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.ServerConnectInfoMessage; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelHandler; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; +import io.netty.buffer.ByteBuf; +import org.apache.logging.log4j.LogManager; + +import java.util.function.Consumer; + +public class ClientPluginChannelApi implements AutoCloseable +{ + private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), + () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); + private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); + private static final IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class); + + private final PluginChannelHandler channelHandler = new PluginChannelHandler(); + + private final Consumer levelUnloadHandler; + private final Consumer multiverseLevelLoadHandler; + + + public boolean allowLoadingLevel() + { + return (KEYED_CLIENT_LEVEL_MANAGER.isEnabled() && KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel() != null) + || !KEYED_CLIENT_LEVEL_MANAGER.isEnabled(); + } + + + public ClientPluginChannelApi(Consumer levelLoadHandler, Consumer levelUnloadHandler) + { + this.levelUnloadHandler = levelUnloadHandler; + this.multiverseLevelLoadHandler = levelLoadHandler; + + this.channelHandler.registerHandler(CurrentLevelKeyMessage.class, this::onCurrentLevelKeyMessage); + this.channelHandler.registerHandler(ServerConnectInfoMessage.class, this::onServerConnectInfoMessage); + this.channelHandler.registerHandler(PluginCloseEvent.class, this::onClose); + } + + private void onCurrentLevelKeyMessage(CurrentLevelKeyMessage msg) + { + if (!msg.levelKey.matches("[a-zA-Z0-9_]+")) + { + throw new IllegalArgumentException("Server sent invalid world key name."); + } + + LOGGER.info("Server level change event received, changing the level to [" + msg.levelKey + "]."); + + MC.executeOnRenderThread(() -> { + IClientLevelWrapper clientLevel = MC.getWrappedClientLevel(); + + if (clientLevel != null) + { + this.levelUnloadHandler.accept(clientLevel); + } + + IServerKeyedClientLevel keyedLevel = KEYED_CLIENT_LEVEL_MANAGER.setServerKeyedLevel(clientLevel, msg.levelKey); + this.multiverseLevelLoadHandler.accept(keyedLevel); + }); + } + + private void onServerConnectInfoMessage(ServerConnectInfoMessage msg) + { + // TODO + } + + public void onClientLevelUnload() + { + KEYED_CLIENT_LEVEL_MANAGER.clearServerKeyedLevel(); + } + + public void onClose(PluginCloseEvent event) + { + this.onClientLevelUnload(); + } + + public void handlePacket(ByteBuf buffer) + { + this.channelHandler.decodeAndHandle(buffer); + } + + @Override + public void close() + { + KEYED_CLIENT_LEVEL_MANAGER.disable(); + this.channelHandler.close(); + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index c98ff2197..072faa46b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -10,8 +10,8 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.multiplayer.client.AbstractFullDataRequestQueue; import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.GenTaskPriorityRequestMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.GenTaskPriorityResponseMessage; +import com.seibel.distanthorizons.core.network.messages.netty.fullData.generation.GenTaskPriorityRequestMessage; +import com.seibel.distanthorizons.core.network.messages.netty.fullData.generation.GenTaskPriorityResponseMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index f468076b7..b2eecf55f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -31,9 +31,10 @@ import com.seibel.distanthorizons.core.generation.WorldRemoteGenerationQueue; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; import com.seibel.distanthorizons.core.multiplayer.client.FullDataRefreshQueue; -import com.seibel.distanthorizons.core.network.NetworkClient; +import com.seibel.distanthorizons.core.network.netty.NettyClient; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; -import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage; +import com.seibel.distanthorizons.core.network.messages.netty.fullData.FullDataPartialUpdateMessage; +import com.seibel.distanthorizons.core.network.netty.NettyMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhSectionPos; @@ -73,7 +74,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel @CheckForNull private final ClientNetworkState networkState; @Nullable - private final ScopedNetworkEventSource eventSource; + private final ScopedNetworkEventSource eventSource; public final WorldGenModule worldGenModule; public final AppliedConfigState worldGeneratorEnabledConfig; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 7e05f9d89..c484211f8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -29,14 +29,15 @@ import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState; import com.seibel.distanthorizons.core.multiplayer.server.RemotePlayerConnectionHandler; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; -import com.seibel.distanthorizons.core.network.NetworkServer; +import com.seibel.distanthorizons.core.network.netty.NettyServer; import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; -import com.seibel.distanthorizons.core.network.messages.base.CancelMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceResponseMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.GenTaskPriorityRequestMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.GenTaskPriorityResponseMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage; +import com.seibel.distanthorizons.core.network.messages.netty.base.CancelMessage; +import com.seibel.distanthorizons.core.network.messages.netty.fullData.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.netty.fullData.FullDataSourceResponseMessage; +import com.seibel.distanthorizons.core.network.messages.netty.fullData.generation.GenTaskPriorityRequestMessage; +import com.seibel.distanthorizons.core.network.messages.netty.fullData.generation.GenTaskPriorityResponseMessage; +import com.seibel.distanthorizons.core.network.messages.netty.fullData.FullDataPartialUpdateMessage; +import com.seibel.distanthorizons.core.network.netty.NettyMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; @@ -61,7 +62,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel private final IServerLevelWrapper serverLevelWrapper; private final RemotePlayerConnectionHandler remotePlayerConnectionHandler; - private final ScopedNetworkEventSource eventSource; + private final ScopedNetworkEventSource eventSource; private final ConcurrentLinkedQueue worldGenLoopingQueue = new ConcurrentLinkedQueue<>(); private final ConcurrentMap incompleteDataSources = new ConcurrentHashMap<>(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/IKeyedClientLevelManager.java b/core/src/main/java/com/seibel/distanthorizons/core/level/IKeyedClientLevelManager.java index ac8654c82..000dee20f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/IKeyedClientLevelManager.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/IKeyedClientLevelManager.java @@ -19,7 +19,7 @@ package com.seibel.distanthorizons.core.level; -import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable; /** @@ -28,15 +28,12 @@ import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindab */ public interface IKeyedClientLevelManager extends IBindable { + IServerKeyedClientLevel getServerKeyedLevel(); /** Called when a client level is wrapped by a ServerEnhancedClientLevel, for integration into mod internals. */ - void setServerKeyedLevel(IServerKeyedClientLevel clientLevel); - IServerKeyedClientLevel getOverrideWrapper(); + IServerKeyedClientLevel setServerKeyedLevel(IClientLevelWrapper clientLevel, String levelKey); + void clearServerKeyedLevel(); - /** Returns a new instance of a ServerEnhancedClientLevel. */ - IServerKeyedClientLevel getServerKeyedLevel(ILevelWrapper level, String serverLevelKey); - - /** Sets the LOD engine to use the override wrapper, if the server has communication enabled. */ - void setUseOverrideWrapper(boolean useOverrideWrapper); - boolean getUseOverrideWrapper(); + boolean isEnabled(); + void disable(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index a0c7e1b55..726f47673 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -12,8 +12,8 @@ import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; -import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceResponseMessage; +import com.seibel.distanthorizons.core.network.messages.netty.fullData.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.netty.fullData.FullDataSourceResponseMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index 34ba3eea3..71ec533b2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -5,13 +5,13 @@ import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; -import com.seibel.distanthorizons.core.network.NetworkClient; +import com.seibel.distanthorizons.core.network.netty.NettyClient; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; -import com.seibel.distanthorizons.core.network.messages.base.AckMessage; -import com.seibel.distanthorizons.core.network.messages.base.CloseEvent; -import com.seibel.distanthorizons.core.network.messages.base.HelloMessage; -import com.seibel.distanthorizons.core.network.messages.session.PlayerUUIDMessage; -import com.seibel.distanthorizons.core.network.messages.session.RemotePlayerConfigMessage; +import com.seibel.distanthorizons.core.network.messages.netty.base.AckMessage; +import com.seibel.distanthorizons.core.network.messages.netty.base.NettyCloseEvent; +import com.seibel.distanthorizons.core.network.messages.netty.base.HelloMessage; +import com.seibel.distanthorizons.core.network.messages.netty.session.PlayerUUIDMessage; +import com.seibel.distanthorizons.core.network.messages.netty.session.RemotePlayerConfigMessage; import org.apache.logging.log4j.LogManager; import java.io.Closeable; @@ -23,7 +23,7 @@ public class ClientNetworkState implements Closeable protected static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); - private final NetworkClient client; + private final NettyClient client; private final UUID playerUUID; public MultiplayerConfig config = new MultiplayerConfig(); @@ -37,17 +37,17 @@ public class ClientNetworkState implements Closeable * Returns the client used by this instance.

* If you need to subscribe to any packet events, create an instance of {@link ScopedNetworkEventSource} using the returned instance. */ - public NetworkClient getClient() { return this.client; } + public NettyClient getClient() { return this.client; } /** * Constructs a new instance. * - * @param networkClient Client to use. It is assumed that this client will be at full control by this instance. + * @param nettyClient Client to use. It is assumed that this client will be at full control by this instance. * @param playerUUID UUID of a player connected. */ - public ClientNetworkState(NetworkClient networkClient, UUID playerUUID) + public ClientNetworkState(NettyClient nettyClient, UUID playerUUID) { - this.client = networkClient; + this.client = nettyClient; this.playerUUID = playerUUID; this.registerNetworkHandlers(); @@ -75,7 +75,7 @@ public class ClientNetworkState implements Closeable this.configReceived = true; }); - this.client.registerHandler(CloseEvent.class, msg -> + this.client.registerHandler(NettyCloseEvent.class, msg -> { this.configReceived = false; }); @@ -93,7 +93,7 @@ public class ClientNetworkState implements Closeable return new String[]{ this.client.getRemoteAddress() != null ? "Connected, ready: " + this.isReady() - : MessageFormat.format("Disconnected, attempts left: {0} / {1}", this.client.getReconnectionAttemptsLeft(), NetworkClient.RECONNECTION_ATTEMPTS) + : MessageFormat.format("Disconnected, attempts left: {0} / {1}", this.client.getReconnectionAttemptsLeft(), NettyClient.RECONNECTION_ATTEMPTS) }; } else diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java index 243d0abb8..026186162 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java @@ -8,17 +8,17 @@ import com.seibel.distanthorizons.core.config.types.ConfigEntry; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; -import com.seibel.distanthorizons.core.network.IConnection; -import com.seibel.distanthorizons.core.network.NetworkServer; +import com.seibel.distanthorizons.core.network.netty.INettyConnection; +import com.seibel.distanthorizons.core.network.netty.NettyServer; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; -import com.seibel.distanthorizons.core.network.messages.base.AckMessage; -import com.seibel.distanthorizons.core.network.messages.base.CloseEvent; -import com.seibel.distanthorizons.core.network.messages.base.ILevelRelatedMessage; -import com.seibel.distanthorizons.core.network.messages.session.PlayerUUIDMessage; -import com.seibel.distanthorizons.core.network.messages.session.RemotePlayerConfigMessage; -import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; -import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; +import com.seibel.distanthorizons.core.network.messages.netty.base.AckMessage; +import com.seibel.distanthorizons.core.network.messages.netty.base.NettyCloseEvent; +import com.seibel.distanthorizons.core.network.messages.netty.ILevelRelatedMessage; +import com.seibel.distanthorizons.core.network.messages.netty.session.PlayerUUIDMessage; +import com.seibel.distanthorizons.core.network.messages.netty.session.RemotePlayerConfigMessage; +import com.seibel.distanthorizons.core.network.netty.TrackableNettyMessage; +import com.seibel.distanthorizons.core.network.netty.NettyMessage; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import org.jetbrains.annotations.Nullable; @@ -34,17 +34,17 @@ public class RemotePlayerConnectionHandler implements Closeable { private static final ConfigEntry GENERATE_MULTIPLE_DIMENSIONS_CONFIG = Config.Client.Advanced.Multiplayer.ServerNetworking.generateMultipleDimensions; - private final ScopedNetworkEventSource eventSource; + private final ScopedNetworkEventSource eventSource; private final ConcurrentHashMap playersByUUID = new ConcurrentHashMap<>(); - private final BiMap playersByConnection = Maps.synchronizedBiMap(HashBiMap.create()); + private final BiMap playersByConnection = Maps.synchronizedBiMap(HashBiMap.create()); - private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::onConfigChanged); + private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::onConfigChanged); - public NetworkServer server() { return this.eventSource.parent; } + public NettyServer server() { return this.eventSource.parent; } - public RemotePlayerConnectionHandler(NetworkServer networkServer) + public RemotePlayerConnectionHandler(NettyServer nettyServer) { - this.eventSource = new ScopedNetworkEventSource<>(networkServer); + this.eventSource = new ScopedNetworkEventSource<>(nettyServer); this.registerNetworkHandlers(); } @@ -52,7 +52,7 @@ public class RemotePlayerConnectionHandler implements Closeable { this.eventSource.registerHandler(PlayerUUIDMessage.class, playerUUIDMessage -> { - IConnection connection = playerUUIDMessage.getConnection(); + INettyConnection connection = playerUUIDMessage.getConnection(); ServerPlayerState serverPlayerState = this.playersByUUID.get(playerUUIDMessage.playerUUID); if (serverPlayerState == null) @@ -79,7 +79,7 @@ public class RemotePlayerConnectionHandler implements Closeable serverPlayerState.connection.sendMessage(new RemotePlayerConfigMessage(serverPlayerState.config)); })); - this.eventSource.registerHandler(CloseEvent.class, closeEvent -> + this.eventSource.registerHandler(NettyCloseEvent.class, closeEvent -> { ServerPlayerState dhPlayer = this.playersByConnection.remove(closeEvent.getConnection()); if (dhPlayer != null) @@ -97,7 +97,7 @@ public class RemotePlayerConnectionHandler implements Closeable } } - public Consumer connectedPlayersOnly(BiConsumer next) + public Consumer connectedPlayersOnly(BiConsumer next) { return msg -> { @@ -109,7 +109,7 @@ public class RemotePlayerConnectionHandler implements Closeable }; } - public Consumer currentLevelOnly(DhServerLevel level, BiConsumer next) + public Consumer currentLevelOnly(DhServerLevel level, BiConsumer next) { return this.connectedPlayersOnly((msg, serverPlayerState) -> { @@ -126,9 +126,9 @@ public class RemotePlayerConnectionHandler implements Closeable && !GENERATE_MULTIPLE_DIMENSIONS_CONFIG.get()) { // If the message can be replied to - reply with error, otherwise just ignore - if (msg instanceof FutureTrackableNetworkMessage) + if (msg instanceof TrackableNettyMessage) { - ((FutureTrackableNetworkMessage) msg).sendResponse(new InvalidLevelException("Invalid level")); + ((TrackableNettyMessage) msg).sendResponse(new InvalidLevelException("Invalid level")); } return; @@ -147,7 +147,7 @@ public class RemotePlayerConnectionHandler implements Closeable } @Nullable - public ServerPlayerState getConnectedPlayer(NetworkMessage msg) + public ServerPlayerState getConnectedPlayer(NettyMessage msg) { return this.playersByConnection.get(msg.getConnection()); } @@ -160,7 +160,7 @@ public class RemotePlayerConnectionHandler implements Closeable public void unregisterLeftPlayer(IServerPlayerWrapper serverPlayer) { ServerPlayerState dhPlayer = this.playersByUUID.remove(serverPlayer.getUUID()); - IConnection connection = this.playersByConnection.inverse().remove(dhPlayer); + INettyConnection connection = this.playersByConnection.inverse().remove(dhPlayer); if (connection != null) { connection.disconnect("You have logged out."); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index 6ef80d445..bab53bdb6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -1,10 +1,10 @@ package com.seibel.distanthorizons.core.multiplayer.server; import com.seibel.distanthorizons.core.level.DhServerLevel; -import com.seibel.distanthorizons.core.network.IConnection; +import com.seibel.distanthorizons.core.network.netty.INettyConnection; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; -import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.GenTaskPriorityRequestMessage; +import com.seibel.distanthorizons.core.network.messages.netty.fullData.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.netty.fullData.generation.GenTaskPriorityRequestMessage; import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateAndConcurrencyLimiter; import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateLimiter; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; @@ -17,7 +17,7 @@ import static com.seibel.distanthorizons.core.config.Config.Client.Advanced.Mult public class ServerPlayerState { public IServerPlayerWrapper serverPlayer; - public IConnection connection; + public INettyConnection connection; @NotNull public ConstrainedMultiplayerConfig config = new ConstrainedMultiplayerConfig(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index 8ef52c513..17152b5b0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -21,191 +21,87 @@ package com.seibel.distanthorizons.core.network; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.network.messages.base.CancelMessage; -import com.seibel.distanthorizons.core.network.messages.base.CloseEvent; -import com.seibel.distanthorizons.core.network.messages.base.ExceptionMessage; -import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; -import com.seibel.distanthorizons.core.network.protocol.MessageRegistry; -import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; +import com.seibel.distanthorizons.core.network.messages.AbstractMessageRegistry; +import com.seibel.distanthorizons.core.network.messages.ICloseEvent; +import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import com.seibel.distanthorizons.coreapi.ModInfo; -import io.netty.channel.ChannelException; import org.apache.logging.log4j.LogManager; -import java.io.InvalidClassException; import java.util.HashSet; import java.util.Set; -import java.util.concurrent.CancellationException; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.Consumer; -public abstract class NetworkEventSource +public abstract class NetworkEventSource { private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); - protected final ConcurrentMap, Set>> handlers = new ConcurrentHashMap<>(); - private final ConcurrentMap> pendingFutures = new ConcurrentHashMap<>(); + protected final ConcurrentMap, Set>> handlers = new ConcurrentHashMap<>(); - protected boolean hasHandler(Class handlerClass) + + protected final AbstractMessageRegistry messageRegistry; + public NetworkEventSource(AbstractMessageRegistry messageRegistry) { - return this.handlers.containsKey(handlerClass); + this.messageRegistry = messageRegistry; } - - protected void handleMessage(NetworkMessage message) + protected final void handleMessage(TMessage message) { boolean handled = false; - Set> handlerList = this.handlers.get(message.getClass()); + Set> handlerList = this.handlers.get(message.getClass()); if (handlerList != null) { - for (Consumer handler : handlerList) + for (Consumer handler : handlerList) { handled = true; handler.accept(message); } } - if (message instanceof FutureTrackableNetworkMessage) - { - FutureTrackableNetworkMessage trackableMessage = (FutureTrackableNetworkMessage)message; - ConcurrentMap subMap = this.pendingFutures.get(message.getConnection()); - if (subMap != null) - { - FutureResponseData responseData = subMap.get(trackableMessage.futureId); - if (responseData != null) - { - handled = true; - - if (message instanceof ExceptionMessage) - { - responseData.future.completeExceptionally(((ExceptionMessage) message).exception); - } - else if (message.getClass() != responseData.responseClass) - { - responseData.future.completeExceptionally(new InvalidClassException("Response with invalid type: expected " + responseData.responseClass.getSimpleName() + ", got:" + message)); - } - else - { - responseData.future.complete(trackableMessage); - } - } - } - } + handled |= this.tryHandleMessage(message); - if (!handled && ModInfo.IS_DEV_BUILD && message.warnWhenUnhandled()) + if (!handled && ModInfo.IS_DEV_BUILD) { LOGGER.warn("Unhandled message: " + message); } } - protected void addNewConnection(IConnection connection) + protected boolean tryHandleMessage(TMessage message) { - this.pendingFutures.put(connection, new ConcurrentHashMap<>()); + // By default, messages are handled only by their direct handlers. + return false; } - public void registerHandler(Class handlerClass, Consumer handlerImplementation) + public void registerHandler(Class handlerClass, Consumer handlerImplementation) { //noinspection unchecked this.handlers.computeIfAbsent(handlerClass, missingHandlerClass -> { - // Will throw if the handler class is not found - if (handlerClass != CloseEvent.class) + // Will throw if the handler class is not found and not a CloseEvent + if (!ICloseEvent.class.isAssignableFrom(missingHandlerClass)) { - MessageRegistry.INSTANCE.getMessageId(handlerClass); + this.messageRegistry.getMessageId(handlerClass); } - return new HashSet<>(); + return ConcurrentHashMap.newKeySet(); }) - .add((Consumer) handlerImplementation); + .add((Consumer) handlerImplementation); } - protected void removeHandler(Class handlerClass, Consumer handlerImplementation) + protected boolean hasHandler(Class handlerClass) + { + return this.handlers.containsKey(handlerClass); + } + + protected void removeHandler(Class handlerClass, Consumer handlerImplementation) { this.handlers.computeIfAbsent(handlerClass, missingHandlerClass -> new HashSet<>()) .remove(handlerImplementation); } - - protected CompletableFuture createRequest(IConnection connection, FutureTrackableNetworkMessage msg, Class responseClass) - { - msg.setConnection(connection); - - CompletableFuture responseFuture = new CompletableFuture<>(); - responseFuture.whenComplete((response, throwable) -> - { - if (!(throwable instanceof ChannelException)) - { - ConcurrentMap subMap = this.pendingFutures.get(connection); - if (subMap != null) - { - subMap.remove(msg.futureId); - } - } - - if (throwable instanceof CancellationException) - { - msg.sendResponse(new CancelMessage()); - } - }); - - ConcurrentMap subMap = this.pendingFutures.get(connection); - if (subMap == null) - { - // Was deleted before adding - responseFuture.completeExceptionally(connection.getCloseReason()); - return responseFuture; - } - subMap.put(msg.futureId, new FutureResponseData(responseClass, responseFuture)); - if (!this.pendingFutures.containsKey(connection)) - { - // Was deleted while adding - // Note: removal from subMap will happen in whenComplete above - responseFuture.completeExceptionally(connection.getCloseReason()); - return responseFuture; - } - // If passed until here, cancelling is up to the cleaning side - - return responseFuture; - } - - protected final void completeAllFuturesExceptionally(IConnection connection, Throwable cause) - { - ConcurrentMap map = this.pendingFutures.remove(connection); - if (map == null) - { - return; - } - - for (FutureResponseData responseData : map.values()) - { - responseData.future.completeExceptionally(cause); - } - } - - protected final void completeAllFuturesExceptionally(Throwable cause) - { - for (IConnection connection : this.pendingFutures.keySet()) - { - this.completeAllFuturesExceptionally(connection, cause); - } - } - public void close() { this.handlers.clear(); - this.completeAllFuturesExceptionally(new ChannelException(this.getClass().getSimpleName() + " is closed.")); - } - - private static class FutureResponseData - { - public final Class responseClass; - public final CompletableFuture future; - - private FutureResponseData(Class responseClass, CompletableFuture future) { - this.responseClass = responseClass; - //noinspection unchecked - this.future = (CompletableFuture) future; - } } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/ScopedNetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/ScopedNetworkEventSource.java index 2da4ab8fb..66874f172 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/ScopedNetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/ScopedNetworkEventSource.java @@ -19,29 +19,33 @@ package com.seibel.distanthorizons.core.network; -import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; +import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import java.util.function.Consumer; /** Provides a way to register network message handlers which are expected to be removed later. */ -public final class ScopedNetworkEventSource extends NetworkEventSource +public final class ScopedNetworkEventSource, TMessage extends INetworkObject> extends NetworkEventSource { public final TParent parent; private boolean isClosed = false; public ScopedNetworkEventSource(TParent parent) { + super(parent.messageRegistry); this.parent = parent; } @Override - public void registerHandler(Class handlerClass, Consumer handlerImplementation) + public void registerHandler(Class handlerClass, Consumer handlerImplementation) { - if (isClosed) return; + if (this.isClosed) + { + return; + } if (!this.hasHandler(handlerClass)) { - parent.registerHandler(handlerClass, this::handleMessage); + this.parent.registerHandler(handlerClass, this::handleMessage); } super.registerHandler(handlerClass, handlerImplementation); @@ -50,10 +54,10 @@ public final class ScopedNetworkEventSource @Override public void close() { - isClosed = true; - for (Class handlerClass : this.handlers.keySet()) + this.isClosed = true; + for (Class handlerClass : this.handlers.keySet()) { - parent.removeHandler(handlerClass, this::handleMessage); + this.parent.removeHandler(handlerClass, this::handleMessage); } } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/AbstractMessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/AbstractMessageRegistry.java new file mode 100644 index 000000000..3cf458a56 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/AbstractMessageRegistry.java @@ -0,0 +1,75 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.network.messages; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.seibel.distanthorizons.core.network.protocol.INetworkObject; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +public abstract class AbstractMessageRegistry +{ + private final Map> idToSupplier = new HashMap<>(); + private final BiMap, Integer> classToId = HashBiMap.create(); + + + + protected void registerMessage(Class clazz, Supplier supplier) + { + int id = this.idToSupplier.size() + 1; + this.idToSupplier.put(id, supplier); + this.classToId.put(clazz, id); + } + + public TMessage createMessage(int messageId) throws IllegalArgumentException + { + try + { + return this.idToSupplier.get(messageId).get(); + } + catch (NullPointerException e) + { + throw new IllegalArgumentException("Invalid message ID: " + messageId); + } + } + + @SuppressWarnings("unchecked") + public int getMessageId(TMessage message) + { + return this.getMessageId((Class) message.getClass()); + } + + public int getMessageId(Class messageClass) + { + try + { + return this.classToId.get(messageClass); + } + catch (NullPointerException e) + { + throw new IllegalArgumentException("Message does not have ID assigned to it: " + messageClass.getSimpleName()); + } + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ICloseEvent.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ICloseEvent.java new file mode 100644 index 000000000..208693a9b --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ICloseEvent.java @@ -0,0 +1,14 @@ +package com.seibel.distanthorizons.core.network.messages; + +import com.seibel.distanthorizons.core.network.protocol.INetworkObject; +import io.netty.buffer.ByteBuf; + +public interface ICloseEvent extends INetworkObject +{ + @Override + default void encode(ByteBuf out) { throw new UnsupportedOperationException(this.getClass().getSimpleName() + " is not a real message, and cannot be sent."); } + + @Override + default void decode(ByteBuf in) { throw new UnsupportedOperationException(this.getClass().getSimpleName() + " is not a real message, and cannot be received."); } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ILevelRelatedMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/ILevelRelatedMessage.java similarity index 87% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ILevelRelatedMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/ILevelRelatedMessage.java index 8cd0bab67..13c6fc4c1 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ILevelRelatedMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/ILevelRelatedMessage.java @@ -1,4 +1,4 @@ -package com.seibel.distanthorizons.core.network.messages.base; +package com.seibel.distanthorizons.core.network.messages.netty; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/NettyMessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/NettyMessageRegistry.java new file mode 100644 index 000000000..2a433e01c --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/NettyMessageRegistry.java @@ -0,0 +1,46 @@ +package com.seibel.distanthorizons.core.network.messages.netty; + +import com.seibel.distanthorizons.core.network.messages.AbstractMessageRegistry; +import com.seibel.distanthorizons.core.network.messages.netty.base.*; +import com.seibel.distanthorizons.core.network.messages.netty.fullData.FullDataPartialUpdateMessage; +import com.seibel.distanthorizons.core.network.messages.netty.fullData.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.netty.fullData.FullDataSourceResponseMessage; +import com.seibel.distanthorizons.core.network.messages.netty.fullData.generation.GenTaskPriorityRequestMessage; +import com.seibel.distanthorizons.core.network.messages.netty.fullData.generation.GenTaskPriorityResponseMessage; +import com.seibel.distanthorizons.core.network.messages.netty.session.PlayerUUIDMessage; +import com.seibel.distanthorizons.core.network.messages.netty.session.RemotePlayerConfigMessage; +import com.seibel.distanthorizons.core.network.netty.NettyMessage; + +public class NettyMessageRegistry extends AbstractMessageRegistry +{ + public static final NettyMessageRegistry INSTANCE = new NettyMessageRegistry(); + + private NettyMessageRegistry() + { + // Note: Messages must have parameterless constructors + + // Opening & closing connection + // These messages should be compatible with any previous protocol versions + this.registerMessage(HelloMessage.class, HelloMessage::new); + this.registerMessage(CloseReasonMessage.class, CloseReasonMessage::new); + + // Core + this.registerMessage(AckMessage.class, AckMessage::new); + this.registerMessage(CancelMessage.class, CancelMessage::new); + this.registerMessage(ExceptionMessage.class, ExceptionMessage::new); + + // ID & config + this.registerMessage(PlayerUUIDMessage.class, PlayerUUIDMessage::new); + this.registerMessage(RemotePlayerConfigMessage.class, RemotePlayerConfigMessage::new); + + // Full data requests & updates + this.registerMessage(FullDataSourceRequestMessage.class, FullDataSourceRequestMessage::new); + this.registerMessage(FullDataSourceResponseMessage.class, FullDataSourceResponseMessage::new); + this.registerMessage(FullDataPartialUpdateMessage.class, FullDataPartialUpdateMessage::new); + + // Generation task prioritization + this.registerMessage(GenTaskPriorityRequestMessage.class, GenTaskPriorityRequestMessage::new); + this.registerMessage(GenTaskPriorityResponseMessage.class, GenTaskPriorityResponseMessage::new); + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/AckMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/AckMessage.java similarity index 74% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/AckMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/AckMessage.java index 865e0999b..6f9ab1079 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/AckMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/AckMessage.java @@ -17,18 +17,16 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.base; +package com.seibel.distanthorizons.core.network.messages.netty.base; -import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; -import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; -import com.seibel.distanthorizons.core.network.protocol.MessageRegistry; +import com.seibel.distanthorizons.core.network.netty.TrackableNettyMessage; import io.netty.buffer.ByteBuf; /** * Simple empty response message. * This message is not sent automatically. */ -public class AckMessage extends FutureTrackableNetworkMessage +public class AckMessage extends TrackableNettyMessage { public AckMessage() { } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CancelMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/CancelMessage.java similarity index 81% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CancelMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/CancelMessage.java index 39e0a5ae8..80c237f76 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CancelMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/CancelMessage.java @@ -17,12 +17,12 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.base; +package com.seibel.distanthorizons.core.network.messages.netty.base; -import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.network.netty.TrackableNettyMessage; import io.netty.buffer.ByteBuf; -public class CancelMessage extends FutureTrackableNetworkMessage +public class CancelMessage extends TrackableNettyMessage { public CancelMessage() { } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CloseReasonMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/CloseReasonMessage.java similarity index 69% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CloseReasonMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/CloseReasonMessage.java index 659af753a..593e6cc00 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CloseReasonMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/CloseReasonMessage.java @@ -17,13 +17,12 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.base; +package com.seibel.distanthorizons.core.network.messages.netty.base; -import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; -import com.seibel.distanthorizons.core.network.protocol.INetworkObject; +import com.seibel.distanthorizons.core.network.netty.NettyMessage; import io.netty.buffer.ByteBuf; -public class CloseReasonMessage extends NetworkMessage +public class CloseReasonMessage extends NettyMessage { public String reason; @@ -31,14 +30,17 @@ public class CloseReasonMessage extends NetworkMessage public CloseReasonMessage(String reason) { this.reason = reason; } @Override - public void encode(ByteBuf out) { encodeString(this.reason, out); } + public void encode(ByteBuf out) + { + this.writeString(this.reason, out); + } @Override - public void decode(ByteBuf in) { this.reason = decodeString(in); } + public void decode(ByteBuf in) { this.reason = this.readString(in); } @Override public String toString() { - return super.toString("reason='" + reason + '\''); + return super.toString("reason='" + this.reason + '\''); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ExceptionMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/ExceptionMessage.java similarity index 71% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ExceptionMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/ExceptionMessage.java index 25f7b6ccd..26a56f103 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/ExceptionMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/ExceptionMessage.java @@ -17,27 +17,27 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.base; +package com.seibel.distanthorizons.core.network.messages.netty.base; import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; import com.seibel.distanthorizons.core.network.exceptions.InvalidSectionPosException; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; -import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.network.netty.TrackableNettyMessage; import io.netty.buffer.ByteBuf; import java.util.ArrayList; import java.util.List; -public class ExceptionMessage extends FutureTrackableNetworkMessage +public class ExceptionMessage extends TrackableNettyMessage { private static final List> exceptionMap = new ArrayList>() {{ // All exceptions here must include constructor: (String) - add(RateLimitedException.class); - add(InvalidLevelException.class); - add(InvalidSectionPosException.class); - add(RequestRejectedException.class); + this.add(RateLimitedException.class); + this.add(InvalidLevelException.class); + this.add(InvalidSectionPosException.class); + this.add(RequestRejectedException.class); }}; public Exception exception; @@ -50,20 +50,20 @@ public class ExceptionMessage extends FutureTrackableNetworkMessage @Override protected void encode0(ByteBuf out) { - out.writeInt(exceptionMap.indexOf(exception.getClass())); - encodeString(exception.getMessage(), out); + out.writeInt(exceptionMap.indexOf(this.exception.getClass())); + this.writeString(this.exception.getMessage(), out); } @Override protected void decode0(ByteBuf in) throws Exception { int id = in.readInt(); - String message = decodeString(in); - exception = exceptionMap.get(id).getDeclaredConstructor(String.class).newInstance(message); + String message = this.readString(in); + this.exception = exceptionMap.get(id).getDeclaredConstructor(String.class).newInstance(message); } @Override public String toString() { - return super.toString("exception=" + exception); + return super.toString("exception=" + this.exception); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/HelloMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/HelloMessage.java similarity index 82% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/HelloMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/HelloMessage.java index 07c2f8869..e1c60e105 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/HelloMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/HelloMessage.java @@ -17,13 +17,13 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.base; +package com.seibel.distanthorizons.core.network.messages.netty.base; -import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; +import com.seibel.distanthorizons.core.network.netty.NettyMessage; import com.seibel.distanthorizons.coreapi.ModInfo; import io.netty.buffer.ByteBuf; -public class HelloMessage extends NetworkMessage +public class HelloMessage extends NettyMessage { public int version = ModInfo.PROTOCOL_VERSION; @@ -37,7 +37,7 @@ public class HelloMessage extends NetworkMessage @Override public String toString() { - return super.toString("version=" + version); + return super.toString("version=" + this.version); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CloseEvent.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/NettyCloseEvent.java similarity index 64% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CloseEvent.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/NettyCloseEvent.java index fdc81dc1a..34a9c3936 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CloseEvent.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/NettyCloseEvent.java @@ -17,22 +17,16 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.base; +package com.seibel.distanthorizons.core.network.messages.netty.base; -import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; -import io.netty.buffer.ByteBuf; +import com.seibel.distanthorizons.core.network.messages.ICloseEvent; +import com.seibel.distanthorizons.core.network.netty.NettyMessage; /** * This is not a "real" message, and only used to indicate a disconnection. * To send a "disconnect reason" message, use {@link CloseReasonMessage}. */ -public class CloseEvent extends NetworkMessage +public class NettyCloseEvent extends NettyMessage implements ICloseEvent { - @Override - public void encode(ByteBuf out) { throw new UnsupportedOperationException("CloseMessage is not a real message, and must not be sent."); } - - @Override - public void decode(ByteBuf in) { throw new UnsupportedOperationException("CloseMessage is not a real message, and must not be received."); } - } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/FullDataPartialUpdateMessage.java similarity index 91% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/FullDataPartialUpdateMessage.java index 652aa0e86..d96491616 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/FullDataPartialUpdateMessage.java @@ -17,13 +17,13 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.fullData; +package com.seibel.distanthorizons.core.network.messages.netty.fullData; import com.seibel.distanthorizons.core.dataObjects.fullData.accessor.ChunkSizedFullDataAccessor; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.level.IDhLevel; -import com.seibel.distanthorizons.core.network.messages.base.ILevelRelatedMessage; -import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; +import com.seibel.distanthorizons.core.network.messages.netty.ILevelRelatedMessage; +import com.seibel.distanthorizons.core.network.netty.NettyMessage; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream; @@ -34,7 +34,7 @@ import javax.annotation.Nullable; import java.io.ByteArrayOutputStream; import java.io.IOException; -public class FullDataPartialUpdateMessage extends NetworkMessage implements ILevelRelatedMessage +public class FullDataPartialUpdateMessage extends NettyMessage implements ILevelRelatedMessage { private ChunkSizedFullDataAccessor fullDataAccessor; private DhServerLevel level; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/FullDataSourceRequestMessage.java similarity index 80% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceRequestMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/FullDataSourceRequestMessage.java index 2e78c31ec..da97e35bf 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/FullDataSourceRequestMessage.java @@ -17,10 +17,10 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.fullData; +package com.seibel.distanthorizons.core.network.messages.netty.fullData; -import com.seibel.distanthorizons.core.network.messages.base.ILevelRelatedMessage; -import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.network.messages.netty.ILevelRelatedMessage; +import com.seibel.distanthorizons.core.network.netty.TrackableNettyMessage; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; @@ -28,7 +28,7 @@ import io.netty.buffer.ByteBuf; import javax.annotation.Nullable; -public class FullDataSourceRequestMessage extends FutureTrackableNetworkMessage implements ILevelRelatedMessage +public class FullDataSourceRequestMessage extends TrackableNettyMessage implements ILevelRelatedMessage { private int levelHashCode; @@ -55,7 +55,7 @@ public class FullDataSourceRequestMessage extends FutureTrackableNetworkMessage { out.writeInt(this.levelHashCode); this.sectionPos.encode(out); - if (this.encodeOptional(out, this.checksum)) + if (this.writeOptional(out, this.checksum)) { out.writeInt(this.checksum); } @@ -65,8 +65,8 @@ public class FullDataSourceRequestMessage extends FutureTrackableNetworkMessage public void decode0(ByteBuf in) { this.levelHashCode = in.readInt(); - this.sectionPos = INetworkObject.decodeStatic(DhSectionPos.zero(), in); - this.checksum = this.decodeOptional(in, in::readInt); + this.sectionPos = INetworkObject.readToObject(DhSectionPos.zero(), in); + this.checksum = this.readOptional(in, in::readInt); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/FullDataSourceResponseMessage.java similarity index 91% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/FullDataSourceResponseMessage.java index 283da878b..8204ea108 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/FullDataSourceResponseMessage.java @@ -17,14 +17,14 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.fullData; +package com.seibel.distanthorizons.core.network.messages.netty.fullData; import com.seibel.distanthorizons.core.dataObjects.fullData.loader.AbstractFullDataSourceLoader; import com.seibel.distanthorizons.core.dataObjects.fullData.loader.CompleteFullDataSourceLoader; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFullDataSource; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.level.IDhLevel; -import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.network.netty.TrackableNettyMessage; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream; @@ -40,7 +40,7 @@ import java.io.IOException; * or nothing if requested in updates-only mode and the data was not updated.
* Decoded full data source is not cached, since it's intended for a single use. */ -public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage +public class FullDataSourceResponseMessage extends TrackableNettyMessage { // Transmitted data @Nullable @@ -66,7 +66,7 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage @Override public void encode0(ByteBuf out) throws IOException { - if (this.encodeOptional(out, this.fullDataSource)) + if (this.writeOptional(out, this.fullDataSource)) { try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { @@ -84,7 +84,7 @@ public class FullDataSourceResponseMessage extends FutureTrackableNetworkMessage @Override public void decode0(ByteBuf in) { - this.dataBuffer = this.decodeOptional(in, () -> + this.dataBuffer = this.readOptional(in, () -> { byte dataVersion = in.readByte(); this.fullDataSourceLoader = (CompleteFullDataSourceLoader) AbstractFullDataSourceLoader.getLoader(CompleteFullDataSource.DATA_TYPE_NAME, dataVersion); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/GenTaskPriorityRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/generation/GenTaskPriorityRequestMessage.java similarity index 78% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/GenTaskPriorityRequestMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/generation/GenTaskPriorityRequestMessage.java index 4d5dfa414..f73fcad3d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/GenTaskPriorityRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/generation/GenTaskPriorityRequestMessage.java @@ -17,18 +17,18 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.fullData.generation; +package com.seibel.distanthorizons.core.network.messages.netty.fullData.generation; import com.seibel.distanthorizons.core.level.IDhLevel; -import com.seibel.distanthorizons.core.network.messages.base.ILevelRelatedMessage; -import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.network.messages.netty.ILevelRelatedMessage; +import com.seibel.distanthorizons.core.network.netty.TrackableNettyMessage; import com.seibel.distanthorizons.core.pos.DhSectionPos; import io.netty.buffer.ByteBuf; import java.util.ArrayList; import java.util.List; -public class GenTaskPriorityRequestMessage extends FutureTrackableNetworkMessage implements ILevelRelatedMessage +public class GenTaskPriorityRequestMessage extends TrackableNettyMessage implements ILevelRelatedMessage { public List posList = new ArrayList<>(); @@ -50,14 +50,14 @@ public class GenTaskPriorityRequestMessage extends FutureTrackableNetworkMessage protected void encode0(ByteBuf out) { out.writeInt(this.levelHashCode); - this.encodeCollection(out, this.posList); + this.writeCollection(out, this.posList); } @Override protected void decode0(ByteBuf in) { this.levelHashCode = in.readInt(); - this.decodeCollection(in, this.posList, DhSectionPos::zero); + this.readCollection(in, this.posList, DhSectionPos::zero); } @Override public String toString() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/GenTaskPriorityResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/generation/GenTaskPriorityResponseMessage.java similarity index 78% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/GenTaskPriorityResponseMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/generation/GenTaskPriorityResponseMessage.java index 5eaa2278d..45bb0955a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/generation/GenTaskPriorityResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/generation/GenTaskPriorityResponseMessage.java @@ -17,16 +17,16 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.fullData.generation; +package com.seibel.distanthorizons.core.network.messages.netty.fullData.generation; -import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; +import com.seibel.distanthorizons.core.network.netty.TrackableNettyMessage; import com.seibel.distanthorizons.core.pos.DhSectionPos; import io.netty.buffer.ByteBuf; import java.util.HashMap; import java.util.Map; -public class GenTaskPriorityResponseMessage extends FutureTrackableNetworkMessage +public class GenTaskPriorityResponseMessage extends TrackableNettyMessage { public Map posList = new HashMap<>(); @@ -39,13 +39,13 @@ public class GenTaskPriorityResponseMessage extends FutureTrackableNetworkMessag @Override protected void encode0(ByteBuf out) { - this.encodeCollection(out, this.posList.entrySet()); + this.writeCollection(out, this.posList.entrySet()); } @Override protected void decode0(ByteBuf in) { - this.decodeMap(in, this.posList, DhSectionPos::zero, () -> 0); + this.readMap(in, this.posList, DhSectionPos::zero, () -> 0); } @Override public String toString() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/session/PlayerUUIDMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/session/PlayerUUIDMessage.java similarity index 79% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/session/PlayerUUIDMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/session/PlayerUUIDMessage.java index 4064142d6..f90f24538 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/session/PlayerUUIDMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/session/PlayerUUIDMessage.java @@ -17,15 +17,14 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.session; +package com.seibel.distanthorizons.core.network.messages.netty.session; -import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; -import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; +import com.seibel.distanthorizons.core.network.netty.TrackableNettyMessage; import io.netty.buffer.ByteBuf; import java.util.UUID; -public class PlayerUUIDMessage extends FutureTrackableNetworkMessage +public class PlayerUUIDMessage extends TrackableNettyMessage { public UUID playerUUID; @@ -44,7 +43,7 @@ public class PlayerUUIDMessage extends FutureTrackableNetworkMessage @Override public String toString() { - return super.toString("playerUUID=" + playerUUID); + return super.toString("playerUUID=" + this.playerUUID); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/session/RemotePlayerConfigMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/session/RemotePlayerConfigMessage.java similarity index 82% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/session/RemotePlayerConfigMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/session/RemotePlayerConfigMessage.java index d4b2f7b55..93c4cdf32 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/session/RemotePlayerConfigMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/session/RemotePlayerConfigMessage.java @@ -17,15 +17,15 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.session; +package com.seibel.distanthorizons.core.network.messages.netty.session; import com.seibel.distanthorizons.core.multiplayer.config.AbstractMultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; -import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; +import com.seibel.distanthorizons.core.network.netty.NettyMessage; import io.netty.buffer.ByteBuf; -public class RemotePlayerConfigMessage extends NetworkMessage +public class RemotePlayerConfigMessage extends NettyMessage { public AbstractMultiplayerConfig payload; @@ -36,11 +36,11 @@ public class RemotePlayerConfigMessage extends NetworkMessage public void encode(ByteBuf out) { this.payload.encode(out); } @Override - public void decode(ByteBuf in) { this.payload = INetworkObject.decodeStatic(new MultiplayerConfig(), in); } + public void decode(ByteBuf in) { this.payload = INetworkObject.readToObject(new MultiplayerConfig(), in); } @Override public String toString() { - return super.toString("payload=" + payload); + return super.toString("payload=" + this.payload); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/CurrentLevelKeyMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/CurrentLevelKeyMessage.java new file mode 100644 index 000000000..cf61f3e36 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/CurrentLevelKeyMessage.java @@ -0,0 +1,32 @@ +package com.seibel.distanthorizons.core.network.messages.plugin; + +import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; +import io.netty.buffer.ByteBuf; + +public class CurrentLevelKeyMessage extends PluginChannelMessage +{ + public String levelKey; + public boolean deleteExistingData; + + public CurrentLevelKeyMessage() { } + public CurrentLevelKeyMessage(String levelKey, boolean deleteExistingData) + { + this.levelKey = levelKey; + this.deleteExistingData = deleteExistingData; + } + + @Override + public void encode(ByteBuf out) + { + this.writeString(this.levelKey, out); + out.writeBoolean(this.deleteExistingData); + } + + @Override + public void decode(ByteBuf in) + { + this.levelKey = this.readString(in); + this.deleteExistingData = in.readBoolean(); + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginCloseEvent.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginCloseEvent.java new file mode 100644 index 000000000..9d3c854fb --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginCloseEvent.java @@ -0,0 +1,11 @@ +package com.seibel.distanthorizons.core.network.messages.plugin; + +import com.seibel.distanthorizons.core.network.messages.ICloseEvent; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; + +/** + * This is not a "real" message, and only used to indicate a disconnection. + */ +public class PluginCloseEvent extends PluginChannelMessage implements ICloseEvent +{ +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginMessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginMessageRegistry.java new file mode 100644 index 000000000..6c0f863d2 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginMessageRegistry.java @@ -0,0 +1,19 @@ +package com.seibel.distanthorizons.core.network.messages.plugin; + +import com.seibel.distanthorizons.core.network.messages.AbstractMessageRegistry; +import com.seibel.distanthorizons.core.network.messages.netty.base.NettyCloseEvent; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; + +public class PluginMessageRegistry extends AbstractMessageRegistry +{ + public static final PluginMessageRegistry INSTANCE = new PluginMessageRegistry(); + + private PluginMessageRegistry() + { + // Note: Messages must have parameterless constructors + + this.registerMessage(CurrentLevelKeyMessage.class, CurrentLevelKeyMessage::new); + this.registerMessage(ServerConnectInfoMessage.class, ServerConnectInfoMessage::new); + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/ServerConnectInfoMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/ServerConnectInfoMessage.java new file mode 100644 index 000000000..f8f391339 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/ServerConnectInfoMessage.java @@ -0,0 +1,32 @@ +package com.seibel.distanthorizons.core.network.messages.plugin; + +import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; +import io.netty.buffer.ByteBuf; + +public class ServerConnectInfoMessage extends PluginChannelMessage +{ + public String ipAddress; + public short port; + + public ServerConnectInfoMessage() { } + public ServerConnectInfoMessage(String ipAddress, short port) + { + this.ipAddress = ipAddress; + this.port = port; + } + + @Override + public void encode(ByteBuf out) + { + this.writeString(this.ipAddress, out); + out.writeShort(this.port); + } + + @Override + public void decode(ByteBuf in) + { + this.ipAddress = this.readString(in); + this.port = in.readShort(); + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/INettyConnection.java similarity index 76% rename from core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/netty/INettyConnection.java index 4039d07cb..fa5ddcc3b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/IConnection.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/INettyConnection.java @@ -1,10 +1,8 @@ -package com.seibel.distanthorizons.core.network; +package com.seibel.distanthorizons.core.network.netty; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage; -import com.seibel.distanthorizons.core.network.protocol.FutureTrackableNetworkMessage; -import com.seibel.distanthorizons.core.network.protocol.NetworkMessage; +import com.seibel.distanthorizons.core.network.messages.netty.base.CloseReasonMessage; import io.netty.channel.ChannelException; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; @@ -14,14 +12,14 @@ import javax.annotation.Nullable; import java.net.SocketAddress; import java.util.concurrent.CompletableFuture; -public interface IConnection +public interface INettyConnection { ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); @Nullable ChannelHandlerContext getChannelContext(); - NetworkEventSource getRequestHandler(); + NettyEventSource getRequestHandler(); @Nullable Throwable getCloseReason(); @@ -37,7 +35,7 @@ public interface IConnection return ctx.channel().remoteAddress(); } - default CompletableFuture sendMessage(NetworkMessage message) + default CompletableFuture sendMessage(NettyMessage message) { LOGGER.debug("Sending message: " + message); CompletableFuture future = new CompletableFuture<>(); @@ -64,7 +62,7 @@ public interface IConnection return future; } - default CompletableFuture sendRequest(FutureTrackableNetworkMessage msg, Class responseClass) + default CompletableFuture sendRequest(TrackableNettyMessage msg, Class responseClass) { CompletableFuture responseFuture = this.getRequestHandler().createRequest(this, msg, responseClass); this.sendMessage(msg).whenComplete((ignored, throwable) -> diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java similarity index 86% rename from core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java index 45c5873b8..71ab47766 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java @@ -17,15 +17,15 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network; +package com.seibel.distanthorizons.core.network.netty; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.network.messages.base.CloseEvent; -import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage; -import com.seibel.distanthorizons.core.network.messages.base.HelloMessage; -import com.seibel.distanthorizons.core.network.protocol.MessageHandler; -import com.seibel.distanthorizons.core.network.protocol.NetworkChannelInitializer; +import com.seibel.distanthorizons.core.network.messages.netty.base.NettyCloseEvent; +import com.seibel.distanthorizons.core.network.messages.netty.base.CloseReasonMessage; +import com.seibel.distanthorizons.core.network.messages.netty.base.HelloMessage; +import com.seibel.distanthorizons.core.network.protocol.netty.NettyMessageHandler; +import com.seibel.distanthorizons.core.network.protocol.netty.NettyChannelInitializer; import io.netty.bootstrap.Bootstrap; import io.netty.channel.*; import io.netty.channel.nio.NioEventLoopGroup; @@ -40,7 +40,7 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; -public class NetworkClient extends NetworkEventSource implements IConnection, AutoCloseable +public class NettyClient extends NettyEventSource implements INettyConnection, AutoCloseable { private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); @@ -77,7 +77,7 @@ public class NetworkClient extends NetworkEventSource implements IConnection, Au .group(this.workerGroup) .channel(NioSocketChannel.class) .option(ChannelOption.SO_KEEPALIVE, true) - .handler(new NetworkChannelInitializer(new MessageHandler( + .handler(new NettyChannelInitializer(new NettyMessageHandler( (ctx, msg) -> { msg.setConnection(this); this.handleMessage(msg); @@ -93,7 +93,7 @@ public class NetworkClient extends NetworkEventSource implements IConnection, Au public int getReconnectionAttemptsLeft() { return this.reconnectionAttemptsLeft; } - public NetworkClient(String host, int port) + public NettyClient(String host, int port) { this.address = new InetSocketAddress(host, port); this.registerHandlers(); @@ -109,7 +109,7 @@ public class NetworkClient extends NetworkEventSource implements IConnection, Au this.connectionState.set(EConnectionState.GOT_CLOSE_REASON); }); - this.registerHandler(CloseEvent.class, closeEvent -> + this.registerHandler(NettyCloseEvent.class, closeEvent -> { LOGGER.info("Disconnected from server: "+this.getRemoteAddress()); if (this.connectionState.get() == EConnectionState.GOT_CLOSE_REASON) @@ -172,11 +172,11 @@ public class NetworkClient extends NetworkEventSource implements IConnection, Au @Override public ChannelHandlerContext getChannelContext() { - return this.channel.pipeline().context(MessageHandler.class); + return this.channel.pipeline().context(NettyMessageHandler.class); } @Override - public NetworkEventSource getRequestHandler() + public NettyEventSource getRequestHandler() { return this; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyEventSource.java new file mode 100644 index 000000000..c4cdd97ed --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyEventSource.java @@ -0,0 +1,163 @@ +package com.seibel.distanthorizons.core.network.netty; + +import com.seibel.distanthorizons.core.network.NetworkEventSource; +import com.seibel.distanthorizons.core.network.messages.netty.base.NettyCloseEvent; +import com.seibel.distanthorizons.core.network.messages.netty.NettyMessageRegistry; +import com.seibel.distanthorizons.core.network.messages.netty.base.CancelMessage; +import com.seibel.distanthorizons.core.network.messages.netty.base.ExceptionMessage; +import io.netty.channel.ChannelException; + +import java.io.InvalidClassException; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.function.Consumer; + +public abstract class NettyEventSource extends NetworkEventSource +{ + private final ConcurrentMap> pendingFutures = new ConcurrentHashMap<>(); + + public NettyEventSource() + { + super(NettyMessageRegistry.INSTANCE); + } + + + @Override + public void registerHandler(Class handlerClass, Consumer handlerImplementation) + { + if (handlerClass != NettyCloseEvent.class) + { + // Will throw if the handler class is not found + this.messageRegistry.getMessageId(handlerClass); + } + super.registerHandler(handlerClass, handlerImplementation); + } + + @Override + protected boolean tryHandleMessage(NettyMessage message) + { + if (message instanceof TrackableNettyMessage) + { + TrackableNettyMessage trackableMessage = (TrackableNettyMessage) message; + ConcurrentMap subMap = this.pendingFutures.get(message.getConnection()); + if (subMap != null) + { + FutureResponseData responseData = subMap.get(trackableMessage.futureId); + if (responseData != null) + { + if (message instanceof ExceptionMessage) + { + responseData.future.completeExceptionally(((ExceptionMessage) message).exception); + } + else if (message.getClass() != responseData.responseClass) + { + responseData.future.completeExceptionally(new InvalidClassException("Response with invalid type: expected " + responseData.responseClass.getSimpleName() + ", got:" + message)); + } + else + { + responseData.future.complete(trackableMessage); + } + + return true; + } + } + } + + // Still return true if message should be silent when unhandled + return !message.warnWhenUnhandled(); + } + + protected void addNewConnection(INettyConnection connection) + { + this.pendingFutures.put(connection, new ConcurrentHashMap<>()); + } + + public CompletableFuture createRequest(INettyConnection connection, TrackableNettyMessage msg, Class responseClass) + { + msg.setConnection(connection); + + CompletableFuture responseFuture = new CompletableFuture<>(); + responseFuture.whenComplete((response, throwable) -> + { + if (!(throwable instanceof ChannelException)) + { + ConcurrentMap subMap = this.pendingFutures.get(connection); + if (subMap != null) + { + subMap.remove(msg.futureId); + } + } + + if (throwable instanceof CancellationException) + { + msg.sendResponse(new CancelMessage()); + } + }); + + ConcurrentMap subMap = this.pendingFutures.get(connection); + if (subMap == null) + { + // Was deleted before adding + responseFuture.completeExceptionally(connection.getCloseReason()); + return responseFuture; + } + subMap.put(msg.futureId, new FutureResponseData(responseClass, responseFuture)); + if (!this.pendingFutures.containsKey(connection)) + { + // Was deleted while adding + // Note: removal from subMap will happen in whenComplete above + responseFuture.completeExceptionally(connection.getCloseReason()); + return responseFuture; + } + // If passed until here, cancelling is up to the cleaning side + + return responseFuture; + } + + protected final void completeAllFuturesExceptionally(INettyConnection connection, Throwable cause) + { + ConcurrentMap map = this.pendingFutures.remove(connection); + if (map == null) + { + return; + } + + for (FutureResponseData responseData : map.values()) + { + responseData.future.completeExceptionally(cause); + } + } + + protected final void completeAllFuturesExceptionally(Throwable cause) + { + for (INettyConnection connection : this.pendingFutures.keySet()) + { + this.completeAllFuturesExceptionally(connection, cause); + } + } + + @Override + public void close() + { + super.close(); + this.completeAllFuturesExceptionally(new ChannelException(this.getClass().getSimpleName() + " is closed.")); + } + + + private static class FutureResponseData + { + public final Class responseClass; + public final CompletableFuture future; + + private FutureResponseData(Class responseClass, CompletableFuture future) + { + this.responseClass = responseClass; + //noinspection unchecked + this.future = (CompletableFuture) future; + } + + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyMessage.java similarity index 72% rename from core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyMessage.java index 0c51455ee..fb648db78 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyMessage.java @@ -17,34 +17,37 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.protocol; +package com.seibel.distanthorizons.core.network.netty; -import com.seibel.distanthorizons.core.network.IConnection; +import com.seibel.distanthorizons.core.network.netty.INettyConnection; +import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import javax.annotation.Nullable; -public abstract class NetworkMessage implements INetworkObject +public abstract class NettyMessage implements INetworkObject { - private IConnection connection = null; + private INettyConnection connection = null; public boolean warnWhenUnhandled() { return true; } - public IConnection getConnection() + public INettyConnection getConnection() { - return connection; + return this.connection; } - public void setConnection(IConnection connection) + public void setConnection(INettyConnection connection) { if (this.connection != null) + { throw new IllegalStateException("Channel context cannot be changed after initial setting."); + } this.connection = connection; } @Override public String toString() { - return toString(""); + return this.toString(""); } protected String toString(@Nullable String extraData) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyServer.java similarity index 83% rename from core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyServer.java index 62f98d35f..613308386 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyServer.java @@ -17,15 +17,15 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network; +package com.seibel.distanthorizons.core.network.netty; import com.google.common.collect.MapMaker; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.network.messages.base.CloseEvent; -import com.seibel.distanthorizons.core.network.messages.base.HelloMessage; -import com.seibel.distanthorizons.core.network.protocol.MessageHandler; -import com.seibel.distanthorizons.core.network.protocol.NetworkChannelInitializer; +import com.seibel.distanthorizons.core.network.messages.netty.base.NettyCloseEvent; +import com.seibel.distanthorizons.core.network.messages.netty.base.HelloMessage; +import com.seibel.distanthorizons.core.network.protocol.netty.NettyMessageHandler; +import com.seibel.distanthorizons.core.network.protocol.netty.NettyChannelInitializer; import com.seibel.distanthorizons.coreapi.ModInfo; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.Channel; @@ -43,7 +43,7 @@ import javax.annotation.Nullable; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicBoolean; -public class NetworkServer extends NetworkEventSource implements AutoCloseable +public class NettyServer extends NettyEventSource implements AutoCloseable { private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); @@ -54,11 +54,11 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable private final EventLoopGroup workerGroup = new NioEventLoopGroup(0, new DefaultThreadFactory("DH-Network - Server Worker Thread")); private final AtomicBoolean isClosed = new AtomicBoolean(); - private final ConcurrentMap connections = new MapMaker().weakKeys().weakValues().makeMap(); + private final ConcurrentMap connections = new MapMaker().weakKeys().weakValues().makeMap(); - public NetworkServer(int port) + public NettyServer(int port) { this.port = port; @@ -71,7 +71,7 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable { this.registerHandler(HelloMessage.class, helloMessage -> { - IConnection connection = helloMessage.getConnection(); + INettyConnection connection = helloMessage.getConnection(); LOGGER.info("Client connected: "+connection.getRemoteAddress()); if (helloMessage.version != ModInfo.PROTOCOL_VERSION) @@ -92,9 +92,9 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable connection.sendMessage(new HelloMessage()); }); - this.registerHandler(CloseEvent.class, closeEvent -> + this.registerHandler(NettyCloseEvent.class, closeEvent -> { - IConnection connection = closeEvent.getConnection(); + INettyConnection connection = closeEvent.getConnection(); LOGGER.info("Client disconnected: "+connection.getRemoteAddress()); this.completeAllFuturesExceptionally(closeEvent.getConnection(), connection.getCloseReason()); }); @@ -106,7 +106,7 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable .group(this.bossGroup, this.workerGroup) .channel(NioServerSocketChannel.class) .handler(new LoggingHandler(LogLevel.DEBUG)) - .childHandler(new NetworkChannelInitializer(new MessageHandler( + .childHandler(new NettyChannelInitializer(new NettyMessageHandler( (ctx, msg) -> { msg.setConnection(this.connections.computeIfAbsent(ctx, Connection::new)); this.handleMessage(msg); @@ -146,7 +146,7 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable super.close(); } - public class Connection implements IConnection + public class Connection implements INettyConnection { private final ChannelHandlerContext channelContext; @@ -168,9 +168,9 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable } @Override - public NetworkEventSource getRequestHandler() + public NettyEventSource getRequestHandler() { - return NetworkServer.this; + return NettyServer.this; } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/TrackableNettyMessage.java similarity index 73% rename from core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/netty/TrackableNettyMessage.java index ce267f67e..c090a6662 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/FutureTrackableNetworkMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/TrackableNettyMessage.java @@ -17,12 +17,11 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.protocol; +package com.seibel.distanthorizons.core.network.netty; import com.google.common.collect.MapMaker; import com.seibel.distanthorizons.core.api.internal.SharedApi; -import com.seibel.distanthorizons.core.network.IConnection; -import com.seibel.distanthorizons.core.network.messages.base.ExceptionMessage; +import com.seibel.distanthorizons.core.network.messages.netty.base.ExceptionMessage; import com.seibel.distanthorizons.core.world.EWorldEnvironment; import io.netty.buffer.ByteBuf; @@ -31,7 +30,7 @@ import java.util.Objects; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; -public abstract class FutureTrackableNetworkMessage extends NetworkMessage +public abstract class TrackableNettyMessage extends NettyMessage { private static final AtomicInteger lastId = new AtomicInteger(); // 32 bits - Context ID (not transmitted) @@ -41,16 +40,16 @@ public abstract class FutureTrackableNetworkMessage extends NetworkMessage | ((Objects.requireNonNull(SharedApi.getEnvironment()) == EWorldEnvironment.Server_Only ? 1 : 0) << 31); private static final AtomicInteger lastContextId = new AtomicInteger(); - private static final ConcurrentMap connectionToIdMap = new MapMaker().weakKeys().makeMap(); + private static final ConcurrentMap connectionToIdMap = new MapMaker().weakKeys().makeMap(); - public void sendResponse(FutureTrackableNetworkMessage responseMessage) + public void sendResponse(TrackableNettyMessage responseMessage) { - responseMessage.futureId = futureId; + responseMessage.futureId = this.futureId; this.getConnection().sendMessage(responseMessage); } @Override - public void setConnection(IConnection connection) + public void setConnection(INettyConnection connection) { super.setConnection(connection); this.futureId |= (long) connectionToIdMap.computeIfAbsent(connection, k -> lastContextId.getAndIncrement()) << 32; @@ -58,14 +57,15 @@ public abstract class FutureTrackableNetworkMessage extends NetworkMessage public void sendResponse(Exception e) { - sendResponse(new ExceptionMessage(e)); + this.sendResponse(new ExceptionMessage(e)); } - @Override public final void encode(ByteBuf out) + @Override + public final void encode(ByteBuf out) { try { - out.writeInt((int)futureId); + out.writeInt((int) this.futureId); this.encode0(out); } catch (Exception e) @@ -74,11 +74,12 @@ public abstract class FutureTrackableNetworkMessage extends NetworkMessage } } - @Override public final void decode(ByteBuf in) + @Override + public final void decode(ByteBuf in) { try { - futureId = in.readInt(); + this.futureId = in.readInt(); this.decode0(in); } catch (Exception e) @@ -93,13 +94,13 @@ public abstract class FutureTrackableNetworkMessage extends NetworkMessage @Override public String toString() { - return toString(null); + return this.toString(null); } - protected String toString(@Nullable String extraData) + @Override protected String toString(@Nullable String extraData) { return super.toString( - "futureId=" + futureId + + "futureId=" + this.futureId + (extraData != null ? ", " + extraData : "") ); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelHandler.java new file mode 100644 index 000000000..7449e5f05 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelHandler.java @@ -0,0 +1,74 @@ +package com.seibel.distanthorizons.core.network.plugin; + +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; +import com.seibel.distanthorizons.core.network.NetworkEventSource; +import com.seibel.distanthorizons.core.network.messages.plugin.PluginMessageRegistry; +import com.seibel.distanthorizons.core.network.protocol.plugin.PluginMessageDecoder; +import com.seibel.distanthorizons.core.network.protocol.plugin.PluginMessageEncoder; +import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender; +import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.PooledByteBufAllocator; +import org.apache.logging.log4j.LogManager; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; + +public class PluginChannelHandler extends NetworkEventSource +{ + private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), + () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); + + private final PluginMessageDecoder messageDecoder = new PluginMessageDecoder(); + private final PluginMessageEncoder messageEncoder = new PluginMessageEncoder(); + private final IPluginPacketSender packetSender = SingletonInjector.INSTANCE.get(IPluginPacketSender.class); + + /** + * When set to true, any received data will be ignored.
+ * This does not include wrong version, which is ignored without setting this flag, + * to allow multi-compat servers. + */ + private boolean isCorrupted = false; + + + public PluginChannelHandler() + { + super(PluginMessageRegistry.INSTANCE); + } + + + public void decodeAndHandle(ByteBuf byteBuf) + { + if (this.isCorrupted) + { + return; + } + + try + { + ArrayList messages = new ArrayList<>(); + this.messageDecoder.decode(byteBuf, messages); + + for (Object msg : messages) + { + this.handleMessage((PluginChannelMessage) msg); + } + } + catch (Throwable e) + { + LOGGER.error("Failed to handle the message. New messages will be ignored. \n" + e); + this.isCorrupted = true; + } + } + + public void sendMessage(@Nullable IServerPlayerWrapper serverPlayer, PluginChannelMessage message) + { + ByteBuf buffer = PooledByteBufAllocator.DEFAULT.buffer(); + this.messageEncoder.encode(message, buffer); + + this.packetSender.sendPluginPacket(serverPlayer, buffer); + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelMessage.java new file mode 100644 index 000000000..0d30fc225 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelMessage.java @@ -0,0 +1,8 @@ +package com.seibel.distanthorizons.core.network.plugin; + +import com.seibel.distanthorizons.core.network.protocol.INetworkObject; + +public abstract class PluginChannelMessage implements INetworkObject +{ + // Exists only to distinguish "message" from "object" +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java index 57a11e198..115f9ac23 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java @@ -35,14 +35,14 @@ public interface INetworkObject void decode(ByteBuf in); - static T decodeStatic(T obj, ByteBuf inputByteBuf) + static T readToObject(T obj, ByteBuf inputByteBuf) { obj.decode(inputByteBuf); return obj; } @Contract("_, null -> false; _, !null -> true") - default boolean encodeOptional(ByteBuf outputByteBuf, Object value) + default boolean writeOptional(ByteBuf outputByteBuf, Object value) { boolean isNull = value != null; outputByteBuf.writeBoolean(isNull); @@ -50,27 +50,27 @@ public interface INetworkObject } @Nullable - default T decodeOptional(ByteBuf inputByteBuf, Supplier decoder) + default T readOptional(ByteBuf inputByteBuf, Supplier decoder) { return inputByteBuf.readBoolean() ? decoder.get() : null; } - default void encodeString(String inputString, ByteBuf outputByteBuf) + default void writeString(String inputString, ByteBuf outputByteBuf) { byte[] bytes = inputString.getBytes(StandardCharsets.UTF_8); outputByteBuf.writeShort(bytes.length); outputByteBuf.writeBytes(bytes); } - default String decodeString(ByteBuf inputByteBuf) + default String readString(ByteBuf inputByteBuf) { int length = inputByteBuf.readShort(); return inputByteBuf.readBytes(length).toString(StandardCharsets.UTF_8); } - default void encodeCollection(ByteBuf outputByteBuf, Collection collection) + default void writeCollection(ByteBuf outputByteBuf, Collection collection) { outputByteBuf.writeInt(collection.size()); @@ -85,7 +85,7 @@ public interface INetworkObject } } - default void decodeCollection(ByteBuf inputByteBuf, Collection collection, Supplier innerValueConstructor) + default void readCollection(ByteBuf inputByteBuf, Collection collection, Supplier innerValueConstructor) { int size = inputByteBuf.readInt(); @@ -105,11 +105,11 @@ public interface INetworkObject } } - default void decodeMap(ByteBuf inputByteBuf, Map map, Supplier keySupplier, Supplier valueSupplier) + default void readMap(ByteBuf inputByteBuf, Map map, Supplier keySupplier, Supplier valueSupplier) { ArrayList> entryList = new ArrayList<>(); - this.decodeCollection(inputByteBuf, entryList, () -> new AbstractMap.SimpleEntry<>(keySupplier.get(), valueSupplier.get())); + this.readCollection(inputByteBuf, entryList, () -> new AbstractMap.SimpleEntry<>(keySupplier.get(), valueSupplier.get())); for (Map.Entry entry : entryList) { map.put(entry.getKey(), entry.getValue()); @@ -127,7 +127,7 @@ public interface INetworkObject // Primitives must be added manually here this.put(Integer.class, new Codec((obj, out) -> out.writeInt((int)obj), (obj, in) -> in.readInt())); - this.put(INetworkObject.class, new Codec(INetworkObject::encode, INetworkObject::decodeStatic)); + this.put(INetworkObject.class, new Codec(INetworkObject::encode, INetworkObject::readToObject)); this.put(Map.Entry.class, new Codec( (obj, out) -> { Map.Entry entry = (Entry) obj; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageDecoder.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageDecoder.java index fa1f281ef..34b1af03b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageDecoder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageDecoder.java @@ -19,19 +19,27 @@ package com.seibel.distanthorizons.core.network.protocol; +import com.seibel.distanthorizons.core.network.messages.AbstractMessageRegistry; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ByteToMessageDecoder; import java.util.List; -public class MessageDecoder extends ByteToMessageDecoder +public class MessageDecoder extends ByteToMessageDecoder { - @Override - protected void decode(ChannelHandlerContext channelContext, ByteBuf inputByteBuf, List outputDecodedObjectList) + private final AbstractMessageRegistry messageRegistry; + + public MessageDecoder(AbstractMessageRegistry messageRegistry) { - NetworkMessage message = MessageRegistry.INSTANCE.createMessage(inputByteBuf.readShort()); - outputDecodedObjectList.add(INetworkObject.decodeStatic(message, inputByteBuf)); - } + this.messageRegistry = messageRegistry; + } + + @Override + protected void decode(ChannelHandlerContext channelContext, ByteBuf inputByteBuf, List outputDecodedObjectList) + { + TMessage message = this.messageRegistry.createMessage(inputByteBuf.readShort()); + outputDecodedObjectList.add(INetworkObject.readToObject(message, inputByteBuf)); + } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageEncoder.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageEncoder.java index f42475639..79bb90f18 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageEncoder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageEncoder.java @@ -19,16 +19,25 @@ package com.seibel.distanthorizons.core.network.protocol; +import com.seibel.distanthorizons.core.network.messages.AbstractMessageRegistry; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; -public class MessageEncoder extends MessageToByteEncoder +public class MessageEncoder extends MessageToByteEncoder { - @Override - protected void encode(ChannelHandlerContext channelContext, NetworkMessage message, ByteBuf outputByteBuf) throws IllegalArgumentException + private final AbstractMessageRegistry messageRegistry; + + public MessageEncoder(AbstractMessageRegistry messageRegistry, Class messageClass) { - outputByteBuf.writeShort(MessageRegistry.INSTANCE.getMessageId(message)); + super(messageClass); + this.messageRegistry = messageRegistry; + } + + @Override + protected void encode(ChannelHandlerContext channelContext, TMessage message, ByteBuf outputByteBuf) throws IllegalArgumentException + { + outputByteBuf.writeShort(this.messageRegistry.getMessageId(message)); message.encode(outputByteBuf); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java deleted file mode 100644 index 94287aab0..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java +++ /dev/null @@ -1,110 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.network.protocol; - -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import com.seibel.distanthorizons.core.network.messages.base.*; -import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceResponseMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.GenTaskPriorityRequestMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.generation.GenTaskPriorityResponseMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage; -import com.seibel.distanthorizons.core.network.messages.session.PlayerUUIDMessage; -import com.seibel.distanthorizons.core.network.messages.session.RemotePlayerConfigMessage; - -import java.util.HashMap; -import java.util.Map; -import java.util.function.Supplier; - -public class MessageRegistry -{ - public static final MessageRegistry INSTANCE = new MessageRegistry(); - - private final Map> idToSupplier = new HashMap<>(); - private final BiMap, Integer> classToId = HashBiMap.create(); - - - - private MessageRegistry() - { - // Note: Messages must have parameterless constructors - - // Opening & closing connection - // These messages should be compatible with any previous protocol versions - this.registerMessage(HelloMessage.class, HelloMessage::new); - this.registerMessage(CloseReasonMessage.class, CloseReasonMessage::new); - - // Core - this.registerMessage(AckMessage.class, AckMessage::new); - this.registerMessage(CancelMessage.class, CancelMessage::new); - this.registerMessage(ExceptionMessage.class, ExceptionMessage::new); - - // ID & config - this.registerMessage(PlayerUUIDMessage.class, PlayerUUIDMessage::new); - this.registerMessage(RemotePlayerConfigMessage.class, RemotePlayerConfigMessage::new); - - // Full data requests & updates - this.registerMessage(FullDataSourceRequestMessage.class, FullDataSourceRequestMessage::new); - this.registerMessage(FullDataSourceResponseMessage.class, FullDataSourceResponseMessage::new); - this.registerMessage(FullDataPartialUpdateMessage.class, FullDataPartialUpdateMessage::new); - - // Generation task prioritization - this.registerMessage(GenTaskPriorityRequestMessage.class, GenTaskPriorityRequestMessage::new); - this.registerMessage(GenTaskPriorityResponseMessage.class, GenTaskPriorityResponseMessage::new); - } - - - - public void registerMessage(Class clazz, Supplier supplier) - { - int id = this.idToSupplier.size() + 1; - this.idToSupplier.put(id, supplier); - this.classToId.put(clazz, id); - } - - public Class getMessageClassById(int messageId) { return this.classToId.inverse().get(messageId); } - - public NetworkMessage createMessage(int messageId) throws IllegalArgumentException - { - try - { - return this.idToSupplier.get(messageId).get(); - } - catch (NullPointerException e) - { - throw new IllegalArgumentException("Invalid message ID: "+messageId); - } - } - - public int getMessageId(NetworkMessage message) { return this.getMessageId(message.getClass()); } - - public int getMessageId(Class messageClass) { - try - { - return this.classToId.get(messageClass); - } - catch (NullPointerException e) - { - throw new IllegalArgumentException("Message does not have ID assigned to it: "+messageClass.getSimpleName()); - } - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkChannelInitializer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyChannelInitializer.java similarity index 67% rename from core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkChannelInitializer.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyChannelInitializer.java index 898798c88..d9946103d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkChannelInitializer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyChannelInitializer.java @@ -17,8 +17,12 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.protocol; +package com.seibel.distanthorizons.core.network.protocol.netty; +import com.seibel.distanthorizons.core.network.messages.netty.NettyMessageRegistry; +import com.seibel.distanthorizons.core.network.netty.NettyMessage; +import com.seibel.distanthorizons.core.network.protocol.MessageDecoder; +import com.seibel.distanthorizons.core.network.protocol.MessageEncoder; import io.netty.channel.*; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; @@ -26,7 +30,7 @@ import io.netty.handler.codec.LengthFieldPrepender; import org.jetbrains.annotations.NotNull; /** Used when creating a network channel */ -public class NetworkChannelInitializer extends ChannelInitializer +public class NettyChannelInitializer extends ChannelInitializer { /** * 4 MiB should be enough for any transferred data.
@@ -34,9 +38,9 @@ public class NetworkChannelInitializer extends ChannelInitializer */ private static final int MAX_MESSAGE_LENGTH = 4194304; - private final MessageHandler messageHandler; - - public NetworkChannelInitializer(MessageHandler messageHandler) { this.messageHandler = messageHandler; } + private final NettyMessageHandler messageHandler; + + public NettyChannelInitializer(NettyMessageHandler messageHandler) { this.messageHandler = messageHandler; } @Override public void initChannel(@NotNull SocketChannel socketChannel) @@ -45,12 +49,12 @@ public class NetworkChannelInitializer extends ChannelInitializer // Encoder pipeline.addLast(new LengthFieldPrepender(Integer.BYTES)); - pipeline.addLast(new MessageEncoder()); - pipeline.addLast(new NetworkOutboundExceptionRouter()); + pipeline.addLast(new MessageEncoder<>(NettyMessageRegistry.INSTANCE, NettyMessage.class)); + pipeline.addLast(new NettyOutboundExceptionRouter()); // Decoder pipeline.addLast(new LengthFieldBasedFrameDecoder(MAX_MESSAGE_LENGTH, 0, Integer.BYTES, 0, Integer.BYTES)); - pipeline.addLast(new MessageDecoder()); + pipeline.addLast(new MessageDecoder<>(NettyMessageRegistry.INSTANCE)); // Handler pipeline.addLast(this.messageHandler); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyMessageHandler.java similarity index 83% rename from core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyMessageHandler.java index 6ff466ef7..6b39c1420 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyMessageHandler.java @@ -17,11 +17,12 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.protocol; +package com.seibel.distanthorizons.core.network.protocol.netty; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.network.messages.base.CloseEvent; +import com.seibel.distanthorizons.core.network.messages.netty.base.NettyCloseEvent; +import com.seibel.distanthorizons.core.network.netty.NettyMessage; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; @@ -33,17 +34,17 @@ import java.util.function.BiConsumer; import java.util.function.Consumer; @ChannelHandler.Sharable -public class MessageHandler extends SimpleChannelInboundHandler +public class NettyMessageHandler extends SimpleChannelInboundHandler { private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); - private final BiConsumer messageConsumer; + private final BiConsumer messageConsumer; private final Consumer channelActiveConsumer; private final BiConsumer exceptionConsumer; - public MessageHandler( - BiConsumer messageConsumer, + public NettyMessageHandler( + BiConsumer messageConsumer, Consumer channelActiveConsumer, BiConsumer exceptionConsumer) { @@ -53,7 +54,7 @@ public class MessageHandler extends SimpleChannelInboundHandler } @Override - protected void channelRead0(ChannelHandlerContext channelContext, NetworkMessage message) + protected void channelRead0(ChannelHandlerContext channelContext, NettyMessage message) { LOGGER.debug("Received message: " + message); this.messageConsumer.accept(channelContext, message); @@ -86,7 +87,7 @@ public class MessageHandler extends SimpleChannelInboundHandler public void channelInactive(@NotNull ChannelHandlerContext channelContext) throws Exception { super.channelInactive(channelContext); - this.channelRead0(channelContext, new CloseEvent()); + this.channelRead0(channelContext, new NettyCloseEvent()); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkOutboundExceptionRouter.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyOutboundExceptionRouter.java similarity index 89% rename from core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkOutboundExceptionRouter.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyOutboundExceptionRouter.java index 592ce46e3..09ec33ac2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/NetworkOutboundExceptionRouter.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyOutboundExceptionRouter.java @@ -17,14 +17,14 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.protocol; +package com.seibel.distanthorizons.core.network.protocol.netty; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelOutboundHandlerAdapter; import io.netty.channel.ChannelPromise; -public class NetworkOutboundExceptionRouter extends ChannelOutboundHandlerAdapter +public class NettyOutboundExceptionRouter extends ChannelOutboundHandlerAdapter { @Override public void write(ChannelHandlerContext channelContext, Object messageObj, ChannelPromise promise) throws Exception diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/plugin/PluginMessageDecoder.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/plugin/PluginMessageDecoder.java new file mode 100644 index 000000000..d1f311f20 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/plugin/PluginMessageDecoder.java @@ -0,0 +1,33 @@ +package com.seibel.distanthorizons.core.network.protocol.plugin; + +import com.seibel.distanthorizons.core.network.messages.AbstractMessageRegistry; +import com.seibel.distanthorizons.core.network.messages.plugin.PluginMessageRegistry; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; +import com.seibel.distanthorizons.core.network.protocol.INetworkObject; +import com.seibel.distanthorizons.core.network.protocol.MessageDecoder; +import com.seibel.distanthorizons.core.network.protocol.MessageEncoder; +import com.seibel.distanthorizons.coreapi.ModInfo; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; + +import java.util.List; + +public class PluginMessageDecoder extends MessageDecoder +{ + public PluginMessageDecoder() + { + super(PluginMessageRegistry.INSTANCE); + } + + public void decode(ByteBuf inputByteBuf, List outputDecodedObjectList) + { + int version = inputByteBuf.readShort(); + if (version != ModInfo.PLUGIN_PROTOCOL_VERSION) + { + return; + } + + super.decode(null, inputByteBuf, outputDecodedObjectList); + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/plugin/PluginMessageEncoder.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/plugin/PluginMessageEncoder.java new file mode 100644 index 000000000..7cc405080 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/plugin/PluginMessageEncoder.java @@ -0,0 +1,22 @@ +package com.seibel.distanthorizons.core.network.protocol.plugin; + +import com.seibel.distanthorizons.core.network.messages.plugin.PluginMessageRegistry; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; +import com.seibel.distanthorizons.core.network.protocol.MessageEncoder; +import com.seibel.distanthorizons.coreapi.ModInfo; +import io.netty.buffer.ByteBuf; + +public class PluginMessageEncoder extends MessageEncoder +{ + public PluginMessageEncoder() + { + super(PluginMessageRegistry.INSTANCE, PluginChannelMessage.class); + } + + public void encode(PluginChannelMessage pluginChannelMessage, ByteBuf outputByteBuf) throws IllegalArgumentException + { + outputByteBuf.writeShort(ModInfo.PLUGIN_PROTOCOL_VERSION); + super.encode(null, pluginChannelMessage, outputByteBuf); + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/RenderUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/RenderUtil.java index 0ea8414ca..b139264d4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/RenderUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/RenderUtil.java @@ -246,7 +246,7 @@ public class RenderUtil return false; } - IDhClientLevel level = clientWorld.getOrLoadClientLevel(levelWrapper); + IDhClientLevel level = (IDhClientLevel) clientWorld.getLevel(levelWrapper); if (level == null) { return false; //Level is not ready yet. diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java index 8e18feeec..c75c41192 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java @@ -25,7 +25,8 @@ import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure; import com.seibel.distanthorizons.core.level.DhClientLevel; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; -import com.seibel.distanthorizons.core.network.NetworkClient; +import com.seibel.distanthorizons.core.network.netty.NettyClient; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelHandler; import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.util.objects.EventLoop; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; @@ -35,7 +36,6 @@ import org.jetbrains.annotations.NotNull; import javax.annotation.CheckForNull; import java.io.File; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; @@ -67,8 +67,8 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld if (Config.Client.Advanced.Multiplayer.ServerNetworking.enableServerNetworking.get()) { // TODO per server configs - NetworkClient networkClient = new NetworkClient(MC_CLIENT.getCurrentServerIp().split(":")[0], Config.Client.Advanced.Multiplayer.ServerNetworking.serverPort.get()); - this.networkState = new ClientNetworkState(networkClient, MC_CLIENT.getPlayerUUID()); + NettyClient nettyClient = new NettyClient(MC_CLIENT.getCurrentServerIp().split(":")[0], Config.Client.Advanced.Multiplayer.ServerNetworking.serverPort.get()); + this.networkState = new ClientNetworkState(nettyClient, MC_CLIENT.getPlayerUUID()); } else { @@ -101,7 +101,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld return null; } - return new DhClientLevel(this.saveStructure, clientLevelWrapper, networkState); + return new DhClientLevel(this.saveStructure, clientLevelWrapper, this.networkState); }); } @@ -140,9 +140,10 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld this.levels.values().forEach(DhClientLevel::clientTick); } - public void clientTick() { this.eventLoop.tick(); } + @Override public void clientTick() { this.eventLoop.tick(); } - public void doWorldGen() { + @Override public void doWorldGen() + { this.levels.values().forEach(DhClientLevel::doWorldGen); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java index 1cf1cd52c..2fe01db62 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java @@ -24,7 +24,7 @@ import com.seibel.distanthorizons.core.file.structure.LocalSaveStructure; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.multiplayer.server.RemotePlayerConnectionHandler; -import com.seibel.distanthorizons.core.network.NetworkServer; +import com.seibel.distanthorizons.core.network.netty.NettyServer; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; @@ -33,7 +33,6 @@ import org.jetbrains.annotations.NotNull; import java.io.File; import java.util.HashMap; -import java.util.concurrent.CompletableFuture; public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld { @@ -52,9 +51,9 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld this.saveStructure = new LocalSaveStructure(); this.levels = new HashMap<>(); - - NetworkServer networkServer = new NetworkServer(Config.Client.Advanced.Multiplayer.ServerNetworking.serverPort.get()); - this.remotePlayerConnectionHandler = new RemotePlayerConnectionHandler(networkServer); + + NettyServer nettyServer = new NettyServer(Config.Client.Advanced.Multiplayer.ServerNetworking.serverPort.get()); + this.remotePlayerConnectionHandler = new RemotePlayerConnectionHandler(nettyServer); LOGGER.info("Started "+DhServerWorld.class.getSimpleName()+" of type "+this.environment); } @@ -126,12 +125,14 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld this.levels.remove(wrapper).close(); } } - - public void serverTick() { + + @Override public void serverTick() + { this.levels.values().forEach(DhServerLevel::serverTick); } - - public void doWorldGen() { + + @Override public void doWorldGen() + { this.levels.values().forEach(DhServerLevel::doWorldGen); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IPluginPacketSender.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IPluginPacketSender.java new file mode 100644 index 000000000..3e51771f4 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IPluginPacketSender.java @@ -0,0 +1,11 @@ +package com.seibel.distanthorizons.core.wrapperInterfaces.misc; + +import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable; +import io.netty.buffer.ByteBuf; +import org.jetbrains.annotations.Nullable; + +public interface IPluginPacketSender extends IBindable +{ + void sendPluginPacket(@Nullable IServerPlayerWrapper serverPlayer, ByteBuf buffer); + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IClientLevelWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IClientLevelWrapper.java index 8ef1e3a01..797ea30cc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IClientLevelWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IClientLevelWrapper.java @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.world; +import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import org.jetbrains.annotations.Nullable; @@ -28,7 +29,6 @@ import org.jetbrains.annotations.Nullable; */ public interface IClientLevelWrapper extends ILevelWrapper { - @Nullable IServerLevelWrapper tryGetServerSideWrapper(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java index 127ff04fa..e741f5bb9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java @@ -21,6 +21,7 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.world; import com.seibel.distanthorizons.api.interfaces.world.IDhApiDimensionTypeWrapper; import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper; +import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; @@ -35,7 +36,6 @@ import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindab */ public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable { - @Override IDimensionTypeWrapper getDimensionType(); From 1f438d8f87861608405f5f8d26aad7b44fa66837 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 10 Mar 2024 21:09:52 +0500 Subject: [PATCH 107/205] Fix world setting --- .../core/api/internal/ClientApi.java | 2 ++ .../api/internal/ClientPluginChannelApi.java | 13 +++++--- .../messages/plugin/PluginHelloMessage.java | 21 ++++++++++++ .../plugin/PluginMessageRegistry.java | 1 + .../network/plugin/PluginChannelHandler.java | 33 ++++++++++++++++--- .../minecraft/IMinecraftClientWrapper.java | 5 +++ 6 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginHelloMessage.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index 631784a29..683945a50 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -132,6 +132,8 @@ public class ClientApi } this.waitingClientLevels.clear(); + + this.pluginChannelApi.onJoin(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index ea07c609f..98192e6b4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -7,6 +7,7 @@ import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.network.messages.plugin.PluginCloseEvent; import com.seibel.distanthorizons.core.network.messages.plugin.CurrentLevelKeyMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.PluginHelloMessage; import com.seibel.distanthorizons.core.network.messages.plugin.ServerConnectInfoMessage; import com.seibel.distanthorizons.core.network.plugin.PluginChannelHandler; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; @@ -46,6 +47,11 @@ public class ClientPluginChannelApi implements AutoCloseable this.channelHandler.registerHandler(PluginCloseEvent.class, this::onClose); } + public void onJoin() + { + this.channelHandler.sendMessage(new PluginHelloMessage()); + } + private void onCurrentLevelKeyMessage(CurrentLevelKeyMessage msg) { if (!msg.levelKey.matches("[a-zA-Z0-9_]+")) @@ -56,7 +62,7 @@ public class ClientPluginChannelApi implements AutoCloseable LOGGER.info("Server level change event received, changing the level to [" + msg.levelKey + "]."); MC.executeOnRenderThread(() -> { - IClientLevelWrapper clientLevel = MC.getWrappedClientLevel(); + IClientLevelWrapper clientLevel = MC.getWrappedClientLevel(true); if (clientLevel != null) { @@ -78,9 +84,9 @@ public class ClientPluginChannelApi implements AutoCloseable KEYED_CLIENT_LEVEL_MANAGER.clearServerKeyedLevel(); } - public void onClose(PluginCloseEvent event) + private void onClose(PluginCloseEvent event) { - this.onClientLevelUnload(); + KEYED_CLIENT_LEVEL_MANAGER.disable(); } public void handlePacket(ByteBuf buffer) @@ -91,7 +97,6 @@ public class ClientPluginChannelApi implements AutoCloseable @Override public void close() { - KEYED_CLIENT_LEVEL_MANAGER.disable(); this.channelHandler.close(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginHelloMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginHelloMessage.java new file mode 100644 index 000000000..7aed2474e --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginHelloMessage.java @@ -0,0 +1,21 @@ +package com.seibel.distanthorizons.core.network.messages.plugin; + +import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; +import io.netty.buffer.ByteBuf; + +/** Serves as a trigger for the server to send the first world change. */ +public class PluginHelloMessage extends PluginChannelMessage +{ + @Override + public void encode(ByteBuf out) + { + + } + + @Override + public void decode(ByteBuf in) + { + + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginMessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginMessageRegistry.java index 6c0f863d2..ffa927d6b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginMessageRegistry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginMessageRegistry.java @@ -12,6 +12,7 @@ public class PluginMessageRegistry extends AbstractMessageRegistry { @@ -27,10 +29,10 @@ public class PluginChannelHandler extends NetworkEventSource - * This does not include wrong version, which is ignored without setting this flag, + * This does not include wrong versions, which are ignored without setting this flag, * to allow multi-compat servers. */ - private boolean isCorrupted = false; + private final AtomicBoolean isClosed = new AtomicBoolean(); public PluginChannelHandler() @@ -41,7 +43,7 @@ public class PluginChannelHandler extends NetworkEventSource + * Returns null if the client isn't in a level. + */ + IClientLevelWrapper getWrappedClientLevel(boolean bypassMultiverse); /** Please move over to getInstallationDirectory() within the IMinecraftSharedWrapper */ @Deprecated From e008eb8a13390e01d153220dd025c9584c5b562c Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Thu, 14 Mar 2024 21:46:23 +0500 Subject: [PATCH 108/205] Server side plugin networking Untested port for Forge --- .../api/internal/ClientPluginChannelApi.java | 8 +++- .../core/api/internal/ServerApi.java | 16 +++++--- .../server/RemotePlayerConnectionHandler.java | 40 ++++++++++++------- .../network/plugin/PluginChannelHandler.java | 9 +++-- .../network/plugin/PluginChannelMessage.java | 3 +- .../core/world/DhServerWorld.java | 11 +++-- 6 files changed, 58 insertions(+), 29 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index 98192e6b4..1aee06fbf 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -11,9 +11,11 @@ import com.seibel.distanthorizons.core.network.messages.plugin.PluginHelloMessag import com.seibel.distanthorizons.core.network.messages.plugin.ServerConnectInfoMessage; import com.seibel.distanthorizons.core.network.plugin.PluginChannelHandler; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import io.netty.buffer.ByteBuf; import org.apache.logging.log4j.LogManager; +import org.jetbrains.annotations.Nullable; import java.util.function.Consumer; @@ -91,7 +93,11 @@ public class ClientPluginChannelApi implements AutoCloseable public void handlePacket(ByteBuf buffer) { - this.channelHandler.decodeAndHandle(buffer); + this.handlePacket(null, buffer); + } + public void handlePacket(@Nullable IServerPlayerWrapper serverPlayer, ByteBuf buffer) + { + this.channelHandler.decodeAndHandle(serverPlayer, buffer); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java index b3c60773a..f1163db28 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java @@ -36,13 +36,9 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; +import io.netty.buffer.ByteBuf; import org.apache.logging.log4j.Logger; -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.concurrent.ThreadPoolExecutor; - /** * This holds the methods that should be called by the host mod loader (Fabric, * Forge, etc.). Specifically server events. @@ -182,4 +178,14 @@ public class ServerApi } } + public void pluginMessageReceived(IServerPlayerWrapper player, ByteBuf buffer) + { + IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); + if (serverWorld instanceof DhServerWorld) // TODO add support for DhClientServerWorld's (lan worlds) as well + { + LOGGER.debug("Player " + player.getUUID() + " sent plugin message: " + buffer.toString()); + ((DhServerWorld) serverWorld).handlePluginMessage(player, buffer); + } + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java index 026186162..d35cb5f05 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java @@ -8,9 +8,9 @@ import com.seibel.distanthorizons.core.config.types.ConfigEntry; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; +import com.seibel.distanthorizons.core.network.messages.plugin.PluginHelloMessage; import com.seibel.distanthorizons.core.network.netty.INettyConnection; import com.seibel.distanthorizons.core.network.netty.NettyServer; -import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; import com.seibel.distanthorizons.core.network.messages.netty.base.AckMessage; import com.seibel.distanthorizons.core.network.messages.netty.base.NettyCloseEvent; @@ -19,8 +19,10 @@ import com.seibel.distanthorizons.core.network.messages.netty.session.PlayerUUID import com.seibel.distanthorizons.core.network.messages.netty.session.RemotePlayerConfigMessage; import com.seibel.distanthorizons.core.network.netty.TrackableNettyMessage; import com.seibel.distanthorizons.core.network.netty.NettyMessage; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelHandler; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; +import io.netty.buffer.ByteBuf; import org.jetbrains.annotations.Nullable; import java.io.Closeable; @@ -34,23 +36,20 @@ public class RemotePlayerConnectionHandler implements Closeable { private static final ConfigEntry GENERATE_MULTIPLE_DIMENSIONS_CONFIG = Config.Client.Advanced.Multiplayer.ServerNetworking.generateMultipleDimensions; - private final ScopedNetworkEventSource eventSource; + private final NettyServer server = new NettyServer(Config.Client.Advanced.Multiplayer.ServerNetworking.serverPort.get()); private final ConcurrentHashMap playersByUUID = new ConcurrentHashMap<>(); private final BiMap playersByConnection = Maps.synchronizedBiMap(HashBiMap.create()); - private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::onConfigChanged); - public NettyServer server() { return this.eventSource.parent; } + private final PluginChannelHandler pluginChannelHandler = new PluginChannelHandler(); + // TODO port change listener - public RemotePlayerConnectionHandler(NettyServer nettyServer) - { - this.eventSource = new ScopedNetworkEventSource<>(nettyServer); - this.registerNetworkHandlers(); - } + public NettyServer server() { return this.server; } - private void registerNetworkHandlers() + public RemotePlayerConnectionHandler() { - this.eventSource.registerHandler(PlayerUUIDMessage.class, playerUUIDMessage -> + //region Netty server + this.server.registerHandler(PlayerUUIDMessage.class, playerUUIDMessage -> { INettyConnection connection = playerUUIDMessage.getConnection(); ServerPlayerState serverPlayerState = this.playersByUUID.get(playerUUIDMessage.playerUUID); @@ -73,13 +72,13 @@ public class RemotePlayerConnectionHandler implements Closeable playerUUIDMessage.sendResponse(new AckMessage()); }); - this.eventSource.registerHandler(RemotePlayerConfigMessage.class, this.connectedPlayersOnly((remotePlayerConfigMessage, serverPlayerState) -> + this.server.registerHandler(RemotePlayerConfigMessage.class, this.connectedPlayersOnly((remotePlayerConfigMessage, serverPlayerState) -> { serverPlayerState.config.clientConfig = (MultiplayerConfig) remotePlayerConfigMessage.payload; serverPlayerState.connection.sendMessage(new RemotePlayerConfigMessage(serverPlayerState.config)); })); - this.eventSource.registerHandler(NettyCloseEvent.class, closeEvent -> + this.server.registerHandler(NettyCloseEvent.class, closeEvent -> { ServerPlayerState dhPlayer = this.playersByConnection.remove(closeEvent.getConnection()); if (dhPlayer != null) @@ -87,6 +86,19 @@ public class RemotePlayerConnectionHandler implements Closeable dhPlayer.connection = null; } }); + //endregion + + //region Plugin channel + this.pluginChannelHandler.registerHandler(PluginHelloMessage.class, msg -> { + // TODO tell to connect somewhere + int i = 0; + }); + //endregion + } + + public void handlePluginMessage(IServerPlayerWrapper player, ByteBuf buffer) + { + this.pluginChannelHandler.decodeAndHandle(player, buffer); } private void onConfigChanged() @@ -171,7 +183,7 @@ public class RemotePlayerConnectionHandler implements Closeable public void close() { this.configChangeListener.close(); - this.eventSource.close(); + this.pluginChannelHandler.close(); this.server().close(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelHandler.java index a7c5705f3..b8c48a15d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelHandler.java @@ -41,7 +41,7 @@ public class PluginChannelHandler extends NetworkEventSource messages = new ArrayList<>(); this.messageDecoder.decode(byteBuf, messages); - for (Object msg : messages) + for (Object msgObj : messages) { - this.handleMessage((PluginChannelMessage) msg); + PluginChannelMessage msg = (PluginChannelMessage) msgObj; + msg.serverPlayer = serverPlayer; + this.handleMessage(msg); } } catch (Throwable e) @@ -73,7 +75,6 @@ public class PluginChannelHandler extends NetworkEventSource levels; public final LocalSaveStructure saveStructure; + private final RemotePlayerConnectionHandler remotePlayerConnectionHandler; @@ -52,8 +52,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld this.saveStructure = new LocalSaveStructure(); this.levels = new HashMap<>(); - NettyServer nettyServer = new NettyServer(Config.Client.Advanced.Multiplayer.ServerNetworking.serverPort.get()); - this.remotePlayerConnectionHandler = new RemotePlayerConnectionHandler(nettyServer); + this.remotePlayerConnectionHandler = new RemotePlayerConnectionHandler(); LOGGER.info("Started "+DhServerWorld.class.getSimpleName()+" of type "+this.environment); } @@ -79,6 +78,10 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld this.getLevel(dest).addPlayer(player); this.getLevel(origin).removePlayer(player); } + public void handlePluginMessage(IServerPlayerWrapper player, ByteBuf buffer) + { + this.remotePlayerConnectionHandler.handlePluginMessage(player, buffer); + } @Override public DhServerLevel getOrLoadLevel(@NotNull ILevelWrapper wrapper) From 3362570123b9833cbff0629aca5501668a409885 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 16 Mar 2024 12:28:38 +0500 Subject: [PATCH 109/205] Add thread pool task buildup limiter --- .../com/seibel/distanthorizons/core/config/Config.java | 10 ++++++++++ .../client/AbstractFullDataRequestQueue.java | 8 ++++++++ .../resources/assets/distanthorizons/lang/en_us.json | 2 ++ 3 files changed, 20 insertions(+) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 6c5df861c..90196a514 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -896,6 +896,16 @@ public class Config + "") .build(); + public static ConfigEntry maxThreadPoolTasks = new ConfigEntry.Builder() + .setMinDefaultMax(0, 300, 10000) + .comment("" + + "Pauses the generation if amount of thread pool tasks is too high. \n" + + "Decrease this value if LODs don't show up for too long.\n" + + "Increase this value if thread pool tasks often get drained completely.\n" + + "Setting this value to 0 will allow thread pool tasks to build up indefinitely.\n" + + "") + .build(); + public static ConfigUIComment generationSectionNote = new ConfigUIComment(); public static ConfigEntry generationRequestRCLimit = new ConfigEntry.Builder() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index a0c7e1b55..fbe9b9893 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -20,6 +20,7 @@ import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateLimiter; +import com.seibel.distanthorizons.core.util.threading.ThreadPools; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import io.netty.channel.ChannelException; import org.apache.logging.log4j.LogManager; @@ -114,6 +115,13 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, return false; } + ThreadPoolExecutor fileHandlerPool = ThreadPools.getFileHandlerExecutor(); + int maxThreadPoolTasks = Config.Client.Advanced.Multiplayer.ServerNetworking.maxThreadPoolTasks.get(); + if (fileHandlerPool != null && maxThreadPoolTasks != 0 && fileHandlerPool.getQueue().size() > maxThreadPoolTasks) + { + return false; + } + while (this.getWaitingTaskCount() > this.getInProgressTaskCount() && this.getInProgressTaskCount() < this.getRequestConcurrencyLimit() && this.pendingTasksSemaphore.tryAcquire()) diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index 677155555..7885ba666 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -358,6 +358,8 @@ "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableServerNetworking.@tooltip": "§6Attention:§r this feature is not fully implemented. \n\nIf true Distant Horizons will attempt to communicate with the connected \nserver in order to load LODs outside your vanilla render distance. \n\nNote: This requires DH to be installed on the server in order to function.", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.serverPort": "Server Port", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.serverPort.@tooltip": "The port on the server that's used for sending LOD data.", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.maxThreadPoolTasks": "Max Thread Pool Tasks", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.maxThreadPoolTasks.@tooltip": "Pauses the generation if amount of thread pool tasks is too high. \nDecrease this value if LODs don't show up for too long.\nIncrease this value if thread pool tasks often get drained completely.\nSetting this value to 0 will allow thread pool tasks to build up indefinitely.", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationSectionNote": " \u25cf Generation", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestRCLimit": "Gen Task Rate/Concurrency Limit", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestRCLimit.@tooltip": "Limits the amount of generation requests sent by client and processed by server.", From af2f0e85824b8774573bd4095e2bea65a8df4747 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 18 Mar 2024 21:37:58 +0500 Subject: [PATCH 110/205] Fix Forge --- .../core/network/plugin/PluginChannelHandler.java | 5 +---- .../com/seibel/distanthorizons/core/world/DhClientWorld.java | 1 - .../core/wrapperInterfaces/misc/IPluginPacketSender.java | 4 +++- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelHandler.java index b8c48a15d..94ccbf01a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelHandler.java @@ -11,7 +11,6 @@ import com.seibel.distanthorizons.core.network.protocol.plugin.PluginMessageEnco import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import io.netty.buffer.ByteBuf; -import io.netty.buffer.PooledByteBufAllocator; import org.apache.logging.log4j.LogManager; import org.jetbrains.annotations.Nullable; @@ -73,9 +72,7 @@ public class PluginChannelHandler extends NetworkEventSource this.messageEncoder.encode(message, buffer)); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java index c75c41192..b28da2556 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java @@ -26,7 +26,6 @@ import com.seibel.distanthorizons.core.level.DhClientLevel; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; import com.seibel.distanthorizons.core.network.netty.NettyClient; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelHandler; import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.util.objects.EventLoop; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IPluginPacketSender.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IPluginPacketSender.java index 3e51771f4..20321562b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IPluginPacketSender.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IPluginPacketSender.java @@ -4,8 +4,10 @@ import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindab import io.netty.buffer.ByteBuf; import org.jetbrains.annotations.Nullable; +import java.util.function.Consumer; + public interface IPluginPacketSender extends IBindable { - void sendPluginPacket(@Nullable IServerPlayerWrapper serverPlayer, ByteBuf buffer); + void sendPluginPacket(@Nullable IServerPlayerWrapper serverPlayer, Consumer encoder); } From 0d355f675af13078d79b133c950646b1b2426e0b Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 26 Mar 2024 00:30:57 +0500 Subject: [PATCH 111/205] Incomplete --- .../core/api/internal/ClientApi.java | 6 +- .../api/internal/ClientPluginChannelApi.java | 18 ++++- .../distanthorizons/core/config/Config.java | 20 ++++++ .../client/ClientNetworkState.java | 22 ++----- .../server/RemotePlayerConnectionHandler.java | 66 ++++++++++++++++--- .../multiplayer/server/ServerPlayerState.java | 4 +- .../plugin/ServerConnectInfoMessage.java | 18 +++-- .../core/network/netty/NettyClient.java | 24 ++++--- .../network/plugin/PluginChannelHandler.java | 9 +-- .../core/world/DhClientWorld.java | 20 ++---- .../misc/IPluginPacketSender.java | 3 +- .../misc/IServerPlayerWrapper.java | 3 + 12 files changed, 148 insertions(+), 65 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index 683945a50..45ec0b42c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -32,6 +32,7 @@ import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.logging.ConfigBasedSpamLogger; import com.seibel.distanthorizons.core.logging.SpamReducedLogger; +import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.render.DhApiRenderProxy; import com.seibel.distanthorizons.core.render.glObject.GLProxy; @@ -122,7 +123,8 @@ public class ClientApi // firing after clientLevelLoadEvent // TODO if level has prepped to load it should fire level load event - SharedApi.setDhWorld(new DhClientWorld()); + DhClientWorld world = new DhClientWorld(); + SharedApi.setDhWorld(world); LOGGER.info("Loading [" + this.waitingClientLevels.size() + "] waiting client level wrappers."); @@ -133,7 +135,7 @@ public class ClientApi this.waitingClientLevels.clear(); - this.pluginChannelApi.onJoin(); + this.pluginChannelApi.onJoin(world.networkState); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index 1aee06fbf..f815586e4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -5,6 +5,7 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager; import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; +import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; import com.seibel.distanthorizons.core.network.messages.plugin.PluginCloseEvent; import com.seibel.distanthorizons.core.network.messages.plugin.CurrentLevelKeyMessage; import com.seibel.distanthorizons.core.network.messages.plugin.PluginHelloMessage; @@ -31,6 +32,9 @@ public class ClientPluginChannelApi implements AutoCloseable private final Consumer levelUnloadHandler; private final Consumer multiverseLevelLoadHandler; + @Nullable + private ClientNetworkState networkState; + public boolean allowLoadingLevel() { @@ -49,9 +53,10 @@ public class ClientPluginChannelApi implements AutoCloseable this.channelHandler.registerHandler(PluginCloseEvent.class, this::onClose); } - public void onJoin() + public void onJoin(@Nullable ClientNetworkState networkState) { - this.channelHandler.sendMessage(new PluginHelloMessage()); + this.networkState = networkState; + this.channelHandler.sendMessageClient(new PluginHelloMessage()); } private void onCurrentLevelKeyMessage(CurrentLevelKeyMessage msg) @@ -78,7 +83,14 @@ public class ClientPluginChannelApi implements AutoCloseable private void onServerConnectInfoMessage(ServerConnectInfoMessage msg) { - // TODO + if (this.networkState != null) + { + this.networkState.getClient().resetAndConnectTo( + msg.ipOverride != null + ? msg.ipOverride + : MC.getCurrentServerIp().split(":")[0], + msg.port); + } } public void onClientLevelUnload() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 6c5df861c..22eb3724b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -880,12 +880,32 @@ public class Config .build(); public static ConfigEntry serverPort = new ConfigEntry.Builder() .setServersideShortName("serverPort") + .setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) .setMinDefaultMax(1, 25049, 65535) .comment("" + "The port on the server that's used for sending LOD data." + "") .build(); + public static ConfigEntry connectIpOverride = new ConfigEntry.Builder() + .setServersideShortName("connectIpOverride") + .setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) + .set("") + .comment("" + + "Overrides the IP address that is sent to the client for connecting to DH server." + + "Leave empty to let client use the IP used to connect to the MC server." + + "") + .build(); + public static ConfigEntry connectPortOverride = new ConfigEntry.Builder() + .setServersideShortName("connectPortOverride") + .setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) + .setMinDefaultMax(0, 0, 65535) + .comment("" + + "Overrides the port that is sent to the client for connecting to DH server." + + "Set to 0 to use port from serverPort." + + "") + .build(); + public static ConfigEntry rateLimitHitTolerance = new ConfigEntry.Builder() .setServersideShortName("rateLimitHitTolerance") .setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index 71ec533b2..deb834db5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -1,6 +1,7 @@ package com.seibel.distanthorizons.core.multiplayer.client; import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; @@ -12,6 +13,7 @@ import com.seibel.distanthorizons.core.network.messages.netty.base.NettyCloseEve import com.seibel.distanthorizons.core.network.messages.netty.base.HelloMessage; import com.seibel.distanthorizons.core.network.messages.netty.session.PlayerUUIDMessage; import com.seibel.distanthorizons.core.network.messages.netty.session.RemotePlayerConfigMessage; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import org.apache.logging.log4j.LogManager; import java.io.Closeable; @@ -22,9 +24,11 @@ public class ClientNetworkState implements Closeable { protected static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); + private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); + + private final NettyClient client = new NettyClient(); + private final UUID playerUUID = MC_CLIENT.getPlayerUUID(); - private final NettyClient client; - private final UUID playerUUID; public MultiplayerConfig config = new MultiplayerConfig(); private volatile boolean configReceived = false; @@ -41,20 +45,8 @@ public class ClientNetworkState implements Closeable /** * Constructs a new instance. - * - * @param nettyClient Client to use. It is assumed that this client will be at full control by this instance. - * @param playerUUID UUID of a player connected. */ - public ClientNetworkState(NettyClient nettyClient, UUID playerUUID) - { - this.client = nettyClient; - this.playerUUID = playerUUID; - - this.registerNetworkHandlers(); - this.client.connect(); - } - - private void registerNetworkHandlers() + public ClientNetworkState() { this.client.registerHandler(HelloMessage.class, helloMessage -> { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java index d35cb5f05..fe92cc14d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java @@ -4,21 +4,23 @@ import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.google.common.collect.Maps; import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener; import com.seibel.distanthorizons.core.config.types.ConfigEntry; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; -import com.seibel.distanthorizons.core.network.messages.plugin.PluginHelloMessage; -import com.seibel.distanthorizons.core.network.netty.INettyConnection; -import com.seibel.distanthorizons.core.network.netty.NettyServer; import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; +import com.seibel.distanthorizons.core.network.messages.netty.ILevelRelatedMessage; import com.seibel.distanthorizons.core.network.messages.netty.base.AckMessage; import com.seibel.distanthorizons.core.network.messages.netty.base.NettyCloseEvent; -import com.seibel.distanthorizons.core.network.messages.netty.ILevelRelatedMessage; import com.seibel.distanthorizons.core.network.messages.netty.session.PlayerUUIDMessage; import com.seibel.distanthorizons.core.network.messages.netty.session.RemotePlayerConfigMessage; -import com.seibel.distanthorizons.core.network.netty.TrackableNettyMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.PluginHelloMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.ServerConnectInfoMessage; +import com.seibel.distanthorizons.core.network.netty.INettyConnection; import com.seibel.distanthorizons.core.network.netty.NettyMessage; +import com.seibel.distanthorizons.core.network.netty.NettyServer; +import com.seibel.distanthorizons.core.network.netty.TrackableNettyMessage; import com.seibel.distanthorizons.core.network.plugin.PluginChannelHandler; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; @@ -26,23 +28,31 @@ import io.netty.buffer.ByteBuf; import org.jetbrains.annotations.Nullable; import java.io.Closeable; +import java.net.InetAddress; +import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import java.util.function.BiConsumer; import java.util.function.Consumer; +import static com.seibel.distanthorizons.core.config.Config.Client.Advanced.Multiplayer.ServerNetworking; + public class RemotePlayerConnectionHandler implements Closeable { + private static final boolean DEBUG_ALWAYS_USE_OVERRIDES = true; + private static final ConfigEntry GENERATE_MULTIPLE_DIMENSIONS_CONFIG = Config.Client.Advanced.Multiplayer.ServerNetworking.generateMultipleDimensions; - private final NettyServer server = new NettyServer(Config.Client.Advanced.Multiplayer.ServerNetworking.serverPort.get()); + private final NettyServer server = new NettyServer(ServerNetworking.serverPort.get()); private final ConcurrentHashMap playersByUUID = new ConcurrentHashMap<>(); private final BiMap playersByConnection = Maps.synchronizedBiMap(HashBiMap.create()); private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::onConfigChanged); private final PluginChannelHandler pluginChannelHandler = new PluginChannelHandler(); - // TODO port change listener + private final ConfigChangeListener portChangeListener = new ConfigChangeListener<>(ServerNetworking.serverPort, this::onServerPortChanged); + private final ConfigChangeListener connectIpOverrideChangeListener = new ConfigChangeListener<>(ServerNetworking.connectIpOverride, this::broadcastConnectInfo); + private final ConfigChangeListener connectPortOverrideChangeListener = new ConfigChangeListener<>(ServerNetworking.serverPort, this::broadcastConnectInfo); public NettyServer server() { return this.server; } @@ -90,8 +100,7 @@ public class RemotePlayerConnectionHandler implements Closeable //region Plugin channel this.pluginChannelHandler.registerHandler(PluginHelloMessage.class, msg -> { - // TODO tell to connect somewhere - int i = 0; + this.sendConnectInfo(msg.serverPlayer); }); //endregion } @@ -109,6 +118,39 @@ public class RemotePlayerConnectionHandler implements Closeable } } + private void onServerPortChanged(int ignored) + { + // stop server + // restart server on new port + this.broadcastConnectInfo(null); + } + private void broadcastConnectInfo(@Nullable Object ignored) + { + for (ServerPlayerState serverPlayerState : this.getConnectedPlayers()) + { + this.sendConnectInfo(serverPlayerState.serverPlayer); + } + } + private void sendConnectInfo(IServerPlayerWrapper serverPlayer) + { + String ipOverride = ServerNetworking.connectIpOverride.get(); + int listenPort = ServerNetworking.serverPort.get(); + int portOverride = ServerNetworking.connectPortOverride.get(); + + // IP/port overrides are intended for using with port forwarding services, + // and LAN clients are unlikely to need to hop through internet + InetAddress ip = ((InetSocketAddress) serverPlayer.getRemoteAddress()).getAddress(); + boolean isLanPlayer = !DEBUG_ALWAYS_USE_OVERRIDES && + (ip.isLoopbackAddress() || + ip.isLinkLocalAddress() || + ip.isSiteLocalAddress()); + + this.pluginChannelHandler.sendMessageServer(serverPlayer, new ServerConnectInfoMessage( + !isLanPlayer && !ipOverride.isEmpty() ? ipOverride : null, + !isLanPlayer && portOverride != 0 ? portOverride : listenPort + )); + } + public Consumer connectedPlayersOnly(BiConsumer next) { return msg -> @@ -182,8 +224,12 @@ public class RemotePlayerConnectionHandler implements Closeable @Override public void close() { - this.configChangeListener.close(); + this.portChangeListener.close(); + this.connectIpOverrideChangeListener.close(); + this.connectPortOverrideChangeListener.close(); this.pluginChannelHandler.close(); + + this.configChangeListener.close(); this.server().close(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index bab53bdb6..e6ad7ce0e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -15,8 +15,8 @@ import java.util.concurrent.ConcurrentHashMap; import static com.seibel.distanthorizons.core.config.Config.Client.Advanced.Multiplayer.ServerNetworking; public class ServerPlayerState -{ - public IServerPlayerWrapper serverPlayer; +{ + public final IServerPlayerWrapper serverPlayer; public INettyConnection connection; @NotNull diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/ServerConnectInfoMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/ServerConnectInfoMessage.java index f8f391339..fd1186ef4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/ServerConnectInfoMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/ServerConnectInfoMessage.java @@ -3,29 +3,35 @@ package com.seibel.distanthorizons.core.network.messages.plugin; import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; import io.netty.buffer.ByteBuf; +import javax.annotation.Nullable; + public class ServerConnectInfoMessage extends PluginChannelMessage { - public String ipAddress; - public short port; + @Nullable + public String ipOverride; + public int port; public ServerConnectInfoMessage() { } - public ServerConnectInfoMessage(String ipAddress, short port) + public ServerConnectInfoMessage(@Nullable String ipOverride, int port) { - this.ipAddress = ipAddress; + this.ipOverride = ipOverride; this.port = port; } @Override public void encode(ByteBuf out) { - this.writeString(this.ipAddress, out); + if (this.writeOptional(out, this.ipOverride)) + { + this.writeString(this.ipOverride, out); + } out.writeShort(this.port); } @Override public void decode(ByteBuf in) { - this.ipAddress = this.readString(in); + this.ipOverride = this.readOptional(in, () -> this.readString(in)); this.port = in.readShort(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java index 71ab47766..a7a324100 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java @@ -64,7 +64,7 @@ public class NettyClient extends NettyEventSource implements INettyConnection, A private static final int RECONNECTION_DELAY_SEC = 5; public static final int RECONNECTION_ATTEMPTS = 3; - private final InetSocketAddress address; + private InetSocketAddress address; @Nullable private Throwable closeReason; @@ -93,13 +93,7 @@ public class NettyClient extends NettyEventSource implements INettyConnection, A public int getReconnectionAttemptsLeft() { return this.reconnectionAttemptsLeft; } - public NettyClient(String host, int port) - { - this.address = new InetSocketAddress(host, port); - this.registerHandlers(); - } - - private void registerHandlers() + public NettyClient() { this.registerHandler(CloseReasonMessage.class, closeReasonMessage -> { @@ -119,6 +113,20 @@ public class NettyClient extends NettyEventSource implements INettyConnection, A }); } + public void resetAndConnectTo(String host, int port) + { + this.connectionState.set(EConnectionState.INITIAL); + this.address = new InetSocketAddress(host, port); + + if (this.channel != null) + { + this.channel.close().syncUninterruptibly(); + } + + this.reconnectionAttemptsLeft = RECONNECTION_ATTEMPTS; + this.connect(); + } + public void connect() { if (!this.connectionState.compareAndSet(EConnectionState.INITIAL, EConnectionState.OPEN)) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelHandler.java index 94ccbf01a..000639e2d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelHandler.java @@ -56,6 +56,7 @@ public class PluginChannelHandler extends NetworkEventSource this.messageEncoder.encode(message, buffer)); } - public void sendMessage(@Nullable IServerPlayerWrapper serverPlayer, PluginChannelMessage message) + public void sendMessageServer(@Nullable IServerPlayerWrapper serverPlayer, PluginChannelMessage message) { - this.packetSender.sendPluginPacket(serverPlayer, buffer -> this.messageEncoder.encode(message, buffer)); + this.packetSender.sendPluginPacketServer(serverPlayer, buffer -> this.messageEncoder.encode(message, buffer)); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java index b28da2556..1dfc98d35 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java @@ -34,18 +34,17 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import org.jetbrains.annotations.NotNull; import javax.annotation.CheckForNull; +import javax.annotation.Nullable; import java.io.File; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld { - private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); - private final ConcurrentHashMap levels; public final ClientOnlySaveStructure saveStructure; - @CheckForNull - private final ClientNetworkState networkState; + @Nullable + public final ClientNetworkState networkState; public ExecutorService dhTickerThread = ThreadUtil.makeSingleThreadPool("Client World Ticker Thread"); public EventLoop eventLoop = new EventLoop(this.dhTickerThread, this::_clientTick); @@ -63,16 +62,9 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld this.saveStructure = new ClientOnlySaveStructure(); this.levels = new ConcurrentHashMap<>(); - if (Config.Client.Advanced.Multiplayer.ServerNetworking.enableServerNetworking.get()) - { - // TODO per server configs - NettyClient nettyClient = new NettyClient(MC_CLIENT.getCurrentServerIp().split(":")[0], Config.Client.Advanced.Multiplayer.ServerNetworking.serverPort.get()); - this.networkState = new ClientNetworkState(nettyClient, MC_CLIENT.getPlayerUUID()); - } - else - { - this.networkState = null; - } + this.networkState = Config.Client.Advanced.Multiplayer.ServerNetworking.enableServerNetworking.get() + ? new ClientNetworkState() + : null; LOGGER.info("Started DhWorld of type " + this.environment); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IPluginPacketSender.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IPluginPacketSender.java index 20321562b..1e4672406 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IPluginPacketSender.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IPluginPacketSender.java @@ -8,6 +8,7 @@ import java.util.function.Consumer; public interface IPluginPacketSender extends IBindable { - void sendPluginPacket(@Nullable IServerPlayerWrapper serverPlayer, Consumer encoder); + void sendPluginPacketClient(Consumer encoder); + void sendPluginPacketServer(IServerPlayerWrapper serverPlayer, Consumer encoder); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java index d96380192..e162a8059 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java @@ -23,6 +23,7 @@ import com.seibel.distanthorizons.api.interfaces.IDhApiUnsafeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import com.seibel.distanthorizons.coreapi.util.math.Vec3d; +import java.net.SocketAddress; import java.util.UUID; public interface IServerPlayerWrapper extends IDhApiUnsafeWrapper @@ -34,4 +35,6 @@ public interface IServerPlayerWrapper extends IDhApiUnsafeWrapper Vec3d getPosition(); int getViewDistance(); + + SocketAddress getRemoteAddress(); } From 682970912331b23b77dd070dc35e57bf8fa1fbb1 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 30 Mar 2024 23:40:05 +0500 Subject: [PATCH 112/205] Fix reconnection logic --- .../client/ClientNetworkState.java | 2 +- .../server/RemotePlayerConnectionHandler.java | 13 ++++--- .../core/network/netty/NettyClient.java | 34 ++++++++++++------- .../minecraft/IMinecraftSharedWrapper.java | 4 +++ 4 files changed, 34 insertions(+), 19 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index deb834db5..ef0b5b249 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -84,7 +84,7 @@ public class ClientNetworkState implements Closeable { return new String[]{ this.client.getRemoteAddress() != null - ? "Connected, ready: " + this.isReady() + ? (this.isReady() ? "Connected to server" : "Connecting to server...") : MessageFormat.format("Disconnected, attempts left: {0} / {1}", this.client.getReconnectionAttemptsLeft(), NettyClient.RECONNECTION_ATTEMPTS) }; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java index fe92cc14d..247e9d121 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java @@ -6,6 +6,7 @@ import com.google.common.collect.Maps; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener; import com.seibel.distanthorizons.core.config.types.ConfigEntry; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; @@ -23,6 +24,7 @@ import com.seibel.distanthorizons.core.network.netty.NettyServer; import com.seibel.distanthorizons.core.network.netty.TrackableNettyMessage; import com.seibel.distanthorizons.core.network.plugin.PluginChannelHandler; import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import io.netty.buffer.ByteBuf; import org.jetbrains.annotations.Nullable; @@ -40,8 +42,9 @@ import static com.seibel.distanthorizons.core.config.Config.Client.Advanced.Mult public class RemotePlayerConnectionHandler implements Closeable { - private static final boolean DEBUG_ALWAYS_USE_OVERRIDES = true; + private static final boolean DEBUG_ENABLE_OVERRIDES_IN_LAN = false; + private static final IMinecraftSharedWrapper MC_SERVER = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class); private static final ConfigEntry GENERATE_MULTIPLE_DIMENSIONS_CONFIG = Config.Client.Advanced.Multiplayer.ServerNetworking.generateMultipleDimensions; private final NettyServer server = new NettyServer(ServerNetworking.serverPort.get()); @@ -52,7 +55,7 @@ public class RemotePlayerConnectionHandler implements Closeable private final PluginChannelHandler pluginChannelHandler = new PluginChannelHandler(); private final ConfigChangeListener portChangeListener = new ConfigChangeListener<>(ServerNetworking.serverPort, this::onServerPortChanged); private final ConfigChangeListener connectIpOverrideChangeListener = new ConfigChangeListener<>(ServerNetworking.connectIpOverride, this::broadcastConnectInfo); - private final ConfigChangeListener connectPortOverrideChangeListener = new ConfigChangeListener<>(ServerNetworking.serverPort, this::broadcastConnectInfo); + private final ConfigChangeListener connectPortOverrideChangeListener = new ConfigChangeListener<>(ServerNetworking.connectPortOverride, this::broadcastConnectInfo); public NettyServer server() { return this.server; } @@ -126,9 +129,9 @@ public class RemotePlayerConnectionHandler implements Closeable } private void broadcastConnectInfo(@Nullable Object ignored) { - for (ServerPlayerState serverPlayerState : this.getConnectedPlayers()) + for (IServerPlayerWrapper serverPlayer : MC_SERVER.getPlayerList()) { - this.sendConnectInfo(serverPlayerState.serverPlayer); + this.sendConnectInfo(serverPlayer); } } private void sendConnectInfo(IServerPlayerWrapper serverPlayer) @@ -140,7 +143,7 @@ public class RemotePlayerConnectionHandler implements Closeable // IP/port overrides are intended for using with port forwarding services, // and LAN clients are unlikely to need to hop through internet InetAddress ip = ((InetSocketAddress) serverPlayer.getRemoteAddress()).getAddress(); - boolean isLanPlayer = !DEBUG_ALWAYS_USE_OVERRIDES && + boolean isLanPlayer = !DEBUG_ENABLE_OVERRIDES_IN_LAN && (ip.isLoopbackAddress() || ip.isLinkLocalAddress() || ip.isSiteLocalAddress()); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java index a7a324100..df0513450 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java @@ -38,6 +38,7 @@ import java.net.InetSocketAddress; import java.util.EnumSet; import java.util.Set; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; public class NettyClient extends NettyEventSource implements INettyConnection, AutoCloseable @@ -88,9 +89,9 @@ public class NettyClient extends NettyEventSource implements INettyConnection, A private Channel channel; - private int reconnectionAttemptsLeft = RECONNECTION_ATTEMPTS; + private final AtomicInteger reconnectionAttemptsLeft = new AtomicInteger(RECONNECTION_ATTEMPTS); /** Returns the amount of reconnections the client will attempt to perform before giving up. */ - public int getReconnectionAttemptsLeft() { return this.reconnectionAttemptsLeft; } + public int getReconnectionAttemptsLeft() { return this.reconnectionAttemptsLeft.get(); } public NettyClient() @@ -115,7 +116,10 @@ public class NettyClient extends NettyEventSource implements INettyConnection, A public void resetAndConnectTo(String host, int port) { - this.connectionState.set(EConnectionState.INITIAL); + if (this.connectionState.getAndUpdate(state -> state != EConnectionState.CLOSED ? EConnectionState.INITIAL : state) == EConnectionState.CLOSED) + { + return; + } this.address = new InetSocketAddress(host, port); if (this.channel != null) @@ -123,7 +127,7 @@ public class NettyClient extends NettyEventSource implements INettyConnection, A this.channel.close().syncUninterruptibly(); } - this.reconnectionAttemptsLeft = RECONNECTION_ATTEMPTS; + this.reconnectionAttemptsLeft.set(RECONNECTION_ATTEMPTS); this.connect(); } @@ -157,21 +161,25 @@ public class NettyClient extends NettyEventSource implements INettyConnection, A if (this.connectionState.get() == EConnectionState.OPEN) { - this.reconnectionAttemptsLeft--; - LOGGER.info("Reconnection attempts left: [" + this.reconnectionAttemptsLeft + "] of [" + RECONNECTION_ATTEMPTS + "]."); + int reconnectionAttemptsLeft = this.reconnectionAttemptsLeft.decrementAndGet(); + LOGGER.info("Reconnection attempts left: [" + this.reconnectionAttemptsLeft.get() + "] of [" + RECONNECTION_ATTEMPTS + "]."); - if (this.reconnectionAttemptsLeft != 0) + if (reconnectionAttemptsLeft != 0) { - this.connectionState.set(EConnectionState.RECONNECTING); - this.workerGroup.schedule(() -> + if (this.connectionState.compareAndSet(EConnectionState.OPEN, EConnectionState.RECONNECTING)) { - this.connectionState.set(EConnectionState.INITIAL); - this.connect(); - }, RECONNECTION_DELAY_SEC, TimeUnit.SECONDS); + this.workerGroup.schedule(() -> + { + if (this.connectionState.compareAndSet(EConnectionState.RECONNECTING, EConnectionState.INITIAL)) + { + this.connect(); + } + }, RECONNECTION_DELAY_SEC, TimeUnit.SECONDS); + } } else { - this.connectionState.set(EConnectionState.GOT_CLOSE_REASON); + this.connectionState.compareAndSet(EConnectionState.OPEN, EConnectionState.GOT_CLOSE_REASON); } } }); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftSharedWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftSharedWrapper.java index 7f7594c8b..b7038e26c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftSharedWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftSharedWrapper.java @@ -19,9 +19,11 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.minecraft; +import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable; import java.io.File; +import java.util.List; //TODO: Maybe have IMCClientWrapper & IMCDedicatedWrapper extend this interface??? public interface IMinecraftSharedWrapper extends IBindable @@ -30,4 +32,6 @@ public interface IMinecraftSharedWrapper extends IBindable File getInstallationDirectory(); + List getPlayerList(); + } From ff7720a8d6106ba19f3acba5f10a8d404b7586f1 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 1 Apr 2024 00:07:32 +0500 Subject: [PATCH 113/205] Adjust server port change behavior --- .../seibel/distanthorizons/core/config/Config.java | 6 ++++++ .../server/RemotePlayerConnectionHandler.java | 11 ++++++----- .../core/network/netty/NettyServer.java | 2 +- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 22eb3724b..d237f848a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -878,6 +878,12 @@ public class Config + "Note: This requires DH to be installed on the server in order to function. \n" + "") .build(); + + /** + * Server port.
+ * Do not use this to retrieve the current server's port; + * Instead, use {@link com.seibel.distanthorizons.core.network.netty.NettyServer#port NettyServer#port}. + */ public static ConfigEntry serverPort = new ConfigEntry.Builder() .setServersideShortName("serverPort") .setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java index 247e9d121..66150390c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java @@ -8,6 +8,7 @@ import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener; import com.seibel.distanthorizons.core.config.types.ConfigEntry; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.level.DhServerLevel; +import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; @@ -27,6 +28,7 @@ import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import io.netty.buffer.ByteBuf; +import org.apache.logging.log4j.LogManager; import org.jetbrains.annotations.Nullable; import java.io.Closeable; @@ -44,6 +46,8 @@ public class RemotePlayerConnectionHandler implements Closeable { private static final boolean DEBUG_ENABLE_OVERRIDES_IN_LAN = false; + private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), + () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); private static final IMinecraftSharedWrapper MC_SERVER = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class); private static final ConfigEntry GENERATE_MULTIPLE_DIMENSIONS_CONFIG = Config.Client.Advanced.Multiplayer.ServerNetworking.generateMultipleDimensions; @@ -123,9 +127,7 @@ public class RemotePlayerConnectionHandler implements Closeable private void onServerPortChanged(int ignored) { - // stop server - // restart server on new port - this.broadcastConnectInfo(null); + LOGGER.warn("Server port change requires a server restart to take effect."); } private void broadcastConnectInfo(@Nullable Object ignored) { @@ -137,7 +139,6 @@ public class RemotePlayerConnectionHandler implements Closeable private void sendConnectInfo(IServerPlayerWrapper serverPlayer) { String ipOverride = ServerNetworking.connectIpOverride.get(); - int listenPort = ServerNetworking.serverPort.get(); int portOverride = ServerNetworking.connectPortOverride.get(); // IP/port overrides are intended for using with port forwarding services, @@ -150,7 +151,7 @@ public class RemotePlayerConnectionHandler implements Closeable this.pluginChannelHandler.sendMessageServer(serverPlayer, new ServerConnectInfoMessage( !isLanPlayer && !ipOverride.isEmpty() ? ipOverride : null, - !isLanPlayer && portOverride != 0 ? portOverride : listenPort + !isLanPlayer && portOverride != 0 ? portOverride : this.server.port )); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyServer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyServer.java index 613308386..ef720fd14 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyServer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyServer.java @@ -48,7 +48,7 @@ public class NettyServer extends NettyEventSource implements AutoCloseable private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); - private final int port; + public final int port; private final EventLoopGroup bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("DH-Network - Server Boss Thread")); private final EventLoopGroup workerGroup = new NioEventLoopGroup(0, new DefaultThreadFactory("DH-Network - Server Worker Thread")); From bdc4fa4477456062ab36c7152d8ddc9e6e5ff156 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 1 Apr 2024 00:55:15 +0500 Subject: [PATCH 114/205] Fix errors when on vanilla server --- .../core/multiplayer/client/ClientNetworkState.java | 5 +++++ .../distanthorizons/core/network/netty/NettyClient.java | 9 ++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index ef0b5b249..9d359f872 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -80,6 +80,11 @@ public class ClientNetworkState implements Closeable private String[] f3Log() { + if (!this.client.isActive()) + { + return new String[]{"Waiting for connection info..."}; + } + if (!this.client.isClosed()) { return new String[]{ diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java index df0513450..8f6e99884 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java @@ -59,6 +59,8 @@ public class NettyClient extends NettyEventSource implements INettyConnection, A EConnectionState.CLOSED ); private final AtomicReference connectionState = new AtomicReference<>(EConnectionState.INITIAL); + /** Indicates whether the client is working. */ + public boolean isActive() { return this.connectionState.get() != EConnectionState.INITIAL && !this.isClosed(); } /** Indicates whether the client is closed(-ing) and should not be used. */ public boolean isClosed() { return closedStates.contains(this.connectionState.get()); } @@ -188,6 +190,10 @@ public class NettyClient extends NettyEventSource implements INettyConnection, A @Override public ChannelHandlerContext getChannelContext() { + if (this.channel == null) + { + return null; + } return this.channel.pipeline().context(NettyMessageHandler.class); } @@ -200,7 +206,8 @@ public class NettyClient extends NettyEventSource implements INettyConnection, A @Override public void close() { - if (this.connectionState.getAndSet(EConnectionState.CLOSED) == EConnectionState.CLOSED) + EConnectionState stateChangeResult = this.connectionState.getAndSet(EConnectionState.CLOSED); + if (stateChangeResult == EConnectionState.CLOSED || stateChangeResult == EConnectionState.INITIAL) { return; } From 1cd2f75dd24cb242483d3dcdc050d26bf8e1e22a Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 1 Apr 2024 21:52:20 +0500 Subject: [PATCH 115/205] Fix compilation --- .../core/multiplayer/client/ClientNetworkState.java | 4 ++-- .../distanthorizons/core/network/netty/NettyClient.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index 9d359f872..9af8ded5e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -80,9 +80,9 @@ public class ClientNetworkState implements Closeable private String[] f3Log() { - if (!this.client.isActive()) + if (!this.client.isInitialized()) { - return new String[]{"Waiting for connection info..."}; + return new String[]{"Did not receive connection info yet..."}; } if (!this.client.isClosed()) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java index 8f6e99884..7655e8e85 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java @@ -60,7 +60,7 @@ public class NettyClient extends NettyEventSource implements INettyConnection, A ); private final AtomicReference connectionState = new AtomicReference<>(EConnectionState.INITIAL); /** Indicates whether the client is working. */ - public boolean isActive() { return this.connectionState.get() != EConnectionState.INITIAL && !this.isClosed(); } + public boolean isInitialized() { return this.connectionState.get() != EConnectionState.INITIAL; } /** Indicates whether the client is closed(-ing) and should not be used. */ public boolean isClosed() { return closedStates.contains(this.connectionState.get()); } From 8485d1585a57158e04f178697e8ae9bd0d5b1cb7 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 2 Apr 2024 22:27:36 +0500 Subject: [PATCH 116/205] Remove localhost from check --- .../multiplayer/server/RemotePlayerConnectionHandler.java | 5 +---- .../main/resources/assets/distanthorizons/lang/en_us.json | 2 -- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java index 66150390c..ab541fde8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java @@ -144,10 +144,7 @@ public class RemotePlayerConnectionHandler implements Closeable // IP/port overrides are intended for using with port forwarding services, // and LAN clients are unlikely to need to hop through internet InetAddress ip = ((InetSocketAddress) serverPlayer.getRemoteAddress()).getAddress(); - boolean isLanPlayer = !DEBUG_ENABLE_OVERRIDES_IN_LAN && - (ip.isLoopbackAddress() || - ip.isLinkLocalAddress() || - ip.isSiteLocalAddress()); + boolean isLanPlayer = !DEBUG_ENABLE_OVERRIDES_IN_LAN && (ip.isLinkLocalAddress() || ip.isSiteLocalAddress()); this.pluginChannelHandler.sendMessageServer(serverPlayer, new ServerConnectInfoMessage( !isLanPlayer && !ipOverride.isEmpty() ? ipOverride : null, diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index 677155555..d848d60b5 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -356,8 +356,6 @@ "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generalSectionNote": " \u25cf General", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableServerNetworking": "Enable Server Networking", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableServerNetworking.@tooltip": "§6Attention:§r this feature is not fully implemented. \n\nIf true Distant Horizons will attempt to communicate with the connected \nserver in order to load LODs outside your vanilla render distance. \n\nNote: This requires DH to be installed on the server in order to function.", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.serverPort": "Server Port", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.serverPort.@tooltip": "The port on the server that's used for sending LOD data.", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationSectionNote": " \u25cf Generation", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestRCLimit": "Gen Task Rate/Concurrency Limit", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestRCLimit.@tooltip": "Limits the amount of generation requests sent by client and processed by server.", From 284191a904628a14e6da68e09395f44cf8e72581 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 12 Apr 2024 22:36:04 +0500 Subject: [PATCH 117/205] Fix Neoforge --- .../distanthorizons/coreapi/ModInfo.java | 1 + .../api/internal/ClientPluginChannelApi.java | 6 +- .../server/RemotePlayerConnectionHandler.java | 57 +++++-------------- .../multiplayer/server/ServerPlayerState.java | 53 ++++++++++++++++- .../network/plugin/PluginChannelHandler.java | 8 ++- .../minecraft/IMinecraftSharedWrapper.java | 2 - 6 files changed, 73 insertions(+), 54 deletions(-) diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java index e9cc8829a..521237db5 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java @@ -37,6 +37,7 @@ public final class ModInfo /** Plugin channel protocol version. */ public static final int PLUGIN_PROTOCOL_VERSION = 1; public static final String PLUGIN_CHANNEL_PATH = "plugin_channel"; + public static final String WRAPPER_PACKET_PATH = "wrapper"; // endregion diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index f815586e4..b77571719 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -105,11 +105,7 @@ public class ClientPluginChannelApi implements AutoCloseable public void handlePacket(ByteBuf buffer) { - this.handlePacket(null, buffer); - } - public void handlePacket(@Nullable IServerPlayerWrapper serverPlayer, ByteBuf buffer) - { - this.channelHandler.decodeAndHandle(serverPlayer, buffer); + this.channelHandler.decodeAndHandle(buffer, null); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java index ab541fde8..c0ca363f5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java @@ -6,7 +6,6 @@ import com.google.common.collect.Maps; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener; import com.seibel.distanthorizons.core.config.types.ConfigEntry; -import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; @@ -17,23 +16,17 @@ import com.seibel.distanthorizons.core.network.messages.netty.base.AckMessage; import com.seibel.distanthorizons.core.network.messages.netty.base.NettyCloseEvent; import com.seibel.distanthorizons.core.network.messages.netty.session.PlayerUUIDMessage; import com.seibel.distanthorizons.core.network.messages.netty.session.RemotePlayerConfigMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.PluginHelloMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.ServerConnectInfoMessage; import com.seibel.distanthorizons.core.network.netty.INettyConnection; import com.seibel.distanthorizons.core.network.netty.NettyMessage; import com.seibel.distanthorizons.core.network.netty.NettyServer; import com.seibel.distanthorizons.core.network.netty.TrackableNettyMessage; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelHandler; import com.seibel.distanthorizons.core.util.LodUtil; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import io.netty.buffer.ByteBuf; import org.apache.logging.log4j.LogManager; import org.jetbrains.annotations.Nullable; import java.io.Closeable; -import java.net.InetAddress; -import java.net.InetSocketAddress; import java.util.ArrayList; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -44,11 +37,8 @@ import static com.seibel.distanthorizons.core.config.Config.Client.Advanced.Mult public class RemotePlayerConnectionHandler implements Closeable { - private static final boolean DEBUG_ENABLE_OVERRIDES_IN_LAN = false; - private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); - private static final IMinecraftSharedWrapper MC_SERVER = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class); private static final ConfigEntry GENERATE_MULTIPLE_DIMENSIONS_CONFIG = Config.Client.Advanced.Multiplayer.ServerNetworking.generateMultipleDimensions; private final NettyServer server = new NettyServer(ServerNetworking.serverPort.get()); @@ -56,16 +46,15 @@ public class RemotePlayerConnectionHandler implements Closeable private final BiMap playersByConnection = Maps.synchronizedBiMap(HashBiMap.create()); private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::onConfigChanged); - private final PluginChannelHandler pluginChannelHandler = new PluginChannelHandler(); private final ConfigChangeListener portChangeListener = new ConfigChangeListener<>(ServerNetworking.serverPort, this::onServerPortChanged); - private final ConfigChangeListener connectIpOverrideChangeListener = new ConfigChangeListener<>(ServerNetworking.connectIpOverride, this::broadcastConnectInfo); - private final ConfigChangeListener connectPortOverrideChangeListener = new ConfigChangeListener<>(ServerNetworking.connectPortOverride, this::broadcastConnectInfo); + private final ConfigChangeListener connectIpOverrideChangeListener = new ConfigChangeListener<>(ServerNetworking.connectIpOverride, this::onConnectOverridesChanged); + private final ConfigChangeListener connectPortOverrideChangeListener = new ConfigChangeListener<>(ServerNetworking.connectPortOverride, this::onConnectOverridesChanged); + public NettyServer server() { return this.server; } public RemotePlayerConnectionHandler() { - //region Netty server this.server.registerHandler(PlayerUUIDMessage.class, playerUUIDMessage -> { INettyConnection connection = playerUUIDMessage.getConnection(); @@ -103,18 +92,11 @@ public class RemotePlayerConnectionHandler implements Closeable dhPlayer.connection = null; } }); - //endregion - - //region Plugin channel - this.pluginChannelHandler.registerHandler(PluginHelloMessage.class, msg -> { - this.sendConnectInfo(msg.serverPlayer); - }); - //endregion } public void handlePluginMessage(IServerPlayerWrapper player, ByteBuf buffer) { - this.pluginChannelHandler.decodeAndHandle(player, buffer); + this.playersByUUID.get(player.getUUID()).pluginChannelHandler.decodeAndHandle(buffer, player); } private void onConfigChanged() @@ -129,28 +111,14 @@ public class RemotePlayerConnectionHandler implements Closeable { LOGGER.warn("Server port change requires a server restart to take effect."); } - private void broadcastConnectInfo(@Nullable Object ignored) + private void onConnectOverridesChanged(@Nullable Object ignored) { - for (IServerPlayerWrapper serverPlayer : MC_SERVER.getPlayerList()) + for (ServerPlayerState playerState : this.playersByUUID.values()) { - this.sendConnectInfo(serverPlayer); + playerState.sendConnectInfo(); } } - private void sendConnectInfo(IServerPlayerWrapper serverPlayer) - { - String ipOverride = ServerNetworking.connectIpOverride.get(); - int portOverride = ServerNetworking.connectPortOverride.get(); - - // IP/port overrides are intended for using with port forwarding services, - // and LAN clients are unlikely to need to hop through internet - InetAddress ip = ((InetSocketAddress) serverPlayer.getRemoteAddress()).getAddress(); - boolean isLanPlayer = !DEBUG_ENABLE_OVERRIDES_IN_LAN && (ip.isLinkLocalAddress() || ip.isSiteLocalAddress()); - - this.pluginChannelHandler.sendMessageServer(serverPlayer, new ServerConnectInfoMessage( - !isLanPlayer && !ipOverride.isEmpty() ? ipOverride : null, - !isLanPlayer && portOverride != 0 ? portOverride : this.server.port - )); - } + public Consumer connectedPlayersOnly(BiConsumer next) { @@ -209,13 +177,15 @@ public class RemotePlayerConnectionHandler implements Closeable public void registerJoinedPlayer(IServerPlayerWrapper serverPlayer) { - this.playersByUUID.put(serverPlayer.getUUID(), new ServerPlayerState(serverPlayer)); + this.playersByUUID.put(serverPlayer.getUUID(), new ServerPlayerState(serverPlayer, this.server.port)); } public void unregisterLeftPlayer(IServerPlayerWrapper serverPlayer) { - ServerPlayerState dhPlayer = this.playersByUUID.remove(serverPlayer.getUUID()); - INettyConnection connection = this.playersByConnection.inverse().remove(dhPlayer); + ServerPlayerState playerState = this.playersByUUID.remove(serverPlayer.getUUID()); + playerState.close(); + + INettyConnection connection = this.playersByConnection.inverse().remove(playerState); if (connection != null) { connection.disconnect("You have logged out."); @@ -228,7 +198,6 @@ public class RemotePlayerConnectionHandler implements Closeable this.portChangeListener.close(); this.connectIpOverrideChangeListener.close(); this.connectPortOverrideChangeListener.close(); - this.pluginChannelHandler.close(); this.configChangeListener.close(); this.server().close(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index e6ad7ce0e..1312704d3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -1,24 +1,42 @@ package com.seibel.distanthorizons.core.multiplayer.server; +import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.level.DhServerLevel; +import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; +import com.seibel.distanthorizons.core.network.messages.plugin.PluginCloseEvent; +import com.seibel.distanthorizons.core.network.messages.plugin.PluginHelloMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.ServerConnectInfoMessage; import com.seibel.distanthorizons.core.network.netty.INettyConnection; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.messages.netty.fullData.FullDataSourceRequestMessage; import com.seibel.distanthorizons.core.network.messages.netty.fullData.generation.GenTaskPriorityRequestMessage; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelHandler; import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateAndConcurrencyLimiter; import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateLimiter; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; +import org.apache.logging.log4j.LogManager; import org.jetbrains.annotations.NotNull; +import java.io.Closeable; +import java.net.InetAddress; +import java.net.InetSocketAddress; import java.util.concurrent.ConcurrentHashMap; import static com.seibel.distanthorizons.core.config.Config.Client.Advanced.Multiplayer.ServerNetworking; public class ServerPlayerState { + private static final boolean DEBUG_ENABLE_OVERRIDES_IN_LAN = false; + + private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), + () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); + public final IServerPlayerWrapper serverPlayer; public INettyConnection connection; + private final int serverPort; + public final PluginChannelHandler pluginChannelHandler = new PluginChannelHandler(); + @NotNull public ConstrainedMultiplayerConfig config = new ConstrainedMultiplayerConfig(); @@ -33,7 +51,40 @@ public class ServerPlayerState return this.rateLimiterSets.computeIfAbsent(level, ignored -> new RateLimiterSet()); } - public ServerPlayerState(IServerPlayerWrapper serverPlayer) { this.serverPlayer = serverPlayer; } + public ServerPlayerState(IServerPlayerWrapper serverPlayer, int serverPort) + { + this.serverPlayer = serverPlayer; + this.serverPort = serverPort; + + this.pluginChannelHandler.registerHandler(PluginHelloMessage.class, msg -> { + this.sendConnectInfo(); + }); + + this.pluginChannelHandler.registerHandler(PluginCloseEvent.class, event -> { + // Noop + }); + } + + public void sendConnectInfo() + { + String ipOverride = ServerNetworking.connectIpOverride.get(); + int portOverride = ServerNetworking.connectPortOverride.get(); + + // IP/port overrides are intended for using with port forwarding services, + // and LAN clients are unlikely to need to hop through internet + InetAddress ip = ((InetSocketAddress) this.serverPlayer.getRemoteAddress()).getAddress(); + boolean isLanPlayer = !DEBUG_ENABLE_OVERRIDES_IN_LAN && (ip.isLinkLocalAddress() || ip.isSiteLocalAddress()); + + this.pluginChannelHandler.sendMessageServer(this.serverPlayer, new ServerConnectInfoMessage( + !isLanPlayer && !ipOverride.isEmpty() ? ipOverride : null, + !isLanPlayer && portOverride != 0 ? portOverride : this.serverPort + )); + } + + public void close() + { + this.pluginChannelHandler.close(); + } public class RateLimiterSet diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelHandler.java index 000639e2d..a35e3e213 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelHandler.java @@ -11,6 +11,7 @@ import com.seibel.distanthorizons.core.network.protocol.plugin.PluginMessageEnco import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufUtil; import org.apache.logging.log4j.LogManager; import org.jetbrains.annotations.Nullable; @@ -40,7 +41,7 @@ public class PluginChannelHandler extends NetworkEventSource getPlayerList(); - } From 5f9f0f23b8ac92a83903ed02d9168e4a84224abc Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 12 Apr 2024 23:14:58 +0500 Subject: [PATCH 118/205] Fix integer overflows --- .../core/network/messages/plugin/ServerConnectInfoMessage.java | 2 +- .../distanthorizons/core/network/protocol/INetworkObject.java | 2 +- .../distanthorizons/core/network/protocol/MessageDecoder.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/ServerConnectInfoMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/ServerConnectInfoMessage.java index fd1186ef4..a974a447b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/ServerConnectInfoMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/ServerConnectInfoMessage.java @@ -32,7 +32,7 @@ public class ServerConnectInfoMessage extends PluginChannelMessage public void decode(ByteBuf in) { this.ipOverride = this.readOptional(in, () -> this.readString(in)); - this.port = in.readShort(); + this.port = in.readUnsignedShort(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java index 115f9ac23..1550f1190 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java @@ -66,7 +66,7 @@ public interface INetworkObject default String readString(ByteBuf inputByteBuf) { - int length = inputByteBuf.readShort(); + int length = inputByteBuf.readUnsignedShort(); return inputByteBuf.readBytes(length).toString(StandardCharsets.UTF_8); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageDecoder.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageDecoder.java index 34b1af03b..c23ef1374 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageDecoder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageDecoder.java @@ -38,7 +38,7 @@ public class MessageDecoder extends ByteToMessa @Override protected void decode(ChannelHandlerContext channelContext, ByteBuf inputByteBuf, List outputDecodedObjectList) { - TMessage message = this.messageRegistry.createMessage(inputByteBuf.readShort()); + TMessage message = this.messageRegistry.createMessage(inputByteBuf.readUnsignedShort()); outputDecodedObjectList.add(INetworkObject.readToObject(message, inputByteBuf)); } From 7c33dda11b2c80002239e88929498f4a4d7aaf41 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 16 Apr 2024 20:32:50 +0500 Subject: [PATCH 119/205] Add FlushConsolidationHandler --- .../core/network/protocol/netty/NettyChannelInitializer.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyChannelInitializer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyChannelInitializer.java index d9946103d..aea4da707 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyChannelInitializer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyChannelInitializer.java @@ -27,6 +27,7 @@ import io.netty.channel.*; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.LengthFieldPrepender; +import io.netty.handler.flush.FlushConsolidationHandler; import org.jetbrains.annotations.NotNull; /** Used when creating a network channel */ @@ -48,6 +49,7 @@ public class NettyChannelInitializer extends ChannelInitializer ChannelPipeline pipeline = socketChannel.pipeline(); // Encoder + pipeline.addLast(new FlushConsolidationHandler(FlushConsolidationHandler.DEFAULT_EXPLICIT_FLUSH_AFTER_FLUSHES, true)); pipeline.addLast(new LengthFieldPrepender(Integer.BYTES)); pipeline.addLast(new MessageEncoder<>(NettyMessageRegistry.INSTANCE, NettyMessage.class)); pipeline.addLast(new NettyOutboundExceptionRouter()); From 7cd0c956f625118142f64e769163932aa8912f00 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 16 Apr 2024 21:27:26 +0500 Subject: [PATCH 120/205] Fix IPv6 addresses not being properly handled --- .../distanthorizons/core/network/netty/NettyClient.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java index 7655e8e85..84ec55345 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java @@ -122,7 +122,10 @@ public class NettyClient extends NettyEventSource implements INettyConnection, A { return; } - this.address = new InetSocketAddress(host, port); + + // Remove IPv6 brackets + host = host.replaceAll("[\\[\\]]", ""); + this.address = new InetSocketAddress(host port); if (this.channel != null) { From 7006c669f07cf3b7b1fc4e4d34344b1174501677 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 16 Apr 2024 21:38:37 +0500 Subject: [PATCH 121/205] More verbose invalid level error --- .../multiplayer/server/RemotePlayerConnectionHandler.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java index c0ca363f5..7218f6504 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java @@ -27,6 +27,7 @@ import org.apache.logging.log4j.LogManager; import org.jetbrains.annotations.Nullable; import java.io.Closeable; +import java.text.MessageFormat; import java.util.ArrayList; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; @@ -151,7 +152,11 @@ public class RemotePlayerConnectionHandler implements Closeable // If the message can be replied to - reply with error, otherwise just ignore if (msg instanceof TrackableNettyMessage) { - ((TrackableNettyMessage) msg).sendResponse(new InvalidLevelException("Invalid level")); + ((TrackableNettyMessage) msg).sendResponse(new InvalidLevelException(MessageFormat.format( + "Generation not allowed. Requested dimension: {0}, player dimension: {1}", + level.getLevelWrapper().getDimensionType().getDimensionName(), + serverPlayerState.serverPlayer.getLevel().getDimensionType().getDimensionName() + ))); } return; From c1f2803c56a9e6013c0bf54f3140f516e7fb33b0 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 16 Apr 2024 21:39:56 +0500 Subject: [PATCH 122/205] Add missing newlines for override configs --- .../com/seibel/distanthorizons/core/config/Config.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 706c16904..4d67efd5c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -898,8 +898,8 @@ public class Config .setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) .set("") .comment("" - + "Overrides the IP address that is sent to the client for connecting to DH server." - + "Leave empty to let client use the IP used to connect to the MC server." + + "Overrides the IP address that is sent to the client for connecting to DH server.\n" + + "Leave empty to let client use the IP used to connect to the MC server.\n" + "") .build(); public static ConfigEntry connectPortOverride = new ConfigEntry.Builder() @@ -907,8 +907,8 @@ public class Config .setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) .setMinDefaultMax(0, 0, 65535) .comment("" - + "Overrides the port that is sent to the client for connecting to DH server." - + "Set to 0 to use port from serverPort." + + "Overrides the port that is sent to the client for connecting to DH server.\n" + + "Set to 0 to use port from serverPort.\n" + "") .build(); From 05fba120381c8a0221d291aa24aa9d2524e0e8b9 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 16 Apr 2024 21:55:02 +0500 Subject: [PATCH 123/205] Prevent generation when config is not acknowledged by server --- .../core/multiplayer/client/ClientNetworkState.java | 1 + .../seibel/distanthorizons/core/network/netty/NettyClient.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index 9af8ded5e..5bf1c96d9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -75,6 +75,7 @@ public class ClientNetworkState implements Closeable private void onConfigChanged() { + this.configReceived = false; this.getClient().sendMessage(new RemotePlayerConfigMessage(new MultiplayerConfig())); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java index 84ec55345..50b343c58 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java @@ -125,7 +125,7 @@ public class NettyClient extends NettyEventSource implements INettyConnection, A // Remove IPv6 brackets host = host.replaceAll("[\\[\\]]", ""); - this.address = new InetSocketAddress(host port); + this.address = new InetSocketAddress(host, port); if (this.channel != null) { From 3e3e97385bc1a695fd8edb413c61f4b9224b2954 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Wed, 17 Apr 2024 21:02:14 +0500 Subject: [PATCH 124/205] Fix 1.16.5 and 1.17.1 builds --- .../core/network/protocol/netty/NettyChannelInitializer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyChannelInitializer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyChannelInitializer.java index aea4da707..4e9ee7e5d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyChannelInitializer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyChannelInitializer.java @@ -49,7 +49,7 @@ public class NettyChannelInitializer extends ChannelInitializer ChannelPipeline pipeline = socketChannel.pipeline(); // Encoder - pipeline.addLast(new FlushConsolidationHandler(FlushConsolidationHandler.DEFAULT_EXPLICIT_FLUSH_AFTER_FLUSHES, true)); + pipeline.addLast(new FlushConsolidationHandler(256, true)); pipeline.addLast(new LengthFieldPrepender(Integer.BYTES)); pipeline.addLast(new MessageEncoder<>(NettyMessageRegistry.INSTANCE, NettyMessage.class)); pipeline.addLast(new NettyOutboundExceptionRouter()); From 2664f96e0fe13d99de1ffd78b328e9a37fb12315 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 3 May 2024 22:56:01 +0500 Subject: [PATCH 125/205] Generation works, updates don't --- .../FullDataSourceProviderV2.java | 28 +++++++++---------- .../RemoteFullDataSourceProvider.java | 2 +- .../core/level/DhServerLevel.java | 6 +++- .../server/RemotePlayerConnectionHandler.java | 3 +- .../multiplayer/server/ServerPlayerState.java | 7 +++-- .../messages/netty/ILevelRelatedMessage.java | 2 +- .../FullDataSourceResponseMessage.java | 3 +- .../core/sql/dto/FullDataSourceV2DTO.java | 2 +- 8 files changed, 31 insertions(+), 22 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java index b521ce1f7..a0387f494 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java @@ -180,18 +180,18 @@ public class FullDataSourceProviderV2 try { PreparedStatement preparedStatement = this.repo.createPreparedStatement( - "SELECT LastModifiedDateTime" + - "FROM " + this.repo.getTableName() + - "WHERE DetailLevel = ?" + - "AND PosX = ?" + - "AND PosZ = ?" + "SELECT LastModifiedUnixDateTime " + + "FROM " + this.repo.getTableName() + " " + + "WHERE DetailLevel = ? " + + "AND PosX = ? " + + "AND PosZ = ?;" ); - preparedStatement.setInt(1, pos.getDetailLevel()); + preparedStatement.setInt(1, pos.getDetailLevel() - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); preparedStatement.setInt(2, pos.getX()); preparedStatement.setInt(3, pos.getX()); List> row = this.repo.query(preparedStatement); - return !row.isEmpty() ? (Long) row.get(0).get("LastModifiedDateTime") : null; + return !row.isEmpty() ? (Long) row.get(0).get("LastModifiedUnixDateTime") : null; } catch (SQLException e) { @@ -208,13 +208,13 @@ public class FullDataSourceProviderV2 try { PreparedStatement preparedStatement = this.repo.createPreparedStatement( - "SELECT PosX, PosZ, LastModifiedDateTime" + - "FROM " + this.repo.getTableName() + - "WHERE DetailLevel = ?" + - "AND PosX BETWEEN ? AND ?" + - "AND PosZ BETWEEN ? AND ?" + "SELECT PosX, PosZ, LastModifiedUnixDateTime " + + "FROM " + this.repo.getTableName() + " " + + "WHERE DetailLevel = ? " + + "AND PosX BETWEEN ? AND ? " + + "AND PosZ BETWEEN ? AND ?;" ); - preparedStatement.setInt(1, start.getDetailLevel()); + preparedStatement.setInt(1, start.getDetailLevel() - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); preparedStatement.setInt(2, start.getX()); preparedStatement.setInt(3, end.getX()); preparedStatement.setInt(4, start.getZ()); @@ -222,7 +222,7 @@ public class FullDataSourceProviderV2 return this.repo.query(preparedStatement).stream().collect(Collectors.toMap( row -> new DhSectionPos(start.getDetailLevel(), (int) row.get("PosX"), (int) row.get("PosZ")), - row -> (long) row.get("LastModifiedDateTime")) + row -> (long) row.get("LastModifiedUnixDateTime")) ); } catch (SQLException e) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataSourceProvider.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataSourceProvider.java index 3b946ef45..936c3ce53 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataSourceProvider.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataSourceProvider.java @@ -46,7 +46,7 @@ public class RemoteFullDataSourceProvider extends GeneratedFullDataSourceProvide public FullDataSourceV2 get(DhSectionPos pos) { FullDataSourceV2 fullDataSource = super.get(pos); - if (fullDataSource == null || this.isFullyGenerated(fullDataSource.columnGenerationSteps) || this.dataRefreshQueue == null) + if (fullDataSource == null || this.dataRefreshQueue == null) { return fullDataSource; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 85c398135..414d634b2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -134,7 +134,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel } Long serverTimestamp = this.serverside.fullDataFileHandler.getTimestampForPos(msg.sectionPos); - if (serverTimestamp == null || serverTimestamp < msg.clientTimestamp) + if (serverTimestamp == null || serverTimestamp <= msg.clientTimestamp) { rateLimiterSet.loginDataSyncRCLimiter.release(); msg.sendResponse(new FullDataSourceResponseMessage(null)); @@ -339,6 +339,10 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel { entry.fullDataSource = fullDataSource; } + else + { + this.serverside.fullDataFileHandler.queuePositionForRetrieval(pos); + } }); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java index 7218f6504..5b6e37364 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java @@ -90,6 +90,7 @@ public class RemotePlayerConnectionHandler implements Closeable ServerPlayerState dhPlayer = this.playersByConnection.remove(closeEvent.getConnection()); if (dhPlayer != null) { + dhPlayer.clearRateLimiterSets(); dhPlayer.connection = null; } }); @@ -208,4 +209,4 @@ public class RemotePlayerConnectionHandler implements Closeable this.server().close(); } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index 1312704d3..d16450ead 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -50,6 +50,10 @@ public class ServerPlayerState { return this.rateLimiterSets.computeIfAbsent(level, ignored -> new RateLimiterSet()); } + public void clearRateLimiterSets() + { + this.rateLimiterSets.clear(); + } public ServerPlayerState(IServerPlayerWrapper serverPlayer, int serverPort) { @@ -115,5 +119,4 @@ public class ServerPlayerState } -} - +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/ILevelRelatedMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/ILevelRelatedMessage.java index 8c33c0736..352665675 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/ILevelRelatedMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/ILevelRelatedMessage.java @@ -12,7 +12,7 @@ public interface ILevelRelatedMessage @SuppressWarnings("BooleanMethodIsAlwaysInverted") default boolean isSameLevelAs(ILevelWrapper levelWrapper) { - return levelWrapper.getDimensionType().getDimensionName().equals(levelWrapper.getDimensionType().getDimensionName()); + return this.getLevelName().equals(levelWrapper.getDimensionType().getDimensionName()); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/FullDataSourceResponseMessage.java index 24885ac76..36cc990eb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/FullDataSourceResponseMessage.java @@ -64,7 +64,8 @@ public class FullDataSourceResponseMessage extends TrackableNettyMessage } } - @Override public void decode0(ByteBuf in) + @Override + public void decode0(ByteBuf in) { this.dataSourceDto = this.readOptional(in, () -> INetworkObject.readToObject(new FullDataSourceV2DTO(), in)); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java index cb7920959..cf7345d10 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java @@ -345,7 +345,7 @@ public class FullDataSourceV2DTO implements IBaseDTO, INetworkObje @Override public void decode(ByteBuf in) { - this.pos.decode(in); + this.pos = INetworkObject.readToObject(DhSectionPos.zero(), in); this.dataChecksum = in.readInt(); this.compressedDataByteArray = new byte[in.readInt()]; From 551a5f1a72e24dcfd84e4f70285b6bd61bf71094 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 5 May 2024 16:55:15 +0500 Subject: [PATCH 126/205] Fix updates --- .../core/file/fullDatafile/FullDataSourceProviderV2.java | 2 +- .../seibel/distanthorizons/core/level/DhServerLevel.java | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java index a0387f494..6a3f99422 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java @@ -188,7 +188,7 @@ public class FullDataSourceProviderV2 ); preparedStatement.setInt(1, pos.getDetailLevel() - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); preparedStatement.setInt(2, pos.getX()); - preparedStatement.setInt(3, pos.getX()); + preparedStatement.setInt(3, pos.getZ()); List> row = this.repo.query(preparedStatement); return !row.isEmpty() ? (Long) row.get(0).get("LastModifiedUnixDateTime") : null; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 414d634b2..10f8f59a7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -235,7 +235,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel { if (!Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates.get()) { - this.getFullDataProvider().updateDataSourceAsync(data); + return this.getFullDataProvider().updateDataSourceAsync(data); } for (ServerPlayerState serverPlayerState : this.remotePlayerConnectionHandler.getConnectedPlayers()) @@ -246,9 +246,9 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel } Vec3d playerPosition = serverPlayerState.serverPlayer.getPosition(); - double distanceFromPlayer = data.getPos().getManhattanBlockDistance(new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)); - if (distanceFromPlayer > serverPlayerState.serverPlayer.getViewDistance() && - distanceFromPlayer < serverPlayerState.config.getRenderDistanceRadius()) + int distanceFromPlayer = data.getPos().getManhattanBlockDistance(new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)) / 16; + if (distanceFromPlayer >= serverPlayerState.serverPlayer.getViewDistance() && + distanceFromPlayer <= serverPlayerState.config.getRenderDistanceRadius()) { serverPlayerState.connection.sendMessage(new FullDataPartialUpdateMessage(this.serverLevelWrapper, data)); } From 3d86c5c5ee63c1ba33907d968efa03dad26615bb Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 10 May 2024 21:13:40 +0500 Subject: [PATCH 127/205] Fix generated sections not appearing --- .../distanthorizons/core/level/DhServerLevel.java | 6 ++---- .../client/AbstractFullDataRequestQueue.java | 15 ++++++++++++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 10f8f59a7..734381d29 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -350,12 +350,10 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel public void onWorldGenTaskComplete(DhSectionPos pos) { IncompleteDataSourceEntry entry = this.incompleteDataSources.get(pos); - if (entry == null) + if (entry != null) { - return; + this.trySetGeneratedDataSourceToEntry(entry, pos); } - - this.trySetGeneratedDataSourceToEntry(entry, pos); } private static class IncompleteDataSourceEntry diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index 4fe3a7128..f45fd4c47 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -233,7 +233,20 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, return entry.future.complete(false); } - return entry.future.complete(true); + // Hack to work around a race condition + // If you finish the request too quickly, the section will never render + new Thread(() -> { + try + { + Thread.sleep(5000); + entry.future.complete(true); + } + catch (InterruptedException e) + { + throw new RuntimeException(e); + } + }).start(); + return null; }); } From 543b5ed49aafcf343dae4545a39b71ffcadff925 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 10 May 2024 23:56:07 +0500 Subject: [PATCH 128/205] Fix dimension switching (at cost of breaking immersive portals) --- .../distanthorizons/core/network/netty/NettyMessage.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyMessage.java index fb648db78..c191c43b7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyMessage.java @@ -47,7 +47,7 @@ public abstract class NettyMessage implements INetworkObject @Override public String toString() { - return this.toString(""); + return this.toString(" "); } protected String toString(@Nullable String extraData) @@ -55,5 +55,4 @@ public abstract class NettyMessage implements INetworkObject return this.getClass().getSimpleName() + "{" + extraData + '}'; } -} - +} \ No newline at end of file From e6b140245b6f88b7df26afcd6e615642646d6af6 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 12 May 2024 17:34:58 +0500 Subject: [PATCH 129/205] Fix awt dependency error --- .../distanthorizons/core/Initializer.java | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/Initializer.java b/core/src/main/java/com/seibel/distanthorizons/core/Initializer.java index 76c2f0515..3ef5e3325 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/Initializer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/Initializer.java @@ -22,6 +22,7 @@ package com.seibel.distanthorizons.core; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.sql.DatabaseUpdater; import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.core.world.DhApiWorldProxy; import com.seibel.distanthorizons.core.api.external.methods.config.DhApiConfig; @@ -76,16 +77,18 @@ public class Initializer throw new RuntimeException(e); } - - // attempt to setup Swing so we can display dialogs (popup windows) - System.setProperty("java.awt.headless", "false"); - if (GraphicsEnvironment.isHeadless()) + if (SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class) != null) { - LOGGER.warn("Java.awt.headless is false. This means Distant Horizons can't display error and info dialog windows."); - } - else - { - LOGGER.info("Java.awt.headless set to true. Distant Horizons can correctly display error and info dialog windows."); + // attempt to setup Swing so we can display dialogs (popup windows) + System.setProperty("java.awt.headless", "false"); + if (GraphicsEnvironment.isHeadless()) + { + LOGGER.warn("Java.awt.headless is false. This means Distant Horizons can't display error and info dialog windows."); + } + else + { + LOGGER.info("Java.awt.headless set to true. Distant Horizons can correctly display error and info dialog windows."); + } } // link Core's config to the API @@ -101,4 +104,4 @@ public class Initializer } -} +} \ No newline at end of file From a1f4442a53a653f16860e535c380fdd0a1f5da75 Mon Sep 17 00:00:00 2001 From: Yeshi0 Date: Tue, 14 May 2024 20:16:44 +0200 Subject: [PATCH 130/205] rewrite config comments --- .../distanthorizons/core/config/Config.java | 80 ++++++++++--------- 1 file changed, 42 insertions(+), 38 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index f87bc2fef..0ce57ad25 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -874,14 +874,15 @@ public class Config .setServersideShortName("enableServerNetworking") .set(true) .comment("" - + "Attention: \n" - + " 1. This feature is not fully implemented. \n" - + " 2. If you really want to use it, enable it only on trusted server/with trusted players. \n" + + "WARNING!\n" + + "Server-client networking is not yet fully implemented!\n" + + "Both the server and client must be running the server-side fork with this option enabled\n" + + "for Distant Horizons data to be transceived.\n" + "\n" - + "If true Distant Horizons will attempt to communicate with the connected \n" - + "server in order to load LODs outside your vanilla render distance. \n" + + "If true, the server and client will attempt to communicate to transceive Distant Horizons data.\n" + + "This allows for further distant generation and LOD updates on all clients.\n" + "\n" - + "Note: This requires DH to be installed on the server in order to function. \n" + + "This should only be used on trusted servers with trusted players!\n" + "") .build(); @@ -895,7 +896,8 @@ public class Config .setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) .setMinDefaultMax(1, 25049, 65535) .comment("" - + "The port on the server that's used for sending LOD data." + + "The port used by the server to transceive Distant Horizons data.\n" + + "Note: This port must be TCP." + "") .build(); @@ -904,8 +906,8 @@ public class Config .setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) .set("") .comment("" - + "Overrides the IP address that is sent to the client for connecting to DH server.\n" - + "Leave empty to let client use the IP used to connect to the MC server.\n" + + "Overrides the IP address sent to the client to transceive Distant Horizons data.\n" + + "Leave this field empty to let the client use the IP used to connect to the server." + "") .build(); public static ConfigEntry connectPortOverride = new ConfigEntry.Builder() @@ -913,8 +915,8 @@ public class Config .setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) .setMinDefaultMax(0, 0, 65535) .comment("" - + "Overrides the port that is sent to the client for connecting to DH server.\n" - + "Set to 0 to use port from serverPort.\n" + + "Overrides the port sent to the client to transceive Distant Horizons data.\n" + + "Set this field to 0 to use the value assigned to serverPort." + "") .build(); @@ -923,8 +925,8 @@ public class Config .setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) .setMinDefaultMax(1, 10, 100) .comment("" - + "Amount of rate/concurrency limit hits in one second before disconnecting the offending clients. \n" - + "This setting is server-only; it does not have effect on the client.\n" + + "The amount of rate/concurrency limit hits a client can make in one second before being disconnected by the server.\n" + + "This setting only applies to the server and has no effect on clients." + "") .build(); @@ -934,24 +936,26 @@ public class Config .setServersideShortName("generationRequestRCLimit") .setMinDefaultMax(1, 20, 100) .comment("" - + "Limits the amount of generation requests sent by client and processed by server. \n" + + "Limits the amount of generation requests the server will handle." + "") .build(); public static ConfigEntry genTaskPriorityDistanceRatio = new ConfigEntry.Builder() .setServersideShortName("genTaskPriorityDistanceRatio") .setMinDefaultMax(1d, 3d, 10d) + // todo: this comment should probably be rewritten, but it's outside my knowledge scope + // - yeshi .comment("" - + "Controls the max ratio between distances of nearest unloaded sections of each priority. \n" - + "For example, value of 2 means that the nearest lower priority section will be allowed to stay \n" - + "unloaded only if it's at most 2x closer than one of a higher priority. \n" + + "Controls the max ratio between distances of nearest unloaded sections of each priority.\n" + + "For example, value of 2 means that the nearest lower priority section will be allowed to stay\n" + + "unloaded only if it's at most 2x closer than one of a higher priority." + "") .build(); public static ConfigEntry genTaskPriorityRequestRateLimit = new ConfigEntry.Builder() .setServersideShortName("genTaskPriorityRequestRateLimit") .setMinDefaultMax(1, 50, 200) .comment("" - + "Limits the amount of LOD sections that the client can request states for, per second. \n" + + "Limits the amount of LOD sections the client can request states for per second." + "") .build(); @@ -961,7 +965,7 @@ public class Config .setServersideShortName("enableRealTimeUpdates") .set(false) .comment("" - + "Enables real time updates from server." + + "Enables client-side real-time LOD updates from the server." + "") .build(); @@ -971,7 +975,7 @@ public class Config .setServersideShortName("enableLoginDataSync") .set(false) .comment("" - + "Enables updating of saved LODs after login." + + "If true, clients will receive updated LODs on join if any changes occured since last join." + "") .build(); @@ -979,7 +983,7 @@ public class Config .setServersideShortName("loginDataSyncRCLimit") .setMinDefaultMax(1, 50, 100) .comment("" - + "Limits the amount of sent/processed LOD *update* requests concurrently, per player. \n" + + "Limits the amount of sent/processed LOD *update* requests concurrently, per player." + "") .build(); @@ -988,11 +992,11 @@ public class Config .setServersideShortName("generateMultipleDimensions") .set(false) .comment("" - + "Controls whether the players should be allowed to generate in multiple dimensions at once. \n" + + "Controls whether clients will request for the server to generate LODs in multiple dimensions at once.\n" + "" - + "For server owners: \n" - + "All dimensions share the same thread pool. This means that empty dimensions (i.e. without players in them) \n" - + "may slow down the generation of non-empty ones." + + "Note that all dimensions share the same thread pool.\n" + + "This means that dimensions without any connected clients inside could slow down LOD updates for\n" + + "all connected clients in other dimensions." + "") .build(); } @@ -1002,17 +1006,17 @@ public class Config public static class MultiThreading { public static final String THREAD_NOTE = "" - + "Multi-threading Note: \n" - + "If the total thread count in Distant Horizon's config is more threads than your CPU has cores, \n" - + "CPU performance may suffer if Distant Horizons has a lot to load or generate. \n" + + "Multi-threading Note:\n" + + "If the total thread count in Distant Horizon's config is more threads than your CPU has cores,\n" + + "CPU performance may suffer if Distant Horizons has a lot to load or generate.\n" + "This can be an issue when first loading into a world, when flying, and/or when generating new terrain."; public static final String THREAD_RUN_TIME_RATIO_NOTE = "" - + "If this value is less than 1.0, it will be treated as a percentage \n" - + "of time each thread can run before going idle. \n" + + "If this value is less than 1.0, it will be treated as a percentage\n" + + "of time each thread can run before going idle.\n" + "\n" - + "This can be used to reduce CPU usage if the thread count \n" - + "is already set to 1 for the given option, or more finely \n" + + "This can be used to reduce CPU usage if the thread count\n" + + "is already set to 1 for the given option, or more finely\n" + "tune CPU performance."; @@ -1022,13 +1026,13 @@ public class Config ThreadPresetConfigEventHandler.getWorldGenDefaultThreadCount(), Runtime.getRuntime().availableProcessors()) .comment("" - + "How many threads should be used when generating LOD \n" - + "chunks outside the normal render distance? \n" + + "How many threads should be used when generating LOD\n" + + "chunks outside the normal render distance?\n" + "\n" - + "If you experience stuttering when generating distant LODs, \n" - + "decrease this number. \n" - + "If you want to increase LOD \n" - + "generation speed, increase this number. \n" + + "If you experience stuttering when generating distant LODs,\n" + + "decrease this number.\n" + + "If you want to increase LOD\n" + + "generation speed, increase this number.\n" + "\n" + THREAD_NOTE) .build(); From bd5866787f91dd0d0b5472ec3a5efbde4e141bb9 Mon Sep 17 00:00:00 2001 From: s809 <11816467-s809@users.noreply.gitlab.com> Date: Fri, 17 May 2024 12:41:09 +0000 Subject: [PATCH 131/205] Update Config.java --- .../java/com/seibel/distanthorizons/core/config/Config.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 0ce57ad25..fa9f90a03 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -965,7 +965,7 @@ public class Config .setServersideShortName("enableRealTimeUpdates") .set(false) .comment("" - + "Enables client-side real-time LOD updates from the server." + + "If true, the client will receive real-time LOD updates for chunks outside the client's render distance." + "") .build(); From 96755e6174dda52b60026d6b2c57fac20ad4014d Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 18 May 2024 22:09:19 +0500 Subject: [PATCH 132/205] Move overrides in LAN to config --- .../com/seibel/distanthorizons/core/config/Config.java | 9 +++++++++ .../core/multiplayer/server/ServerPlayerState.java | 7 +------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index f87bc2fef..e6440bf32 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -917,6 +917,15 @@ public class Config + "Set to 0 to use port from serverPort.\n" + "") .build(); + public static ConfigEntry enableConnectOverridesInLan = new ConfigEntry.Builder() + .setServersideShortName("enableConnectOverridesInLan") + .setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) + .set(false) + .comment("" + + "Controls whether the connect overrides are sent to the clients originating from LAN. \n" + + "Unless you have a good reason to do otherwise, leave this disabled. \n" + + "") + .build(); public static ConfigEntry rateLimitHitTolerance = new ConfigEntry.Builder() .setServersideShortName("rateLimitHitTolerance") diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index d16450ead..593723bab 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -17,7 +17,6 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapp import org.apache.logging.log4j.LogManager; import org.jetbrains.annotations.NotNull; -import java.io.Closeable; import java.net.InetAddress; import java.net.InetSocketAddress; import java.util.concurrent.ConcurrentHashMap; @@ -26,8 +25,6 @@ import static com.seibel.distanthorizons.core.config.Config.Client.Advanced.Mult public class ServerPlayerState { - private static final boolean DEBUG_ENABLE_OVERRIDES_IN_LAN = false; - private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); @@ -74,10 +71,8 @@ public class ServerPlayerState String ipOverride = ServerNetworking.connectIpOverride.get(); int portOverride = ServerNetworking.connectPortOverride.get(); - // IP/port overrides are intended for using with port forwarding services, - // and LAN clients are unlikely to need to hop through internet InetAddress ip = ((InetSocketAddress) this.serverPlayer.getRemoteAddress()).getAddress(); - boolean isLanPlayer = !DEBUG_ENABLE_OVERRIDES_IN_LAN && (ip.isLinkLocalAddress() || ip.isSiteLocalAddress()); + boolean isLanPlayer = !ServerNetworking.enableConnectOverridesInLan.get() && (ip.isLinkLocalAddress() || ip.isSiteLocalAddress()); this.pluginChannelHandler.sendMessageServer(this.serverPlayer, new ServerConnectInfoMessage( !isLanPlayer && !ipOverride.isEmpty() ? ipOverride : null, From 44205664b5d7ba28b6a33bcc0fd9a578f73bb60a Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 21 May 2024 22:54:57 +0500 Subject: [PATCH 133/205] [skip ci] Incomplete --- .../com/seibel/distanthorizons/api/DhApi.java | 4 +- .../distanthorizons/coreapi/ModInfo.java | 5 +- .../api/internal/ClientPluginChannelApi.java | 12 +- .../distanthorizons/core/config/Config.java | 42 ---- .../WorldRemoteGenerationQueue.java | 6 +- .../core/level/DhClientLevel.java | 6 +- .../core/level/DhServerLevel.java | 12 +- .../client/AbstractFullDataRequestQueue.java | 6 +- .../client/ClientNetworkState.java | 47 ++-- .../server/RemotePlayerConnectionHandler.java | 143 +---------- .../multiplayer/server/ServerPlayerState.java | 64 +++-- .../core/network/NetworkEventSource.java | 169 ++++++++++--- .../network/ScopedNetworkEventSource.java | 14 +- .../messages/AbstractMessageRegistry.java | 75 ------ .../core/network/messages/ICloseEvent.java | 14 -- .../messages/PluginMessageRegistry.java | 118 +++++++++ .../messages/netty/NettyMessageRegistry.java | 46 ---- .../messages/netty/base/HelloMessage.java | 43 ---- .../messages/netty/base/NettyCloseEvent.java | 32 --- .../GenTaskPriorityRequestMessage.java | 68 ------ .../GenTaskPriorityResponseMessage.java | 56 ----- .../ILevelRelatedMessage.java | 2 +- .../messages/plugin/PluginCloseEvent.java | 11 +- .../messages/plugin/PluginHelloMessage.java | 21 -- .../plugin/PluginMessageRegistry.java | 20 -- .../{netty => plugin}/base/AckMessage.java | 9 +- .../{netty => plugin}/base/CancelMessage.java | 9 +- .../base/CloseReasonMessage.java | 7 +- .../base/ExceptionMessage.java | 9 +- .../messages/plugin/base/HelloMessage.java | 24 ++ .../FullDataPartialUpdateMessage.java | 7 +- .../FullDataSourceRequestMessage.java | 9 +- .../FullDataSourceResponseMessage.java | 10 +- .../session/PlayerUUIDMessage.java | 9 +- .../session/RemotePlayerConfigMessage.java | 27 +-- .../core/network/netty/NettyClient.java | 224 ------------------ .../core/network/netty/NettyEventSource.java | 163 ------------- .../core/network/netty/NettyMessage.java | 58 ----- .../core/network/netty/NettyServer.java | 177 -------------- .../network/plugin/PluginChannelMessage.java | 20 +- ...Handler.java => PluginChannelSession.java} | 73 ++++-- .../PluginChannelSessionAAAAA.java} | 63 ++--- .../TrackableMessage.java} | 31 +-- .../core/network/protocol/MessageDecoder.java | 45 ---- .../core/network/protocol/MessageEncoder.java | 44 ---- .../netty/NettyChannelInitializer.java | 65 ----- .../protocol/netty/NettyMessageHandler.java | 93 -------- .../netty/NettyOutboundExceptionRouter.java | 36 --- .../protocol/plugin/PluginMessageDecoder.java | 33 --- .../protocol/plugin/PluginMessageEncoder.java | 22 -- 50 files changed, 535 insertions(+), 1768 deletions(-) delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/AbstractMessageRegistry.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/ICloseEvent.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/PluginMessageRegistry.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/NettyMessageRegistry.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/HelloMessage.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/NettyCloseEvent.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/generation/GenTaskPriorityRequestMessage.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/generation/GenTaskPriorityResponseMessage.java rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{netty => plugin}/ILevelRelatedMessage.java (86%) delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginHelloMessage.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginMessageRegistry.java rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{netty => plugin}/base/AckMessage.java (79%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{netty => plugin}/base/CancelMessage.java (77%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{netty => plugin}/base/CloseReasonMessage.java (86%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{netty => plugin}/base/ExceptionMessage.java (88%) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/HelloMessage.java rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{netty => plugin}/fullData/FullDataPartialUpdateMessage.java (87%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{netty => plugin}/fullData/FullDataSourceRequestMessage.java (84%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{netty => plugin}/fullData/FullDataSourceResponseMessage.java (86%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{netty => plugin}/session/PlayerUUIDMessage.java (83%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{netty => plugin}/session/RemotePlayerConfigMessage.java (60%) delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyEventSource.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyMessage.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyServer.java rename core/src/main/java/com/seibel/distanthorizons/core/network/plugin/{PluginChannelHandler.java => PluginChannelSession.java} (53%) rename core/src/main/java/com/seibel/distanthorizons/core/network/{netty/INettyConnection.java => plugin/PluginChannelSessionAAAAA.java} (54%) rename core/src/main/java/com/seibel/distanthorizons/core/network/{netty/TrackableNettyMessage.java => plugin/TrackableMessage.java} (74%) delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageDecoder.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageEncoder.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyChannelInitializer.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyMessageHandler.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyOutboundExceptionRouter.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/protocol/plugin/PluginMessageDecoder.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/protocol/plugin/PluginMessageEncoder.java diff --git a/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java b/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java index e0fc13fc4..3d9153843 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java @@ -191,7 +191,7 @@ public class DhApi * Returns the network protocol version. * @since API 1.0.0 */ - public static int getNetworkProtocolVersion() { return ModInfo.PROTOCOL_VERSION; } + public static int getNetworkProtocolVersion() { return ModInfo.OLD_PROTOCOL_VERSION; } // methods // @@ -202,4 +202,4 @@ public class DhApi */ public static boolean isDhThread() { return Thread.currentThread().getName().startsWith(ModInfo.THREAD_NAME_PREFIX); } -} +} \ No newline at end of file diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java index 5076c69d7..c5120220c 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java @@ -32,10 +32,7 @@ public final class ModInfo // region Protocol versions // Incremented every time any packets are added, changed or removed, with a few exceptions. - /** Netty protocol version. */ - public static final int PROTOCOL_VERSION = 3; - /** Plugin channel protocol version. */ - public static final int PLUGIN_PROTOCOL_VERSION = 1; + public static final int PROTOCOL_VERSION = 1; public static final String PLUGIN_CHANNEL_PATH = "plugin_channel"; public static final String WRAPPER_PACKET_PATH = "wrapper"; // endregion diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index efcbd3ab9..f46c3445a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -8,9 +8,9 @@ import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; import com.seibel.distanthorizons.core.network.messages.plugin.PluginCloseEvent; import com.seibel.distanthorizons.core.network.messages.plugin.CurrentLevelKeyMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.PluginHelloMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.base.HelloMessage; import com.seibel.distanthorizons.core.network.messages.plugin.ServerConnectInfoMessage; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelHandler; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import io.netty.buffer.ByteBuf; @@ -26,7 +26,7 @@ public class ClientPluginChannelApi implements AutoCloseable private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); private static final IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class); - private final PluginChannelHandler channelHandler = new PluginChannelHandler(); + private final PluginChannelSession channelHandler = new PluginChannelSession(); private final Consumer levelUnloadHandler; private final Consumer multiverseLevelLoadHandler; @@ -55,7 +55,7 @@ public class ClientPluginChannelApi implements AutoCloseable public void onJoin(@Nullable ClientNetworkState networkState) { this.networkState = networkState; - this.channelHandler.sendMessageClient(new PluginHelloMessage()); + this.channelHandler.sendMessageClient(new HelloMessage()); } private void onCurrentLevelKeyMessage(CurrentLevelKeyMessage msg) @@ -84,7 +84,7 @@ public class ClientPluginChannelApi implements AutoCloseable { if (this.networkState != null) { - this.networkState.getClient().resetAndConnectTo( + this.networkState.getSession().resetAndConnectTo( msg.ipOverride != null ? msg.ipOverride : MC.getCurrentServerIp().split(":")[0], @@ -104,7 +104,7 @@ public class ClientPluginChannelApi implements AutoCloseable public void handlePacket(ByteBuf buffer) { - this.channelHandler.decodeAndHandle(buffer, null); + this.channelHandler.decodeAndHandle(buffer); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 72378392e..63a7489a0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -886,48 +886,6 @@ public class Config + "") .build(); - /** - * Server port.
- * Do not use this to retrieve the current server's port; - * Instead, use {@link com.seibel.distanthorizons.core.network.netty.NettyServer#port NettyServer#port}. - */ - public static ConfigEntry serverPort = new ConfigEntry.Builder() - .setServersideShortName("serverPort") - .setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) - .setMinDefaultMax(1, 25049, 65535) - .comment("" - + "The port used by the server to transceive Distant Horizons data.\n" - + "Note: This port must be TCP." - + "") - .build(); - - public static ConfigEntry connectIpOverride = new ConfigEntry.Builder() - .setServersideShortName("connectIpOverride") - .setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) - .set("") - .comment("" - + "Overrides the IP address sent to the client to transceive Distant Horizons data.\n" - + "Leave this field empty to let the client use the IP used to connect to the server." - + "") - .build(); - public static ConfigEntry connectPortOverride = new ConfigEntry.Builder() - .setServersideShortName("connectPortOverride") - .setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) - .setMinDefaultMax(0, 0, 65535) - .comment("" - + "Overrides the port sent to the client to transceive Distant Horizons data.\n" - + "Set this field to 0 to use the value assigned to serverPort." - + "") - .build(); - public static ConfigEntry enableConnectOverridesInLan = new ConfigEntry.Builder() - .setServersideShortName("enableConnectOverridesInLan") - .setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) - .set(false) - .comment("" - + "Controls whether the connect overrides are sent to the clients originating from LAN. \n" - + "Unless you have a good reason to do otherwise, leave this disabled. \n" - + "") - .build(); public static ConfigEntry rateLimitHitTolerance = new ConfigEntry.Builder() .setServersideShortName("rateLimitHitTolerance") diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 2f8c76630..171c2463b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -9,8 +9,8 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.multiplayer.client.AbstractFullDataRequestQueue; import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; -import com.seibel.distanthorizons.core.network.messages.netty.fullData.generation.GenTaskPriorityRequestMessage; -import com.seibel.distanthorizons.core.network.messages.netty.fullData.generation.GenTaskPriorityResponseMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.fullData.generation.GenTaskPriorityRequestMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.fullData.generation.GenTaskPriorityResponseMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; @@ -92,7 +92,7 @@ public class WorldRemoteGenerationQueue extends AbstractFullDataRequestQueue imp return; }; - CompletableFuture request = this.networkState.getClient().sendRequest(new GenTaskPriorityRequestMessage(posList, this.level), GenTaskPriorityResponseMessage.class); + CompletableFuture request = this.networkState.getSession().sendRequest(new GenTaskPriorityRequestMessage(posList, this.level), GenTaskPriorityResponseMessage.class); this.genTaskPriorityRequest = request; request.handleAsync((response, throwable) -> { try diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 78bb8b8fc..edfef8a36 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -33,7 +33,7 @@ import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; import com.seibel.distanthorizons.core.multiplayer.client.FullDataRefreshQueue; import com.seibel.distanthorizons.core.network.netty.NettyClient; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; -import com.seibel.distanthorizons.core.network.messages.netty.fullData.FullDataPartialUpdateMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataPartialUpdateMessage; import com.seibel.distanthorizons.core.network.netty.NettyMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; @@ -102,7 +102,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel this.networkState = networkState; if (networkState != null) { - this.eventSource = new ScopedNetworkEventSource<>(networkState.getClient()); + this.eventSource = new ScopedNetworkEventSource<>(networkState.getSession()); this.dataRefreshQueue = new FullDataRefreshQueue(this, networkState); this.registerNetworkHandlers(); } @@ -177,7 +177,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel boolean isClientUsable = false, isAllowedDimension = false; if (networkState != null) { - isClientUsable = !networkState.getClient().isClosed(); + isClientUsable = !networkState.getSession().isClosed(); isAllowedDimension = MC_CLIENT.getWrappedClientLevel() == this.levelWrapper || networkState.config.generateMultipleDimensions; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 734381d29..742c4b78a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -28,12 +28,12 @@ import com.seibel.distanthorizons.core.multiplayer.server.RemotePlayerConnection import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.netty.NettyServer; import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; -import com.seibel.distanthorizons.core.network.messages.netty.base.CancelMessage; -import com.seibel.distanthorizons.core.network.messages.netty.fullData.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.netty.fullData.FullDataSourceResponseMessage; -import com.seibel.distanthorizons.core.network.messages.netty.fullData.generation.GenTaskPriorityRequestMessage; -import com.seibel.distanthorizons.core.network.messages.netty.fullData.generation.GenTaskPriorityResponseMessage; -import com.seibel.distanthorizons.core.network.messages.netty.fullData.FullDataPartialUpdateMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.base.CancelMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceResponseMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.fullData.generation.GenTaskPriorityRequestMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.fullData.generation.GenTaskPriorityResponseMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataPartialUpdateMessage; import com.seibel.distanthorizons.core.network.netty.NettyMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhSectionPos; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index f45fd4c47..66cc77559 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -11,8 +11,8 @@ import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; -import com.seibel.distanthorizons.core.network.messages.netty.fullData.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.netty.fullData.FullDataSourceResponseMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceResponseMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; @@ -181,7 +181,7 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, DhSectionPos sectionPos = mapEntry.getKey(); RequestQueueEntry entry = mapEntry.getValue(); - CompletableFuture request = this.networkState.getClient().sendRequest(new FullDataSourceRequestMessage(this.level.getLevelWrapper(), sectionPos, entry.updateTimestamp), FullDataSourceResponseMessage.class); + CompletableFuture request = this.networkState.getSession().sendRequest(new FullDataSourceRequestMessage(this.level.getLevelWrapper(), sectionPos, entry.updateTimestamp), FullDataSourceResponseMessage.class); entry.request = request; request.handleAsync((response, throwable) -> { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index 5bf1c96d9..691032914 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -8,11 +8,12 @@ import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; import com.seibel.distanthorizons.core.network.netty.NettyClient; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; -import com.seibel.distanthorizons.core.network.messages.netty.base.AckMessage; -import com.seibel.distanthorizons.core.network.messages.netty.base.NettyCloseEvent; -import com.seibel.distanthorizons.core.network.messages.netty.base.HelloMessage; -import com.seibel.distanthorizons.core.network.messages.netty.session.PlayerUUIDMessage; -import com.seibel.distanthorizons.core.network.messages.netty.session.RemotePlayerConfigMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.base.AckMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.base.NettyCloseEvent; +import com.seibel.distanthorizons.core.network.messages.plugin.base.HelloMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.session.PlayerUUIDMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.session.RemotePlayerConfigMessage; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import org.apache.logging.log4j.LogManager; @@ -26,7 +27,7 @@ public class ClientNetworkState implements Closeable () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); - private final NettyClient client = new NettyClient(); + private final PluginChannelSession session; private final UUID playerUUID = MC_CLIENT.getPlayerUUID(); @@ -41,33 +42,35 @@ public class ClientNetworkState implements Closeable * Returns the client used by this instance.

* If you need to subscribe to any packet events, create an instance of {@link ScopedNetworkEventSource} using the returned instance. */ - public NettyClient getClient() { return this.client; } + public PluginChannelSession getSession() { return this.session; } /** * Constructs a new instance. */ - public ClientNetworkState() + public ClientNetworkState(PluginChannelSession session) { - this.client.registerHandler(HelloMessage.class, helloMessage -> + this.session = session; + + this.session.registerHandler(HelloMessage.class, helloMessage -> { LOGGER.info("Connected to server: "+helloMessage.getConnection().getRemoteAddress()); - this.getClient().sendRequest(new PlayerUUIDMessage(this.playerUUID), AckMessage.class) - .thenAccept(ack -> this.getClient().sendMessage(new RemotePlayerConfigMessage(new MultiplayerConfig()))) + this.getSession().sendRequest(new PlayerUUIDMessage(this.playerUUID), AckMessage.class) + .thenAccept(ack -> this.getSession().sendMessage(new RemotePlayerConfigMessage(new MultiplayerConfig()))) .exceptionally(throwable -> { LOGGER.error("Error while fetching server's config", throwable); return null; }); }); - this.client.registerHandler(RemotePlayerConfigMessage.class, msg -> + this.session.registerHandler(RemotePlayerConfigMessage.class, msg -> { LOGGER.info("Connection config has been changed: " + msg.payload); this.config = (MultiplayerConfig) msg.payload; this.configReceived = true; }); - this.client.registerHandler(NettyCloseEvent.class, msg -> + this.session.registerHandler(NettyCloseEvent.class, msg -> { this.configReceived = false; }); @@ -76,29 +79,29 @@ public class ClientNetworkState implements Closeable private void onConfigChanged() { this.configReceived = false; - this.getClient().sendMessage(new RemotePlayerConfigMessage(new MultiplayerConfig())); + this.getSession().sendMessage(new RemotePlayerConfigMessage(new MultiplayerConfig())); } private String[] f3Log() { - if (!this.client.isInitialized()) + if (!this.session.isInitialized()) { return new String[]{"Did not receive connection info yet..."}; } - if (!this.client.isClosed()) + if (!this.session.isClosed()) { return new String[]{ - this.client.getRemoteAddress() != null + this.session.getRemoteAddress() != null ? (this.isReady() ? "Connected to server" : "Connecting to server...") - : MessageFormat.format("Disconnected, attempts left: {0} / {1}", this.client.getReconnectionAttemptsLeft(), NettyClient.RECONNECTION_ATTEMPTS) + : MessageFormat.format("Disconnected, attempts left: {0} / {1}", this.session.getReconnectionAttemptsLeft(), NettyClient.RECONNECTION_ATTEMPTS) }; } else { return new String[]{ - this.client.getCloseReason() != null - ? "Disconnected: " + this.client.getCloseReason().getMessage() + this.session.getCloseReason() != null + ? "Disconnected: " + this.session.getCloseReason().getMessage() : "Disconnected (check logs for more information)" }; } @@ -109,6 +112,6 @@ public class ClientNetworkState implements Closeable { this.f3Message.close(); this.configChangeListener.close(); - this.client.close(); + this.session.close(); } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java index 5b6e37364..3b3ce66d2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java @@ -1,142 +1,43 @@ package com.seibel.distanthorizons.core.multiplayer.server; -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import com.google.common.collect.Maps; import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener; import com.seibel.distanthorizons.core.config.types.ConfigEntry; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; -import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; -import com.seibel.distanthorizons.core.network.messages.netty.ILevelRelatedMessage; -import com.seibel.distanthorizons.core.network.messages.netty.base.AckMessage; -import com.seibel.distanthorizons.core.network.messages.netty.base.NettyCloseEvent; -import com.seibel.distanthorizons.core.network.messages.netty.session.PlayerUUIDMessage; -import com.seibel.distanthorizons.core.network.messages.netty.session.RemotePlayerConfigMessage; -import com.seibel.distanthorizons.core.network.netty.INettyConnection; +import com.seibel.distanthorizons.core.network.messages.plugin.ILevelRelatedMessage; import com.seibel.distanthorizons.core.network.netty.NettyMessage; -import com.seibel.distanthorizons.core.network.netty.NettyServer; -import com.seibel.distanthorizons.core.network.netty.TrackableNettyMessage; +import com.seibel.distanthorizons.core.network.plugin.TrackableNettyMessage; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import io.netty.buffer.ByteBuf; import org.apache.logging.log4j.LogManager; -import org.jetbrains.annotations.Nullable; import java.io.Closeable; import java.text.MessageFormat; -import java.util.ArrayList; -import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import java.util.function.BiConsumer; import java.util.function.Consumer; -import static com.seibel.distanthorizons.core.config.Config.Client.Advanced.Multiplayer.ServerNetworking; - public class RemotePlayerConnectionHandler implements Closeable { private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); private static final ConfigEntry GENERATE_MULTIPLE_DIMENSIONS_CONFIG = Config.Client.Advanced.Multiplayer.ServerNetworking.generateMultipleDimensions; - private final NettyServer server = new NettyServer(ServerNetworking.serverPort.get()); - private final ConcurrentHashMap playersByUUID = new ConcurrentHashMap<>(); - private final BiMap playersByConnection = Maps.synchronizedBiMap(HashBiMap.create()); - private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::onConfigChanged); + private final ConcurrentMap connectedPlayers = new ConcurrentHashMap<>(); - private final ConfigChangeListener portChangeListener = new ConfigChangeListener<>(ServerNetworking.serverPort, this::onServerPortChanged); - private final ConfigChangeListener connectIpOverrideChangeListener = new ConfigChangeListener<>(ServerNetworking.connectIpOverride, this::onConnectOverridesChanged); - private final ConfigChangeListener connectPortOverrideChangeListener = new ConfigChangeListener<>(ServerNetworking.connectPortOverride, this::onConnectOverridesChanged); - - - public NettyServer server() { return this.server; } - - public RemotePlayerConnectionHandler() - { - this.server.registerHandler(PlayerUUIDMessage.class, playerUUIDMessage -> - { - INettyConnection connection = playerUUIDMessage.getConnection(); - ServerPlayerState serverPlayerState = this.playersByUUID.get(playerUUIDMessage.playerUUID); - - if (serverPlayerState == null) - { - connection.disconnect("Player is not logged in."); - return; - } - - if (serverPlayerState.connection != null) - { - connection.disconnect("Another connection is already in use."); - return; - } - - serverPlayerState.connection = connection; - this.playersByConnection.put(connection, serverPlayerState); - - playerUUIDMessage.sendResponse(new AckMessage()); - }); - - this.server.registerHandler(RemotePlayerConfigMessage.class, this.connectedPlayersOnly((remotePlayerConfigMessage, serverPlayerState) -> - { - serverPlayerState.config.clientConfig = (MultiplayerConfig) remotePlayerConfigMessage.payload; - serverPlayerState.connection.sendMessage(new RemotePlayerConfigMessage(serverPlayerState.config)); - })); - - this.server.registerHandler(NettyCloseEvent.class, closeEvent -> - { - ServerPlayerState dhPlayer = this.playersByConnection.remove(closeEvent.getConnection()); - if (dhPlayer != null) - { - dhPlayer.clearRateLimiterSets(); - dhPlayer.connection = null; - } - }); - } public void handlePluginMessage(IServerPlayerWrapper player, ByteBuf buffer) { - this.playersByUUID.get(player.getUUID()).pluginChannelHandler.decodeAndHandle(buffer, player); + this.connectedPlayers.get(player).connection.decodeAndHandle(buffer); } - private void onConfigChanged() - { - for (ServerPlayerState serverPlayerState : this.getConnectedPlayers()) - { - serverPlayerState.connection.sendMessage(new RemotePlayerConfigMessage(serverPlayerState.config)); - } - } - - private void onServerPortChanged(int ignored) - { - LOGGER.warn("Server port change requires a server restart to take effect."); - } - private void onConnectOverridesChanged(@Nullable Object ignored) - { - for (ServerPlayerState playerState : this.playersByUUID.values()) - { - playerState.sendConnectInfo(); - } - } - - - public Consumer connectedPlayersOnly(BiConsumer next) - { - return msg -> - { - ServerPlayerState serverPlayerState = this.getConnectedPlayer(msg); - if (serverPlayerState != null) - { - next.accept(msg, serverPlayerState); - } - }; - } public Consumer currentLevelOnly(DhServerLevel level, BiConsumer next) { - return this.connectedPlayersOnly((msg, serverPlayerState) -> + return (msg) -> { LodUtil.assertTrue(msg instanceof ILevelRelatedMessage, "Received message does not implement " + ILevelRelatedMessage.class.getSimpleName() + ": " + msg.getClass().getSimpleName()); @@ -164,49 +65,27 @@ public class RemotePlayerConnectionHandler implements Closeable } next.accept(msg, serverPlayerState); - }); - } - - public Iterable getConnectedPlayers() - { - synchronized (this.playersByConnection) - { - return new ArrayList<>(this.playersByConnection.values()); - } - } - - @Nullable - public ServerPlayerState getConnectedPlayer(NettyMessage msg) - { - return this.playersByConnection.get(msg.getConnection()); + }; } public void registerJoinedPlayer(IServerPlayerWrapper serverPlayer) { - this.playersByUUID.put(serverPlayer.getUUID(), new ServerPlayerState(serverPlayer, this.server.port)); + this.connectedPlayers.put(serverPlayer, new ServerPlayerState(serverPlayer)); } public void unregisterLeftPlayer(IServerPlayerWrapper serverPlayer) { - ServerPlayerState playerState = this.playersByUUID.remove(serverPlayer.getUUID()); - playerState.close(); - - INettyConnection connection = this.playersByConnection.inverse().remove(playerState); - if (connection != null) + ServerPlayerState playerState = this.connectedPlayers.remove(serverPlayer); + if (playerState != null) { - connection.disconnect("You have logged out."); + playerState.close(); } } @Override public void close() { - this.portChangeListener.close(); - this.connectIpOverrideChangeListener.close(); - this.connectPortOverrideChangeListener.close(); - this.configChangeListener.close(); - this.server().close(); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index 593723bab..6a514750b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -3,22 +3,21 @@ package com.seibel.distanthorizons.core.multiplayer.server; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; +import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; +import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; +import com.seibel.distanthorizons.core.network.messages.plugin.session.RemotePlayerConfigMessage; import com.seibel.distanthorizons.core.network.messages.plugin.PluginCloseEvent; -import com.seibel.distanthorizons.core.network.messages.plugin.PluginHelloMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.ServerConnectInfoMessage; -import com.seibel.distanthorizons.core.network.netty.INettyConnection; +import com.seibel.distanthorizons.core.network.messages.plugin.base.HelloMessage; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; -import com.seibel.distanthorizons.core.network.messages.netty.fullData.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.netty.fullData.generation.GenTaskPriorityRequestMessage; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelHandler; +import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.fullData.generation.GenTaskPriorityRequestMessage; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateAndConcurrencyLimiter; import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateLimiter; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import org.apache.logging.log4j.LogManager; import org.jetbrains.annotations.NotNull; -import java.net.InetAddress; -import java.net.InetSocketAddress; import java.util.concurrent.ConcurrentHashMap; import static com.seibel.distanthorizons.core.config.Config.Client.Advanced.Multiplayer.ServerNetworking; @@ -29,19 +28,12 @@ public class ServerPlayerState () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); public final IServerPlayerWrapper serverPlayer; - public INettyConnection connection; - - private final int serverPort; - public final PluginChannelHandler pluginChannelHandler = new PluginChannelHandler(); + public final PluginChannelSession connection = new PluginChannelSession(); + private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::onConfigChanged); @NotNull public ConstrainedMultiplayerConfig config = new ConstrainedMultiplayerConfig(); - public final SupplierBasedRateLimiter rateLimitKickTrigger = new SupplierBasedRateLimiter<>( - () -> ServerNetworking.rateLimitHitTolerance.get(), - ignored -> this.connection.disconnect("You have been repeatedly exceeding rate/concurrency limits.") - ); - private final ConcurrentHashMap rateLimiterSets = new ConcurrentHashMap<>(); public RateLimiterSet getRateLimiterSet(DhServerLevel level) { @@ -52,37 +44,38 @@ public class ServerPlayerState this.rateLimiterSets.clear(); } - public ServerPlayerState(IServerPlayerWrapper serverPlayer, int serverPort) + public ServerPlayerState(IServerPlayerWrapper serverPlayer) { this.serverPlayer = serverPlayer; - this.serverPort = serverPort; - this.pluginChannelHandler.registerHandler(PluginHelloMessage.class, msg -> { - this.sendConnectInfo(); + this.connection.registerHandler(RemotePlayerConfigMessage.class, remotePlayerConfigMessage -> + { + this.config.clientConfig = (MultiplayerConfig) remotePlayerConfigMessage.payload; + this.connection.sendMessage(new RemotePlayerConfigMessage(this.config)); }); - this.pluginChannelHandler.registerHandler(PluginCloseEvent.class, event -> { + this.connection.registerHandler(HelloMessage.class, msg -> { + this.initializeLodSession(); + }); + + this.connection.registerHandler(PluginCloseEvent.class, event -> { // Noop }); } - public void sendConnectInfo() + public void initializeLodSession() { - String ipOverride = ServerNetworking.connectIpOverride.get(); - int portOverride = ServerNetworking.connectPortOverride.get(); - - InetAddress ip = ((InetSocketAddress) this.serverPlayer.getRemoteAddress()).getAddress(); - boolean isLanPlayer = !ServerNetworking.enableConnectOverridesInLan.get() && (ip.isLinkLocalAddress() || ip.isSiteLocalAddress()); - - this.pluginChannelHandler.sendMessageServer(this.serverPlayer, new ServerConnectInfoMessage( - !isLanPlayer && !ipOverride.isEmpty() ? ipOverride : null, - !isLanPlayer && portOverride != 0 ? portOverride : this.serverPort - )); } public void close() { - this.pluginChannelHandler.close(); + this.configChangeListener.close(); + this.connection.close(); + } + + private void onConfigChanged() + { + this.connection.sendMessage(new RemotePlayerConfigMessage(this.config)); } @@ -92,7 +85,6 @@ public class ServerPlayerState () -> ServerNetworking.generationRequestRCLimit.get(), msg -> { msg.sendResponse(new RateLimitedException("Full data request rate/concurrency limit: " + ServerPlayerState.this.config.getFullDataRequestConcurrencyLimit())); - ServerPlayerState.this.rateLimitKickTrigger.tryAcquire(null); } ); @@ -100,7 +92,6 @@ public class ServerPlayerState () -> ServerNetworking.genTaskPriorityRequestRateLimit.get(), msg -> { msg.sendResponse(new RateLimitedException("Generation task priority check rate limit: " + ServerPlayerState.this.config.getFullDataRequestConcurrencyLimit())); - ServerPlayerState.this.rateLimitKickTrigger.tryAcquire(null); } ); @@ -108,7 +99,6 @@ public class ServerPlayerState () -> ServerNetworking.loginDataSyncRCLimit.get(), msg -> { msg.sendResponse(new RateLimitedException("Data sync rate/concurrency limit: " + ServerPlayerState.this.config.getLoginDataSyncRCLimit())); - ServerPlayerState.this.rateLimitKickTrigger.tryAcquire(null); } ); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index 17152b5b0..228dc18cb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -21,87 +21,196 @@ package com.seibel.distanthorizons.core.network; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.network.messages.AbstractMessageRegistry; -import com.seibel.distanthorizons.core.network.messages.ICloseEvent; -import com.seibel.distanthorizons.core.network.protocol.INetworkObject; +import com.seibel.distanthorizons.core.network.messages.plugin.base.CancelMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.base.ExceptionMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.PluginCloseEvent; +import com.seibel.distanthorizons.core.network.messages.plugin.PluginMessageRegistry; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; +import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; import com.seibel.distanthorizons.coreapi.ModInfo; +import io.netty.channel.ChannelException; +import io.netty.channel.ChannelHandlerContext; import org.apache.logging.log4j.LogManager; +import java.io.InvalidClassException; import java.util.HashSet; import java.util.Set; +import java.util.concurrent.CancellationException; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.function.Consumer; -public abstract class NetworkEventSource +public abstract class NetworkEventSource { private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); - protected final ConcurrentMap, Set>> handlers = new ConcurrentHashMap<>(); + protected final ConcurrentMap, Set>> handlers = new ConcurrentHashMap<>(); + private final ConcurrentMap> pendingFutures = new ConcurrentHashMap<>(); - - protected final AbstractMessageRegistry messageRegistry; - public NetworkEventSource(AbstractMessageRegistry messageRegistry) + protected boolean hasHandler(Class handlerClass) { - this.messageRegistry = messageRegistry; + return this.handlers.containsKey(handlerClass); } - protected final void handleMessage(TMessage message) + + protected void handleMessage(PluginChannelMessage message) { boolean handled = false; - Set> handlerList = this.handlers.get(message.getClass()); + Set> handlerList = this.handlers.get(message.getClass()); if (handlerList != null) { - for (Consumer handler : handlerList) + for (Consumer handler : handlerList) { handled = true; handler.accept(message); } } - handled |= this.tryHandleMessage(message); + if (message instanceof TrackableMessage) + { + TrackableMessage trackableMessage = (TrackableMessage) message; + ConcurrentMap subMap = this.pendingFutures.get(message.getConnection()); + if (subMap != null) + { + FutureResponseData responseData = subMap.get(trackableMessage.futureId); + if (responseData != null) + { + handled = true; + + if (message instanceof ExceptionMessage) + { + responseData.future.completeExceptionally(((ExceptionMessage) message).exception); + } + else if (message.getClass() != responseData.responseClass) + { + responseData.future.completeExceptionally(new InvalidClassException("Response with invalid type: expected " + responseData.responseClass.getSimpleName() + ", got:" + message)); + } + else + { + responseData.future.complete(trackableMessage); + } + } + } + } - if (!handled && ModInfo.IS_DEV_BUILD) + if (!handled && ModInfo.IS_DEV_BUILD && message.warnWhenUnhandled()) { LOGGER.warn("Unhandled message: " + message); } } - protected boolean tryHandleMessage(TMessage message) + protected void addNewConnection(PluginChannelSession connection) { - // By default, messages are handled only by their direct handlers. - return false; + this.pendingFutures.put(connection, new ConcurrentHashMap<>()); } - public void registerHandler(Class handlerClass, Consumer handlerImplementation) + public void registerHandler(Class handlerClass, Consumer handlerImplementation) { //noinspection unchecked this.handlers.computeIfAbsent(handlerClass, missingHandlerClass -> { - // Will throw if the handler class is not found and not a CloseEvent - if (!ICloseEvent.class.isAssignableFrom(missingHandlerClass)) + // Will throw if the handler class is not found + if (handlerClass != PluginCloseEvent.class) { - this.messageRegistry.getMessageId(handlerClass); + PluginMessageRegistry.INSTANCE.getMessageId(handlerClass); } - return ConcurrentHashMap.newKeySet(); + return new HashSet<>(); }) - .add((Consumer) handlerImplementation); + .add((Consumer) handlerImplementation); } - protected boolean hasHandler(Class handlerClass) - { - return this.handlers.containsKey(handlerClass); - } - - protected void removeHandler(Class handlerClass, Consumer handlerImplementation) + protected void removeHandler(Class handlerClass, Consumer handlerImplementation) { this.handlers.computeIfAbsent(handlerClass, missingHandlerClass -> new HashSet<>()) .remove(handlerImplementation); } + + protected CompletableFuture createRequest(PluginChannelSession connection, TrackableMessage msg, Class responseClass) + { + msg.setConnection(connection); + + CompletableFuture responseFuture = new CompletableFuture<>(); + responseFuture.whenComplete((response, throwable) -> + { + if (!(throwable instanceof ChannelException)) + { + ConcurrentMap subMap = this.pendingFutures.get(connection); + if (subMap != null) + { + subMap.remove(msg.futureId); + } + } + + if (throwable instanceof CancellationException) + { + msg.sendResponse(new CancelMessage()); + } + }); + + ConcurrentMap subMap = this.pendingFutures.get(connection); + if (subMap == null) + { + // Was deleted before adding + responseFuture.completeExceptionally(connection.getCloseReason()); + return responseFuture; + } + subMap.put(msg.futureId, new FutureResponseData(responseClass, responseFuture)); + if (!this.pendingFutures.containsKey(connection)) + { + // Was deleted while adding + // Note: removal from subMap will happen in whenComplete above + responseFuture.completeExceptionally(connection.getCloseReason()); + return responseFuture; + } + // If passed until here, cancelling is up to the cleaning side + + return responseFuture; + } + + protected final void completeAllFuturesExceptionally(PluginChannelSession connection, Throwable cause) + { + ConcurrentMap map = this.pendingFutures.remove(connection); + if (map == null) + { + return; + } + + for (FutureResponseData responseData : map.values()) + { + responseData.future.completeExceptionally(cause); + } + } + + protected final void completeAllFuturesExceptionally(Throwable cause) + { + for (PluginChannelSession connection : this.pendingFutures.keySet()) + { + this.completeAllFuturesExceptionally(connection, cause); + } + } + public void close() { this.handlers.clear(); + this.completeAllFuturesExceptionally(new ChannelException(this.getClass().getSimpleName() + " is closed.")); } -} + + private static class FutureResponseData + { + public final Class responseClass; + public final CompletableFuture future; + + private FutureResponseData(Class responseClass, CompletableFuture future) + { + this.responseClass = responseClass; + //noinspection unchecked + this.future = (CompletableFuture) future; + } + + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/ScopedNetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/ScopedNetworkEventSource.java index 66874f172..a66a2f2b7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/ScopedNetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/ScopedNetworkEventSource.java @@ -19,24 +19,24 @@ package com.seibel.distanthorizons.core.network; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import java.util.function.Consumer; /** Provides a way to register network message handlers which are expected to be removed later. */ -public final class ScopedNetworkEventSource, TMessage extends INetworkObject> extends NetworkEventSource +public final class ScopedNetworkEventSource extends NetworkEventSource { - public final TParent parent; + public final NetworkEventSource parent; private boolean isClosed = false; - public ScopedNetworkEventSource(TParent parent) + public ScopedNetworkEventSource(NetworkEventSource parent) { - super(parent.messageRegistry); this.parent = parent; } @Override - public void registerHandler(Class handlerClass, Consumer handlerImplementation) + public void registerHandler(Class handlerClass, Consumer handlerImplementation) { if (this.isClosed) { @@ -55,9 +55,9 @@ public final class ScopedNetworkEventSource handlerClass : this.handlers.keySet()) + for (Class handlerClass : this.handlers.keySet()) { this.parent.removeHandler(handlerClass, this::handleMessage); } } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/AbstractMessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/AbstractMessageRegistry.java deleted file mode 100644 index 3cf458a56..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/AbstractMessageRegistry.java +++ /dev/null @@ -1,75 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.network.messages; - -import com.google.common.collect.BiMap; -import com.google.common.collect.HashBiMap; -import com.seibel.distanthorizons.core.network.protocol.INetworkObject; - -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.function.Supplier; - -public abstract class AbstractMessageRegistry -{ - private final Map> idToSupplier = new HashMap<>(); - private final BiMap, Integer> classToId = HashBiMap.create(); - - - - protected void registerMessage(Class clazz, Supplier supplier) - { - int id = this.idToSupplier.size() + 1; - this.idToSupplier.put(id, supplier); - this.classToId.put(clazz, id); - } - - public TMessage createMessage(int messageId) throws IllegalArgumentException - { - try - { - return this.idToSupplier.get(messageId).get(); - } - catch (NullPointerException e) - { - throw new IllegalArgumentException("Invalid message ID: " + messageId); - } - } - - @SuppressWarnings("unchecked") - public int getMessageId(TMessage message) - { - return this.getMessageId((Class) message.getClass()); - } - - public int getMessageId(Class messageClass) - { - try - { - return this.classToId.get(messageClass); - } - catch (NullPointerException e) - { - throw new IllegalArgumentException("Message does not have ID assigned to it: " + messageClass.getSimpleName()); - } - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ICloseEvent.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ICloseEvent.java deleted file mode 100644 index 208693a9b..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ICloseEvent.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.seibel.distanthorizons.core.network.messages; - -import com.seibel.distanthorizons.core.network.protocol.INetworkObject; -import io.netty.buffer.ByteBuf; - -public interface ICloseEvent extends INetworkObject -{ - @Override - default void encode(ByteBuf out) { throw new UnsupportedOperationException(this.getClass().getSimpleName() + " is not a real message, and cannot be sent."); } - - @Override - default void decode(ByteBuf in) { throw new UnsupportedOperationException(this.getClass().getSimpleName() + " is not a real message, and cannot be received."); } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PluginMessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PluginMessageRegistry.java new file mode 100644 index 000000000..6befbfa5a --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PluginMessageRegistry.java @@ -0,0 +1,118 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.network.messages; + +import com.google.common.collect.BiMap; +import com.google.common.collect.HashBiMap; +import com.seibel.distanthorizons.core.network.messages.plugin.CurrentLevelKeyMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.base.HelloMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.PluginMessageRegistry; +import com.seibel.distanthorizons.core.network.messages.plugin.base.AckMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.base.CancelMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.base.CloseReasonMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.base.ExceptionMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataPartialUpdateMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceResponseMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.fullData.generation.GenTaskPriorityRequestMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.fullData.generation.GenTaskPriorityResponseMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.session.PlayerUUIDMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.session.RemotePlayerConfigMessage; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +public class PluginMessageRegistry +{ + public static final PluginMessageRegistry INSTANCE = new PluginMessageRegistry(); + + private final Map> idToSupplier = new HashMap<>(); + private final BiMap, Integer> classToId = HashBiMap.create(); + + + + private PluginMessageRegistry() + { + // Note: Messages must have parameterless constructors + + // Always sent by the client + this.registerMessage(HelloMessage.class, HelloMessage::new); + this.registerMessage(CloseReasonMessage.class, CloseReasonMessage::new); + + // Multiverse support + this.registerMessage(CurrentLevelKeyMessage.class, CurrentLevelKeyMessage::new); + + // Core + this.registerMessage(AckMessage.class, AckMessage::new); + this.registerMessage(CancelMessage.class, CancelMessage::new); + this.registerMessage(ExceptionMessage.class, ExceptionMessage::new); + + // ID & config + this.registerMessage(PlayerUUIDMessage.class, PlayerUUIDMessage::new); + this.registerMessage(RemotePlayerConfigMessage.class, RemotePlayerConfigMessage::new); + + // Full data requests & updates + this.registerMessage(FullDataSourceRequestMessage.class, FullDataSourceRequestMessage::new); + this.registerMessage(FullDataSourceResponseMessage.class, FullDataSourceResponseMessage::new); + this.registerMessage(FullDataPartialUpdateMessage.class, FullDataPartialUpdateMessage::new); + } + + + + protected void registerMessage(Class clazz, Supplier supplier) + { + int id = this.idToSupplier.size() + 1; + this.idToSupplier.put(id, supplier); + this.classToId.put(clazz, id); + } + + public PluginChannelMessage createMessage(int messageId) throws IllegalArgumentException + { + try + { + return this.idToSupplier.get(messageId).get(); + } + catch (NullPointerException e) + { + throw new IllegalArgumentException("Invalid message ID: " + messageId); + } + } + + @SuppressWarnings("unchecked") + public int getMessageId(PluginChannelMessage message) + { + return this.gePluginChannelMessageId(message.getClass()); + } + + public int gePluginChannelMessageId(Class messageClass) + { + try + { + return this.classToId.get(messageClass); + } + catch (NullPointerException e) + { + throw new IllegalArgumentException("Message does not have ID assigned to it: " + messageClass.getSimpleName()); + } + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/NettyMessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/NettyMessageRegistry.java deleted file mode 100644 index 2a433e01c..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/NettyMessageRegistry.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.seibel.distanthorizons.core.network.messages.netty; - -import com.seibel.distanthorizons.core.network.messages.AbstractMessageRegistry; -import com.seibel.distanthorizons.core.network.messages.netty.base.*; -import com.seibel.distanthorizons.core.network.messages.netty.fullData.FullDataPartialUpdateMessage; -import com.seibel.distanthorizons.core.network.messages.netty.fullData.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.netty.fullData.FullDataSourceResponseMessage; -import com.seibel.distanthorizons.core.network.messages.netty.fullData.generation.GenTaskPriorityRequestMessage; -import com.seibel.distanthorizons.core.network.messages.netty.fullData.generation.GenTaskPriorityResponseMessage; -import com.seibel.distanthorizons.core.network.messages.netty.session.PlayerUUIDMessage; -import com.seibel.distanthorizons.core.network.messages.netty.session.RemotePlayerConfigMessage; -import com.seibel.distanthorizons.core.network.netty.NettyMessage; - -public class NettyMessageRegistry extends AbstractMessageRegistry -{ - public static final NettyMessageRegistry INSTANCE = new NettyMessageRegistry(); - - private NettyMessageRegistry() - { - // Note: Messages must have parameterless constructors - - // Opening & closing connection - // These messages should be compatible with any previous protocol versions - this.registerMessage(HelloMessage.class, HelloMessage::new); - this.registerMessage(CloseReasonMessage.class, CloseReasonMessage::new); - - // Core - this.registerMessage(AckMessage.class, AckMessage::new); - this.registerMessage(CancelMessage.class, CancelMessage::new); - this.registerMessage(ExceptionMessage.class, ExceptionMessage::new); - - // ID & config - this.registerMessage(PlayerUUIDMessage.class, PlayerUUIDMessage::new); - this.registerMessage(RemotePlayerConfigMessage.class, RemotePlayerConfigMessage::new); - - // Full data requests & updates - this.registerMessage(FullDataSourceRequestMessage.class, FullDataSourceRequestMessage::new); - this.registerMessage(FullDataSourceResponseMessage.class, FullDataSourceResponseMessage::new); - this.registerMessage(FullDataPartialUpdateMessage.class, FullDataPartialUpdateMessage::new); - - // Generation task prioritization - this.registerMessage(GenTaskPriorityRequestMessage.class, GenTaskPriorityRequestMessage::new); - this.registerMessage(GenTaskPriorityResponseMessage.class, GenTaskPriorityResponseMessage::new); - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/HelloMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/HelloMessage.java deleted file mode 100644 index e1c60e105..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/HelloMessage.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.network.messages.netty.base; - -import com.seibel.distanthorizons.core.network.netty.NettyMessage; -import com.seibel.distanthorizons.coreapi.ModInfo; -import io.netty.buffer.ByteBuf; - -public class HelloMessage extends NettyMessage -{ - public int version = ModInfo.PROTOCOL_VERSION; - - - - @Override - public void encode(ByteBuf out) { out.writeInt(this.version); } - - @Override - public void decode(ByteBuf in) { this.version = in.readInt(); } - - @Override public String toString() - { - return super.toString("version=" + this.version); - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/NettyCloseEvent.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/NettyCloseEvent.java deleted file mode 100644 index 34a9c3936..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/NettyCloseEvent.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.network.messages.netty.base; - -import com.seibel.distanthorizons.core.network.messages.ICloseEvent; -import com.seibel.distanthorizons.core.network.netty.NettyMessage; - -/** - * This is not a "real" message, and only used to indicate a disconnection. - * To send a "disconnect reason" message, use {@link CloseReasonMessage}. - */ -public class NettyCloseEvent extends NettyMessage implements ICloseEvent -{ -} - diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/generation/GenTaskPriorityRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/generation/GenTaskPriorityRequestMessage.java deleted file mode 100644 index 36757c86b..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/generation/GenTaskPriorityRequestMessage.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.network.messages.netty.fullData.generation; - -import com.seibel.distanthorizons.core.level.IDhLevel; -import com.seibel.distanthorizons.core.network.messages.netty.ILevelRelatedMessage; -import com.seibel.distanthorizons.core.network.netty.TrackableNettyMessage; -import com.seibel.distanthorizons.core.pos.DhSectionPos; -import io.netty.buffer.ByteBuf; - -import java.util.ArrayList; -import java.util.List; - -public class GenTaskPriorityRequestMessage extends TrackableNettyMessage implements ILevelRelatedMessage -{ - public List posList = new ArrayList<>(); - - private String levelName; - @Override - public String getLevelName() { return this.levelName; } - - - public GenTaskPriorityRequestMessage() { } - public GenTaskPriorityRequestMessage(List posList, IDhLevel level) - { - this.posList = posList; - - // TODO Multiverse support - this.levelName = level.getLevelWrapper().getDimensionType().getDimensionName(); - } - - @Override - protected void encode0(ByteBuf out) - { - this.writeString(this.levelName, out); - this.writeCollection(out, this.posList); - } - - @Override - protected void decode0(ByteBuf in) - { - this.levelName = this.readString(in); - this.readCollection(in, this.posList, DhSectionPos::zero); - } - - @Override public String toString() - { - return super.toString("posList=" + this.posList); - } - -} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/generation/GenTaskPriorityResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/generation/GenTaskPriorityResponseMessage.java deleted file mode 100644 index 45bb0955a..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/generation/GenTaskPriorityResponseMessage.java +++ /dev/null @@ -1,56 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.network.messages.netty.fullData.generation; - -import com.seibel.distanthorizons.core.network.netty.TrackableNettyMessage; -import com.seibel.distanthorizons.core.pos.DhSectionPos; -import io.netty.buffer.ByteBuf; - -import java.util.HashMap; -import java.util.Map; - -public class GenTaskPriorityResponseMessage extends TrackableNettyMessage -{ - public Map posList = new HashMap<>(); - - public GenTaskPriorityResponseMessage() { } - public GenTaskPriorityResponseMessage(Map posList) - { - this.posList = posList; - } - - @Override - protected void encode0(ByteBuf out) - { - this.writeCollection(out, this.posList.entrySet()); - } - - @Override - protected void decode0(ByteBuf in) - { - this.readMap(in, this.posList, DhSectionPos::zero, () -> 0); - } - - @Override public String toString() - { - return super.toString("posList=" + this.posList); - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/ILevelRelatedMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/ILevelRelatedMessage.java similarity index 86% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/ILevelRelatedMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/ILevelRelatedMessage.java index 352665675..8db135a64 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/ILevelRelatedMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/ILevelRelatedMessage.java @@ -1,4 +1,4 @@ -package com.seibel.distanthorizons.core.network.messages.netty; +package com.seibel.distanthorizons.core.network.messages.plugin; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginCloseEvent.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginCloseEvent.java index 9d3c854fb..0174680eb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginCloseEvent.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginCloseEvent.java @@ -2,10 +2,17 @@ package com.seibel.distanthorizons.core.network.messages.plugin; import com.seibel.distanthorizons.core.network.messages.ICloseEvent; import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; +import com.seibel.distanthorizons.core.network.protocol.INetworkObject; +import io.netty.buffer.ByteBuf; /** * This is not a "real" message, and only used to indicate a disconnection. */ -public class PluginCloseEvent extends PluginChannelMessage implements ICloseEvent +public class PluginCloseEvent extends PluginChannelMessage implements ICloseEvent, INetworkObject { -} + @Override + public void encode(ByteBuf out) { throw new UnsupportedOperationException(this.getClass().getSimpleName() + " is not a real message, and cannot be sent."); } + @Override + public void decode(ByteBuf in) { throw new UnsupportedOperationException(this.getClass().getSimpleName() + " is not a real message, and cannot be received."); } + +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginHelloMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginHelloMessage.java deleted file mode 100644 index 7aed2474e..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginHelloMessage.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.seibel.distanthorizons.core.network.messages.plugin; - -import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; -import io.netty.buffer.ByteBuf; - -/** Serves as a trigger for the server to send the first world change. */ -public class PluginHelloMessage extends PluginChannelMessage -{ - @Override - public void encode(ByteBuf out) - { - - } - - @Override - public void decode(ByteBuf in) - { - - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginMessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginMessageRegistry.java deleted file mode 100644 index ffa927d6b..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginMessageRegistry.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.seibel.distanthorizons.core.network.messages.plugin; - -import com.seibel.distanthorizons.core.network.messages.AbstractMessageRegistry; -import com.seibel.distanthorizons.core.network.messages.netty.base.NettyCloseEvent; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; - -public class PluginMessageRegistry extends AbstractMessageRegistry -{ - public static final PluginMessageRegistry INSTANCE = new PluginMessageRegistry(); - - private PluginMessageRegistry() - { - // Note: Messages must have parameterless constructors - - this.registerMessage(PluginHelloMessage.class, PluginHelloMessage::new); - this.registerMessage(CurrentLevelKeyMessage.class, CurrentLevelKeyMessage::new); - this.registerMessage(ServerConnectInfoMessage.class, ServerConnectInfoMessage::new); - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/AckMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/AckMessage.java similarity index 79% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/AckMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/AckMessage.java index 6f9ab1079..3b5d23823 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/AckMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/AckMessage.java @@ -17,16 +17,17 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.netty.base; +package com.seibel.distanthorizons.core.network.messages.plugin.base; -import com.seibel.distanthorizons.core.network.netty.TrackableNettyMessage; +import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; +import com.seibel.distanthorizons.core.network.plugin.TrackableNettyMessage; import io.netty.buffer.ByteBuf; /** * Simple empty response message. * This message is not sent automatically. */ -public class AckMessage extends TrackableNettyMessage +public class AckMessage extends TrackableMessage { public AckMessage() { } @@ -36,4 +37,4 @@ public class AckMessage extends TrackableNettyMessage @Override public void decode0(ByteBuf in) { } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/CancelMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/CancelMessage.java similarity index 77% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/CancelMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/CancelMessage.java index 80c237f76..2fb9b6ac0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/CancelMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/CancelMessage.java @@ -17,12 +17,13 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.netty.base; +package com.seibel.distanthorizons.core.network.messages.plugin.base; -import com.seibel.distanthorizons.core.network.netty.TrackableNettyMessage; +import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; +import com.seibel.distanthorizons.core.network.plugin.TrackableNettyMessage; import io.netty.buffer.ByteBuf; -public class CancelMessage extends TrackableNettyMessage +public class CancelMessage extends TrackableMessage { public CancelMessage() { } @@ -35,4 +36,4 @@ public class CancelMessage extends TrackableNettyMessage public void decode0(ByteBuf in) { } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/CloseReasonMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/CloseReasonMessage.java similarity index 86% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/CloseReasonMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/CloseReasonMessage.java index 593e6cc00..3b38ccd0d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/CloseReasonMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/CloseReasonMessage.java @@ -17,12 +17,13 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.netty.base; +package com.seibel.distanthorizons.core.network.messages.plugin.base; import com.seibel.distanthorizons.core.network.netty.NettyMessage; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; import io.netty.buffer.ByteBuf; -public class CloseReasonMessage extends NettyMessage +public class CloseReasonMessage extends PluginChannelMessage { public String reason; @@ -43,4 +44,4 @@ public class CloseReasonMessage extends NettyMessage return super.toString("reason='" + this.reason + '\''); } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/ExceptionMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ExceptionMessage.java similarity index 88% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/ExceptionMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ExceptionMessage.java index 26a56f103..f22678c78 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/base/ExceptionMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ExceptionMessage.java @@ -17,19 +17,20 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.netty.base; +package com.seibel.distanthorizons.core.network.messages.plugin.base; import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; import com.seibel.distanthorizons.core.network.exceptions.InvalidSectionPosException; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; -import com.seibel.distanthorizons.core.network.netty.TrackableNettyMessage; +import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; +import com.seibel.distanthorizons.core.network.plugin.TrackableNettyMessage; import io.netty.buffer.ByteBuf; import java.util.ArrayList; import java.util.List; -public class ExceptionMessage extends TrackableNettyMessage +public class ExceptionMessage extends TrackableMessage { private static final List> exceptionMap = new ArrayList>() {{ @@ -66,4 +67,4 @@ public class ExceptionMessage extends TrackableNettyMessage return super.toString("exception=" + this.exception); } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/HelloMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/HelloMessage.java new file mode 100644 index 000000000..63f5c826f --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/HelloMessage.java @@ -0,0 +1,24 @@ +package com.seibel.distanthorizons.core.network.messages.plugin.base; + +import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; +import com.seibel.distanthorizons.coreapi.ModInfo; +import io.netty.buffer.ByteBuf; + +/** Serves as a trigger for the server to send the first world change. */ +public class HelloMessage extends PluginChannelMessage +{ + public short version = ModInfo.PROTOCOL_VERSION; + + @Override + public void encode(ByteBuf out) + { + out.writeShort(this.version); + } + + @Override + public void decode(ByteBuf in) + { + this.version = in.readShort(); + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/FullDataPartialUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataPartialUpdateMessage.java similarity index 87% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/FullDataPartialUpdateMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataPartialUpdateMessage.java index 069bbdafd..c3bd8d20d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/FullDataPartialUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataPartialUpdateMessage.java @@ -17,13 +17,14 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.netty.fullData; +package com.seibel.distanthorizons.core.network.messages.plugin.fullData; import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; -import com.seibel.distanthorizons.core.network.messages.netty.ILevelRelatedMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.ILevelRelatedMessage; import com.seibel.distanthorizons.core.network.netty.NettyMessage; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; @@ -31,7 +32,7 @@ import io.netty.buffer.ByteBuf; import java.io.IOException; -public class FullDataPartialUpdateMessage extends NettyMessage implements ILevelRelatedMessage +public class FullDataPartialUpdateMessage extends PluginChannelMessage implements ILevelRelatedMessage { private String levelName; @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/FullDataSourceRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceRequestMessage.java similarity index 84% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/FullDataSourceRequestMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceRequestMessage.java index f1d943dec..4a3417759 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/FullDataSourceRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceRequestMessage.java @@ -17,10 +17,11 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.netty.fullData; +package com.seibel.distanthorizons.core.network.messages.plugin.fullData; -import com.seibel.distanthorizons.core.network.messages.netty.ILevelRelatedMessage; -import com.seibel.distanthorizons.core.network.netty.TrackableNettyMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.ILevelRelatedMessage; +import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; +import com.seibel.distanthorizons.core.network.plugin.TrackableNettyMessage; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; @@ -28,7 +29,7 @@ import io.netty.buffer.ByteBuf; import javax.annotation.Nullable; -public class FullDataSourceRequestMessage extends TrackableNettyMessage implements ILevelRelatedMessage +public class FullDataSourceRequestMessage extends TrackableMessage implements ILevelRelatedMessage { private String levelName; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceResponseMessage.java similarity index 86% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/FullDataSourceResponseMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceResponseMessage.java index 36cc990eb..2d423875c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/fullData/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceResponseMessage.java @@ -17,12 +17,13 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.netty.fullData; +package com.seibel.distanthorizons.core.network.messages.plugin.fullData; import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; -import com.seibel.distanthorizons.core.network.netty.TrackableNettyMessage; +import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; +import com.seibel.distanthorizons.core.network.plugin.TrackableNettyMessage; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; import io.netty.buffer.ByteBuf; @@ -34,7 +35,7 @@ import java.io.IOException; * Response message, containing the requested full data source, * or nothing if requested in updates-only mode and the data was not updated. */ -public class FullDataSourceResponseMessage extends TrackableNettyMessage +public class FullDataSourceResponseMessage extends TrackableMessage { @Nullable public FullDataSourceV2DTO dataSourceDto; @@ -56,7 +57,8 @@ public class FullDataSourceResponseMessage extends TrackableNettyMessage } } - @Override public void encode0(ByteBuf out) + @Override + public void encode0(ByteBuf out) { if (this.writeOptional(out, this.dataSourceDto)) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/session/PlayerUUIDMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/PlayerUUIDMessage.java similarity index 83% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/session/PlayerUUIDMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/PlayerUUIDMessage.java index f90f24538..571950061 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/session/PlayerUUIDMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/PlayerUUIDMessage.java @@ -17,14 +17,15 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.netty.session; +package com.seibel.distanthorizons.core.network.messages.plugin.session; -import com.seibel.distanthorizons.core.network.netty.TrackableNettyMessage; +import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; +import com.seibel.distanthorizons.core.network.plugin.TrackableNettyMessage; import io.netty.buffer.ByteBuf; import java.util.UUID; -public class PlayerUUIDMessage extends TrackableNettyMessage +public class PlayerUUIDMessage extends TrackableMessage { public UUID playerUUID; @@ -46,4 +47,4 @@ public class PlayerUUIDMessage extends TrackableNettyMessage return super.toString("playerUUID=" + this.playerUUID); } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/session/RemotePlayerConfigMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/RemotePlayerConfigMessage.java similarity index 60% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/session/RemotePlayerConfigMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/RemotePlayerConfigMessage.java index 93c4cdf32..7c1395b41 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/netty/session/RemotePlayerConfigMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/RemotePlayerConfigMessage.java @@ -17,30 +17,25 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.netty.session; +package com.seibel.distanthorizons.core.network.messages.plugin.session; import com.seibel.distanthorizons.core.multiplayer.config.AbstractMultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; -import com.seibel.distanthorizons.core.network.netty.NettyMessage; import io.netty.buffer.ByteBuf; -public class RemotePlayerConfigMessage extends NettyMessage +public class RemotePlayerConfigMessage extends PluginChannelMessage { - public AbstractMultiplayerConfig payload; - - public RemotePlayerConfigMessage() { } - public RemotePlayerConfigMessage(AbstractMultiplayerConfig payload) { this.payload = payload; } + public AbstractMultiplayerConfig payload; - @Override - public void encode(ByteBuf out) { this.payload.encode(out); } + public RemotePlayerConfigMessage() { } + public RemotePlayerConfigMessage(AbstractMultiplayerConfig payload) { this.payload = payload; } - @Override - public void decode(ByteBuf in) { this.payload = INetworkObject.readToObject(new MultiplayerConfig(), in); } + @Override + public void encode(ByteBuf out) { this.payload.encode(out); } - @Override public String toString() - { - return super.toString("payload=" + this.payload); - } + @Override + public void decode(ByteBuf in) { this.payload = INetworkObject.readToObject(new MultiplayerConfig(), in); } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java deleted file mode 100644 index 50b343c58..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyClient.java +++ /dev/null @@ -1,224 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.network.netty; - -import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.network.messages.netty.base.NettyCloseEvent; -import com.seibel.distanthorizons.core.network.messages.netty.base.CloseReasonMessage; -import com.seibel.distanthorizons.core.network.messages.netty.base.HelloMessage; -import com.seibel.distanthorizons.core.network.protocol.netty.NettyMessageHandler; -import com.seibel.distanthorizons.core.network.protocol.netty.NettyChannelInitializer; -import io.netty.bootstrap.Bootstrap; -import io.netty.channel.*; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.nio.NioSocketChannel; -import io.netty.util.concurrent.DefaultThreadFactory; -import org.apache.logging.log4j.LogManager; - -import javax.annotation.Nullable; -import java.net.InetSocketAddress; -import java.util.EnumSet; -import java.util.Set; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; - -public class NettyClient extends NettyEventSource implements INettyConnection, AutoCloseable -{ - private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), - () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); - - private enum EConnectionState - { - INITIAL, - OPEN, - RECONNECTING, - GOT_CLOSE_REASON, - CLOSED - } - private static final Set closedStates = EnumSet.of( - EConnectionState.GOT_CLOSE_REASON, - EConnectionState.CLOSED - ); - private final AtomicReference connectionState = new AtomicReference<>(EConnectionState.INITIAL); - /** Indicates whether the client is working. */ - public boolean isInitialized() { return this.connectionState.get() != EConnectionState.INITIAL; } - /** Indicates whether the client is closed(-ing) and should not be used. */ - public boolean isClosed() { return closedStates.contains(this.connectionState.get()); } - - private static final int RECONNECTION_DELAY_SEC = 5; - public static final int RECONNECTION_ATTEMPTS = 3; - - private InetSocketAddress address; - - @Nullable - private Throwable closeReason; - @Override - @Nullable - public Throwable getCloseReason() { return this.closeReason; } - - private final EventLoopGroup workerGroup = new NioEventLoopGroup(0, new DefaultThreadFactory("DH-Network - Client Thread")); - private final Bootstrap clientBootstrap = new Bootstrap() - .group(this.workerGroup) - .channel(NioSocketChannel.class) - .option(ChannelOption.SO_KEEPALIVE, true) - .handler(new NettyChannelInitializer(new NettyMessageHandler( - (ctx, msg) -> { - msg.setConnection(this); - this.handleMessage(msg); - }, - ctx -> this.addNewConnection(this), - (ctx, closeReason) -> this.closeReason = closeReason - ))); - - private Channel channel; - - private final AtomicInteger reconnectionAttemptsLeft = new AtomicInteger(RECONNECTION_ATTEMPTS); - /** Returns the amount of reconnections the client will attempt to perform before giving up. */ - public int getReconnectionAttemptsLeft() { return this.reconnectionAttemptsLeft.get(); } - - - public NettyClient() - { - this.registerHandler(CloseReasonMessage.class, closeReasonMessage -> - { - String fullCloseText = "[Server] " + closeReasonMessage.reason; - LOGGER.warn(fullCloseText); - this.closeReason = new Exception(fullCloseText); - this.connectionState.set(EConnectionState.GOT_CLOSE_REASON); - }); - - this.registerHandler(NettyCloseEvent.class, closeEvent -> - { - LOGGER.info("Disconnected from server: "+this.getRemoteAddress()); - if (this.connectionState.get() == EConnectionState.GOT_CLOSE_REASON) - { - this.close(); - } - }); - } - - public void resetAndConnectTo(String host, int port) - { - if (this.connectionState.getAndUpdate(state -> state != EConnectionState.CLOSED ? EConnectionState.INITIAL : state) == EConnectionState.CLOSED) - { - return; - } - - // Remove IPv6 brackets - host = host.replaceAll("[\\[\\]]", ""); - this.address = new InetSocketAddress(host, port); - - if (this.channel != null) - { - this.channel.close().syncUninterruptibly(); - } - - this.reconnectionAttemptsLeft.set(RECONNECTION_ATTEMPTS); - this.connect(); - } - - public void connect() - { - if (!this.connectionState.compareAndSet(EConnectionState.INITIAL, EConnectionState.OPEN)) - { - return; - } - - LOGGER.info("Connecting to server: " + this.address); - ChannelFuture connectFuture = this.clientBootstrap.connect(this.address); - this.channel = connectFuture.channel(); - - connectFuture.addListener((ChannelFuture channelFuture) -> - { - if (!channelFuture.isSuccess()) - { - LOGGER.info("Connection failed: " + channelFuture.cause()); - return; - } - - this.sendMessage(new HelloMessage()); - }); - - this.channel.closeFuture().addListener((ChannelFuture channelFuture) -> - { - this.completeAllFuturesExceptionally(channelFuture.cause() != null - ? channelFuture.cause() - : new ChannelException("Channel is closed.")); - - if (this.connectionState.get() == EConnectionState.OPEN) - { - int reconnectionAttemptsLeft = this.reconnectionAttemptsLeft.decrementAndGet(); - LOGGER.info("Reconnection attempts left: [" + this.reconnectionAttemptsLeft.get() + "] of [" + RECONNECTION_ATTEMPTS + "]."); - - if (reconnectionAttemptsLeft != 0) - { - if (this.connectionState.compareAndSet(EConnectionState.OPEN, EConnectionState.RECONNECTING)) - { - this.workerGroup.schedule(() -> - { - if (this.connectionState.compareAndSet(EConnectionState.RECONNECTING, EConnectionState.INITIAL)) - { - this.connect(); - } - }, RECONNECTION_DELAY_SEC, TimeUnit.SECONDS); - } - } - else - { - this.connectionState.compareAndSet(EConnectionState.OPEN, EConnectionState.GOT_CLOSE_REASON); - } - } - }); - } - - @Override - public ChannelHandlerContext getChannelContext() - { - if (this.channel == null) - { - return null; - } - return this.channel.pipeline().context(NettyMessageHandler.class); - } - - @Override - public NettyEventSource getRequestHandler() - { - return this; - } - - @Override - public void close() - { - EConnectionState stateChangeResult = this.connectionState.getAndSet(EConnectionState.CLOSED); - if (stateChangeResult == EConnectionState.CLOSED || stateChangeResult == EConnectionState.INITIAL) - { - return; - } - - this.channel.close().syncUninterruptibly(); - this.workerGroup.shutdownGracefully(); - - super.close(); - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyEventSource.java deleted file mode 100644 index c4cdd97ed..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyEventSource.java +++ /dev/null @@ -1,163 +0,0 @@ -package com.seibel.distanthorizons.core.network.netty; - -import com.seibel.distanthorizons.core.network.NetworkEventSource; -import com.seibel.distanthorizons.core.network.messages.netty.base.NettyCloseEvent; -import com.seibel.distanthorizons.core.network.messages.netty.NettyMessageRegistry; -import com.seibel.distanthorizons.core.network.messages.netty.base.CancelMessage; -import com.seibel.distanthorizons.core.network.messages.netty.base.ExceptionMessage; -import io.netty.channel.ChannelException; - -import java.io.InvalidClassException; -import java.util.concurrent.CancellationException; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; -import java.util.function.Consumer; - -public abstract class NettyEventSource extends NetworkEventSource -{ - private final ConcurrentMap> pendingFutures = new ConcurrentHashMap<>(); - - public NettyEventSource() - { - super(NettyMessageRegistry.INSTANCE); - } - - - @Override - public void registerHandler(Class handlerClass, Consumer handlerImplementation) - { - if (handlerClass != NettyCloseEvent.class) - { - // Will throw if the handler class is not found - this.messageRegistry.getMessageId(handlerClass); - } - super.registerHandler(handlerClass, handlerImplementation); - } - - @Override - protected boolean tryHandleMessage(NettyMessage message) - { - if (message instanceof TrackableNettyMessage) - { - TrackableNettyMessage trackableMessage = (TrackableNettyMessage) message; - ConcurrentMap subMap = this.pendingFutures.get(message.getConnection()); - if (subMap != null) - { - FutureResponseData responseData = subMap.get(trackableMessage.futureId); - if (responseData != null) - { - if (message instanceof ExceptionMessage) - { - responseData.future.completeExceptionally(((ExceptionMessage) message).exception); - } - else if (message.getClass() != responseData.responseClass) - { - responseData.future.completeExceptionally(new InvalidClassException("Response with invalid type: expected " + responseData.responseClass.getSimpleName() + ", got:" + message)); - } - else - { - responseData.future.complete(trackableMessage); - } - - return true; - } - } - } - - // Still return true if message should be silent when unhandled - return !message.warnWhenUnhandled(); - } - - protected void addNewConnection(INettyConnection connection) - { - this.pendingFutures.put(connection, new ConcurrentHashMap<>()); - } - - public CompletableFuture createRequest(INettyConnection connection, TrackableNettyMessage msg, Class responseClass) - { - msg.setConnection(connection); - - CompletableFuture responseFuture = new CompletableFuture<>(); - responseFuture.whenComplete((response, throwable) -> - { - if (!(throwable instanceof ChannelException)) - { - ConcurrentMap subMap = this.pendingFutures.get(connection); - if (subMap != null) - { - subMap.remove(msg.futureId); - } - } - - if (throwable instanceof CancellationException) - { - msg.sendResponse(new CancelMessage()); - } - }); - - ConcurrentMap subMap = this.pendingFutures.get(connection); - if (subMap == null) - { - // Was deleted before adding - responseFuture.completeExceptionally(connection.getCloseReason()); - return responseFuture; - } - subMap.put(msg.futureId, new FutureResponseData(responseClass, responseFuture)); - if (!this.pendingFutures.containsKey(connection)) - { - // Was deleted while adding - // Note: removal from subMap will happen in whenComplete above - responseFuture.completeExceptionally(connection.getCloseReason()); - return responseFuture; - } - // If passed until here, cancelling is up to the cleaning side - - return responseFuture; - } - - protected final void completeAllFuturesExceptionally(INettyConnection connection, Throwable cause) - { - ConcurrentMap map = this.pendingFutures.remove(connection); - if (map == null) - { - return; - } - - for (FutureResponseData responseData : map.values()) - { - responseData.future.completeExceptionally(cause); - } - } - - protected final void completeAllFuturesExceptionally(Throwable cause) - { - for (INettyConnection connection : this.pendingFutures.keySet()) - { - this.completeAllFuturesExceptionally(connection, cause); - } - } - - @Override - public void close() - { - super.close(); - this.completeAllFuturesExceptionally(new ChannelException(this.getClass().getSimpleName() + " is closed.")); - } - - - private static class FutureResponseData - { - public final Class responseClass; - public final CompletableFuture future; - - private FutureResponseData(Class responseClass, CompletableFuture future) - { - this.responseClass = responseClass; - //noinspection unchecked - this.future = (CompletableFuture) future; - } - - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyMessage.java deleted file mode 100644 index c191c43b7..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyMessage.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.network.netty; - -import com.seibel.distanthorizons.core.network.netty.INettyConnection; -import com.seibel.distanthorizons.core.network.protocol.INetworkObject; - -import javax.annotation.Nullable; - -public abstract class NettyMessage implements INetworkObject -{ - private INettyConnection connection = null; - - public boolean warnWhenUnhandled() { return true; } - - public INettyConnection getConnection() - { - return this.connection; - } - - public void setConnection(INettyConnection connection) - { - if (this.connection != null) - { - throw new IllegalStateException("Channel context cannot be changed after initial setting."); - } - this.connection = connection; - } - - @Override - public String toString() - { - return this.toString(" "); - } - - protected String toString(@Nullable String extraData) - { - return this.getClass().getSimpleName() + "{" + extraData + '}'; - } - -} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyServer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyServer.java deleted file mode 100644 index ef720fd14..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/NettyServer.java +++ /dev/null @@ -1,177 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.network.netty; - -import com.google.common.collect.MapMaker; -import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.network.messages.netty.base.NettyCloseEvent; -import com.seibel.distanthorizons.core.network.messages.netty.base.HelloMessage; -import com.seibel.distanthorizons.core.network.protocol.netty.NettyMessageHandler; -import com.seibel.distanthorizons.core.network.protocol.netty.NettyChannelInitializer; -import com.seibel.distanthorizons.coreapi.ModInfo; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.Channel; -import io.netty.channel.ChannelFuture; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import io.netty.util.concurrent.DefaultThreadFactory; -import org.apache.logging.log4j.LogManager; - -import javax.annotation.Nullable; -import java.util.concurrent.ConcurrentMap; -import java.util.concurrent.atomic.AtomicBoolean; - -public class NettyServer extends NettyEventSource implements AutoCloseable -{ - private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), - () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); - - public final int port; - - private final EventLoopGroup bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("DH-Network - Server Boss Thread")); - private final EventLoopGroup workerGroup = new NioEventLoopGroup(0, new DefaultThreadFactory("DH-Network - Server Worker Thread")); - private final AtomicBoolean isClosed = new AtomicBoolean(); - - private final ConcurrentMap connections = new MapMaker().weakKeys().weakValues().makeMap(); - - - - public NettyServer(int port) - { - this.port = port; - - LOGGER.info("Starting server on port "+port); - this.registerHandlers(); - this.bind(); - } - - private void registerHandlers() - { - this.registerHandler(HelloMessage.class, helloMessage -> - { - INettyConnection connection = helloMessage.getConnection(); - LOGGER.info("Client connected: "+connection.getRemoteAddress()); - - if (helloMessage.version != ModInfo.PROTOCOL_VERSION) - { - try - { - String disconnectReason = "Version mismatch. Server version: ["+ModInfo.PROTOCOL_VERSION+"], client version: ["+helloMessage.version+"]."; - LOGGER.info("Disconnecting the client ["+connection.getRemoteAddress()+"]: "+disconnectReason); - connection.disconnect(disconnectReason); - } - catch (Exception e) - { - throw new RuntimeException(e); - } - return; - } - - connection.sendMessage(new HelloMessage()); - }); - - this.registerHandler(NettyCloseEvent.class, closeEvent -> - { - INettyConnection connection = closeEvent.getConnection(); - LOGGER.info("Client disconnected: "+connection.getRemoteAddress()); - this.completeAllFuturesExceptionally(closeEvent.getConnection(), connection.getCloseReason()); - }); - } - - private void bind() - { - ServerBootstrap bootstrap = new ServerBootstrap() - .group(this.bossGroup, this.workerGroup) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.DEBUG)) - .childHandler(new NettyChannelInitializer(new NettyMessageHandler( - (ctx, msg) -> { - msg.setConnection(this.connections.computeIfAbsent(ctx, Connection::new)); - this.handleMessage(msg); - }, - ctx -> this.addNewConnection(this.connections.computeIfAbsent(ctx, Connection::new)), - (ctx, closeReason) -> ((Connection) this.connections.computeIfAbsent(ctx, Connection::new)).closeReason = closeReason - ))); - - ChannelFuture bindFuture = bootstrap.bind(this.port); - bindFuture.addListener((ChannelFuture channelFuture) -> - { - if (!channelFuture.isSuccess()) - { - throw new RuntimeException("Failed to bind: " + channelFuture.cause()); - } - - LOGGER.info("Server is started on port "+this.port); - }); - - Channel channel = bindFuture.channel(); - channel.closeFuture().addListener(future -> this.close()); - } - - @Override - public void close() - { - if (!this.isClosed.compareAndSet(false, true)) - { - return; - } - - LOGGER.info("Shutting down the network server."); - this.workerGroup.shutdownGracefully().syncUninterruptibly(); - this.bossGroup.shutdownGracefully().syncUninterruptibly(); - LOGGER.info("Network server has been closed."); - - super.close(); - } - - public class Connection implements INettyConnection - { - private final ChannelHandlerContext channelContext; - - @Nullable - private Throwable closeReason; - @Override - @Nullable - public Throwable getCloseReason() { return this.closeReason; } - - public Connection(ChannelHandlerContext channelContext) - { - this.channelContext = channelContext; - } - - @Override - public ChannelHandlerContext getChannelContext() - { - return this.channelContext; - } - - @Override - public NettyEventSource getRequestHandler() - { - return NettyServer.this; - } - - } -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelMessage.java index eeec477c1..0d85d53e8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelMessage.java @@ -5,5 +5,21 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapp public abstract class PluginChannelMessage implements INetworkObject { - public IServerPlayerWrapper serverPlayer; -} + public PluginChannelSession session = null; + + public boolean warnWhenUnhandled() { return true; } + + public PluginChannelSession getConnection() + { + return this.session; + } + + public void setConnection(PluginChannelSession connection) + { + if (this.session != null) + { + throw new IllegalStateException("Session cannot be changed after initialization."); + } + this.session = connection; + } +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java similarity index 53% rename from core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelHandler.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java index a35e3e213..7824d2872 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java @@ -6,25 +6,32 @@ import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.network.NetworkEventSource; import com.seibel.distanthorizons.core.network.messages.plugin.PluginCloseEvent; import com.seibel.distanthorizons.core.network.messages.plugin.PluginMessageRegistry; -import com.seibel.distanthorizons.core.network.protocol.plugin.PluginMessageDecoder; import com.seibel.distanthorizons.core.network.protocol.plugin.PluginMessageEncoder; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; +import com.seibel.distanthorizons.coreapi.ModInfo; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; +import io.netty.channel.ChannelException; +import io.netty.channel.ChannelHandlerContext; import org.apache.logging.log4j.LogManager; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; +import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Consumer; -public class PluginChannelHandler extends NetworkEventSource +public class PluginChannelSession extends NetworkEventSource { + /** + * 4 MiB should be enough for any transferred data.
+ * Currently largest transferred data is DH full data sections, which usually don't exceed 1-2 MiB in size. + */ + private static final int MAX_MESSAGE_LENGTH = 4194304; + private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); - private final PluginMessageDecoder messageDecoder = new PluginMessageDecoder(); - private final PluginMessageEncoder messageEncoder = new PluginMessageEncoder(); private final IPluginPacketSender packetSender = SingletonInjector.INSTANCE.get(IPluginPacketSender.class); /** @@ -33,15 +40,22 @@ public class PluginChannelHandler extends NetworkEventSource messages = new ArrayList<>(); - this.messageDecoder.decode(byteBuf, messages); - - for (Object msgObj : messages) + int version = byteBuf.readShort(); + if (version != ModInfo.PROTOCOL_VERSION) { - PluginChannelMessage msg = (PluginChannelMessage) msgObj; - msg.serverPlayer = serverPlayer; - - this.handleMessage(msg); + return; } + + PluginChannelMessage msg = PluginMessageRegistry.INSTANCE.createMessage(byteBuf.readUnsignedShort()); + msg.decode(byteBuf); + msg.serverPlayer = this.serverPlayer; + + this.handleMessage(msg); } catch (Throwable e) { @@ -71,13 +86,31 @@ public class PluginChannelHandler extends NetworkEventSource CompletableFuture sendRequest(TrackableMessage msg, Class responseClass) { - this.packetSender.sendPluginPacketClient(buffer -> this.messageEncoder.encode(message, buffer)); + CompletableFuture responseFuture = this.createRequest(this, msg, responseClass); + this.sendMessage(msg); + return responseFuture; } - public void sendMessageServer(@Nullable IServerPlayerWrapper serverPlayer, PluginChannelMessage message) + + public void sendMessage(PluginChannelMessage message) { - this.packetSender.sendPluginPacketServer(serverPlayer, buffer -> this.messageEncoder.encode(message, buffer)); + LOGGER.debug("Sending message: " + message); + + Consumer encoder = buffer -> { + buffer.writeShort(ModInfo.PROTOCOL_VERSION); + buffer.writeShort(PluginMessageRegistry.INSTANCE.getMessageId(message)); + message.encode(buffer); + }; + + if (this.serverPlayer != null) + { + this.packetSender.sendPluginPacketServer(this.serverPlayer, encoder); + } + else + { + this.packetSender.sendPluginPacketClient(encoder); + } } @Override @@ -99,4 +132,4 @@ public class PluginChannelHandler extends NetworkEventSource Config.Client.Advanced.Logging.logNetworkEvent.get()); - @Nullable - ChannelHandlerContext getChannelContext(); - NettyEventSource getRequestHandler(); - @Nullable - Throwable getCloseReason(); - - @Nullable - default SocketAddress getRemoteAddress() + public void sendMessage(PluginChannelMessage message) { - ChannelHandlerContext ctx = this.getChannelContext(); - if (ctx == null) - { - return null; - } - - return ctx.channel().remoteAddress(); - } - - default CompletableFuture sendMessage(NettyMessage message) - { - LOGGER.debug("Sending message: " + message); + this.LOGGER.debug("Sending message: " + message); CompletableFuture future = new CompletableFuture<>(); - ChannelHandlerContext ctx = this.getChannelContext(); - if (ctx == null) + if (this.serverPlayer != null) { - future.completeExceptionally(new ChannelException("Channel is closed.")); - return future; + packetSender.sendPluginPacketServer(this.serverPlayer, message); } - ctx.writeAndFlush(message).addListener(writeFuture -> - { - if (writeFuture.cause() != null) - { - future.completeExceptionally(writeFuture.cause()); - } - else - { - future.complete(null); - } - }); return future; } @@ -88,4 +67,4 @@ public interface INettyConnection .addListener(ChannelFutureListener.CLOSE); } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/TrackableNettyMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/TrackableMessage.java similarity index 74% rename from core/src/main/java/com/seibel/distanthorizons/core/network/netty/TrackableNettyMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/plugin/TrackableMessage.java index c090a6662..878a2d6d5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/netty/TrackableNettyMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/TrackableMessage.java @@ -17,20 +17,19 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.netty; +package com.seibel.distanthorizons.core.network.plugin; import com.google.common.collect.MapMaker; import com.seibel.distanthorizons.core.api.internal.SharedApi; -import com.seibel.distanthorizons.core.network.messages.netty.base.ExceptionMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.base.ExceptionMessage; import com.seibel.distanthorizons.core.world.EWorldEnvironment; import io.netty.buffer.ByteBuf; -import javax.annotation.Nullable; import java.util.Objects; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; -public abstract class TrackableNettyMessage extends NettyMessage +public abstract class TrackableMessage extends PluginChannelMessage { private static final AtomicInteger lastId = new AtomicInteger(); // 32 bits - Context ID (not transmitted) @@ -40,16 +39,16 @@ public abstract class TrackableNettyMessage extends NettyMessage | ((Objects.requireNonNull(SharedApi.getEnvironment()) == EWorldEnvironment.Server_Only ? 1 : 0) << 31); private static final AtomicInteger lastContextId = new AtomicInteger(); - private static final ConcurrentMap connectionToIdMap = new MapMaker().weakKeys().makeMap(); + private static final ConcurrentMap connectionToIdMap = new MapMaker().weakKeys().makeMap(); - public void sendResponse(TrackableNettyMessage responseMessage) + public void sendResponse(TrackableMessage responseMessage) { responseMessage.futureId = this.futureId; - this.getConnection().sendMessage(responseMessage); + this.session.sendMessage(responseMessage); } @Override - public void setConnection(INettyConnection connection) + public void setConnection(PluginChannelSession connection) { super.setConnection(connection); this.futureId |= (long) connectionToIdMap.computeIfAbsent(connection, k -> lastContextId.getAndIncrement()) << 32; @@ -91,18 +90,4 @@ public abstract class TrackableNettyMessage extends NettyMessage protected abstract void encode0(ByteBuf out) throws Exception; protected abstract void decode0(ByteBuf in) throws Exception; - @Override - public String toString() - { - return this.toString(null); - } - - @Override protected String toString(@Nullable String extraData) - { - return super.toString( - "futureId=" + this.futureId + - (extraData != null ? ", " + extraData : "") - ); - } - -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageDecoder.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageDecoder.java deleted file mode 100644 index c23ef1374..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageDecoder.java +++ /dev/null @@ -1,45 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.network.protocol; - -import com.seibel.distanthorizons.core.network.messages.AbstractMessageRegistry; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.ByteToMessageDecoder; - -import java.util.List; - -public class MessageDecoder extends ByteToMessageDecoder -{ - private final AbstractMessageRegistry messageRegistry; - - public MessageDecoder(AbstractMessageRegistry messageRegistry) - { - this.messageRegistry = messageRegistry; - } - - @Override - protected void decode(ChannelHandlerContext channelContext, ByteBuf inputByteBuf, List outputDecodedObjectList) - { - TMessage message = this.messageRegistry.createMessage(inputByteBuf.readUnsignedShort()); - outputDecodedObjectList.add(INetworkObject.readToObject(message, inputByteBuf)); - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageEncoder.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageEncoder.java deleted file mode 100644 index 79bb90f18..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageEncoder.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.network.protocol; - -import com.seibel.distanthorizons.core.network.messages.AbstractMessageRegistry; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; -import io.netty.handler.codec.MessageToByteEncoder; - -public class MessageEncoder extends MessageToByteEncoder -{ - private final AbstractMessageRegistry messageRegistry; - - public MessageEncoder(AbstractMessageRegistry messageRegistry, Class messageClass) - { - super(messageClass); - this.messageRegistry = messageRegistry; - } - - @Override - protected void encode(ChannelHandlerContext channelContext, TMessage message, ByteBuf outputByteBuf) throws IllegalArgumentException - { - outputByteBuf.writeShort(this.messageRegistry.getMessageId(message)); - message.encode(outputByteBuf); - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyChannelInitializer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyChannelInitializer.java deleted file mode 100644 index 4e9ee7e5d..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyChannelInitializer.java +++ /dev/null @@ -1,65 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.network.protocol.netty; - -import com.seibel.distanthorizons.core.network.messages.netty.NettyMessageRegistry; -import com.seibel.distanthorizons.core.network.netty.NettyMessage; -import com.seibel.distanthorizons.core.network.protocol.MessageDecoder; -import com.seibel.distanthorizons.core.network.protocol.MessageEncoder; -import io.netty.channel.*; -import io.netty.channel.socket.SocketChannel; -import io.netty.handler.codec.LengthFieldBasedFrameDecoder; -import io.netty.handler.codec.LengthFieldPrepender; -import io.netty.handler.flush.FlushConsolidationHandler; -import org.jetbrains.annotations.NotNull; - -/** Used when creating a network channel */ -public class NettyChannelInitializer extends ChannelInitializer -{ - /** - * 4 MiB should be enough for any transferred data.
- * Currently largest transferred data is DH full data sections, which usually don't exceed 1-2 MiB in size. - */ - private static final int MAX_MESSAGE_LENGTH = 4194304; - - private final NettyMessageHandler messageHandler; - - public NettyChannelInitializer(NettyMessageHandler messageHandler) { this.messageHandler = messageHandler; } - - @Override - public void initChannel(@NotNull SocketChannel socketChannel) - { - ChannelPipeline pipeline = socketChannel.pipeline(); - - // Encoder - pipeline.addLast(new FlushConsolidationHandler(256, true)); - pipeline.addLast(new LengthFieldPrepender(Integer.BYTES)); - pipeline.addLast(new MessageEncoder<>(NettyMessageRegistry.INSTANCE, NettyMessage.class)); - pipeline.addLast(new NettyOutboundExceptionRouter()); - - // Decoder - pipeline.addLast(new LengthFieldBasedFrameDecoder(MAX_MESSAGE_LENGTH, 0, Integer.BYTES, 0, Integer.BYTES)); - pipeline.addLast(new MessageDecoder<>(NettyMessageRegistry.INSTANCE)); - - // Handler - pipeline.addLast(this.messageHandler); - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyMessageHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyMessageHandler.java deleted file mode 100644 index 6b39c1420..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyMessageHandler.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.network.protocol.netty; - -import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.network.messages.netty.base.NettyCloseEvent; -import com.seibel.distanthorizons.core.network.netty.NettyMessage; -import io.netty.channel.ChannelHandler; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.SimpleChannelInboundHandler; -import org.apache.logging.log4j.LogManager; -import org.jetbrains.annotations.NotNull; - -import java.net.SocketException; -import java.util.function.BiConsumer; -import java.util.function.Consumer; - -@ChannelHandler.Sharable -public class NettyMessageHandler extends SimpleChannelInboundHandler -{ - private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), - () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); - - private final BiConsumer messageConsumer; - private final Consumer channelActiveConsumer; - private final BiConsumer exceptionConsumer; - - public NettyMessageHandler( - BiConsumer messageConsumer, - Consumer channelActiveConsumer, - BiConsumer exceptionConsumer) - { - this.messageConsumer = messageConsumer; - this.channelActiveConsumer = channelActiveConsumer; - this.exceptionConsumer = exceptionConsumer; - } - - @Override - protected void channelRead0(ChannelHandlerContext channelContext, NettyMessage message) - { - LOGGER.debug("Received message: " + message); - this.messageConsumer.accept(channelContext, message); - } - - @Override - public void channelActive(@NotNull ChannelHandlerContext ctx) throws Exception - { - super.channelActive(ctx); - this.channelActiveConsumer.accept(ctx); - } - - @Override - public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) - { - if (cause instanceof SocketException) - { - LOGGER.info("Exception caught in channel: [" + ctx.name() + "]: " + cause.getMessage()); - } - else - { - LOGGER.error("Exception caught in channel: [" + ctx.name() + "].", cause); - this.exceptionConsumer.accept(ctx, cause); - } - - ctx.close(); - } - - @Override - public void channelInactive(@NotNull ChannelHandlerContext channelContext) throws Exception - { - super.channelInactive(channelContext); - this.channelRead0(channelContext, new NettyCloseEvent()); - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyOutboundExceptionRouter.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyOutboundExceptionRouter.java deleted file mode 100644 index 09ec33ac2..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/netty/NettyOutboundExceptionRouter.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.network.protocol.netty; - -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandlerContext; -import io.netty.channel.ChannelOutboundHandlerAdapter; -import io.netty.channel.ChannelPromise; - -public class NettyOutboundExceptionRouter extends ChannelOutboundHandlerAdapter -{ - @Override - public void write(ChannelHandlerContext channelContext, Object messageObj, ChannelPromise promise) throws Exception - { - promise.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); - super.write(channelContext, messageObj, promise); - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/plugin/PluginMessageDecoder.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/plugin/PluginMessageDecoder.java deleted file mode 100644 index d1f311f20..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/plugin/PluginMessageDecoder.java +++ /dev/null @@ -1,33 +0,0 @@ -package com.seibel.distanthorizons.core.network.protocol.plugin; - -import com.seibel.distanthorizons.core.network.messages.AbstractMessageRegistry; -import com.seibel.distanthorizons.core.network.messages.plugin.PluginMessageRegistry; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; -import com.seibel.distanthorizons.core.network.protocol.INetworkObject; -import com.seibel.distanthorizons.core.network.protocol.MessageDecoder; -import com.seibel.distanthorizons.core.network.protocol.MessageEncoder; -import com.seibel.distanthorizons.coreapi.ModInfo; -import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandlerContext; - -import java.util.List; - -public class PluginMessageDecoder extends MessageDecoder -{ - public PluginMessageDecoder() - { - super(PluginMessageRegistry.INSTANCE); - } - - public void decode(ByteBuf inputByteBuf, List outputDecodedObjectList) - { - int version = inputByteBuf.readShort(); - if (version != ModInfo.PLUGIN_PROTOCOL_VERSION) - { - return; - } - - super.decode(null, inputByteBuf, outputDecodedObjectList); - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/plugin/PluginMessageEncoder.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/plugin/PluginMessageEncoder.java deleted file mode 100644 index 7cc405080..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/plugin/PluginMessageEncoder.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.seibel.distanthorizons.core.network.protocol.plugin; - -import com.seibel.distanthorizons.core.network.messages.plugin.PluginMessageRegistry; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; -import com.seibel.distanthorizons.core.network.protocol.MessageEncoder; -import com.seibel.distanthorizons.coreapi.ModInfo; -import io.netty.buffer.ByteBuf; - -public class PluginMessageEncoder extends MessageEncoder -{ - public PluginMessageEncoder() - { - super(PluginMessageRegistry.INSTANCE, PluginChannelMessage.class); - } - - public void encode(PluginChannelMessage pluginChannelMessage, ByteBuf outputByteBuf) throws IllegalArgumentException - { - outputByteBuf.writeShort(ModInfo.PLUGIN_PROTOCOL_VERSION); - super.encode(null, pluginChannelMessage, outputByteBuf); - } - -} From 1f63bdf1244eac847752201ddc395dbc72b2249d Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Wed, 22 May 2024 23:18:18 +0500 Subject: [PATCH 134/205] [skip ci] Somewhere --- .../com/seibel/distanthorizons/api/DhApi.java | 2 +- .../distanthorizons/coreapi/ModInfo.java | 2 +- .../core/api/internal/ClientApi.java | 5 +- .../api/internal/ClientPluginChannelApi.java | 42 +++-------- .../core/level/DhServerLevel.java | 2 +- .../client/ClientNetworkState.java | 57 ++++++++------- .../server/RemotePlayerConnectionHandler.java | 2 +- .../multiplayer/server/ServerPlayerState.java | 32 ++++----- .../core/network/NetworkEventSource.java | 17 +++-- .../messages/PluginMessageRegistry.java | 4 +- .../plugin/ServerConnectInfoMessage.java | 38 ---------- .../network/plugin/PluginChannelSession.java | 39 ++++------- .../plugin/PluginChannelSessionAAAAA.java | 70 ------------------- .../core/world/DhClientWorld.java | 12 +--- 14 files changed, 87 insertions(+), 237 deletions(-) delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/ServerConnectInfoMessage.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSessionAAAAA.java diff --git a/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java b/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java index 3d9153843..23ffd6669 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java @@ -191,7 +191,7 @@ public class DhApi * Returns the network protocol version. * @since API 1.0.0 */ - public static int getNetworkProtocolVersion() { return ModInfo.OLD_PROTOCOL_VERSION; } + public static int getNetworkProtocolVersion() { return ModInfo.PROTOCOL_VERSION; } // methods // diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java index c5120220c..e2c13696f 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java @@ -32,7 +32,7 @@ public final class ModInfo // region Protocol versions // Incremented every time any packets are added, changed or removed, with a few exceptions. - public static final int PROTOCOL_VERSION = 1; + public static final int PROTOCOL_VERSION = 2; public static final String PLUGIN_CHANNEL_PATH = "plugin_channel"; public static final String WRAPPER_PACKET_PATH = "wrapper"; // endregion diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index a10ce8750..538672873 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -139,7 +139,7 @@ public class ClientApi this.waitingClientLevels.clear(); - this.pluginChannelApi.onJoin(world.networkState); + this.pluginChannelApi.onJoin(world.networkState.getSession()); } } @@ -156,7 +156,6 @@ public class ClientApi } // clear the previous server's information - this.pluginChannelApi.close(); this.pluginChannelApi = new ClientPluginChannelApi(this::clientLevelLoadEvent, this::clientLevelUnloadEvent); // remove any waiting items @@ -541,4 +540,4 @@ public class ClientApi */ public void showChatMessageNextFrame(String chatMessage) { this.chatMessageQueueForNextFrame.add(chatMessage); } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index f46c3445a..a09e7adee 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -9,7 +9,6 @@ import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; import com.seibel.distanthorizons.core.network.messages.plugin.PluginCloseEvent; import com.seibel.distanthorizons.core.network.messages.plugin.CurrentLevelKeyMessage; import com.seibel.distanthorizons.core.network.messages.plugin.base.HelloMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.ServerConnectInfoMessage; import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; @@ -19,20 +18,18 @@ import org.jetbrains.annotations.Nullable; import java.util.function.Consumer; -public class ClientPluginChannelApi implements AutoCloseable +/** This class is used to manage the plugin channel session and Multiverse level keys. */ +public class ClientPluginChannelApi { private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); private static final IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class); - private final PluginChannelSession channelHandler = new PluginChannelSession(); - private final Consumer levelUnloadHandler; private final Consumer multiverseLevelLoadHandler; - @Nullable - private ClientNetworkState networkState; + private PluginChannelSession session; public boolean allowLoadingLevel() @@ -47,15 +44,16 @@ public class ClientPluginChannelApi implements AutoCloseable this.levelUnloadHandler = levelUnloadHandler; this.multiverseLevelLoadHandler = levelLoadHandler; - this.channelHandler.registerHandler(CurrentLevelKeyMessage.class, this::onCurrentLevelKeyMessage); - this.channelHandler.registerHandler(ServerConnectInfoMessage.class, this::onServerConnectInfoMessage); - this.channelHandler.registerHandler(PluginCloseEvent.class, this::onClose); } - public void onJoin(@Nullable ClientNetworkState networkState) + public void onJoin(PluginChannelSession session) { - this.networkState = networkState; - this.channelHandler.sendMessageClient(new HelloMessage()); + this.session = session; + + this.session.sendMessage(new HelloMessage()); + + this.session.registerHandler(CurrentLevelKeyMessage.class, this::onCurrentLevelKeyMessage); + this.session.registerHandler(PluginCloseEvent.class, this::onClose); } private void onCurrentLevelKeyMessage(CurrentLevelKeyMessage msg) @@ -80,18 +78,6 @@ public class ClientPluginChannelApi implements AutoCloseable }); } - private void onServerConnectInfoMessage(ServerConnectInfoMessage msg) - { - if (this.networkState != null) - { - this.networkState.getSession().resetAndConnectTo( - msg.ipOverride != null - ? msg.ipOverride - : MC.getCurrentServerIp().split(":")[0], - msg.port); - } - } - public void onClientLevelUnload() { KEYED_CLIENT_LEVEL_MANAGER.clearServerKeyedLevel(); @@ -104,13 +90,7 @@ public class ClientPluginChannelApi implements AutoCloseable public void handlePacket(ByteBuf buffer) { - this.channelHandler.decodeAndHandle(buffer); - } - - @Override - public void close() - { - this.channelHandler.close(); + this.session.decodeAndHandle(buffer); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 742c4b78a..7a9b2ae1c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -250,7 +250,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel if (distanceFromPlayer >= serverPlayerState.serverPlayer.getViewDistance() && distanceFromPlayer <= serverPlayerState.config.getRenderDistanceRadius()) { - serverPlayerState.connection.sendMessage(new FullDataPartialUpdateMessage(this.serverLevelWrapper, data)); + serverPlayerState.session.sendMessage(new FullDataPartialUpdateMessage(this.serverLevelWrapper, data)); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index 691032914..8bc900b5b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -6,10 +6,9 @@ import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; -import com.seibel.distanthorizons.core.network.netty.NettyClient; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; +import com.seibel.distanthorizons.core.network.messages.plugin.PluginCloseEvent; import com.seibel.distanthorizons.core.network.messages.plugin.base.AckMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.base.NettyCloseEvent; import com.seibel.distanthorizons.core.network.messages.plugin.base.HelloMessage; import com.seibel.distanthorizons.core.network.messages.plugin.session.PlayerUUIDMessage; import com.seibel.distanthorizons.core.network.messages.plugin.session.RemotePlayerConfigMessage; @@ -18,7 +17,6 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftCli import org.apache.logging.log4j.LogManager; import java.io.Closeable; -import java.text.MessageFormat; import java.util.UUID; public class ClientNetworkState implements Closeable @@ -27,8 +25,9 @@ public class ClientNetworkState implements Closeable () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); - private final PluginChannelSession session; private final UUID playerUUID = MC_CLIENT.getPlayerUUID(); + private final PluginChannelSession session = new PluginChannelSession(null); + private EServerSupportStatus serverSupportStatus = EServerSupportStatus.NONE; public MultiplayerConfig config = new MultiplayerConfig(); @@ -47,13 +46,12 @@ public class ClientNetworkState implements Closeable /** * Constructs a new instance. */ - public ClientNetworkState(PluginChannelSession session) + public ClientNetworkState() { - this.session = session; - this.session.registerHandler(HelloMessage.class, helloMessage -> { - LOGGER.info("Connected to server: "+helloMessage.getConnection().getRemoteAddress()); + LOGGER.info("Server reported full DH support."); + serverSupportStatus = EServerSupportStatus.FULL; this.getSession().sendRequest(new PlayerUUIDMessage(this.playerUUID), AckMessage.class) .thenAccept(ack -> this.getSession().sendMessage(new RemotePlayerConfigMessage(new MultiplayerConfig()))) @@ -70,7 +68,7 @@ public class ClientNetworkState implements Closeable this.configReceived = true; }); - this.session.registerHandler(NettyCloseEvent.class, msg -> + this.session.registerHandler(PluginCloseEvent.class, msg -> { this.configReceived = false; }); @@ -84,27 +82,22 @@ public class ClientNetworkState implements Closeable private String[] f3Log() { - if (!this.session.isInitialized()) + if (this.session.isClosed()) { - return new String[]{"Did not receive connection info yet..."}; + return new String[]{ + "Session closed: " + this.session.getCloseReason().getMessage() + }; } - if (!this.session.isClosed()) + if (!this.configReceived) { - return new String[]{ - this.session.getRemoteAddress() != null - ? (this.isReady() ? "Connected to server" : "Connecting to server...") - : MessageFormat.format("Disconnected, attempts left: {0} / {1}", this.session.getReconnectionAttemptsLeft(), NettyClient.RECONNECTION_ATTEMPTS) - }; - } - else - { - return new String[]{ - this.session.getCloseReason() != null - ? "Disconnected: " + this.session.getCloseReason().getMessage() - : "Disconnected (check logs for more information)" - }; + return new String[]{"Server does not support DH"}; } + + if (!this.session.isClosed()) return new String[]{ + "Server has full DH support" + }; + return } @Override @@ -114,4 +107,18 @@ public class ClientNetworkState implements Closeable this.configChangeListener.close(); this.session.close(); } + + private enum EServerSupportStatus + { + NONE("Server does not support DH"), + LEVELS_ONLY("Server supports shared level keys"), + FULL("Server has full DH support"); + + public final String message; + + EServerSupportStatus(String message) + { + this.message = message; + } + } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java index 3b3ce66d2..480b006e2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java @@ -31,7 +31,7 @@ public class RemotePlayerConnectionHandler implements Closeable public void handlePluginMessage(IServerPlayerWrapper player, ByteBuf buffer) { - this.connectedPlayers.get(player).connection.decodeAndHandle(buffer); + this.connectedPlayers.get(player).session.decodeAndHandle(buffer); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index 6a514750b..7f97ff472 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -10,10 +10,8 @@ import com.seibel.distanthorizons.core.network.messages.plugin.PluginCloseEvent; import com.seibel.distanthorizons.core.network.messages.plugin.base.HelloMessage; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.fullData.generation.GenTaskPriorityRequestMessage; import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateAndConcurrencyLimiter; -import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateLimiter; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import org.apache.logging.log4j.LogManager; import org.jetbrains.annotations.NotNull; @@ -27,12 +25,11 @@ public class ServerPlayerState private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); - public final IServerPlayerWrapper serverPlayer; - public final PluginChannelSession connection = new PluginChannelSession(); - private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::onConfigChanged); + public final PluginChannelSession session; @NotNull public ConstrainedMultiplayerConfig config = new ConstrainedMultiplayerConfig(); + private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::onConfigChanged); private final ConcurrentHashMap rateLimiterSets = new ConcurrentHashMap<>(); public RateLimiterSet getRateLimiterSet(DhServerLevel level) @@ -44,25 +41,29 @@ public class ServerPlayerState this.rateLimiterSets.clear(); } + + public ServerPlayerState(IServerPlayerWrapper serverPlayer) { - this.serverPlayer = serverPlayer; + this.session = new PluginChannelSession(serverPlayer); - this.connection.registerHandler(RemotePlayerConfigMessage.class, remotePlayerConfigMessage -> + this.session.registerHandler(RemotePlayerConfigMessage.class, remotePlayerConfigMessage -> { this.config.clientConfig = (MultiplayerConfig) remotePlayerConfigMessage.payload; - this.connection.sendMessage(new RemotePlayerConfigMessage(this.config)); + this.session.sendMessage(new RemotePlayerConfigMessage(this.config)); }); - this.connection.registerHandler(HelloMessage.class, msg -> { + this.session.registerHandler(HelloMessage.class, msg -> { this.initializeLodSession(); }); - this.connection.registerHandler(PluginCloseEvent.class, event -> { + this.session.registerHandler(PluginCloseEvent.class, event -> { // Noop }); } + + public void initializeLodSession() { } @@ -70,12 +71,12 @@ public class ServerPlayerState public void close() { this.configChangeListener.close(); - this.connection.close(); + this.session.close(); } private void onConfigChanged() { - this.connection.sendMessage(new RemotePlayerConfigMessage(this.config)); + this.session.sendMessage(new RemotePlayerConfigMessage(this.config)); } @@ -88,13 +89,6 @@ public class ServerPlayerState } ); - public final SupplierBasedRateLimiter genTaskPriorityRequestRateLimiter = new SupplierBasedRateLimiter<>( - () -> ServerNetworking.genTaskPriorityRequestRateLimit.get(), - msg -> { - msg.sendResponse(new RateLimitedException("Generation task priority check rate limit: " + ServerPlayerState.this.config.getFullDataRequestConcurrencyLimit())); - } - ); - public final SupplierBasedRateAndConcurrencyLimiter loginDataSyncRCLimiter = new SupplierBasedRateAndConcurrencyLimiter<>( () -> ServerNetworking.loginDataSyncRCLimit.get(), msg -> { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index 228dc18cb..adf006203 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -21,16 +21,15 @@ package com.seibel.distanthorizons.core.network; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; +import com.seibel.distanthorizons.core.network.messages.PluginMessageRegistry; import com.seibel.distanthorizons.core.network.messages.plugin.base.CancelMessage; import com.seibel.distanthorizons.core.network.messages.plugin.base.ExceptionMessage; import com.seibel.distanthorizons.core.network.messages.plugin.PluginCloseEvent; -import com.seibel.distanthorizons.core.network.messages.plugin.PluginMessageRegistry; import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; import com.seibel.distanthorizons.coreapi.ModInfo; import io.netty.channel.ChannelException; -import io.netty.channel.ChannelHandlerContext; import org.apache.logging.log4j.LogManager; import java.io.InvalidClassException; @@ -129,16 +128,16 @@ public abstract class NetworkEventSource } - protected CompletableFuture createRequest(PluginChannelSession connection, TrackableMessage msg, Class responseClass) + protected CompletableFuture createRequest(PluginChannelSession session, TrackableMessage msg, Class responseClass) { - msg.setConnection(connection); + msg.setConnection(session); CompletableFuture responseFuture = new CompletableFuture<>(); responseFuture.whenComplete((response, throwable) -> { if (!(throwable instanceof ChannelException)) { - ConcurrentMap subMap = this.pendingFutures.get(connection); + ConcurrentMap subMap = this.pendingFutures.get(session); if (subMap != null) { subMap.remove(msg.futureId); @@ -151,19 +150,19 @@ public abstract class NetworkEventSource } }); - ConcurrentMap subMap = this.pendingFutures.get(connection); + ConcurrentMap subMap = this.pendingFutures.get(session); if (subMap == null) { // Was deleted before adding - responseFuture.completeExceptionally(connection.getCloseReason()); + responseFuture.completeExceptionally(session.getCloseReason()); return responseFuture; } subMap.put(msg.futureId, new FutureResponseData(responseClass, responseFuture)); - if (!this.pendingFutures.containsKey(connection)) + if (!this.pendingFutures.containsKey(session)) { // Was deleted while adding // Note: removal from subMap will happen in whenComplete above - responseFuture.completeExceptionally(connection.getCloseReason()); + responseFuture.completeExceptionally(session.getCloseReason()); return responseFuture; } // If passed until here, cancelling is up to the cleaning side diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PluginMessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PluginMessageRegistry.java index 6befbfa5a..341af3237 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PluginMessageRegistry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PluginMessageRegistry.java @@ -100,10 +100,10 @@ public class PluginMessageRegistry @SuppressWarnings("unchecked") public int getMessageId(PluginChannelMessage message) { - return this.gePluginChannelMessageId(message.getClass()); + return this.getMessageId(message.getClass()); } - public int gePluginChannelMessageId(Class messageClass) + public int getMessageId(Class messageClass) { try { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/ServerConnectInfoMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/ServerConnectInfoMessage.java deleted file mode 100644 index a974a447b..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/ServerConnectInfoMessage.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.seibel.distanthorizons.core.network.messages.plugin; - -import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; -import io.netty.buffer.ByteBuf; - -import javax.annotation.Nullable; - -public class ServerConnectInfoMessage extends PluginChannelMessage -{ - @Nullable - public String ipOverride; - public int port; - - public ServerConnectInfoMessage() { } - public ServerConnectInfoMessage(@Nullable String ipOverride, int port) - { - this.ipOverride = ipOverride; - this.port = port; - } - - @Override - public void encode(ByteBuf out) - { - if (this.writeOptional(out, this.ipOverride)) - { - this.writeString(this.ipOverride, out); - } - out.writeShort(this.port); - } - - @Override - public void decode(ByteBuf in) - { - this.ipOverride = this.readOptional(in, () -> this.readString(in)); - this.port = in.readUnsignedShort(); - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java index 7824d2872..0fd42135f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java @@ -4,60 +4,48 @@ import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.network.NetworkEventSource; +import com.seibel.distanthorizons.core.network.messages.PluginMessageRegistry; import com.seibel.distanthorizons.core.network.messages.plugin.PluginCloseEvent; -import com.seibel.distanthorizons.core.network.messages.plugin.PluginMessageRegistry; -import com.seibel.distanthorizons.core.network.protocol.plugin.PluginMessageEncoder; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.coreapi.ModInfo; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufUtil; -import io.netty.channel.ChannelException; -import io.netty.channel.ChannelHandlerContext; import org.apache.logging.log4j.LogManager; import org.jetbrains.annotations.Nullable; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; public class PluginChannelSession extends NetworkEventSource { - /** - * 4 MiB should be enough for any transferred data.
- * Currently largest transferred data is DH full data sections, which usually don't exceed 1-2 MiB in size. - */ - private static final int MAX_MESSAGE_LENGTH = 4194304; - private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); private final IPluginPacketSender packetSender = SingletonInjector.INSTANCE.get(IPluginPacketSender.class); /** - * When set to true, any received data will be ignored.
+ * When non-null, any received data will be ignored.
* This does not include wrong versions, which are ignored without setting this flag, * to allow multi-compat servers. */ - private final AtomicBoolean isClosed = new AtomicBoolean(); + private final AtomicReference closeReason = new AtomicReference<>(); + public Throwable getCloseReason() { return this.closeReason.get(); } + public boolean isClosed() { return this.closeReason.get() != null; } + @Nullable - private IServerPlayerWrapper serverPlayer; + private final IServerPlayerWrapper serverPlayer; - - public PluginChannelSession() - { - super(PluginMessageRegistry.INSTANCE); - } public PluginChannelSession(@Nullable IServerPlayerWrapper serverPlayer) { - super(PluginMessageRegistry.INSTANCE); this.serverPlayer = serverPlayer; } public void decodeAndHandle(ByteBuf byteBuf) { - if (this.isClosed.get()) + if (this.closeReason.get() != null) { return; } @@ -72,7 +60,7 @@ public class PluginChannelSession extends NetworkEventSource PluginChannelMessage msg = PluginMessageRegistry.INSTANCE.createMessage(byteBuf.readUnsignedShort()); msg.decode(byteBuf); - msg.serverPlayer = this.serverPlayer; + msg.session = this; this.handleMessage(msg); } @@ -86,7 +74,7 @@ public class PluginChannelSession extends NetworkEventSource } } - CompletableFuture sendRequest(TrackableMessage msg, Class responseClass) + public CompletableFuture sendRequest(TrackableMessage msg, Class responseClass) { CompletableFuture responseFuture = this.createRequest(this, msg, responseClass); this.sendMessage(msg); @@ -113,10 +101,9 @@ public class PluginChannelSession extends NetworkEventSource } } - @Override - public void close() + public void close(Throwable closeReason) { - if (!this.isClosed.compareAndSet(false, true)) + if (!this.closeReason.compareAndSet(null, closeReason)) { return; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSessionAAAAA.java b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSessionAAAAA.java deleted file mode 100644 index 7a5e70d0c..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSessionAAAAA.java +++ /dev/null @@ -1,70 +0,0 @@ -package com.seibel.distanthorizons.core.network.plugin; - -import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.network.messages.plugin.base.CloseReasonMessage; -import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender; -import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; -import io.netty.channel.ChannelFutureListener; -import io.netty.channel.ChannelHandlerContext; -import org.apache.logging.log4j.LogManager; - -import java.util.concurrent.CompletableFuture; - -public class PluginChannelSessionAAAAA -{ - private static final IPluginPacketSender packetSender = SingletonInjector.INSTANCE.get(IPluginPacketSender.class); - - public final IServerPlayerWrapper serverPlayer; - public boolean isClosed = false; - - public PluginChannelSessionAAAAA(IServerPlayerWrapper serverPlayer) - { - this.serverPlayer = serverPlayer; - } - - ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), - () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); - - public void sendMessage(PluginChannelMessage message) - { - this.LOGGER.debug("Sending message: " + message); - CompletableFuture future = new CompletableFuture<>(); - - if (this.serverPlayer != null) - { - packetSender.sendPluginPacketServer(this.serverPlayer, message); - } - - - return future; - } - - default CompletableFuture sendRequest(TrackableNettyMessage msg, Class responseClass) - { - CompletableFuture responseFuture = this.getRequestHandler().createRequest(this, msg, responseClass); - this.sendMessage(msg).whenComplete((ignored, throwable) -> - { - if (throwable != null) - { - responseFuture.completeExceptionally(throwable); - } - }); - return responseFuture; - } - - default void disconnect(String reason) - { - ChannelHandlerContext ctx = this.getChannelContext(); - if (ctx == null) - { - return; - } - - ctx.channel().config().setAutoRead(false); - ctx.writeAndFlush(new CloseReasonMessage(reason)) - .addListener(ChannelFutureListener.CLOSE); - } - -} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java index fdf2c45d2..18115e6d8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java @@ -39,8 +39,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld { private final ConcurrentHashMap levels; public final ClientOnlySaveStructure saveStructure; - @Nullable - public final ClientNetworkState networkState; + public final ClientNetworkState networkState = new ClientNetworkState(); public ExecutorService dhTickerThread = ThreadUtil.makeSingleThreadPool("Client World Ticker Thread"); public EventLoop eventLoop = new EventLoop(this.dhTickerThread, this::_clientTick); @@ -58,10 +57,6 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld this.saveStructure = new ClientOnlySaveStructure(); this.levels = new ConcurrentHashMap<>(); - this.networkState = Config.Client.Advanced.Multiplayer.ServerNetworking.enableServerNetworking.get() - ? new ClientNetworkState() - : null; - LOGGER.info("Started DhWorld of type " + this.environment); } @@ -137,10 +132,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld @Override public void close() { - if (this.networkState != null) - { - this.networkState.close(); - } + this.networkState.close(); for (DhClientLevel dhClientLevel : this.levels.values()) From a3e34c973862cfeafbdc7d979ae8fcd568b52980 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 27 May 2024 22:01:57 +0500 Subject: [PATCH 135/205] [skip ci] Still a mess --- .../distanthorizons/core/config/Config.java | 13 ---- .../WorldRemoteGenerationQueue.java | 72 +---------------- .../core/level/DhClientLevel.java | 6 +- .../core/level/DhServerLevel.java | 77 ++++++++++++------- .../client/ClientNetworkState.java | 12 +-- .../server/RemotePlayerConnectionHandler.java | 64 +++------------ .../multiplayer/server/ServerPlayerState.java | 1 + .../messages/PluginMessageRegistry.java | 3 - .../messages/plugin/PluginCloseEvent.java | 3 +- .../messages/plugin/base/AckMessage.java | 1 - .../messages/plugin/base/CancelMessage.java | 1 - .../plugin/base/CloseReasonMessage.java | 6 -- .../plugin/base/ExceptionMessage.java | 6 -- .../FullDataPartialUpdateMessage.java | 9 --- .../FullDataSourceRequestMessage.java | 11 --- .../FullDataSourceResponseMessage.java | 1 - .../plugin/session/PlayerUUIDMessage.java | 6 -- .../network/plugin/PluginChannelMessage.java | 3 +- .../network/plugin/PluginChannelSession.java | 8 +- .../core/network/plugin/TrackableMessage.java | 2 +- .../core/world/DhServerWorld.java | 14 +++- 21 files changed, 87 insertions(+), 232 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 63a7489a0..223c6fed8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -953,19 +953,6 @@ public class Config + "Limits the amount of sent/processed LOD *update* requests concurrently, per player." + "") .build(); - - public static ConfigUIComment immersivePortalsSectionNote = new ConfigUIComment(); - public static ConfigEntry generateMultipleDimensions = new ConfigEntry.Builder() - .setServersideShortName("generateMultipleDimensions") - .set(false) - .comment("" - + "Controls whether clients will request for the server to generate LODs in multiple dimensions at once.\n" - + "" - + "Note that all dimensions share the same thread pool.\n" - + "This means that dimensions without any connected clients inside could slow down LOD updates for\n" - + "all connected clients in other dimensions." - + "") - .build(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 171c2463b..8d2f058c5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -9,8 +9,6 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.multiplayer.client.AbstractFullDataRequestQueue; import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; -import com.seibel.distanthorizons.core.network.messages.plugin.fullData.generation.GenTaskPriorityRequestMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.fullData.generation.GenTaskPriorityResponseMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; @@ -29,9 +27,6 @@ public class WorldRemoteGenerationQueue extends AbstractFullDataRequestQueue imp private int estimatedTotalTaskCount; - private CompletableFuture genTaskPriorityRequest = CompletableFuture.completedFuture(null); - private final Semaphore genTaskPriorityRequestSemaphore = new Semaphore(1, true); - @Override protected int getRequestConcurrencyLimit() { return this.networkState.config.fullDataRequestConcurrencyLimit; } @@ -75,54 +70,7 @@ public class WorldRemoteGenerationQueue extends AbstractFullDataRequestQueue imp @Override public void startAndSetTargetPos(DhBlockPos2D targetPos) { - if (!super.tick(targetPos)) - { - return; - } - - if (this.genTaskPriorityRequestSemaphore.tryAcquire()) { - List posList = this.waitingTasks.entrySet().stream() - .filter(task -> task.getValue().request == null && task.getValue().priority == 0) - .sorted((x, y) -> this.posDistanceSquared(targetPos, x.getKey()) - this.posDistanceSquared(targetPos, y.getKey())) - .limit(this.networkState.config.genTaskPriorityRequestRateLimit) - .map(Map.Entry::getKey) - .collect(Collectors.toList()); - if (posList.isEmpty()) { - this.genTaskPriorityRequestSemaphore.release(); - return; - }; - - CompletableFuture request = this.networkState.getSession().sendRequest(new GenTaskPriorityRequestMessage(posList, this.level), GenTaskPriorityResponseMessage.class); - this.genTaskPriorityRequest = request; - request.handleAsync((response, throwable) -> { - try - { - if (throwable != null) - { - throw throwable; - } - - for (Map.Entry mapEntry : response.posList.entrySet()) - { - RequestQueueEntry entry = this.waitingTasks.get(mapEntry.getKey()); - if (entry != null) - { - entry.priority = mapEntry.getValue(); - } - } - } - catch (ChannelException | CancellationException | RateLimitedException ignored) - { - } - catch (Throwable e) - { - LOGGER.error("Error while fetching gen task priorities", e); - } - - this.genTaskPriorityRequestSemaphore.release(); - return null; - }); - } + super.tick(targetPos); } @@ -134,22 +82,6 @@ public class WorldRemoteGenerationQueue extends AbstractFullDataRequestQueue imp @Override public CompletableFuture startClosing(boolean cancelCurrentGeneration, boolean alsoInterruptRunning) { - return CompletableFuture.allOf(super.startClosing(alsoInterruptRunning), CompletableFuture.runAsync(() -> { - Stopwatch stopwatch = Stopwatch.createStarted(); - - do - { - if (this.genTaskPriorityRequest.cancel(false)) - { - this.genTaskPriorityRequestSemaphore.release(); - } - } - while (!this.genTaskPriorityRequestSemaphore.tryAcquire() && stopwatch.elapsed(TimeUnit.SECONDS) < SHUTDOWN_TIMEOUT_SECONDS); - - if (stopwatch.elapsed(TimeUnit.SECONDS) >= SHUTDOWN_TIMEOUT_SECONDS) - { - LOGGER.warn("Priority request queue for " + this.level.getLevelWrapper() + " did not shutdown in " + SHUTDOWN_TIMEOUT_SECONDS + " seconds! It might be left hanging."); - } - })); + return super.startClosing(alsoInterruptRunning); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index edfef8a36..4411f229a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -31,10 +31,8 @@ import com.seibel.distanthorizons.core.generation.WorldRemoteGenerationQueue; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; import com.seibel.distanthorizons.core.multiplayer.client.FullDataRefreshQueue; -import com.seibel.distanthorizons.core.network.netty.NettyClient; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataPartialUpdateMessage; -import com.seibel.distanthorizons.core.network.netty.NettyMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhSectionPos; @@ -75,7 +73,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel @CheckForNull private final ClientNetworkState networkState; @Nullable - private final ScopedNetworkEventSource eventSource; + private final ScopedNetworkEventSource eventSource; public final WorldGenModule worldGenModule; public final AppliedConfigState worldGeneratorEnabledConfig; @@ -102,7 +100,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel this.networkState = networkState; if (networkState != null) { - this.eventSource = new ScopedNetworkEventSource<>(networkState.getSession()); + this.eventSource = new ScopedNetworkEventSource(networkState.getSession()); this.dataRefreshQueue = new FullDataRefreshQueue(this, networkState); this.registerNetworkHandlers(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 7a9b2ae1c..09372f3be 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -25,30 +25,34 @@ import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState; import com.seibel.distanthorizons.core.multiplayer.server.RemotePlayerConnectionHandler; -import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; -import com.seibel.distanthorizons.core.network.netty.NettyServer; +import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; +import com.seibel.distanthorizons.core.network.messages.plugin.ILevelRelatedMessage; import com.seibel.distanthorizons.core.network.messages.plugin.base.CancelMessage; import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceRequestMessage; import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceResponseMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.fullData.generation.GenTaskPriorityRequestMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.fullData.generation.GenTaskPriorityResponseMessage; import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataPartialUpdateMessage; -import com.seibel.distanthorizons.core.network.netty.NettyMessage; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; +import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import com.seibel.distanthorizons.coreapi.util.math.Vec3d; import org.apache.logging.log4j.Logger; +import java.text.MessageFormat; import java.util.concurrent.CompletableFuture; import javax.annotation.CheckForNull; import java.util.Map; import java.util.concurrent.*; +import java.util.function.BiConsumer; +import java.util.function.Consumer; public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel { @@ -58,7 +62,6 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel private final IServerLevelWrapper serverLevelWrapper; private final RemotePlayerConnectionHandler remotePlayerConnectionHandler; - private final ScopedNetworkEventSource eventSource; private final ConcurrentLinkedQueue worldGenLoopingQueue = new ConcurrentLinkedQueue<>(); private final ConcurrentMap incompleteDataSources = new ConcurrentHashMap<>(); @@ -75,13 +78,11 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel LOGGER.info("Started DHLevel for {} with saves at {}", serverLevelWrapper, saveStructure); this.remotePlayerConnectionHandler = remotePlayerConnectionHandler; - this.eventSource = new ScopedNetworkEventSource<>(remotePlayerConnectionHandler.server()); - this.registerNetworkHandlers(); } - private void registerNetworkHandlers() + public void registerNetworkHandlers(ServerPlayerState serverPlayerState) { - this.eventSource.registerHandler(FullDataSourceRequestMessage.class, this.remotePlayerConnectionHandler.currentLevelOnly(this, (msg, serverPlayerState) -> + serverPlayerState.session.registerHandler(FullDataSourceRequestMessage.class, this.currentLevelOnly(msg -> { ServerPlayerState.RateLimiterSet rateLimiterSet = serverPlayerState.getRateLimiterSet(this); @@ -149,16 +150,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel } })); - this.eventSource.registerHandler(GenTaskPriorityRequestMessage.class, this.remotePlayerConnectionHandler.currentLevelOnly(this, (msg, serverPlayerState) -> - { - msg.sendResponse(new GenTaskPriorityResponseMessage( - this.serverside.fullDataFileHandler.getLoadStates(msg.posList.stream() - .limit(serverPlayerState.getRateLimiterSet(this).genTaskPriorityRequestRateLimiter.acquireOrDrain(msg.posList.size())) - ::iterator) - )); - })); - - this.eventSource.registerHandler(CancelMessage.class, msg -> + serverPlayerState.session.registerHandler(CancelMessage.class, msg -> { IncompleteDataSourceEntry entry = this.fullDataRequests.remove(msg.futureId); if (entry == null) @@ -167,11 +159,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel } FullDataSourceRequestMessage requestMessage = entry.requestMessages.remove(msg.futureId); - ServerPlayerState serverPlayerState = this.remotePlayerConnectionHandler.getConnectedPlayer(msg); - if (serverPlayerState != null) - { - serverPlayerState.getRateLimiterSet(this).fullDataRequestConcurrencyLimiter.release(); - } + serverPlayerState.getRateLimiterSet(this).fullDataRequestConcurrencyLimiter.release(); entry.requestCollectionSemaphore.acquireUninterruptibly(Short.MAX_VALUE); if (entry.requestMessages.isEmpty()) @@ -184,6 +172,39 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel }); } + public Consumer currentLevelOnly(Consumer next) + { + return msg -> + { + LodUtil.assertTrue(msg instanceof ILevelRelatedMessage, "Received message does not implement " + ILevelRelatedMessage.class.getSimpleName() + ": " + msg.getClass().getSimpleName()); + + // Handle only in requested dimension + if (!((ILevelRelatedMessage) msg).isSameLevelAs(this.getLevelWrapper())) + { + return; + } + + // If player is not in this dimension and handling multiple dimensions at once is not allowed + assert msg.session.serverPlayer != null; + if (msg.session.serverPlayer.getLevel() != this.getLevelWrapper()) + { + // If the message can be replied to - reply with error, otherwise just ignore + if (msg instanceof TrackableMessage) + { + ((TrackableMessage) msg).sendResponse(new InvalidLevelException(MessageFormat.format( + "Generation not allowed. Requested dimension: {0}, player dimension: {1}", + this.getLevelWrapper().getDimensionType().getDimensionName(), + msg.session.serverPlayer.getLevel().getDimensionType().getDimensionName() + ))); + } + + return; + } + + next.accept(msg); + }; + } + public void addPlayer(IServerPlayerWrapper serverPlayer) { this.worldGenLoopingQueue.add(serverPlayer); @@ -218,7 +239,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel { this.fullDataRequests.remove(msg.futureId); - ServerPlayerState serverPlayerState = this.remotePlayerConnectionHandler.getConnectedPlayer(msg); + ServerPlayerState serverPlayerState = this.remotePlayerConnectionHandler.getConnectedPlayer(msg.serverPlayer()); if (serverPlayerState == null) { continue; @@ -245,9 +266,9 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel continue; } - Vec3d playerPosition = serverPlayerState.serverPlayer.getPosition(); + Vec3d playerPosition = serverPlayerState.serverPlayer().getPosition(); int distanceFromPlayer = data.getPos().getManhattanBlockDistance(new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)) / 16; - if (distanceFromPlayer >= serverPlayerState.serverPlayer.getViewDistance() && + if (distanceFromPlayer >= serverPlayerState.serverPlayer().getViewDistance() && distanceFromPlayer <= serverPlayerState.config.getRenderDistanceRadius()) { serverPlayerState.session.sendMessage(new FullDataPartialUpdateMessage(this.serverLevelWrapper, data)); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index 8bc900b5b..37c06b364 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -51,7 +51,7 @@ public class ClientNetworkState implements Closeable this.session.registerHandler(HelloMessage.class, helloMessage -> { LOGGER.info("Server reported full DH support."); - serverSupportStatus = EServerSupportStatus.FULL; + this.serverSupportStatus = EServerSupportStatus.FULL; this.getSession().sendRequest(new PlayerUUIDMessage(this.playerUUID), AckMessage.class) .thenAccept(ack -> this.getSession().sendMessage(new RemotePlayerConfigMessage(new MultiplayerConfig()))) @@ -89,15 +89,9 @@ public class ClientNetworkState implements Closeable }; } - if (!this.configReceived) - { - return new String[]{"Server does not support DH"}; - } - - if (!this.session.isClosed()) return new String[]{ - "Server has full DH support" + return new String[]{ + this.serverSupportStatus.message }; - return } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java index 480b006e2..cc3958425 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java @@ -1,30 +1,18 @@ package com.seibel.distanthorizons.core.multiplayer.server; import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.config.types.ConfigEntry; -import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; -import com.seibel.distanthorizons.core.network.messages.plugin.ILevelRelatedMessage; -import com.seibel.distanthorizons.core.network.netty.NettyMessage; -import com.seibel.distanthorizons.core.network.plugin.TrackableNettyMessage; -import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import io.netty.buffer.ByteBuf; import org.apache.logging.log4j.LogManager; -import java.io.Closeable; -import java.text.MessageFormat; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -public class RemotePlayerConnectionHandler implements Closeable +public class RemotePlayerConnectionHandler { private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); - private static final ConfigEntry GENERATE_MULTIPLE_DIMENSIONS_CONFIG = Config.Client.Advanced.Multiplayer.ServerNetworking.generateMultipleDimensions; private final ConcurrentMap connectedPlayers = new ConcurrentHashMap<>(); @@ -34,43 +22,21 @@ public class RemotePlayerConnectionHandler implements Closeable this.connectedPlayers.get(player).session.decodeAndHandle(buffer); } - - public Consumer currentLevelOnly(DhServerLevel level, BiConsumer next) + public ServerPlayerState getConnectedPlayer(IServerPlayerWrapper player) { - return (msg) -> - { - LodUtil.assertTrue(msg instanceof ILevelRelatedMessage, "Received message does not implement " + ILevelRelatedMessage.class.getSimpleName() + ": " + msg.getClass().getSimpleName()); - - // Handle only in requested dimension - if (!((ILevelRelatedMessage) msg).isSameLevelAs(level.getLevelWrapper())) - { - return; - } - - // If player is not in this dimension and handling multiple dimensions at once is not allowed - if (serverPlayerState.serverPlayer.getLevel() != level.getLevelWrapper() - && !GENERATE_MULTIPLE_DIMENSIONS_CONFIG.get()) - { - // If the message can be replied to - reply with error, otherwise just ignore - if (msg instanceof TrackableNettyMessage) - { - ((TrackableNettyMessage) msg).sendResponse(new InvalidLevelException(MessageFormat.format( - "Generation not allowed. Requested dimension: {0}, player dimension: {1}", - level.getLevelWrapper().getDimensionType().getDimensionName(), - serverPlayerState.serverPlayer.getLevel().getDimensionType().getDimensionName() - ))); - } - - return; - } - - next.accept(msg, serverPlayerState); - }; + return this.connectedPlayers.get(player); + } + public Iterable getConnectedPlayers() + { + return this.connectedPlayers.values(); } - public void registerJoinedPlayer(IServerPlayerWrapper serverPlayer) + + public ServerPlayerState registerJoinedPlayer(IServerPlayerWrapper serverPlayer) { - this.connectedPlayers.put(serverPlayer, new ServerPlayerState(serverPlayer)); + ServerPlayerState state = new ServerPlayerState(serverPlayer); + this.connectedPlayers.put(serverPlayer, state); + return state; } public void unregisterLeftPlayer(IServerPlayerWrapper serverPlayer) @@ -82,10 +48,4 @@ public class RemotePlayerConnectionHandler implements Closeable } } - @Override - public void close() - { - this.configChangeListener.close(); - } - } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index 7f97ff472..283517343 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -26,6 +26,7 @@ public class ServerPlayerState () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); public final PluginChannelSession session; + public IServerPlayerWrapper serverPlayer() { return this.session.serverPlayer; } @NotNull public ConstrainedMultiplayerConfig config = new ConstrainedMultiplayerConfig(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PluginMessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PluginMessageRegistry.java index 341af3237..24c7009ac 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PluginMessageRegistry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PluginMessageRegistry.java @@ -23,7 +23,6 @@ import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.seibel.distanthorizons.core.network.messages.plugin.CurrentLevelKeyMessage; import com.seibel.distanthorizons.core.network.messages.plugin.base.HelloMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.PluginMessageRegistry; import com.seibel.distanthorizons.core.network.messages.plugin.base.AckMessage; import com.seibel.distanthorizons.core.network.messages.plugin.base.CancelMessage; import com.seibel.distanthorizons.core.network.messages.plugin.base.CloseReasonMessage; @@ -31,8 +30,6 @@ import com.seibel.distanthorizons.core.network.messages.plugin.base.ExceptionMes import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataPartialUpdateMessage; import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceRequestMessage; import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceResponseMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.fullData.generation.GenTaskPriorityRequestMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.fullData.generation.GenTaskPriorityResponseMessage; import com.seibel.distanthorizons.core.network.messages.plugin.session.PlayerUUIDMessage; import com.seibel.distanthorizons.core.network.messages.plugin.session.RemotePlayerConfigMessage; import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginCloseEvent.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginCloseEvent.java index 0174680eb..8d2fbb693 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginCloseEvent.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginCloseEvent.java @@ -1,6 +1,5 @@ package com.seibel.distanthorizons.core.network.messages.plugin; -import com.seibel.distanthorizons.core.network.messages.ICloseEvent; import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import io.netty.buffer.ByteBuf; @@ -8,7 +7,7 @@ import io.netty.buffer.ByteBuf; /** * This is not a "real" message, and only used to indicate a disconnection. */ -public class PluginCloseEvent extends PluginChannelMessage implements ICloseEvent, INetworkObject +public class PluginCloseEvent extends PluginChannelMessage { @Override public void encode(ByteBuf out) { throw new UnsupportedOperationException(this.getClass().getSimpleName() + " is not a real message, and cannot be sent."); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/AckMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/AckMessage.java index 3b5d23823..09488217d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/AckMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/AckMessage.java @@ -20,7 +20,6 @@ package com.seibel.distanthorizons.core.network.messages.plugin.base; import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; -import com.seibel.distanthorizons.core.network.plugin.TrackableNettyMessage; import io.netty.buffer.ByteBuf; /** diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/CancelMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/CancelMessage.java index 2fb9b6ac0..15c173901 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/CancelMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/CancelMessage.java @@ -20,7 +20,6 @@ package com.seibel.distanthorizons.core.network.messages.plugin.base; import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; -import com.seibel.distanthorizons.core.network.plugin.TrackableNettyMessage; import io.netty.buffer.ByteBuf; public class CancelMessage extends TrackableMessage diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/CloseReasonMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/CloseReasonMessage.java index 3b38ccd0d..42ef502c4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/CloseReasonMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/CloseReasonMessage.java @@ -19,7 +19,6 @@ package com.seibel.distanthorizons.core.network.messages.plugin.base; -import com.seibel.distanthorizons.core.network.netty.NettyMessage; import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; import io.netty.buffer.ByteBuf; @@ -39,9 +38,4 @@ public class CloseReasonMessage extends PluginChannelMessage @Override public void decode(ByteBuf in) { this.reason = this.readString(in); } - @Override public String toString() - { - return super.toString("reason='" + this.reason + '\''); - } - } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ExceptionMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ExceptionMessage.java index f22678c78..d327196d0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ExceptionMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ExceptionMessage.java @@ -24,7 +24,6 @@ import com.seibel.distanthorizons.core.network.exceptions.InvalidSectionPosExcep import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; -import com.seibel.distanthorizons.core.network.plugin.TrackableNettyMessage; import io.netty.buffer.ByteBuf; import java.util.ArrayList; @@ -62,9 +61,4 @@ public class ExceptionMessage extends TrackableMessage this.exception = exceptionMap.get(id).getDeclaredConstructor(String.class).newInstance(message); } - @Override public String toString() - { - return super.toString("exception=" + this.exception); - } - } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataPartialUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataPartialUpdateMessage.java index c3bd8d20d..055205e17 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataPartialUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataPartialUpdateMessage.java @@ -23,7 +23,6 @@ import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.network.messages.plugin.ILevelRelatedMessage; -import com.seibel.distanthorizons.core.network.netty.NettyMessage; import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; @@ -75,12 +74,4 @@ public class FullDataPartialUpdateMessage extends PluginChannelMessage implement this.dataSourceDto = INetworkObject.readToObject(new FullDataSourceV2DTO(), in); } - @Override - public String toString() - { - return super.toString( - "levelHashCode=" + this.levelName - ); - } - } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceRequestMessage.java index 4a3417759..47bfa6c22 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceRequestMessage.java @@ -21,7 +21,6 @@ package com.seibel.distanthorizons.core.network.messages.plugin.fullData; import com.seibel.distanthorizons.core.network.messages.plugin.ILevelRelatedMessage; import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; -import com.seibel.distanthorizons.core.network.plugin.TrackableNettyMessage; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; @@ -69,14 +68,4 @@ public class FullDataSourceRequestMessage extends TrackableMessage implements IL this.clientTimestamp = this.readOptional(in, in::readLong); } - @Override - public String toString() - { - return super.toString( - "dhSectionPos=" + this.sectionPos + - ", levelHashCode=" + this.levelName + - ", checksum=" + this.clientTimestamp - ); - } - } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceResponseMessage.java index 2d423875c..efa7b003b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceResponseMessage.java @@ -23,7 +23,6 @@ import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; -import com.seibel.distanthorizons.core.network.plugin.TrackableNettyMessage; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; import io.netty.buffer.ByteBuf; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/PlayerUUIDMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/PlayerUUIDMessage.java index 571950061..699d66cf6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/PlayerUUIDMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/PlayerUUIDMessage.java @@ -20,7 +20,6 @@ package com.seibel.distanthorizons.core.network.messages.plugin.session; import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; -import com.seibel.distanthorizons.core.network.plugin.TrackableNettyMessage; import io.netty.buffer.ByteBuf; import java.util.UUID; @@ -42,9 +41,4 @@ public class PlayerUUIDMessage extends TrackableMessage @Override public void decode0(ByteBuf in) { this.playerUUID = new UUID(in.readLong(), in.readLong()); } - @Override public String toString() - { - return super.toString("playerUUID=" + this.playerUUID); - } - } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelMessage.java index 0d85d53e8..ad2004bfe 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelMessage.java @@ -6,6 +6,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapp public abstract class PluginChannelMessage implements INetworkObject { public PluginChannelSession session = null; + public IServerPlayerWrapper serverPlayer() { return this.session.serverPlayer; } public boolean warnWhenUnhandled() { return true; } @@ -18,7 +19,7 @@ public abstract class PluginChannelMessage implements INetworkObject { if (this.session != null) { - throw new IllegalStateException("Session cannot be changed after initialization."); + throw new IllegalStateException("Session object cannot be changed after initialization."); } this.session = connection; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java index 0fd42135f..15123e643 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java @@ -23,7 +23,7 @@ public class PluginChannelSession extends NetworkEventSource private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); - private final IPluginPacketSender packetSender = SingletonInjector.INSTANCE.get(IPluginPacketSender.class); + private static final IPluginPacketSender PACKET_SENDER = SingletonInjector.INSTANCE.get(IPluginPacketSender.class); /** * When non-null, any received data will be ignored.
@@ -35,7 +35,7 @@ public class PluginChannelSession extends NetworkEventSource public boolean isClosed() { return this.closeReason.get() != null; } @Nullable - private final IServerPlayerWrapper serverPlayer; + public final IServerPlayerWrapper serverPlayer; public PluginChannelSession(@Nullable IServerPlayerWrapper serverPlayer) { @@ -93,11 +93,11 @@ public class PluginChannelSession extends NetworkEventSource if (this.serverPlayer != null) { - this.packetSender.sendPluginPacketServer(this.serverPlayer, encoder); + PACKET_SENDER.sendPluginPacketServer(this.serverPlayer, encoder); } else { - this.packetSender.sendPluginPacketClient(encoder); + PACKET_SENDER.sendPluginPacketClient(encoder); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/TrackableMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/TrackableMessage.java index 878a2d6d5..bbaa3de68 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/TrackableMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/TrackableMessage.java @@ -39,7 +39,7 @@ public abstract class TrackableMessage extends PluginChannelMessage | ((Objects.requireNonNull(SharedApi.getEnvironment()) == EWorldEnvironment.Server_Only ? 1 : 0) << 31); private static final AtomicInteger lastContextId = new AtomicInteger(); - private static final ConcurrentMap connectionToIdMap = new MapMaker().weakKeys().makeMap(); + private static final ConcurrentMap connectionToIdMap = new MapMaker().weakKeys().makeMap(); public void sendResponse(TrackableMessage responseMessage) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java index 8a60c0aaf..724fbcc3d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java @@ -23,6 +23,7 @@ import com.seibel.distanthorizons.core.file.structure.LocalSaveStructure; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.multiplayer.server.RemotePlayerConnectionHandler; +import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; @@ -65,13 +66,20 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld public void addPlayer(IServerPlayerWrapper serverPlayer) { - this.remotePlayerConnectionHandler.registerJoinedPlayer(serverPlayer); + ServerPlayerState playerState = this.remotePlayerConnectionHandler.registerJoinedPlayer(serverPlayer); this.getLevel(serverPlayer.getLevel()).addPlayer(serverPlayer); + + for (DhServerLevel level : this.levels.values()) + { + level.registerNetworkHandlers(playerState); + } } public void removePlayer(IServerPlayerWrapper serverPlayer) { this.getLevel(serverPlayer.getLevel()).removePlayer(serverPlayer); this.remotePlayerConnectionHandler.unregisterLeftPlayer(serverPlayer); + + // If player's left, session is already closed } public void changePlayerLevel(IServerPlayerWrapper player, IServerLevelWrapper origin, IServerLevelWrapper dest) { @@ -142,8 +150,6 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld @Override public void close() { - this.remotePlayerConnectionHandler.close(); - for (DhServerLevel level : this.levels.values()) { LOGGER.info("Unloading level " + level.getLevelWrapper().getDimensionType().getDimensionName()); @@ -162,4 +168,4 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld LOGGER.info("Closed DhWorld of type " + this.environment); } -} +} \ No newline at end of file From a000afbc609595aa2321bdc1a0f863241033a597 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Wed, 29 May 2024 23:36:34 +0500 Subject: [PATCH 136/205] Kinda works, rejoining is broken --- .../api/internal/ClientPluginChannelApi.java | 6 +- .../core/level/DhClientLevel.java | 2 +- .../client/ClientNetworkState.java | 23 +----- .../config/AbstractMultiplayerConfig.java | 4 +- .../multiplayer/config/MultiplayerConfig.java | 10 +-- .../MultiplayerConfigChangeListener.java | 3 +- .../server/ConstrainedMultiplayerConfig.java | 8 +- .../multiplayer/server/ServerPlayerState.java | 14 ++-- .../core/network/NetworkEventSource.java | 80 +++++-------------- .../messages/PluginMessageRegistry.java | 9 +-- .../messages/plugin/base/AckMessage.java | 39 --------- .../plugin/base/ClientHelloMessage.java | 22 +++++ .../plugin/base/ExceptionMessage.java | 1 + .../messages/plugin/base/HelloMessage.java | 24 ------ .../plugin/session/PlayerUUIDMessage.java | 44 ---------- 15 files changed, 60 insertions(+), 229 deletions(-) delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/AckMessage.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ClientHelloMessage.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/HelloMessage.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/PlayerUUIDMessage.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index a09e7adee..9f2aa9c4e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -5,16 +5,14 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager; import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; import com.seibel.distanthorizons.core.network.messages.plugin.PluginCloseEvent; import com.seibel.distanthorizons.core.network.messages.plugin.CurrentLevelKeyMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.base.HelloMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.base.ClientHelloMessage; import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import io.netty.buffer.ByteBuf; import org.apache.logging.log4j.LogManager; -import org.jetbrains.annotations.Nullable; import java.util.function.Consumer; @@ -50,7 +48,7 @@ public class ClientPluginChannelApi { this.session = session; - this.session.sendMessage(new HelloMessage()); + this.session.sendMessage(new ClientHelloMessage()); this.session.registerHandler(CurrentLevelKeyMessage.class, this::onCurrentLevelKeyMessage); this.session.registerHandler(PluginCloseEvent.class, this::onClose); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 4411f229a..dc9570f12 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -176,7 +176,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel if (networkState != null) { isClientUsable = !networkState.getSession().isClosed(); - isAllowedDimension = MC_CLIENT.getWrappedClientLevel() == this.levelWrapper || networkState.config.generateMultipleDimensions; + isAllowedDimension = MC_CLIENT.getWrappedClientLevel() == this.levelWrapper; } boolean shouldDoWorldGen = isClientUsable diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index 37c06b364..370ae1136 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -1,31 +1,23 @@ package com.seibel.distanthorizons.core.multiplayer.client; import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.messages.plugin.PluginCloseEvent; -import com.seibel.distanthorizons.core.network.messages.plugin.base.AckMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.base.HelloMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.session.PlayerUUIDMessage; import com.seibel.distanthorizons.core.network.messages.plugin.session.RemotePlayerConfigMessage; import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import org.apache.logging.log4j.LogManager; import java.io.Closeable; -import java.util.UUID; public class ClientNetworkState implements Closeable { protected static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); - private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); - private final UUID playerUUID = MC_CLIENT.getPlayerUUID(); private final PluginChannelSession session = new PluginChannelSession(null); private EServerSupportStatus serverSupportStatus = EServerSupportStatus.NONE; @@ -48,21 +40,10 @@ public class ClientNetworkState implements Closeable */ public ClientNetworkState() { - this.session.registerHandler(HelloMessage.class, helloMessage -> - { - LOGGER.info("Server reported full DH support."); - this.serverSupportStatus = EServerSupportStatus.FULL; - - this.getSession().sendRequest(new PlayerUUIDMessage(this.playerUUID), AckMessage.class) - .thenAccept(ack -> this.getSession().sendMessage(new RemotePlayerConfigMessage(new MultiplayerConfig()))) - .exceptionally(throwable -> { - LOGGER.error("Error while fetching server's config", throwable); - return null; - }); - }); - this.session.registerHandler(RemotePlayerConfigMessage.class, msg -> { + this.serverSupportStatus = EServerSupportStatus.FULL; + LOGGER.info("Connection config has been changed: " + msg.payload); this.config = (MultiplayerConfig) msg.payload; this.configReceived = true; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java index f070a3dd2..341780b8d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java @@ -12,7 +12,6 @@ public abstract class AbstractMultiplayerConfig implements INetworkObject public abstract boolean isRealTimeUpdatesEnabled(); public abstract boolean isLoginDataSyncEnabled(); public abstract int getLoginDataSyncRCLimit(); - public abstract boolean getGenerateMultipleDimensions(); @Override public void encode(ByteBuf out) @@ -24,7 +23,6 @@ public abstract class AbstractMultiplayerConfig implements INetworkObject out.writeBoolean(this.isRealTimeUpdatesEnabled()); out.writeBoolean(this.isLoginDataSyncEnabled()); out.writeInt(this.getLoginDataSyncRCLimit()); - out.writeBoolean(this.getGenerateMultipleDimensions()); } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java index 9f2ef5974..88c10e848 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java @@ -28,9 +28,6 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig public int loginDataSyncRCLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.loginDataSyncRCLimit.get(); @Override public int getLoginDataSyncRCLimit() { return this.loginDataSyncRCLimit; } - public boolean generateMultipleDimensions = Config.Client.Advanced.Multiplayer.ServerNetworking.generateMultipleDimensions.get(); - @Override public boolean getGenerateMultipleDimensions() { return this.generateMultipleDimensions; } - @Override public void decode(ByteBuf in) @@ -42,10 +39,10 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig this.realTimeUpdatesEnabled = in.readBoolean(); this.loginDataSyncEnabled = in.readBoolean(); this.loginDataSyncRCLimit = in.readInt(); - this.generateMultipleDimensions = in.readBoolean(); } - @Override public String toString() + @Override + public String toString() { return "MultiplayerConfig{" + "renderDistanceRadius=" + this.renderDistanceRadius + @@ -55,8 +52,7 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig ", realTimeUpdatesEnabled=" + this.realTimeUpdatesEnabled + ", loginDataSyncEnabled=" + this.loginDataSyncEnabled + ", loginDataSyncRCLimit=" + this.loginDataSyncRCLimit + - ", generateMultipleDimensions=" + this.generateMultipleDimensions + '}'; } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java index 08651b052..07e4d0835 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java @@ -18,7 +18,6 @@ public class MultiplayerConfigChangeListener implements Closeable Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates, Config.Client.Advanced.Multiplayer.ServerNetworking.enableLoginDataSync, Config.Client.Advanced.Multiplayer.ServerNetworking.loginDataSyncRCLimit, - Config.Client.Advanced.Multiplayer.ServerNetworking.generateMultipleDimensions, }; private final ArrayList changeListeners; @@ -42,4 +41,4 @@ public class MultiplayerConfigChangeListener implements Closeable this.changeListeners.clear(); } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ConstrainedMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ConstrainedMultiplayerConfig.java index 9e136c94d..825082bbe 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ConstrainedMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ConstrainedMultiplayerConfig.java @@ -54,16 +54,10 @@ public class ConstrainedMultiplayerConfig extends AbstractMultiplayerConfig return Math.min(this.clientConfig.loginDataSyncRCLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.loginDataSyncRCLimit.get()); } - @Override - public boolean getGenerateMultipleDimensions() - { - return this.clientConfig.generateMultipleDimensions && Config.Client.Advanced.Multiplayer.ServerNetworking.generateMultipleDimensions.get(); - } - @Override public void decode(ByteBuf in) { throw new UnsupportedOperationException("Decoding is not supported for server-only class."); } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index 283517343..8253a530c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -7,7 +7,7 @@ import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; import com.seibel.distanthorizons.core.network.messages.plugin.session.RemotePlayerConfigMessage; import com.seibel.distanthorizons.core.network.messages.plugin.PluginCloseEvent; -import com.seibel.distanthorizons.core.network.messages.plugin.base.HelloMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.base.ClientHelloMessage; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceRequestMessage; import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; @@ -54,9 +54,7 @@ public class ServerPlayerState this.session.sendMessage(new RemotePlayerConfigMessage(this.config)); }); - this.session.registerHandler(HelloMessage.class, msg -> { - this.initializeLodSession(); - }); + this.session.registerHandler(ClientHelloMessage.class, msg -> this.onConfigChanged()); this.session.registerHandler(PluginCloseEvent.class, event -> { // Noop @@ -65,8 +63,10 @@ public class ServerPlayerState - public void initializeLodSession() + + private void onConfigChanged() { + this.session.sendMessage(new RemotePlayerConfigMessage(this.config)); } public void close() @@ -75,10 +75,6 @@ public class ServerPlayerState this.session.close(); } - private void onConfigChanged() - { - this.session.sendMessage(new RemotePlayerConfigMessage(this.config)); - } public class RateLimiterSet diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index adf006203..8e98f52f8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -46,7 +46,7 @@ public abstract class NetworkEventSource private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); protected final ConcurrentMap, Set>> handlers = new ConcurrentHashMap<>(); - private final ConcurrentMap> pendingFutures = new ConcurrentHashMap<>(); + private final ConcurrentMap pendingFutures = new ConcurrentHashMap<>(); protected boolean hasHandler(Class handlerClass) { @@ -71,26 +71,22 @@ public abstract class NetworkEventSource if (message instanceof TrackableMessage) { TrackableMessage trackableMessage = (TrackableMessage) message; - ConcurrentMap subMap = this.pendingFutures.get(message.getConnection()); - if (subMap != null) + FutureResponseData responseData = this.pendingFutures.get(trackableMessage.futureId); + if (responseData != null) { - FutureResponseData responseData = subMap.get(trackableMessage.futureId); - if (responseData != null) + handled = true; + + if (message instanceof ExceptionMessage) { - handled = true; - - if (message instanceof ExceptionMessage) - { - responseData.future.completeExceptionally(((ExceptionMessage) message).exception); - } - else if (message.getClass() != responseData.responseClass) - { - responseData.future.completeExceptionally(new InvalidClassException("Response with invalid type: expected " + responseData.responseClass.getSimpleName() + ", got:" + message)); - } - else - { - responseData.future.complete(trackableMessage); - } + responseData.future.completeExceptionally(((ExceptionMessage) message).exception); + } + else if (message.getClass() != responseData.responseClass) + { + responseData.future.completeExceptionally(new InvalidClassException("Response with invalid type: expected " + responseData.responseClass.getSimpleName() + ", got:" + message)); + } + else + { + responseData.future.complete(trackableMessage); } } } @@ -101,11 +97,6 @@ public abstract class NetworkEventSource } } - protected void addNewConnection(PluginChannelSession connection) - { - this.pendingFutures.put(connection, new ConcurrentHashMap<>()); - } - public void registerHandler(Class handlerClass, Consumer handlerImplementation) { //noinspection unchecked @@ -137,11 +128,7 @@ public abstract class NetworkEventSource { if (!(throwable instanceof ChannelException)) { - ConcurrentMap subMap = this.pendingFutures.get(session); - if (subMap != null) - { - subMap.remove(msg.futureId); - } + this.pendingFutures.remove(msg.futureId); } if (throwable instanceof CancellationException) @@ -150,45 +137,16 @@ public abstract class NetworkEventSource } }); - ConcurrentMap subMap = this.pendingFutures.get(session); - if (subMap == null) - { - // Was deleted before adding - responseFuture.completeExceptionally(session.getCloseReason()); - return responseFuture; - } - subMap.put(msg.futureId, new FutureResponseData(responseClass, responseFuture)); - if (!this.pendingFutures.containsKey(session)) - { - // Was deleted while adding - // Note: removal from subMap will happen in whenComplete above - responseFuture.completeExceptionally(session.getCloseReason()); - return responseFuture; - } - // If passed until here, cancelling is up to the cleaning side + this.pendingFutures.put(msg.futureId, new FutureResponseData(responseClass, responseFuture)); return responseFuture; } - protected final void completeAllFuturesExceptionally(PluginChannelSession connection, Throwable cause) - { - ConcurrentMap map = this.pendingFutures.remove(connection); - if (map == null) - { - return; - } - - for (FutureResponseData responseData : map.values()) - { - responseData.future.completeExceptionally(cause); - } - } - protected final void completeAllFuturesExceptionally(Throwable cause) { - for (PluginChannelSession connection : this.pendingFutures.keySet()) + for (FutureResponseData responseData : this.pendingFutures.values()) { - this.completeAllFuturesExceptionally(connection, cause); + responseData.future.completeExceptionally(cause); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PluginMessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PluginMessageRegistry.java index 24c7009ac..a61d9277e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PluginMessageRegistry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PluginMessageRegistry.java @@ -22,15 +22,13 @@ package com.seibel.distanthorizons.core.network.messages; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.seibel.distanthorizons.core.network.messages.plugin.CurrentLevelKeyMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.base.HelloMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.base.AckMessage; +import com.seibel.distanthorizons.core.network.messages.plugin.base.ClientHelloMessage; import com.seibel.distanthorizons.core.network.messages.plugin.base.CancelMessage; import com.seibel.distanthorizons.core.network.messages.plugin.base.CloseReasonMessage; import com.seibel.distanthorizons.core.network.messages.plugin.base.ExceptionMessage; import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataPartialUpdateMessage; import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceRequestMessage; import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceResponseMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.session.PlayerUUIDMessage; import com.seibel.distanthorizons.core.network.messages.plugin.session.RemotePlayerConfigMessage; import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; @@ -52,19 +50,17 @@ public class PluginMessageRegistry // Note: Messages must have parameterless constructors // Always sent by the client - this.registerMessage(HelloMessage.class, HelloMessage::new); + this.registerMessage(ClientHelloMessage.class, ClientHelloMessage::new); this.registerMessage(CloseReasonMessage.class, CloseReasonMessage::new); // Multiverse support this.registerMessage(CurrentLevelKeyMessage.class, CurrentLevelKeyMessage::new); // Core - this.registerMessage(AckMessage.class, AckMessage::new); this.registerMessage(CancelMessage.class, CancelMessage::new); this.registerMessage(ExceptionMessage.class, ExceptionMessage::new); // ID & config - this.registerMessage(PlayerUUIDMessage.class, PlayerUUIDMessage::new); this.registerMessage(RemotePlayerConfigMessage.class, RemotePlayerConfigMessage::new); // Full data requests & updates @@ -94,7 +90,6 @@ public class PluginMessageRegistry } } - @SuppressWarnings("unchecked") public int getMessageId(PluginChannelMessage message) { return this.getMessageId(message.getClass()); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/AckMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/AckMessage.java deleted file mode 100644 index 09488217d..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/AckMessage.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.network.messages.plugin.base; - -import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; -import io.netty.buffer.ByteBuf; - -/** - * Simple empty response message. - * This message is not sent automatically. - */ -public class AckMessage extends TrackableMessage -{ - public AckMessage() { } - - @Override - public void encode0(ByteBuf out) { } - - @Override - public void decode0(ByteBuf in) { } - -} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ClientHelloMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ClientHelloMessage.java new file mode 100644 index 000000000..c8399e538 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ClientHelloMessage.java @@ -0,0 +1,22 @@ +package com.seibel.distanthorizons.core.network.messages.plugin.base; + +import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; +import io.netty.buffer.ByteBuf; + +/** + * Serves as a trigger for the server to send some useful data to the client. + * Not integral to establishing a session. + */ +public class ClientHelloMessage extends PluginChannelMessage +{ + @Override + public void encode(ByteBuf out) + { + } + + @Override + public void decode(ByteBuf in) + { + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ExceptionMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ExceptionMessage.java index d327196d0..1c305aa0c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ExceptionMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ExceptionMessage.java @@ -29,6 +29,7 @@ import io.netty.buffer.ByteBuf; import java.util.ArrayList; import java.util.List; +// TODO appears to be useless yelling at user public class ExceptionMessage extends TrackableMessage { private static final List> exceptionMap = new ArrayList>() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/HelloMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/HelloMessage.java deleted file mode 100644 index 63f5c826f..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/HelloMessage.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.seibel.distanthorizons.core.network.messages.plugin.base; - -import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; -import com.seibel.distanthorizons.coreapi.ModInfo; -import io.netty.buffer.ByteBuf; - -/** Serves as a trigger for the server to send the first world change. */ -public class HelloMessage extends PluginChannelMessage -{ - public short version = ModInfo.PROTOCOL_VERSION; - - @Override - public void encode(ByteBuf out) - { - out.writeShort(this.version); - } - - @Override - public void decode(ByteBuf in) - { - this.version = in.readShort(); - } - -} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/PlayerUUIDMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/PlayerUUIDMessage.java deleted file mode 100644 index 699d66cf6..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/PlayerUUIDMessage.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.network.messages.plugin.session; - -import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; -import io.netty.buffer.ByteBuf; - -import java.util.UUID; - -public class PlayerUUIDMessage extends TrackableMessage -{ - public UUID playerUUID; - - public PlayerUUIDMessage() { } - public PlayerUUIDMessage(UUID playerUUID) { this.playerUUID = playerUUID; } - - @Override - public void encode0(ByteBuf out) - { - out.writeLong(this.playerUUID.getMostSignificantBits()); - out.writeLong(this.playerUUID.getLeastSignificantBits()); - } - - @Override - public void decode0(ByteBuf in) { this.playerUUID = new UUID(in.readLong(), in.readLong()); } - -} \ No newline at end of file From e08faa79439b4ab21fdf24ed8a563ed3cd01f68d Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 1 Jun 2024 12:17:04 +0500 Subject: [PATCH 137/205] Fix requests breaking on rejoining --- .../core/network/NetworkEventSource.java | 4 +--- .../core/network/plugin/PluginChannelSession.java | 3 ++- .../core/network/plugin/TrackableMessage.java | 10 ---------- 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index 8e98f52f8..93ddc05ad 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -119,10 +119,8 @@ public abstract class NetworkEventSource } - protected CompletableFuture createRequest(PluginChannelSession session, TrackableMessage msg, Class responseClass) + protected CompletableFuture createRequest(TrackableMessage msg, Class responseClass) { - msg.setConnection(session); - CompletableFuture responseFuture = new CompletableFuture<>(); responseFuture.whenComplete((response, throwable) -> { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java index 15123e643..9f0685988 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java @@ -76,7 +76,8 @@ public class PluginChannelSession extends NetworkEventSource public CompletableFuture sendRequest(TrackableMessage msg, Class responseClass) { - CompletableFuture responseFuture = this.createRequest(this, msg, responseClass); + msg.setConnection(this); + CompletableFuture responseFuture = this.createRequest(msg, responseClass); this.sendMessage(msg); return responseFuture; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/TrackableMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/TrackableMessage.java index bbaa3de68..dd899974d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/TrackableMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/TrackableMessage.java @@ -38,22 +38,12 @@ public abstract class TrackableMessage extends PluginChannelMessage public long futureId = lastId.getAndIncrement() | ((Objects.requireNonNull(SharedApi.getEnvironment()) == EWorldEnvironment.Server_Only ? 1 : 0) << 31); - private static final AtomicInteger lastContextId = new AtomicInteger(); - private static final ConcurrentMap connectionToIdMap = new MapMaker().weakKeys().makeMap(); - public void sendResponse(TrackableMessage responseMessage) { responseMessage.futureId = this.futureId; this.session.sendMessage(responseMessage); } - @Override - public void setConnection(PluginChannelSession connection) - { - super.setConnection(connection); - this.futureId |= (long) connectionToIdMap.computeIfAbsent(connection, k -> lastContextId.getAndIncrement()) << 32; - } - public void sendResponse(Exception e) { this.sendResponse(new ExceptionMessage(e)); From 24a2a48debddcdc49613083ae14bd472967b90a4 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 1 Jun 2024 19:49:23 +0500 Subject: [PATCH 138/205] Increase section reload delay --- .../core/api/internal/ClientApi.java | 5 +---- .../core/api/internal/ClientPluginChannelApi.java | 5 ++++- .../client/AbstractFullDataRequestQueue.java | 14 ++++---------- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index 538672873..fe9ad3d87 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -86,7 +86,7 @@ public class ClientApi private long lastFlushNanoTime = 0; - private ClientPluginChannelApi pluginChannelApi = new ClientPluginChannelApi(this::clientLevelLoadEvent, this::clientLevelUnloadEvent); + private final ClientPluginChannelApi pluginChannelApi = new ClientPluginChannelApi(this::clientLevelLoadEvent, this::clientLevelUnloadEvent); /** Holds any levels that were loaded before the {@link ClientApi#onClientOnlyConnected} was fired. */ @@ -155,9 +155,6 @@ public class ClientApi SharedApi.setDhWorld(null); } - // clear the previous server's information - this.pluginChannelApi = new ClientPluginChannelApi(this::clientLevelLoadEvent, this::clientLevelUnloadEvent); - // remove any waiting items this.waitingChunkByClientLevelAndPos.clear(); this.waitingClientLevels.clear(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index 9f2aa9c4e..d2e375afd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -16,7 +16,10 @@ import org.apache.logging.log4j.LogManager; import java.util.function.Consumer; -/** This class is used to manage the plugin channel session and Multiverse level keys. */ +/** + * This class is used to manage the level keys. + * Its purpose is to separate MC's and plugin channel's event handling. + */ public class ClientPluginChannelApi { private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index 66cc77559..abad2448e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -60,6 +60,8 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, private final SupplierBasedRateLimiter rateLimiter = new SupplierBasedRateLimiter<>(this::getRequestConcurrencyLimit); + private final ScheduledExecutorService taskFinishScheduler = Executors.newScheduledThreadPool(1); + @SuppressWarnings("BooleanMethodIsAlwaysInverted") protected boolean showInDebug() { return true; } @@ -235,17 +237,9 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, // Hack to work around a race condition // If you finish the request too quickly, the section will never render - new Thread(() -> { - try - { - Thread.sleep(5000); + this.taskFinishScheduler.schedule(() -> { entry.future.complete(true); - } - catch (InterruptedException e) - { - throw new RuntimeException(e); - } - }).start(); + }, 10, TimeUnit.SECONDS); return null; }); } From a6fc8f1702d8796beb0e051091f215f3774ad090 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 2 Jun 2024 20:00:09 +0500 Subject: [PATCH 139/205] Fix compilation --- .../core/api/internal/ClientApi.java | 6 ++-- .../api/internal/ClientPluginChannelApi.java | 7 +---- .../core/api/internal/ServerApi.java | 10 ++++--- .../FullDataSourceProviderV2.java | 19 +++++------- .../RemoteFullDataSourceProvider.java | 14 +++++---- .../WorldRemoteGenerationQueue.java | 9 +----- .../core/level/DhClientLevel.java | 2 +- .../core/level/DhServerLevel.java | 9 +++--- .../client/AbstractFullDataRequestQueue.java | 30 +++++++++---------- .../server/RemotePlayerConnectionHandler.java | 9 ++++-- .../FullDataPartialUpdateMessage.java | 2 +- .../FullDataSourceRequestMessage.java | 8 ++--- .../FullDataSourceResponseMessage.java | 2 +- .../session/RemotePlayerConfigMessage.java | 2 +- .../network/plugin/PluginChannelMessage.java | 2 +- .../network/plugin/PluginChannelSession.java | 25 ++++------------ .../core/network/protocol/INetworkObject.java | 4 +-- .../core/world/DhServerWorld.java | 11 +++---- .../misc/IPluginPacketSender.java | 5 ++-- 19 files changed, 80 insertions(+), 96 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index 94e1c904b..da97c8af6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -24,6 +24,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.config.Config; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering; import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode; @@ -52,6 +53,7 @@ import com.seibel.distanthorizons.coreapi.util.math.Mat4f; import io.netty.buffer.ByteBuf; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; import org.lwjgl.glfw.GLFW; import java.util.HashMap; @@ -325,9 +327,9 @@ public class ClientApi // networking // //============// - public void pluginMessageReceived(ByteBuf byteBuf) + public void pluginMessageReceived(@NotNull PluginChannelMessage message) { - this.pluginChannelApi.handlePacket(byteBuf); + this.pluginChannelApi.session.tryHandleMessage(message); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index d2e375afd..84c2adfaa 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -30,7 +30,7 @@ public class ClientPluginChannelApi private final Consumer levelUnloadHandler; private final Consumer multiverseLevelLoadHandler; - private PluginChannelSession session; + public PluginChannelSession session; public boolean allowLoadingLevel() @@ -89,9 +89,4 @@ public class ClientPluginChannelApi KEYED_CLIENT_LEVEL_MANAGER.disable(); } - public void handlePacket(ByteBuf buffer) - { - this.session.decodeAndHandle(buffer); - } - } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java index f1163db28..4ae6a3d57 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java @@ -23,6 +23,7 @@ import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLo import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelUnloadEvent; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.generation.DhLightingEngine; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; @@ -38,6 +39,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import io.netty.buffer.ByteBuf; import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; /** * This holds the methods that should be called by the host mod loader (Fabric, @@ -178,14 +180,14 @@ public class ServerApi } } - public void pluginMessageReceived(IServerPlayerWrapper player, ByteBuf buffer) + public void pluginMessageReceived(IServerPlayerWrapper player, @NotNull PluginChannelMessage message) { IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); if (serverWorld instanceof DhServerWorld) // TODO add support for DhClientServerWorld's (lan worlds) as well { - LOGGER.debug("Player " + player.getUUID() + " sent plugin message: " + buffer.toString()); - ((DhServerWorld) serverWorld).handlePluginMessage(player, buffer); + LOGGER.debug("Player " + player.getUUID() + " sent plugin message: " + message); + ((DhServerWorld) serverWorld).remotePlayerConnectionHandler.handlePluginMessage(player, message); } } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java index d5b4a5cf1..7b289d51f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java @@ -201,13 +201,8 @@ public class FullDataSourceProviderV2 throw new RuntimeException(e); } } - public Map getTimestampsForRange(long startPos, long endPos) + public Map getTimestampsForRange(byte detailLevel, int startPosX, int startPosZ, int endPosX, int endPosZ) { - if (DhSectionPos.getDetailLevel(startPos) != DhSectionPos.getDetailLevel(endPos)) - { - throw new IllegalArgumentException("Start and end must have the same detail level"); - } - try { PreparedStatement preparedStatement = this.repo.createPreparedStatement( @@ -217,14 +212,14 @@ public class FullDataSourceProviderV2 "AND PosX BETWEEN ? AND ? " + "AND PosZ BETWEEN ? AND ?;" ); - preparedStatement.setInt(1, DhSectionPos.getDetailLevel(startPos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); - preparedStatement.setInt(2, DhSectionPos.getX(startPos)); - preparedStatement.setInt(3, DhSectionPos.getX(endPos)); - preparedStatement.setInt(4, DhSectionPos.getZ(startPos)); - preparedStatement.setInt(5, DhSectionPos.getZ(endPos)); + preparedStatement.setInt(1, detailLevel - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); + preparedStatement.setInt(2, startPosX); + preparedStatement.setInt(3, endPosX); + preparedStatement.setInt(4, startPosZ); + preparedStatement.setInt(5, endPosZ); return this.repo.query(preparedStatement).stream().collect(Collectors.toMap( - row -> DhSectionPos.encode(DhSectionPos.getDetailLevel(startPos), (int) row.get("PosX"), (int) row.get("PosZ")), + row -> DhSectionPos.encode(detailLevel, (int) row.get("PosX"), (int) row.get("PosZ")), row -> (long) row.get("LastModifiedUnixDateTime")) ); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataSourceProvider.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataSourceProvider.java index 936c3ce53..c9db0136a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataSourceProvider.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataSourceProvider.java @@ -43,7 +43,7 @@ public class RemoteFullDataSourceProvider extends GeneratedFullDataSourceProvide @Override @Nullable - public FullDataSourceV2 get(DhSectionPos pos) + public FullDataSourceV2 get(long pos) { FullDataSourceV2 fullDataSource = super.get(pos); if (fullDataSource == null || this.dataRefreshQueue == null) @@ -51,11 +51,15 @@ public class RemoteFullDataSourceProvider extends GeneratedFullDataSourceProvide return fullDataSource; } - Map timestamps = this.getTimestampsForRange( - pos.getMinCornerPos(DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL), - pos.getMaxCornerPos(DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL) + int posToMinimumDetailScale = (DhSectionPos.getDetailLevel(pos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL + 1); + Map timestamps = this.getTimestampsForRange( + DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL, + DhSectionPos.getX(pos) * posToMinimumDetailScale, + DhSectionPos.getZ(pos) * posToMinimumDetailScale, + (DhSectionPos.getX(pos) + 1) * posToMinimumDetailScale - 1, + (DhSectionPos.getZ(pos) + 1) * posToMinimumDetailScale - 1 ); - for (Map.Entry entry : timestamps.entrySet()) + for (Map.Entry entry : timestamps.entrySet()) { this.dataRefreshQueue.submitRequest(entry.getKey(), entry.getValue(), this.delayedFullDataSourceSaveCache::queueDataSourceForUpdateAndSave); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 8d2f058c5..0f2b9aa35 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -1,6 +1,5 @@ package com.seibel.distanthorizons.core.generation; -import com.google.common.base.Stopwatch; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker; import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult; @@ -8,18 +7,12 @@ import com.seibel.distanthorizons.core.level.IDhClientLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.multiplayer.client.AbstractFullDataRequestQueue; import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; -import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; -import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; import com.seibel.distanthorizons.core.util.LodUtil; -import io.netty.channel.ChannelException; import org.apache.logging.log4j.Logger; -import java.util.*; -import java.util.List; import java.util.concurrent.*; -import java.util.stream.Collectors; public class WorldRemoteGenerationQueue extends AbstractFullDataRequestQueue implements IFullDataSourceRetrievalQueue, IDebugRenderable { @@ -59,7 +52,7 @@ public class WorldRemoteGenerationQueue extends AbstractFullDataRequestQueue imp } @Override - public CompletableFuture submitGenTask(DhSectionPos sectionPos, byte requiredDataDetail, IWorldGenTaskTracker tracker) + public CompletableFuture submitGenTask(long sectionPos, byte requiredDataDetail, IWorldGenTaskTracker tracker) { return super.submitRequest(sectionPos, tracker.getChunkDataConsumer()) .thenApply(result -> result diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index dc9570f12..5581dac18 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -276,7 +276,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel @Override - public void onWorldGenTaskComplete(DhSectionPos pos) + public void onWorldGenTaskComplete(long pos) { DebugRenderer.makeParticle( new DebugRenderer.BoxParticle( diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index db2c4e014..c7cf2c277 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -37,6 +37,7 @@ import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; @@ -63,7 +64,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel private final RemotePlayerConnectionHandler remotePlayerConnectionHandler; private final ConcurrentLinkedQueue worldGenLoopingQueue = new ConcurrentLinkedQueue<>(); - private final ConcurrentMap incompleteDataSources = new ConcurrentHashMap<>(); + private final ConcurrentMap incompleteDataSources = new ConcurrentHashMap<>(); private final ConcurrentMap fullDataRequests = new ConcurrentHashMap<>(); public DhServerLevel(AbstractSaveStructure saveStructure, IServerLevelWrapper serverLevelWrapper, RemotePlayerConnectionHandler remotePlayerConnectionHandler) @@ -220,7 +221,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel this.chunkToLodBuilder.tick(); // Send finished data source requests - for (Map.Entry mapEntry : this.incompleteDataSources.entrySet()) + for (Map.Entry mapEntry : this.incompleteDataSources.entrySet()) { IncompleteDataSourceEntry entry = mapEntry.getValue(); @@ -266,7 +267,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel } Vec3d playerPosition = serverPlayerState.serverPlayer().getPosition(); - int distanceFromPlayer = data.getPos().getManhattanBlockDistance(new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)) / 16; + int distanceFromPlayer = DhSectionPos.getManhattanBlockDistance(data.getPos(), new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)) / 16; if (distanceFromPlayer >= serverPlayerState.serverPlayer().getViewDistance() && distanceFromPlayer <= serverPlayerState.config.getRenderDistanceRadius()) { @@ -352,7 +353,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel @Override public boolean hasSkyLight() { return this.serverLevelWrapper.hasSkyLight(); } - private void trySetGeneratedDataSourceToEntry(IncompleteDataSourceEntry entry, DhSectionPos pos) + private void trySetGeneratedDataSourceToEntry(IncompleteDataSourceEntry entry, long pos) { this.serverside.fullDataFileHandler.getAsync(pos).thenAccept(fullDataSource -> { if (this.serverside.fullDataFileHandler.isFullyGenerated(fullDataSource.columnGenerationSteps)) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index abad2448e..d1edc76c4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -48,7 +48,7 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, private volatile CompletableFuture closingFuture = null; - protected final ConcurrentMap waitingTasks = new ConcurrentHashMap<>(); + protected final ConcurrentMap waitingTasks = new ConcurrentHashMap<>(); private final Semaphore pendingTasksSemaphore = new Semaphore(Short.MAX_VALUE, true); private final F3Screen.NestedMessage f3Message = new F3Screen.NestedMessage(this::f3Log); @@ -56,7 +56,7 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, private final AtomicInteger failedRequests = new AtomicInteger(); private final ConfigEntry showDebugWireframeConfig; - private final Set alreadyRequestedPositions = ConcurrentHashMap.newKeySet(); + private final Set alreadyRequestedPositions = ConcurrentHashMap.newKeySet(); private final SupplierBasedRateLimiter rateLimiter = new SupplierBasedRateLimiter<>(this::getRequestConcurrencyLimit); @@ -82,13 +82,13 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, DebugRenderer.register(this, this.showDebugWireframeConfig); } - public CompletableFuture submitRequest(DhSectionPos sectionPos, Consumer chunkDataConsumer) + public CompletableFuture submitRequest(long sectionPos, Consumer chunkDataConsumer) { return this.submitRequest(sectionPos, null, chunkDataConsumer); } - public CompletableFuture submitRequest(DhSectionPos sectionPos, @Nullable Long clientTimestamp, Consumer chunkDataConsumer) + public CompletableFuture submitRequest(long sectionPos, @Nullable Long clientTimestamp, Consumer chunkDataConsumer) { - LodUtil.assertTrue(sectionPos.getDetailLevel() == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL, "Only highest-detail sections are allowed."); + LodUtil.assertTrue(DhSectionPos.getDetailLevel(sectionPos) == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL, "Only highest-detail sections are allowed."); // check if this is a duplicate task if (this.alreadyRequestedPositions.contains(sectionPos)) @@ -104,9 +104,9 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, return entry.future; } - protected int posDistanceSquared(DhBlockPos2D targetPos, DhSectionPos pos) + protected int posDistanceSquared(DhBlockPos2D targetPos, long pos) { - return (int) pos.getCenterBlockPos().distSquared(targetPos); + return (int) DhSectionPos.getCenterBlockPos(pos).distSquared(targetPos); } public synchronized boolean tick(DhBlockPos2D targetPos) @@ -132,14 +132,14 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, return true; } - public void removeRetrievalRequestIf(Function removeIf) + public void removeRetrievalRequestIf(DhSectionPos.ICancelablePrimitiveLongConsumer removeIf) { - for (Map.Entry mapEntry : this.waitingTasks.entrySet()) + for (Map.Entry mapEntry : this.waitingTasks.entrySet()) { - DhSectionPos pos = mapEntry.getKey(); + long pos = mapEntry.getKey(); RequestQueueEntry entry = mapEntry.getValue(); - if (removeIf.apply(pos)) + if (removeIf.accept(pos)) { entry.future.cancel(false); if (entry.request != null) @@ -153,7 +153,7 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, private void sendNewRequest(DhBlockPos2D targetPos) { - Map.Entry mapEntry = this.waitingTasks.entrySet().stream() + Map.Entry mapEntry = this.waitingTasks.entrySet().stream() .filter(task -> task.getValue().request == null) .reduce(null, (a, b) -> { if (a == null) @@ -163,7 +163,7 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, if (b.getValue().priority < a.getValue().priority) { - Map.Entry temp = b; + Map.Entry temp = b; b = a; a = temp; } @@ -180,7 +180,7 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, return; } - DhSectionPos sectionPos = mapEntry.getKey(); + long sectionPos = mapEntry.getKey(); RequestQueueEntry entry = mapEntry.getValue(); CompletableFuture request = this.networkState.getSession().sendRequest(new FullDataSourceRequestMessage(this.level.getLevelWrapper(), sectionPos, entry.updateTimestamp), FullDataSourceResponseMessage.class); @@ -308,7 +308,7 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, return; } - for (Map.Entry mapEntry : this.waitingTasks.entrySet()) + for (Map.Entry mapEntry : this.waitingTasks.entrySet()) { r.renderBox(new DebugRenderer.Box(mapEntry.getKey(), -32f, 64f, 0.05f, mapEntry.getValue().request != null ? Color.red diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java index cc3958425..2b7c027a4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java @@ -2,8 +2,9 @@ package com.seibel.distanthorizons.core.multiplayer.server; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; -import io.netty.buffer.ByteBuf; import org.apache.logging.log4j.LogManager; import java.util.concurrent.ConcurrentHashMap; @@ -17,9 +18,11 @@ public class RemotePlayerConnectionHandler private final ConcurrentMap connectedPlayers = new ConcurrentHashMap<>(); - public void handlePluginMessage(IServerPlayerWrapper player, ByteBuf buffer) + public void handlePluginMessage(IServerPlayerWrapper player, PluginChannelMessage message) { - this.connectedPlayers.get(player).session.decodeAndHandle(buffer); + PluginChannelSession session = this.connectedPlayers.get(player).session; + message.setSession(session); + session.tryHandleMessage(message); } public ServerPlayerState getConnectedPlayer(IServerPlayerWrapper player) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataPartialUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataPartialUpdateMessage.java index 055205e17..7273380c7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataPartialUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataPartialUpdateMessage.java @@ -71,7 +71,7 @@ public class FullDataPartialUpdateMessage extends PluginChannelMessage implement public void decode(ByteBuf in) { this.levelName = this.readString(in); - this.dataSourceDto = INetworkObject.readToObject(new FullDataSourceV2DTO(), in); + this.dataSourceDto = INetworkObject.decodeToInstance(new FullDataSourceV2DTO(), in); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceRequestMessage.java index 47bfa6c22..329151493 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceRequestMessage.java @@ -32,7 +32,7 @@ public class FullDataSourceRequestMessage extends TrackableMessage implements IL { private String levelName; - public DhSectionPos sectionPos; + public long sectionPos; /** Only present when requesting for changes. */ @Nullable @@ -42,7 +42,7 @@ public class FullDataSourceRequestMessage extends TrackableMessage implements IL public String getLevelName() { return this.levelName; } public FullDataSourceRequestMessage() {} - public FullDataSourceRequestMessage(ILevelWrapper levelWrapper, DhSectionPos sectionPos, @Nullable Long clientTimestamp) + public FullDataSourceRequestMessage(ILevelWrapper levelWrapper, long sectionPos, @Nullable Long clientTimestamp) { this.levelName = levelWrapper.getDimensionType().getDimensionName(); this.sectionPos = sectionPos; @@ -53,7 +53,7 @@ public class FullDataSourceRequestMessage extends TrackableMessage implements IL public void encode0(ByteBuf out) { this.writeString(this.levelName, out); - this.sectionPos.encode(out); + out.writeLong(this.sectionPos); if (this.writeOptional(out, this.clientTimestamp)) { out.writeLong(this.clientTimestamp); @@ -64,7 +64,7 @@ public class FullDataSourceRequestMessage extends TrackableMessage implements IL public void decode0(ByteBuf in) { this.levelName = this.readString(in); - this.sectionPos = INetworkObject.readToObject(DhSectionPos.zero(), in); + this.sectionPos = in.readLong(); this.clientTimestamp = this.readOptional(in, in::readLong); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceResponseMessage.java index efa7b003b..bcb2ed00a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceResponseMessage.java @@ -68,7 +68,7 @@ public class FullDataSourceResponseMessage extends TrackableMessage @Override public void decode0(ByteBuf in) { - this.dataSourceDto = this.readOptional(in, () -> INetworkObject.readToObject(new FullDataSourceV2DTO(), in)); + this.dataSourceDto = this.readOptional(in, () -> INetworkObject.decodeToInstance(new FullDataSourceV2DTO(), in)); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/RemotePlayerConfigMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/RemotePlayerConfigMessage.java index 7c1395b41..930a8d41a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/RemotePlayerConfigMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/RemotePlayerConfigMessage.java @@ -36,6 +36,6 @@ public class RemotePlayerConfigMessage extends PluginChannelMessage public void encode(ByteBuf out) { this.payload.encode(out); } @Override - public void decode(ByteBuf in) { this.payload = INetworkObject.readToObject(new MultiplayerConfig(), in); } + public void decode(ByteBuf in) { this.payload = INetworkObject.decodeToInstance(new MultiplayerConfig(), in); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelMessage.java index ad2004bfe..372d66d4a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelMessage.java @@ -15,7 +15,7 @@ public abstract class PluginChannelMessage implements INetworkObject return this.session; } - public void setConnection(PluginChannelSession connection) + public void setSession(PluginChannelSession connection) { if (this.session != null) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java index 9f0685988..f8fe6d94a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java @@ -10,7 +10,6 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSende import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.coreapi.ModInfo; import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufUtil; import org.apache.logging.log4j.LogManager; import org.jetbrains.annotations.Nullable; @@ -43,7 +42,7 @@ public class PluginChannelSession extends NetworkEventSource } - public void decodeAndHandle(ByteBuf byteBuf) + public void tryHandleMessage(PluginChannelMessage message) { if (this.closeReason.get() != null) { @@ -52,31 +51,19 @@ public class PluginChannelSession extends NetworkEventSource try { - int version = byteBuf.readShort(); - if (version != ModInfo.PROTOCOL_VERSION) - { - return; - } - - PluginChannelMessage msg = PluginMessageRegistry.INSTANCE.createMessage(byteBuf.readUnsignedShort()); - msg.decode(byteBuf); - msg.session = this; - - this.handleMessage(msg); + this.handleMessage(message); } catch (Throwable e) { LOGGER.error("Failed to handle the message. New messages will be ignored.", e); - LOGGER.error("Buffer: " + byteBuf.toString()); - byteBuf.resetReaderIndex(); - LOGGER.error("Buffer contents: " + ByteBufUtil.hexDump(byteBuf)); + LOGGER.error("Message: " + message); this.close(); } } public CompletableFuture sendRequest(TrackableMessage msg, Class responseClass) { - msg.setConnection(this); + msg.setSession(this); CompletableFuture responseFuture = this.createRequest(msg, responseClass); this.sendMessage(msg); return responseFuture; @@ -94,11 +81,11 @@ public class PluginChannelSession extends NetworkEventSource if (this.serverPlayer != null) { - PACKET_SENDER.sendPluginPacketServer(this.serverPlayer, encoder); + PACKET_SENDER.sendPluginPacketServer(this.serverPlayer, message); } else { - PACKET_SENDER.sendPluginPacketClient(encoder); + PACKET_SENDER.sendPluginPacketClient(message); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java index 24d875208..3e746c075 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java @@ -35,7 +35,7 @@ public interface INetworkObject void decode(ByteBuf in); - static T readToObject(T obj, ByteBuf inputByteBuf) + static T decodeToInstance(T obj, ByteBuf inputByteBuf) { obj.decode(inputByteBuf); return obj; @@ -135,7 +135,7 @@ public interface INetworkObject // Primitives must be added manually here this.put(Integer.class, new Codec((obj, out) -> out.writeInt((int)obj), (obj, in) -> in.readInt())); - this.put(INetworkObject.class, new Codec(INetworkObject::encode, INetworkObject::readToObject)); + this.put(INetworkObject.class, new Codec(INetworkObject::encode, INetworkObject::decodeToInstance)); this.put(Map.Entry.class, new Codec( (obj, out) -> { Map.Entry entry = (Entry) obj; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java index 724fbcc3d..3ce9fb6f4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java @@ -24,6 +24,7 @@ import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.multiplayer.server.RemotePlayerConnectionHandler; import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; @@ -39,7 +40,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld private final HashMap levels; public final LocalSaveStructure saveStructure; - private final RemotePlayerConnectionHandler remotePlayerConnectionHandler; + public final RemotePlayerConnectionHandler remotePlayerConnectionHandler; //==============// @@ -74,6 +75,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld level.registerNetworkHandlers(playerState); } } + public void removePlayer(IServerPlayerWrapper serverPlayer) { this.getLevel(serverPlayer.getLevel()).removePlayer(serverPlayer); @@ -81,15 +83,14 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld // If player's left, session is already closed } + public void changePlayerLevel(IServerPlayerWrapper player, IServerLevelWrapper origin, IServerLevelWrapper dest) { this.getLevel(dest).addPlayer(player); this.getLevel(origin).removePlayer(player); } - public void handlePluginMessage(IServerPlayerWrapper player, ByteBuf buffer) - { - this.remotePlayerConnectionHandler.handlePluginMessage(player, buffer); - } + + @Override public DhServerLevel getOrLoadLevel(@NotNull ILevelWrapper wrapper) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IPluginPacketSender.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IPluginPacketSender.java index 29bf73256..b16ba0a4b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IPluginPacketSender.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IPluginPacketSender.java @@ -1,5 +1,6 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.misc; +import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable; import io.netty.buffer.ByteBuf; @@ -7,7 +8,7 @@ import java.util.function.Consumer; public interface IPluginPacketSender extends IBindable { - void sendPluginPacketClient(Consumer encoder); - void sendPluginPacketServer(IServerPlayerWrapper serverPlayer, Consumer encoder); + void sendPluginPacketClient(PluginChannelMessage message); + void sendPluginPacketServer(IServerPlayerWrapper serverPlayer, PluginChannelMessage message); } \ No newline at end of file From 39391b944f495b92b36f6cc9ca5a053b9cd47d76 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Thu, 6 Jun 2024 21:25:19 +0500 Subject: [PATCH 140/205] Add toString to messages & fix incorrect call --- .../core/level/DhClientLevel.java | 6 ----- .../client/AbstractFullDataRequestQueue.java | 23 +++++++------------ .../config/AbstractMultiplayerConfig.java | 16 +++++++++++++ .../multiplayer/config/MultiplayerConfig.java | 14 ----------- .../plugin/CurrentLevelKeyMessage.java | 12 +++++++++- .../plugin/base/CloseReasonMessage.java | 9 ++++++++ .../plugin/base/ExceptionMessage.java | 9 ++++++++ .../FullDataPartialUpdateMessage.java | 10 ++++++++ .../FullDataSourceRequestMessage.java | 11 +++++++++ .../FullDataSourceResponseMessage.java | 9 ++++++++ .../session/RemotePlayerConfigMessage.java | 9 ++++++++ .../network/plugin/PluginChannelMessage.java | 14 +++++++++++ .../network/plugin/PluginChannelSession.java | 1 + .../core/network/plugin/TrackableMessage.java | 8 +++++++ .../core/sql/dto/FullDataSourceV2DTO.java | 20 +++++++++++++++- 15 files changed, 134 insertions(+), 37 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 5581dac18..bdf2f3494 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -204,12 +204,6 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel if (this.worldGenModule.isWorldGenRunning()) { - ClientLevelModule.ClientRenderState renderState = this.clientside.ClientRenderStateRef.get(); - if (renderState != null && renderState.quadtree != null) - { - this.dataFileHandler.removeRetrievalRequestIf(p -> !renderState.quadtree.isSectionPosInBounds(p)); - } - this.worldGenModule.worldGenTick( new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos()) .scale(MC_CLIENT.getWrappedClientLevel().getDimensionType().getTeleportationScale(this.getLevelWrapper().getDimensionType())) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index d1edc76c4..f304fda1d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -56,8 +56,6 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, private final AtomicInteger failedRequests = new AtomicInteger(); private final ConfigEntry showDebugWireframeConfig; - private final Set alreadyRequestedPositions = ConcurrentHashMap.newKeySet(); - private final SupplierBasedRateLimiter rateLimiter = new SupplierBasedRateLimiter<>(this::getRequestConcurrencyLimit); private final ScheduledExecutorService taskFinishScheduler = Executors.newScheduledThreadPool(1); @@ -90,15 +88,6 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, { LodUtil.assertTrue(DhSectionPos.getDetailLevel(sectionPos) == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL, "Only highest-detail sections are allowed."); - // check if this is a duplicate task - if (this.alreadyRequestedPositions.contains(sectionPos)) - { - // temporary solution to prevent requesting the same section multiple times - LOGGER.debug("Duplicate section " + sectionPos + ". Skipping..."); - return CompletableFuture.completedFuture(false); - } - this.alreadyRequestedPositions.add(sectionPos); - RequestQueueEntry entry = new RequestQueueEntry(chunkDataConsumer, clientTimestamp); this.waitingTasks.put(sectionPos, entry); return entry.future; @@ -141,12 +130,13 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, if (removeIf.accept(pos)) { + LOGGER.debug("Removing request " + mapEntry.getKey() + "..."); + entry.future.cancel(false); if (entry.request != null) { entry.request.cancel(false); } - this.alreadyRequestedPositions.remove(pos); } } } @@ -183,7 +173,10 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, long sectionPos = mapEntry.getKey(); RequestQueueEntry entry = mapEntry.getValue(); - CompletableFuture request = this.networkState.getSession().sendRequest(new FullDataSourceRequestMessage(this.level.getLevelWrapper(), sectionPos, entry.updateTimestamp), FullDataSourceResponseMessage.class); + CompletableFuture request = this.networkState.getSession().sendRequest( + new FullDataSourceRequestMessage(this.level.getLevelWrapper(), sectionPos, entry.updateTimestamp), + FullDataSourceResponseMessage.class + ); entry.request = request; request.handleAsync((response, throwable) -> { @@ -192,13 +185,13 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, try { + this.waitingTasks.remove(sectionPos); + if (throwable != null) { throw throwable; } - this.waitingTasks.remove(sectionPos); - if (response.dataSourceDto != null) { FullDataSourceV2 fullDataSource = response.dataSourceDto.createPooledDataSource(this.level.getLevelWrapper()); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java index 341780b8d..a3c834406 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java @@ -1,5 +1,6 @@ package com.seibel.distanthorizons.core.multiplayer.config; +import com.google.common.base.MoreObjects; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import io.netty.buffer.ByteBuf; @@ -25,4 +26,19 @@ public abstract class AbstractMultiplayerConfig implements INetworkObject out.writeInt(this.getLoginDataSyncRCLimit()); } + + @Override + public String toString() + { + return MoreObjects.toStringHelper(this) + .add("renderDistanceRadius", this.getRenderDistanceRadius()) + .add("distantGenerationEnabled", this.isDistantGenerationEnabled()) + .add("fullDataRequestConcurrencyLimit", this.getFullDataRequestConcurrencyLimit()) + .add("genTaskPriorityRequestRateLimit", this.getGenTaskPriorityRequestRateLimit()) + .add("realTimeUpdatesEnabled", this.isRealTimeUpdatesEnabled()) + .add("loginDataSyncEnabled", this.isLoginDataSyncEnabled()) + .add("loginDataSyncRCLimit", this.getLoginDataSyncRCLimit()) + .toString(); + } + } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java index 88c10e848..cdb08d678 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java @@ -41,18 +41,4 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig this.loginDataSyncRCLimit = in.readInt(); } - @Override - public String toString() - { - return "MultiplayerConfig{" + - "renderDistanceRadius=" + this.renderDistanceRadius + - ", distantGenerationEnabled=" + this.distantGenerationEnabled + - ", fullDataRequestConcurrencyLimit=" + this.fullDataRequestConcurrencyLimit + - ", genTaskPriorityRequestRateLimit=" + this.genTaskPriorityRequestRateLimit + - ", realTimeUpdatesEnabled=" + this.realTimeUpdatesEnabled + - ", loginDataSyncEnabled=" + this.loginDataSyncEnabled + - ", loginDataSyncRCLimit=" + this.loginDataSyncRCLimit + - '}'; - } - } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/CurrentLevelKeyMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/CurrentLevelKeyMessage.java index cf61f3e36..7f1ec7177 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/CurrentLevelKeyMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/CurrentLevelKeyMessage.java @@ -1,5 +1,6 @@ package com.seibel.distanthorizons.core.network.messages.plugin; +import com.google.common.base.MoreObjects; import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; import io.netty.buffer.ByteBuf; @@ -29,4 +30,13 @@ public class CurrentLevelKeyMessage extends PluginChannelMessage this.deleteExistingData = in.readBoolean(); } -} + + @Override + public MoreObjects.ToStringHelper toStringHelper() + { + return super.toStringHelper() + .add("levelKey", this.levelKey) + .add("deleteExistingData", this.deleteExistingData); + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/CloseReasonMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/CloseReasonMessage.java index 42ef502c4..367a609a8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/CloseReasonMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/CloseReasonMessage.java @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.network.messages.plugin.base; +import com.google.common.base.MoreObjects; import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; import io.netty.buffer.ByteBuf; @@ -38,4 +39,12 @@ public class CloseReasonMessage extends PluginChannelMessage @Override public void decode(ByteBuf in) { this.reason = this.readString(in); } + + @Override + public MoreObjects.ToStringHelper toStringHelper() + { + return super.toStringHelper() + .add("reason", this.reason); + } + } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ExceptionMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ExceptionMessage.java index 1c305aa0c..d10589f5c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ExceptionMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ExceptionMessage.java @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.network.messages.plugin.base; +import com.google.common.base.MoreObjects; import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; import com.seibel.distanthorizons.core.network.exceptions.InvalidSectionPosException; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; @@ -62,4 +63,12 @@ public class ExceptionMessage extends TrackableMessage this.exception = exceptionMap.get(id).getDeclaredConstructor(String.class).newInstance(message); } + + @Override + public MoreObjects.ToStringHelper toStringHelper() + { + return super.toStringHelper() + .add("exception", this.exception); + } + } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataPartialUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataPartialUpdateMessage.java index 7273380c7..321901d88 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataPartialUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataPartialUpdateMessage.java @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.network.messages.plugin.fullData; +import com.google.common.base.MoreObjects; import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; @@ -74,4 +75,13 @@ public class FullDataPartialUpdateMessage extends PluginChannelMessage implement this.dataSourceDto = INetworkObject.decodeToInstance(new FullDataSourceV2DTO(), in); } + + @Override + public MoreObjects.ToStringHelper toStringHelper() + { + return super.toStringHelper() + .add("levelName", this.levelName) + .add("dataSourceDto", this.dataSourceDto); + } + } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceRequestMessage.java index 329151493..8d030a7af 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceRequestMessage.java @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.network.messages.plugin.fullData; +import com.google.common.base.MoreObjects; import com.seibel.distanthorizons.core.network.messages.plugin.ILevelRelatedMessage; import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; @@ -68,4 +69,14 @@ public class FullDataSourceRequestMessage extends TrackableMessage implements IL this.clientTimestamp = this.readOptional(in, in::readLong); } + + @Override + public MoreObjects.ToStringHelper toStringHelper() + { + return super.toStringHelper() + .add("levelName", this.levelName) + .add("sectionPos", this.sectionPos) + .add("clientTimestamp", this.clientTimestamp); + } + } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceResponseMessage.java index bcb2ed00a..74176220d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceResponseMessage.java @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.network.messages.plugin.fullData; +import com.google.common.base.MoreObjects; import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; @@ -71,4 +72,12 @@ public class FullDataSourceResponseMessage extends TrackableMessage this.dataSourceDto = this.readOptional(in, () -> INetworkObject.decodeToInstance(new FullDataSourceV2DTO(), in)); } + + @Override + public MoreObjects.ToStringHelper toStringHelper() + { + return super.toStringHelper() + .add("dataSourceDto", this.dataSourceDto); + } + } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/RemotePlayerConfigMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/RemotePlayerConfigMessage.java index 930a8d41a..3cb0f6795 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/RemotePlayerConfigMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/RemotePlayerConfigMessage.java @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.network.messages.plugin.session; +import com.google.common.base.MoreObjects; import com.seibel.distanthorizons.core.multiplayer.config.AbstractMultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; @@ -38,4 +39,12 @@ public class RemotePlayerConfigMessage extends PluginChannelMessage @Override public void decode(ByteBuf in) { this.payload = INetworkObject.decodeToInstance(new MultiplayerConfig(), in); } + + @Override + public MoreObjects.ToStringHelper toStringHelper() + { + return super.toStringHelper() + .add("payload", this.payload); + } + } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelMessage.java index 372d66d4a..003e8bc39 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelMessage.java @@ -1,5 +1,6 @@ package com.seibel.distanthorizons.core.network.plugin; +import com.google.common.base.MoreObjects; import com.seibel.distanthorizons.core.network.protocol.INetworkObject; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; @@ -23,4 +24,17 @@ public abstract class PluginChannelMessage implements INetworkObject } this.session = connection; } + + + @Override + public String toString() + { + return this.toStringHelper().toString(); + } + + public MoreObjects.ToStringHelper toStringHelper() + { + return MoreObjects.toStringHelper(this); + } + } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java index f8fe6d94a..17939b0cf 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java @@ -51,6 +51,7 @@ public class PluginChannelSession extends NetworkEventSource try { + LOGGER.debug("Received message: " + message); this.handleMessage(message); } catch (Throwable e) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/TrackableMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/TrackableMessage.java index dd899974d..e303f934c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/TrackableMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/TrackableMessage.java @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.network.plugin; +import com.google.common.base.MoreObjects; import com.google.common.collect.MapMaker; import com.seibel.distanthorizons.core.api.internal.SharedApi; import com.seibel.distanthorizons.core.network.messages.plugin.base.ExceptionMessage; @@ -80,4 +81,11 @@ public abstract class TrackableMessage extends PluginChannelMessage protected abstract void encode0(ByteBuf out) throws Exception; protected abstract void decode0(ByteBuf in) throws Exception; + + @Override public MoreObjects.ToStringHelper toStringHelper() + { + return super.toStringHelper() + .add("futureId", this.futureId); + } + } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java index 6fb3148a4..62a69b4fc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.sql.dto; +import com.google.common.base.MoreObjects; import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; import com.seibel.distanthorizons.api.enums.config.EDhApiWorldCompressionMode; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep; @@ -416,7 +417,24 @@ public class FullDataSourceV2DTO implements IBaseDTO, INetworkObject @Override public Long getKey() { return this.pos; } - + @Override + public String toString() + { + return MoreObjects.toStringHelper(this) + .add("levelMinY", this.levelMinY) + .add("pos", this.pos) + .add("dataChecksum", this.dataChecksum) + .add("compressedDataByteArray", this.compressedDataByteArray) + .add("compressedColumnGenStepByteArray", this.compressedColumnGenStepByteArray) + .add("compressedWorldCompressionModeByteArray", this.compressedWorldCompressionModeByteArray) + .add("compressedMappingByteArray", this.compressedMappingByteArray) + .add("dataFormatVersion", this.dataFormatVersion) + .add("compressionModeValue", this.compressionModeValue) + .add("applyToParent", this.applyToParent) + .add("lastModifiedUnixDateTime", this.lastModifiedUnixDateTime) + .add("createdUnixDateTime", this.createdUnixDateTime) + .toString(); + } //================// // helper methods // From dc3aa939db1b70d07ed66e217457b110083413c1 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 8 Jun 2024 00:07:24 +0500 Subject: [PATCH 141/205] Fix logging errors --- .../core/network/plugin/PluginChannelSession.java | 4 ++-- .../distanthorizons/core/sql/dto/FullDataSourceV2DTO.java | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java index 17939b0cf..1243b73aa 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java @@ -51,7 +51,7 @@ public class PluginChannelSession extends NetworkEventSource try { - LOGGER.debug("Received message: " + message); + LOGGER.debug("Received message: {}", message); this.handleMessage(message); } catch (Throwable e) @@ -72,7 +72,7 @@ public class PluginChannelSession extends NetworkEventSource public void sendMessage(PluginChannelMessage message) { - LOGGER.debug("Sending message: " + message); + LOGGER.debug("Sending message: {}", message); Consumer encoder = buffer -> { buffer.writeShort(ModInfo.PROTOCOL_VERSION); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java index 62a69b4fc..97f2ca7a3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java @@ -424,10 +424,10 @@ public class FullDataSourceV2DTO implements IBaseDTO, INetworkObject .add("levelMinY", this.levelMinY) .add("pos", this.pos) .add("dataChecksum", this.dataChecksum) - .add("compressedDataByteArray", this.compressedDataByteArray) - .add("compressedColumnGenStepByteArray", this.compressedColumnGenStepByteArray) - .add("compressedWorldCompressionModeByteArray", this.compressedWorldCompressionModeByteArray) - .add("compressedMappingByteArray", this.compressedMappingByteArray) + .add("compressedDataByteArray length", this.compressedDataByteArray.length) + .add("compressedColumnGenStepByteArray length", this.compressedColumnGenStepByteArray.length) + .add("compressedWorldCompressionModeByteArray length", this.compressedWorldCompressionModeByteArray.length) + .add("compressedMappingByteArray length", this.compressedMappingByteArray.length) .add("dataFormatVersion", this.dataFormatVersion) .add("compressionModeValue", this.compressionModeValue) .add("applyToParent", this.applyToParent) From 562594de2ff89cc4d2d49540f6831a5b05112540 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 9 Jun 2024 22:01:30 +0500 Subject: [PATCH 142/205] Better error on invalid dimension --- .../seibel/distanthorizons/core/level/DhServerLevel.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index c7cf2c277..6ba296c56 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -176,7 +176,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel { return msg -> { - LodUtil.assertTrue(msg instanceof ILevelRelatedMessage, "Received message does not implement " + ILevelRelatedMessage.class.getSimpleName() + ": " + msg.getClass().getSimpleName()); + LodUtil.assertTrue(msg instanceof ILevelRelatedMessage, MessageFormat.format("Received message does not implement {0}: {1}", ILevelRelatedMessage.class.getSimpleName(), msg.getClass().getSimpleName())); // Handle only in requested dimension if (!((ILevelRelatedMessage) msg).isSameLevelAs(this.getLevelWrapper())) @@ -192,9 +192,10 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel if (msg instanceof TrackableMessage) { ((TrackableMessage) msg).sendResponse(new InvalidLevelException(MessageFormat.format( - "Generation not allowed. Requested dimension: {0}, player dimension: {1}", - this.getLevelWrapper().getDimensionType().getDimensionName(), - msg.session.serverPlayer.getLevel().getDimensionType().getDimensionName() + "Generation not allowed. Requested dimension: {0}, player dimension: {1}, handler dimension: {2}", + ((ILevelRelatedMessage) msg).getLevelName(), + msg.session.serverPlayer.getLevel().getDimensionType().getDimensionName(), + this.getLevelWrapper().getDimensionType().getDimensionName() ))); } From bcb21be8489ba54269f353ff490095aa729e2b14 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 10 Jun 2024 23:39:57 +0500 Subject: [PATCH 143/205] Show dimension names --- .../core/file/fullDatafile/GeneratedFullDataSourceProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java index ee61fdba0..2e5ab7273 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java @@ -143,7 +143,7 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im { boolean oldQueueExists = this.worldGenQueueRef.compareAndSet(null, newWorldGenQueue); LodUtil.assertTrue(oldQueueExists, "previous world gen queue is still here!"); - LOGGER.info("Set world gen queue for level [" + this.level + "]."); + LOGGER.info("Set world gen queue for level [" + this.level.getLevelWrapper().getDimensionType().getDimensionName() + "]."); } @Override From 7c705015e61e0b4f3bf3c8badd85c3a488c87bc5 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Thu, 13 Jun 2024 16:44:37 +0500 Subject: [PATCH 144/205] Use level keys --- .../distanthorizons/coreapi/ModInfo.java | 4 +-- .../core/api/internal/ClientApi.java | 20 +++++++------- .../api/internal/ClientPluginChannelApi.java | 16 +++++------- .../distanthorizons/core/config/Config.java | 22 ++++++++++++++++ .../client/ClientNetworkState.java | 4 +-- .../config/AbstractMultiplayerConfig.java | 3 --- .../multiplayer/config/MultiplayerConfig.java | 4 --- .../server/ConstrainedMultiplayerConfig.java | 6 ----- .../server/RemotePlayerConnectionHandler.java | 6 ----- .../multiplayer/server/ServerPlayerState.java | 26 ++++++++++++++----- .../messages/PluginMessageRegistry.java | 14 +++++----- .../plugin/CurrentLevelKeyMessage.java | 9 ++----- .../plugin/base/ClientHelloMessage.java | 22 ---------------- .../network/plugin/PluginChannelSession.java | 6 ----- .../core/world/DhClientWorld.java | 1 - .../core/world/DhServerWorld.java | 2 -- 16 files changed, 72 insertions(+), 93 deletions(-) delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ClientHelloMessage.java diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java index ed39817c5..de67f7f3d 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java @@ -33,8 +33,8 @@ public final class ModInfo // region Protocol versions // Incremented every time any packets are added, changed or removed, with a few exceptions. public static final int PROTOCOL_VERSION = 2; - public static final String PLUGIN_CHANNEL_PATH = "plugin_channel"; - public static final String WRAPPER_PACKET_PATH = "wrapper"; + public static final String PLUGIN_CHANNEL_PATH = "main"; + public static final String WRAPPER_PACKET_PATH = "message"; // endregion diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index da97c8af6..b72219dad 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -50,7 +50,6 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrap import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import com.seibel.distanthorizons.coreapi.util.math.Mat4f; -import io.netty.buffer.ByteBuf; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; @@ -132,6 +131,8 @@ public class ClientApi DhClientWorld world = new DhClientWorld(); SharedApi.setDhWorld(world); + this.pluginChannelApi.onJoin(world.networkState.getSession()); + world.networkState.sendConfigMessage(); LOGGER.info("Loading [" + this.waitingClientLevels.size() + "] waiting client level wrappers."); for (IClientLevelWrapper level : this.waitingClientLevels) @@ -140,8 +141,6 @@ public class ClientApi } this.waitingClientLevels.clear(); - - this.pluginChannelApi.onJoin(world.networkState.getSession()); } } @@ -206,17 +205,20 @@ public class ClientApi { try { - if (!this.pluginChannelApi.allowLoadingLevel()) - { - LOGGER.info("Levels in this connection are managed by the server and loading is not allowed, ignoring auto-load."); - return; - } - LOGGER.info("Loading client level [" + level + "]-["+level.getDimensionType().getDimensionName()+"]."); AbstractDhWorld world = SharedApi.getAbstractDhWorld(); if (world != null) { + if (!this.pluginChannelApi.allowLevelAutoload()) + { + LOGGER.info("Levels in this connection are managed by the server, skipping auto-load."); + + // Instead of attempting to load themselves, send config and wait for level key. + ((DhClientWorld) world).networkState.sendConfigMessage(); + return; + } + world.getOrLoadLevel(level); ApiEventInjector.INSTANCE.fireAllEvents(DhApiLevelLoadEvent.class, new DhApiLevelLoadEvent.EventParam(level)); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index 84c2adfaa..7565f0b20 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -7,18 +7,17 @@ import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.network.messages.plugin.PluginCloseEvent; import com.seibel.distanthorizons.core.network.messages.plugin.CurrentLevelKeyMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.base.ClientHelloMessage; import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; -import io.netty.buffer.ByteBuf; import org.apache.logging.log4j.LogManager; +import org.checkerframework.checker.nullness.qual.NonNull; +import java.util.Objects; import java.util.function.Consumer; /** * This class is used to manage the level keys. - * Its purpose is to separate MC's and plugin channel's event handling. */ public class ClientPluginChannelApi { @@ -33,7 +32,7 @@ public class ClientPluginChannelApi public PluginChannelSession session; - public boolean allowLoadingLevel() + public boolean allowLevelAutoload() { return (KEYED_CLIENT_LEVEL_MANAGER.isEnabled() && KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel() != null) || !KEYED_CLIENT_LEVEL_MANAGER.isEnabled(); @@ -44,15 +43,12 @@ public class ClientPluginChannelApi { this.levelUnloadHandler = levelUnloadHandler; this.multiverseLevelLoadHandler = levelLoadHandler; - } - public void onJoin(PluginChannelSession session) + public void onJoin(@NonNull PluginChannelSession session) { + Objects.requireNonNull(session); this.session = session; - - this.session.sendMessage(new ClientHelloMessage()); - this.session.registerHandler(CurrentLevelKeyMessage.class, this::onCurrentLevelKeyMessage); this.session.registerHandler(PluginCloseEvent.class, this::onClose); } @@ -71,7 +67,9 @@ public class ClientPluginChannelApi if (clientLevel != null) { + // In either case only one of them will have an effect. this.levelUnloadHandler.accept(clientLevel); + this.levelUnloadHandler.accept(KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel()); } IServerKeyedClientLevel keyedLevel = KEYED_CLIENT_LEVEL_MANAGER.setServerKeyedLevel(clientLevel, msg.levelKey); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 74e4ca464..61a8495d7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -899,6 +899,28 @@ public class Config .build(); + + public static ConfigEntry sendLevelKeys = new ConfigEntry.Builder() + .setServersideShortName("sendLevelKeys") + .setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) + .set(true) + .comment("" + + "Makes the server send level keys for each world.\n" + + "Disable this if you use alternative ways to send level keys.\n" + + "") + .build(); + public static ConfigEntry levelKeyPrefix = new ConfigEntry.Builder() + .setServersideShortName("levelKeyPrefix") + .setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) + .set("") + .comment("" + + "Prefix of the level keys sent to the clients.\n" + + "Should be set to a unique value for each backend server behind a proxy,\n" + + "or empty if you don't use a proxy.\n" + + "") + .build(); + + public static ConfigUIComment generationSectionNote = new ConfigUIComment(); public static ConfigEntry generationRequestRCLimit = new ConfigEntry.Builder() .setServersideShortName("generationRequestRCLimit") diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index 370ae1136..28173bb84 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -24,7 +24,7 @@ public class ClientNetworkState implements Closeable public MultiplayerConfig config = new MultiplayerConfig(); private volatile boolean configReceived = false; - private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::onConfigChanged); + private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::sendConfigMessage); public boolean isReady() { return this.configReceived; } private final F3Screen.NestedMessage f3Message = new F3Screen.NestedMessage(this::f3Log); @@ -55,7 +55,7 @@ public class ClientNetworkState implements Closeable }); } - private void onConfigChanged() + public void sendConfigMessage() { this.configReceived = false; this.getSession().sendMessage(new RemotePlayerConfigMessage(new MultiplayerConfig())); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java index a3c834406..317bb0910 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java @@ -9,7 +9,6 @@ public abstract class AbstractMultiplayerConfig implements INetworkObject public abstract int getRenderDistanceRadius(); public abstract boolean isDistantGenerationEnabled(); public abstract int getFullDataRequestConcurrencyLimit(); - public abstract int getGenTaskPriorityRequestRateLimit(); public abstract boolean isRealTimeUpdatesEnabled(); public abstract boolean isLoginDataSyncEnabled(); public abstract int getLoginDataSyncRCLimit(); @@ -20,7 +19,6 @@ public abstract class AbstractMultiplayerConfig implements INetworkObject out.writeInt(this.getRenderDistanceRadius()); out.writeBoolean(this.isDistantGenerationEnabled()); out.writeInt(this.getFullDataRequestConcurrencyLimit()); - out.writeInt(this.getGenTaskPriorityRequestRateLimit()); out.writeBoolean(this.isRealTimeUpdatesEnabled()); out.writeBoolean(this.isLoginDataSyncEnabled()); out.writeInt(this.getLoginDataSyncRCLimit()); @@ -34,7 +32,6 @@ public abstract class AbstractMultiplayerConfig implements INetworkObject .add("renderDistanceRadius", this.getRenderDistanceRadius()) .add("distantGenerationEnabled", this.isDistantGenerationEnabled()) .add("fullDataRequestConcurrencyLimit", this.getFullDataRequestConcurrencyLimit()) - .add("genTaskPriorityRequestRateLimit", this.getGenTaskPriorityRequestRateLimit()) .add("realTimeUpdatesEnabled", this.isRealTimeUpdatesEnabled()) .add("loginDataSyncEnabled", this.isLoginDataSyncEnabled()) .add("loginDataSyncRCLimit", this.getLoginDataSyncRCLimit()) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java index cdb08d678..9fef9e5d4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java @@ -16,9 +16,6 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig public int fullDataRequestConcurrencyLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.generationRequestRCLimit.get(); @Override public int getFullDataRequestConcurrencyLimit() { return this.fullDataRequestConcurrencyLimit; } - public int genTaskPriorityRequestRateLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.genTaskPriorityRequestRateLimit.get(); - @Override public int getGenTaskPriorityRequestRateLimit() { return this.genTaskPriorityRequestRateLimit; } - public boolean realTimeUpdatesEnabled = Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates.get(); @Override public boolean isRealTimeUpdatesEnabled() { return this.realTimeUpdatesEnabled; } @@ -35,7 +32,6 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig this.renderDistanceRadius = in.readInt(); this.distantGenerationEnabled = in.readBoolean(); this.fullDataRequestConcurrencyLimit = in.readInt(); - this.genTaskPriorityRequestRateLimit = in.readInt(); this.realTimeUpdatesEnabled = in.readBoolean(); this.loginDataSyncEnabled = in.readBoolean(); this.loginDataSyncRCLimit = in.readInt(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ConstrainedMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ConstrainedMultiplayerConfig.java index 825082bbe..544cc3963 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ConstrainedMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ConstrainedMultiplayerConfig.java @@ -30,12 +30,6 @@ public class ConstrainedMultiplayerConfig extends AbstractMultiplayerConfig return Math.min(this.clientConfig.fullDataRequestConcurrencyLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.generationRequestRCLimit.get()); } - @Override - public int getGenTaskPriorityRequestRateLimit() - { - return Math.min(this.clientConfig.genTaskPriorityRequestRateLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.genTaskPriorityRequestRateLimit.get()); - } - @Override public boolean isRealTimeUpdatesEnabled() { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java index 2b7c027a4..36d49a866 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java @@ -1,20 +1,14 @@ package com.seibel.distanthorizons.core.multiplayer.server; -import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; -import org.apache.logging.log4j.LogManager; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; public class RemotePlayerConnectionHandler { - private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), - () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); - private final ConcurrentMap connectedPlayers = new ConcurrentHashMap<>(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index 8253a530c..f5e75a877 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -5,9 +5,9 @@ import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; +import com.seibel.distanthorizons.core.network.messages.plugin.CurrentLevelKeyMessage; import com.seibel.distanthorizons.core.network.messages.plugin.session.RemotePlayerConfigMessage; import com.seibel.distanthorizons.core.network.messages.plugin.PluginCloseEvent; -import com.seibel.distanthorizons.core.network.messages.plugin.base.ClientHelloMessage; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceRequestMessage; import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; @@ -22,9 +22,6 @@ import static com.seibel.distanthorizons.core.config.Config.Client.Advanced.Mult public class ServerPlayerState { - private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), - () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); - public final PluginChannelSession session; public IServerPlayerWrapper serverPlayer() { return this.session.serverPlayer; } @@ -51,11 +48,28 @@ public class ServerPlayerState this.session.registerHandler(RemotePlayerConfigMessage.class, remotePlayerConfigMessage -> { this.config.clientConfig = (MultiplayerConfig) remotePlayerConfigMessage.payload; + + if (ServerNetworking.sendLevelKeys.get()) + { + String levelKeyPrefix = ServerNetworking.levelKeyPrefix.get(); + String dimensionName = serverPlayer.getLevel().getDimensionType().getDimensionName(); + + String levelKey; + if (!levelKeyPrefix.isEmpty()) + { + levelKey = levelKeyPrefix + "_" + dimensionName; + } + else + { + levelKey = dimensionName; + } + + this.session.sendMessage(new CurrentLevelKeyMessage(levelKey)); + } + this.session.sendMessage(new RemotePlayerConfigMessage(this.config)); }); - this.session.registerHandler(ClientHelloMessage.class, msg -> this.onConfigChanged()); - this.session.registerHandler(PluginCloseEvent.class, event -> { // Noop }); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PluginMessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PluginMessageRegistry.java index a61d9277e..71bbcff9d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PluginMessageRegistry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PluginMessageRegistry.java @@ -22,7 +22,6 @@ package com.seibel.distanthorizons.core.network.messages; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.seibel.distanthorizons.core.network.messages.plugin.CurrentLevelKeyMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.base.ClientHelloMessage; import com.seibel.distanthorizons.core.network.messages.plugin.base.CancelMessage; import com.seibel.distanthorizons.core.network.messages.plugin.base.CloseReasonMessage; import com.seibel.distanthorizons.core.network.messages.plugin.base.ExceptionMessage; @@ -49,20 +48,19 @@ public class PluginMessageRegistry { // Note: Messages must have parameterless constructors - // Always sent by the client - this.registerMessage(ClientHelloMessage.class, ClientHelloMessage::new); + // When the communication is about to be stopped, either side can send this message this.registerMessage(CloseReasonMessage.class, CloseReasonMessage::new); - // Multiverse support + // Level keys this.registerMessage(CurrentLevelKeyMessage.class, CurrentLevelKeyMessage::new); - // Core + // Config (for full DH support) + this.registerMessage(RemotePlayerConfigMessage.class, RemotePlayerConfigMessage::new); + + // Requests this.registerMessage(CancelMessage.class, CancelMessage::new); this.registerMessage(ExceptionMessage.class, ExceptionMessage::new); - // ID & config - this.registerMessage(RemotePlayerConfigMessage.class, RemotePlayerConfigMessage::new); - // Full data requests & updates this.registerMessage(FullDataSourceRequestMessage.class, FullDataSourceRequestMessage::new); this.registerMessage(FullDataSourceResponseMessage.class, FullDataSourceResponseMessage::new); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/CurrentLevelKeyMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/CurrentLevelKeyMessage.java index 7f1ec7177..b5fd4dd97 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/CurrentLevelKeyMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/CurrentLevelKeyMessage.java @@ -7,27 +7,23 @@ import io.netty.buffer.ByteBuf; public class CurrentLevelKeyMessage extends PluginChannelMessage { public String levelKey; - public boolean deleteExistingData; public CurrentLevelKeyMessage() { } - public CurrentLevelKeyMessage(String levelKey, boolean deleteExistingData) + public CurrentLevelKeyMessage(String levelKey) { this.levelKey = levelKey; - this.deleteExistingData = deleteExistingData; } @Override public void encode(ByteBuf out) { this.writeString(this.levelKey, out); - out.writeBoolean(this.deleteExistingData); } @Override public void decode(ByteBuf in) { this.levelKey = this.readString(in); - this.deleteExistingData = in.readBoolean(); } @@ -35,8 +31,7 @@ public class CurrentLevelKeyMessage extends PluginChannelMessage public MoreObjects.ToStringHelper toStringHelper() { return super.toStringHelper() - .add("levelKey", this.levelKey) - .add("deleteExistingData", this.deleteExistingData); + .add("levelKey", this.levelKey); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ClientHelloMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ClientHelloMessage.java deleted file mode 100644 index c8399e538..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ClientHelloMessage.java +++ /dev/null @@ -1,22 +0,0 @@ -package com.seibel.distanthorizons.core.network.messages.plugin.base; - -import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; -import io.netty.buffer.ByteBuf; - -/** - * Serves as a trigger for the server to send some useful data to the client. - * Not integral to establishing a session. - */ -public class ClientHelloMessage extends PluginChannelMessage -{ - @Override - public void encode(ByteBuf out) - { - } - - @Override - public void decode(ByteBuf in) - { - } - -} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java index 1243b73aa..3974b0fbc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java @@ -74,12 +74,6 @@ public class PluginChannelSession extends NetworkEventSource { LOGGER.debug("Sending message: {}", message); - Consumer encoder = buffer -> { - buffer.writeShort(ModInfo.PROTOCOL_VERSION); - buffer.writeShort(PluginMessageRegistry.INSTANCE.getMessageId(message)); - message.encode(buffer); - }; - if (this.serverPlayer != null) { PACKET_SENDER.sendPluginPacketServer(this.serverPlayer, message); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java index 18115e6d8..9e8b4832c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java @@ -19,7 +19,6 @@ package com.seibel.distanthorizons.core.world; -import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure; import com.seibel.distanthorizons.core.level.DhClientLevel; import com.seibel.distanthorizons.core.level.IDhLevel; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java index 3ce9fb6f4..555210829 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java @@ -24,12 +24,10 @@ import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.multiplayer.server.RemotePlayerConnectionHandler; import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; -import io.netty.buffer.ByteBuf; import org.jetbrains.annotations.NotNull; import java.io.File; From acb299530d112530fa61b46cec162f09c897ea4a Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 14 Jun 2024 16:25:20 +0500 Subject: [PATCH 145/205] Avoid reloading levels when unnecessary --- .../api/internal/ClientPluginChannelApi.java | 34 ++++++++++++++----- .../multiplayer/server/ServerPlayerState.java | 10 +++--- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index 7565f0b20..d3452ec37 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -55,25 +55,41 @@ public class ClientPluginChannelApi private void onCurrentLevelKeyMessage(CurrentLevelKeyMessage msg) { - if (!msg.levelKey.matches("[a-zA-Z0-9_]+")) + if (!msg.levelKey.matches("[a-zA-Z0-9_]{1,50}")) { - throw new IllegalArgumentException("Server sent invalid world key name."); + throw new IllegalArgumentException("Server sent invalid level key."); } - LOGGER.info("Server level change event received, changing the level to [" + msg.levelKey + "]."); + LOGGER.info("Server level key received: " + msg.levelKey); MC.executeOnRenderThread(() -> { IClientLevelWrapper clientLevel = MC.getWrappedClientLevel(true); - - if (clientLevel != null) + IServerKeyedClientLevel existingKeyedClientLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel(); + + if (existingKeyedClientLevel != null) { - // In either case only one of them will have an effect. + if (!existingKeyedClientLevel.getServerLevelKey().equals(msg.levelKey)) + { + LOGGER.info("Unloading previous level with key: " + existingKeyedClientLevel.getServerLevelKey()); + this.levelUnloadHandler.accept(existingKeyedClientLevel); + } + else + { + LOGGER.info("Level key matches the previous level key, ignoring the message."); + } + } + else + { + LOGGER.info("Unloading non-keyed level: " + clientLevel.getDimensionType().getDimensionName()); this.levelUnloadHandler.accept(clientLevel); - this.levelUnloadHandler.accept(KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel()); } - IServerKeyedClientLevel keyedLevel = KEYED_CLIENT_LEVEL_MANAGER.setServerKeyedLevel(clientLevel, msg.levelKey); - this.multiverseLevelLoadHandler.accept(keyedLevel); + if (existingKeyedClientLevel == null || !existingKeyedClientLevel.getServerLevelKey().equals(msg.levelKey)) + { + LOGGER.info("Loading level with key: " + msg.levelKey); + IServerKeyedClientLevel keyedLevel = KEYED_CLIENT_LEVEL_MANAGER.setServerKeyedLevel(clientLevel, msg.levelKey); + this.multiverseLevelLoadHandler.accept(keyedLevel); + } }); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index f5e75a877..540624b34 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -1,8 +1,6 @@ package com.seibel.distanthorizons.core.multiplayer.server; -import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.level.DhServerLevel; -import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; import com.seibel.distanthorizons.core.network.messages.plugin.CurrentLevelKeyMessage; @@ -13,7 +11,6 @@ import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullData import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateAndConcurrencyLimiter; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; -import org.apache.logging.log4j.LogManager; import org.jetbrains.annotations.NotNull; import java.util.concurrent.ConcurrentHashMap; @@ -28,6 +25,7 @@ public class ServerPlayerState @NotNull public ConstrainedMultiplayerConfig config = new ConstrainedMultiplayerConfig(); private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::onConfigChanged); + private String lastLevelKey = ""; private final ConcurrentHashMap rateLimiterSets = new ConcurrentHashMap<>(); public RateLimiterSet getRateLimiterSet(DhServerLevel level) @@ -64,7 +62,11 @@ public class ServerPlayerState levelKey = dimensionName; } - this.session.sendMessage(new CurrentLevelKeyMessage(levelKey)); + if (!levelKey.equals(this.lastLevelKey)) + { + this.lastLevelKey = levelKey; + this.session.sendMessage(new CurrentLevelKeyMessage(levelKey)); + } } this.session.sendMessage(new RemotePlayerConfigMessage(this.config)); From d2f49726934698f5a9b7d44f16df5ad3c058a9bb Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 16 Jun 2024 00:45:16 +0500 Subject: [PATCH 146/205] Put messages received before player joining into queue --- .../core/api/internal/ServerApi.java | 13 ++------ .../server/RemotePlayerConnectionHandler.java | 31 +++++++++++++++++-- .../misc/IServerPlayerWrapper.java | 5 ++- 3 files changed, 33 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java index 4ae6a3d57..11793c1e0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java @@ -21,14 +21,9 @@ 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.config.Config; -import com.seibel.distanthorizons.core.generation.DhLightingEngine; import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; -import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; -import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.world.AbstractDhWorld; import com.seibel.distanthorizons.core.world.DhClientServerWorld; import com.seibel.distanthorizons.core.world.DhServerWorld; @@ -37,7 +32,6 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; -import io.netty.buffer.ByteBuf; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; @@ -157,7 +151,7 @@ public class ServerApi IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); if (serverWorld instanceof DhServerWorld) // TODO add support for DhClientServerWorld's (lan worlds) as well { - LOGGER.debug("Waiting for player to connect: " + player.getUUID()); + LOGGER.info("Creating state for player: " + player.getName()); ((DhServerWorld) serverWorld).addPlayer(player); } } @@ -166,7 +160,7 @@ public class ServerApi IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); if (serverWorld instanceof DhServerWorld) // TODO add support for DhClientServerWorld's (lan worlds) as well { - LOGGER.debug("Removing player from connect wait list: " + player.getUUID()); + LOGGER.info("Destroying state for player: " + player.getName()); ((DhServerWorld) serverWorld).removePlayer(player); } } @@ -175,7 +169,7 @@ public class ServerApi IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); if (serverWorld instanceof DhServerWorld) // TODO add support for DhClientServerWorld's (lan worlds) as well { - LOGGER.debug("Player changed level: " + player.getUUID()); + LOGGER.info("Player changed level: " + player.getName()); ((DhServerWorld) serverWorld).changePlayerLevel(player, origin, dest); } } @@ -185,7 +179,6 @@ public class ServerApi IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); if (serverWorld instanceof DhServerWorld) // TODO add support for DhClientServerWorld's (lan worlds) as well { - LOGGER.debug("Player " + player.getUUID() + " sent plugin message: " + message); ((DhServerWorld) serverWorld).remotePlayerConnectionHandler.handlePluginMessage(player, message); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java index 36d49a866..97b28c093 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java @@ -4,19 +4,30 @@ import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; +import java.util.Queue; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentMap; public class RemotePlayerConnectionHandler { private final ConcurrentMap connectedPlayers = new ConcurrentHashMap<>(); + private final ConcurrentMap> messageQueue = new ConcurrentHashMap<>(); public void handlePluginMessage(IServerPlayerWrapper player, PluginChannelMessage message) { - PluginChannelSession session = this.connectedPlayers.get(player).session; - message.setSession(session); - session.tryHandleMessage(message); + ServerPlayerState playerState = this.connectedPlayers.get(player); + if (playerState != null) + { + PluginChannelSession session = playerState.session; + message.setSession(session); + session.tryHandleMessage(message); + } + else + { + this.messageQueue.computeIfAbsent(player, k -> new ConcurrentLinkedQueue<>()).add(message); + } } public ServerPlayerState getConnectedPlayer(IServerPlayerWrapper player) @@ -33,6 +44,20 @@ public class RemotePlayerConnectionHandler { ServerPlayerState state = new ServerPlayerState(serverPlayer); this.connectedPlayers.put(serverPlayer, state); + + Queue queuedMessages = this.messageQueue.get(serverPlayer); + if (queuedMessages != null) + { + PluginChannelSession session = state.session; + for (PluginChannelMessage message : queuedMessages) + { + message.setSession(session); + session.tryHandleMessage(message); + } + + this.messageQueue.remove(serverPlayer); + } + return state; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java index e162a8059..f33d53354 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java @@ -24,11 +24,10 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapp import com.seibel.distanthorizons.coreapi.util.math.Vec3d; import java.net.SocketAddress; -import java.util.UUID; public interface IServerPlayerWrapper extends IDhApiUnsafeWrapper { - UUID getUUID(); + String getName(); IServerLevelWrapper getLevel(); @@ -37,4 +36,4 @@ public interface IServerPlayerWrapper extends IDhApiUnsafeWrapper int getViewDistance(); SocketAddress getRemoteAddress(); -} +} \ No newline at end of file From 4d6e11fdebba2f3893be9aabf6af038988130e5e Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Wed, 26 Jun 2024 14:55:21 +0500 Subject: [PATCH 147/205] Fix compilation --- .../IFullDataSourceRetrievalQueue.java | 5 ++++- .../core/generation/WorldGenerationQueue.java | 5 ++++- .../core/level/DhClientLevel.java | 4 ++++ .../core/level/DhClientServerLevel.java | 9 ++------- .../core/level/WorldGenModule.java | 12 ++++++----- .../core/logging/f3/F3Screen.java | 4 ++-- .../client/AbstractFullDataRequestQueue.java | 20 +++++++------------ .../client/ClientNetworkState.java | 15 +++++--------- .../core/network/NetworkEventSource.java | 7 +++---- .../exceptions/SessionClosedException.java | 10 ++++++++-- .../core/world/AbstractDhWorld.java | 5 +++-- .../core/world/DhClientWorld.java | 9 ++++++++- 12 files changed, 57 insertions(+), 48 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/IFullDataSourceRetrievalQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/IFullDataSourceRetrievalQueue.java index 9a398d25f..8c9c0440a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/IFullDataSourceRetrievalQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/IFullDataSourceRetrievalQueue.java @@ -26,6 +26,7 @@ import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.LodQuadTree; import java.io.Closeable; +import java.util.List; import java.util.concurrent.CompletableFuture; /** @@ -104,6 +105,8 @@ public interface IFullDataSourceRetrievalQueue extends Closeable /** used for rendering to the F3 menu */ int getEstimatedTotalTaskCount(); void setEstimatedTotalTaskCount(int newEstimate); + + void addDebugMenuStringsToList(List messageList); -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java index 95b712948..452a7b97e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java @@ -47,6 +47,7 @@ import org.apache.logging.log4j.Logger; import java.awt.*; import java.util.*; +import java.util.List; import java.util.concurrent.*; import java.util.function.Consumer; @@ -509,6 +510,8 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb @Override public void setEstimatedTotalTaskCount(int newEstimate) { this.estimatedTotalTaskCount = newEstimate; } + public void addDebugMenuStringsToList(List messageList) { } + //==========// @@ -672,4 +675,4 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 9991693ae..3dd26305a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -277,6 +277,10 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel { messageList.add(" Migration Failed"); } + + + // world gen + this.worldGenModule.addDebugMenuStringsToList(messageList); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java index 42b204e21..94a03fd83 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java @@ -229,12 +229,7 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev // world gen - WorldGenModule worldGenState = this.serverside.worldGenModule; - String worldGenDisplayString = worldGenState.getDebugMenuString(); - if (worldGenDisplayString != null) - { - messageList.add(worldGenDisplayString); - } + this.serverside.worldGenModule.addDebugMenuStringsToList(messageList); } @@ -265,4 +260,4 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev this.clientside.reloadPos(pos); } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java b/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java index b12683f24..301be86de 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java @@ -27,6 +27,7 @@ import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import org.apache.logging.log4j.Logger; import java.io.Closeable; +import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; @@ -137,20 +138,21 @@ public class WorldGenModule implements Closeable public boolean isWorldGenRunning() { return this.worldGenStateRef.get() != null; } - public String getDebugMenuString() + public void addDebugMenuStringsToList(List messageList) { AbstractWorldGenState worldGenState = this.worldGenStateRef.get(); if (worldGenState == null) { - return null; + return; } 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()); - - return "World Gen Tasks: "+waitingCountStr+"/"+totalCountEstimateStr+" (in progress: "+inProgressCountStr+")"; + messageList.add("World Gen Tasks: "+waitingCountStr+"/"+totalCountEstimateStr+" (in progress: "+inProgressCountStr+")"); + + worldGenState.worldGenerationQueue.addDebugMenuStringsToList(messageList); } @@ -185,4 +187,4 @@ public class WorldGenModule implements Closeable { this.worldGenerationQueue.startAndSetTargetPos(targetPosForGeneration); } } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java b/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java index a3d4666be..c317e3def 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java @@ -119,7 +119,7 @@ public class F3Screen messageList.add(""); } // world / levels - messageList.add(world.GetDebugMenuString()); + world.addDebugMenuStringsToList(messageList); for (IDhLevel level : levelIterator) { level.addDebugMenuStringsToList(messageList); @@ -142,4 +142,4 @@ public class F3Screen -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index f304fda1d..da340bd86 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -7,10 +7,10 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSour import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.level.IDhClientLevel; import com.seibel.distanthorizons.core.logging.ConfigBasedSpamLogger; -import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; +import com.seibel.distanthorizons.core.network.exceptions.SessionClosedException; import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceRequestMessage; import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceResponseMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; @@ -20,18 +20,16 @@ import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateLimiter; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; -import io.netty.channel.ChannelException; import org.apache.logging.log4j.LogManager; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import java.awt.*; +import java.util.List; import java.util.Map; -import java.util.Set; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; -import java.util.function.Function; public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, AutoCloseable { @@ -51,7 +49,6 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, protected final ConcurrentMap waitingTasks = new ConcurrentHashMap<>(); private final Semaphore pendingTasksSemaphore = new Semaphore(Short.MAX_VALUE, true); - private final F3Screen.NestedMessage f3Message = new F3Screen.NestedMessage(this::f3Log); private final AtomicInteger finishedRequests = new AtomicInteger(); private final AtomicInteger failedRequests = new AtomicInteger(); private final ConfigEntry showDebugWireframeConfig; @@ -207,7 +204,7 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, { // We're too late / some cases might trigger a bunch of expected rejections } - catch (ChannelException | RateLimitedException e) + catch (SessionClosedException | RateLimitedException e) { if (e instanceof RateLimitedException) { @@ -237,17 +234,15 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, }); } - private String[] f3Log() + public void addDebugMenuStringsToList(List messageList) { if (!this.showInDebug()) { - return new String[0]; + return; } - return new String[]{ - this.getQueueName() + " [" + this.level.getClientLevelWrapper().getDimensionType().getDimensionName() + "]", - "Requests: " + this.finishedRequests + " / " + (this.getWaitingTaskCount() + this.finishedRequests.get()) + " (failed: " + this.failedRequests + ", rate limit: " + this.getRequestConcurrencyLimit() + ")" - }; + messageList.add(this.getQueueName() + " [" + this.level.getClientLevelWrapper().getDimensionType().getDimensionName() + "]"); + messageList.add("Requests: " + this.finishedRequests + " / " + (this.getWaitingTaskCount() + this.finishedRequests.get()) + " (failed: " + this.failedRequests + ", rate limit: " + this.getRequestConcurrencyLimit() + ")"); } @@ -284,7 +279,6 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, @Override public void close() { - this.f3Message.close(); DebugRenderer.unregister(this, this.showDebugWireframeConfig); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index 28173bb84..804b78120 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -12,6 +12,7 @@ import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; import org.apache.logging.log4j.LogManager; import java.io.Closeable; +import java.util.List; public class ClientNetworkState implements Closeable { @@ -27,8 +28,6 @@ public class ClientNetworkState implements Closeable private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::sendConfigMessage); public boolean isReady() { return this.configReceived; } - private final F3Screen.NestedMessage f3Message = new F3Screen.NestedMessage(this::f3Log); - /** * Returns the client used by this instance.

* If you need to subscribe to any packet events, create an instance of {@link ScopedNetworkEventSource} using the returned instance. @@ -61,24 +60,20 @@ public class ClientNetworkState implements Closeable this.getSession().sendMessage(new RemotePlayerConfigMessage(new MultiplayerConfig())); } - private String[] f3Log() + public void addDebugMenuStringsToList(List messageList) { if (this.session.isClosed()) { - return new String[]{ - "Session closed: " + this.session.getCloseReason().getMessage() - }; + messageList.add("Session closed: " + this.session.getCloseReason().getMessage()); + return; } - return new String[]{ - this.serverSupportStatus.message - }; + messageList.add(this.serverSupportStatus.message); } @Override public void close() { - this.f3Message.close(); this.configChangeListener.close(); this.session.close(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java index 93ddc05ad..1b99328b7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java @@ -21,15 +21,14 @@ package com.seibel.distanthorizons.core.network; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; +import com.seibel.distanthorizons.core.network.exceptions.SessionClosedException; import com.seibel.distanthorizons.core.network.messages.PluginMessageRegistry; import com.seibel.distanthorizons.core.network.messages.plugin.base.CancelMessage; import com.seibel.distanthorizons.core.network.messages.plugin.base.ExceptionMessage; import com.seibel.distanthorizons.core.network.messages.plugin.PluginCloseEvent; import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; import com.seibel.distanthorizons.coreapi.ModInfo; -import io.netty.channel.ChannelException; import org.apache.logging.log4j.LogManager; import java.io.InvalidClassException; @@ -124,7 +123,7 @@ public abstract class NetworkEventSource CompletableFuture responseFuture = new CompletableFuture<>(); responseFuture.whenComplete((response, throwable) -> { - if (!(throwable instanceof ChannelException)) + if (!(throwable instanceof SessionClosedException)) { this.pendingFutures.remove(msg.futureId); } @@ -151,7 +150,7 @@ public abstract class NetworkEventSource public void close() { this.handlers.clear(); - this.completeAllFuturesExceptionally(new ChannelException(this.getClass().getSimpleName() + " is closed.")); + this.completeAllFuturesExceptionally(new SessionClosedException(this.getClass().getSimpleName() + " is closed.")); } private static class FutureResponseData diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/SessionClosedException.java b/core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/SessionClosedException.java index d85fcebe2..1c4f0571c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/SessionClosedException.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/SessionClosedException.java @@ -1,5 +1,11 @@ package com.seibel.distanthorizons.core.network.exceptions; -public class SessionClosedException { +import java.io.IOException; -} +public class SessionClosedException extends IOException +{ + public SessionClosedException(String message) + { + super(message); + } +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhWorld.java index 5b08c99a8..785c381b4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhWorld.java @@ -24,6 +24,7 @@ import com.seibel.distanthorizons.core.logging.f3.F3Screen; import org.apache.logging.log4j.Logger; import java.io.Closeable; +import java.util.List; /** * Represents an entire world (aka server) and @@ -53,6 +54,6 @@ public abstract class AbstractDhWorld implements IDhWorld, Closeable // helper methods // - public String GetDebugMenuString() { return this.environment + " World with " + F3Screen.NUMBER_FORMAT.format(this.getLoadedLevelCount()) + " levels"; } + public void addDebugMenuStringsToList(List messageList) { messageList.add(this.environment + " World with " + F3Screen.NUMBER_FORMAT.format(this.getLoadedLevelCount()) + " levels"); } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java index a594a8e6d..0577d2b05 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java @@ -29,8 +29,8 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapp import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import org.jetbrains.annotations.NotNull; -import javax.annotation.Nullable; import java.io.File; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutorService; @@ -130,6 +130,13 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld this.levels.values().forEach(DhClientLevel::doWorldGen); } + @Override + public void addDebugMenuStringsToList(List messageList) + { + super.addDebugMenuStringsToList(messageList); + networkState.addDebugMenuStringsToList(messageList); + } + @Override public void close() { From a0efe44e8d04e4ef25778abfe2efd537d9002a36 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Wed, 26 Jun 2024 23:57:37 +0500 Subject: [PATCH 148/205] Clean up package structure --- .../core/api/internal/ClientApi.java | 4 +-- .../api/internal/ClientPluginChannelApi.java | 10 +++--- .../core/api/internal/ServerApi.java | 4 +-- .../core/level/DhClientLevel.java | 5 ++- .../core/level/DhServerLevel.java | 17 +++++---- .../client/AbstractFullDataRequestQueue.java | 6 ++-- .../client/ClientNetworkState.java | 13 ++++--- .../config/AbstractMultiplayerConfig.java | 2 +- .../server/RemotePlayerConnectionHandler.java | 16 ++++----- .../multiplayer/server/ServerPlayerState.java | 14 ++++---- .../{protocol => }/INetworkObject.java | 2 +- .../{ => event}/NetworkEventSource.java | 33 +++++++++-------- .../plugin => event}/PluginCloseEvent.java | 7 ++-- .../{ => event}/ScopedNetworkEventSource.java | 9 +++-- .../{plugin => }/ILevelRelatedMessage.java | 2 +- ...sageRegistry.java => MessageRegistry.java} | 35 +++++++++---------- .../NetworkMessage.java} | 13 +++---- .../TrackableMessage.java | 8 ++--- .../{plugin => }/base/CloseReasonMessage.java | 6 ++-- .../CurrentLevelKeyMessage.java | 6 ++-- .../RemotePlayerConfigMessage.java | 8 ++--- .../FullDataPartialUpdateMessage.java | 10 +++--- .../FullDataSourceRequestMessage.java | 8 ++--- .../FullDataSourceResponseMessage.java | 6 ++-- .../base => requests}/CancelMessage.java | 4 +-- .../base => requests}/ExceptionMessage.java | 4 +-- .../Session.java} | 20 +++++------ .../SessionClosedException.java | 2 +- .../core/sql/dto/FullDataSourceV2DTO.java | 2 +- .../misc/IPluginPacketSender.java | 9 ++--- 30 files changed, 135 insertions(+), 150 deletions(-) rename core/src/main/java/com/seibel/distanthorizons/core/network/{protocol => }/INetworkObject.java (98%) rename core/src/main/java/com/seibel/distanthorizons/core/network/{ => event}/NetworkEventSource.java (76%) rename core/src/main/java/com/seibel/distanthorizons/core/network/{messages/plugin => event}/PluginCloseEvent.java (63%) rename core/src/main/java/com/seibel/distanthorizons/core/network/{ => event}/ScopedNetworkEventSource.java (79%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{plugin => }/ILevelRelatedMessage.java (86%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{PluginMessageRegistry.java => MessageRegistry.java} (62%) rename core/src/main/java/com/seibel/distanthorizons/core/network/{plugin/PluginChannelMessage.java => messages/NetworkMessage.java} (65%) rename core/src/main/java/com/seibel/distanthorizons/core/network/{plugin => messages}/TrackableMessage.java (88%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{plugin => }/base/CloseReasonMessage.java (86%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{plugin => base}/CurrentLevelKeyMessage.java (72%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{plugin/session => base}/RemotePlayerConfigMessage.java (84%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{plugin => }/fullData/FullDataPartialUpdateMessage.java (85%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{plugin => }/fullData/FullDataSourceRequestMessage.java (86%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{plugin => }/fullData/FullDataSourceResponseMessage.java (91%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{plugin/base => requests}/CancelMessage.java (87%) rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/{plugin/base => requests}/ExceptionMessage.java (94%) rename core/src/main/java/com/seibel/distanthorizons/core/network/{plugin/PluginChannelSession.java => session/Session.java} (79%) rename core/src/main/java/com/seibel/distanthorizons/core/network/{exceptions => session}/SessionClosedException.java (74%) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index b5dfc7c7c..80a8a6d88 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -24,7 +24,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.config.Config; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; +import com.seibel.distanthorizons.core.network.messages.NetworkMessage; import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering; import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode; @@ -330,7 +330,7 @@ public class ClientApi // networking // //============// - public void pluginMessageReceived(@NotNull PluginChannelMessage message) + public void pluginMessageReceived(@NotNull NetworkMessage message) { this.pluginChannelApi.session.tryHandleMessage(message); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index d3452ec37..465372a2f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -5,9 +5,9 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager; import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.network.messages.plugin.PluginCloseEvent; -import com.seibel.distanthorizons.core.network.messages.plugin.CurrentLevelKeyMessage; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; +import com.seibel.distanthorizons.core.network.event.PluginCloseEvent; +import com.seibel.distanthorizons.core.network.messages.base.CurrentLevelKeyMessage; +import com.seibel.distanthorizons.core.network.session.Session; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import org.apache.logging.log4j.LogManager; @@ -29,7 +29,7 @@ public class ClientPluginChannelApi private final Consumer levelUnloadHandler; private final Consumer multiverseLevelLoadHandler; - public PluginChannelSession session; + public Session session; public boolean allowLevelAutoload() @@ -45,7 +45,7 @@ public class ClientPluginChannelApi this.multiverseLevelLoadHandler = levelLoadHandler; } - public void onJoin(@NonNull PluginChannelSession session) + public void onJoin(@NonNull Session session) { Objects.requireNonNull(session); this.session = session; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java index 11793c1e0..7f16944bf 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java @@ -21,7 +21,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.plugin.PluginChannelMessage; +import com.seibel.distanthorizons.core.network.messages.NetworkMessage; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import com.seibel.distanthorizons.core.world.AbstractDhWorld; @@ -174,7 +174,7 @@ public class ServerApi } } - public void pluginMessageReceived(IServerPlayerWrapper player, @NotNull PluginChannelMessage message) + public void pluginMessageReceived(IServerPlayerWrapper player, @NotNull NetworkMessage message) { IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); if (serverWorld instanceof DhServerWorld) // TODO add support for DhClientServerWorld's (lan worlds) as well diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index ae8c7c98c..3c95cfee4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -31,11 +31,10 @@ import com.seibel.distanthorizons.core.generation.WorldRemoteGenerationQueue; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; import com.seibel.distanthorizons.core.multiplayer.client.FullDataRefreshQueue; -import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; -import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataPartialUpdateMessage; +import com.seibel.distanthorizons.core.network.event.ScopedNetworkEventSource; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; -import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 3932deb7e..6dcf896ce 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -27,14 +27,13 @@ import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState; import com.seibel.distanthorizons.core.multiplayer.server.RemotePlayerConnectionHandler; import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; -import com.seibel.distanthorizons.core.network.messages.plugin.ILevelRelatedMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.base.CancelMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceResponseMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataPartialUpdateMessage; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; -import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; +import com.seibel.distanthorizons.core.network.messages.ILevelRelatedMessage; +import com.seibel.distanthorizons.core.network.messages.requests.CancelMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceResponseMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage; +import com.seibel.distanthorizons.core.network.messages.NetworkMessage; +import com.seibel.distanthorizons.core.network.messages.TrackableMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhSectionPos; @@ -178,7 +177,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel - public Consumer currentLevelOnly(Consumer next) + public Consumer currentLevelOnly(Consumer next) { return msg -> { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index da340bd86..725cf2ce0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -10,9 +10,9 @@ 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.RequestRejectedException; -import com.seibel.distanthorizons.core.network.exceptions.SessionClosedException; -import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceResponseMessage; +import com.seibel.distanthorizons.core.network.session.SessionClosedException; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceResponseMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index 804b78120..db5389b23 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -2,13 +2,12 @@ package com.seibel.distanthorizons.core.multiplayer.client; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; -import com.seibel.distanthorizons.core.network.ScopedNetworkEventSource; -import com.seibel.distanthorizons.core.network.messages.plugin.PluginCloseEvent; -import com.seibel.distanthorizons.core.network.messages.plugin.session.RemotePlayerConfigMessage; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; +import com.seibel.distanthorizons.core.network.event.ScopedNetworkEventSource; +import com.seibel.distanthorizons.core.network.event.PluginCloseEvent; +import com.seibel.distanthorizons.core.network.messages.base.RemotePlayerConfigMessage; +import com.seibel.distanthorizons.core.network.session.Session; import org.apache.logging.log4j.LogManager; import java.io.Closeable; @@ -19,7 +18,7 @@ public class ClientNetworkState implements Closeable protected static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); - private final PluginChannelSession session = new PluginChannelSession(null); + private final Session session = new Session(null); private EServerSupportStatus serverSupportStatus = EServerSupportStatus.NONE; @@ -32,7 +31,7 @@ public class ClientNetworkState implements Closeable * Returns the client used by this instance.

* If you need to subscribe to any packet events, create an instance of {@link ScopedNetworkEventSource} using the returned instance. */ - public PluginChannelSession getSession() { return this.session; } + public Session getSession() { return this.session; } /** * Constructs a new instance. diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java index 317bb0910..444a53464 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java @@ -1,7 +1,7 @@ package com.seibel.distanthorizons.core.multiplayer.config; import com.google.common.base.MoreObjects; -import com.seibel.distanthorizons.core.network.protocol.INetworkObject; +import com.seibel.distanthorizons.core.network.INetworkObject; import io.netty.buffer.ByteBuf; public abstract class AbstractMultiplayerConfig implements INetworkObject diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java index 97b28c093..be1c9798f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java @@ -1,7 +1,7 @@ package com.seibel.distanthorizons.core.multiplayer.server; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; +import com.seibel.distanthorizons.core.network.messages.NetworkMessage; +import com.seibel.distanthorizons.core.network.session.Session; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import java.util.Queue; @@ -12,15 +12,15 @@ import java.util.concurrent.ConcurrentMap; public class RemotePlayerConnectionHandler { private final ConcurrentMap connectedPlayers = new ConcurrentHashMap<>(); - private final ConcurrentMap> messageQueue = new ConcurrentHashMap<>(); + private final ConcurrentMap> messageQueue = new ConcurrentHashMap<>(); - public void handlePluginMessage(IServerPlayerWrapper player, PluginChannelMessage message) + public void handlePluginMessage(IServerPlayerWrapper player, NetworkMessage message) { ServerPlayerState playerState = this.connectedPlayers.get(player); if (playerState != null) { - PluginChannelSession session = playerState.session; + Session session = playerState.session; message.setSession(session); session.tryHandleMessage(message); } @@ -45,11 +45,11 @@ public class RemotePlayerConnectionHandler ServerPlayerState state = new ServerPlayerState(serverPlayer); this.connectedPlayers.put(serverPlayer, state); - Queue queuedMessages = this.messageQueue.get(serverPlayer); + Queue queuedMessages = this.messageQueue.get(serverPlayer); if (queuedMessages != null) { - PluginChannelSession session = state.session; - for (PluginChannelMessage message : queuedMessages) + Session session = state.session; + for (NetworkMessage message : queuedMessages) { message.setSession(session); session.tryHandleMessage(message); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index 540624b34..4fc4204ee 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -3,12 +3,12 @@ package com.seibel.distanthorizons.core.multiplayer.server; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; -import com.seibel.distanthorizons.core.network.messages.plugin.CurrentLevelKeyMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.session.RemotePlayerConfigMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.PluginCloseEvent; +import com.seibel.distanthorizons.core.network.messages.base.CurrentLevelKeyMessage; +import com.seibel.distanthorizons.core.network.messages.base.RemotePlayerConfigMessage; +import com.seibel.distanthorizons.core.network.event.PluginCloseEvent; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; -import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelSession; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.session.Session; import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateAndConcurrencyLimiter; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import org.jetbrains.annotations.NotNull; @@ -19,7 +19,7 @@ import static com.seibel.distanthorizons.core.config.Config.Client.Advanced.Mult public class ServerPlayerState { - public final PluginChannelSession session; + public final Session session; public IServerPlayerWrapper serverPlayer() { return this.session.serverPlayer; } @NotNull @@ -41,7 +41,7 @@ public class ServerPlayerState public ServerPlayerState(IServerPlayerWrapper serverPlayer) { - this.session = new PluginChannelSession(serverPlayer); + this.session = new Session(serverPlayer); this.session.registerHandler(RemotePlayerConfigMessage.class, remotePlayerConfigMessage -> { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java b/core/src/main/java/com/seibel/distanthorizons/core/network/INetworkObject.java similarity index 98% rename from core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/INetworkObject.java index 3e746c075..4d81250e1 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/INetworkObject.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/INetworkObject.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.protocol; +package com.seibel.distanthorizons.core.network; import io.netty.buffer.ByteBuf; import org.jetbrains.annotations.Contract; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java similarity index 76% rename from core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java index 1b99328b7..b0926ffda 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java @@ -17,17 +17,16 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network; +package com.seibel.distanthorizons.core.network.event; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.network.exceptions.SessionClosedException; -import com.seibel.distanthorizons.core.network.messages.PluginMessageRegistry; -import com.seibel.distanthorizons.core.network.messages.plugin.base.CancelMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.base.ExceptionMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.PluginCloseEvent; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; -import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; +import com.seibel.distanthorizons.core.network.messages.NetworkMessage; +import com.seibel.distanthorizons.core.network.messages.TrackableMessage; +import com.seibel.distanthorizons.core.network.messages.MessageRegistry; +import com.seibel.distanthorizons.core.network.session.SessionClosedException; +import com.seibel.distanthorizons.core.network.messages.requests.CancelMessage; +import com.seibel.distanthorizons.core.network.messages.requests.ExceptionMessage; import com.seibel.distanthorizons.coreapi.ModInfo; import org.apache.logging.log4j.LogManager; @@ -44,23 +43,23 @@ public abstract class NetworkEventSource { private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); - protected final ConcurrentMap, Set>> handlers = new ConcurrentHashMap<>(); + protected final ConcurrentMap, Set>> handlers = new ConcurrentHashMap<>(); private final ConcurrentMap pendingFutures = new ConcurrentHashMap<>(); - protected boolean hasHandler(Class handlerClass) + protected boolean hasHandler(Class handlerClass) { return this.handlers.containsKey(handlerClass); } - protected void handleMessage(PluginChannelMessage message) + protected void handleMessage(NetworkMessage message) { boolean handled = false; - Set> handlerList = this.handlers.get(message.getClass()); + Set> handlerList = this.handlers.get(message.getClass()); if (handlerList != null) { - for (Consumer handler : handlerList) + for (Consumer handler : handlerList) { handled = true; handler.accept(message); @@ -96,7 +95,7 @@ public abstract class NetworkEventSource } } - public void registerHandler(Class handlerClass, Consumer handlerImplementation) + public void registerHandler(Class handlerClass, Consumer handlerImplementation) { //noinspection unchecked this.handlers.computeIfAbsent(handlerClass, missingHandlerClass -> @@ -104,14 +103,14 @@ public abstract class NetworkEventSource // Will throw if the handler class is not found if (handlerClass != PluginCloseEvent.class) { - PluginMessageRegistry.INSTANCE.getMessageId(handlerClass); + MessageRegistry.INSTANCE.getMessageId(handlerClass); } return new HashSet<>(); }) - .add((Consumer) handlerImplementation); + .add((Consumer) handlerImplementation); } - protected void removeHandler(Class handlerClass, Consumer handlerImplementation) + protected void removeHandler(Class handlerClass, Consumer handlerImplementation) { this.handlers.computeIfAbsent(handlerClass, missingHandlerClass -> new HashSet<>()) .remove(handlerImplementation); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginCloseEvent.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/PluginCloseEvent.java similarity index 63% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginCloseEvent.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/event/PluginCloseEvent.java index 8d2fbb693..1a6cee6b7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/PluginCloseEvent.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/PluginCloseEvent.java @@ -1,13 +1,12 @@ -package com.seibel.distanthorizons.core.network.messages.plugin; +package com.seibel.distanthorizons.core.network.event; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; -import com.seibel.distanthorizons.core.network.protocol.INetworkObject; +import com.seibel.distanthorizons.core.network.messages.NetworkMessage; import io.netty.buffer.ByteBuf; /** * This is not a "real" message, and only used to indicate a disconnection. */ -public class PluginCloseEvent extends PluginChannelMessage +public class PluginCloseEvent extends NetworkMessage { @Override public void encode(ByteBuf out) { throw new UnsupportedOperationException(this.getClass().getSimpleName() + " is not a real message, and cannot be sent."); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/ScopedNetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/ScopedNetworkEventSource.java similarity index 79% rename from core/src/main/java/com/seibel/distanthorizons/core/network/ScopedNetworkEventSource.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/event/ScopedNetworkEventSource.java index a66a2f2b7..2c99e25dd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/ScopedNetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/ScopedNetworkEventSource.java @@ -17,10 +17,9 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network; +package com.seibel.distanthorizons.core.network.event; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; -import com.seibel.distanthorizons.core.network.protocol.INetworkObject; +import com.seibel.distanthorizons.core.network.messages.NetworkMessage; import java.util.function.Consumer; @@ -36,7 +35,7 @@ public final class ScopedNetworkEventSource extends NetworkEventSource } @Override - public void registerHandler(Class handlerClass, Consumer handlerImplementation) + public void registerHandler(Class handlerClass, Consumer handlerImplementation) { if (this.isClosed) { @@ -55,7 +54,7 @@ public final class ScopedNetworkEventSource extends NetworkEventSource public void close() { this.isClosed = true; - for (Class handlerClass : this.handlers.keySet()) + for (Class handlerClass : this.handlers.keySet()) { this.parent.removeHandler(handlerClass, this::handleMessage); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/ILevelRelatedMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ILevelRelatedMessage.java similarity index 86% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/ILevelRelatedMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/ILevelRelatedMessage.java index 8db135a64..3bbb5826d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/ILevelRelatedMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ILevelRelatedMessage.java @@ -1,4 +1,4 @@ -package com.seibel.distanthorizons.core.network.messages.plugin; +package com.seibel.distanthorizons.core.network.messages; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PluginMessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/MessageRegistry.java similarity index 62% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/PluginMessageRegistry.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/MessageRegistry.java index 71bbcff9d..d1c3d00fc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PluginMessageRegistry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/MessageRegistry.java @@ -21,30 +21,29 @@ package com.seibel.distanthorizons.core.network.messages; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; -import com.seibel.distanthorizons.core.network.messages.plugin.CurrentLevelKeyMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.base.CancelMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.base.CloseReasonMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.base.ExceptionMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataPartialUpdateMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.fullData.FullDataSourceResponseMessage; -import com.seibel.distanthorizons.core.network.messages.plugin.session.RemotePlayerConfigMessage; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; +import com.seibel.distanthorizons.core.network.messages.base.CurrentLevelKeyMessage; +import com.seibel.distanthorizons.core.network.messages.base.RemotePlayerConfigMessage; +import com.seibel.distanthorizons.core.network.messages.requests.CancelMessage; +import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage; +import com.seibel.distanthorizons.core.network.messages.requests.ExceptionMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceResponseMessage; import java.util.HashMap; import java.util.Map; import java.util.function.Supplier; -public class PluginMessageRegistry +public class MessageRegistry { - public static final PluginMessageRegistry INSTANCE = new PluginMessageRegistry(); + public static final MessageRegistry INSTANCE = new MessageRegistry(); - private final Map> idToSupplier = new HashMap<>(); - private final BiMap, Integer> classToId = HashBiMap.create(); + private final Map> idToSupplier = new HashMap<>(); + private final BiMap, Integer> classToId = HashBiMap.create(); - private PluginMessageRegistry() + private MessageRegistry() { // Note: Messages must have parameterless constructors @@ -69,14 +68,14 @@ public class PluginMessageRegistry - protected void registerMessage(Class clazz, Supplier supplier) + protected void registerMessage(Class clazz, Supplier supplier) { int id = this.idToSupplier.size() + 1; this.idToSupplier.put(id, supplier); this.classToId.put(clazz, id); } - public PluginChannelMessage createMessage(int messageId) throws IllegalArgumentException + public NetworkMessage createMessage(int messageId) throws IllegalArgumentException { try { @@ -88,12 +87,12 @@ public class PluginMessageRegistry } } - public int getMessageId(PluginChannelMessage message) + public int getMessageId(NetworkMessage message) { return this.getMessageId(message.getClass()); } - public int getMessageId(Class messageClass) + public int getMessageId(Class messageClass) { try { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/NetworkMessage.java similarity index 65% rename from core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/NetworkMessage.java index 003e8bc39..64e684075 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/NetworkMessage.java @@ -1,22 +1,23 @@ -package com.seibel.distanthorizons.core.network.plugin; +package com.seibel.distanthorizons.core.network.messages; import com.google.common.base.MoreObjects; -import com.seibel.distanthorizons.core.network.protocol.INetworkObject; +import com.seibel.distanthorizons.core.network.INetworkObject; +import com.seibel.distanthorizons.core.network.session.Session; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; -public abstract class PluginChannelMessage implements INetworkObject +public abstract class NetworkMessage implements INetworkObject { - public PluginChannelSession session = null; + public Session session = null; public IServerPlayerWrapper serverPlayer() { return this.session.serverPlayer; } public boolean warnWhenUnhandled() { return true; } - public PluginChannelSession getConnection() + public Session getConnection() { return this.session; } - public void setSession(PluginChannelSession connection) + public void setSession(Session connection) { if (this.session != null) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/TrackableMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/TrackableMessage.java similarity index 88% rename from core/src/main/java/com/seibel/distanthorizons/core/network/plugin/TrackableMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/TrackableMessage.java index e303f934c..ca9227c3a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/TrackableMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/TrackableMessage.java @@ -17,20 +17,18 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.plugin; +package com.seibel.distanthorizons.core.network.messages; import com.google.common.base.MoreObjects; -import com.google.common.collect.MapMaker; import com.seibel.distanthorizons.core.api.internal.SharedApi; -import com.seibel.distanthorizons.core.network.messages.plugin.base.ExceptionMessage; +import com.seibel.distanthorizons.core.network.messages.requests.ExceptionMessage; import com.seibel.distanthorizons.core.world.EWorldEnvironment; import io.netty.buffer.ByteBuf; import java.util.Objects; -import java.util.concurrent.ConcurrentMap; import java.util.concurrent.atomic.AtomicInteger; -public abstract class TrackableMessage extends PluginChannelMessage +public abstract class TrackableMessage extends NetworkMessage { private static final AtomicInteger lastId = new AtomicInteger(); // 32 bits - Context ID (not transmitted) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/CloseReasonMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CloseReasonMessage.java similarity index 86% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/CloseReasonMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CloseReasonMessage.java index 367a609a8..3539fb5d7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/CloseReasonMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CloseReasonMessage.java @@ -17,13 +17,13 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.plugin.base; +package com.seibel.distanthorizons.core.network.messages.base; import com.google.common.base.MoreObjects; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; +import com.seibel.distanthorizons.core.network.messages.NetworkMessage; import io.netty.buffer.ByteBuf; -public class CloseReasonMessage extends PluginChannelMessage +public class CloseReasonMessage extends NetworkMessage { public String reason; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/CurrentLevelKeyMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CurrentLevelKeyMessage.java similarity index 72% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/CurrentLevelKeyMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CurrentLevelKeyMessage.java index b5fd4dd97..e2d4e8d7f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/CurrentLevelKeyMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CurrentLevelKeyMessage.java @@ -1,10 +1,10 @@ -package com.seibel.distanthorizons.core.network.messages.plugin; +package com.seibel.distanthorizons.core.network.messages.base; import com.google.common.base.MoreObjects; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; +import com.seibel.distanthorizons.core.network.messages.NetworkMessage; import io.netty.buffer.ByteBuf; -public class CurrentLevelKeyMessage extends PluginChannelMessage +public class CurrentLevelKeyMessage extends NetworkMessage { public String levelKey; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/RemotePlayerConfigMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/RemotePlayerConfigMessage.java similarity index 84% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/RemotePlayerConfigMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/RemotePlayerConfigMessage.java index 3cb0f6795..605c63998 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/session/RemotePlayerConfigMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/RemotePlayerConfigMessage.java @@ -17,16 +17,16 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.plugin.session; +package com.seibel.distanthorizons.core.network.messages.base; import com.google.common.base.MoreObjects; import com.seibel.distanthorizons.core.multiplayer.config.AbstractMultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; -import com.seibel.distanthorizons.core.network.protocol.INetworkObject; +import com.seibel.distanthorizons.core.network.INetworkObject; +import com.seibel.distanthorizons.core.network.messages.NetworkMessage; import io.netty.buffer.ByteBuf; -public class RemotePlayerConfigMessage extends PluginChannelMessage +public class RemotePlayerConfigMessage extends NetworkMessage { public AbstractMultiplayerConfig payload; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataPartialUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java similarity index 85% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataPartialUpdateMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java index 321901d88..be01ba975 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataPartialUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java @@ -17,22 +17,22 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.plugin.fullData; +package com.seibel.distanthorizons.core.network.messages.fullData; import com.google.common.base.MoreObjects; import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; -import com.seibel.distanthorizons.core.network.messages.plugin.ILevelRelatedMessage; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; -import com.seibel.distanthorizons.core.network.protocol.INetworkObject; +import com.seibel.distanthorizons.core.network.messages.ILevelRelatedMessage; +import com.seibel.distanthorizons.core.network.messages.NetworkMessage; +import com.seibel.distanthorizons.core.network.INetworkObject; import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import io.netty.buffer.ByteBuf; import java.io.IOException; -public class FullDataPartialUpdateMessage extends PluginChannelMessage implements ILevelRelatedMessage +public class FullDataPartialUpdateMessage extends NetworkMessage implements ILevelRelatedMessage { private String levelName; @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceRequestMessage.java similarity index 86% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceRequestMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceRequestMessage.java index 8d030a7af..09f72c190 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceRequestMessage.java @@ -17,13 +17,11 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.plugin.fullData; +package com.seibel.distanthorizons.core.network.messages.fullData; import com.google.common.base.MoreObjects; -import com.seibel.distanthorizons.core.network.messages.plugin.ILevelRelatedMessage; -import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; -import com.seibel.distanthorizons.core.network.protocol.INetworkObject; -import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.network.messages.ILevelRelatedMessage; +import com.seibel.distanthorizons.core.network.messages.TrackableMessage; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import io.netty.buffer.ByteBuf; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java similarity index 91% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceResponseMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java index 74176220d..9d8b30908 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/fullData/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java @@ -17,14 +17,14 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.plugin.fullData; +package com.seibel.distanthorizons.core.network.messages.fullData; import com.google.common.base.MoreObjects; import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; -import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; -import com.seibel.distanthorizons.core.network.protocol.INetworkObject; +import com.seibel.distanthorizons.core.network.messages.TrackableMessage; +import com.seibel.distanthorizons.core.network.INetworkObject; import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; import io.netty.buffer.ByteBuf; import org.jetbrains.annotations.Nullable; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/CancelMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/requests/CancelMessage.java similarity index 87% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/CancelMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/requests/CancelMessage.java index 15c173901..b7b52a92e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/CancelMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/requests/CancelMessage.java @@ -17,9 +17,9 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.plugin.base; +package com.seibel.distanthorizons.core.network.messages.requests; -import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; +import com.seibel.distanthorizons.core.network.messages.TrackableMessage; import io.netty.buffer.ByteBuf; public class CancelMessage extends TrackableMessage diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ExceptionMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/requests/ExceptionMessage.java similarity index 94% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ExceptionMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/requests/ExceptionMessage.java index d10589f5c..e1268bf91 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/plugin/base/ExceptionMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/requests/ExceptionMessage.java @@ -17,14 +17,14 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.network.messages.plugin.base; +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.InvalidSectionPosException; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; -import com.seibel.distanthorizons.core.network.plugin.TrackableMessage; +import com.seibel.distanthorizons.core.network.messages.TrackableMessage; import io.netty.buffer.ByteBuf; import java.util.ArrayList; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java b/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java similarity index 79% rename from core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java index 3974b0fbc..609633966 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/plugin/PluginChannelSession.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java @@ -1,23 +1,21 @@ -package com.seibel.distanthorizons.core.network.plugin; +package com.seibel.distanthorizons.core.network.session; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.network.NetworkEventSource; -import com.seibel.distanthorizons.core.network.messages.PluginMessageRegistry; -import com.seibel.distanthorizons.core.network.messages.plugin.PluginCloseEvent; +import com.seibel.distanthorizons.core.network.event.NetworkEventSource; +import com.seibel.distanthorizons.core.network.event.PluginCloseEvent; +import com.seibel.distanthorizons.core.network.messages.NetworkMessage; +import com.seibel.distanthorizons.core.network.messages.TrackableMessage; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; -import com.seibel.distanthorizons.coreapi.ModInfo; -import io.netty.buffer.ByteBuf; import org.apache.logging.log4j.LogManager; import org.jetbrains.annotations.Nullable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; -public class PluginChannelSession extends NetworkEventSource +public class Session extends NetworkEventSource { private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); @@ -36,13 +34,13 @@ public class PluginChannelSession extends NetworkEventSource @Nullable public final IServerPlayerWrapper serverPlayer; - public PluginChannelSession(@Nullable IServerPlayerWrapper serverPlayer) + public Session(@Nullable IServerPlayerWrapper serverPlayer) { this.serverPlayer = serverPlayer; } - public void tryHandleMessage(PluginChannelMessage message) + public void tryHandleMessage(NetworkMessage message) { if (this.closeReason.get() != null) { @@ -70,7 +68,7 @@ public class PluginChannelSession extends NetworkEventSource return responseFuture; } - public void sendMessage(PluginChannelMessage message) + public void sendMessage(NetworkMessage message) { LOGGER.debug("Sending message: {}", message); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/SessionClosedException.java b/core/src/main/java/com/seibel/distanthorizons/core/network/session/SessionClosedException.java similarity index 74% rename from core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/SessionClosedException.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/session/SessionClosedException.java index 1c4f0571c..da6399dcb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/SessionClosedException.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/session/SessionClosedException.java @@ -1,4 +1,4 @@ -package com.seibel.distanthorizons.core.network.exceptions; +package com.seibel.distanthorizons.core.network.session; import java.io.IOException; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java index 987289616..0755b8fcf 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java @@ -26,7 +26,7 @@ import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGeneratio import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.pos.DhSectionPos; -import com.seibel.distanthorizons.core.network.protocol.INetworkObject; +import com.seibel.distanthorizons.core.network.INetworkObject; import com.seibel.distanthorizons.core.util.FullDataPointUtil; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.objects.DataCorruptedException; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IPluginPacketSender.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IPluginPacketSender.java index b16ba0a4b..5ab16ecfa 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IPluginPacketSender.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IPluginPacketSender.java @@ -1,14 +1,11 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.misc; -import com.seibel.distanthorizons.core.network.plugin.PluginChannelMessage; +import com.seibel.distanthorizons.core.network.messages.NetworkMessage; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable; -import io.netty.buffer.ByteBuf; - -import java.util.function.Consumer; public interface IPluginPacketSender extends IBindable { - void sendPluginPacketClient(PluginChannelMessage message); - void sendPluginPacketServer(IServerPlayerWrapper serverPlayer, PluginChannelMessage message); + void sendPluginPacketClient(NetworkMessage message); + void sendPluginPacketServer(IServerPlayerWrapper serverPlayer, NetworkMessage message); } \ No newline at end of file From 083a03666670e0cdae00c7334da973996cd41024 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 28 Jun 2024 15:28:33 +0500 Subject: [PATCH 149/205] Use level's ResourceLocation instead of dimension type's --- .../world/IDhApiDimensionTypeWrapper.java | 4 +- .../interfaces/world/IDhApiLevelWrapper.java | 4 +- .../core/api/internal/ClientApi.java | 4 +- .../api/internal/ClientPluginChannelApi.java | 4 +- .../FullDataToRenderDataTransformer.java | 4 +- .../FullDataSourceProviderV2.java | 8 ++-- .../GeneratedFullDataSourceProvider.java | 2 +- .../structure/ClientOnlySaveStructure.java | 18 ++++---- .../SubDimensionLevelMatcher.java | 4 +- .../core/level/DhClientLevel.java | 2 +- .../core/level/DhClientServerLevel.java | 10 ++--- .../core/level/DhServerLevel.java | 8 ++-- .../core/logging/f3/F3Screen.java | 6 +-- .../client/AbstractFullDataRequestQueue.java | 2 +- .../multiplayer/server/ServerPlayerState.java | 43 ++++++++----------- .../messages/ILevelRelatedMessage.java | 6 +-- .../FullDataPartialUpdateMessage.java | 2 +- .../FullDataSourceRequestMessage.java | 2 +- .../core/render/LodQuadTree.java | 4 +- .../core/world/DhApiWorldProxy.java | 4 +- .../core/world/DhClientServerWorld.java | 6 +-- .../core/world/DhClientWorld.java | 2 +- .../core/world/DhServerWorld.java | 2 +- .../world/IDimensionTypeWrapper.java | 8 +--- .../world/ILevelWrapper.java | 3 ++ .../world/IServerLevelWrapper.java | 21 ++++++++- 26 files changed, 97 insertions(+), 86 deletions(-) diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiDimensionTypeWrapper.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiDimensionTypeWrapper.java index 226ed77a4..a91c1088f 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiDimensionTypeWrapper.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiDimensionTypeWrapper.java @@ -28,10 +28,8 @@ import com.seibel.distanthorizons.api.interfaces.IDhApiUnsafeWrapper; */ public interface IDhApiDimensionTypeWrapper extends IDhApiUnsafeWrapper { - String getDimensionName(); - boolean hasCeiling(); boolean hasSkyLight(); -} +} \ No newline at end of file diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java index 2583dfb4a..b846f1880 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java @@ -34,6 +34,8 @@ public interface IDhApiLevelWrapper extends IDhApiUnsafeWrapper { IDhApiDimensionTypeWrapper getDimensionType(); + String getDimensionName(); + EDhApiLevelType getLevelType(); boolean hasCeiling(); @@ -49,4 +51,4 @@ public interface IDhApiLevelWrapper extends IDhApiUnsafeWrapper */ default int getMinHeight() { return 0; } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index 80a8a6d88..43723a102 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -177,7 +177,7 @@ public class ClientApi { try { - LOGGER.info("Unloading client level [" + level + "]-["+level.getDimensionType().getDimensionName()+"]."); + LOGGER.info("Unloading client level [" + level + "]-["+level.getDimensionName()+"]."); if (level instanceof IServerKeyedClientLevel && !respawn) { @@ -206,7 +206,7 @@ public class ClientApi { try { - LOGGER.info("Loading client level [" + level + "]-["+level.getDimensionType().getDimensionName()+"]."); + LOGGER.info("Loading client level [" + level + "]-["+level.getDimensionName()+"]."); AbstractDhWorld world = SharedApi.getAbstractDhWorld(); if (world != null) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index 465372a2f..bbddfea23 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -55,7 +55,7 @@ public class ClientPluginChannelApi private void onCurrentLevelKeyMessage(CurrentLevelKeyMessage msg) { - if (!msg.levelKey.matches("[a-zA-Z0-9_]{1,50}")) + if (!msg.levelKey.matches("^(?=.{1,50}$)([a-zA-Z0-9-_]+@)?[a-zA-Z0-9-_]+(:[a-zA-Z0-9-_]+)?$")) { throw new IllegalArgumentException("Server sent invalid level key."); } @@ -80,7 +80,7 @@ public class ClientPluginChannelApi } else { - LOGGER.info("Unloading non-keyed level: " + clientLevel.getDimensionType().getDimensionName()); + LOGGER.info("Unloading non-keyed level: " + clientLevel.getDimensionName()); this.levelUnloadHandler.accept(clientLevel); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java index 3f0e29f52..6f3117a6b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java @@ -215,7 +215,7 @@ public class FullDataToRenderDataTransformer if (!brokenPos.contains(fullDataMapping.getPos())) { brokenPos.add(fullDataMapping.getPos()); - String dimName = level.getLevelWrapper().getDimensionType().getDimensionName(); + String dimName = level.getLevelWrapper().getDimensionName(); LOGGER.warn("Unable to get data point with id ["+id+"] " + "(Max possible ID: ["+fullDataMapping.getMaxValidId()+"]) " + "for pos ["+fullDataMapping.getPos()+"] in dimension ["+dimName+"]. " + @@ -321,4 +321,4 @@ public class FullDataToRenderDataTransformer } } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java index d7c220e1b..c5e7d68c5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java @@ -124,7 +124,7 @@ public class FullDataSourceProviderV2 DebugRenderer.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showFullDataUpdateStatus); - String dimensionName = level.getLevelWrapper().getDimensionType().getDimensionName(); + String dimensionName = level.getLevelWrapper().getDimensionName(); // start migrating any legacy data sources present in the background this.migrationThreadPool = ThreadUtil.makeRateLimitedThreadPool(1, MIGRATION_THREAD_NAME_PREFIX + "[" + dimensionName + "]", Config.Client.Advanced.MultiThreading.runTimeRatioForUpdatePropagatorThreads.get(), Thread.MIN_PRIORITY, (Semaphore) null); @@ -376,7 +376,7 @@ public class FullDataSourceProviderV2 private void convertLegacyDataSources() { - String dimensionName = this.level.getLevelWrapper().getDimensionType().getDimensionName(); + String dimensionName = this.level.getLevelWrapper().getDimensionName(); LOGGER.info("Attempting to migrate data sources for: [" + dimensionName + "]-[" + this.saveDir + "]..."); @@ -559,7 +559,7 @@ public class FullDataSourceProviderV2 } this.migrationStartMessageQueued = true; - String dimName = this.level.getLevelWrapper().getDimensionType().getDimensionName(); + String dimName = this.level.getLevelWrapper().getDimensionName(); ClientApi.INSTANCE.showChatMessageNextFrame( "Old Distant Horizons data is being migrated for ["+dimName+"]. \n" + "While migrating LODs may load slowly \n" + @@ -570,7 +570,7 @@ public class FullDataSourceProviderV2 private void showMigrationEndMessage(boolean success) { - String dimName = this.level.getLevelWrapper().getDimensionType().getDimensionName(); + String dimName = this.level.getLevelWrapper().getDimensionName(); if (success) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java index 2e5ab7273..bf433668b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java @@ -143,7 +143,7 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im { boolean oldQueueExists = this.worldGenQueueRef.compareAndSet(null, newWorldGenQueue); LodUtil.assertTrue(oldQueueExists, "previous world gen queue is still here!"); - LOGGER.info("Set world gen queue for level [" + this.level.getLevelWrapper().getDimensionType().getDimensionName() + "]."); + LOGGER.info("Set world gen queue for level [" + this.level.getLevelWrapper().getDimensionName() + "]."); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java b/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java index dc8cad673..84cf97b5c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java @@ -84,7 +84,7 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure if (newLevelWrapper instanceof IServerKeyedClientLevel) { IServerKeyedClientLevel keyedClientLevel = (IServerKeyedClientLevel) newLevelWrapper; - LOGGER.info("Loading level " + newLevelWrapper.getDimensionType().getDimensionName() + " with key: " + keyedClientLevel.getServerLevelKey()); + LOGGER.info("Loading level " + newLevelWrapper.getDimensionName() + " with key: " + keyedClientLevel.getServerLevelKey()); // This world was identified by the server directly, so we can know for sure which folder to use. return new File(getSaveStructureFolderPath() + File.separatorChar + keyedClientLevel.getServerLevelKey()); } @@ -98,9 +98,9 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure // create the matcher if one doesn't exist if (this.subDimMatcher == null || !this.subDimMatcher.isFindingLevel(newClientLevelWrapper)) { - LOGGER.info("Loading level " + newClientLevelWrapper.getDimensionType().getDimensionName()); + LOGGER.info("Loading level " + newClientLevelWrapper.getDimensionName()); - List levelFolders = this.getDhDataFoldersForDimension(newClientLevelWrapper.getDimensionType()); + List levelFolders = this.getDhDataFoldersForDimension(newClientLevelWrapper); this.subDimMatcher = new SubDimensionLevelMatcher(newClientLevelWrapper, this.folder, levelFolders); } @@ -129,7 +129,7 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure private File getLevelFolderWithoutSimilarityMatching(ILevelWrapper level) { - List folders = this.getDhDataFoldersForDimension(level.getDimensionType()); + List folders = this.getDhDataFoldersForDimension(level); if (!folders.isEmpty() && folders.get(0) != null) { // use the first existing sub-dimension @@ -140,12 +140,12 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure else { // no valid sub dimension was found, create a new one - LOGGER.info("Default Sub Dimension not found. Creating: [" + level.getDimensionType().getDimensionName() + "]"); - return new File(this.folder, level.getDimensionType().getDimensionName()); + LOGGER.info("Default Sub Dimension not found. Creating: [" + level.getDimensionName() + "]"); + return new File(this.folder, level.getDimensionName()); } } - public List getDhDataFoldersForDimension(IDimensionTypeWrapper dimensionType) + public List getDhDataFoldersForDimension(ILevelWrapper level) { File[] folders = this.folder.listFiles(); if (folders == null) @@ -154,7 +154,7 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure } // filter by dimension name - String expectedDimName = dimensionType.getDimensionName(); + String expectedDimName = level.getDimensionName(); ArrayList possibleDimFolders = new ArrayList<>(); for (File dimFolder : folders) { @@ -297,4 +297,4 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure @Override public String toString() { return "[" + this.getClass().getSimpleName() + "@" + this.folder.getName() + "]"; } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java index bb76066d0..6263bc23c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java @@ -200,7 +200,7 @@ public class SubDimensionLevelMatcher implements AutoCloseable //================================// // log the start of this attempt - LOGGER.info("Attempting to determine sub-dimension for [" + MC_CLIENT.getWrappedClientLevel().getDimensionType().getDimensionName() + "]"); + LOGGER.info("Attempting to determine sub-dimension for [" + MC_CLIENT.getWrappedClientLevel().getDimensionName() + "]"); LOGGER.info("Player block pos in dimension: [" + this.playerData.playerBlockPos.x + "," + this.playerData.playerBlockPos.y + "," + this.playerData.playerBlockPos.z + "]"); LOGGER.info("Potential Sub Dimension folders: [" + this.potentialLevelFolders.size() + "]"); @@ -379,7 +379,7 @@ public class SubDimensionLevelMatcher implements AutoCloseable } - private File CreateSubDimFolder(String subDimId) { return new File(this.levelsFolder.getPath() + File.separatorChar + this.currentClientLevel.getDimensionType().getDimensionName(), subDimId); } + private File CreateSubDimFolder(String subDimId) { return new File(this.levelsFolder.getPath() + File.separatorChar + this.currentClientLevel.getDimensionName(), subDimId); } @Override public void close() { this.matcherThread.shutdownNow(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 3c95cfee4..7656bd83c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -252,7 +252,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel @Override public void addDebugMenuStringsToList(List messageList) { - String dimName = this.levelWrapper.getDimensionType().getDimensionName(); + String dimName = this.levelWrapper.getDimensionName(); boolean rendering = this.clientside.isRendering(); messageList.add("["+dimName+"] rendering: "+(rendering ? "yes" : "no")); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java index fd946f84f..42698d6d7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java @@ -165,13 +165,13 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev @Override public void clearRenderCache() { - clientside.clearRenderCache(); + this.clientside.clearRenderCache(); } @Override - public IServerLevelWrapper getServerLevelWrapper() { return serverLevelWrapper; } + public IServerLevelWrapper getServerLevelWrapper() { return this.serverLevelWrapper; } @Override - public ILevelWrapper getLevelWrapper() { return getServerLevelWrapper(); } + public ILevelWrapper getLevelWrapper() { return this.getServerLevelWrapper(); } @Override public FullDataSourceProviderV2 getFullDataProvider() { return this.serverside.fullDataFileHandler; } @@ -179,7 +179,7 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev @Override public AbstractSaveStructure getSaveStructure() { - return serverside.saveStructure; + return this.serverside.saveStructure; } @Override @@ -201,7 +201,7 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev public void addDebugMenuStringsToList(List messageList) { // header - String dimName = this.serverLevelWrapper.getDimensionType().getDimensionName(); + String dimName = this.serverLevelWrapper.getDimensionName(); boolean rendering = this.clientside.isRendering(); messageList.add("["+dimName+"] rendering: "+(rendering ? "yes" : "no")); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 6dcf896ce..defa45d1c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -184,7 +184,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel LodUtil.assertTrue(msg instanceof ILevelRelatedMessage, MessageFormat.format("Received message does not implement {0}: {1}", ILevelRelatedMessage.class.getSimpleName(), msg.getClass().getSimpleName())); // Handle only in requested dimension - if (!((ILevelRelatedMessage) msg).isSameLevelAs(this.getLevelWrapper())) + if (!((ILevelRelatedMessage) msg).isSameLevelAs(this.getServerLevelWrapper())) { return; } @@ -199,8 +199,8 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel ((TrackableMessage) msg).sendResponse(new InvalidLevelException(MessageFormat.format( "Generation not allowed. Requested dimension: {0}, player dimension: {1}, handler dimension: {2}", ((ILevelRelatedMessage) msg).getLevelName(), - msg.session.serverPlayer.getLevel().getDimensionType().getDimensionName(), - this.getLevelWrapper().getDimensionType().getDimensionName() + msg.session.serverPlayer.getLevel().getDimensionName(), + this.getLevelWrapper().getDimensionName() ))); } @@ -392,7 +392,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel @Override public void addDebugMenuStringsToList(List messageList) { - String dimName = this.serverLevelWrapper.getDimensionType().getDimensionName(); + String dimName = this.serverLevelWrapper.getDimensionName(); messageList.add("["+dimName+"]"); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java b/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java index c317e3def..eacad1045 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java @@ -78,9 +78,9 @@ public class F3Screen Parent Update #: 12

Client_Server World with 3 levels
- [overworld] rendering: Active
- [the_end] rendering: Inactive
- [the_nether] rendering: Inactive

+ [minecraft:overworld] rendering: Active
+ [minecraft:the_end] rendering: Inactive
+ [minecraft:the_nether] rendering: Inactive

VBO Render Count: 199/374
diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index 725cf2ce0..bae5b6429 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -241,7 +241,7 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, return; } - messageList.add(this.getQueueName() + " [" + this.level.getClientLevelWrapper().getDimensionType().getDimensionName() + "]"); + messageList.add(this.getQueueName() + " [" + this.level.getClientLevelWrapper().getDimensionName() + "]"); messageList.add("Requests: " + this.finishedRequests + " / " + (this.getWaitingTaskCount() + this.finishedRequests.get()) + " (failed: " + this.failedRequests + ", rate limit: " + this.getRequestConcurrencyLimit() + ")"); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index 4fc4204ee..f9f0558de 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -1,5 +1,6 @@ package com.seibel.distanthorizons.core.multiplayer.server; +import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener; import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; @@ -25,8 +26,10 @@ public class ServerPlayerState @NotNull public ConstrainedMultiplayerConfig config = new ConstrainedMultiplayerConfig(); private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::onConfigChanged); + private String lastLevelKey = ""; - + private final ConfigChangeListener levelKeyPrefixChangeListener = new ConfigChangeListener<>(ServerNetworking.levelKeyPrefix, this::sendLevelKey); + private final ConcurrentHashMap rateLimiterSets = new ConcurrentHashMap<>(); public RateLimiterSet getRateLimiterSet(DhServerLevel level) { @@ -46,29 +49,7 @@ public class ServerPlayerState this.session.registerHandler(RemotePlayerConfigMessage.class, remotePlayerConfigMessage -> { this.config.clientConfig = (MultiplayerConfig) remotePlayerConfigMessage.payload; - - if (ServerNetworking.sendLevelKeys.get()) - { - String levelKeyPrefix = ServerNetworking.levelKeyPrefix.get(); - String dimensionName = serverPlayer.getLevel().getDimensionType().getDimensionName(); - - String levelKey; - if (!levelKeyPrefix.isEmpty()) - { - levelKey = levelKeyPrefix + "_" + dimensionName; - } - else - { - levelKey = dimensionName; - } - - if (!levelKey.equals(this.lastLevelKey)) - { - this.lastLevelKey = levelKey; - this.session.sendMessage(new CurrentLevelKeyMessage(levelKey)); - } - } - + this.sendLevelKey(null); this.session.sendMessage(new RemotePlayerConfigMessage(this.config)); }); @@ -78,7 +59,18 @@ public class ServerPlayerState } - + private void sendLevelKey(String ignored) + { + if (ServerNetworking.sendLevelKeys.get()) + { + String levelKey = this.serverPlayer().getLevel().getKeyedLevelDimensionName(); + if (!levelKey.equals(this.lastLevelKey)) + { + this.lastLevelKey = levelKey; + this.session.sendMessage(new CurrentLevelKeyMessage(levelKey)); + } + } + } private void onConfigChanged() { @@ -87,6 +79,7 @@ public class ServerPlayerState public void close() { + this.levelKeyPrefixChangeListener.close(); this.configChangeListener.close(); this.session.close(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ILevelRelatedMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ILevelRelatedMessage.java index 3bbb5826d..c952d1ca1 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ILevelRelatedMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ILevelRelatedMessage.java @@ -1,6 +1,6 @@ package com.seibel.distanthorizons.core.network.messages; -import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; public interface ILevelRelatedMessage { @@ -10,9 +10,9 @@ public interface ILevelRelatedMessage * Checks whether the message's level matches the given level. */ @SuppressWarnings("BooleanMethodIsAlwaysInverted") - default boolean isSameLevelAs(ILevelWrapper levelWrapper) + default boolean isSameLevelAs(IServerLevelWrapper levelWrapper) { - return this.getLevelName().equals(levelWrapper.getDimensionType().getDimensionName()); + return this.getLevelName().equals(levelWrapper.getKeyedLevelDimensionName()); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java index be01ba975..05b0a4a39 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java @@ -44,7 +44,7 @@ public class FullDataPartialUpdateMessage extends NetworkMessage implements ILev public FullDataPartialUpdateMessage() { } public FullDataPartialUpdateMessage(ILevelWrapper level, FullDataSourceV2 fullDataSource) { - this.levelName = level.getDimensionType().getDimensionName(); + this.levelName = level.getDimensionName(); try { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceRequestMessage.java index 09f72c190..0803cf7d4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceRequestMessage.java @@ -43,7 +43,7 @@ public class FullDataSourceRequestMessage extends TrackableMessage implements IL public FullDataSourceRequestMessage() {} public FullDataSourceRequestMessage(ILevelWrapper levelWrapper, long sectionPos, @Nullable Long clientTimestamp) { - this.levelName = levelWrapper.getDimensionType().getDimensionName(); + this.levelName = levelWrapper.getDimensionName(); this.sectionPos = sectionPos; this.clientTimestamp = clientTimestamp; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java index 49c349a42..3c819d544 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java @@ -150,7 +150,7 @@ public class LodQuadTree extends QuadTree implements IDebugRen } catch (Exception e) { - LOGGER.error("Quad Tree tick exception for dimension: " + this.level.getClientLevelWrapper().getDimensionType().getDimensionName() + ", exception: " + e.getMessage(), e); + LOGGER.error("Quad Tree tick exception for dimension: " + this.level.getClientLevelWrapper().getDimensionName() + ", exception: " + e.getMessage(), e); } finally { @@ -690,4 +690,4 @@ public class LodQuadTree extends QuadTree implements IDebugRen LOGGER.info("Finished shutting down " + LodQuadTree.class.getSimpleName()); } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhApiWorldProxy.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhApiWorldProxy.java index d7855be30..88893d9ac 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhApiWorldProxy.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhApiWorldProxy.java @@ -130,7 +130,7 @@ public class DhApiWorldProxy implements IDhApiWorldProxy for (IDhLevel dhLevel : world.getAllLoadedLevels()) { ILevelWrapper levelWrapper = dhLevel.getLevelWrapper(); - String levelDimName = levelWrapper.getDimensionType().getDimensionName().toLowerCase(); + String levelDimName = levelWrapper.getDimensionName().toLowerCase(); if (levelDimName.contains(soughtDimName)) { returnList.add(levelWrapper); @@ -140,4 +140,4 @@ public class DhApiWorldProxy implements IDhApiWorldProxy return returnList; } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java index 7b39f48e7..1471cfabb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java @@ -87,7 +87,7 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor LodUtil.assertTrue(serverLevelWrapper != null); if (!clientLevelWrapper.getDimensionType().equals(serverLevelWrapper.getDimensionType())) { - LodUtil.assertNotReach("tryGetServerSideWrapper returned a level for a different dimension. ClientLevelWrapper dim: " + clientLevelWrapper.getDimensionType().getDimensionName() + " ServerLevelWrapper dim: " + serverLevelWrapper.getDimensionType().getDimensionName()); + LodUtil.assertNotReach("tryGetServerSideWrapper returned a level for a different dimension. ClientLevelWrapper dim: " + clientLevelWrapper.getDimensionName() + " ServerLevelWrapper dim: " + serverLevelWrapper.getDimensionName()); } @@ -167,7 +167,7 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor // close each level for (DhClientServerLevel level : levelsToClose) { - LOGGER.info("Unloading level " + level.getServerLevelWrapper().getDimensionType().getDimensionName()); + LOGGER.info("Unloading level " + level.getServerLevelWrapper().getDimensionName()); // level wrapper shouldn't be null, but just in case IServerLevelWrapper serverLevelWrapper = level.getServerLevelWrapper(); @@ -184,4 +184,4 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor LOGGER.info("Closed DhWorld of type " + this.environment); } -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java index 0577d2b05..fe293a903 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java @@ -145,7 +145,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld for (DhClientLevel dhClientLevel : this.levels.values()) { - LOGGER.info("Unloading level " + dhClientLevel.getLevelWrapper().getDimensionType().getDimensionName()); + LOGGER.info("Unloading level " + dhClientLevel.getLevelWrapper().getDimensionName()); // level wrapper shouldn't be null, but just in case IClientLevelWrapper clientLevelWrapper = dhClientLevel.getClientLevelWrapper(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java index d8bafb0db..92ca865f6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhServerWorld.java @@ -159,7 +159,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld { for (DhServerLevel level : this.levels.values()) { - LOGGER.info("Unloading level " + level.getLevelWrapper().getDimensionType().getDimensionName()); + LOGGER.info("Unloading level " + level.getLevelWrapper().getDimensionName()); // level wrapper shouldn't be null, but just in case IServerLevelWrapper serverLevelWrapper = level.getServerLevelWrapper(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IDimensionTypeWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IDimensionTypeWrapper.java index c1d3b79f0..c1d7f67c9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IDimensionTypeWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IDimensionTypeWrapper.java @@ -24,18 +24,14 @@ import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindab public interface IDimensionTypeWrapper extends IDhApiDimensionTypeWrapper, IBindable { - @Override - String getDimensionName(); - @Override boolean hasCeiling(); @Override boolean hasSkyLight(); - // there's definitely a better way of doing this, but it should work well enough for now - default boolean isTheEnd() { return this.getDimensionName().equalsIgnoreCase("the_end"); } + boolean isTheEnd(); double getTeleportationScale(IDimensionTypeWrapper to); -} +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java index bc00c2562..d9cea7b95 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java @@ -37,6 +37,9 @@ public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable @Override IDimensionTypeWrapper getDimensionType(); + @Override + String getDimensionName(); + @Override boolean hasCeiling(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java index b6740bb13..b4ae28dc8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java @@ -22,11 +22,30 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.world; import org.jetbrains.annotations.Nullable; import java.io.File; +import static com.seibel.distanthorizons.core.config.Config.Client.Advanced.Multiplayer.ServerNetworking; + public interface IServerLevelWrapper extends ILevelWrapper { @Nullable IClientLevelWrapper tryGetClientLevelWrapper(); File getSaveFolder(); + + default String getKeyedLevelDimensionName() + { + String dimensionName = this.getDimensionName(); + + if (ServerNetworking.sendLevelKeys.get()) + { + String levelKeyPrefix = ServerNetworking.levelKeyPrefix.get(); + + if (!levelKeyPrefix.isEmpty()) + { + return levelKeyPrefix + "@" + dimensionName; + } + } + + return dimensionName; + } -} +} \ No newline at end of file From 3ff9a9306615e73e251c1daa5d85436ecc8a3ce7 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 28 Jun 2024 15:52:39 +0500 Subject: [PATCH 150/205] Fix test compilation failing --- .../worldGeneratorInjection/objects/LevelWrapperTest.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/src/test/java/testItems/worldGeneratorInjection/objects/LevelWrapperTest.java b/core/src/test/java/testItems/worldGeneratorInjection/objects/LevelWrapperTest.java index 187a86c41..b22178b55 100644 --- a/core/src/test/java/testItems/worldGeneratorInjection/objects/LevelWrapperTest.java +++ b/core/src/test/java/testItems/worldGeneratorInjection/objects/LevelWrapperTest.java @@ -37,6 +37,9 @@ public class LevelWrapperTest implements IDhApiLevelWrapper @Override public IDhApiDimensionTypeWrapper getDimensionType() { return null; } + @Override + public String getDimensionName() { return null; } + @Override public EDhApiLevelType getLevelType() { return null; } @@ -52,4 +55,4 @@ public class LevelWrapperTest implements IDhApiLevelWrapper @Override public int getMinHeight() { return IDhApiLevelWrapper.super.getMinHeight(); } -} +} \ No newline at end of file From 4cdf31cfe8d28ca45707a68b9dcf131a71807067 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 28 Jun 2024 17:25:09 +0500 Subject: [PATCH 151/205] Fix paths unable to be created on Windows --- .../core/api/internal/ClientPluginChannelApi.java | 2 +- .../core/wrapperInterfaces/world/IServerLevelWrapper.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index bbddfea23..5b37ada58 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -55,7 +55,7 @@ public class ClientPluginChannelApi private void onCurrentLevelKeyMessage(CurrentLevelKeyMessage msg) { - if (!msg.levelKey.matches("^(?=.{1,50}$)([a-zA-Z0-9-_]+@)?[a-zA-Z0-9-_]+(:[a-zA-Z0-9-_]+)?$")) + if (!msg.levelKey.matches("^[a-zA-Z0-9-_@]{1,50}$")) { throw new IllegalArgumentException("Server sent invalid level key."); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java index b4ae28dc8..06b68f50c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java @@ -41,7 +41,7 @@ public interface IServerLevelWrapper extends ILevelWrapper if (!levelKeyPrefix.isEmpty()) { - return levelKeyPrefix + "@" + dimensionName; + return levelKeyPrefix + "@" + dimensionName.replace(":", "@@"); } } From 13638cedee4d1544dbbaee464dc5677cc63f7369 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 28 Jun 2024 19:28:36 +0500 Subject: [PATCH 152/205] Move fixing the dimension name into correct place --- .../core/wrapperInterfaces/world/IServerLevelWrapper.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java index 06b68f50c..816ff5644 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java @@ -33,7 +33,7 @@ public interface IServerLevelWrapper extends ILevelWrapper default String getKeyedLevelDimensionName() { - String dimensionName = this.getDimensionName(); + String dimensionName = this.getDimensionName().replace(":", "@@"); if (ServerNetworking.sendLevelKeys.get()) { @@ -41,7 +41,7 @@ public interface IServerLevelWrapper extends ILevelWrapper if (!levelKeyPrefix.isEmpty()) { - return levelKeyPrefix + "@" + dimensionName.replace(":", "@@"); + return levelKeyPrefix + "@" + dimensionName; } } From 47541fa99cc00c8fd472648e04a253efa288d60d Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 28 Jun 2024 23:18:24 +0500 Subject: [PATCH 153/205] Move fixing the dimension name into even more correct place --- .../core/api/internal/ClientPluginChannelApi.java | 2 +- .../core/file/structure/ClientOnlySaveStructure.java | 4 ++-- .../core/wrapperInterfaces/world/IServerLevelWrapper.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index 5b37ada58..bbddfea23 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -55,7 +55,7 @@ public class ClientPluginChannelApi private void onCurrentLevelKeyMessage(CurrentLevelKeyMessage msg) { - if (!msg.levelKey.matches("^[a-zA-Z0-9-_@]{1,50}$")) + if (!msg.levelKey.matches("^(?=.{1,50}$)([a-zA-Z0-9-_]+@)?[a-zA-Z0-9-_]+(:[a-zA-Z0-9-_]+)?$")) { throw new IllegalArgumentException("Server sent invalid level key."); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java b/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java index 84cf97b5c..7c9c36308 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java @@ -86,7 +86,7 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure IServerKeyedClientLevel keyedClientLevel = (IServerKeyedClientLevel) newLevelWrapper; LOGGER.info("Loading level " + newLevelWrapper.getDimensionName() + " with key: " + keyedClientLevel.getServerLevelKey()); // This world was identified by the server directly, so we can know for sure which folder to use. - return new File(getSaveStructureFolderPath() + File.separatorChar + keyedClientLevel.getServerLevelKey()); + return new File(getSaveStructureFolderPath() + File.separatorChar + keyedClientLevel.getServerLevelKey().replaceAll(":", "@@")); } @@ -141,7 +141,7 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure { // no valid sub dimension was found, create a new one LOGGER.info("Default Sub Dimension not found. Creating: [" + level.getDimensionName() + "]"); - return new File(this.folder, level.getDimensionName()); + return new File(this.folder, level.getDimensionName().replaceAll(":", "@@")); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java index 816ff5644..b4ae28dc8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java @@ -33,7 +33,7 @@ public interface IServerLevelWrapper extends ILevelWrapper default String getKeyedLevelDimensionName() { - String dimensionName = this.getDimensionName().replace(":", "@@"); + String dimensionName = this.getDimensionName(); if (ServerNetworking.sendLevelKeys.get()) { From 1b48d61d3ffaab7ca133ea8738ae4045d936563e Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 30 Jun 2024 22:35:41 +0500 Subject: [PATCH 154/205] Rename CloseEvent --- .../core/api/internal/ClientPluginChannelApi.java | 6 +++--- .../core/multiplayer/client/ClientNetworkState.java | 4 ++-- .../core/multiplayer/server/ServerPlayerState.java | 4 ++-- .../event/{PluginCloseEvent.java => CloseEvent.java} | 2 +- .../core/network/event/NetworkEventSource.java | 2 +- .../distanthorizons/core/network/session/Session.java | 4 ++-- 6 files changed, 11 insertions(+), 11 deletions(-) rename core/src/main/java/com/seibel/distanthorizons/core/network/event/{PluginCloseEvent.java => CloseEvent.java} (91%) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index bbddfea23..ae9847328 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -5,7 +5,7 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager; import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.network.event.PluginCloseEvent; +import com.seibel.distanthorizons.core.network.event.CloseEvent; import com.seibel.distanthorizons.core.network.messages.base.CurrentLevelKeyMessage; import com.seibel.distanthorizons.core.network.session.Session; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; @@ -50,7 +50,7 @@ public class ClientPluginChannelApi Objects.requireNonNull(session); this.session = session; this.session.registerHandler(CurrentLevelKeyMessage.class, this::onCurrentLevelKeyMessage); - this.session.registerHandler(PluginCloseEvent.class, this::onClose); + this.session.registerHandler(CloseEvent.class, this::onClose); } private void onCurrentLevelKeyMessage(CurrentLevelKeyMessage msg) @@ -98,7 +98,7 @@ public class ClientPluginChannelApi KEYED_CLIENT_LEVEL_MANAGER.clearServerKeyedLevel(); } - private void onClose(PluginCloseEvent event) + private void onClose(CloseEvent event) { KEYED_CLIENT_LEVEL_MANAGER.disable(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index db5389b23..7a27e13d6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -5,7 +5,7 @@ import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; import com.seibel.distanthorizons.core.network.event.ScopedNetworkEventSource; -import com.seibel.distanthorizons.core.network.event.PluginCloseEvent; +import com.seibel.distanthorizons.core.network.event.CloseEvent; import com.seibel.distanthorizons.core.network.messages.base.RemotePlayerConfigMessage; import com.seibel.distanthorizons.core.network.session.Session; import org.apache.logging.log4j.LogManager; @@ -47,7 +47,7 @@ public class ClientNetworkState implements Closeable this.configReceived = true; }); - this.session.registerHandler(PluginCloseEvent.class, msg -> + this.session.registerHandler(CloseEvent.class, msg -> { this.configReceived = false; }); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index f9f0558de..6a3c04502 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -6,7 +6,7 @@ import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; import com.seibel.distanthorizons.core.network.messages.base.CurrentLevelKeyMessage; import com.seibel.distanthorizons.core.network.messages.base.RemotePlayerConfigMessage; -import com.seibel.distanthorizons.core.network.event.PluginCloseEvent; +import com.seibel.distanthorizons.core.network.event.CloseEvent; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage; import com.seibel.distanthorizons.core.network.session.Session; @@ -53,7 +53,7 @@ public class ServerPlayerState this.session.sendMessage(new RemotePlayerConfigMessage(this.config)); }); - this.session.registerHandler(PluginCloseEvent.class, event -> { + this.session.registerHandler(CloseEvent.class, event -> { // Noop }); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/PluginCloseEvent.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/CloseEvent.java similarity index 91% rename from core/src/main/java/com/seibel/distanthorizons/core/network/event/PluginCloseEvent.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/event/CloseEvent.java index 1a6cee6b7..8ee84fbfb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/event/PluginCloseEvent.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/CloseEvent.java @@ -6,7 +6,7 @@ import io.netty.buffer.ByteBuf; /** * This is not a "real" message, and only used to indicate a disconnection. */ -public class PluginCloseEvent extends NetworkMessage +public class CloseEvent extends NetworkMessage { @Override public void encode(ByteBuf out) { throw new UnsupportedOperationException(this.getClass().getSimpleName() + " is not a real message, and cannot be sent."); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java index b0926ffda..d77ae366a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java @@ -101,7 +101,7 @@ public abstract class NetworkEventSource this.handlers.computeIfAbsent(handlerClass, missingHandlerClass -> { // Will throw if the handler class is not found - if (handlerClass != PluginCloseEvent.class) + if (handlerClass != CloseEvent.class) { MessageRegistry.INSTANCE.getMessageId(handlerClass); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java b/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java index 609633966..954748324 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java @@ -4,7 +4,7 @@ import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.network.event.NetworkEventSource; -import com.seibel.distanthorizons.core.network.event.PluginCloseEvent; +import com.seibel.distanthorizons.core.network.event.CloseEvent; import com.seibel.distanthorizons.core.network.messages.NetworkMessage; import com.seibel.distanthorizons.core.network.messages.TrackableMessage; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender; @@ -91,7 +91,7 @@ public class Session extends NetworkEventSource try { - this.handleMessage(new PluginCloseEvent()); + this.handleMessage(new CloseEvent()); } catch (Throwable ignored) { From 7e48c49e337694c59d53191a123150cb8234130e Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 1 Jul 2024 00:15:54 +0500 Subject: [PATCH 155/205] Make data source encoding lazy and move it off server thread --- .../core/level/DhServerLevel.java | 6 ++-- .../FullDataPartialUpdateMessage.java | 36 ++++++++++++++----- .../FullDataSourceResponseMessage.java | 28 ++++++++++----- 3 files changed, 51 insertions(+), 19 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index defa45d1c..30f19b99f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -241,6 +241,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel // This semaphore is intentionally acquired forever entry.requestCollectionSemaphore.acquireUninterruptibly(Short.MAX_VALUE); + FullDataSourceResponseMessage response = new FullDataSourceResponseMessage(entry.fullDataSource); for (FullDataSourceRequestMessage msg : entry.requestMessages.values()) { this.fullDataRequests.remove(msg.futureId); @@ -252,7 +253,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel } serverPlayerState.getRateLimiterSet(this).fullDataRequestConcurrencyLimiter.release(); - msg.sendResponse(new FullDataSourceResponseMessage(entry.fullDataSource)); + msg.sendResponse(response); } } } @@ -265,6 +266,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel return this.getFullDataProvider().updateDataSourceAsync(data); } + FullDataPartialUpdateMessage updateMessage = new FullDataPartialUpdateMessage(this.serverLevelWrapper, data); for (ServerPlayerState serverPlayerState : this.remotePlayerConnectionHandler.getConnectedPlayers()) { if (!serverPlayerState.config.isRealTimeUpdatesEnabled()) @@ -277,7 +279,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel if (distanceFromPlayer >= serverPlayerState.serverPlayer().getViewDistance() && distanceFromPlayer <= serverPlayerState.config.getRenderDistanceRadius()) { - serverPlayerState.session.sendMessage(new FullDataPartialUpdateMessage(this.serverLevelWrapper, data)); + serverPlayerState.session.sendMessage(updateMessage); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java index 05b0a4a39..bc419b869 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java @@ -20,6 +20,7 @@ package com.seibel.distanthorizons.core.network.messages.fullData; import com.google.common.base.MoreObjects; +import com.google.common.base.Suppliers; import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; @@ -29,8 +30,11 @@ import com.seibel.distanthorizons.core.network.INetworkObject; import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import io.netty.buffer.ByteBuf; +import org.checkerframework.checker.nullness.qual.NonNull; +import org.jetbrains.annotations.Nullable; import java.io.IOException; +import java.util.function.Supplier; public class FullDataPartialUpdateMessage extends NetworkMessage implements ILevelRelatedMessage { @@ -38,18 +42,35 @@ public class FullDataPartialUpdateMessage extends NetworkMessage implements ILev @Override public String getLevelName() { return this.levelName; } + // Encode only + @Nullable + private final FullDataSourceV2 fullDataSource; + private final Supplier dataSourceDtoSupplier = Suppliers.memoize(this::createDataSourceDto); + + // Decode only + @Nullable public FullDataSourceV2DTO dataSourceDto; - public FullDataPartialUpdateMessage() { } - public FullDataPartialUpdateMessage(ILevelWrapper level, FullDataSourceV2 fullDataSource) + public FullDataPartialUpdateMessage() { this.fullDataSource = null; } + public FullDataPartialUpdateMessage(ILevelWrapper level, @NonNull FullDataSourceV2 fullDataSource) { this.levelName = level.getDimensionName(); - + this.fullDataSource = fullDataSource; + } + + + @Override + public boolean warnWhenUnhandled() { return false; } + + + private FullDataSourceV2DTO createDataSourceDto() + { try { + assert this.fullDataSource != null; EDhApiDataCompressionMode compressionMode = Config.Client.Advanced.LodBuilding.dataCompression.get(); - this.dataSourceDto = FullDataSourceV2DTO.CreateFromDataSource(fullDataSource, compressionMode); + return FullDataSourceV2DTO.CreateFromDataSource(this.fullDataSource, compressionMode); } catch (IOException e) { @@ -57,15 +78,11 @@ public class FullDataPartialUpdateMessage extends NetworkMessage implements ILev } } - - @Override - public boolean warnWhenUnhandled() { return false; } - @Override public void encode(ByteBuf out) { this.writeString(this.levelName, out); - this.dataSourceDto.encode(out); + this.dataSourceDtoSupplier.get().encode(out); } @Override @@ -81,6 +98,7 @@ public class FullDataPartialUpdateMessage extends NetworkMessage implements ILev { return super.toStringHelper() .add("levelName", this.levelName) + .add("fullDataSource", this.fullDataSource) .add("dataSourceDto", this.dataSourceDto); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java index 9d8b30908..04715d91f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java @@ -20,6 +20,7 @@ package com.seibel.distanthorizons.core.network.messages.fullData; import com.google.common.base.MoreObjects; +import com.google.common.base.Suppliers; import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; @@ -30,6 +31,7 @@ import io.netty.buffer.ByteBuf; import org.jetbrains.annotations.Nullable; import java.io.IOException; +import java.util.function.Supplier; /** * Response message, containing the requested full data source, @@ -37,19 +39,28 @@ import java.io.IOException; */ public class FullDataSourceResponseMessage extends TrackableMessage { + // Encode only + @Nullable + private final FullDataSourceV2 fullDataSource; + private final Supplier dataSourceDtoSupplier = Suppliers.memoize(this::createDataSourceDto); + + // Decode only @Nullable public FullDataSourceV2DTO dataSourceDto; - public FullDataSourceResponseMessage() { } + public FullDataSourceResponseMessage() { this(null); } public FullDataSourceResponseMessage(@Nullable FullDataSourceV2 fullDataSource) + { + this.fullDataSource = fullDataSource; + } + + private FullDataSourceV2DTO createDataSourceDto() { try { - if (fullDataSource != null) - { - EDhApiDataCompressionMode compressionMode = Config.Client.Advanced.LodBuilding.dataCompression.get(); - this.dataSourceDto = FullDataSourceV2DTO.CreateFromDataSource(fullDataSource, compressionMode); - } + assert this.fullDataSource != null; + EDhApiDataCompressionMode compressionMode = Config.Client.Advanced.LodBuilding.dataCompression.get(); + return FullDataSourceV2DTO.CreateFromDataSource(this.fullDataSource, compressionMode); } catch (IOException e) { @@ -60,9 +71,9 @@ public class FullDataSourceResponseMessage extends TrackableMessage @Override public void encode0(ByteBuf out) { - if (this.writeOptional(out, this.dataSourceDto)) + if (this.writeOptional(out, this.fullDataSource)) { - this.dataSourceDto.encode(out); + this.dataSourceDtoSupplier.get().encode(out); } } @@ -77,6 +88,7 @@ public class FullDataSourceResponseMessage extends TrackableMessage public MoreObjects.ToStringHelper toStringHelper() { return super.toStringHelper() + .add("fullDataSource", this.fullDataSource) .add("dataSourceDto", this.dataSourceDto); } From 171e6b9bcde0277635575230831acb3f9d3867f0 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 7 Jul 2024 01:49:44 +0500 Subject: [PATCH 156/205] Reset state of level detection on world exit --- .../distanthorizons/core/api/internal/ClientApi.java | 9 ++++++++- .../core/api/internal/ClientPluginChannelApi.java | 12 ++++++++++-- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index 43723a102..ad3f5a3f7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -25,6 +25,7 @@ import com.seibel.distanthorizons.api.methods.events.abstractEvents.*; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.network.messages.NetworkMessage; +import com.seibel.distanthorizons.core.network.session.Session; import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering; import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode; @@ -157,6 +158,8 @@ public class ClientApi SharedApi.setDhWorld(null); } + this.pluginChannelApi.reset(); + // remove any waiting items this.waitingChunkByClientLevelAndPos.clear(); this.waitingClientLevels.clear(); @@ -332,7 +335,11 @@ public class ClientApi public void pluginMessageReceived(@NotNull NetworkMessage message) { - this.pluginChannelApi.session.tryHandleMessage(message); + Session session = this.pluginChannelApi.session; + if (session != null) + { + session.tryHandleMessage(message); + } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index ae9847328..688d2f809 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -12,6 +12,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftCli import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import org.apache.logging.log4j.LogManager; import org.checkerframework.checker.nullness.qual.NonNull; +import org.jetbrains.annotations.Nullable; import java.util.Objects; import java.util.function.Consumer; @@ -29,6 +30,7 @@ public class ClientPluginChannelApi private final Consumer levelUnloadHandler; private final Consumer multiverseLevelLoadHandler; + @Nullable public Session session; @@ -49,8 +51,8 @@ public class ClientPluginChannelApi { Objects.requireNonNull(session); this.session = session; - this.session.registerHandler(CurrentLevelKeyMessage.class, this::onCurrentLevelKeyMessage); - this.session.registerHandler(CloseEvent.class, this::onClose); + session.registerHandler(CurrentLevelKeyMessage.class, this::onCurrentLevelKeyMessage); + session.registerHandler(CloseEvent.class, this::onClose); } private void onCurrentLevelKeyMessage(CurrentLevelKeyMessage msg) @@ -100,6 +102,12 @@ public class ClientPluginChannelApi private void onClose(CloseEvent event) { + this.reset(); + } + + public void reset() + { + this.session = null; KEYED_CLIENT_LEVEL_MANAGER.disable(); } From 48a8cdc365f866d93e4878a5ac8cb92e9a1b201c Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 7 Jul 2024 19:46:47 +0500 Subject: [PATCH 157/205] Disable parent update propagation --- .../FullDataSourceProviderV2.java | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java index c5e7d68c5..b398622a4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java @@ -21,6 +21,7 @@ package com.seibel.distanthorizons.core.file.fullDatafile; import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; 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.dataObjects.fullData.sources.FullDataSourceV1; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; @@ -37,6 +38,7 @@ import com.seibel.distanthorizons.core.sql.repo.FullDataSourceV2Repo; import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.util.objects.DataCorruptedException; import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; +import com.seibel.distanthorizons.core.world.EWorldEnvironment; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import it.unimi.dsi.fastutil.longs.LongArrayList; import org.apache.logging.log4j.Logger; @@ -108,6 +110,7 @@ public class FullDataSourceProviderV2 * This isn't in {@link AbstractDataSourceHandler} since we don't need parent updating logic * for render data, only full data. */ + @Nullable private final ThreadPoolExecutor updateQueueProcessor; @@ -128,10 +131,17 @@ public class FullDataSourceProviderV2 // start migrating any legacy data sources present in the background this.migrationThreadPool = ThreadUtil.makeRateLimitedThreadPool(1, MIGRATION_THREAD_NAME_PREFIX + "[" + dimensionName + "]", Config.Client.Advanced.MultiThreading.runTimeRatioForUpdatePropagatorThreads.get(), Thread.MIN_PRIORITY, (Semaphore) null); - this.migrationThreadPool.execute(() -> this.convertLegacyDataSources()); + this.migrationThreadPool.execute(this::convertLegacyDataSources); - this.updateQueueProcessor = ThreadUtil.makeSingleThreadPool("Parent Update Queue [" + dimensionName + "]"); - this.updateQueueProcessor.execute(() -> this.runUpdateQueue()); + if (SharedApi.getEnvironment() != EWorldEnvironment.Server_Only) + { + this.updateQueueProcessor = ThreadUtil.makeSingleThreadPool("Parent Update Queue [" + dimensionName + "]"); + this.updateQueueProcessor.execute(this::runUpdateQueue); + } + else + { + this.updateQueueProcessor = null; + } } @@ -678,7 +688,10 @@ public class FullDataSourceProviderV2 public void close() { super.close(); - this.updateQueueProcessor.shutdownNow(); + if (this.updateQueueProcessor != null) + { + this.updateQueueProcessor.shutdownNow(); + } this.legacyFileHandler.close(); From 84e90a7a9b741b313fff5d9b19641d1d9ddddd53 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 8 Jul 2024 23:07:58 +0500 Subject: [PATCH 158/205] Use dedicated thread pool for data compression --- .../distanthorizons/core/config/Config.java | 18 ++++ .../ThreadPresetConfigEventHandler.java | 25 ++++++ .../core/level/DhServerLevel.java | 82 ++++++++++++------- .../FullDataPartialUpdateMessage.java | 36 ++------ .../FullDataSourceResponseMessage.java | 28 ++----- .../core/util/threading/ThreadPoolUtil.java | 6 ++ 6 files changed, 120 insertions(+), 75 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 2f72f424d..9233e59af 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -1093,6 +1093,24 @@ public class Config + "") .build(); + public static final ConfigEntry numberOfNetworkCompressionThreads = new ConfigEntry.Builder() + .setServersideShortName("numberOfNetworkCompressionThreads") + .setMinDefaultMax(1, + ThreadPresetConfigEventHandler.getNetworkCompressionDefaultThreadCount(), + Runtime.getRuntime().availableProcessors()) + .comment("" + + "How many threads should be used when building LODs? \n" + + "\n" + + "These threads run when terrain is generated, when\n" + + "certain graphics settings are changed, and when moving around the world. \n" + + "\n" + + THREAD_NOTE) + .build(); + public static final ConfigEntry runTimeRatioForNetworkCompressionThreads = new ConfigEntry.Builder() + .setServersideShortName("runTimeRatioForNetworkCompressionThreads") + .setMinDefaultMax(0.01, ThreadPresetConfigEventHandler.getNetworkCompressionDefaultRunTimeRatio(), 1.0) + .comment(THREAD_RUN_TIME_RATIO_NOTE) + .build(); } public static class GpuBuffers diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/ThreadPresetConfigEventHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/ThreadPresetConfigEventHandler.java index 54d27d419..bcdbb45c9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/ThreadPresetConfigEventHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/ThreadPresetConfigEventHandler.java @@ -128,6 +128,28 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan }}); + public static int getNetworkCompressionDefaultThreadCount() { return getThreadCountByPercent(0.1); } + private final ConfigEntryWithPresetOptions networkCompressionThreadCount = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.numberOfNetworkCompressionThreads, + new HashMap() + {{ + this.put(EDhApiThreadPreset.MINIMAL_IMPACT, 1); + this.put(EDhApiThreadPreset.LOW_IMPACT, getNetworkCompressionDefaultThreadCount()); + this.put(EDhApiThreadPreset.BALANCED, getThreadCountByPercent(0.2)); + this.put(EDhApiThreadPreset.AGGRESSIVE, getThreadCountByPercent(0.4)); + this.put(EDhApiThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, getThreadCountByPercent(0.6)); + }}); + public static double getNetworkCompressionDefaultRunTimeRatio() { return 0.25; } + private final ConfigEntryWithPresetOptions networkCompressionRunTime = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.runTimeRatioForNetworkCompressionThreads, + new HashMap() + {{ + this.put(EDhApiThreadPreset.MINIMAL_IMPACT, 0.1); + this.put(EDhApiThreadPreset.LOW_IMPACT, getNetworkCompressionDefaultRunTimeRatio()); + this.put(EDhApiThreadPreset.BALANCED, 0.5); + this.put(EDhApiThreadPreset.AGGRESSIVE, 0.75); + this.put(EDhApiThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, 1.0); + }}); + + //==============// // constructors // @@ -149,6 +171,9 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan this.configList.add(this.lodBuilderThreadCount); this.configList.add(this.lodBuilderRunTime); + this.configList.add(this.networkCompressionThreadCount); + this.configList.add(this.networkCompressionRunTime); + for (ConfigEntryWithPresetOptions config : this.configList) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 30f19b99f..91b1402ef 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -38,6 +38,7 @@ import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; @@ -145,11 +146,17 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel return; } - this.serverside.fullDataFileHandler.getAsync(msg.sectionPos).thenAccept(fullDataSource -> + ThreadPoolExecutor executor = ThreadPoolUtil.getNetworkCompressionExecutor(); + if (executor == null) + { + LOGGER.warn("Unable to send FullDataSourceResponseMessage - getNetworkCompressionExecutor() is null"); + return; + } + this.serverside.fullDataFileHandler.getAsync(msg.sectionPos).thenAcceptAsync(fullDataSource -> { rateLimiterSet.loginDataSyncRCLimiter.release(); msg.sendResponse(new FullDataSourceResponseMessage(fullDataSource)); - }); + }, executor); } })); @@ -241,20 +248,29 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel // This semaphore is intentionally acquired forever entry.requestCollectionSemaphore.acquireUninterruptibly(Short.MAX_VALUE); - FullDataSourceResponseMessage response = new FullDataSourceResponseMessage(entry.fullDataSource); - for (FullDataSourceRequestMessage msg : entry.requestMessages.values()) + ThreadPoolExecutor executor = ThreadPoolUtil.getNetworkCompressionExecutor(); + if (executor == null) { - this.fullDataRequests.remove(msg.futureId); - - ServerPlayerState serverPlayerState = this.remotePlayerConnectionHandler.getConnectedPlayer(msg.serverPlayer()); - if (serverPlayerState == null) - { - continue; - } - - serverPlayerState.getRateLimiterSet(this).fullDataRequestConcurrencyLimiter.release(); - msg.sendResponse(response); + LOGGER.warn("Unable to send FullDataSourceResponseMessage - getNetworkCompressionExecutor() is null"); + continue; } + CompletableFuture.runAsync(() -> + { + FullDataSourceResponseMessage response = new FullDataSourceResponseMessage(entry.fullDataSource); + for (FullDataSourceRequestMessage msg : entry.requestMessages.values()) + { + this.fullDataRequests.remove(msg.futureId); + + ServerPlayerState serverPlayerState = this.remotePlayerConnectionHandler.getConnectedPlayer(msg.serverPlayer()); + if (serverPlayerState == null) + { + continue; + } + + serverPlayerState.getRateLimiterSet(this).fullDataRequestConcurrencyLimiter.release(); + msg.sendResponse(response); + } + }, executor); } } @@ -266,22 +282,32 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel return this.getFullDataProvider().updateDataSourceAsync(data); } - FullDataPartialUpdateMessage updateMessage = new FullDataPartialUpdateMessage(this.serverLevelWrapper, data); - for (ServerPlayerState serverPlayerState : this.remotePlayerConnectionHandler.getConnectedPlayers()) + ThreadPoolExecutor executor = ThreadPoolUtil.getNetworkCompressionExecutor(); + if (executor == null) { - if (!serverPlayerState.config.isRealTimeUpdatesEnabled()) - { - continue; - } - - Vec3d playerPosition = serverPlayerState.serverPlayer().getPosition(); - int distanceFromPlayer = DhSectionPos.getManhattanBlockDistance(data.getPos(), new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)) / 16; - if (distanceFromPlayer >= serverPlayerState.serverPlayer().getViewDistance() && - distanceFromPlayer <= serverPlayerState.config.getRenderDistanceRadius()) - { - serverPlayerState.session.sendMessage(updateMessage); - } + LOGGER.warn("Unable to send FullDataPartialUpdateMessage - getNetworkCompressionExecutor() is null"); + return this.getFullDataProvider().updateDataSourceAsync(data); } + CompletableFuture.runAsync(() -> + { + FullDataPartialUpdateMessage updateMessage = new FullDataPartialUpdateMessage(this.serverLevelWrapper, data); + for (ServerPlayerState serverPlayerState : this.remotePlayerConnectionHandler.getConnectedPlayers()) + { + if (!serverPlayerState.config.isRealTimeUpdatesEnabled()) + { + continue; + } + + Vec3d playerPosition = serverPlayerState.serverPlayer().getPosition(); + int distanceFromPlayer = DhSectionPos.getManhattanBlockDistance(data.getPos(), new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)) / 16; + if (distanceFromPlayer >= serverPlayerState.serverPlayer().getViewDistance() && + distanceFromPlayer <= serverPlayerState.config.getRenderDistanceRadius()) + { + serverPlayerState.session.sendMessage(updateMessage); + } + } + }, executor); + return this.getFullDataProvider().updateDataSourceAsync(data); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java index bc419b869..05b0a4a39 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java @@ -20,7 +20,6 @@ package com.seibel.distanthorizons.core.network.messages.fullData; import com.google.common.base.MoreObjects; -import com.google.common.base.Suppliers; import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; @@ -30,11 +29,8 @@ import com.seibel.distanthorizons.core.network.INetworkObject; import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import io.netty.buffer.ByteBuf; -import org.checkerframework.checker.nullness.qual.NonNull; -import org.jetbrains.annotations.Nullable; import java.io.IOException; -import java.util.function.Supplier; public class FullDataPartialUpdateMessage extends NetworkMessage implements ILevelRelatedMessage { @@ -42,35 +38,18 @@ public class FullDataPartialUpdateMessage extends NetworkMessage implements ILev @Override public String getLevelName() { return this.levelName; } - // Encode only - @Nullable - private final FullDataSourceV2 fullDataSource; - private final Supplier dataSourceDtoSupplier = Suppliers.memoize(this::createDataSourceDto); - - // Decode only - @Nullable public FullDataSourceV2DTO dataSourceDto; - public FullDataPartialUpdateMessage() { this.fullDataSource = null; } - public FullDataPartialUpdateMessage(ILevelWrapper level, @NonNull FullDataSourceV2 fullDataSource) + public FullDataPartialUpdateMessage() { } + public FullDataPartialUpdateMessage(ILevelWrapper level, FullDataSourceV2 fullDataSource) { this.levelName = level.getDimensionName(); - this.fullDataSource = fullDataSource; - } - - - @Override - public boolean warnWhenUnhandled() { return false; } - - - private FullDataSourceV2DTO createDataSourceDto() - { + try { - assert this.fullDataSource != null; EDhApiDataCompressionMode compressionMode = Config.Client.Advanced.LodBuilding.dataCompression.get(); - return FullDataSourceV2DTO.CreateFromDataSource(this.fullDataSource, compressionMode); + this.dataSourceDto = FullDataSourceV2DTO.CreateFromDataSource(fullDataSource, compressionMode); } catch (IOException e) { @@ -78,11 +57,15 @@ public class FullDataPartialUpdateMessage extends NetworkMessage implements ILev } } + + @Override + public boolean warnWhenUnhandled() { return false; } + @Override public void encode(ByteBuf out) { this.writeString(this.levelName, out); - this.dataSourceDtoSupplier.get().encode(out); + this.dataSourceDto.encode(out); } @Override @@ -98,7 +81,6 @@ public class FullDataPartialUpdateMessage extends NetworkMessage implements ILev { return super.toStringHelper() .add("levelName", this.levelName) - .add("fullDataSource", this.fullDataSource) .add("dataSourceDto", this.dataSourceDto); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java index 04715d91f..9d8b30908 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java @@ -20,7 +20,6 @@ package com.seibel.distanthorizons.core.network.messages.fullData; import com.google.common.base.MoreObjects; -import com.google.common.base.Suppliers; import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; @@ -31,7 +30,6 @@ import io.netty.buffer.ByteBuf; import org.jetbrains.annotations.Nullable; import java.io.IOException; -import java.util.function.Supplier; /** * Response message, containing the requested full data source, @@ -39,28 +37,19 @@ import java.util.function.Supplier; */ public class FullDataSourceResponseMessage extends TrackableMessage { - // Encode only - @Nullable - private final FullDataSourceV2 fullDataSource; - private final Supplier dataSourceDtoSupplier = Suppliers.memoize(this::createDataSourceDto); - - // Decode only @Nullable public FullDataSourceV2DTO dataSourceDto; - public FullDataSourceResponseMessage() { this(null); } + public FullDataSourceResponseMessage() { } public FullDataSourceResponseMessage(@Nullable FullDataSourceV2 fullDataSource) - { - this.fullDataSource = fullDataSource; - } - - private FullDataSourceV2DTO createDataSourceDto() { try { - assert this.fullDataSource != null; - EDhApiDataCompressionMode compressionMode = Config.Client.Advanced.LodBuilding.dataCompression.get(); - return FullDataSourceV2DTO.CreateFromDataSource(this.fullDataSource, compressionMode); + if (fullDataSource != null) + { + EDhApiDataCompressionMode compressionMode = Config.Client.Advanced.LodBuilding.dataCompression.get(); + this.dataSourceDto = FullDataSourceV2DTO.CreateFromDataSource(fullDataSource, compressionMode); + } } catch (IOException e) { @@ -71,9 +60,9 @@ public class FullDataSourceResponseMessage extends TrackableMessage @Override public void encode0(ByteBuf out) { - if (this.writeOptional(out, this.fullDataSource)) + if (this.writeOptional(out, this.dataSourceDto)) { - this.dataSourceDtoSupplier.get().encode(out); + this.dataSourceDto.encode(out); } } @@ -88,7 +77,6 @@ public class FullDataSourceResponseMessage extends TrackableMessage public MoreObjects.ToStringHelper toStringHelper() { return super.toStringHelper() - .add("fullDataSource", this.fullDataSource) .add("dataSourceDto", this.dataSourceDto); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPoolUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPoolUtil.java index e4f786a84..63b200dfc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPoolUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPoolUtil.java @@ -66,6 +66,11 @@ public class ThreadPoolUtil @Nullable public static ThreadPoolExecutor getCleanupExecutor() { return cleanupThreadPool; } + public static final DhThreadFactory NETWORK_COMPRESSION_THREAD_FACTORY = new DhThreadFactory("Network Compression", Thread.MIN_PRIORITY); + private static ConfigThreadPool networkCompressionThreadPool; + @Nullable + public static ThreadPoolExecutor getNetworkCompressionExecutor() { return networkCompressionThreadPool.executor; } + //======================// @@ -112,6 +117,7 @@ public class ThreadPoolUtil fileHandlerThreadPool = new ConfigThreadPool(FILE_HANDLER_THREAD_FACTORY, Config.Client.Advanced.MultiThreading.numberOfFileHandlerThreads, Config.Client.Advanced.MultiThreading.runTimeRatioForFileHandlerThreads, null); updatePropagatorThreadPool = new ConfigThreadPool(UPDATE_PROPAGATOR_THREAD_FACTORY, Config.Client.Advanced.MultiThreading.numberOfUpdatePropagatorThreads, Config.Client.Advanced.MultiThreading.runTimeRatioForUpdatePropagatorThreads, null); worldGenThreadPool = new ConfigThreadPool(WORLD_GEN_THREAD_FACTORY, Config.Client.Advanced.MultiThreading.numberOfWorldGenerationThreads, Config.Client.Advanced.MultiThreading.runTimeRatioForWorldGenerationThreads, null); + networkCompressionThreadPool = new ConfigThreadPool(NETWORK_COMPRESSION_THREAD_FACTORY, Config.Client.Advanced.MultiThreading.numberOfNetworkCompressionThreads, Config.Client.Advanced.MultiThreading.runTimeRatioForNetworkCompressionThreads, null); bufferUploaderThreadPool = ThreadUtil.makeSingleThreadPool(BUFFER_UPLOADER_THREAD_NAME); cleanupThreadPool = ThreadUtil.makeSingleThreadPool(CLEANUP_THREAD_NAME); From 9e0edd0cf38884a5ba9704176c72cd4c772d2509 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 9 Jul 2024 14:47:45 +0500 Subject: [PATCH 159/205] Prevent request cancellation deadlock --- .../core/level/DhServerLevel.java | 85 +++++++++++-------- 1 file changed, 48 insertions(+), 37 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 91b1402ef..42328cd88 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -66,8 +66,8 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel private final RemotePlayerConnectionHandler remotePlayerConnectionHandler; private final ConcurrentLinkedQueue worldGenLoopingQueue = new ConcurrentLinkedQueue<>(); - private final ConcurrentMap incompleteDataSources = new ConcurrentHashMap<>(); - private final ConcurrentMap fullDataRequests = new ConcurrentHashMap<>(); + private final ConcurrentMap requestGroupsByPos = new ConcurrentHashMap<>(); + private final ConcurrentMap requestGroupsByFutureId = new ConcurrentHashMap<>(); public DhServerLevel(AbstractSaveStructure saveStructure, IServerLevelWrapper serverLevelWrapper, RemotePlayerConnectionHandler remotePlayerConnectionHandler) { @@ -107,18 +107,18 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel while (true) { - IncompleteDataSourceEntry entry = this.incompleteDataSources.computeIfAbsent(msg.sectionPos, pos -> + DataSourceRequestGroup requestGroup = this.requestGroupsByPos.computeIfAbsent(msg.sectionPos, pos -> { - IncompleteDataSourceEntry newEntry = new IncompleteDataSourceEntry(); - this.trySetGeneratedDataSourceToEntry(newEntry, pos); - return newEntry; + DataSourceRequestGroup newGroup = new DataSourceRequestGroup(); + this.tryFulfillDataSourceRequestGroup(newGroup, pos); + return newGroup; }); - // If this fails, current entry is being drained and need to create another one - if (entry.requestCollectionSemaphore.tryAcquire()) + // If this fails, current group is being drained and need to create another one + if (requestGroup.requestAddSemaphore.tryAcquire()) { - this.fullDataRequests.put(msg.futureId, entry); - entry.requestMessages.put(msg.futureId, msg); - entry.requestCollectionSemaphore.release(); + this.requestGroupsByFutureId.put(msg.futureId, requestGroup); + requestGroup.requestMessages.put(msg.futureId, msg); + requestGroup.requestAddSemaphore.release(); break; } } @@ -162,23 +162,30 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel serverPlayerState.session.registerHandler(CancelMessage.class, msg -> { - IncompleteDataSourceEntry entry = this.fullDataRequests.remove(msg.futureId); - if (entry == null) + DataSourceRequestGroup requestGroup = this.requestGroupsByFutureId.remove(msg.futureId); + if (requestGroup == null) { return; } - FullDataSourceRequestMessage requestMessage = entry.requestMessages.remove(msg.futureId); + FullDataSourceRequestMessage requestMessage = requestGroup.requestMessages.remove(msg.futureId); serverPlayerState.getRateLimiterSet(this).fullDataRequestConcurrencyLimiter.release(); - entry.requestCollectionSemaphore.acquireUninterruptibly(Short.MAX_VALUE); - if (entry.requestMessages.isEmpty()) + if (requestGroup.requestRemoveSemaphore.tryAcquire()) { - this.incompleteDataSources.remove(requestMessage.sectionPos); - this.serverside.fullDataFileHandler.removeRetrievalRequestIf(pos -> pos == requestMessage.sectionPos); + // Prevent adding requests in case request will be removed by cancellation + requestGroup.requestAddSemaphore.acquireUninterruptibly(Short.MAX_VALUE); + + if (requestGroup.requestMessages.isEmpty()) + { + this.requestGroupsByPos.remove(requestMessage.sectionPos); + this.serverside.fullDataFileHandler.removeRetrievalRequestIf(pos -> pos == requestMessage.sectionPos); + } + else + { + requestGroup.requestAddSemaphore.release(Short.MAX_VALUE); + } } - - entry.requestCollectionSemaphore.release(Short.MAX_VALUE); }); } @@ -234,19 +241,20 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel this.chunkToLodBuilder.tick(); // Send finished data source requests - for (Map.Entry mapEntry : this.incompleteDataSources.entrySet()) + for (Map.Entry entry : this.requestGroupsByPos.entrySet()) { - IncompleteDataSourceEntry entry = mapEntry.getValue(); + DataSourceRequestGroup requestGroup = entry.getValue(); - if (entry.fullDataSource == null) + if (requestGroup.fullDataSource == null) { continue; } - this.incompleteDataSources.remove(mapEntry.getKey()); + // Prevent adding or removing requests + requestGroup.requestRemoveSemaphore.acquireUninterruptibly(Short.MAX_VALUE); + requestGroup.requestAddSemaphore.acquireUninterruptibly(Short.MAX_VALUE); - // This semaphore is intentionally acquired forever - entry.requestCollectionSemaphore.acquireUninterruptibly(Short.MAX_VALUE); + this.requestGroupsByPos.remove(entry.getKey()); ThreadPoolExecutor executor = ThreadPoolUtil.getNetworkCompressionExecutor(); if (executor == null) @@ -256,10 +264,10 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel } CompletableFuture.runAsync(() -> { - FullDataSourceResponseMessage response = new FullDataSourceResponseMessage(entry.fullDataSource); - for (FullDataSourceRequestMessage msg : entry.requestMessages.values()) + FullDataSourceResponseMessage response = new FullDataSourceResponseMessage(requestGroup.fullDataSource); + for (FullDataSourceRequestMessage msg : requestGroup.requestMessages.values()) { - this.fullDataRequests.remove(msg.futureId); + this.requestGroupsByFutureId.remove(msg.futureId); ServerPlayerState serverPlayerState = this.remotePlayerConnectionHandler.getConnectedPlayer(msg.serverPlayer()); if (serverPlayerState == null) @@ -387,12 +395,12 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel @Override public boolean hasSkyLight() { return this.serverLevelWrapper.hasSkyLight(); } - private void trySetGeneratedDataSourceToEntry(IncompleteDataSourceEntry entry, long pos) + private void tryFulfillDataSourceRequestGroup(DataSourceRequestGroup requestGroup, long pos) { this.serverside.fullDataFileHandler.getAsync(pos).thenAccept(fullDataSource -> { if (this.serverside.fullDataFileHandler.isFullyGenerated(fullDataSource.columnGenerationSteps)) { - entry.fullDataSource = fullDataSource; + requestGroup.fullDataSource = fullDataSource; } else { @@ -404,10 +412,10 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel @Override public void onWorldGenTaskComplete(long pos) { - IncompleteDataSourceEntry entry = this.incompleteDataSources.get(pos); - if (entry != null) + DataSourceRequestGroup requestGroup = this.requestGroupsByPos.get(pos); + if (requestGroup != null) { - this.trySetGeneratedDataSourceToEntry(entry, pos); + this.tryFulfillDataSourceRequestGroup(requestGroup, pos); } } @@ -424,12 +432,15 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel messageList.add("["+dimName+"]"); } - private static class IncompleteDataSourceEntry + private static class DataSourceRequestGroup { + public final ConcurrentMap requestMessages = new ConcurrentHashMap<>(); + @CheckForNull public FullDataSourceV2 fullDataSource; - public final ConcurrentMap requestMessages = new ConcurrentHashMap<>(); - public final Semaphore requestCollectionSemaphore = new Semaphore(Short.MAX_VALUE, true); + + public final Semaphore requestAddSemaphore = new Semaphore(Short.MAX_VALUE, true); + public final Semaphore requestRemoveSemaphore = new Semaphore(Short.MAX_VALUE, true); } } \ No newline at end of file From 85341f7a6d91dbff07a80e2442225b238e8a4739 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 9 Jul 2024 18:06:16 +0500 Subject: [PATCH 160/205] Add missing semaphore release --- .../com/seibel/distanthorizons/core/level/DhServerLevel.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 42328cd88..2b3573638 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -171,10 +171,12 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel serverPlayerState.getRateLimiterSet(this).fullDataRequestConcurrencyLimiter.release(); + // If this fails, group is being removed and completing cancellation is not necessary if (requestGroup.requestRemoveSemaphore.tryAcquire()) { - // Prevent adding requests in case request will be removed by cancellation + // Prevent adding requests in case request will be removed by this cancellation requestGroup.requestAddSemaphore.acquireUninterruptibly(Short.MAX_VALUE); + requestGroup.requestRemoveSemaphore.release(); if (requestGroup.requestMessages.isEmpty()) { From af6967854529953ae3c97c6d6077657806cebe35 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Wed, 10 Jul 2024 23:51:39 +0500 Subject: [PATCH 161/205] Fix player tracking on server --- .../core/api/internal/ClientApi.java | 1 + .../core/level/DhServerLevel.java | 27 +++++++++++-------- .../network/messages/TrackableMessage.java | 10 ++++++- .../core/network/session/Session.java | 4 +++ 4 files changed, 30 insertions(+), 12 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index ad3f5a3f7..2e438978a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -338,6 +338,7 @@ public class ClientApi Session session = this.pluginChannelApi.session; if (session != null) { + message.setSession(session); session.tryHandleMessage(message); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 2b3573638..17420178c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -113,14 +113,18 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel this.tryFulfillDataSourceRequestGroup(newGroup, pos); return newGroup; }); - // If this fails, current group is being drained and need to create another one - if (requestGroup.requestAddSemaphore.tryAcquire()) + + // If this fails, loop until either permit is acquired or group is removed to create another one + if (!requestGroup.requestAddSemaphore.tryAcquire()) { - this.requestGroupsByFutureId.put(msg.futureId, requestGroup); - requestGroup.requestMessages.put(msg.futureId, msg); - requestGroup.requestAddSemaphore.release(); - break; + Thread.yield(); + continue; } + + this.requestGroupsByFutureId.put(msg.futureId, requestGroup); + requestGroup.requestMessages.put(msg.futureId, msg); + requestGroup.requestAddSemaphore.release(); + break; } } else @@ -167,17 +171,17 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel { return; } - FullDataSourceRequestMessage requestMessage = requestGroup.requestMessages.remove(msg.futureId); - - serverPlayerState.getRateLimiterSet(this).fullDataRequestConcurrencyLimiter.release(); // If this fails, group is being removed and completing cancellation is not necessary if (requestGroup.requestRemoveSemaphore.tryAcquire()) { - // Prevent adding requests in case request will be removed by this cancellation + // Prevent adding requests in case the group will be removed by this cancellation requestGroup.requestAddSemaphore.acquireUninterruptibly(Short.MAX_VALUE); requestGroup.requestRemoveSemaphore.release(); - + + serverPlayerState.getRateLimiterSet(this).fullDataRequestConcurrencyLimiter.release(); + + FullDataSourceRequestMessage requestMessage = requestGroup.requestMessages.remove(msg.futureId); if (requestGroup.requestMessages.isEmpty()) { this.requestGroupsByPos.remove(requestMessage.sectionPos); @@ -441,6 +445,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel @CheckForNull public FullDataSourceV2 fullDataSource; + // Maybe there's a better way to do synchronization, but this should suffice public final Semaphore requestAddSemaphore = new Semaphore(Short.MAX_VALUE, true); public final Semaphore requestRemoveSemaphore = new Semaphore(Short.MAX_VALUE, true); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/TrackableMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/TrackableMessage.java index ca9227c3a..dc0d76bf2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/TrackableMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/TrackableMessage.java @@ -22,6 +22,7 @@ package com.seibel.distanthorizons.core.network.messages; import com.google.common.base.MoreObjects; import com.seibel.distanthorizons.core.api.internal.SharedApi; import com.seibel.distanthorizons.core.network.messages.requests.ExceptionMessage; +import com.seibel.distanthorizons.core.network.session.Session; import com.seibel.distanthorizons.core.world.EWorldEnvironment; import io.netty.buffer.ByteBuf; @@ -31,12 +32,19 @@ import java.util.concurrent.atomic.AtomicInteger; public abstract class TrackableMessage extends NetworkMessage { private static final AtomicInteger lastId = new AtomicInteger(); - // 32 bits - Context ID (not transmitted) + + // 32 bits - Session ID (not transmitted) // 1 bit - Requesting side (client - 0, server - 1) // 31 bits - Request ID public long futureId = lastId.getAndIncrement() | ((Objects.requireNonNull(SharedApi.getEnvironment()) == EWorldEnvironment.Server_Only ? 1 : 0) << 31); + public void setSession(Session session) + { + super.setSession(session); + this.futureId |= (long) session.id << 32; + } + public void sendResponse(TrackableMessage responseMessage) { responseMessage.futureId = this.futureId; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java b/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java index 954748324..064ab5620 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java @@ -13,6 +13,7 @@ import org.apache.logging.log4j.LogManager; import org.jetbrains.annotations.Nullable; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; public class Session extends NetworkEventSource @@ -22,6 +23,9 @@ public class Session extends NetworkEventSource private static final IPluginPacketSender PACKET_SENDER = SingletonInjector.INSTANCE.get(IPluginPacketSender.class); + private static final AtomicInteger lastId = new AtomicInteger(); + public final int id = lastId.getAndIncrement(); + /** * When non-null, any received data will be ignored.
* This does not include wrong versions, which are ignored without setting this flag, From 43b421042d1fd03790e56a0d0eda8ef2c01aee86 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Thu, 11 Jul 2024 22:08:10 +0500 Subject: [PATCH 162/205] Add a comment to semaphores --- .../com/seibel/distanthorizons/core/level/DhServerLevel.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 17420178c..155add792 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -446,6 +446,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel public FullDataSourceV2 fullDataSource; // Maybe there's a better way to do synchronization, but this should suffice + // Why not something like ReentrantReadWriteLock: locks should not be bound to threads public final Semaphore requestAddSemaphore = new Semaphore(Short.MAX_VALUE, true); public final Semaphore requestRemoveSemaphore = new Semaphore(Short.MAX_VALUE, true); } From 2617cd294df1faa6b7eae776ef9ad19f6cc4b35a Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Thu, 11 Jul 2024 23:06:23 +0500 Subject: [PATCH 163/205] Add cache for ignoring responses for cancelled requests --- .../network/event/NetworkEventSource.java | 32 ++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java index d77ae366a..699f3a516 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.network.event; +import com.google.common.cache.CacheBuilder; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.network.messages.NetworkMessage; @@ -31,26 +32,29 @@ import com.seibel.distanthorizons.coreapi.ModInfo; import org.apache.logging.log4j.LogManager; import java.io.InvalidClassException; +import java.util.Collections; import java.util.HashSet; import java.util.Set; -import java.util.concurrent.CancellationException; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.*; import java.util.function.Consumer; public abstract class NetworkEventSource { private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); - protected final ConcurrentMap, Set>> handlers = new ConcurrentHashMap<>(); - private final ConcurrentMap pendingFutures = new ConcurrentHashMap<>(); + protected final ConcurrentMap, Set>> handlers = new ConcurrentHashMap<>(); protected boolean hasHandler(Class handlerClass) { return this.handlers.containsKey(handlerClass); } + private final ConcurrentMap pendingFutures = new ConcurrentHashMap<>(); + private final Set cancelledFutures = Collections.newSetFromMap(CacheBuilder.newBuilder() + .expireAfterWrite(10, TimeUnit.SECONDS) + .build() + .asMap()); + protected void handleMessage(NetworkMessage message) { @@ -69,6 +73,7 @@ public abstract class NetworkEventSource if (message instanceof TrackableMessage) { TrackableMessage trackableMessage = (TrackableMessage) message; + FutureResponseData responseData = this.pendingFutures.get(trackableMessage.futureId); if (responseData != null) { @@ -87,6 +92,10 @@ public abstract class NetworkEventSource responseData.future.complete(trackableMessage); } } + else if (this.cancelledFutures.remove(trackableMessage.futureId)) + { + handled = true; + } } if (!handled && ModInfo.IS_DEV_BUILD && message.warnWhenUnhandled()) @@ -122,15 +131,16 @@ public abstract class NetworkEventSource CompletableFuture responseFuture = new CompletableFuture<>(); responseFuture.whenComplete((response, throwable) -> { + if (throwable instanceof CancellationException) + { + this.cancelledFutures.add(msg.futureId); + msg.sendResponse(new CancelMessage()); + } + if (!(throwable instanceof SessionClosedException)) { this.pendingFutures.remove(msg.futureId); } - - if (throwable instanceof CancellationException) - { - msg.sendResponse(new CancelMessage()); - } }); this.pendingFutures.put(msg.futureId, new FutureResponseData(responseClass, responseFuture)); From 778c2f894e412c69929d5d0aff3df08f3de92bd3 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 12 Jul 2024 23:11:36 +0500 Subject: [PATCH 164/205] Fix level handling for real time updates --- .../core/level/DhClientLevel.java | 8 +++++- .../core/level/DhServerLevel.java | 26 +++++++++++++------ .../messages/ILevelRelatedMessage.java | 10 +++++-- .../FullDataPartialUpdateMessage.java | 6 ++--- 4 files changed, 36 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 7656bd83c..3bbe1c2c0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -133,7 +133,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel { try { - if (msg.dataSourceDto == null) + if (!msg.isSameLevelAs(this.levelWrapper)) { return; } @@ -288,6 +288,12 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel { this.worldGenModule.close(); } + + if (this.eventSource != null) + { + this.eventSource.close(); + } + this.clientside.close(); super.close(); this.dataFileHandler.close(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 155add792..f2c620089 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -61,14 +61,19 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel public final ServerLevelModule serverside; private final IServerLevelWrapper serverLevelWrapper; - - private final RemotePlayerConnectionHandler remotePlayerConnectionHandler; - private final ConcurrentLinkedQueue worldGenLoopingQueue = new ConcurrentLinkedQueue<>(); + /** + * This queue is used for ensuring fair generation speed for each player.
+ * Every tick the first player gets used for centering generation, and then is immediately moved into the back of the queue.
+ * TODO only add players that actually have something to generate + */ + private final ConcurrentLinkedQueue worldGenPlayerCenteringQueue = new ConcurrentLinkedQueue<>(); + private final ConcurrentMap requestGroupsByPos = new ConcurrentHashMap<>(); private final ConcurrentMap requestGroupsByFutureId = new ConcurrentHashMap<>(); + public DhServerLevel(AbstractSaveStructure saveStructure, IServerLevelWrapper serverLevelWrapper, RemotePlayerConnectionHandler remotePlayerConnectionHandler) { if (saveStructure.getFullDataFolder(serverLevelWrapper).mkdirs()) @@ -233,12 +238,12 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel public void addPlayer(IServerPlayerWrapper serverPlayer) { - this.worldGenLoopingQueue.add(serverPlayer); + this.worldGenPlayerCenteringQueue.add(serverPlayer); } public void removePlayer(IServerPlayerWrapper serverPlayer) { - this.worldGenLoopingQueue.remove(serverPlayer); + this.worldGenPlayerCenteringQueue.remove(serverPlayer); } @Override @@ -307,6 +312,11 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel FullDataPartialUpdateMessage updateMessage = new FullDataPartialUpdateMessage(this.serverLevelWrapper, data); for (ServerPlayerState serverPlayerState : this.remotePlayerConnectionHandler.getConnectedPlayers()) { + if (serverPlayerState.serverPlayer().getLevel() != this.serverLevelWrapper) + { + continue; + } + if (!serverPlayerState.config.isRealTimeUpdatesEnabled()) { continue; @@ -358,7 +368,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel if (this.serverside.worldGenModule.isWorldGenRunning()) { - IServerPlayerWrapper firstPlayer = this.worldGenLoopingQueue.peek(); + IServerPlayerWrapper firstPlayer = this.worldGenPlayerCenteringQueue.peek(); if (firstPlayer == null) { return; @@ -366,8 +376,8 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel // Put first player in back before removing from front, so it can be removed by other thread without blocking // - if it gets removed, remove() below will remove the item we just put instead - this.worldGenLoopingQueue.add(firstPlayer); - this.worldGenLoopingQueue.remove(firstPlayer); + this.worldGenPlayerCenteringQueue.add(firstPlayer); + this.worldGenPlayerCenteringQueue.remove(firstPlayer); Vec3d position = firstPlayer.getPosition(); this.serverside.worldGenModule.worldGenTick(new DhBlockPos2D((int) position.x, (int) position.z)); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ILevelRelatedMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ILevelRelatedMessage.java index c952d1ca1..6791e098c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ILevelRelatedMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ILevelRelatedMessage.java @@ -1,5 +1,6 @@ package com.seibel.distanthorizons.core.network.messages; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; public interface ILevelRelatedMessage @@ -10,9 +11,14 @@ public interface ILevelRelatedMessage * Checks whether the message's level matches the given level. */ @SuppressWarnings("BooleanMethodIsAlwaysInverted") - default boolean isSameLevelAs(IServerLevelWrapper levelWrapper) + default boolean isSameLevelAs(ILevelWrapper levelWrapper) { - return this.getLevelName().equals(levelWrapper.getKeyedLevelDimensionName()); + if (levelWrapper instanceof IServerLevelWrapper) + { + return this.getLevelName().equals(((IServerLevelWrapper) levelWrapper).getKeyedLevelDimensionName()); + } + + return this.getLevelName().equals(levelWrapper.getDimensionName()); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java index 05b0a4a39..1f06b3888 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java @@ -27,7 +27,7 @@ import com.seibel.distanthorizons.core.network.messages.ILevelRelatedMessage; import com.seibel.distanthorizons.core.network.messages.NetworkMessage; import com.seibel.distanthorizons.core.network.INetworkObject; import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; -import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import io.netty.buffer.ByteBuf; import java.io.IOException; @@ -42,9 +42,9 @@ public class FullDataPartialUpdateMessage extends NetworkMessage implements ILev public FullDataPartialUpdateMessage() { } - public FullDataPartialUpdateMessage(ILevelWrapper level, FullDataSourceV2 fullDataSource) + public FullDataPartialUpdateMessage(IServerLevelWrapper level, FullDataSourceV2 fullDataSource) { - this.levelName = level.getDimensionName(); + this.levelName = level.getKeyedLevelDimensionName(); try { From 16abc0faa1590c491c45c14c236a8fe5d13917da Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 15 Jul 2024 07:57:20 +0500 Subject: [PATCH 165/205] [skip ci] Initial chunked full data message payloads --- .../fullData/FullDataChunkMessage.java | 72 +++++++++++++++++++ .../FullDataPartialUpdateMessage.java | 8 ++- .../FullDataSourceResponseMessage.java | 4 +- .../fullData/IFullDataPayloadMessage.java | 36 ++++++++++ .../core/sql/dto/FullDataSourceV2DTO.java | 9 +++ 5 files changed, 125 insertions(+), 4 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataChunkMessage.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/IFullDataPayloadMessage.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataChunkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataChunkMessage.java new file mode 100644 index 000000000..55c4bc161 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataChunkMessage.java @@ -0,0 +1,72 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.network.messages.fullData; + +import com.google.common.base.MoreObjects; +import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; +import com.seibel.distanthorizons.core.network.INetworkObject; +import com.seibel.distanthorizons.core.network.messages.ILevelRelatedMessage; +import com.seibel.distanthorizons.core.network.messages.NetworkMessage; +import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; +import io.netty.buffer.ByteBuf; + +import java.io.IOException; + +public class FullDataChunkMessage extends NetworkMessage +{ + public ByteBuf buffer; + + + public FullDataChunkMessage() { } + public FullDataChunkMessage(ByteBuf buffer) + { + } + + + @Override + public boolean warnWhenUnhandled() { return false; } + + @Override + public void encode(ByteBuf out) + { + out.writeInt(this.buffer.writerIndex()); + this.buffer.resetReaderIndex(); + out.writeBytes(this.buffer); + } + + @Override + public void decode(ByteBuf in) + { + this.buffer = in.readBytes(in.readInt()); + } + + + @Override + public MoreObjects.ToStringHelper toStringHelper() + { + return super.toStringHelper() + .add("levelName", this.levelName) + .add("dataSourceDto", this.dataSourceDto); + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java index 1f06b3888..1ce11fae9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java @@ -31,14 +31,16 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapp import io.netty.buffer.ByteBuf; import java.io.IOException; +import java.util.Objects; -public class FullDataPartialUpdateMessage extends NetworkMessage implements ILevelRelatedMessage +public class FullDataPartialUpdateMessage extends NetworkMessage implements ILevelRelatedMessage, IFullDataPayloadMessage { private String levelName; @Override public String getLevelName() { return this.levelName; } public FullDataSourceV2DTO dataSourceDto; + @Override public FullDataSourceV2DTO getDataSourceDto() { return Objects.requireNonNull(this.dataSourceDto); } public FullDataPartialUpdateMessage() { } @@ -65,14 +67,14 @@ public class FullDataPartialUpdateMessage extends NetworkMessage implements ILev public void encode(ByteBuf out) { this.writeString(this.levelName, out); - this.dataSourceDto.encode(out); + // dataSourceDto must be sent separately } @Override public void decode(ByteBuf in) { this.levelName = this.readString(in); - this.dataSourceDto = INetworkObject.decodeToInstance(new FullDataSourceV2DTO(), in); + // dataSourceDto is received separately } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java index 9d8b30908..282422788 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java @@ -30,15 +30,17 @@ import io.netty.buffer.ByteBuf; import org.jetbrains.annotations.Nullable; import java.io.IOException; +import java.util.Objects; /** * Response message, containing the requested full data source, * or nothing if requested in updates-only mode and the data was not updated. */ -public class FullDataSourceResponseMessage extends TrackableMessage +public class FullDataSourceResponseMessage extends TrackableMessage implements IFullDataPayloadMessage { @Nullable public FullDataSourceV2DTO dataSourceDto; + @Override public FullDataSourceV2DTO getDataSourceDto() { return Objects.requireNonNull(this.dataSourceDto); } public FullDataSourceResponseMessage() { } public FullDataSourceResponseMessage(@Nullable FullDataSourceV2 fullDataSource) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/IFullDataPayloadMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/IFullDataPayloadMessage.java new file mode 100644 index 000000000..16d61b2d4 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/IFullDataPayloadMessage.java @@ -0,0 +1,36 @@ +package com.seibel.distanthorizons.core.network.messages.fullData; + +import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.CompositeByteBuf; + +import java.util.ArrayList; +import java.util.List; + +public interface IFullDataPayloadMessage +{ + FullDataSourceV2DTO getDataSourceDto(); + + default List getDataSourceDtoChunks(int chunkSize) + { + FullDataSourceV2DTO dto = this.getDataSourceDto(); + int chunkCount = dto.estimatedEncodedSize() / chunkSize; + + CompositeByteBuf composite = ByteBufAllocator.DEFAULT.compositeBuffer(); + + ArrayList result = new ArrayList<>(); + for (int i = 0; i < chunkCount; i++) + { + ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer(); + result.add(buffer); + composite.addComponent(buffer); + } + + dto.encode(composite); + + composite.release(); + return result; + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java index 0755b8fcf..0280d455e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java @@ -409,6 +409,15 @@ public class FullDataSourceV2DTO implements IBaseDTO, INetworkObject this.createdUnixDateTime = in.readLong(); } + public int estimatedEncodedSize() + { + return 64 // Metadata (not exact) + + this.compressedDataByteArray.length + + this.compressedColumnGenStepByteArray.length + + this.compressedWorldCompressionModeByteArray.length + + this.compressedMappingByteArray.length; + } + //===========// From b3b5ac63c8b44c841c9bc10b2c8a07b09e517af5 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 16 Jul 2024 00:27:22 +0500 Subject: [PATCH 166/205] Server-side logic is pretty much finished --- .../core/level/DhServerLevel.java | 8 ++- .../network/messages/MessageRegistry.java | 2 + .../fullData/FullDataChunkMessage.java | 23 +++---- .../FullDataPartialUpdateMessage.java | 39 ++++++----- .../FullDataSourceResponseMessage.java | 43 ++++++------ .../fullData/IFullDataPayloadMessage.java | 65 ++++++++++++++----- 6 files changed, 107 insertions(+), 73 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index f2c620089..f0a792379 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -57,6 +57,7 @@ import java.util.function.Consumer; public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + public static final int FULL_DATA_CHUNK_SIZE = 32766; public final ServerLevelModule serverside; private final IServerLevelWrapper serverLevelWrapper; @@ -164,7 +165,10 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel this.serverside.fullDataFileHandler.getAsync(msg.sectionPos).thenAcceptAsync(fullDataSource -> { rateLimiterSet.loginDataSyncRCLimiter.release(); - msg.sendResponse(new FullDataSourceResponseMessage(fullDataSource)); + + FullDataSourceResponseMessage responseMessage = new FullDataSourceResponseMessage(fullDataSource); + responseMessage.splitIntoChunks(FULL_DATA_CHUNK_SIZE, msg.session::sendMessage); + msg.sendResponse(responseMessage); }, executor); } })); @@ -287,6 +291,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel } serverPlayerState.getRateLimiterSet(this).fullDataRequestConcurrencyLimiter.release(); + response.splitIntoChunks(FULL_DATA_CHUNK_SIZE, msg.session::sendMessage); msg.sendResponse(response); } }, executor); @@ -327,6 +332,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel if (distanceFromPlayer >= serverPlayerState.serverPlayer().getViewDistance() && distanceFromPlayer <= serverPlayerState.config.getRenderDistanceRadius()) { + updateMessage.splitIntoChunks(FULL_DATA_CHUNK_SIZE, serverPlayerState.session::sendMessage); serverPlayerState.session.sendMessage(updateMessage); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/MessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/MessageRegistry.java index d1c3d00fc..aa4bcd2c3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/MessageRegistry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/MessageRegistry.java @@ -23,6 +23,7 @@ import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.seibel.distanthorizons.core.network.messages.base.CurrentLevelKeyMessage; import com.seibel.distanthorizons.core.network.messages.base.RemotePlayerConfigMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataChunkMessage; import com.seibel.distanthorizons.core.network.messages.requests.CancelMessage; import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage; import com.seibel.distanthorizons.core.network.messages.requests.ExceptionMessage; @@ -64,6 +65,7 @@ public class MessageRegistry this.registerMessage(FullDataSourceRequestMessage.class, FullDataSourceRequestMessage::new); this.registerMessage(FullDataSourceResponseMessage.class, FullDataSourceResponseMessage::new); this.registerMessage(FullDataPartialUpdateMessage.class, FullDataPartialUpdateMessage::new); + this.registerMessage(FullDataChunkMessage.class, FullDataChunkMessage::new); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataChunkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataChunkMessage.java index 55c4bc161..ac9768a0f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataChunkMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataChunkMessage.java @@ -20,26 +20,20 @@ package com.seibel.distanthorizons.core.network.messages.fullData; import com.google.common.base.MoreObjects; -import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; -import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; -import com.seibel.distanthorizons.core.network.INetworkObject; -import com.seibel.distanthorizons.core.network.messages.ILevelRelatedMessage; import com.seibel.distanthorizons.core.network.messages.NetworkMessage; -import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; -import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import io.netty.buffer.ByteBuf; -import java.io.IOException; - public class FullDataChunkMessage extends NetworkMessage { + public int bufferId; public ByteBuf buffer; public FullDataChunkMessage() { } - public FullDataChunkMessage(ByteBuf buffer) + public FullDataChunkMessage(int bufferId, ByteBuf buffer) { + this.bufferId = bufferId; + this.buffer = buffer; } @@ -49,6 +43,8 @@ public class FullDataChunkMessage extends NetworkMessage @Override public void encode(ByteBuf out) { + out.writeInt(this.bufferId); + out.writeInt(this.buffer.writerIndex()); this.buffer.resetReaderIndex(); out.writeBytes(this.buffer); @@ -57,7 +53,8 @@ public class FullDataChunkMessage extends NetworkMessage @Override public void decode(ByteBuf in) { - this.buffer = in.readBytes(in.readInt()); + int bufferSize = in.readInt(); + this.buffer = in.readBytes(bufferSize); } @@ -65,8 +62,8 @@ public class FullDataChunkMessage extends NetworkMessage public MoreObjects.ToStringHelper toStringHelper() { return super.toStringHelper() - .add("levelName", this.levelName) - .add("dataSourceDto", this.dataSourceDto); + .add("bufferId", this.bufferId) + .add("buffer", this.buffer); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java index 1ce11fae9..ab022bdee 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java @@ -20,17 +20,13 @@ package com.seibel.distanthorizons.core.network.messages.fullData; import com.google.common.base.MoreObjects; -import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; -import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.network.messages.ILevelRelatedMessage; import com.seibel.distanthorizons.core.network.messages.NetworkMessage; -import com.seibel.distanthorizons.core.network.INetworkObject; -import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import io.netty.buffer.ByteBuf; +import org.jetbrains.annotations.Nullable; -import java.io.IOException; import java.util.Objects; public class FullDataPartialUpdateMessage extends NetworkMessage implements ILevelRelatedMessage, IFullDataPayloadMessage @@ -39,24 +35,25 @@ public class FullDataPartialUpdateMessage extends NetworkMessage implements ILev @Override public String getLevelName() { return this.levelName; } - public FullDataSourceV2DTO dataSourceDto; - @Override public FullDataSourceV2DTO getDataSourceDto() { return Objects.requireNonNull(this.dataSourceDto); } + @Nullable + public Integer dtoBufferId; + @Override @Nullable + public Integer getDtoBufferId() { return this.dtoBufferId; } + @Override + public void setDtoBufferId(int bufferId) { this.dtoBufferId = bufferId; } + + public ByteBuf dtoBuffer; + @Override + public ByteBuf getDtoBuffer() { return this.dtoBuffer; } + @Override + public void setDtoBuffer(ByteBuf buffer) { this.dtoBuffer = buffer; } public FullDataPartialUpdateMessage() { } public FullDataPartialUpdateMessage(IServerLevelWrapper level, FullDataSourceV2 fullDataSource) { this.levelName = level.getKeyedLevelDimensionName(); - - try - { - EDhApiDataCompressionMode compressionMode = Config.Client.Advanced.LodBuilding.dataCompression.get(); - this.dataSourceDto = FullDataSourceV2DTO.CreateFromDataSource(fullDataSource, compressionMode); - } - catch (IOException e) - { - throw new RuntimeException(e); - } + this.createCompressedDtoBuffer(fullDataSource); } @@ -67,14 +64,15 @@ public class FullDataPartialUpdateMessage extends NetworkMessage implements ILev public void encode(ByteBuf out) { this.writeString(this.levelName, out); - // dataSourceDto must be sent separately + out.writeInt(Objects.requireNonNull(this.dtoBufferId)); + this.dtoBuffer.release(); } @Override public void decode(ByteBuf in) { this.levelName = this.readString(in); - // dataSourceDto is received separately + this.dtoBufferId = in.readInt(); } @@ -83,7 +81,8 @@ public class FullDataPartialUpdateMessage extends NetworkMessage implements ILev { return super.toStringHelper() .add("levelName", this.levelName) - .add("dataSourceDto", this.dataSourceDto); + .add("dtoBufferId", this.dtoBufferId) + .add("dtoBuffer", this.dtoBuffer); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java index 282422788..cf016ba28 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java @@ -20,18 +20,11 @@ package com.seibel.distanthorizons.core.network.messages.fullData; import com.google.common.base.MoreObjects; -import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; -import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.network.messages.TrackableMessage; -import com.seibel.distanthorizons.core.network.INetworkObject; -import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; import io.netty.buffer.ByteBuf; import org.jetbrains.annotations.Nullable; -import java.io.IOException; -import java.util.Objects; - /** * Response message, containing the requested full data source, * or nothing if requested in updates-only mode and the data was not updated. @@ -39,39 +32,42 @@ import java.util.Objects; public class FullDataSourceResponseMessage extends TrackableMessage implements IFullDataPayloadMessage { @Nullable - public FullDataSourceV2DTO dataSourceDto; - @Override public FullDataSourceV2DTO getDataSourceDto() { return Objects.requireNonNull(this.dataSourceDto); } + public Integer dtoBufferId; + @Override @Nullable + public Integer getDtoBufferId() { return this.dtoBufferId; } + @Override + public void setDtoBufferId(int bufferId) { this.dtoBufferId = bufferId; } + + public ByteBuf dtoBuffer; + @Override + public ByteBuf getDtoBuffer() { return this.dtoBuffer; } + @Override + public void setDtoBuffer(ByteBuf buffer) { this.dtoBuffer = buffer; } + public FullDataSourceResponseMessage() { } public FullDataSourceResponseMessage(@Nullable FullDataSourceV2 fullDataSource) { - try + if (fullDataSource != null) { - if (fullDataSource != null) - { - EDhApiDataCompressionMode compressionMode = Config.Client.Advanced.LodBuilding.dataCompression.get(); - this.dataSourceDto = FullDataSourceV2DTO.CreateFromDataSource(fullDataSource, compressionMode); - } - } - catch (IOException e) - { - throw new RuntimeException(e); + this.createCompressedDtoBuffer(fullDataSource); } } @Override public void encode0(ByteBuf out) { - if (this.writeOptional(out, this.dataSourceDto)) + if (this.writeOptional(out, this.dtoBufferId)) { - this.dataSourceDto.encode(out); + out.writeInt(this.dtoBufferId); + this.dtoBuffer.release(); } } @Override public void decode0(ByteBuf in) { - this.dataSourceDto = this.readOptional(in, () -> INetworkObject.decodeToInstance(new FullDataSourceV2DTO(), in)); + this.dtoBufferId = this.readOptional(in, in::readInt); } @@ -79,7 +75,8 @@ public class FullDataSourceResponseMessage extends TrackableMessage implements I public MoreObjects.ToStringHelper toStringHelper() { return super.toStringHelper() - .add("dataSourceDto", this.dataSourceDto); + .add("dtoBufferId", this.dtoBufferId) + .add("dtoBuffer", this.dtoBuffer); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/IFullDataPayloadMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/IFullDataPayloadMessage.java index 16d61b2d4..71dfd98cc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/IFullDataPayloadMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/IFullDataPayloadMessage.java @@ -1,36 +1,69 @@ package com.seibel.distanthorizons.core.network.messages.fullData; +import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; -import io.netty.buffer.CompositeByteBuf; +import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; +import java.io.IOException; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; public interface IFullDataPayloadMessage { - FullDataSourceV2DTO getDataSourceDto(); + AtomicInteger lastBufferId = new AtomicInteger(); + @Nullable + Integer getDtoBufferId(); + void setDtoBufferId(int bufferId); - default List getDataSourceDtoChunks(int chunkSize) + ByteBuf getDtoBuffer(); + void setDtoBuffer(ByteBuf buffer); + + + default void createCompressedDtoBuffer(FullDataSourceV2 fullDataSource) { - FullDataSourceV2DTO dto = this.getDataSourceDto(); - int chunkCount = dto.estimatedEncodedSize() / chunkSize; + Objects.requireNonNull(fullDataSource); - CompositeByteBuf composite = ByteBufAllocator.DEFAULT.compositeBuffer(); + int bufferId = lastBufferId.getAndIncrement(); + this.setDtoBufferId(bufferId); - ArrayList result = new ArrayList<>(); - for (int i = 0; i < chunkCount; i++) + try { + EDhApiDataCompressionMode compressionMode = Config.Client.Advanced.LodBuilding.dataCompression.get(); + FullDataSourceV2DTO dataSourceDto = FullDataSourceV2DTO.CreateFromDataSource(fullDataSource, compressionMode); + ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer(); - result.add(buffer); - composite.addComponent(buffer); + dataSourceDto.encode(buffer); + this.setDtoBuffer(buffer); } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + + default void splitIntoChunks(int chunkSize, Consumer chunkMessageConsumer) + { + ByteBuf dtoBuffer = this.getDtoBuffer(); + int bufferId = Objects.requireNonNull(this.getDtoBufferId()); - dto.encode(composite); - - composite.release(); - return result; + for (int chunkNum = 0; ; chunkNum++) + { + int offset = chunkNum * chunkSize; + int bytesLeft = dtoBuffer.writerIndex() - offset; + + if (offset >= dtoBuffer.writerIndex()) + { + break; + } + + FullDataChunkMessage chunk = new FullDataChunkMessage(bufferId, dtoBuffer.slice(offset, Math.min(bytesLeft, chunkSize))); + chunkMessageConsumer.accept(chunk); + } } } From 1a30f240ef9a70588ee9443cbd42716fddc50ec2 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 16 Jul 2024 17:49:09 +0500 Subject: [PATCH 167/205] Client-side chunk handling --- .../core/level/DhClientLevel.java | 6 +- .../core/level/DhServerLevel.java | 62 ++++++++++--------- .../client/AbstractFullDataRequestQueue.java | 6 +- .../client/ClientNetworkState.java | 48 ++++++++++++++ .../fullData/FullDataChunkMessage.java | 16 +++-- .../FullDataPartialUpdateMessage.java | 7 +-- .../FullDataSourceResponseMessage.java | 8 ++- .../fullData/IFullDataPayloadMessage.java | 31 +++++++--- 8 files changed, 132 insertions(+), 52 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 3bbe1c2c0..9c7eda259 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -36,6 +36,7 @@ import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartial import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; +import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; @@ -128,17 +129,20 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel private void registerNetworkHandlers() { assert this.eventSource != null; + assert this.networkState != null; this.eventSource.registerHandler(FullDataPartialUpdateMessage.class, msg -> { try { + FullDataSourceV2DTO dataSourceDto = this.networkState.decodeDataSourceAndReleaseBuffer(msg); + if (!msg.isSameLevelAs(this.levelWrapper)) { return; } - this.updateDataSourcesAsync(msg.dataSourceDto.createPooledDataSource(this.levelWrapper)); + this.updateDataSourcesAsync(dataSourceDto.createPooledDataSource(this.levelWrapper)); } catch (Exception e) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index f0a792379..241181ba2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -279,20 +279,22 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel } CompletableFuture.runAsync(() -> { - FullDataSourceResponseMessage response = new FullDataSourceResponseMessage(requestGroup.fullDataSource); - for (FullDataSourceRequestMessage msg : requestGroup.requestMessages.values()) + try (FullDataSourceResponseMessage response = new FullDataSourceResponseMessage(requestGroup.fullDataSource)) { - this.requestGroupsByFutureId.remove(msg.futureId); - - ServerPlayerState serverPlayerState = this.remotePlayerConnectionHandler.getConnectedPlayer(msg.serverPlayer()); - if (serverPlayerState == null) + for (FullDataSourceRequestMessage msg : requestGroup.requestMessages.values()) { - continue; + this.requestGroupsByFutureId.remove(msg.futureId); + + ServerPlayerState serverPlayerState = this.remotePlayerConnectionHandler.getConnectedPlayer(msg.serverPlayer()); + if (serverPlayerState == null) + { + continue; + } + + serverPlayerState.getRateLimiterSet(this).fullDataRequestConcurrencyLimiter.release(); + response.splitIntoChunks(FULL_DATA_CHUNK_SIZE, msg.session::sendMessage); + msg.sendResponse(response.retain()); } - - serverPlayerState.getRateLimiterSet(this).fullDataRequestConcurrencyLimiter.release(); - response.splitIntoChunks(FULL_DATA_CHUNK_SIZE, msg.session::sendMessage); - msg.sendResponse(response); } }, executor); } @@ -314,26 +316,28 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel } CompletableFuture.runAsync(() -> { - FullDataPartialUpdateMessage updateMessage = new FullDataPartialUpdateMessage(this.serverLevelWrapper, data); - for (ServerPlayerState serverPlayerState : this.remotePlayerConnectionHandler.getConnectedPlayers()) + try (FullDataPartialUpdateMessage updateMessage = new FullDataPartialUpdateMessage(this.serverLevelWrapper, data)) { - if (serverPlayerState.serverPlayer().getLevel() != this.serverLevelWrapper) + for (ServerPlayerState serverPlayerState : this.remotePlayerConnectionHandler.getConnectedPlayers()) { - continue; - } - - if (!serverPlayerState.config.isRealTimeUpdatesEnabled()) - { - continue; - } - - Vec3d playerPosition = serverPlayerState.serverPlayer().getPosition(); - int distanceFromPlayer = DhSectionPos.getManhattanBlockDistance(data.getPos(), new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)) / 16; - if (distanceFromPlayer >= serverPlayerState.serverPlayer().getViewDistance() && - distanceFromPlayer <= serverPlayerState.config.getRenderDistanceRadius()) - { - updateMessage.splitIntoChunks(FULL_DATA_CHUNK_SIZE, serverPlayerState.session::sendMessage); - serverPlayerState.session.sendMessage(updateMessage); + if (serverPlayerState.serverPlayer().getLevel() != this.serverLevelWrapper) + { + continue; + } + + if (!serverPlayerState.config.isRealTimeUpdatesEnabled()) + { + continue; + } + + Vec3d playerPosition = serverPlayerState.serverPlayer().getPosition(); + int distanceFromPlayer = DhSectionPos.getManhattanBlockDistance(data.getPos(), new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)) / 16; + if (distanceFromPlayer >= serverPlayerState.serverPlayer().getViewDistance() && + distanceFromPlayer <= serverPlayerState.config.getRenderDistanceRadius()) + { + updateMessage.splitIntoChunks(FULL_DATA_CHUNK_SIZE, serverPlayerState.session::sendMessage); + serverPlayerState.session.sendMessage(updateMessage.retain()); + } } } }, executor); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index bae5b6429..5f5e2eaf4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -17,6 +17,7 @@ import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; 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.wrapperInterfaces.minecraft.IMinecraftClientWrapper; @@ -189,9 +190,10 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, throw throwable; } - if (response.dataSourceDto != null) + if (response.dtoBufferId != null) { - FullDataSourceV2 fullDataSource = response.dataSourceDto.createPooledDataSource(this.level.getLevelWrapper()); + FullDataSourceV2DTO dataSourceDto = this.networkState.decodeDataSourceAndReleaseBuffer(response); + FullDataSourceV2 fullDataSource = dataSourceDto.createPooledDataSource(this.level.getLevelWrapper()); entry.chunkDataConsumer.accept(fullDataSource); FullDataSourceV2.DATA_SOURCE_POOL.returnPooledDataSource(fullDataSource); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index 7a27e13d6..a63f8e2db 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -1,17 +1,27 @@ package com.seibel.distanthorizons.core.multiplayer.client; +import com.google.common.cache.CacheBuilder; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; +import com.seibel.distanthorizons.core.network.INetworkObject; import com.seibel.distanthorizons.core.network.event.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.event.CloseEvent; import com.seibel.distanthorizons.core.network.messages.base.RemotePlayerConfigMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataChunkMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.IFullDataPayloadMessage; import com.seibel.distanthorizons.core.network.session.Session; +import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; +import io.netty.buffer.ByteBufAllocator; +import io.netty.buffer.CompositeByteBuf; import org.apache.logging.log4j.LogManager; import java.io.Closeable; import java.util.List; +import java.util.Objects; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.TimeUnit; public class ClientNetworkState implements Closeable { @@ -33,6 +43,12 @@ public class ClientNetworkState implements Closeable */ public Session getSession() { return this.session; } + private final ConcurrentMap fullDataBuffers = CacheBuilder.newBuilder() + .expireAfterAccess(10, TimeUnit.SECONDS) + .build() + .asMap(); + + /** * Constructs a new instance. */ @@ -51,6 +67,38 @@ public class ClientNetworkState implements Closeable { this.configReceived = false; }); + + this.session.registerHandler(FullDataChunkMessage.class, msg -> + { + if (msg.isFirst) + { + CompositeByteBuf composite = this.fullDataBuffers.remove(msg.bufferId); + if (composite != null) + { + composite.release(); + LOGGER.debug("Released full data buffer {}: {}", msg.bufferId, composite); + } + } + + CompositeByteBuf composite = this.fullDataBuffers.computeIfAbsent(msg.bufferId, bufferId -> ByteBufAllocator.DEFAULT.compositeBuffer()); + composite.addComponent(true, msg.buffer); + LOGGER.debug("Full data buffer {}: {}", msg.bufferId, composite); + }); + } + + public FullDataSourceV2DTO decodeDataSourceAndReleaseBuffer(IFullDataPayloadMessage msg) + { + CompositeByteBuf composite = this.fullDataBuffers.remove(msg.getDtoBufferId()); + + try + { + Objects.requireNonNull(composite); + return INetworkObject.decodeToInstance(new FullDataSourceV2DTO(), composite); + } + finally + { + composite.release(); + } } public void sendConfigMessage() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataChunkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataChunkMessage.java index ac9768a0f..000715c74 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataChunkMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataChunkMessage.java @@ -27,13 +27,15 @@ public class FullDataChunkMessage extends NetworkMessage { public int bufferId; public ByteBuf buffer; + public boolean isFirst; public FullDataChunkMessage() { } - public FullDataChunkMessage(int bufferId, ByteBuf buffer) + public FullDataChunkMessage(int bufferId, boolean isFirst, ByteBuf buffer) { this.bufferId = bufferId; this.buffer = buffer; + this.isFirst = isFirst; } @@ -46,15 +48,20 @@ public class FullDataChunkMessage extends NetworkMessage out.writeInt(this.bufferId); out.writeInt(this.buffer.writerIndex()); - this.buffer.resetReaderIndex(); - out.writeBytes(this.buffer); + out.writeBytes(this.buffer.readerIndex(0)); + + out.writeBoolean(this.isFirst); } @Override public void decode(ByteBuf in) { + this.bufferId = in.readInt(); + int bufferSize = in.readInt(); this.buffer = in.readBytes(bufferSize); + + this.isFirst = in.readBoolean(); } @@ -63,7 +70,8 @@ public class FullDataChunkMessage extends NetworkMessage { return super.toStringHelper() .add("bufferId", this.bufferId) - .add("buffer", this.buffer); + .add("buffer", this.buffer) + .add("isFirst", this.isFirst); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java index ab022bdee..973d5560a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java @@ -29,7 +29,7 @@ import org.jetbrains.annotations.Nullable; import java.util.Objects; -public class FullDataPartialUpdateMessage extends NetworkMessage implements ILevelRelatedMessage, IFullDataPayloadMessage +public class FullDataPartialUpdateMessage extends NetworkMessage implements ILevelRelatedMessage, IFullDataPayloadMessage { private String levelName; @Override @@ -37,8 +37,8 @@ public class FullDataPartialUpdateMessage extends NetworkMessage implements ILev @Nullable public Integer dtoBufferId; - @Override @Nullable - public Integer getDtoBufferId() { return this.dtoBufferId; } + @Override + public int getDtoBufferId() { return Objects.requireNonNull(this.dtoBufferId); } @Override public void setDtoBufferId(int bufferId) { this.dtoBufferId = bufferId; } @@ -56,7 +56,6 @@ public class FullDataPartialUpdateMessage extends NetworkMessage implements ILev this.createCompressedDtoBuffer(fullDataSource); } - @Override public boolean warnWhenUnhandled() { return false; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java index cf016ba28..9cfc5fea4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java @@ -25,16 +25,18 @@ import com.seibel.distanthorizons.core.network.messages.TrackableMessage; import io.netty.buffer.ByteBuf; import org.jetbrains.annotations.Nullable; +import java.util.Objects; + /** * Response message, containing the requested full data source, * or nothing if requested in updates-only mode and the data was not updated. */ -public class FullDataSourceResponseMessage extends TrackableMessage implements IFullDataPayloadMessage +public class FullDataSourceResponseMessage extends TrackableMessage implements IFullDataPayloadMessage { @Nullable public Integer dtoBufferId; - @Override @Nullable - public Integer getDtoBufferId() { return this.dtoBufferId; } + @Override + public int getDtoBufferId() { return Objects.requireNonNull(this.dtoBufferId); } @Override public void setDtoBufferId(int bufferId) { this.dtoBufferId = bufferId; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/IFullDataPayloadMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/IFullDataPayloadMessage.java index 71dfd98cc..a631e0b76 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/IFullDataPayloadMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/IFullDataPayloadMessage.java @@ -6,18 +6,19 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSour import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; -import org.jetbrains.annotations.Nullable; +import java.io.Closeable; import java.io.IOException; import java.util.Objects; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; -public interface IFullDataPayloadMessage + +public interface IFullDataPayloadMessage> extends Closeable { AtomicInteger lastBufferId = new AtomicInteger(); - @Nullable - Integer getDtoBufferId(); + + int getDtoBufferId(); void setDtoBufferId(int bufferId); ByteBuf getDtoBuffer(); @@ -48,22 +49,34 @@ public interface IFullDataPayloadMessage default void splitIntoChunks(int chunkSize, Consumer chunkMessageConsumer) { + int bufferId = this.getDtoBufferId(); ByteBuf dtoBuffer = this.getDtoBuffer(); - int bufferId = Objects.requireNonNull(this.getDtoBufferId()); for (int chunkNum = 0; ; chunkNum++) { int offset = chunkNum * chunkSize; - int bytesLeft = dtoBuffer.writerIndex() - offset; - - if (offset >= dtoBuffer.writerIndex()) + + int actualChunkSize = Math.min(dtoBuffer.writerIndex() - offset, chunkSize); + if (actualChunkSize <= 0) { break; } - FullDataChunkMessage chunk = new FullDataChunkMessage(bufferId, dtoBuffer.slice(offset, Math.min(bytesLeft, chunkSize))); + FullDataChunkMessage chunk = new FullDataChunkMessage(bufferId, chunkNum == 0, dtoBuffer.slice(offset, actualChunkSize)); chunkMessageConsumer.accept(chunk); } } + + default T retain() + { + this.getDtoBuffer().retain(); + //noinspection unchecked + return (T)this; + } + + default void close() + { + this.getDtoBuffer().release(); + } } From b4cf962a855bc3afa1bc7051f7ec3066f8390640 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 16 Jul 2024 23:40:20 +0500 Subject: [PATCH 168/205] Fix event handler registration --- .../client/ClientNetworkState.java | 2 +- .../network/event/NetworkEventSource.java | 33 +++++++++---------- .../event/ScopedNetworkEventSource.java | 12 ++----- .../core/network/session/Session.java | 12 +++++++ 4 files changed, 31 insertions(+), 28 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index a63f8e2db..b84263111 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -89,10 +89,10 @@ public class ClientNetworkState implements Closeable public FullDataSourceV2DTO decodeDataSourceAndReleaseBuffer(IFullDataPayloadMessage msg) { CompositeByteBuf composite = this.fullDataBuffers.remove(msg.getDtoBufferId()); + Objects.requireNonNull(composite); try { - Objects.requireNonNull(composite); return INetworkObject.decodeToInstance(new FullDataSourceV2DTO(), composite); } finally diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java index 699f3a516..50ba33e05 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java @@ -33,7 +33,6 @@ import org.apache.logging.log4j.LogManager; import java.io.InvalidClassException; import java.util.Collections; -import java.util.HashSet; import java.util.Set; import java.util.concurrent.*; import java.util.function.Consumer; @@ -43,11 +42,7 @@ public abstract class NetworkEventSource private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); - protected final ConcurrentMap, Set>> handlers = new ConcurrentHashMap<>(); - protected boolean hasHandler(Class handlerClass) - { - return this.handlers.containsKey(handlerClass); - } + protected final ConcurrentMap, ConcurrentMap>> handlers = new ConcurrentHashMap<>(); private final ConcurrentMap pendingFutures = new ConcurrentHashMap<>(); private final Set cancelledFutures = Collections.newSetFromMap(CacheBuilder.newBuilder() @@ -60,10 +55,10 @@ public abstract class NetworkEventSource { boolean handled = false; - Set> handlerList = this.handlers.get(message.getClass()); - if (handlerList != null) + ConcurrentMap> handlerMap = this.handlers.get(message.getClass()); + if (handlerMap != null) { - for (Consumer handler : handlerList) + for (Consumer handler : handlerMap.values()) { handled = true; handler.accept(message); @@ -104,25 +99,29 @@ public abstract class NetworkEventSource } } - public void registerHandler(Class handlerClass, Consumer handlerImplementation) + public abstract void registerHandler(Class handlerClass, Consumer handlerImplementation); + + protected void registerHandler(NetworkEventSource instance, Class handlerClass, Consumer handlerImplementation) { //noinspection unchecked this.handlers.computeIfAbsent(handlerClass, missingHandlerClass -> { // Will throw if the handler class is not found - if (handlerClass != CloseEvent.class) + if (missingHandlerClass != CloseEvent.class) { - MessageRegistry.INSTANCE.getMessageId(handlerClass); + MessageRegistry.INSTANCE.getMessageId(missingHandlerClass); } - return new HashSet<>(); + return new ConcurrentHashMap<>(); }) - .add((Consumer) handlerImplementation); + .put(instance, (Consumer) handlerImplementation); } - protected void removeHandler(Class handlerClass, Consumer handlerImplementation) + protected void removeAllHandlers(NetworkEventSource childInstance) { - this.handlers.computeIfAbsent(handlerClass, missingHandlerClass -> new HashSet<>()) - .remove(handlerImplementation); + for (ConcurrentMap> handlerMap : this.handlers.values()) + { + handlerMap.remove(childInstance); + } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/ScopedNetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/ScopedNetworkEventSource.java index 2c99e25dd..cf690aeb0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/event/ScopedNetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/ScopedNetworkEventSource.java @@ -42,21 +42,13 @@ public final class ScopedNetworkEventSource extends NetworkEventSource return; } - if (!this.hasHandler(handlerClass)) - { - this.parent.registerHandler(handlerClass, this::handleMessage); - } - - super.registerHandler(handlerClass, handlerImplementation); + this.parent.registerHandler(this, handlerClass, this::handleMessage); } @Override public void close() { this.isClosed = true; - for (Class handlerClass : this.handlers.keySet()) - { - this.parent.removeHandler(handlerClass, this::handleMessage); - } + this.parent.removeAllHandlers(this); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java b/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java index 064ab5620..3527536be 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java @@ -15,6 +15,7 @@ import org.jetbrains.annotations.Nullable; import java.util.concurrent.CompletableFuture; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; public class Session extends NetworkEventSource { @@ -64,6 +65,17 @@ public class Session extends NetworkEventSource } } + @Override + public void registerHandler(Class handlerClass, Consumer handlerImplementation) + { + if (this.closeReason.get() != null) + { + return; + } + + this.registerHandler(this, handlerClass, handlerImplementation); + } + public CompletableFuture sendRequest(TrackableMessage msg, Class responseClass) { msg.setSession(this); From 71e54cc9abd25fd6b0736270d927f5c1f8066baf Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 16 Jul 2024 23:51:30 +0500 Subject: [PATCH 169/205] Up protocol version --- .../main/java/com/seibel/distanthorizons/coreapi/ModInfo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java index 5025e1d68..a382dca0d 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java @@ -32,7 +32,7 @@ public final class ModInfo // region Protocol versions // Incremented every time any packets are added, changed or removed, with a few exceptions. - public static final int PROTOCOL_VERSION = 2; + public static final int PROTOCOL_VERSION = 3; public static final String PLUGIN_CHANNEL_PATH = "main"; public static final String WRAPPER_PACKET_PATH = "message"; // endregion From 3723137fea4c71e9136b9ba155b53a0d50d1ab65 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 16 Jul 2024 23:52:28 +0500 Subject: [PATCH 170/205] Remove unused method --- .../core/sql/dto/FullDataSourceV2DTO.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java index 0280d455e..0755b8fcf 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/FullDataSourceV2DTO.java @@ -409,15 +409,6 @@ public class FullDataSourceV2DTO implements IBaseDTO, INetworkObject this.createdUnixDateTime = in.readLong(); } - public int estimatedEncodedSize() - { - return 64 // Metadata (not exact) - + this.compressedDataByteArray.length - + this.compressedColumnGenStepByteArray.length - + this.compressedWorldCompressionModeByteArray.length - + this.compressedMappingByteArray.length; - } - //===========// From 0e904a388cd4facef0178e3693577d928b48e5d8 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 16 Jul 2024 23:56:48 +0500 Subject: [PATCH 171/205] Raise full data chunk size limit --- .../com/seibel/distanthorizons/core/level/DhServerLevel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 241181ba2..5c805aa96 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -57,7 +57,7 @@ import java.util.function.Consumer; public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - public static final int FULL_DATA_CHUNK_SIZE = 32766; + public static final int FULL_DATA_CHUNK_SIZE = 1048000; // 576 bytes left for other contents public final ServerLevelModule serverside; private final IServerLevelWrapper serverLevelWrapper; From 83c1a2fd6315174dccedaaba8650305e07cca09e Mon Sep 17 00:00:00 2001 From: s809 <11816467-s809@users.noreply.gitlab.com> Date: Sat, 20 Jul 2024 23:56:24 +0500 Subject: [PATCH 172/205] Fix handler registration --- .../core/level/DhServerLevel.java | 5 ++-- .../network/event/NetworkEventSource.java | 26 +++++++++++++------ 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 5c805aa96..a4ee82e46 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -265,12 +265,11 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel continue; } - // Prevent adding or removing requests + // Make this group unavailable for adding into + this.requestGroupsByPos.remove(entry.getKey()); requestGroup.requestRemoveSemaphore.acquireUninterruptibly(Short.MAX_VALUE); requestGroup.requestAddSemaphore.acquireUninterruptibly(Short.MAX_VALUE); - this.requestGroupsByPos.remove(entry.getKey()); - ThreadPoolExecutor executor = ThreadPoolUtil.getNetworkCompressionExecutor(); if (executor == null) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java index 50ba33e05..f1c0f3dd5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java @@ -42,7 +42,13 @@ public abstract class NetworkEventSource private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(), () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); - protected final ConcurrentMap, ConcurrentMap>> handlers = new ConcurrentHashMap<>(); + protected final ConcurrentMap< + Class, + ConcurrentMap< + NetworkEventSource, + Set> + > + > handlers = new ConcurrentHashMap<>(); private final ConcurrentMap pendingFutures = new ConcurrentHashMap<>(); private final Set cancelledFutures = Collections.newSetFromMap(CacheBuilder.newBuilder() @@ -55,13 +61,16 @@ public abstract class NetworkEventSource { boolean handled = false; - ConcurrentMap> handlerMap = this.handlers.get(message.getClass()); - if (handlerMap != null) + ConcurrentMap>> handlersByEventSource = this.handlers.get(message.getClass()); + if (handlersByEventSource != null) { - for (Consumer handler : handlerMap.values()) + for (Set> handlerSet : handlersByEventSource.values()) { - handled = true; - handler.accept(message); + for (Consumer handler : handlerSet) + { + handled = true; + handler.accept(message); + } } } @@ -113,12 +122,13 @@ public abstract class NetworkEventSource } return new ConcurrentHashMap<>(); }) - .put(instance, (Consumer) handlerImplementation); + .computeIfAbsent(instance, _instance -> ConcurrentHashMap.newKeySet()) + .add((Consumer) handlerImplementation); } protected void removeAllHandlers(NetworkEventSource childInstance) { - for (ConcurrentMap> handlerMap : this.handlers.values()) + for (ConcurrentMap>> handlerMap : this.handlers.values()) { handlerMap.remove(childInstance); } From d26cb410482b6884ef70824796a4b9913ee21504 Mon Sep 17 00:00:00 2001 From: s809 <11816467-s809@users.noreply.gitlab.com> Date: Sat, 27 Jul 2024 23:06:34 +0500 Subject: [PATCH 173/205] Revert "Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core into serverside" This reverts commit a15aaa573d127068f98e6fac604f237e9e3ed39a, reversing changes made to 83c1a2fd6315174dccedaaba8650305e07cca09e. --- .../com/seibel/distanthorizons/api/DhApi.java | 8 - .../enums/rendering/EDhApiBlockMaterial.java | 67 -- .../block/IDhApiBlockStateWrapper.java | 15 +- .../client/IDhApiGenericRenderingConfig.java | 46 -- .../config/client/IDhApiGraphicsConfig.java | 1 - .../data/IDhApiTerrainDataCache.java | 21 - .../data/IDhApiTerrainDataRepo.java | 81 +-- .../factories/IDhApiWrapperFactory.java | 35 +- .../rendering/IDhApiCullingFrustum.java | 4 +- .../IDhApiGenericObjectShaderProgram.java | 80 --- .../rendering/IDhApiShaderProgram.java | 7 +- .../rendering/IDhApiShadowCullingFrustum.java | 4 + .../AbstractDhApiChunkWorldGenerator.java | 49 +- .../worldGenerator/IDhApiWorldGenerator.java | 1 - .../IDhApiCustomRenderObjectFactory.java | 67 -- .../render/IDhApiCustomRenderRegister.java | 30 - .../render/IDhApiRenderableBoxGroup.java | 98 --- .../api/interfaces/util/IDhApiCopyable.java | 18 - .../interfaces/world/IDhApiLevelWrapper.java | 9 +- .../abstractEvents/DhApiAfterRenderEvent.java | 15 +- .../DhApiBeforeBufferRenderEvent.java | 18 +- .../DhApiBeforeGenericObjectRenderEvent.java | 97 --- .../DhApiBeforeGenericRenderCleanupEvent.java | 47 -- .../DhApiBeforeGenericRenderSetupEvent.java | 50 -- .../DhApiBeforeRenderCleanupEvent.java | 2 +- .../DhApiChunkModifiedEvent.java | 12 +- .../DhApiColorDepthTextureCreatedEvent.java | 13 +- .../abstractEvents/DhApiLevelLoadEvent.java | 6 +- .../abstractEvents/DhApiLevelUnloadEvent.java | 7 +- .../events/interfaces/IDhApiEventParam.java | 13 - .../DhApiRenderParam.java | 45 +- .../api/objects/data/DhApiChunk.java | 8 +- .../api/objects/data/DhApiRaycastResult.java | 3 +- .../objects/data/DhApiTerrainDataPoint.java | 8 +- .../api/objects/math/DhApiVec3d.java | 94 --- .../api/objects/math/DhApiVec3f.java | 98 --- .../objects/render/DhApiRenderableBox.java | 52 -- .../DhApiRenderableBoxGroupShading.java | 93 --- .../DependencyInjection/ApiEventInjector.java | 22 +- .../distanthorizons/coreapi/ModInfo.java | 4 +- .../interfaces/config/IConfigEntry.java | 3 +- .../util/math/Mat4f.java} | 486 ++++++++++---- .../coreapi}/util/math/Vec3d.java | 139 +++- .../coreapi/util/math/Vec3f.java | 263 ++++++++ .../coreapi}/util/math/Vec3i.java | 2 +- .../distanthorizons/core/Initializer.java | 2 - .../client/DhApiGenericRenderingConfig.java | 46 -- .../config/client/DhApiGraphicsConfig.java | 10 +- .../methods/data/DhApiTerrainDataCache.java | 87 --- .../methods/data/DhApiTerrainDataRepo.java | 229 +++---- .../core/api/internal/ClientApi.java | 42 +- .../core/api/internal/ServerApi.java | 2 +- .../core/api/internal/SharedApi.java | 53 +- .../distanthorizons/core/config/Config.java | 134 +--- .../core/config/types/ConfigEntry.java | 28 +- .../core/config/types/ConfigLinkedEntry.java | 1 - .../fullData/sources/FullDataSourceV2.java | 2 - .../render/ColumnRenderSource.java | 2 +- .../render/bufferBuilding/ColumnBox.java | 5 +- .../bufferBuilding/ColumnRenderBuffer.java | 7 +- .../ColumnRenderBufferBuilder.java | 28 +- .../bufferBuilding/CubicLodTemplate.java | 36 +- .../render/bufferBuilding/LodQuadBuilder.java | 11 +- .../FullDataToRenderDataTransformer.java | 226 +++---- .../transformers/LodDataBuilder.java | 67 +- .../core/enums/EDhDirection.java | 2 +- .../structure/ClientOnlySaveStructure.java | 22 +- .../SubDimensionLevelMatcher.java | 4 +- .../core/generation/AdjacentChunkHolder.java | 117 ---- .../core/generation/DhLightingEngine.java | 61 +- .../core/generation/WorldGenerationQueue.java | 11 +- .../core/level/AbstractDhLevel.java | 125 +--- .../core/level/ClientLevelModule.java | 50 +- .../core/level/DhClientLevel.java | 18 +- .../core/level/DhClientServerLevel.java | 31 +- .../core/level/DhServerLevel.java | 27 +- .../distanthorizons/core/level/IDhLevel.java | 20 - .../core/logging/f3/F3Screen.java | 49 +- .../distanthorizons/core/pos/DhChunkPos.java | 27 +- .../core/pos/DhSectionPos.java | 36 +- .../core/render/DhFrustumBounds.java | 7 +- .../core/render/LodQuadTree.java | 6 - .../core/render/LodRenderSection.java | 3 +- .../core/render/NeverCullFrustum.java | 5 +- .../core/render/RenderBufferHandler.java | 13 +- .../core/render/fog/LodFogConfig.java | 12 +- .../core/render/glObject/GLProxy.java | 32 +- .../core/render/glObject/buffer/GLBuffer.java | 6 +- .../render/glObject/shader/ShaderProgram.java | 14 +- .../AbstractVertexAttribute.java | 2 +- .../core/render/renderer/DebugRenderer.java | 155 +++-- .../core/render/renderer/FogRenderer.java | 135 ---- ...aderProgram.java => LodRenderProgram.java} | 123 ++-- .../core/render/renderer/LodRenderer.java | 231 +++---- .../core/render/renderer/SSAORenderer.java | 11 +- .../renderer/generic/BeaconRenderHandler.java | 241 ------- .../renderer/generic/CloudRenderHandler.java | 403 ------------ .../generic/GenericObjectRenderer.java | 607 ------------------ .../generic/GenericObjectShaderProgram.java | 229 ------- .../generic/GenericRenderObjectFactory.java | 78 --- .../renderer/generic/RenderableBoxGroup.java | 354 ---------- .../renderer/shaders/FogApplyShader.java | 104 --- .../render/renderer/shaders/FogShader.java | 114 ++-- .../renderer/shaders/SSAOApplyShader.java | 12 - .../render/renderer/shaders/SSAOShader.java | 19 +- .../core/sql/dto/BeaconBeamDTO.java | 55 -- .../core/sql/repo/AbstractDhRepo.java | 30 - .../core/sql/repo/BeaconBeamRepo.java | 196 ------ .../distanthorizons/core/util/ColorUtil.java | 5 - .../distanthorizons/core/util/LodUtil.java | 32 +- .../core/util/RenderDataPointUtil.java | 4 +- .../distanthorizons/core/util/RenderUtil.java | 6 +- .../distanthorizons/core/util/math/Mat4f.java | 249 ------- .../distanthorizons/core/util/math/Vec3f.java | 173 ----- .../core/world/DhApiWorldProxy.java | 8 - .../core/world/DhClientServerWorld.java | 1 - .../wrapperInterfaces/IWrapperFactory.java | 11 - .../block/IBlockStateWrapper.java | 49 +- .../chunk/IChunkWrapper.java | 109 ---- .../minecraft/IMinecraftClientWrapper.java | 6 +- .../minecraft/IMinecraftRenderWrapper.java | 6 +- .../misc/IServerPlayerWrapper.java | 2 +- .../world/IClientLevelWrapper.java | 2 - .../world/ILevelWrapper.java | 8 +- .../world/IServerLevelWrapper.java | 3 + .../assets/distanthorizons/lang/en_us.json | 42 +- .../textures/clouds - original.png | Bin 6214 -> 0 bytes .../distanthorizons/textures/clouds.png | Bin 1777 -> 0 bytes core/src/main/resources/shaders/curve.vert | 34 +- .../main/resources/shaders/debug/frag.frag | 1 - .../main/resources/shaders/debug/vert.vert | 4 +- .../main/resources/shaders/flat_shaded.frag | 22 +- .../src/main/resources/shaders/fog/apply.frag | 24 - core/src/main/resources/shaders/fog/fog.frag | 34 +- .../shaders/genericObject/direct/frag.frag | 10 - .../shaders/genericObject/direct/vert.vert | 41 -- .../shaders/genericObject/instanced/frag.frag | 10 - .../shaders/genericObject/instanced/vert.vert | 66 -- .../main/resources/shaders/noise/noise.frag | 12 +- core/src/main/resources/shaders/normal.vert | 1 - core/src/main/resources/shaders/standard.vert | 30 +- .../0070-sqlite-createBeaconBeamTable.sql | 16 - .../main/resources/sqlScripts/scriptList.txt | 1 - .../LightingTestBlockStateWrapper.java | 16 +- .../LightingTestChunkWrapper.java | 2 +- .../objects/LevelWrapperTest.java | 8 +- .../src/test/java/tests/DhSectionPosTest.java | 36 +- 147 files changed, 1799 insertions(+), 6341 deletions(-) delete mode 100644 api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiBlockMaterial.java delete mode 100644 api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGenericRenderingConfig.java delete mode 100644 api/src/main/java/com/seibel/distanthorizons/api/interfaces/data/IDhApiTerrainDataCache.java delete mode 100644 api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiGenericObjectShaderProgram.java delete mode 100644 api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiCustomRenderObjectFactory.java delete mode 100644 api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiCustomRenderRegister.java delete mode 100644 api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiRenderableBoxGroup.java delete mode 100644 api/src/main/java/com/seibel/distanthorizons/api/interfaces/util/IDhApiCopyable.java delete mode 100644 api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeGenericObjectRenderEvent.java delete mode 100644 api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeGenericRenderCleanupEvent.java delete mode 100644 api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeGenericRenderSetupEvent.java delete mode 100644 api/src/main/java/com/seibel/distanthorizons/api/methods/events/interfaces/IDhApiEventParam.java delete mode 100644 api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiVec3d.java delete mode 100644 api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiVec3f.java delete mode 100644 api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBox.java delete mode 100644 api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBoxGroupShading.java rename api/src/main/java/com/seibel/distanthorizons/{api/objects/math/DhApiMat4f.java => coreapi/util/math/Mat4f.java} (57%) rename {core/src/main/java/com/seibel/distanthorizons/core => api/src/main/java/com/seibel/distanthorizons/coreapi}/util/math/Vec3d.java (50%) create mode 100644 api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Vec3f.java rename {core/src/main/java/com/seibel/distanthorizons/core => api/src/main/java/com/seibel/distanthorizons/coreapi}/util/math/Vec3i.java (98%) delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGenericRenderingConfig.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataCache.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/generation/AdjacentChunkHolder.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/render/renderer/FogRenderer.java rename core/src/main/java/com/seibel/distanthorizons/core/render/renderer/{DhTerrainShaderProgram.java => LodRenderProgram.java} (52%) delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/BeaconRenderHandler.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/CloudRenderHandler.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectShaderProgram.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericRenderObjectFactory.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/RenderableBoxGroup.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogApplyShader.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/sql/dto/BeaconBeamDTO.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/sql/repo/BeaconBeamRepo.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/util/math/Mat4f.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/util/math/Vec3f.java delete mode 100644 core/src/main/resources/assets/distanthorizons/textures/clouds - original.png delete mode 100644 core/src/main/resources/assets/distanthorizons/textures/clouds.png delete mode 100644 core/src/main/resources/shaders/fog/apply.frag delete mode 100644 core/src/main/resources/shaders/genericObject/direct/frag.frag delete mode 100644 core/src/main/resources/shaders/genericObject/direct/vert.vert delete mode 100644 core/src/main/resources/shaders/genericObject/instanced/frag.frag delete mode 100644 core/src/main/resources/shaders/genericObject/instanced/vert.vert delete mode 100644 core/src/main/resources/sqlScripts/0070-sqlite-createBeaconBeamTable.sql diff --git a/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java b/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java index 21995fced..80ae01702 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java @@ -23,9 +23,7 @@ import com.seibel.distanthorizons.api.interfaces.events.IDhApiEventInjector; import com.seibel.distanthorizons.api.interfaces.factories.IDhApiWrapperFactory; import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable; import com.seibel.distanthorizons.api.interfaces.override.worldGenerator.IDhApiWorldGeneratorOverrideRegister; -import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderObjectFactory; import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderProxy; -import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiAfterDhInitEvent; import com.seibel.distanthorizons.api.methods.override.DhApiWorldGeneratorOverrideRegister; import com.seibel.distanthorizons.coreapi.ModInfo; @@ -129,12 +127,6 @@ public class DhApi */ public static IDhApiWrapperFactory wrapperFactory = null; - /** - * Used to create custom renderable objects.
- * These objects can be added to the renderer in {@link IDhApiLevelWrapper} - * @since API 3.0.0 - */ - public static IDhApiCustomRenderObjectFactory customRenderObjectFactory = null; } diff --git a/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiBlockMaterial.java b/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiBlockMaterial.java deleted file mode 100644 index dfedf3278..000000000 --- a/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiBlockMaterial.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.seibel.distanthorizons.api.enums.rendering; - -/** - * contains the indices used by shaders to determine - * how different block types should be rendered.

- * - * UNKNOWN,
- * LEAVES,
- * STONE,
- * WOOD,
- * METAL,
- * DIRT,
- * LAVA,
- * DEEPSLATE,
- * SNOW,
- * SAND,
- * TERRACOTTA,
- * NETHER_STONE,
- * WATER,
- * GRASS,
- * AIR,
- * ILLUMINATED,
- * - * @author James Seibel - * @since API 3.0.0 - * @version 2024-7-11 - */ -public enum EDhApiBlockMaterial -{ - UNKNOWN(0), - LEAVES(1), - STONE(2), - WOOD(3), - METAL(4), - DIRT(5), - LAVA(6), - DEEPSLATE(7), - SNOW(8), - SAND(9), - TERRACOTTA(10), - NETHER_STONE(11), - WATER(12), - GRASS(13), - /** shouldn't normally be needed, but just in case */ - AIR(14), - ILLUMINATED(15); // Max value - - - - public final byte index; - - EDhApiBlockMaterial(int index) { this.index = (byte)index;} - - public static EDhApiBlockMaterial getFromIndex(int index) - { - for(EDhApiBlockMaterial material : EDhApiBlockMaterial.values()) - { - if (material.index == index) - { - return material; - } - } - - return EDhApiBlockMaterial.UNKNOWN; - } - -} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/block/IDhApiBlockStateWrapper.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/block/IDhApiBlockStateWrapper.java index 8a71a090f..682b04f06 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/block/IDhApiBlockStateWrapper.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/block/IDhApiBlockStateWrapper.java @@ -30,21 +30,12 @@ import com.seibel.distanthorizons.api.interfaces.IDhApiUnsafeWrapper; */ public interface IDhApiBlockStateWrapper extends IDhApiUnsafeWrapper { - /** @since API 1.0.0 */ boolean isAir(); - /** @since API 1.0.0 */ boolean isSolid(); - /** @since API 1.0.0 */ boolean isLiquid(); - /** - * Returns the full serialized form of the given block - * as defined by DH's serialization methods. - * @since API 3.0.0 - */ - String getSerialString(); - /** @since API 3.0.0 */ - byte getMaterialId(); - + // TODO: + // boolean hasNoCollision(); + // boolean noFaceIsFullFace(); } diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGenericRenderingConfig.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGenericRenderingConfig.java deleted file mode 100644 index 3209b07ba..000000000 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGenericRenderingConfig.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.api.interfaces.config.client; - -import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigGroup; -import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue; - -/** - * Distant Horizons' generic rendering configuration.

- * - * @author James Seibel - * @version 2024-7-11 - * @since API 3.0.0 - */ -public interface IDhApiGenericRenderingConfig extends IDhApiConfigGroup -{ - /** - * If enabled DH will render generic objects into its terrain pass.
- * This includes: clouds, beacons, and API added objects. - */ - IDhApiConfigValue renderingEnabled(); - - /** If enabled DH will render beacon beams. */ - IDhApiConfigValue beaconRenderingEnabled(); - - /** If enabled DH will render clouds. */ - IDhApiConfigValue cloudRenderingEnabled(); - -} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGraphicsConfig.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGraphicsConfig.java index f69869aeb..d7c2792f4 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGraphicsConfig.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGraphicsConfig.java @@ -41,7 +41,6 @@ public interface IDhApiGraphicsConfig extends IDhApiConfigGroup IDhApiFogConfig fog(); IDhApiAmbientOcclusionConfig ambientOcclusion(); IDhApiNoiseTextureConfig noiseTexture(); - IDhApiGenericRenderingConfig genericRendering(); diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/data/IDhApiTerrainDataCache.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/data/IDhApiTerrainDataCache.java deleted file mode 100644 index a57a8a56e..000000000 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/data/IDhApiTerrainDataCache.java +++ /dev/null @@ -1,21 +0,0 @@ -package com.seibel.distanthorizons.api.interfaces.data; - -/** - * Can be used to drastically speed up repeat read operations in {@link IDhApiTerrainDataRepo}. - * - * @see IDhApiTerrainDataRepo - * - * @author James Seibel - * @version 2024-7-14 - * @since API 3.0.0 - */ -public interface IDhApiTerrainDataCache -{ - /** - * Removes any data that's currently stored in this cache. - * This cane be done to free up memory or invalidate - * the cache so fresh data can be pulled in. - */ - void clear(); - -} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/data/IDhApiTerrainDataRepo.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/data/IDhApiTerrainDataRepo.java index 45f8c1d72..2a4f81e24 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/data/IDhApiTerrainDataRepo.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/data/IDhApiTerrainDataRepo.java @@ -29,8 +29,6 @@ import com.seibel.distanthorizons.api.objects.data.DhApiTerrainDataPoint; /** * Used to interface with Distant Horizons' terrain data. * - * @see IDhApiTerrainDataCache - * * @author James Seibel * @version 2023-6-22 * @since API 1.0.0 @@ -42,50 +40,29 @@ public interface IDhApiTerrainDataRepo // getters // //=========// - /** @see IDhApiTerrainDataRepo#getSingleDataPointAtBlockPos(IDhApiLevelWrapper, int, int, int, IDhApiTerrainDataCache) */ - default DhApiResult getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ) { return this.getSingleDataPointAtBlockPos(levelWrapper, blockPosX, blockPosY, blockPosZ, null); } - /** - * Returns the terrain datapoint at the given block position, at/or containing the given Y position. - * @since API 3.0.0 - */ - DhApiResult getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ, IDhApiTerrainDataCache dataCache); + /** Returns the terrain datapoint at the given block position, at/or containing the given Y position. */ + DhApiResult getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ); + /** Returns every datapoint in the column located at the given block X and Z position top to bottom. */ + DhApiResult getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ); - /** @see IDhApiTerrainDataRepo#getColumnDataAtBlockPos(IDhApiLevelWrapper, int, int, IDhApiTerrainDataCache) */ - default DhApiResult getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ) { return this.getColumnDataAtBlockPos(levelWrapper, blockPosX, blockPosZ, null); } - /** - * Returns every datapoint in the column located at the given block X and Z position top to bottom. - * @since API 3.0.0 - */ - DhApiResult getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ, IDhApiTerrainDataCache dataCache); - - /** @see IDhApiTerrainDataRepo#getAllTerrainDataAtChunkPos(IDhApiLevelWrapper, int, int, IDhApiTerrainDataCache) */ - default DhApiResult getAllTerrainDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ) { return this.getAllTerrainDataAtChunkPos(levelWrapper, chunkPosX, chunkPosZ, null); } /** * Returns every datapoint in the given chunk's X and Z position.

* * The returned array is ordered: [relativeBlockX][relativeBlockZ][columnIndex]
* RelativeBlockX/Z are relative to the block position closest to negative infinity in the chunk's position.
* The column data is ordered from top to bottom. Note: each column may have a different number of values.
- * - * @since API 3.0.0 */ - DhApiResult getAllTerrainDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ, IDhApiTerrainDataCache dataCache); + DhApiResult getAllTerrainDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ); - /** @see IDhApiTerrainDataRepo#getAllTerrainDataAtRegionPos(IDhApiLevelWrapper, int, int, IDhApiTerrainDataCache) */ - default DhApiResult getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ) { return this.getAllTerrainDataAtRegionPos(levelWrapper, regionPosX, regionPosZ, null); } /** * Returns every datapoint in the given region's X and Z position.

* * The returned array is ordered: [relativeBlockX][relativeBlockZ][columnIndex]
* RelativeBlockX/Z are relative to the block position closest to negative infinity in the region's position.
* The column data is ordered from top to bottom. Note: each column may have a different number of values.
- * - * @since API 3.0.0 */ - DhApiResult getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ, IDhApiTerrainDataCache dataCache); + DhApiResult getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ); - /** @see IDhApiTerrainDataRepo#getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper, byte, int, int, IDhApiTerrainDataCache) */ - default DhApiResult getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ) { return this.getAllTerrainDataAtDetailLevelAndPos(levelWrapper, detailLevel, posX, posZ, null); } /** * Returns every datapoint in the column located at the given detail level and X/Z position.
* This can be used to return terrain data for non-standard sizes (IE 2x2 blocks or 2x2 chunks). @@ -94,42 +71,20 @@ public interface IDhApiTerrainDataRepo * Every increase doubles the width of the returned area.
* Example values: 0 = block, 1 = 2x2 blocks, 2 = 4x4 blocks, ... 4 = chunk (16x16 blocks), ... 9 = region (512x512 blocks)
* See {@link EDhApiDetailLevel} for more information. - * - * @since API 3.0.0 */ - DhApiResult getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ, IDhApiTerrainDataCache dataCache); - - - - /** @see IDhApiTerrainDataRepo#raycast(IDhApiLevelWrapper, double, double, double, float, float, float, int, IDhApiTerrainDataCache) */ - default DhApiResult raycast( - IDhApiLevelWrapper levelWrapper, - double rayOriginX, double rayOriginY, double rayOriginZ, - float rayDirectionX, float rayDirectionY, float rayDirectionZ, - int maxRayBlockLength) - { - return this.raycast( - levelWrapper, - rayOriginX, rayOriginY, rayOriginZ, - rayDirectionX, rayDirectionY, rayDirectionZ, - maxRayBlockLength, - null); - } + DhApiResult getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ); /** * Returns the datapoint and position of the LOD * at the end of the given ray.

* * Will return "success" with a null datapoint if the ray reaches the max length without finding any data. - * - * @since API 3.0.0 */ DhApiResult raycast( IDhApiLevelWrapper levelWrapper, double rayOriginX, double rayOriginY, double rayOriginZ, float rayDirectionX, float rayDirectionY, float rayDirectionZ, - int maxRayBlockLength, - IDhApiTerrainDataCache dataCache); + int maxRayBlockLength); @@ -143,27 +98,15 @@ public interface IDhApiTerrainDataRepo * Notes:
* - Only works if the given {@link IDhApiLevelWrapper} points to a loaded level.
* - If the player travels to this chunk, or the chunk is updated in some other way; your data will be replaced - * by whatever the current chunk is.
- * - This method may not update the LOD data immediately. Any other chunks that have - * been queued to update will be handled first. + * by whatever the current chunk is.
+ * - This method may not update the LOD data immediately. Any other chunks have + * been queued to update, they will be handled first. * * @param levelWrapper the level wrapper that the chunk should be saved to. * @param chunkObjectArray see {@link IDhApiWorldGenerator#generateChunks} for what objects are expected. * @throws ClassCastException if chunkObjectArray doesn't contain the right objects. * The exception will contain the expected object(s). */ - DhApiResult overwriteChunkDataAsync(IDhApiLevelWrapper levelWrapper, Object[] chunkObjectArray) throws ClassCastException; - - - - //=========// - // helpers // - //=========// - - /** - * @return a {@link IDhApiTerrainDataCache} backed by {@link java.lang.ref.SoftReference}'s. - * @since API 3.0.0 - */ - IDhApiTerrainDataCache getSoftCache(); + public DhApiResult overwriteChunkDataAsync(IDhApiLevelWrapper levelWrapper, Object[] chunkObjectArray) throws ClassCastException; } diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/factories/IDhApiWrapperFactory.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/factories/IDhApiWrapperFactory.java index ce3e70662..524742f04 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/factories/IDhApiWrapperFactory.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/factories/IDhApiWrapperFactory.java @@ -79,34 +79,11 @@ public interface IDhApiWrapperFactory - /** - * Constructs a {@link IDhApiBiomeWrapper} for use by other DhApi methods. - * - * @param resourceLocationString example: "minecraft:plains" - * - * @param levelWrapper Expects a {@link IDhApiLevelWrapper} returned by one of DH's {@link DhApi.Delayed#worldProxy} methods.
- * A custom implementation of {@link IDhApiLevelWrapper} will not be accepted. - * - * @throws IOException if the resourceLocationString wasn't able to be parsed or converted into a valid {@link IDhApiBiomeWrapper} - * @throws ClassCastException if the wrong levelWrapper type was given - * - * @since API 3.0.0 - */ - IDhApiBiomeWrapper getBiomeWrapper(String resourceLocationString, IDhApiLevelWrapper levelWrapper) throws IOException, ClassCastException; - - /** - * Constructs a {@link IDhApiBlockStateWrapper} for use by other DhApi methods. - * This returns the default blockstate for the given resource location. - * - * @param resourceLocationString examples: "minecraft:bedrock", "minecraft:stone", "minecraft:grass_block" - * @param levelWrapper Expects a {@link IDhApiBlockStateWrapper} returned by one of DH's {@link DhApi.Delayed#worldProxy} methods.
- * A custom implementation of {@link IDhApiBlockStateWrapper} will not be accepted. - * - * @throws IOException if the resourceLocationString wasn't able to be parsed or converted into a valid {@link IDhApiBlockStateWrapper} - * @throws ClassCastException if the wrong levelWrapper type was given - * - * @since API 3.0.0 - */ - IDhApiBlockStateWrapper getDefaultBlockStateWrapper(String resourceLocationString, IDhApiLevelWrapper levelWrapper) throws IOException, ClassCastException; + ///** + // * Specifically designed to be used with the API. + // * + // * @throws ClassCastException with instructions on expected objects if the object couldn't be cast + // */ + //IChunkWrapper createChunkWrapper(Object[] objectArray) throws ClassCastException; } diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiCullingFrustum.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiCullingFrustum.java index 33cebacd2..44106b82a 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiCullingFrustum.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiCullingFrustum.java @@ -21,7 +21,7 @@ package com.seibel.distanthorizons.api.interfaces.override.rendering; import com.seibel.distanthorizons.api.enums.EDhApiDetailLevel; import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable; -import com.seibel.distanthorizons.api.objects.math.DhApiMat4f; +import com.seibel.distanthorizons.coreapi.util.math.Mat4f; /** * Used to determine if a LOD should be rendered or is outside the @@ -41,7 +41,7 @@ public interface IDhApiCullingFrustum extends IDhApiOverrideable * @param worldMaxBlockY the highest block position this level allows. * @param worldViewProjection the projection matrix used in this render pass. */ - void update(int worldMinBlockY, int worldMaxBlockY, DhApiMat4f worldViewProjection); + void update(int worldMinBlockY, int worldMaxBlockY, Mat4f worldViewProjection); /** * returns true if the LOD bounds intersect this frustum diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiGenericObjectShaderProgram.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiGenericObjectShaderProgram.java deleted file mode 100644 index ce1d6fcde..000000000 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiGenericObjectShaderProgram.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.api.interfaces.override.rendering; - -import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable; -import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; -import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; -import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; -import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; - -/** - * @see IDhApiShaderProgram - * - * @author James Seibel - * @version 2024-7-11 - * @since API 3.0.0 - */ -public interface IDhApiGenericObjectShaderProgram extends IDhApiOverrideable -{ - - /** - * If this method is called that means this program has the highest priority as defined by {@link IDhApiOverrideable#getPriority()} - * and gets to decide if it wants to be used to render this frame or not.

- * - * If this method returns true then this program will be used for this frame.
- * If this returns false then the default DH {@link IDhApiGenericObjectShaderProgram} will be used instead. - */ - boolean overrideThisFrame(); - - /** @return the OpenGL ID for this shader program */ - int getId(); - - /** Free any OpenGL objects owned by this program. */ - void free(); - - /** Runs any necessary binding this program needs so rendering can be done. */ - void bind(DhApiRenderParam renderEventParam); - /** Runs any necessary unbinding this program needs so rendering can be done by another program. */ - void unbind(); - - /** Binds the given Vertex Buffer Object to this shader program for rendering. */ - void bindVertexBuffer(int vbo); - - - /** sets up the necessary uniforms for rendering */ - void fillIndirectUniformData( - DhApiRenderParam renderParameters, - DhApiRenderableBoxGroupShading shading, IDhApiRenderableBoxGroup boxGroup, - DhApiVec3d camPos); - - /** sets up the necessary uniforms for rendering */ - void fillSharedDirectUniformData( - DhApiRenderParam renderParameters, - DhApiRenderableBoxGroupShading shading, IDhApiRenderableBoxGroup boxGroup, - DhApiVec3d camPos); - void fillDirectUniformData( - DhApiRenderParam renderParameters, - IDhApiRenderableBoxGroup boxGroup, DhApiRenderableBox box, - DhApiVec3d camPos); - - -} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiShaderProgram.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiShaderProgram.java index 6844715cf..6f329121d 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiShaderProgram.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiShaderProgram.java @@ -21,11 +21,10 @@ package com.seibel.distanthorizons.api.interfaces.override.rendering; import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; +import com.seibel.distanthorizons.coreapi.util.math.Mat4f; +import com.seibel.distanthorizons.coreapi.util.math.Vec3f; /** - * @see IDhApiGenericObjectShaderProgram - * * @author James Seibel * @version 2024-1-24 * @since API 2.0.0 @@ -58,7 +57,7 @@ public interface IDhApiShaderProgram extends IDhApiOverrideable void fillUniformData(DhApiRenderParam renderParameters); /** sets the vec3 that all DH verticies should be offset by when rendering */ - void setModelOffsetPos(DhApiVec3f modelPos); + void setModelOffsetPos(Vec3f modelPos); /** Binds the given Vertex Buffer Object to this shader program for rendering. */ void bindVertexBuffer(int vbo); diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiShadowCullingFrustum.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiShadowCullingFrustum.java index 564c27838..5748d06d9 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiShadowCullingFrustum.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiShadowCullingFrustum.java @@ -19,6 +19,10 @@ package com.seibel.distanthorizons.api.interfaces.override.rendering; +import com.seibel.distanthorizons.api.enums.EDhApiDetailLevel; +import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable; +import com.seibel.distanthorizons.coreapi.util.math.Mat4f; + /** * The culling frustum used during Distant Horizons' shadow pass * if another mod has enabled Distant Horizons' shadow diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/AbstractDhApiChunkWorldGenerator.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/AbstractDhApiChunkWorldGenerator.java index 09e71d706..e34fe2ecb 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/AbstractDhApiChunkWorldGenerator.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/AbstractDhApiChunkWorldGenerator.java @@ -22,8 +22,6 @@ package com.seibel.distanthorizons.api.interfaces.override.worldGenerator; import com.seibel.distanthorizons.api.enums.EDhApiDetailLevel; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode; import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable; -import com.seibel.distanthorizons.api.objects.data.DhApiChunk; -import com.seibel.distanthorizons.api.objects.data.DhApiTerrainDataPoint; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import java.io.Closeable; @@ -79,41 +77,13 @@ public abstract class AbstractDhApiChunkWorldGenerator implements Closeable, IDh }, worldGeneratorThreadPool); } - @Override - public final CompletableFuture generateApiChunks( - int chunkPosMinX, - int chunkPosMinZ, - byte granularity, - byte targetDataDetail, - EDhApiDistantGeneratorMode generatorMode, - ExecutorService worldGeneratorThreadPool, - Consumer resultConsumer - ) - { - return CompletableFuture.runAsync(() -> - { - // TODO what does this mean? - int genChunkWidth = BitShiftUtil.powerOfTwo(granularity - 4); - - for (int chunkX = chunkPosMinX; chunkX < chunkPosMinX + genChunkWidth; chunkX++) - { - for (int chunkZ = chunkPosMinZ; chunkZ < chunkPosMinZ + genChunkWidth; chunkZ++) - { - DhApiChunk apiChunk = this.generateApiChunk(chunkX, chunkZ, generatorMode); - resultConsumer.accept(apiChunk); - } - } - }, worldGeneratorThreadPool); - } - - /** * This method is called to generate terrain over a given area * from a thread defined by Distant Horizons.

* * @param chunkPosX the chunk X position in the level (not to be confused with the chunk's BlockPos in the level) * @param chunkPosZ the chunk Z position in the level (not to be confused with the chunk's BlockPos in the level) - * @param generatorMode how far into the world gen pipeline this method should run. See {@link EDhApiDistantGeneratorMode} for additional documentation. + * @param generatorMode how far into the world gen pipeline this method run. See {@link EDhApiDistantGeneratorMode} for additional documentation. * * @return See {@link IDhApiWorldGenerator#generateChunks(int, int, byte, byte, EDhApiDistantGeneratorMode, ExecutorService, Consumer) IDhApiWorldGenerator.generateChunks} * for the list of Object's this method should return along with additional documentation. @@ -122,21 +92,4 @@ public abstract class AbstractDhApiChunkWorldGenerator implements Closeable, IDh */ public abstract Object[] generateChunk(int chunkPosX, int chunkPosZ, EDhApiDistantGeneratorMode generatorMode); - /** - * This method is called to generate terrain over a given area - * from a thread defined by Distant Horizons.

- * - * @param chunkPosX the chunk X position in the level (not to be confused with the chunk's BlockPos in the level) - * @param chunkPosZ the chunk Z position in the level (not to be confused with the chunk's BlockPos in the level) - * @param generatorMode how far into the world gen pipeline this method should run. See {@link EDhApiDistantGeneratorMode} for additional documentation. - * - * @return A {@link DhApiChunk} with the generated {@link DhApiTerrainDataPoint} including air blocks. - * Note: if air blocks aren't included with the proper lighting, lower detail levels will appear as black/unlit. - * - * @see IDhApiWorldGenerator#generateApiChunks(int, int, byte, byte, EDhApiDistantGeneratorMode, ExecutorService, Consumer) - * - * @since API 3.0.0 - */ - public abstract DhApiChunk generateApiChunk(int chunkPosX, int chunkPosZ, EDhApiDistantGeneratorMode generatorMode); - } diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/IDhApiWorldGenerator.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/IDhApiWorldGenerator.java index 7861040cc..eb1831973 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/IDhApiWorldGenerator.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/IDhApiWorldGenerator.java @@ -154,7 +154,6 @@ public interface IDhApiWorldGenerator extends Closeable, IDhApiOverrideable * * After the {@link DhApiChunk} has been generated, it should be passed into the * resultConsumer's {@link Consumer#accept(Object)} method. - * Note: if air blocks aren't included in the with the {@link DhApiChunk} with proper lighting, lower detail levels will appear as black/unlit. * * @implNote the default implementation of this method throws an {@link UnsupportedOperationException}, * and must be overridden when {@link #getReturnType()} returns {@link EDhApiWorldGeneratorReturnType#API_CHUNKS}. diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiCustomRenderObjectFactory.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiCustomRenderObjectFactory.java deleted file mode 100644 index 334c3edd1..000000000 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiCustomRenderObjectFactory.java +++ /dev/null @@ -1,67 +0,0 @@ -package com.seibel.distanthorizons.api.interfaces.render; - -import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; -import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; - -import java.util.List; - -/** - * Handles creating - * {@link IDhApiRenderableBoxGroup} objects, - * which can be added via a {@link IDhApiCustomRenderRegister}. - * - * @see IDhApiCustomRenderRegister - * @see IDhApiRenderableBoxGroup - * - * @author James Seibel - * @version 2024-7-3 - * @since API 3.0.0 - */ -public interface IDhApiCustomRenderObjectFactory -{ - /** - * Creates a {@link IDhApiRenderableBoxGroup} from for the given {@link DhApiRenderableBox} - * where the box is positioned relative to the level's origin. - * - * @param resourceLocation A colon separated Resource Location string, similar to vanilla Minecraft, for example: "DistantHorizons:Clouds" - * - * @see DhApiRenderableBox - * @see IDhApiRenderableBoxGroup#getResourceLocationNamespace() - * @see IDhApiRenderableBoxGroup#getResourceLocationPath() - * - * @throws IllegalArgumentException if resourceLocation is null, isn't separated by a colon, or has multiple colons. - */ - IDhApiRenderableBoxGroup createForSingleBox(String resourceLocation, DhApiRenderableBox cube) throws IllegalArgumentException; - - /** - * Creates a {@link IDhApiRenderableBoxGroup} from the given list of {@link DhApiRenderableBox} where each - * one is positioned relative to given originBlockPos, which in turn is relative to the level's origin. - * - * @param resourceLocation A colon separated Resource Location string, similar to vanilla Minecraft, for example: "DistantHorizons:Clouds" - * @param originBlockPos The starting position for this {@link IDhApiRenderableBoxGroup}, can be changed during runtime. - * - * - * @see DhApiRenderableBox - * @see IDhApiRenderableBoxGroup#getResourceLocationNamespace() - * @see IDhApiRenderableBoxGroup#getResourceLocationPath() - * - * @throws IllegalArgumentException if resourceLocation is null, isn't separated by a colon, or has multiple colons. - */ - IDhApiRenderableBoxGroup createRelativePositionedGroup(String resourceLocation, DhApiVec3d originBlockPos, List cubeList); - - /** - * Creates a {@link IDhApiRenderableBoxGroup} from the given list of {@link DhApiRenderableBox} where each - * one is positioned relative to the level's origin. - * - * @param resourceLocation A colon separated Resource Location string, similar to vanilla Minecraft, for example: "DistantHorizons:Clouds" - * - * @see DhApiRenderableBox - * @see IDhApiRenderableBoxGroup#getResourceLocationNamespace() - * @see IDhApiRenderableBoxGroup#getResourceLocationPath() - * - * @throws IllegalArgumentException if resourceLocation is null, isn't separated by a colon, or has multiple colons. - */ - IDhApiRenderableBoxGroup createAbsolutePositionedGroup(String resourceLocation, List cubeList); - -} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiCustomRenderRegister.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiCustomRenderRegister.java deleted file mode 100644 index c156c0c8c..000000000 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiCustomRenderRegister.java +++ /dev/null @@ -1,30 +0,0 @@ -package com.seibel.distanthorizons.api.interfaces.render; - -import com.seibel.distanthorizons.api.DhApi; -import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper; -import com.seibel.distanthorizons.api.interfaces.world.IDhApiWorldProxy; - -/** - * Handles adding and removing - * {@link IDhApiRenderableBoxGroup} objects, - * from DH's renderer.

- * - * Can be accessed in - * {@link DhApi.Delayed#worldProxy} -> {@link IDhApiLevelWrapper}. - * - * @see IDhApiCustomRenderObjectFactory - * @see IDhApiRenderableBoxGroup - * @see IDhApiWorldProxy - * @see IDhApiLevelWrapper - * - * @author James Seibel - * @version 2024-7-3 - * @since API 3.0.0 - */ -public interface IDhApiCustomRenderRegister -{ - void add(IDhApiRenderableBoxGroup cubeGroup) throws IllegalArgumentException; - - IDhApiRenderableBoxGroup remove(long id); - -} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiRenderableBoxGroup.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiRenderableBoxGroup.java deleted file mode 100644 index 399ba1571..000000000 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiRenderableBoxGroup.java +++ /dev/null @@ -1,98 +0,0 @@ -package com.seibel.distanthorizons.api.interfaces.render; - -import com.seibel.distanthorizons.api.enums.config.EDhApiLodShading; -import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3i; -import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; -import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; - -import java.util.List; -import java.util.function.Consumer; - -/** - * A list of {@link DhApiRenderableBox}'s that - * can be rendered to DH's terrain pass. - * - * @see DhApiRenderableBox - * - * @author James Seibel - * @version 2024-6-30 - * @since API 3.0.0 - */ -public interface IDhApiRenderableBoxGroup extends List -{ - /** - * A unique numerical ID used by DH during rendering. - * This can also be used to bind/unbind specific {@link IDhApiRenderableBoxGroup}'s from the renderer. - * @return the ID for this specific group - */ - long getId(); - - /** - * Used to determine which mods have added what to the DH renderer. - * This can be used both by the F3 pie chart so you as a mod developer can profile your code - * or by shader developers who want to render your objects differently.

- * - * Should be used the same as a vanilla Minecraft ResourceLocation. - * For example if your mod named "Heavy Thunder" adds additional clouds named "Storm Front", - * your Resource Location would be something like "HeavyThunder:StormFront" - * and this method would return "HeavyThunder". - */ - String getResourceLocationNamespace(); - /** - * Used to determine what type of object mods have added what to the DH renderer. - * This can be used both by the F3 pie chart so you as a mod developer can profile your code - * or by shader developers who want to render your objects differently.

- * - * Should be used the same as a vanilla Minecraft ResourceLocation. - * For example if your mod named "Heavy Thunder" adds additional clouds named "Storm Front", - * your Resource Location would be something like "HeavyThunder:StormFront" - * and this method would return "StormFront". - */ - String getResourceLocationPath(); - - /** Sets whether this group should render or not. */ - void setActive(boolean active); - /** @return if active this group will render. */ - boolean isActive(); - - /** Sets whether this group should render with Screen Space Ambient Occlusioning. */ - void setSsaoEnabled(boolean ssaoEnabled); - /** @return if active this group will render with Screen Space Ambient Occlusioning. */ - boolean isSsaoEnabled(); - - /** Sets where this group will render in the level. */ - void setOriginBlockPos(DhApiVec3d pos); - /** @return the block position in the level that all {@see DhApiRenderableBox} will render relative to. */ - DhApiVec3d getOriginBlockPos(); - - /** - * Called right before this group is rendered.
- * This is a good place to change the origin or notify of any box changes. - */ - void setPreRenderFunc(Consumer renderEventParam); - void setPostRenderFunc(Consumer renderEventParam); // TODO name? - - /** - * If a cube's color, position, or other property is changed this method - * must be called for those changes to render.

- * - * Note: changing the group's position via {@link #setOriginBlockPos} doesn't - * require calling this method. - */ - void triggerBoxChange(); - - /** Only accepts values between 0 and 15 */ - void setSkyLight(int skyLight); - int getSkyLight(); - - /** Only accepts values between 0 and 15 */ - void setBlockLight(int blockLight); - int getBlockLight(); - - void setShading(DhApiRenderableBoxGroupShading shading); - DhApiRenderableBoxGroupShading getShading(); - -} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/util/IDhApiCopyable.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/util/IDhApiCopyable.java deleted file mode 100644 index 4a86357ef..000000000 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/util/IDhApiCopyable.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.seibel.distanthorizons.api.interfaces.util; - -/** - * Used for objects that need deep clones.
- * Replacement for {@link Cloneable}. - * - * @see Cloneable - * - * @author James Seibel - * @version 2024-7-12 - * @since API 3.0.0 - */ -public interface IDhApiCopyable -{ - /** Returns a deep clone of all parameters whenever possible. */ - IDhApiCopyable copy(); - -} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java index 1c159e746..b846f1880 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java @@ -21,7 +21,6 @@ package com.seibel.distanthorizons.api.interfaces.world; import com.seibel.distanthorizons.api.interfaces.IDhApiUnsafeWrapper; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType; -import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister; /** * Can be either a Server or Client level.
@@ -44,7 +43,7 @@ public interface IDhApiLevelWrapper extends IDhApiUnsafeWrapper boolean hasSkyLight(); /** Returns the max block height of the level(?) */ - int getMaxHeight(); + int getHeight(); /** * Returns the lowest possible block position for the level.
@@ -52,10 +51,4 @@ public interface IDhApiLevelWrapper extends IDhApiUnsafeWrapper */ default int getMinHeight() { return 0; } - /** - * Will return null if called on the server, - * or if called before the renderer has been set up. - */ - IDhApiCustomRenderRegister getRenderRegister(); - } \ No newline at end of file diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiAfterRenderEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiAfterRenderEvent.java index 7bc6dab51..05da17b96 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiAfterRenderEvent.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiAfterRenderEvent.java @@ -26,20 +26,17 @@ import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhAp /** * Fired after Distant Horizons finishes rendering a frame.
* At this point DH will have also finished cleaning up any modifications it - * did to the OpenGL state, so the state should be back to Minecraft's defaults.

+ * did to the OpenGL state, so the state should be back to Minecraft's defaults. * - * Note: as of API v 3.0.0 no {@link DhApiRenderParam} is included in this event - * because the specific parameters may change - * depending on whether deferred rendering is enabled or not. - * * @author James Seibel - * @version 2024-7-14 + * @version 2024-1-31 + * @see DhApiRenderParam * @since API 1.0.0 */ -public abstract class DhApiAfterRenderEvent implements IDhApiEvent +public abstract class DhApiAfterRenderEvent implements IDhApiEvent { /** Fired after Distant Horizons finishes rendering fake chunks. */ - public abstract void afterRender(DhApiEventParam event); + public abstract void afterRender(DhApiEventParam event); //=========================// @@ -47,6 +44,6 @@ public abstract class DhApiAfterRenderEvent implements IDhApiEvent //=========================// @Override - public final void fireEvent(DhApiEventParam event) { this.afterRender(event); } + public final void fireEvent(DhApiEventParam event) { this.afterRender(event); } } \ No newline at end of file diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeBufferRenderEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeBufferRenderEvent.java index 01b8fcaa1..7716fda0c 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeBufferRenderEvent.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeBufferRenderEvent.java @@ -20,10 +20,9 @@ package com.seibel.distanthorizons.api.methods.events.abstractEvents; import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; -import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; +import com.seibel.distanthorizons.coreapi.util.math.Vec3f; /** * Called before Distant Horizons starts rendering a buffer.
@@ -53,29 +52,20 @@ public abstract class DhApiBeforeBufferRenderEvent implements IDhApiEvent. - */ - -package com.seibel.distanthorizons.api.methods.events.abstractEvents; - -import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; -import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper; -import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiCancelableEvent; -import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam; -import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiCancelableEventParam; -import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; - -/** - * Called before Distant Horizons starts rendering a generic object.
- * Canceling this event will prevent the triggering {@link IDhApiRenderableBoxGroup} from rendering this frame. - * - * @author James Seibel - * @version 2024-7-11 - * @since API 3.0.0 - */ -public abstract class DhApiBeforeGenericObjectRenderEvent implements IDhApiCancelableEvent -{ - /** Fired before Distant Horizons renders a generic object. */ - public abstract void beforeRender(DhApiCancelableEventParam event); - - - //=========================// - // internal DH API methods // - //=========================// - - @Override - public final void fireEvent(DhApiCancelableEventParam input) { this.beforeRender(input); } - - - //==================// - // parameter object // - //==================// - - public static class EventParam extends DhApiRenderParam implements IDhApiEventParam - { - public final long boxGroupId; - public final String resourceLocationNamespace; - public final String resourceLocationPath; - - - public EventParam( - DhApiRenderParam renderParam, - IDhApiRenderableBoxGroup boxGroup - ) - { - super(renderParam); - - this.boxGroupId = boxGroup.getId(); - this.resourceLocationNamespace = boxGroup.getResourceLocationNamespace(); - this.resourceLocationPath = boxGroup.getResourceLocationPath(); - } - public EventParam( - DhApiRenderParam renderParam, - long boxGroupId, String resourceLocationNamespace, String resourceLocationPath - ) - { - super(renderParam); - - this.boxGroupId = boxGroupId; - this.resourceLocationNamespace = resourceLocationNamespace; - this.resourceLocationPath = resourceLocationPath; - } - - - - @Override - public EventParam copy() - { - return new EventParam( - this, - this.boxGroupId, this.resourceLocationNamespace, this.resourceLocationPath - ); - } - } - -} \ No newline at end of file diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeGenericRenderCleanupEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeGenericRenderCleanupEvent.java deleted file mode 100644 index e9c6bfbd4..000000000 --- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeGenericRenderCleanupEvent.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.api.methods.events.abstractEvents; - -import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; -import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; -import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; - -/** - * Called before Distant Horizons starts the cleanup process done after rendering generic objects.
- * This is called after all generic objects have finished rendering. - * - * @author James Seibel - * @version 2024-7-13 - * @since API 3.0.0 - */ -public abstract class DhApiBeforeGenericRenderCleanupEvent implements IDhApiEvent -{ - /** Fired before Distant Horizons starts the cleanup process once rendering has finished. */ - public abstract void beforeCleanup(DhApiEventParam event); - - - //=========================// - // internal DH API methods // - //=========================// - - @Override - public final void fireEvent(DhApiEventParam event) { this.beforeCleanup(event); } - -} \ No newline at end of file diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeGenericRenderSetupEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeGenericRenderSetupEvent.java deleted file mode 100644 index 2dea2942c..000000000 --- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeGenericRenderSetupEvent.java +++ /dev/null @@ -1,50 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.api.methods.events.abstractEvents; - -import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; -import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; -import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; - -/** - * Called before Distant Horizons has started setting up OpenGL objects for rendering generic objects.
- * If you want to modify already bound DH OpenGL objects try using {@link DhApiBeforeGenericObjectRenderEvent}. - * - * @author James Seibel - * @version 2024-7-12 - * @since API 3.0.0 - * - * @see DhApiBeforeGenericObjectRenderEvent - */ -public abstract class DhApiBeforeGenericRenderSetupEvent implements IDhApiEvent -{ - /** Fired before Distant Horizons has started setting up OpenGL objects for rendering generic objects. */ - public abstract void beforeSetup(DhApiEventParam input); - - - //=========================// - // internal DH API methods // - //=========================// - - @Override - public final void fireEvent(DhApiEventParam input) { this.beforeSetup(input); } - - -} \ No newline at end of file diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeRenderCleanupEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeRenderCleanupEvent.java index 85533fa35..b8eb08568 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeRenderCleanupEvent.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeRenderCleanupEvent.java @@ -33,7 +33,7 @@ import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhAp */ public abstract class DhApiBeforeRenderCleanupEvent implements IDhApiEvent { - /** Fired before Distant Horizons starts the cleanup process once rendering has finished. */ + /** Fired before Distant Horizons renders LODs. */ public abstract void beforeCleanup(DhApiEventParam event); diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiChunkModifiedEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiChunkModifiedEvent.java index abc7bd0b7..6afe312cf 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiChunkModifiedEvent.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiChunkModifiedEvent.java @@ -22,7 +22,6 @@ package com.seibel.distanthorizons.api.methods.events.abstractEvents; import com.seibel.distanthorizons.api.interfaces.data.IDhApiTerrainDataRepo; import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper; import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; -import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; /** @@ -54,7 +53,7 @@ public abstract class DhApiChunkModifiedEvent implements IDhApiEvent> dataPoints; @@ -52,12 +52,12 @@ public class DhApiChunk // constructors // //==============// - public DhApiChunk(int chunkPosX, int chunkPosZ, int bottomYBlockPos, int topYBlockPos) + public DhApiChunk(int chunkPosX, int chunkPosZ, int topYBlockPos, int bottomYBlockPos) { this.chunkPosX = chunkPosX; this.chunkPosZ = chunkPosZ; - this.bottomYBlockPos = bottomYBlockPos; this.topYBlockPos = topYBlockPos; + this.bottomYBlockPos = bottomYBlockPos; // populate the array to prevent null pointers this.dataPoints = new ArrayList<>(16 * 16); // 256 diff --git a/api/src/main/java/com/seibel/distanthorizons/api/objects/data/DhApiRaycastResult.java b/api/src/main/java/com/seibel/distanthorizons/api/objects/data/DhApiRaycastResult.java index ad3f182d2..c54866bf0 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/objects/data/DhApiRaycastResult.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/objects/data/DhApiRaycastResult.java @@ -20,6 +20,7 @@ package com.seibel.distanthorizons.api.objects.data; import com.seibel.distanthorizons.api.objects.math.DhApiVec3i; +import com.seibel.distanthorizons.coreapi.util.math.Vec3i; /** * Holds a single datapoint of terrain data @@ -46,7 +47,7 @@ public class DhApiRaycastResult - public DhApiRaycastResult(DhApiTerrainDataPoint dataPoint, DhApiVec3i blockPos) + public DhApiRaycastResult(DhApiTerrainDataPoint dataPoint, Vec3i blockPos) { this.dataPoint = dataPoint; this.pos = blockPos; diff --git a/api/src/main/java/com/seibel/distanthorizons/api/objects/data/DhApiTerrainDataPoint.java b/api/src/main/java/com/seibel/distanthorizons/api/objects/data/DhApiTerrainDataPoint.java index ab2cb9f01..0c70dfbfb 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/objects/data/DhApiTerrainDataPoint.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/objects/data/DhApiTerrainDataPoint.java @@ -26,7 +26,7 @@ import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper; * Holds a single datapoint of terrain data. * * @author James Seibel - * @version 2024-7-20 + * @version 2022-11-13 * @since API 1.0.0 */ public class DhApiTerrainDataPoint @@ -42,22 +42,22 @@ public class DhApiTerrainDataPoint public final int blockLightLevel; public final int skyLightLevel; - public final int bottomYBlockPos; public final int topYBlockPos; + public final int bottomYBlockPos; public final IDhApiBlockStateWrapper blockStateWrapper; public final IDhApiBiomeWrapper biomeWrapper; - public DhApiTerrainDataPoint(byte detailLevel, int blockLightLevel, int skyLightLevel, int bottomYBlockPos, int topYBlockPos, IDhApiBlockStateWrapper blockStateWrapper, IDhApiBiomeWrapper biomeWrapper) + public DhApiTerrainDataPoint(byte detailLevel, int blockLightLevel, int skyLightLevel, int topYBlockPos, int bottomYBlockPos, IDhApiBlockStateWrapper blockStateWrapper, IDhApiBiomeWrapper biomeWrapper) { this.detailLevel = detailLevel; this.blockLightLevel = blockLightLevel; this.skyLightLevel = skyLightLevel; - this.bottomYBlockPos = bottomYBlockPos; this.topYBlockPos = topYBlockPos; + this.bottomYBlockPos = bottomYBlockPos; this.blockStateWrapper = blockStateWrapper; this.biomeWrapper = biomeWrapper; diff --git a/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiVec3d.java b/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiVec3d.java deleted file mode 100644 index 9e1344262..000000000 --- a/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiVec3d.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.api.objects.math; - -/** - * Often used to store block positions or any other - * position in 3D space. - * - * @author James Seibel - * @version 2024-7-9 - * @since API 3.0.0 - */ -public class DhApiVec3d -{ - public double x; - public double y; - public double z; - - - - /** creates a Vec3 at (0,0,0) */ - public DhApiVec3d() - { - this.x = 0; - this.y = 0; - this.z = 0; - } - - public DhApiVec3d(double x, double y, double z) - { - this.x = x; - this.y = y; - this.z = z; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - else if (obj != null && this.getClass() == obj.getClass()) - { - DhApiVec3d Vec3d = (DhApiVec3d) obj; - if (Double.compare(Vec3d.x, this.x) != 0) - { - return false; - } - else if (Double.compare(Vec3d.y, this.y) != 0) - { - return false; - } - else - { - return Double.compare(Vec3d.z, this.z) == 0; - } - } - else - { - return false; - } - } - - @Override - public int hashCode() - { - long i = Double.doubleToLongBits(this.x); - i = 31 * i + Double.doubleToLongBits(this.y); - i = 31 * i + Double.doubleToLongBits(this.z); - return Long.hashCode(i); - } - - @Override - public String toString() { return "[" + this.x + ", " + this.y + ", " + this.z + "]"; } - -} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiVec3f.java b/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiVec3f.java deleted file mode 100644 index 8120b149a..000000000 --- a/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiVec3f.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.api.objects.math; - -import com.seibel.distanthorizons.api.interfaces.util.IDhApiCopyable; - -/** - * Often used to store block positions or any other - * position in 3D space. - * - * @author James Seibel - * @version 2024-6-3 - * @since API 2.2.0 - */ -public class DhApiVec3f implements IDhApiCopyable -{ - public float x; - public float y; - public float z; - - - - /** creates a Vec3 at (0,0,0) */ - public DhApiVec3f() - { - this.x = 0; - this.y = 0; - this.z = 0; - } - - public DhApiVec3f(float x, float y, float z) - { - this.x = x; - this.y = y; - this.z = z; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - else if (obj != null && this.getClass() == obj.getClass()) - { - DhApiVec3f Vec3f = (DhApiVec3f) obj; - if (Float.compare(Vec3f.x, this.x) != 0) - { - return false; - } - else if (Float.compare(Vec3f.y, this.y) != 0) - { - return false; - } - else - { - return Float.compare(Vec3f.z, this.z) == 0; - } - } - else - { - return false; - } - } - - @Override - public int hashCode() - { - int i = Float.floatToIntBits(this.x); - i = 31 * i + Float.floatToIntBits(this.y); - return 31 * i + Float.floatToIntBits(this.z); - } - - @Override - public String toString() { return "[" + this.x + ", " + this.y + ", " + this.z + "]"; } - - @Override - public DhApiVec3f copy() { return new DhApiVec3f(this.x, this.y, this.z); } - -} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBox.java b/api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBox.java deleted file mode 100644 index d3411dc15..000000000 --- a/api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBox.java +++ /dev/null @@ -1,52 +0,0 @@ -package com.seibel.distanthorizons.api.objects.render; - - -import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial; -import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; - -import java.awt.*; - -/** - * @see IDhApiRenderableBoxGroup - * - * @author James Seibel - * @version 2024-6-30 - * @since API 3.0.0 - */ -public class DhApiRenderableBox -{ - /** the position closest to (-inf,-inf) */ - public DhApiVec3d minPos; - /** the position closest to (+inf,+inf) */ - public DhApiVec3d maxPos; - - public Color color; - public byte material; - - - - //==============// - // constructors // - //==============// - - public DhApiRenderableBox(DhApiVec3d minPos, float width, Color color, EDhApiBlockMaterial material) - { - this(minPos, new DhApiVec3d( - minPos.x + width, - minPos.y + width, - minPos.z + width - ), color, material); - } - - public DhApiRenderableBox(DhApiVec3d minPos, DhApiVec3d maxPos, Color color, EDhApiBlockMaterial material) - { - this.minPos = minPos; - this.maxPos = maxPos; - this.color = color; - this.material = material.index; - } - -} - diff --git a/api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBoxGroupShading.java b/api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBoxGroupShading.java deleted file mode 100644 index 396f73bc5..000000000 --- a/api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBoxGroupShading.java +++ /dev/null @@ -1,93 +0,0 @@ -package com.seibel.distanthorizons.api.objects.render; - - -import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; - -import java.awt.*; - -/** - * @see IDhApiRenderableBoxGroup - * - * Shading values are multiplied against the color for each direction, - * for example:
- * A shading value of 1.0 indicates the color is unchanged.
- * A shading value of 0.0 changes the color to black.
- * - * @author James Seibel - * @version 2024-7-7 - * @since API 3.0.0 - */ -public class DhApiRenderableBoxGroupShading -{ - /** negative X */ - public float north = 1.0f; - /** positive X */ - public float south = 1.0f; - - /** positive X */ - public float east = 1.0f; - /** negative X */ - public float west = 1.0f; - - /** positive Y */ - public float top = 1.0f; - /** negative Y */ - public float bottom = 1.0f; - - - - //==============// - // constructors // - //==============// - - public static DhApiRenderableBoxGroupShading getDefaultShaded() - { - DhApiRenderableBoxGroupShading shading = new DhApiRenderableBoxGroupShading(); - shading.setDefaultShaded(); - return shading; - } - - public static DhApiRenderableBoxGroupShading getUnshaded() - { - DhApiRenderableBoxGroupShading shading = new DhApiRenderableBoxGroupShading(); - shading.setUnshaded(); - return shading; - } - - - - //=========// - // methods // - //=========// - - /** - * Directions will have different brightness similar to Minecraft blocks.
- * This is a good default for un-lit objects. - */ - public void setDefaultShaded() - { - this.north = 0.8f; - this.south = 0.8f; - this.east = 0.6f; - this.west = 0.6f; - this.top = 1.0f; - this.bottom = 0.5f; - } - - /** - * All directions render with the same brightness.
- * This is best used for glowing objects like beacons. - */ - public void setUnshaded() - { - this.north = 1.0f; - this.south = 1.0f; - this.east = 1.0f; - this.west = 1.0f; - this.top = 1.0f; - this.bottom = 1.0f; - } - -} - diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/DependencyInjection/ApiEventInjector.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/DependencyInjection/ApiEventInjector.java index 386ab2aef..e6d49f611 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/DependencyInjection/ApiEventInjector.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/DependencyInjection/ApiEventInjector.java @@ -22,7 +22,6 @@ package com.seibel.distanthorizons.coreapi.DependencyInjection; import com.seibel.distanthorizons.api.interfaces.events.IDhApiEventInjector; import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiCancelableEvent; import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; -import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam; import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiOneTimeEvent; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiCancelableEventParam; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; @@ -141,26 +140,7 @@ public class ApiEventInjector extends DependencyInjector implements // fire each event and record if any of them // request to cancel the event. - - // attempt to clone the event input if possible - // this is done to reduce the likely hood that one event listener - // will make change the event parameter for other listeners - T input = eventInput; - if (eventInput instanceof IDhApiEventParam) - { - try - { - //noinspection unchecked - input = (T) ((IDhApiEventParam) eventInput).copy(); - } - catch (Exception e) - { - LOGGER.error("Unable to clone event parameter ["+eventInput.getClass().getSimpleName()+"], error: ["+e.getMessage()+"].", e); - } - } - - - DhApiEventParam eventParam = createEventParamWrapper(event, input); + DhApiEventParam eventParam = createEventParamWrapper(event, eventInput); event.fireEvent(eventParam); if (eventParam instanceof DhApiCancelableEventParam) diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java index 526c5e10e..a382dca0d 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java @@ -47,9 +47,9 @@ public final class ModInfo public static boolean IS_DEV_BUILD = VERSION.toLowerCase().contains("dev"); /** This version should only be updated when breaking changes are introduced to the DH API */ - public static final int API_MAJOR_VERSION = 3; + public static final int API_MAJOR_VERSION = 2; /** This version should be updated whenever new methods are added to the DH API */ - public static final int API_MINOR_VERSION = 0; + public static final int API_MINOR_VERSION = 1; /** This version should be updated whenever non-breaking fixes are added to the DH API */ public static final int API_PATH_VERSION = 0; diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/interfaces/config/IConfigEntry.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/interfaces/config/IConfigEntry.java index 3297a1a26..a31745a8e 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/interfaces/config/IConfigEntry.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/interfaces/config/IConfigEntry.java @@ -67,11 +67,10 @@ public interface IConfigEntry * Checks if the option is valid * * 0 == valid - * 2 == invalid * 1 == number too high * -1 == number too low */ - byte isValid(); // TODO replace with an enum + byte isValid(); /** Checks if a value is valid */ byte isValid(T value); diff --git a/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiMat4f.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Mat4f.java similarity index 57% rename from api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiMat4f.java rename to api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Mat4f.java index 2c05ae708..9d24e9f41 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiMat4f.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Mat4f.java @@ -17,55 +17,46 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.api.objects.math; +package com.seibel.distanthorizons.coreapi.util.math; -import com.seibel.distanthorizons.api.interfaces.util.IDhApiCopyable; +import org.joml.Matrix4f; +import org.joml.Matrix4fc; + +import java.nio.FloatBuffer; /** * An (almost) exact copy of Minecraft's 1.16.5 - * implementation of a 4x4 float matrix.

+ * implementation of a 4x4 float matrix. * - * - * m00, m10, m20, m30,
- * m01, m11, m21, m31,
- * m02, m12, m22, m32,
- * m03, m13, m23, m33
- *
- * * @author James Seibel - * @version 2024-6-30 + * @version 11-11-2021 */ -public class DhApiMat4f implements IDhApiCopyable +public class Mat4f { - public float m00; - public float m01; - public float m02; - public float m03; - - public float m10; - public float m11; - public float m12; - public float m13; - - public float m20; - public float m21; - public float m22; - public float m23; - - public float m30; - public float m31; - public float m32; - public float m33; + private float m00; + private float m01; + private float m02; + private float m03; + private float m10; + private float m11; + private float m12; + private float m13; + private float m20; + private float m21; + private float m22; + private float m23; + private float m30; + private float m31; + private float m32; + private float m33; + public Mat4f() + { + + } - //==============// - // constructors // - //==============// - - public DhApiMat4f() { /* all values are 0 */ } - - public DhApiMat4f(DhApiMat4f sourceMatrix) + public Mat4f(Mat4f sourceMatrix) { this.m00 = sourceMatrix.m00; this.m01 = sourceMatrix.m01; @@ -85,36 +76,163 @@ public class DhApiMat4f implements IDhApiCopyable this.m33 = sourceMatrix.m33; } - /** Expects the values of the input array to be in row major order (AKA rows then columns) */ - public DhApiMat4f(float[] values) + public Mat4f(Matrix4fc sourceMatrix) { this(convertJomlMatrixToArray(sourceMatrix)); } + private static float[] convertJomlMatrixToArray(Matrix4fc sourceMatrix) { - m00 = values[0]; - m01 = values[1]; - m02 = values[2]; - m03 = values[3]; + FloatBuffer buffer = FloatBuffer.allocate(16); - m10 = values[4]; - m11 = values[5]; - m12 = values[6]; - m13 = values[7]; + buffer.put(bufferIndex(0, 0), sourceMatrix.m00()); + buffer.put(bufferIndex(0, 1), sourceMatrix.m01()); + buffer.put(bufferIndex(0, 2), sourceMatrix.m02()); + buffer.put(bufferIndex(0, 3), sourceMatrix.m03()); + buffer.put(bufferIndex(1, 0), sourceMatrix.m10()); + buffer.put(bufferIndex(1, 1), sourceMatrix.m11()); + buffer.put(bufferIndex(1, 2), sourceMatrix.m12()); + buffer.put(bufferIndex(1, 3), sourceMatrix.m13()); + buffer.put(bufferIndex(2, 0), sourceMatrix.m20()); + buffer.put(bufferIndex(2, 1), sourceMatrix.m21()); + buffer.put(bufferIndex(2, 2), sourceMatrix.m22()); + buffer.put(bufferIndex(2, 3), sourceMatrix.m23()); + buffer.put(bufferIndex(3, 0), sourceMatrix.m30()); + buffer.put(bufferIndex(3, 1), sourceMatrix.m31()); + buffer.put(bufferIndex(3, 2), sourceMatrix.m32()); + buffer.put(bufferIndex(3, 3), sourceMatrix.m33()); - m20 = values[8]; - m21 = values[9]; - m22 = values[10]; - m23 = values[11]; - - m30 = values[12]; - m31 = values[13]; - m32 = values[14]; - m33 = values[15]; + return buffer.array(); + } + + /* Quaternions are not currently needed/implemented + public Matrix4float(Quaternion p_i48104_1_) + { + float f = p_i48104_1_.i(); + float f1 = p_i48104_1_.j(); + float f2 = p_i48104_1_.k(); + float f3 = p_i48104_1_.r(); + float f4 = 2.0F * f * f; + float f5 = 2.0F * f1 * f1; + float f6 = 2.0F * f2 * f2; + this.m00 = 1.0F - f5 - f6; + this.m11 = 1.0F - f6 - f4; + this.m22 = 1.0F - f4 - f5; + this.m33 = 1.0F; + float f7 = f * f1; + float f8 = f1 * f2; + float f9 = f2 * f; + float f10 = f * f3; + float f11 = f1 * f3; + float f12 = f2 * f3; + this.m10 = 2.0F * (f7 + f12); + this.m01 = 2.0F * (f7 - f12); + this.m20 = 2.0F * (f9 - f11); + this.m02 = 2.0F * (f9 + f11); + this.m21 = 2.0F * (f8 + f10); + this.m12 = 2.0F * (f8 - f10); + } + */ + + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else if (obj != null && this.getClass() == obj.getClass()) + { + Mat4f otherMatrix = (Mat4f) obj; + return Float.compare(otherMatrix.m00, this.m00) == 0 + && Float.compare(otherMatrix.m01, this.m01) == 0 + && Float.compare(otherMatrix.m02, this.m02) == 0 + && Float.compare(otherMatrix.m03, this.m03) == 0 + && Float.compare(otherMatrix.m10, this.m10) == 0 + && Float.compare(otherMatrix.m11, this.m11) == 0 + && Float.compare(otherMatrix.m12, this.m12) == 0 + && Float.compare(otherMatrix.m13, this.m13) == 0 + && Float.compare(otherMatrix.m20, this.m20) == 0 + && Float.compare(otherMatrix.m21, this.m21) == 0 + && Float.compare(otherMatrix.m22, this.m22) == 0 + && Float.compare(otherMatrix.m23, this.m23) == 0 + && Float.compare(otherMatrix.m30, this.m30) == 0 + && Float.compare(otherMatrix.m31, this.m31) == 0 + && Float.compare(otherMatrix.m32, this.m32) == 0 + && Float.compare(otherMatrix.m33, this.m33) == 0; + } + else + { + return false; + } + } + + @Override + public int hashCode() + { + int i = this.m00 != 0.0F ? Float.floatToIntBits(this.m00) : 0; + i = 31 * i + (this.m01 != 0.0F ? Float.floatToIntBits(this.m01) : 0); + i = 31 * i + (this.m02 != 0.0F ? Float.floatToIntBits(this.m02) : 0); + i = 31 * i + (this.m03 != 0.0F ? Float.floatToIntBits(this.m03) : 0); + i = 31 * i + (this.m10 != 0.0F ? Float.floatToIntBits(this.m10) : 0); + i = 31 * i + (this.m11 != 0.0F ? Float.floatToIntBits(this.m11) : 0); + i = 31 * i + (this.m12 != 0.0F ? Float.floatToIntBits(this.m12) : 0); + i = 31 * i + (this.m13 != 0.0F ? Float.floatToIntBits(this.m13) : 0); + i = 31 * i + (this.m20 != 0.0F ? Float.floatToIntBits(this.m20) : 0); + i = 31 * i + (this.m21 != 0.0F ? Float.floatToIntBits(this.m21) : 0); + i = 31 * i + (this.m22 != 0.0F ? Float.floatToIntBits(this.m22) : 0); + i = 31 * i + (this.m23 != 0.0F ? Float.floatToIntBits(this.m23) : 0); + i = 31 * i + (this.m30 != 0.0F ? Float.floatToIntBits(this.m30) : 0); + i = 31 * i + (this.m31 != 0.0F ? Float.floatToIntBits(this.m31) : 0); + i = 31 * i + (this.m32 != 0.0F ? Float.floatToIntBits(this.m32) : 0); + return 31 * i + (this.m33 != 0.0F ? Float.floatToIntBits(this.m33) : 0); } + @Override + public String toString() + { + return "Matrix4f:\n" + + this.m00 + " " + this.m01 + " " + this.m02 + " " + this.m03 + "\n" + + this.m10 + " " + this.m11 + " " + this.m12 + " " + this.m13 + "\n" + + this.m20 + " " + this.m21 + " " + this.m22 + " " + this.m23 + "\n" + + this.m30 + " " + this.m31 + " " + this.m32 + " " + this.m33 + "\n"; + } - //=========// - // methods // - //=========// + public void store(FloatBuffer floatBuffer) + { + floatBuffer.put(bufferIndex(0, 0), this.m00); + floatBuffer.put(bufferIndex(0, 1), this.m01); + floatBuffer.put(bufferIndex(0, 2), this.m02); + floatBuffer.put(bufferIndex(0, 3), this.m03); + floatBuffer.put(bufferIndex(1, 0), this.m10); + floatBuffer.put(bufferIndex(1, 1), this.m11); + floatBuffer.put(bufferIndex(1, 2), this.m12); + floatBuffer.put(bufferIndex(1, 3), this.m13); + floatBuffer.put(bufferIndex(2, 0), this.m20); + floatBuffer.put(bufferIndex(2, 1), this.m21); + floatBuffer.put(bufferIndex(2, 2), this.m22); + floatBuffer.put(bufferIndex(2, 3), this.m23); + floatBuffer.put(bufferIndex(3, 0), this.m30); + floatBuffer.put(bufferIndex(3, 1), this.m31); + floatBuffer.put(bufferIndex(3, 2), this.m32); + floatBuffer.put(bufferIndex(3, 3), this.m33); + } + + public Matrix4f createJomlMatrix() + { + return new Matrix4f( + this.m00, this.m10, this.m20, this.m30, + this.m01, this.m11, this.m21, this.m31, + this.m02, this.m12, this.m22, this.m32, + this.m03, this.m13, this.m23, this.m33 + ); + } + + + private static int bufferIndex(int xIndex, int zIndex) + { + return (zIndex * 4) + xIndex; + } + public void setIdentity() { @@ -223,7 +341,7 @@ public class DhApiMat4f implements IDhApiCopyable } } - public void multiply(DhApiMat4f multMatrix) + public void multiply(Mat4f multMatrix) { float f = this.m00 * multMatrix.m00 + this.m01 * multMatrix.m10 + this.m02 * multMatrix.m20 + this.m03 * multMatrix.m30; float f1 = this.m00 * multMatrix.m01 + this.m01 * multMatrix.m11 + this.m02 * multMatrix.m21 + this.m03 * multMatrix.m31; @@ -259,6 +377,13 @@ public class DhApiMat4f implements IDhApiCopyable this.m33 = f15; } + /* Quaternions aren't currently needed/implemented + public void multiply(Quaternion p_226596_1_) + { + this.multiply(new Matrix4f(p_226596_1_)); + } + */ + public void multiply(float scalar) { this.m00 *= scalar; @@ -279,6 +404,81 @@ public class DhApiMat4f implements IDhApiCopyable this.m33 *= scalar; } + public static Mat4f perspective(double fov, float widthHeightRatio, float nearClipPlane, float farClipPlane) + { + float f = (float) (1.0D / Math.tan(fov * ((float) Math.PI / 180F) / 2.0D)); + Mat4f matrix = new Mat4f(); + matrix.m00 = f / widthHeightRatio; + matrix.m11 = f; + matrix.m22 = (farClipPlane + nearClipPlane) / (nearClipPlane - farClipPlane); + matrix.m32 = -1.0F; + matrix.m23 = 2.0F * farClipPlane * nearClipPlane / (nearClipPlane - farClipPlane); + return matrix; + } + + + /* not currently needed/implemented + * Also the parameter names should be double checked as they may be incorrect + public static Matrix4Float orthographic(float left, float right, float top, float bottom) + { + Matrix4Float matrix4f = new Matrix4Float(); + matrix4f.m00 = 2.0F / left; + matrix4f.m11 = 2.0F / right; + float f = bottom - top; + matrix4f.m22 = -2.0F / f; + matrix4f.m33 = 1.0F; + matrix4f.m03 = -1.0F; + matrix4f.m13 = -1.0F; + matrix4f.m23 = -(bottom + top) / f; + return matrix4f; + } + */ + + /** + * TODO: what kind of translation is this? + * and how is this different from "multiplyTranslationMatrix"? + * Answer: This is faster and direct (but only if this is pure translation matrix without rotate) + */ + public void translate(Vec3f vec) + { + this.m03 += vec.x; + this.m13 += vec.y; + this.m23 += vec.z; + } + + /** originally "translate" from Minecraft's MatrixStack */ + public void multiplyTranslationMatrix(double x, double y, double z) + { + multiply(createTranslateMatrix((float) x, (float) y, (float) z)); + } + + public Mat4f copy() + { + return new Mat4f(this); + } + + public static Mat4f createScaleMatrix(float x, float y, float z) + { + Mat4f matrix = new Mat4f(); + matrix.m00 = x; + matrix.m11 = y; + matrix.m22 = z; + matrix.m33 = 1.0F; + return matrix; + } + + public static Mat4f createTranslateMatrix(float x, float y, float z) + { + Mat4f matrix = new Mat4f(); + matrix.m00 = 1.0F; + matrix.m11 = 1.0F; + matrix.m22 = 1.0F; + matrix.m33 = 1.0F; + matrix.m03 = x; + matrix.m13 = y; + matrix.m23 = z; + return matrix; + } @@ -287,8 +487,6 @@ public class DhApiMat4f implements IDhApiCopyable // methods // //==================// - private static int getArrayIndex(int xIndex, int zIndex) { return (zIndex * 4) + xIndex; } - /** Returns the values of this matrix in row major order (AKA rows then columns) */ public float[] getValuesAsArray() { @@ -315,77 +513,113 @@ public class DhApiMat4f implements IDhApiCopyable }; } - - - //================// - // base overrides // - //================// - - @Override - public boolean equals(Object obj) + public Vec3f asNonNormalizedLookForwardVector() { - if (this == obj) - { - return true; - } - else if (obj != null && this.getClass() == obj.getClass()) - { - DhApiMat4f otherMatrix = (DhApiMat4f) obj; - return Float.compare(otherMatrix.m00, this.m00) == 0 - && Float.compare(otherMatrix.m01, this.m01) == 0 - && Float.compare(otherMatrix.m02, this.m02) == 0 - && Float.compare(otherMatrix.m03, this.m03) == 0 - && Float.compare(otherMatrix.m10, this.m10) == 0 - && Float.compare(otherMatrix.m11, this.m11) == 0 - && Float.compare(otherMatrix.m12, this.m12) == 0 - && Float.compare(otherMatrix.m13, this.m13) == 0 - && Float.compare(otherMatrix.m20, this.m20) == 0 - && Float.compare(otherMatrix.m21, this.m21) == 0 - && Float.compare(otherMatrix.m22, this.m22) == 0 - && Float.compare(otherMatrix.m23, this.m23) == 0 - && Float.compare(otherMatrix.m30, this.m30) == 0 - && Float.compare(otherMatrix.m31, this.m31) == 0 - && Float.compare(otherMatrix.m32, this.m32) == 0 - && Float.compare(otherMatrix.m33, this.m33) == 0; - } - else - { - return false; - } + return new Vec3f(this.m02, this.m12, this.m22); } - @Override - public int hashCode() + //===============// + // Forge methods // + //===============// + + public Mat4f(float[] values) { - int i = this.m00 != 0.0F ? Float.floatToIntBits(this.m00) : 0; - i = 31 * i + (this.m01 != 0.0F ? Float.floatToIntBits(this.m01) : 0); - i = 31 * i + (this.m02 != 0.0F ? Float.floatToIntBits(this.m02) : 0); - i = 31 * i + (this.m03 != 0.0F ? Float.floatToIntBits(this.m03) : 0); - i = 31 * i + (this.m10 != 0.0F ? Float.floatToIntBits(this.m10) : 0); - i = 31 * i + (this.m11 != 0.0F ? Float.floatToIntBits(this.m11) : 0); - i = 31 * i + (this.m12 != 0.0F ? Float.floatToIntBits(this.m12) : 0); - i = 31 * i + (this.m13 != 0.0F ? Float.floatToIntBits(this.m13) : 0); - i = 31 * i + (this.m20 != 0.0F ? Float.floatToIntBits(this.m20) : 0); - i = 31 * i + (this.m21 != 0.0F ? Float.floatToIntBits(this.m21) : 0); - i = 31 * i + (this.m22 != 0.0F ? Float.floatToIntBits(this.m22) : 0); - i = 31 * i + (this.m23 != 0.0F ? Float.floatToIntBits(this.m23) : 0); - i = 31 * i + (this.m30 != 0.0F ? Float.floatToIntBits(this.m30) : 0); - i = 31 * i + (this.m31 != 0.0F ? Float.floatToIntBits(this.m31) : 0); - i = 31 * i + (this.m32 != 0.0F ? Float.floatToIntBits(this.m32) : 0); - return 31 * i + (this.m33 != 0.0F ? Float.floatToIntBits(this.m33) : 0); + m00 = values[0]; + m01 = values[1]; + m02 = values[2]; + m03 = values[3]; + m10 = values[4]; + m11 = values[5]; + m12 = values[6]; + m13 = values[7]; + m20 = values[8]; + m21 = values[9]; + m22 = values[10]; + m23 = values[11]; + m30 = values[12]; + m31 = values[13]; + m32 = values[14]; + m33 = values[15]; } - @Override - public String toString() + public Mat4f(FloatBuffer buffer) { - return "Matrix4f:\n" + - this.m00 + " " + this.m01 + " " + this.m02 + " " + this.m03 + "\n" + - this.m10 + " " + this.m11 + " " + this.m12 + " " + this.m13 + "\n" + - this.m20 + " " + this.m21 + " " + this.m22 + " " + this.m23 + "\n" + - this.m30 + " " + this.m31 + " " + this.m32 + " " + this.m33 + "\n"; + this(buffer.array()); } - @Override - public DhApiMat4f copy() { return new DhApiMat4f(this); } + public void set(Mat4f mat) + { + this.m00 = mat.m00; + this.m01 = mat.m01; + this.m02 = mat.m02; + this.m03 = mat.m03; + this.m10 = mat.m10; + this.m11 = mat.m11; + this.m12 = mat.m12; + this.m13 = mat.m13; + this.m20 = mat.m20; + this.m21 = mat.m21; + this.m22 = mat.m22; + this.m23 = mat.m23; + this.m30 = mat.m30; + this.m31 = mat.m31; + this.m32 = mat.m32; + this.m33 = mat.m33; + } + + public void add(Mat4f other) + { + m00 += other.m00; + m01 += other.m01; + m02 += other.m02; + m03 += other.m03; + m10 += other.m10; + m11 += other.m11; + m12 += other.m12; + m13 += other.m13; + m20 += other.m20; + m21 += other.m21; + m22 += other.m22; + m23 += other.m23; + m30 += other.m30; + m31 += other.m31; + m32 += other.m32; + m33 += other.m33; + } + + public void multiplyBackward(Mat4f other) + { + Mat4f copy = other.copy(); + copy.multiply(this); + this.set(copy); + } + + public void setTranslation(float x, float y, float z) + { + this.m00 = 1.0F; + this.m11 = 1.0F; + this.m22 = 1.0F; + this.m33 = 1.0F; + this.m03 = x; + this.m13 = y; + this.m23 = z; + } + + /** + * Changes the values that store the clipping planes. + * Formula for calculating matrix values is the same that OpenGL uses when making matrices. + * + * @param nearClip New near clipping plane value. + * @param farClip New far clipping plane value. + */ + public void setClipPlanes(float nearClip, float farClip) + { + //convert to matrix values, formula copied from a textbook / openGL specification. + float matNearClip = -((farClip + nearClip) / (farClip - nearClip)); + float matFarClip = -((2 * farClip * nearClip) / (farClip - nearClip)); + //set new values for the clip planes. + this.m22 = matNearClip; + this.m23 = matFarClip; + } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/math/Vec3d.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Vec3d.java similarity index 50% rename from core/src/main/java/com/seibel/distanthorizons/core/util/math/Vec3d.java rename to api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Vec3d.java index 57911612f..a1374d8de 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/math/Vec3d.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Vec3d.java @@ -17,9 +17,8 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.util.math; +package com.seibel.distanthorizons.coreapi.util.math; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; import com.seibel.distanthorizons.coreapi.util.MathUtil; /** @@ -30,7 +29,7 @@ import com.seibel.distanthorizons.coreapi.util.MathUtil; * @author James Seibel * @version 11-18-2021 */ -public class Vec3d extends DhApiVec3d +public class Vec3d { public static Vec3d XNeg = new Vec3d(-1.0F, 0.0F, 0.0F); public static Vec3d XPos = new Vec3d(1.0F, 0.0F, 0.0F); @@ -41,13 +40,16 @@ public class Vec3d extends DhApiVec3d public static final Vec3d ZERO_VECTOR = new Vec3d(0.0D, 0.0D, 0.0D); + public double x; + public double y; + public double z; - //==============// - // constructors // - //==============// - public Vec3d() { } + public Vec3d() + { + + } public Vec3d(double x, double y, double z) { @@ -56,11 +58,47 @@ public class Vec3d extends DhApiVec3d this.z = z; } + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else if (obj != null && this.getClass() == obj.getClass()) + { + Vec3d Vec3f = (Vec3d) obj; + if (Double.compare(Vec3f.x, this.x) != 0) + { + return false; + } + else if (Double.compare(Vec3f.y, this.y) != 0) + { + return false; + } + else + { + return Double.compare(Vec3f.z, this.z) == 0; + } + } + else + { + return false; + } + } - - //=========// - // methods // - //=========// + @Override + public int hashCode() + { + long longVal = Double.doubleToLongBits(this.x); + + int intVal = (int) (longVal ^ longVal >>> 32); + longVal = Double.doubleToLongBits(this.y); + intVal = 31 * intVal + (int) (longVal ^ longVal >>> 32); + longVal = Double.doubleToLongBits(this.z); + + return 31 * intVal + (int) (longVal ^ longVal >>> 32); + } public void mul(double scalar) { @@ -111,7 +149,10 @@ public class Vec3d extends DhApiVec3d this.z -= vector.z; } - public double dotProduct(Vec3d vector) { return this.x * vector.x + this.y * vector.y + this.z * vector.z; } + public double dotProduct(Vec3d vector) + { + return this.x * vector.x + this.y * vector.y + this.z * vector.z; + } public Vec3d normalize() { @@ -132,10 +173,80 @@ public class Vec3d extends DhApiVec3d this.z = f * f4 - f1 * f3; } - public Vec3d copy() { return new Vec3d(this.x, this.y, this.z); } + /* Matrix3f is not currently needed/implemented + public void transform(Matrix3f p_229188_1_) + { + double f = this.x; + double f1 = this.y; + double f2 = this.z; + this.x = p_229188_1_.m00 * f + p_229188_1_.m01 * f1 + p_229188_1_.m02 * f2; + this.y = p_229188_1_.m10 * f + p_229188_1_.m11 * f1 + p_229188_1_.m12 * f2; + this.z = p_229188_1_.m20 * f + p_229188_1_.m21 * f1 + p_229188_1_.m22 * f2; + } + */ + + /* Quaternions are not currently needed/implemented + public void transform(Quaternion p_214905_1_) + { + Quaternion quaternion = new Quaternion(p_214905_1_); + quaternion.mul(new Quaternion(this.x(), this.y(), this.z(), 0.0F)); + Quaternion quaternion1 = new Quaternion(p_214905_1_); + quaternion1.conj(); + quaternion.mul(quaternion1); + this.set(quaternion.i(), quaternion.j(), quaternion.k()); + } + */ + + /* not currently needed + * percent may actually be partial ticks (which is available when rendering) + public void linearInterp(Vec3f resultingVector, double percent) + { + double f = 1.0F - percent; + this.x = this.x * f + resultingVector.x * percent; + this.y = this.y * f + resultingVector.y * percent; + this.z = this.z * f + resultingVector.z * percent; + } + */ + + /* Quaternions are not currently needed/implemented + public Quaternion rotation(double p_229193_1_) + { + return new Quaternion(this, p_229193_1_, false); + } + + + @OnlyIn(Dist.CLIENT) + public Quaternion rotationDegrees(double p_229187_1_) + { + return new Quaternion(this, p_229187_1_, true); + } + */ + + public Vec3d copy() + { + return new Vec3d(this.x, this.y, this.z); + } + + /* not currently needed/implemented + public void map(double2doubleFunction p_229191_1_) + { + this.x = p_229191_1_.get(this.x); + this.y = p_229191_1_.get(this.y); + this.z = p_229191_1_.get(this.z); + } + */ + + @Override + public String toString() + { + return "[" + this.x + ", " + this.y + ", " + this.z + "]"; + } // Forge start - public Vec3d(double[] values) { this.set(values); } + public Vec3d(double[] values) + { + set(values); + } public void set(double[] values) { diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Vec3f.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Vec3f.java new file mode 100644 index 000000000..b45ba98be --- /dev/null +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Vec3f.java @@ -0,0 +1,263 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.coreapi.util.math; + +import com.seibel.distanthorizons.coreapi.util.MathUtil; + +/** + * A (almost) exact copy of Minecraft's 1.16.5 + * implementation of a 3 element float vector. + * + * @author James Seibel + * @version 11-11-2021 + */ +public class Vec3f +{ + public static Vec3f XNeg = new Vec3f(-1.0F, 0.0F, 0.0F); + public static Vec3f XPos = new Vec3f(1.0F, 0.0F, 0.0F); + public static Vec3f YNeg = new Vec3f(0.0F, -1.0F, 0.0F); + public static Vec3f YPos = new Vec3f(0.0F, 1.0F, 0.0F); + public static Vec3f ZNeg = new Vec3f(0.0F, 0.0F, -1.0F); + public static Vec3f ZPos = new Vec3f(0.0F, 0.0F, 1.0F); + + + public float x; + public float y; + public float z; + + + + public Vec3f() + { + + } + + public Vec3f(float x, float y, float z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else if (obj != null && this.getClass() == obj.getClass()) + { + Vec3f Vec3f = (Vec3f) obj; + if (Float.compare(Vec3f.x, this.x) != 0) + { + return false; + } + else if (Float.compare(Vec3f.y, this.y) != 0) + { + return false; + } + else + { + return Float.compare(Vec3f.z, this.z) == 0; + } + } + else + { + return false; + } + } + + @Override + public int hashCode() + { + int i = Float.floatToIntBits(this.x); + i = 31 * i + Float.floatToIntBits(this.y); + return 31 * i + Float.floatToIntBits(this.z); + } + + public void mul(float scalar) + { + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + } + + public void mul(float x, float y, float z) + { + this.x *= x; + this.y *= y; + this.z *= z; + } + + public void clamp(float min, float max) + { + this.x = MathUtil.clamp(min, this.x, max); + this.y = MathUtil.clamp(min, this.y, max); + this.z = MathUtil.clamp(min, this.z, max); + } + + public void set(float x, float y, float z) + { + this.x = x; + this.y = y; + this.z = z; + } + + public void add(float x, float y, float z) + { + this.x += x; + this.y += y; + this.z += z; + } + + public void add(Vec3f vector) + { + this.x += vector.x; + this.y += vector.y; + this.z += vector.z; + } + + public void subtract(Vec3f vector) + { + this.x -= vector.x; + this.y -= vector.y; + this.z -= vector.z; + } + + public float dotProduct(Vec3f vector) + { + return this.x * vector.x + this.y * vector.y + this.z * vector.z; + } + + /** Returns true if normalization had to be done */ + public boolean normalize() + { + float squaredSum = this.x * this.x + this.y * this.y + this.z * this.z; + if (squaredSum < 1.0E-5D) + { + return false; + } + else + { + float f1 = MathUtil.fastInvSqrt(squaredSum); + this.x *= f1; + this.y *= f1; + this.z *= f1; + return true; + } + } + + public void crossProduct(Vec3f vector) + { + float f = this.x; + float f1 = this.y; + float f2 = this.z; + float f3 = vector.x; + float f4 = vector.y; + float f5 = vector.z; + this.x = f1 * f5 - f2 * f4; + this.y = f2 * f3 - f * f5; + this.z = f * f4 - f1 * f3; + } + + /* Matrix3f is not currently needed/implemented + public void transform(Matrix3f p_229188_1_) + { + float f = this.x; + float f1 = this.y; + float f2 = this.z; + this.x = p_229188_1_.m00 * f + p_229188_1_.m01 * f1 + p_229188_1_.m02 * f2; + this.y = p_229188_1_.m10 * f + p_229188_1_.m11 * f1 + p_229188_1_.m12 * f2; + this.z = p_229188_1_.m20 * f + p_229188_1_.m21 * f1 + p_229188_1_.m22 * f2; + } + */ + + /* Quaternions are not currently needed/implemented + public void transform(Quaternion p_214905_1_) + { + Quaternion quaternion = new Quaternion(p_214905_1_); + quaternion.mul(new Quaternion(this.x(), this.y(), this.z(), 0.0F)); + Quaternion quaternion1 = new Quaternion(p_214905_1_); + quaternion1.conj(); + quaternion.mul(quaternion1); + this.set(quaternion.i(), quaternion.j(), quaternion.k()); + } + */ + + /* not currently needed + * percent may actually be partial ticks (which is available when rendering) + public void linearInterp(Vec3f resultingVector, float percent) + { + float f = 1.0F - percent; + this.x = this.x * f + resultingVector.x * percent; + this.y = this.y * f + resultingVector.y * percent; + this.z = this.z * f + resultingVector.z * percent; + } + */ + + /* Quaternions are not currently needed/implemented + public Quaternion rotation(float p_229193_1_) + { + return new Quaternion(this, p_229193_1_, false); + } + + + @OnlyIn(Dist.CLIENT) + public Quaternion rotationDegrees(float p_229187_1_) + { + return new Quaternion(this, p_229187_1_, true); + } + */ + + public Vec3f copy() + { + return new Vec3f(this.x, this.y, this.z); + } + + /* not currently needed/implemented + public void map(Float2FloatFunction p_229191_1_) + { + this.x = p_229191_1_.get(this.x); + this.y = p_229191_1_.get(this.y); + this.z = p_229191_1_.get(this.z); + } + */ + + @Override + public String toString() + { + return "[" + this.x + ", " + this.y + ", " + this.z + "]"; + } + + // Forge start + public Vec3f(float[] values) + { + set(values); + } + + public void set(float[] values) + { + this.x = values[0]; + this.y = values[1]; + this.z = values[2]; + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/math/Vec3i.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Vec3i.java similarity index 98% rename from core/src/main/java/com/seibel/distanthorizons/core/util/math/Vec3i.java rename to api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Vec3i.java index bd4d609e1..c85fdea51 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/math/Vec3i.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Vec3i.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.core.util.math; +package com.seibel.distanthorizons.coreapi.util.math; import com.seibel.distanthorizons.coreapi.util.MathUtil; import com.seibel.distanthorizons.api.objects.math.DhApiVec3i; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/Initializer.java b/core/src/main/java/com/seibel/distanthorizons/core/Initializer.java index 3bed23eed..3ef5e3325 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/Initializer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/Initializer.java @@ -20,7 +20,6 @@ package com.seibel.distanthorizons.core; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.render.renderer.generic.GenericRenderObjectFactory; import com.seibel.distanthorizons.core.sql.DatabaseUpdater; import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; @@ -97,7 +96,6 @@ public class Initializer DhApi.Delayed.terrainRepo = DhApiTerrainDataRepo.INSTANCE; DhApi.Delayed.worldProxy = DhApiWorldProxy.INSTANCE; DhApi.Delayed.renderProxy = DhApiRenderProxy.INSTANCE; - DhApi.Delayed.customRenderObjectFactory = GenericRenderObjectFactory.INSTANCE; DhApi.Delayed.wrapperFactory = SingletonInjector.INSTANCE.get(IWrapperFactory.class); if (DhApi.Delayed.wrapperFactory == null) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGenericRenderingConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGenericRenderingConfig.java deleted file mode 100644 index f7fded3e5..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGenericRenderingConfig.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.api.external.methods.config.client; - -import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue; -import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiGenericRenderingConfig; -import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiNoiseTextureConfig; -import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue; -import com.seibel.distanthorizons.core.config.Config; - -public class DhApiGenericRenderingConfig implements IDhApiGenericRenderingConfig -{ - public static DhApiGenericRenderingConfig INSTANCE = new DhApiGenericRenderingConfig(); - - private DhApiGenericRenderingConfig() { } - - - - @Override - public IDhApiConfigValue renderingEnabled() - { return new DhApiConfigValue(Config.Client.Advanced.Graphics.GenericRendering.enableRendering); } - @Override - public IDhApiConfigValue beaconRenderingEnabled() - { return new DhApiConfigValue(Config.Client.Advanced.Graphics.GenericRendering.enableBeaconRendering); } - @Override - public IDhApiConfigValue cloudRenderingEnabled() - { return new DhApiConfigValue(Config.Client.Advanced.Graphics.GenericRendering.enableCloudRendering); } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGraphicsConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGraphicsConfig.java index ef4f66c74..27c1d48eb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGraphicsConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGraphicsConfig.java @@ -22,7 +22,10 @@ package com.seibel.distanthorizons.core.api.external.methods.config.client; import com.seibel.distanthorizons.api.enums.config.*; import com.seibel.distanthorizons.api.enums.rendering.EDhApiTransparency; import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue; -import com.seibel.distanthorizons.api.interfaces.config.client.*; +import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiAmbientOcclusionConfig; +import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiFogConfig; +import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiGraphicsConfig; +import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiNoiseTextureConfig; import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue; import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode; import com.seibel.distanthorizons.core.config.Config; @@ -39,14 +42,9 @@ public class DhApiGraphicsConfig implements IDhApiGraphicsConfig // inner layers // //==============// - @Override public IDhApiFogConfig fog() { return DhApiFogConfig.INSTANCE; } - @Override public IDhApiAmbientOcclusionConfig ambientOcclusion() { return DhApiAmbientOcclusionConfig.INSTANCE; } - @Override public IDhApiNoiseTextureConfig noiseTexture() { return DhApiNoiseTextureConfig.INSTANCE; } - @Override - public IDhApiGenericRenderingConfig genericRendering() { return DhApiGenericRenderingConfig.INSTANCE; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataCache.java b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataCache.java deleted file mode 100644 index 37cabfb97..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataCache.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.seibel.distanthorizons.core.api.external.methods.data; - -import com.seibel.distanthorizons.api.interfaces.data.IDhApiTerrainDataCache; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; -import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap; -import it.unimi.dsi.fastutil.longs.LongSet; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.Nullable; - -import java.lang.ref.SoftReference; - -public class DhApiTerrainDataCache implements IDhApiTerrainDataCache -{ - private final Object modificationLock = new Object(); - private Long2ReferenceOpenHashMap> posToFullDataRef = new Long2ReferenceOpenHashMap<>(); - - private static final Logger LOGGER = LogManager.getLogger(DhApiTerrainDataCache.class.getSimpleName()); - - - - //==================// - // internal methods // - //==================// - - public void add(long pos, FullDataSourceV2 dataSource) - { - synchronized (this.modificationLock) - { - this.posToFullDataRef.put(pos, new SoftReference<>(dataSource)); - } - } - - @Nullable - public FullDataSourceV2 get(long pos) - { - synchronized (this.modificationLock) - { - SoftReference ref = this.posToFullDataRef.get(pos); - if (ref != null) - { - return ref.get(); - } - else - { - return null; - } - } - } - - - - //=============// - // API methods // - //=============// - - @Override - public void clear() - { - synchronized (this.modificationLock) - { - LongSet keySet = this.posToFullDataRef.keySet(); - for (long pos : keySet) - { - SoftReference dataRef = this.posToFullDataRef.remove(pos); - if (dataRef != null) - { - FullDataSourceV2 dataSource = dataRef.get(); - if (dataSource != null) - { - try - { - dataSource.close(); - } - catch (Exception e) - { - LOGGER.warn("Unable to close data source, error: [" + e.getMessage() + "].", e); - } - } - } - } - } - } - - - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataRepo.java b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataRepo.java index cf7d337c0..d08989ad8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataRepo.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataRepo.java @@ -19,7 +19,6 @@ package com.seibel.distanthorizons.core.api.external.methods.data; -import com.seibel.distanthorizons.api.interfaces.data.IDhApiTerrainDataCache; import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper; import com.seibel.distanthorizons.api.objects.DhApiResult; import com.seibel.distanthorizons.api.objects.data.DhApiRaycastResult; @@ -36,7 +35,6 @@ import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.FullDataPointUtil; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.RayCastUtil; -import com.seibel.distanthorizons.core.util.math.Vec3f; import com.seibel.distanthorizons.core.world.AbstractDhWorld; import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; @@ -45,12 +43,12 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRen import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; -import com.seibel.distanthorizons.core.util.math.Vec3d; -import com.seibel.distanthorizons.core.util.math.Vec3i; +import com.seibel.distanthorizons.coreapi.util.math.Vec3d; +import com.seibel.distanthorizons.coreapi.util.math.Vec3f; +import com.seibel.distanthorizons.coreapi.util.math.Vec3i; import it.unimi.dsi.fastutil.longs.LongArrayList; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.concurrent.ExecutionException; @@ -58,6 +56,9 @@ import java.util.concurrent.ExecutionException; /** * Allows interfacing with the terrain data Distant Horizons has stored. + * + * @author James Seibel + * @version 2022-11-19 */ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo { @@ -67,15 +68,12 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo // debugging values private static volatile boolean debugThreadRunning = false; - private static DhApiTerrainDataCache debugDataCache = new DhApiTerrainDataCache(); + private static String currentDebugBiomeName = ""; + private static int currentDebugBlockColorInt = -1; private static DhApiVec3i currentDebugVec3i = new Vec3i(); - //=============// - // constructor // - //=============// - private DhApiTerrainDataRepo() { @@ -88,32 +86,41 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo //================// @Override - public DhApiResult getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ, @Nullable IDhApiTerrainDataCache dataCache) - { return getTerrainDataAtBlockYPos(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), blockPosY, dataCache); } + public DhApiResult getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ) + { + return getTerrainDataAtBlockYPos(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), blockPosY); + } @Override - public DhApiResult getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ, @Nullable IDhApiTerrainDataCache dataCache) - { return getTerrainDataColumnArray(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), null, dataCache); } + public DhApiResult getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ) + { + return getTerrainDataColumnArray(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), null); + } @Override - public DhApiResult getAllTerrainDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ, @Nullable IDhApiTerrainDataCache dataCache) - { return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPosX, chunkPosZ), dataCache); } + public DhApiResult getAllTerrainDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ) + { + return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPosX, chunkPosZ)); + } @Override - public DhApiResult getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ, @Nullable IDhApiTerrainDataCache dataCache) - { return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(LodUtil.REGION_DETAIL_LEVEL, regionPosX, regionPosZ), dataCache); } + public DhApiResult getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ) + { + return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(LodUtil.REGION_DETAIL_LEVEL, regionPosX, regionPosZ)); + } @Override - public DhApiResult getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ, @Nullable IDhApiTerrainDataCache dataCache) - { return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(detailLevel, posX, posZ), dataCache); } - + public DhApiResult getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ) + { + return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(detailLevel, posX, posZ)); + } // private getters // /** Returns a single API terrain datapoint that contains the given Y block position */ - private static DhApiResult getTerrainDataAtBlockYPos(IDhApiLevelWrapper levelWrapper, DhLodPos requestedColumnPos, Integer blockYPos, @Nullable IDhApiTerrainDataCache dataCache) + private static DhApiResult getTerrainDataAtBlockYPos(IDhApiLevelWrapper levelWrapper, DhLodPos requestedColumnPos, Integer blockYPos) { - DhApiResult result = getTerrainDataColumnArray(levelWrapper, requestedColumnPos, blockYPos, dataCache); + DhApiResult result = getTerrainDataColumnArray(levelWrapper, requestedColumnPos, blockYPos); if (result.success && result.payload.length > 0) { return DhApiResult.createSuccess(result.message, result.payload[0]); @@ -133,9 +140,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo * * will stop and return the in progress data if any errors are encountered. */ - private static DhApiResult getTerrainDataOverAreaForPositionDetailLevel( - IDhApiLevelWrapper levelWrapper, DhLodPos requestedAreaPos, - @Nullable IDhApiTerrainDataCache dataCache) + private static DhApiResult getTerrainDataOverAreaForPositionDetailLevel(IDhApiLevelWrapper levelWrapper, DhLodPos requestedAreaPos) { DhLodPos startingBlockPos = requestedAreaPos.getCornerLodPos(LodUtil.BLOCK_DETAIL_LEVEL); int widthOfAreaInBlocks = BitShiftUtil.powerOfTwo(requestedAreaPos.detailLevel); @@ -149,7 +154,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo for (int z = 0; z < widthOfAreaInBlocks; z++) { DhLodPos blockColumnPos = new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, startingBlockPos.x + x, startingBlockPos.z + z); - DhApiResult result = getTerrainDataColumnArray(levelWrapper, blockColumnPos, null, dataCache); + DhApiResult result = getTerrainDataColumnArray(levelWrapper, blockColumnPos, null); if (result.success) { returnArray[x][z] = result.payload; @@ -172,15 +177,8 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo * If the ApiResult is successful there will be an array of data.
* The returned array will be empty if no data could be retrieved. */ - private static DhApiResult getTerrainDataColumnArray( - IDhApiLevelWrapper levelWrapper, - DhLodPos requestedColumnPos, Integer nullableBlockYPos, - @Nullable IDhApiTerrainDataCache apiDataCache) + private static DhApiResult getTerrainDataColumnArray(IDhApiLevelWrapper levelWrapper, DhLodPos requestedColumnPos, Integer nullableBlockYPos) { - //============// - // validation // - //============// - AbstractDhWorld currentWorld = SharedApi.getAbstractDhWorld(); if (currentWorld == null) { @@ -196,15 +194,6 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo ILevelWrapper coreLevelWrapper = (ILevelWrapper) levelWrapper; - if (!(apiDataCache instanceof DhApiTerrainDataCache)) - { - // custom level wrappers aren't supported, - // the API user must get a level wrapper from our code somewhere - return DhApiResult.createFail("Unsupported [" + IDhApiTerrainDataCache.class.getSimpleName() + "] implementation, only the core class [" + DhApiTerrainDataCache.class.getSimpleName() + "] is a valid parameter."); - } - DhApiTerrainDataCache dataCache = (DhApiTerrainDataCache) apiDataCache; - - IDhLevel level = currentWorld.getLevel(coreLevelWrapper); if (level == null) { @@ -220,96 +209,70 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo DhLodPos relativePos = requestedColumnPos.getDhSectionRelativePositionForDetailLevel(); - - //=====================// - // get the data source // - //=====================// - try { - FullDataSourceV2 dataSource = null; - - // try using the cached data if possible - if (dataCache != null) - { - dataSource = dataCache.get(sectionPos); - } - + // attempt to get/generate the data source for this section + FullDataSourceV2 dataSource = level.getFullDataProvider().getAsync(sectionPos).get(); if (dataSource == null) { - // attempt to get/generate the data source for this section - dataSource = level.getFullDataProvider().getAsync(sectionPos).get(); - if (dataSource == null) - { - return DhApiResult.createFail("Unable to find/generate any data at the " + DhSectionPos.class.getSimpleName() + " [" + DhSectionPos.toString(sectionPos) + "]."); - } - dataCache.add(sectionPos, dataSource); + return DhApiResult.createFail("Unable to find/generate any data at the " + DhSectionPos.class.getSimpleName() + " [" + DhSectionPos.toString(sectionPos) + "]."); } - - - - //===============================// - // get LOD data from data source // - //===============================// - - FullDataPointIdMap mapping = dataSource.mapping; - LongArrayList dataColumn = dataSource.get(relativePos.x, relativePos.z); - if (dataColumn != null) + else { - int dataColumnIndexCount = dataColumn.size(); - DhApiTerrainDataPoint[] returnArray = new DhApiTerrainDataPoint[dataColumnIndexCount]; - long dataPoint; - - boolean getSpecificYCoordinate = nullableBlockYPos != null; - int levelMinimumHeight = levelWrapper.getMinHeight(); - - - // search for a datapoint that contains the block y position - for (int i = 0; i < dataColumnIndexCount; i++) + // attempt to get the LOD data from the data source + FullDataPointIdMap mapping = dataSource.mapping; + LongArrayList dataColumn = dataSource.get(relativePos.x, relativePos.z); + if (dataColumn != null) { - dataPoint = dataColumn.getLong(i); + int dataColumnIndexCount = dataColumn.size(); + DhApiTerrainDataPoint[] returnArray = new DhApiTerrainDataPoint[dataColumnIndexCount]; + long dataPoint; - if (!getSpecificYCoordinate) + boolean getSpecificYCoordinate = nullableBlockYPos != null; + int levelMinimumHeight = levelWrapper.getMinHeight(); + + + // search for a datapoint that contains the block y position + for (int i = 0; i < dataColumnIndexCount; i++) { - // if we aren't look for a specific datapoint, add each datapoint to the return array - returnArray[i] = generateApiDatapoint(levelWrapper, mapping, requestedDetailLevel, dataPoint); - } - else - { - // we are looking for a specific datapoint, - // don't look at null ones - if (dataPoint != 0) + dataPoint = dataColumn.getLong(i); + + if (!getSpecificYCoordinate) { - int requestedY = nullableBlockYPos; - int bottomY = FullDataPointUtil.getBottomY(dataPoint) + levelMinimumHeight; - int height = FullDataPointUtil.getHeight(dataPoint); - int topY = bottomY + height; - - // does this datapoint contain the requested Y position? - if (bottomY <= requestedY && requestedY < topY) // blockPositions start from the bottom of the block, thus "<=" for bottomY, just "<" for topY + // if we aren't look for a specific datapoint, add each datapoint to the return array + returnArray[i] = generateApiDatapoint(levelWrapper, mapping, requestedDetailLevel, dataPoint); + } + else + { + // we are looking for a specific datapoint, + // don't look at null ones + if (dataPoint != 0) { - // this datapoint contains the requested block position, return it - DhApiTerrainDataPoint apiTerrainData = generateApiDatapoint(levelWrapper, mapping, requestedDetailLevel, dataPoint); - return DhApiResult.createSuccess(new DhApiTerrainDataPoint[]{apiTerrainData}); + int requestedY = nullableBlockYPos; + int bottomY = FullDataPointUtil.getBottomY(dataPoint) + levelMinimumHeight; + int height = FullDataPointUtil.getHeight(dataPoint); + int topY = bottomY + height; + + // does this datapoint contain the requested Y position? + if (bottomY <= requestedY && requestedY < topY) // blockPositions start from the bottom of the block, thus "<=" for bottomY, just "<" for topY + { + // this datapoint contains the requested block position, return it + DhApiTerrainDataPoint apiTerrainData = generateApiDatapoint(levelWrapper, mapping, requestedDetailLevel, dataPoint); + return DhApiResult.createSuccess(new DhApiTerrainDataPoint[]{apiTerrainData}); + } } } } + + // return all collected data + return DhApiResult.createSuccess(returnArray); } - // return all collected data - return DhApiResult.createSuccess(returnArray); + // the requested data wasn't present in this column (and/or the column wasn't able to be accessed/generated) + return DhApiResult.createSuccess(new DhApiTerrainDataPoint[0]); } - - // the requested data wasn't present in this column (and/or the column wasn't able to be accessed/generated) - return DhApiResult.createSuccess(new DhApiTerrainDataPoint[0]); } catch (InterruptedException | ExecutionException e) - { - // shouldn't normally happen, but just in case - LOGGER.error("getTerrainDataColumnArray operation canceled. Error: [" + e.getMessage() + "]", e); - return DhApiResult.createFail("Operation cancled before it could complete: [" + e.getMessage() + "]."); - } - catch (Exception e) { // shouldn't normally happen, but just in case LOGGER.error("Unexpected exception in getTerrainDataColumnArray. Error: [" + e.getMessage() + "]", e); @@ -343,11 +306,9 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo IDhApiLevelWrapper levelWrapper, double rayOriginX, double rayOriginY, double rayOriginZ, float rayDirectionX, float rayDirectionY, float rayDirectionZ, - int maxRayBlockLength, - @Nullable - IDhApiTerrainDataCache dataCache) + int maxRayBlockLength) { - return this.raycastLodData(levelWrapper, new Vec3d(rayOriginX, rayOriginY, rayOriginZ), new Vec3f(rayDirectionX, rayDirectionY, rayDirectionZ), maxRayBlockLength, dataCache); + return this.raycastLodData(levelWrapper, new Vec3d(rayOriginX, rayOriginY, rayOriginZ), new Vec3f(rayDirectionX, rayDirectionY, rayDirectionZ), maxRayBlockLength); } /** @@ -356,17 +317,12 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo * Works by walking through the world and attempting to get the LOD
* data present at each step. */ - private DhApiResult raycastLodData( - IDhApiLevelWrapper levelWrapper, - Vec3d rayOrigin, Vec3f rayDirection, - int maxRayBlockLength, - @Nullable - IDhApiTerrainDataCache dataCache) + private DhApiResult raycastLodData(IDhApiLevelWrapper levelWrapper, Vec3d rayOrigin, Vec3f rayDirection, int maxRayBlockLength) { rayDirection.normalize(); int minBlockHeight = levelWrapper.getMinHeight(); - int maxBlockHeight = levelWrapper.getMaxHeight(); + int maxBlockHeight = levelWrapper.getHeight(); @@ -390,7 +346,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo for (Vec3i columnPos : columnPositions) { // check each column - DhApiResult result = this.getColumnDataAtBlockPos(levelWrapper, columnPos.x, columnPos.z, dataCache); + DhApiResult result = this.getColumnDataAtBlockPos(levelWrapper, columnPos.x, columnPos.z); if (!result.success) { // if there was an error, stop and return it @@ -458,7 +414,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo */ private static ArrayList getIntersectingColumnsAtPosition(Vec3i rayEndingPos, Vec3f rayDirection) { - ArrayList returnList = new ArrayList<>(9); + ArrayList returnList = new ArrayList(9); for (int x = -1; x <= 1; x++) { @@ -513,15 +469,6 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo - //=============// - // API helpers // - //=============// - - @Override - public IDhApiTerrainDataCache getSoftCache() { return new DhApiTerrainDataCache(); } - - - //===============// // debug methods // //===============// @@ -538,15 +485,15 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo Thread thread = new Thread(() -> { try { - DhApiResult single = getTerrainDataAtBlockYPos(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), blockPosY, debugDataCache); - DhApiResult column = getTerrainDataColumnArray(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), null, debugDataCache); + DhApiResult single = getTerrainDataAtBlockYPos(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), blockPosY); + DhApiResult column = getTerrainDataColumnArray(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), null); DhLodPos chunkPos = new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ).convertToDetailLevel(LodUtil.CHUNK_DETAIL_LEVEL); - DhApiResult area = getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, chunkPos, debugDataCache); + DhApiResult area = getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, chunkPos); IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); - DhApiResult rayCast = INSTANCE.raycastLodData(levelWrapper, MC_RENDER.getCameraExactPosition(), MC_RENDER.getLookAtVector(), 1000, debugDataCache); + DhApiResult rayCast = INSTANCE.raycastLodData(levelWrapper, MC_RENDER.getCameraExactPosition(), MC_RENDER.getLookAtVector(), 1000); if (rayCast.payload != null && !rayCast.payload.pos.equals(currentDebugVec3i)) { currentDebugVec3i = rayCast.payload.pos; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index ec4df1ad7..2e438978a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -23,12 +23,6 @@ import com.seibel.distanthorizons.api.DhApi; 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.pos.DhChunkPos; -import com.seibel.distanthorizons.core.render.DhApiRenderProxy; -import com.seibel.distanthorizons.core.util.objects.Pair; -import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; -import com.seibel.distanthorizons.core.level.IDhClientLevel; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.network.messages.NetworkMessage; import com.seibel.distanthorizons.core.network.session.Session; @@ -36,14 +30,17 @@ 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.IDhClientLevel; import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.logging.ConfigBasedSpamLogger; import com.seibel.distanthorizons.core.logging.SpamReducedLogger; -import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.pos.DhChunkPos; +import com.seibel.distanthorizons.core.render.DhApiRenderProxy; import com.seibel.distanthorizons.core.render.glObject.GLProxy; import com.seibel.distanthorizons.core.render.renderer.TestRenderer; import com.seibel.distanthorizons.core.util.RenderUtil; +import com.seibel.distanthorizons.core.util.objects.Pair; import com.seibel.distanthorizons.core.world.AbstractDhWorld; import com.seibel.distanthorizons.core.world.DhClientServerWorld; import com.seibel.distanthorizons.core.world.DhClientWorld; @@ -52,12 +49,13 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; +import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; +import com.seibel.distanthorizons.coreapi.util.math.Mat4f; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.lwjgl.glfw.GLFW; -import java.io.File; import java.util.HashMap; import java.util.HashSet; import java.util.Queue; @@ -126,28 +124,9 @@ public class ClientApi public synchronized void onClientOnlyConnected() { // only continue if the client is connected to a different server - boolean connectedToServer = MC.clientConnectedToDedicatedServer(); - boolean connectedToReplay = MC.connectedToReplay(); - if (connectedToServer || connectedToReplay) + if (MC.clientConnectedToDedicatedServer()) { - if (connectedToServer) - { - LOGGER.info("Client on ClientOnly mode connecting."); - } - else - { - LOGGER.info("Replay on ClientServer mode connecting."); - - if (Config.Client.Advanced.Logging.showReplayWarningOnStartup.get()) - { - MC.sendChatMessage("\u00A76" + "Distant Horizons: Replay detected." + "\u00A7r"); // gold color - MC.sendChatMessage("DH may behave strangely or have missing functionality."); - MC.sendChatMessage("In order to use pre-generated LODs, put your DH database(s) in:"); - MC.sendChatMessage("\u00A77"+".Minecraft" + File.separator + ClientOnlySaveStructure.SERVER_DATA_FOLDER_NAME + File.separator + ClientOnlySaveStructure.REPLAY_SERVER_FOLDER_NAME + File.separator + "DIMENSION_NAME"+"\u00A7r"); // light gray color - MC.sendChatMessage("This can be disabled in DH's config under Advanced -> Logging."); - MC.sendChatMessage(""); - } - } + LOGGER.info("Client on ClientOnly mode connecting."); // firing after clientLevelLoadEvent // TODO if level has prepped to load it should fire level load event @@ -468,7 +447,7 @@ public class ClientApi if (!DhApi.Delayed.renderProxy.getDeferTransparentRendering()) { - ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterRenderEvent.class, null); + ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterRenderEvent.class, renderEventParam); } } else if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DEBUG) @@ -489,7 +468,7 @@ public class ClientApi if (DhApi.Delayed.renderProxy.getDeferTransparentRendering()) { - ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterRenderEvent.class, null); + ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterRenderEvent.class, renderEventParam); } } } @@ -500,7 +479,6 @@ public class ClientApi MC.sendChatMessage("\u00A74\u00A7l\u00A7uERROR: Distant Horizons renderer has encountered an exception!"); MC.sendChatMessage("\u00A74Renderer is now disabled to prevent further issues."); - MC.sendChatMessage("\u00A74Please restart your game to re-enable Distant Horizons' LOD rendering."); MC.sendChatMessage("\u00A74Exception detail: " + e); } finally diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java index 0c811b0a0..7f16944bf 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java @@ -138,7 +138,7 @@ public class ServerApi //=======================// public void serverChunkLoadEvent(IChunkWrapper chunkWrapper, ILevelWrapper level) { SharedApi.INSTANCE.applyChunkUpdate(chunkWrapper, level, false); } - public void serverChunkSaveEvent(IChunkWrapper chunkWrapper, ILevelWrapper level) { SharedApi.INSTANCE.applyChunkUpdate(chunkWrapper, level, true); } + public void serverChunkSaveEvent(IChunkWrapper chunkWrapper, ILevelWrapper level) { SharedApi.INSTANCE.applyChunkUpdate(chunkWrapper, level, false); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java index 26b1afa9b..91ed0745b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java @@ -29,8 +29,7 @@ import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; -import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; -import com.seibel.distanthorizons.core.sql.repo.AbstractDhRepo; +import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.TimerUtil; import com.seibel.distanthorizons.core.util.objects.Pair; import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; @@ -57,7 +56,7 @@ public class SharedApi private static final Set UPDATING_CHUNK_POS_SET = ConcurrentHashMap.newKeySet(); /** how many chunks can be queued for updating per thread, used to prevent updates from infinitely pilling up if the user flys around extremely fast */ private static final int MAX_UPDATING_CHUNK_COUNT_PER_THREAD = 500; - private static final int MIN_MS_BETWEEN_OVERLOADED_LOG_MESSAGE = 30_000; + private static final int MIN_MS_BETWEEN_OVERLOADED_LOG_MESSAGE = 5_000; private static final Timer CHUNK_UPDATE_TIMER = TimerUtil.CreateTimer("ChunkUpdateTimer"); @@ -102,9 +101,7 @@ public class SharedApi { DebugRenderer.clearRenderables(); MC_RENDER.clearTargetFrameBuffer(); - // shouldn't be necessary, but if we missed closing one of the connections this should make sure they're all closed - AbstractDhRepo.closeAllConnections(); - // needs to be closed on world shutdown to clear out un-processed chunks + // needs to be closed on world shutdown to clear out un-processed chunks UPDATING_CHUNK_POS_SET.clear(); } @@ -141,8 +138,7 @@ public class SharedApi * Used to prevent getting a full chunk from MC if it isn't necessary.
* This is important since asking MC for a chunk is slow and may block the render thread. */ - public static boolean isChunkAtBlockPosAlreadyUpdating(int blockPosX, int blockPosZ) - { return UPDATING_CHUNK_POS_SET.contains(new DhChunkPos(new DhBlockPos2D(blockPosX, blockPosZ))); } + public static boolean isChunkAtBlockPosAlreadyUpdating(int blockPosX, int blockPosZ) { return UPDATING_CHUNK_POS_SET.contains(new DhChunkPos(new DhBlockPos2D(blockPosX, blockPosZ))); } /** handles both block place and break events */ @@ -219,13 +215,7 @@ public class SharedApi if (msBetweenLastLog >= MIN_MS_BETWEEN_OVERLOADED_LOG_MESSAGE) { lastOverloadedLogMessageMsTime = System.currentTimeMillis(); - - String message = "Distant Horizons overloaded, too many chunks queued for updating. " + - "\nThis may result in holes in your LODs. " + - "\nPlease move through the world slower, decrease your vanilla render distance, slow down your world pre-generator, or increase the Distant Horizons' CPU load config. " + - "\nMax queue count ["+maxQueueCount+"] (["+MAX_UPDATING_CHUNK_COUNT_PER_THREAD+"] per thread)."; - ClientApi.INSTANCE.showChatMessageNextFrame(message); - LOGGER.warn(message); + LOGGER.warn("Too many chunks queued for updating, max queue count ["+maxQueueCount+"] (["+MAX_UPDATING_CHUNK_COUNT_PER_THREAD+"] per thread). This may result in holes in your LODs. Please move through the world slower, decrease your vanilla render distance, slow down your world pre-generator, or increase the CPU load config."); } return; @@ -320,18 +310,6 @@ public class SharedApi } - // having a list of the nearby chunks is needed for lighting and beacon generation - ArrayList nearbyChunkList; - if (neighbourChunkList != null) - { - nearbyChunkList = neighbourChunkList; - } - else - { - nearbyChunkList = new ArrayList<>(1); - nearbyChunkList.add(chunkWrapper); - } - // Save or populate the chunk wrapper's lighting // this is done so we don't have to worry about MC unloading the lighting data for this chunk @@ -341,7 +319,7 @@ public class SharedApi try { // If MC's lighting engine isn't thread safe this may cause the server thread to lag - chunkWrapper.bakeDhLightingUsingMcLightingEngine(); + chunkWrapper.bakeDhLightingUsingMcLightingEngine(); // TODO handle unlit chunks, would pulling in the chunk from disk be a good idea? Look at ChunkLoader in the world gen code for an example } catch (IllegalStateException e) { @@ -351,16 +329,21 @@ public class SharedApi else { // generate the chunk's lighting, using neighboring chunks if present + + ArrayList nearbyChunkList; + if (neighbourChunkList != null) + { + nearbyChunkList = neighbourChunkList; + } + else + { + nearbyChunkList = new ArrayList<>(1); + nearbyChunkList.add(chunkWrapper); + } + DhLightingEngine.INSTANCE.lightChunk(chunkWrapper, nearbyChunkList, dhLevel.hasSkyLight() ? 15 : 0); } - - - // get this chunk's active beacons - List beaconBeamList = chunkWrapper.getAllActiveBeacons(nearbyChunkList); - dhLevel.setBeaconBeamsForChunk(chunkWrapper.getChunkPos(), beaconBeamList); - - dhLevel.updateChunkAsync(chunkWrapper); dhLevel.setChunkHash(chunkWrapper.getChunkPos(), newChunkHash); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index f865f9ec7..9233e59af 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -20,20 +20,17 @@ package com.seibel.distanthorizons.core.config; -import com.seibel.distanthorizons.api.DhApi; import com.seibel.distanthorizons.api.enums.config.*; import com.seibel.distanthorizons.api.enums.config.quickOptions.*; import com.seibel.distanthorizons.api.enums.rendering.*; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode; import com.seibel.distanthorizons.core.config.eventHandlers.*; import com.seibel.distanthorizons.core.config.eventHandlers.presets.*; -import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener; import com.seibel.distanthorizons.core.config.types.*; import com.seibel.distanthorizons.core.config.types.enums.*; 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.IMinecraftSharedWrapper; import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.util.StringUtil; @@ -139,7 +136,6 @@ public class Config public static ConfigCategory fog = new ConfigCategory.Builder().set(Fog.class).build(); public static ConfigCategory ssao = new ConfigCategory.Builder().set(Ssao.class).build(); public static ConfigCategory noiseTextureSettings = new ConfigCategory.Builder().set(NoiseTextureSettings.class).build(); - public static ConfigCategory genericRendering = new ConfigCategory.Builder().set(GenericRendering.class).build(); public static ConfigCategory advancedGraphics = new ConfigCategory.Builder().set(AdvancedGraphics.class).build(); @@ -292,7 +288,7 @@ public class Config private static final Double FOG_RANGE_MAX = Math.sqrt(2.0); public static ConfigEntry farFogStart = new ConfigEntry.Builder() - .setMinDefaultMax(FOG_RANGE_MIN, 0.4, FOG_RANGE_MAX) + .setMinDefaultMax(FOG_RANGE_MIN, 0.0, FOG_RANGE_MAX) .comment("" + "At what distance should the far fog start? \n" + "\n" @@ -523,8 +519,7 @@ public class Config public static ConfigEntry noiseSteps = new ConfigEntry.Builder() .setMinDefaultMax(1, 4, null) .comment("" - + "How many steps of noise should be applied to LODs?" - + "") + + "How many steps of noise should be applied to LODs?") .build(); public static ConfigEntry noiseIntensity = new ConfigEntry.Builder() // TODO: Make this a float (the ClassicConfigGUI doesn't support floats) @@ -537,34 +532,7 @@ public class Config .setMinDefaultMax(0, 1024, null) .comment("" + "Defines how far should the noise texture render before it fades away. (in blocks) \n" - + "Set to 0 to disable noise from fading away \n" - + "") - .build(); - - } - - public static class GenericRendering - { - public static ConfigEntry enableRendering = new ConfigEntry.Builder() - .set(true) - .comment("" - + "If true non terrain objects will be rendered in DH's terrain. \n" - + "This includes beacon beams and clouds. \n" - + "") - .build(); - - public static ConfigEntry enableBeaconRendering = new ConfigEntry.Builder() - .set(true) - .comment("" - + "If true LOD beacon beams will be rendered. \n" - + "") - .build(); - - public static ConfigEntry enableCloudRendering = new ConfigEntry.Builder() - .set(true) - .comment("" - + "If true LOD clouds will be rendered. \n" - + "") + + "Set to 0 to disable noise from fading away") .build(); } @@ -621,10 +589,8 @@ public class Config + " does not have a ceiling.") .build(); - @Deprecated public static ConfigEntry caveCullingHeight = new ConfigEntry.Builder() .setMinDefaultMax(-4096, 40, 4096) - .setAppearance(EConfigEntryAppearance.ONLY_IN_API) .comment("" + "At what Y value should cave culling start?") .build(); @@ -795,20 +761,6 @@ public class Config + "") .build(); - public static ConfigEntry pullLightingForPregeneratedChunks = new ConfigEntry.Builder() - .set(false) - .comment("" - + "If true LOD generation for pre-existing chunks will attempt to pull the lighting data \n" - + "saved in Minecraft's Region files. \n" - + "If false DH will pull in chunks without lighting and re-light them. \n" - + " \n" - + "Setting this to true will result in faster LOD generation \n" - + "for already generated worlds, but is broken by most lighting mods. \n" - + " \n" - + "Set this to false if LODs are black. \n" - + "") - .build(); - public static ConfigEntry dataCompression = new ConfigEntry.Builder() .set(EDhApiDataCompressionMode.LZMA2) .comment("" @@ -857,47 +809,13 @@ public class Config + "") .build(); - public static ConfigEntry ignoredRenderBlockCsv = new ConfigEntry.Builder() - .set("minecraft:barrier,minecraft:structure_void,minecraft:light,minecraft:tripwire") - .comment("" - + "A comma separated list of block resource locations that won't be rendered by DH. \n" - + "Note: air is always included in this list. \n" - + "") - .build(); + //public static ConfigEntry showMigrationChatWarning = new ConfigEntry.Builder() + // .set(true) + // .comment("" + // + "Determines if a message should be displayed in the chat when LOD migration starts. \n" + // + "") + // .build(); - public static ConfigEntry ignoredRenderCaveBlockCsv = new ConfigEntry.Builder() - .set("minecraft:glow_lichen,minecraft:rail,minecraft:water,minecraft:lava,minecraft:bubble_column") - .comment("" - + "A comma separated list of block resource locations that shouldn't be rendered \n" - + "if they are in a 0 sky light underground area. \n" - + "Note: air is always included in this list. \n" - + "") - .build(); - - static - { - ignoredRenderBlockCsv.addListener(new ConfigChangeListener(Config.Client.Advanced.LodBuilding.ignoredRenderBlockCsv, - (blockCsv) -> - { - IWrapperFactory wrapperFactory = SingletonInjector.INSTANCE.get(IWrapperFactory.class); - if (wrapperFactory != null) - { - wrapperFactory.resetRendererIgnoredBlocksSet(); - DhApi.Delayed.renderProxy.clearRenderDataCache(); - } - })); - - ignoredRenderCaveBlockCsv.addListener(new ConfigChangeListener(Config.Client.Advanced.LodBuilding.ignoredRenderCaveBlockCsv, - (blockCsv) -> - { - IWrapperFactory wrapperFactory = SingletonInjector.INSTANCE.get(IWrapperFactory.class); - if (wrapperFactory != null) - { - wrapperFactory.resetRendererIgnoredCaveBlocks(); - DhApi.Delayed.renderProxy.clearRenderDataCache(); - } - })); - } } public static class Multiplayer @@ -1274,7 +1192,7 @@ public class Config // TODO default to error chat and info file public static ConfigEntry logWorldGenEvent = new ConfigEntry.Builder() .setServersideShortName("logWorldGenEvent") - .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT) + .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_FILE) .comment("" + "If enabled, the mod will log information about the world generation process. \n" + "This can be useful for debugging.") @@ -1282,7 +1200,7 @@ public class Config public static ConfigEntry logWorldGenPerformance = new ConfigEntry.Builder() .setServersideShortName("logWorldGenPerformance") - .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT) + .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_FILE) .comment("" + "If enabled, the mod will log performance about the world generation process. \n" + "This can be useful for debugging.") @@ -1290,7 +1208,7 @@ public class Config public static ConfigEntry logWorldGenLoadEvent = new ConfigEntry.Builder() .setServersideShortName("logWorldGenPerformance") - .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT) + .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_FILE) .comment("" + "If enabled, the mod will log information about the world generation process. \n" + "This can be useful for debugging.") @@ -1298,21 +1216,21 @@ public class Config public static ConfigEntry logLodBuilderEvent = new ConfigEntry.Builder() .setServersideShortName("logLodBuilderEvent") - .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT) + .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE) .comment("" + "If enabled, the mod will log information about the LOD generation process. \n" + "This can be useful for debugging.") .build(); public static ConfigEntry logRendererBufferEvent = new ConfigEntry.Builder() - .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT) + .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE) .comment("" + "If enabled, the mod will log information about the renderer buffer process. \n" + "This can be useful for debugging.") .build(); public static ConfigEntry logRendererGLEvent = new ConfigEntry.Builder() - .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT) + .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE) .comment("" + "If enabled, the mod will log information about the renderer OpenGL process. \n" + "This can be useful for debugging.") @@ -1320,7 +1238,7 @@ public class Config public static ConfigEntry logFileReadWriteEvent = new ConfigEntry.Builder() .setServersideShortName("logFileReadWriteEvent") - .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT) + .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE) .comment("" + "If enabled, the mod will log information about file read/write operations. \n" + "This can be useful for debugging.") @@ -1328,7 +1246,7 @@ public class Config public static ConfigEntry logFileSubDimEvent = new ConfigEntry.Builder() .setServersideShortName("logFileSubDimEvent") - .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT) + .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE) .comment("" + "If enabled, the mod will log information about file sub-dimension operations. \n" + "This can be useful for debugging.") @@ -1336,7 +1254,7 @@ public class Config public static ConfigEntry logNetworkEvent = new ConfigEntry.Builder() .setServersideShortName("logNetworkEvent") - .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT) + .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE) .comment("" + "If enabled, the mod will log information about network operations. \n" + "This can be useful for debugging.") @@ -1350,13 +1268,6 @@ public class Config + "memory allocated to run DH well.") .build(); - public static ConfigEntry showReplayWarningOnStartup = new ConfigEntry.Builder() - .set(true) - .comment("" - + "If enabled, a chat message will be displayed when a replay is started \n" - + "giving some basic information about how DH will function.") - .build(); - } public static class Debugging @@ -1424,15 +1335,6 @@ public class Config + "") .build(); - public static ConfigEntry logBufferGarbageCollection = new ConfigEntry.Builder() - .set(false) - .comment("" - + "If true OpenGL Buffer garbage collection will be logged \n" - + "this also includes the number of live buffers. \n" - + "") - .build(); - - // Note: This will reset on game restart, and should have a warning on the tooltip public static ConfigEntry allowUnsafeValues = new ConfigEntry.Builder() .set(false) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java index b982c621d..5a877f9f7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java @@ -260,38 +260,20 @@ public class ConfigEntry extends AbstractConfigType> implem public byte isValid(T value, T min, T max) { if (this.configBase.disableMinMax) - { return 0; - } - else if (min == null && max == null) - { - // no validation is needed for this field - return 0; - } - else if (value == null || this.value == null - || value.getClass() != this.value.getClass()) - { - // If the 2 variables aren't the same type then it will be invalid + + if (value == null || this.value == null || value.getClass() != this.value.getClass()) // If the 2 variables aren't the same type then it will be invalid return 2; - } - else if (Number.class.isAssignableFrom(value.getClass())) - { - // Only check min max if it is a number + if (Number.class.isAssignableFrom(value.getClass())) + { // Only check min max if it is a number if (max != null && NumberUtil.greaterThan((Number) value, (Number) max)) - { return 1; - } if (min != null && NumberUtil.lessThan((Number) value, (Number) min)) - { return -1; - } return 0; } - else - { - return 0; - } + return 0; } /** This should normally not be called since set() automatically calls this */ diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigLinkedEntry.java b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigLinkedEntry.java index 5483f7769..bac72896c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigLinkedEntry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigLinkedEntry.java @@ -27,7 +27,6 @@ import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryAppearance * * @author coolGi */ -@Deprecated // FIXME doesn't work with localization public class ConfigLinkedEntry extends AbstractConfigType, ConfigLinkedEntry> { public ConfigLinkedEntry(AbstractConfigType value) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/FullDataSourceV2.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/FullDataSourceV2.java index 87519bc3f..d10f2361e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/FullDataSourceV2.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/FullDataSourceV2.java @@ -62,8 +62,6 @@ public class FullDataSourceV2 implements IDataSource /** measured in data columns */ public static final int WIDTH = 64; - /** how many chunks wide this datasource is. */ - public static final int NUMB_OF_CHUNKS_WIDE = WIDTH / LodUtil.CHUNK_WIDTH; public static final byte DATA_FORMAT_VERSION = 1; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderSource.java index a00fb750d..9d3407c46 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderSource.java @@ -182,7 +182,7 @@ public class ColumnRenderSource implements IDataSource EDhApiWorldGenerationStep worldGenStep = inputFullDataSource.getWorldGenStepAtRelativePos(x, z); if (dataColumn != null && worldGenStep != EDhApiWorldGenerationStep.EMPTY) { - FullDataToRenderDataTransformer.updateRenderDataViewWithFullDataColumn( + FullDataToRenderDataTransformer.convertColumnData( level, inputFullDataSource.mapping, minBlockPos.x + x, minBlockPos.z + z, diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnBox.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnBox.java index 0cc51e422..edf2a4c67 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnBox.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnBox.java @@ -19,7 +19,6 @@ package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding; -import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.enums.EDhDirection; import com.seibel.distanthorizons.core.util.ColorUtil; @@ -476,10 +475,10 @@ public class ColumnBox throw new RuntimeException("Loop error"); if (previousAdjDepth > adjYMax) { - if (irisBlockMaterialId == EDhApiBlockMaterial.GRASS.index) + if (irisBlockMaterialId == IBlockStateWrapper.IrisBlockMaterial.GRASS) { // this LOD is underneath another, grass will never show here - irisBlockMaterialId = EDhApiBlockMaterial.DIRT.index; + irisBlockMaterialId = IBlockStateWrapper.IrisBlockMaterial.DIRT; } builder.addQuadAdj(direction, x, adjYMax, z, horizontalWidth, (short) (previousAdjDepth - adjYMax), color, irisBlockMaterialId, diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java index 8a95bc2c7..8762bec32 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java @@ -50,11 +50,8 @@ public class ColumnRenderBuffer implements AutoCloseable private static final long MAX_BUFFER_UPLOAD_TIMEOUT_NANOSECONDS = 1_000_000; - /** number of bytes a single quad takes */ - public static final int QUADS_BYTE_SIZE = LodUtil.LOD_VERTEX_FORMAT.getByteSize() * 4; - /** how big a single VBO can be in bytes */ - public static final int MAX_VBO_BYTE_SIZE = 10 * 1024 * 1024; // 10 MB - public static final int MAX_QUADS_PER_BUFFER = MAX_VBO_BYTE_SIZE / QUADS_BYTE_SIZE; + public static final int QUADS_BYTE_SIZE = LodUtil.LOD_VERTEX_FORMAT.getByteSize() * 4; // TODO what does the 4 represent + public static final int MAX_QUADS_PER_BUFFER = (1024 * 1024 * 1) / QUADS_BYTE_SIZE; // TODO what do these multiples represent? public static final int FULL_SIZED_BUFFER = MAX_QUADS_PER_BUFFER * QUADS_BYTE_SIZE; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java index 64c1abd02..02c064408 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java @@ -54,6 +54,11 @@ public class ColumnRenderBufferBuilder private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + public static final int MAX_NUMBER_OF_CONCURRENT_CALLS_PER_THREAD = 3; + public static int maxNumberOfConcurrentCalls = MAX_NUMBER_OF_CONCURRENT_CALLS_PER_THREAD; + + + //==============// // vbo building // @@ -82,14 +87,33 @@ public class ColumnRenderBufferBuilder { boolean enableTransparency = Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled; + //EVENT_LOGGER.trace("RenderRegion start QuadBuild @ " + renderSource.sectionPos); + boolean enableSkyLightCulling = + Config.Client.Advanced.Graphics.AdvancedGraphics.enableCaveCulling.get() + && ( + // dimensions with a ceiling will be all caves so we don't want cave culling + !clientLevel.getLevelWrapper().hasCeiling() + // the end has a lot of overhangs with 0 lighting above the void, which look broken with + // the current cave culling logic (this could probably be improved, but just skipping it works best for now) + && !clientLevel.getLevelWrapper().getDimensionType().isTheEnd() + // FIXME temporary fix + // Cave culling is currently broken for any detail level above 0 + && DhSectionPos.getDetailLevel(renderSource.pos) == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL + ); + + int skyLightCullingBelow = Config.Client.Advanced.Graphics.AdvancedGraphics.caveCullingHeight.get(); + // FIXME: Clamp also to the max world height. + skyLightCullingBelow = Math.max(skyLightCullingBelow, clientLevel.getMinY()); + + long builderStartTime = System.currentTimeMillis(); - LodQuadBuilder builder = new LodQuadBuilder(enableTransparency, clientLevel.getClientLevelWrapper()); + LodQuadBuilder builder = new LodQuadBuilder(enableSkyLightCulling, (short) (skyLightCullingBelow - clientLevel.getMinY()), enableTransparency, clientLevel.getClientLevelWrapper()); makeLodRenderData(builder, renderSource, adjData); long builderEndTime = System.currentTimeMillis(); long buildMs = builderEndTime - builderStartTime; - //LOGGER.debug("RenderRegion end QuadBuild @ " + renderSource.pos + " took: " + buildMs); + LOGGER.debug("RenderRegion end QuadBuild @ " + renderSource.pos + " took: " + buildMs); return builder; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/CubicLodTemplate.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/CubicLodTemplate.java index a45fb2ec4..87b2e7720 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/CubicLodTemplate.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/CubicLodTemplate.java @@ -19,7 +19,6 @@ package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding; -import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource; import com.seibel.distanthorizons.core.pos.DhLodPos; @@ -96,54 +95,53 @@ public class CubicLodTemplate } case SHOW_BLOCK_MATERIAL: { - - switch (EDhApiBlockMaterial.getFromIndex(blockMaterialId)) + switch (blockMaterialId) { - case UNKNOWN: - case AIR: // shouldn't normally be rendered, but just in case + case IBlockStateWrapper.IrisBlockMaterial.UNKOWN: + case IBlockStateWrapper.IrisBlockMaterial.AIR: // shouldn't normally be rendered, but just in case color = ColorUtil.HOT_PINK; break; - case LEAVES: + case IBlockStateWrapper.IrisBlockMaterial.LEAVES: color = ColorUtil.DARK_GREEN; break; - case STONE: + case IBlockStateWrapper.IrisBlockMaterial.STONE: color = ColorUtil.GRAY; break; - case WOOD: + case IBlockStateWrapper.IrisBlockMaterial.WOOD: color = ColorUtil.BROWN; break; - case METAL: + case IBlockStateWrapper.IrisBlockMaterial.METAL: color = ColorUtil.DARK_GRAY; break; - case DIRT: + case IBlockStateWrapper.IrisBlockMaterial.DIRT: color = ColorUtil.LIGHT_BROWN; break; - case LAVA: + case IBlockStateWrapper.IrisBlockMaterial.LAVA: color = ColorUtil.ORANGE; break; - case DEEPSLATE: + case IBlockStateWrapper.IrisBlockMaterial.DEEPSLATE: color = ColorUtil.BLACK; break; - case SNOW: + case IBlockStateWrapper.IrisBlockMaterial.SNOW: color = ColorUtil.WHITE; break; - case SAND: + case IBlockStateWrapper.IrisBlockMaterial.SAND: color = ColorUtil.TAN; break; - case TERRACOTTA: + case IBlockStateWrapper.IrisBlockMaterial.TERRACOTTA: color = ColorUtil.DARK_ORANGE; break; - case NETHER_STONE: + case IBlockStateWrapper.IrisBlockMaterial.NETHER_STONE: color = ColorUtil.DARK_RED; break; - case WATER: + case IBlockStateWrapper.IrisBlockMaterial.WATER: color = ColorUtil.BLUE; break; - case GRASS: + case IBlockStateWrapper.IrisBlockMaterial.GRASS: color = ColorUtil.GREEN; break; - case ILLUMINATED: + case IBlockStateWrapper.IrisBlockMaterial.ILLUMINATED: color = ColorUtil.YELLOW; break; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodQuadBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodQuadBuilder.java index ab3605bdd..07744dcb9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodQuadBuilder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodQuadBuilder.java @@ -24,7 +24,6 @@ import java.nio.ByteOrder; import java.util.*; import com.seibel.distanthorizons.api.enums.config.EDhApiGrassSideRendering; -import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial; import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; @@ -51,9 +50,7 @@ public class LodQuadBuilder private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); - @Deprecated public final boolean skipQuadsWithZeroSkylight; - @Deprecated public final short skyLightCullingBelow; @SuppressWarnings("unchecked") @@ -125,7 +122,7 @@ public class LodQuadBuilder // constructor // //=============// - public LodQuadBuilder(boolean doTransparency, IClientLevelWrapper clientLevelWrapper) + public LodQuadBuilder(boolean enableSkylightCulling, short skyLightCullingBelow, boolean doTransparency, IClientLevelWrapper clientLevelWrapper) { this.doTransparency = doTransparency; for (int i = 0; i < 6; i++) @@ -134,8 +131,8 @@ public class LodQuadBuilder this.transparentQuads[i] = new ArrayList<>(); } - this.skipQuadsWithZeroSkylight = false; - this.skyLightCullingBelow = 0; + this.skipQuadsWithZeroSkylight = enableSkylightCulling; + this.skyLightCullingBelow = skyLightCullingBelow; this.clientLevelWrapper = clientLevelWrapper; this.debugRenderingMode = Config.Client.Advanced.Debugging.debugRendering.get(); @@ -276,7 +273,7 @@ public class LodQuadBuilder int color = quad.color; // use custom side color logic for grass blocks - if (quad.irisBlockMaterialId == EDhApiBlockMaterial.GRASS.index) + if (quad.irisBlockMaterialId == IBlockStateWrapper.IrisBlockMaterial.GRASS) { // only use dirt colors if debug rendering is disabled if (this.debugRenderingMode == EDhApiDebugRendering.OFF) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java index 5c658e232..6f3117a6b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java @@ -20,7 +20,6 @@ package com.seibel.distanthorizons.core.dataObjects.transformers; import com.seibel.distanthorizons.api.enums.config.EDhApiBlocksToAvoid; -import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; @@ -33,7 +32,6 @@ import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.ColorUtil; import com.seibel.distanthorizons.core.util.FullDataPointUtil; -import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.RenderDataPointUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; @@ -111,85 +109,74 @@ public class FullDataToRenderDataTransformer } columnSource.markNotEmpty(); - int baseX = DhSectionPos.getMinCornerBlockX(pos); - int baseZ = DhSectionPos.getMinCornerBlockZ(pos); - for (int x = 0; x < DhSectionPos.getWidthCountForLowerDetailedSection(pos, dataDetail); x++) + if (dataDetail == columnSource.getDataDetailLevel()) { - for (int z = 0; z < DhSectionPos.getWidthCountForLowerDetailedSection(pos, dataDetail); z++) - { - throwIfThreadInterrupted(); - - ColumnArrayView columnArrayView = columnSource.getVerticalDataPointView(x, z); - LongArrayList dataColumn = fullDataSource.get(x, z); - updateRenderDataViewWithFullDataColumn(level, fullDataSource.mapping, baseX + x, baseZ + z, columnArrayView, dataColumn); - } - } - - columnSource.fillDebugFlag(0, 0, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.DebugSourceFlag.FULL); + int baseX = DhSectionPos.getMinCornerBlockX(pos); + int baseZ = DhSectionPos.getMinCornerBlockZ(pos); + + for (int x = 0; x < DhSectionPos.getWidthCountForLowerDetailedSection(pos, dataDetail); x++) + { + for (int z = 0; z < DhSectionPos.getWidthCountForLowerDetailedSection(pos, dataDetail); z++) + { + throwIfThreadInterrupted(); + + ColumnArrayView columnArrayView = columnSource.getVerticalDataPointView(x, z); + LongArrayList dataColumn = fullDataSource.get(x, z); + convertColumnData(level, fullDataSource.mapping, baseX + x, baseZ + z, columnArrayView, dataColumn); + } + } + + columnSource.fillDebugFlag(0, 0, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.DebugSourceFlag.FULL); - return columnSource; - } - - /** Updates the given {@link ColumnArrayView} to match the incoming Full data {@link LongArrayList} */ - public static void updateRenderDataViewWithFullDataColumn( - IDhClientLevel level, - FullDataPointIdMap fullDataMapping, int blockX, int blockZ, - ColumnArrayView columnArrayView, - LongArrayList fullDataColumn) - { - if (fullDataColumn == null || fullDataColumn.size() == 0) - { - return; - } - - int dataTotalLength = fullDataColumn.size(); - if (dataTotalLength > columnArrayView.verticalSize()) - { - ColumnArrayView totalColumnData = new ColumnArrayView(new LongArrayList(new long[dataTotalLength]), dataTotalLength, 0, dataTotalLength); - iterateAndConvert(level, fullDataMapping, blockX, blockZ, totalColumnData, fullDataColumn); - columnArrayView.changeVerticalSizeFrom(totalColumnData); } else { - iterateAndConvert(level, fullDataMapping, blockX, blockZ, columnArrayView, fullDataColumn); //Directly use the arrayView since it fits. + throw new UnsupportedOperationException("To be implemented"); + //FIXME: Implement different size creation of renderData + } + return columnSource; + } + + + + //================// + // helper methods // + //================// + + /** + * Called in loops that may run for an extended period of time.
+ * This is necessary to allow canceling these transformers since running + * them after the client has left a given world will throw exceptions. + */ + private static void throwIfThreadInterrupted() throws InterruptedException + { + if (Thread.interrupted()) + { + throw new InterruptedException(FullDataToRenderDataTransformer.class.getSimpleName() + " task interrupted."); } } + + + // TODO what does this mean? private static void iterateAndConvert( - IDhClientLevel level, FullDataPointIdMap fullDataMapping, - int blockX, int blockZ, + IDhClientLevel level, FullDataPointIdMap fullDataMapping, + int blockX, int blockZ, ColumnArrayView renderColumnData, LongArrayList fullColumnData) { - boolean ignoreNonCollidingBlocks = (Config.Client.Advanced.Graphics.Quality.blocksToIgnore.get() == EDhApiBlocksToAvoid.NON_COLLIDING); + boolean avoidSolidBlocks = (Config.Client.Advanced.Graphics.Quality.blocksToIgnore.get() == EDhApiBlocksToAvoid.NON_COLLIDING); boolean colorBelowWithAvoidedBlocks = Config.Client.Advanced.Graphics.Quality.tintWithAvoidedBlocks.get(); HashSet blockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredBlocks(level.getLevelWrapper()); - HashSet caveBlockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredCaveBlocks(level.getLevelWrapper()); - - boolean caveCullingEnabled = - Config.Client.Advanced.Graphics.AdvancedGraphics.enableCaveCulling.get() - && ( - // dimensions with a ceiling will be all caves so we don't want cave culling - !level.getLevelWrapper().hasCeiling() - // the end has a lot of overhangs with 0 lighting above the void, which look broken with - // the current cave culling logic (this could probably be improved, but just skipping it works best for now) - && !level.getLevelWrapper().getDimensionType().isTheEnd() - ); boolean isVoid = true; - int colorToApplyToNextBlock = -1; int lastColor = 0; int lastBottom = -10000; - int skylightToApplyToNextBlock = -1; int blocklightToApplyToNextBlock = -1; int columnOffset = 0; - IBiomeWrapper biome = null; - IBlockStateWrapper block = null; - - // goes from the top down for (int i = 0; i < fullColumnData.size(); i++) { @@ -200,6 +187,23 @@ public class FullDataToRenderDataTransformer int blockLight = FullDataPointUtil.getBlockLight(fullData); int skyLight = FullDataPointUtil.getSkyLight(fullData); + // TODO how should corrupted data be handled? + // TODO why is the full data corrupted in the first place? FullDataPointUtil hasn't been changed in a long time, could one of the full data point objects be corrupted? + // TODO if either of these happen the ID might also be invalid + //if (bottomY + blockHeight > 300) + //{ + // // this data point is too tall, it's probably a monolith + // int k = 0; + // throw new RuntimeException(); + //} + //if (light > 16 || light < 0) + //{ + // // light is out of range + // throw new RuntimeException(); + //} + + IBiomeWrapper biome; + IBlockStateWrapper block; try { biome = fullDataMapping.getBiomeWrapper(id); @@ -224,72 +228,28 @@ public class FullDataToRenderDataTransformer } - //====================// - // ignored block and // - // cave culling check // - //====================// - - boolean ignoreBlock = blockStatesToIgnore.contains(block); - boolean caveBlock = caveBlockStatesToIgnore.contains(block); - if (caveBlock) + if (blockStatesToIgnore.contains(block)) { - if (caveCullingEnabled - // assume this data point is underground if it has no sky-light - && skyLight == LodUtil.MIN_MC_LIGHT - // cave culling shouldn't happen when at the top of the world - && columnOffset != 0 - // cave culling can't happen when at the bottom of the world - && columnOffset != fullColumnData.size()) - { - // we need to get the next sky/block lights because - // the air block here will always have a light of 0/0 due to only the top of the LOD's light being saved. - long nextFullData = fullColumnData.getLong(i+1); - int nextSkyLight = FullDataPointUtil.getSkyLight(nextFullData); - - if (nextSkyLight == LodUtil.MIN_MC_LIGHT - && ColorUtil.getAlpha(lastColor) == 255) - { - // replace the previous block with new bottom - long columnData = renderColumnData.get(columnOffset - 1); - columnData = RenderDataPointUtil.setYMin(columnData, bottomY); - renderColumnData.set(columnOffset - 1, columnData); - } - - continue; - } - - - if (ignoreBlock) - { - // this is a merged block and a cave block, so it should never be rendered - continue; - } - } - else if (ignoreBlock) - { - // this is an ignored block, but shouldn't be merged like a cave block + // Don't render: air, barriers, light blocks, etc. continue; } - //===================// - // solid block check // - //===================// - - if (ignoreNonCollidingBlocks && !block.isSolid() && !block.isLiquid() && block.getOpacity() != LodUtil.BLOCK_FULLY_OPAQUE) + // solid block check + if (avoidSolidBlocks && !block.isSolid() && !block.isLiquid() && block.getOpacity() != IBlockStateWrapper.FULLY_OPAQUE) { if (colorBelowWithAvoidedBlocks) { int tempColor = level.computeBaseColor(new DhBlockPos(blockX, bottomY + level.getMinY(), blockZ), biome, block); - // don't transfer the color when alpha is 0 - if (ColorUtil.getAlpha(tempColor) != 0) + if (ColorUtil.getAlpha(tempColor) == 0) { - // don't transfer alpha if for some reason grass is semi transparent - colorToApplyToNextBlock = ColorUtil.setAlpha(tempColor,255); - - skylightToApplyToNextBlock = skyLight; - blocklightToApplyToNextBlock = blockLight; + //make sure to not transfer the color when alpha is 0 + continue; } + //mare sure to not trnasfer alpha if for some reason grass is semi transparent + colorToApplyToNextBlock = ColorUtil.setAlpha(tempColor,255); + skylightToApplyToNextBlock = skyLight; + blocklightToApplyToNextBlock = blockLight; } // don't add this block @@ -311,11 +271,11 @@ public class FullDataToRenderDataTransformer skyLight = skylightToApplyToNextBlock; blockLight = blocklightToApplyToNextBlock; } - - //check if they share a top-bottom face and if they have same color + + //check if they share a top-bottom face and if they have same collor if (color == lastColor && bottomY + blockHeight == lastBottom && columnOffset > 0) { - //replace the previous block with new bottom + //replace the previus block with new bottom long columnData = renderColumnData.get(columnOffset - 1); columnData = RenderDataPointUtil.setYMin(columnData, bottomY); renderColumnData.set(columnOffset - 1, columnData); @@ -324,13 +284,13 @@ public class FullDataToRenderDataTransformer { // add the block isVoid = false; - long columnData = RenderDataPointUtil.createDataPoint(bottomY + blockHeight, bottomY, color, skyLight, blockLight, block.getMaterialId()); + long columnData = RenderDataPointUtil.createDataPoint(bottomY + blockHeight, bottomY, color, skyLight, blockLight, block.getIrisBlockMaterialId()); renderColumnData.set(columnOffset, columnData); columnOffset++; } lastBottom = bottomY; lastColor = color; - + } @@ -340,22 +300,24 @@ public class FullDataToRenderDataTransformer } } - - - //================// - // helper methods // - //================// - - /** - * Called in loops that may run for an extended period of time.
- * This is necessary to allow canceling these transformers since running - * them after the client has left a given world will throw exceptions. - */ - private static void throwIfThreadInterrupted() throws InterruptedException + // TODO what does this mean? + public static void convertColumnData(IDhClientLevel level, FullDataPointIdMap fullDataMapping, int blockX, int blockZ, ColumnArrayView columnArrayView, LongArrayList fullDataColumn) { - if (Thread.interrupted()) + if (fullDataColumn == null || fullDataColumn.size() == 0) { - throw new InterruptedException(FullDataToRenderDataTransformer.class.getSimpleName() + " task interrupted."); + return; + } + + int dataTotalLength = fullDataColumn.size(); + if (dataTotalLength > columnArrayView.verticalSize()) + { + ColumnArrayView totalColumnData = new ColumnArrayView(new LongArrayList(new long[dataTotalLength]), dataTotalLength, 0, dataTotalLength); + iterateAndConvert(level, fullDataMapping, blockX, blockZ, totalColumnData, fullDataColumn); + columnArrayView.changeVerticalSizeFrom(totalColumnData); + } + else + { + iterateAndConvert(level, fullDataMapping, blockX, blockZ, columnArrayView, fullDataColumn); //Directly use the arrayView since it fits. } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/LodDataBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/LodDataBuilder.java index 917aad07e..5f3001621 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/LodDataBuilder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/LodDataBuilder.java @@ -31,6 +31,7 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.enums.EDhDirection; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhBlockPos; +import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.FullDataPointUtil; import com.seibel.distanthorizons.core.util.LodUtil; @@ -46,6 +47,8 @@ public class LodDataBuilder { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final IBlockStateWrapper AIR = SingletonInjector.INSTANCE.get(IWrapperFactory.class).getAirBlockStateWrapper(); + /** how many chunks wide the {@link FullDataSourceV2} is. */ + private static final int NUMB_OF_CHUNKS_WIDE = FullDataSourceV2.WIDTH / LodUtil.CHUNK_WIDTH; private static boolean getTopErrorLogged = false; @@ -64,8 +67,12 @@ public class LodDataBuilder - int sectionPosX = getXOrZSectionPosFromChunkPos(chunkWrapper.getChunkPos().x); - int sectionPosZ = getXOrZSectionPosFromChunkPos(chunkWrapper.getChunkPos().z); + // get the section position + int sectionPosX = chunkWrapper.getChunkPos().x; + // negative positions start at -1 so the logic there is slightly different + sectionPosX = (sectionPosX < 0) ? ((sectionPosX + 1) / NUMB_OF_CHUNKS_WIDE) - 1 : (sectionPosX / NUMB_OF_CHUNKS_WIDE); + int sectionPosZ = chunkWrapper.getChunkPos().z; + sectionPosZ = (sectionPosZ < 0) ? ((sectionPosZ + 1) / NUMB_OF_CHUNKS_WIDE) - 1 : (sectionPosZ / NUMB_OF_CHUNKS_WIDE); long pos = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, sectionPosX, sectionPosZ); FullDataSourceV2 dataSource = FullDataSourceV2.createEmpty(pos); @@ -91,30 +98,30 @@ public class LodDataBuilder // -3 -> 1 // -4 -> 0 --- // -5 -> 3 - chunkOffsetX = ((chunkOffsetX) % FullDataSourceV2.NUMB_OF_CHUNKS_WIDE); + chunkOffsetX = ((chunkOffsetX) % NUMB_OF_CHUNKS_WIDE); if (chunkOffsetX != 0) { - chunkOffsetX += FullDataSourceV2.NUMB_OF_CHUNKS_WIDE; + chunkOffsetX += NUMB_OF_CHUNKS_WIDE; } } else { - chunkOffsetX %= FullDataSourceV2.NUMB_OF_CHUNKS_WIDE; + chunkOffsetX %= NUMB_OF_CHUNKS_WIDE; } chunkOffsetX *= LodUtil.CHUNK_WIDTH; int chunkOffsetZ = chunkWrapper.getChunkPos().z; if (chunkWrapper.getChunkPos().z < 0) { - chunkOffsetZ = ((chunkOffsetZ) % FullDataSourceV2.NUMB_OF_CHUNKS_WIDE); + chunkOffsetZ = ((chunkOffsetZ) % NUMB_OF_CHUNKS_WIDE); if (chunkOffsetZ != 0) { - chunkOffsetZ += FullDataSourceV2.NUMB_OF_CHUNKS_WIDE; + chunkOffsetZ += NUMB_OF_CHUNKS_WIDE; } } else { - chunkOffsetZ %= FullDataSourceV2.NUMB_OF_CHUNKS_WIDE; + chunkOffsetZ %= NUMB_OF_CHUNKS_WIDE; } chunkOffsetZ *= LodUtil.CHUNK_WIDTH; @@ -205,7 +212,6 @@ public class LodDataBuilder longs.add(FullDataPointUtil.encode(mappedId, lastY - y, y + 1 - chunkWrapper.getMinBuildHeight(), blockLight, skyLight)); biome = newBiome; blockState = newBlockState; - mappedId = dataSource.mapping.addIfNotPresentAndGetId(biome, blockState); blockLight = newBlockLight; skyLight = newSkyLight; @@ -290,28 +296,19 @@ public class LodDataBuilder // this block isn't on a chunk boundary, check if it is next to a transparent/air block IBlockStateWrapper blockState = chunkWrapper.getBlockState(testBlockPos); - return blockState.isAir() || blockState.getOpacity() != LodUtil.BLOCK_FULLY_OPAQUE; + return blockState.isAir() || blockState.getOpacity() != IBlockStateWrapper.FULLY_OPAQUE; } /** @throws ClassCastException if an API user returns the wrong object type(s) */ - public static FullDataSourceV2 createFromApiChunkData(DhApiChunk apiChunk) throws ClassCastException, DataCorruptedException + public static FullDataSourceV2 createFromApiChunkData(DhApiChunk dataPoints) throws ClassCastException, DataCorruptedException { - // get the section position - int sectionPosX = getXOrZSectionPosFromChunkPos(apiChunk.chunkPosX); - int sectionPosZ = getXOrZSectionPosFromChunkPos(apiChunk.chunkPosZ); - long pos = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, sectionPosX, sectionPosZ); - - // chunk relative block position in the data source - int relSourceBlockX = Math.floorMod(apiChunk.chunkPosX, 4) * LodUtil.CHUNK_WIDTH; - int relSourceBlockZ = Math.floorMod(apiChunk.chunkPosZ, 4) * LodUtil.CHUNK_WIDTH; - - FullDataSourceV2 dataSource = FullDataSourceV2.createEmpty(pos); - for (int relBlockZ = 0; relBlockZ < LodUtil.CHUNK_WIDTH; relBlockZ++) + FullDataSourceV2 accessor = FullDataSourceV2.createEmpty(DhSectionPos.encode(new DhChunkPos(dataPoints.chunkPosX, dataPoints.chunkPosZ))); + for (int relZ = 0; relZ < LodUtil.CHUNK_WIDTH; relZ++) { - for (int relBlockX = 0; relBlockX < LodUtil.CHUNK_WIDTH; relBlockX++) + for (int relX = 0; relX < LodUtil.CHUNK_WIDTH; relX++) { - List columnDataPoints = apiChunk.getDataPoints(relBlockX, relBlockZ); + List columnDataPoints = dataPoints.getDataPoints(relX, relZ); // this null check does 2 nice things at the same time: @@ -325,7 +322,7 @@ public class LodDataBuilder { DhApiTerrainDataPoint dataPoint = columnDataPoints.get(index); - int id = dataSource.mapping.addIfNotPresentAndGetId( + int id = accessor.mapping.addIfNotPresentAndGetId( (IBiomeWrapper) (dataPoint.biomeWrapper), (IBlockStateWrapper) (dataPoint.blockStateWrapper) ); @@ -333,7 +330,7 @@ public class LodDataBuilder packedDataPoints.set(index, FullDataPointUtil.encode( id, dataPoint.topYBlockPos - dataPoint.bottomYBlockPos, - dataPoint.bottomYBlockPos - apiChunk.bottomYBlockPos, + dataPoint.bottomYBlockPos - dataPoints.topYBlockPos, (byte) (dataPoint.blockLightLevel), (byte) (dataPoint.skyLightLevel) )); @@ -341,14 +338,11 @@ public class LodDataBuilder // TODO add the ability for API users to define a different compression mode // or add a "unkown" compression mode - dataSource.setSingleColumn( - packedDataPoints, - relBlockX + relSourceBlockX, relBlockZ + relSourceBlockZ, - EDhApiWorldGenerationStep.LIGHT, EDhApiWorldCompressionMode.MERGE_SAME_BLOCKS); - dataSource.isEmpty = false; + accessor.setSingleColumn(packedDataPoints, relX, relZ, EDhApiWorldGenerationStep.LIGHT, EDhApiWorldCompressionMode.MERGE_SAME_BLOCKS); } } - return dataSource; + + return accessor; } @@ -359,13 +353,4 @@ public class LodDataBuilder public static boolean canGenerateLodFromChunk(IChunkWrapper chunk) { return chunk != null && chunk.isLightCorrect(); } - public static int getXOrZSectionPosFromChunkPos(int chunkXOrZPos) - { - // get the section position - int sectionPos = chunkXOrZPos; - // negative positions start at -1 so the logic there is slightly different - sectionPos = (sectionPos < 0) ? ((sectionPos + 1) / FullDataSourceV2.NUMB_OF_CHUNKS_WIDE) - 1 : (sectionPos / FullDataSourceV2.NUMB_OF_CHUNKS_WIDE); - return sectionPos; - } - } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/enums/EDhDirection.java b/core/src/main/java/com/seibel/distanthorizons/core/enums/EDhDirection.java index 757200b18..ff7cebd50 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/enums/EDhDirection.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/enums/EDhDirection.java @@ -25,7 +25,7 @@ import java.util.Map; import java.util.function.Predicate; import java.util.stream.Collectors; -import com.seibel.distanthorizons.core.util.math.Vec3i; +import com.seibel.distanthorizons.coreapi.util.math.Vec3i; /** * An (almost) exact copy of Minecraft's diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java b/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java index 628aabf80..7c9c36308 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java @@ -41,17 +41,13 @@ import java.util.*; */ public class ClientOnlySaveStructure extends AbstractSaveStructure { - public static final String SERVER_DATA_FOLDER_NAME = "Distant_Horizons_server_data"; - public static final String REPLAY_SERVER_FOLDER_NAME = "REPLAY"; - public static final String INVALID_FILE_CHARACTERS_REGEX = "[\\\\/:*?\"<>|]"; - + final File folder; private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); private static final IMinecraftSharedWrapper MC_SHARED = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class); + public static final String INVALID_FILE_CHARACTERS_REGEX = "[\\\\/:*?\"<>|]"; - - private SubDimensionLevelMatcher subDimMatcher = null; - private final File folder; - private final HashMap levelWrapperToFileMap = new HashMap<>(); + SubDimensionLevelMatcher subDimMatcher = null; + final HashMap levelWrapperToFileMap = new HashMap<>(); @@ -241,7 +237,7 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure private static String getSaveStructureFolderPath() { String path = MC_SHARED.getInstallationDirectory().getPath() + File.separatorChar - + SERVER_DATA_FOLDER_NAME + File.separatorChar + + "Distant_Horizons_server_data" + File.separatorChar + getServerFolderName(); return path; } @@ -249,14 +245,6 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure /** Generated from the server the client is currently connected to. */ private static String getServerFolderName() { - // if connected to a replay we won't have any server info - // use the dedicated replay server folder - if (MC_CLIENT.connectedToReplay()) - { - return REPLAY_SERVER_FOLDER_NAME; - } - - // parse the current server's IP ParsedIp parsedIp = new ParsedIp(MC_CLIENT.getCurrentServerIp()); String serverIpCleaned = parsedIp.ip.replaceAll(INVALID_FILE_CHARACTERS_REGEX, ""); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java index 841b0b2f8..6263bc23c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java @@ -190,7 +190,7 @@ public class SubDimensionLevelMatcher implements AutoCloseable } FullDataSourceV2 newChunkSizedFullDataView = FullDataSourceV2.createFromChunk(newlyLoadedChunk); // convert to a data source for easier comparing - FullDataSourceV2 newDataSource = FullDataSourceV2.createEmpty(DhSectionPos.encodeContaining(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, this.playerData.playerBlockPos)); + FullDataSourceV2 newDataSource = FullDataSourceV2.createEmpty(DhSectionPos.encode(this.playerData.playerBlockPos)); newDataSource.update(newChunkSizedFullDataView); @@ -215,7 +215,7 @@ public class SubDimensionLevelMatcher implements AutoCloseable // get the data source to compare against try (IDhLevel tempLevel = new DhClientLevel(new ClientOnlySaveStructure(), this.currentClientLevel, testLevelFolder, false, null)) { - testFullDataSource = tempLevel.getFullDataProvider().getAsync(DhSectionPos.encodeContaining(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, this.playerData.playerBlockPos)).join(); + testFullDataSource = tempLevel.getFullDataProvider().getAsync(DhSectionPos.encode(this.playerData.playerBlockPos)).join(); if (testFullDataSource == null) { continue; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/AdjacentChunkHolder.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/AdjacentChunkHolder.java deleted file mode 100644 index a3d5603e3..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/AdjacentChunkHolder.java +++ /dev/null @@ -1,117 +0,0 @@ -package com.seibel.distanthorizons.core.generation; - -import com.seibel.distanthorizons.core.pos.DhChunkPos; -import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; -import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.HashSet; - -/** holds adjacent chunks without having to create new Pos objects */ -public class AdjacentChunkHolder -{ - final IChunkWrapper[] chunkArray = new IChunkWrapper[9]; - - - //==============// - // constructors // - //==============// - - public AdjacentChunkHolder(IChunkWrapper centerWrapper) { this.chunkArray[4] = centerWrapper; } - - public AdjacentChunkHolder(IChunkWrapper centerWrapper, @NotNull ArrayList nearbyChunkList) - { - this.chunkArray[4] = centerWrapper; - - DhChunkPos centerChunkPos = centerWrapper.getChunkPos(); - - // generate the list of chunk pos we need, - // currently a 3x3 grid - HashSet requestedAdjacentPositions = new HashSet<>(9); - for (int xOffset = -1; xOffset <= 1; xOffset++) - { - for (int zOffset = -1; zOffset <= 1; zOffset++) - { - DhChunkPos adjacentPos = new DhChunkPos(centerChunkPos.x + xOffset, centerChunkPos.z + zOffset); - requestedAdjacentPositions.add(adjacentPos); - } - } - - for (int chunkIndex = 0; chunkIndex < nearbyChunkList.size(); chunkIndex++) // using iterators in high traffic areas can cause GC issues due to allocating a bunch of iterators, use an indexed for-loop instead - { - IChunkWrapper chunk = nearbyChunkList.get(chunkIndex); - if (chunk != null && requestedAdjacentPositions.contains(chunk.getChunkPos())) - { - // remove the newly found position - requestedAdjacentPositions.remove(chunk.getChunkPos()); - - // add the adjacent chunk - this.add(chunk); - } - - if (requestedAdjacentPositions.isEmpty()) - { - // we found every chunk we needed, we don't need to keep iterating - break; - } - } - } - - - - //=========// - // methods // - //=========// - - public void add(IChunkWrapper centerWrapper) - { - DhChunkPos centerPos = this.chunkArray[4].getChunkPos(); - DhChunkPos offsetPos = centerWrapper.getChunkPos(); - - int offsetX = offsetPos.x - centerPos.x; - if (offsetX < -1 || offsetX > 1) - { - return; - } - - int offsetZ = offsetPos.z - centerPos.z; - if (offsetZ < -1 || offsetZ > 1) - { - return; - } - - // equivalent to 4 + offsetX + (offsetZ * 3). - this.chunkArray[4 + offsetX + offsetZ + (offsetZ << 1)] = centerWrapper; - } - - public IChunkWrapper getByBlockPos(int blockX, int blockZ) - { - int chunkX = BitShiftUtil.divideByPowerOfTwo(blockX, 4); - int chunkZ = BitShiftUtil.divideByPowerOfTwo(blockZ, 4); - IChunkWrapper centerChunk = this.chunkArray[4]; - DhChunkPos centerPos = centerChunk.getChunkPos(); - if (centerPos.x == chunkX && centerPos.z == chunkZ) - { - return centerChunk; - } - - int offsetX = chunkX - centerPos.x; - if (offsetX < -1 || offsetX > 1) - { - return null; - } - - int offsetZ = chunkZ - centerPos.z; - if (offsetZ < -1 || offsetZ > 1) - { - return null; - } - - // equivalent to 4 + offsetX + (offsetZ * 3). - return this.chunkArray[4 + offsetX + offsetZ + (offsetZ << 1)]; - } - - -} - \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/DhLightingEngine.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/DhLightingEngine.java index c160ef482..6648f62ee 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/DhLightingEngine.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/DhLightingEngine.java @@ -26,6 +26,7 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; +import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import org.apache.logging.log4j.Logger; import java.util.*; @@ -151,7 +152,7 @@ public class DhLightingEngine for (int y = maxY; y >= minY; y--) { IBlockStateWrapper block = chunk.getBlockState(relX, y, relZ); - if (block != null && block.getOpacity() != LodUtil.BLOCK_FULLY_TRANSPARENT) + if (block != null && block.getOpacity() != IBlockStateWrapper.FULLY_TRANSPARENT) { // keep moving down until we find a non-transparent block break; @@ -305,6 +306,64 @@ public class DhLightingEngine } + /** holds the adjacent chunks without having to create new Pos objects */ + private static class AdjacentChunkHolder + { + final IChunkWrapper[] chunkArray = new IChunkWrapper[9]; + + + public AdjacentChunkHolder(IChunkWrapper centerWrapper) { this.chunkArray[4] = centerWrapper; } + + + public void add(IChunkWrapper centerWrapper) + { + DhChunkPos centerPos = this.chunkArray[4].getChunkPos(); + DhChunkPos offsetPos = centerWrapper.getChunkPos(); + + int offsetX = offsetPos.x - centerPos.x; + if (offsetX < -1 || offsetX > 1) + { + return; + } + + int offsetZ = offsetPos.z - centerPos.z; + if (offsetZ < -1 || offsetZ > 1) + { + return; + } + + // equivalent to 4 + offsetX + (offsetZ * 3). + this.chunkArray[4 + offsetX + offsetZ + (offsetZ << 1)] = centerWrapper; + } + + public IChunkWrapper getByBlockPos(int blockX, int blockZ) + { + int chunkX = BitShiftUtil.divideByPowerOfTwo(blockX, 4); + int chunkZ = BitShiftUtil.divideByPowerOfTwo(blockZ, 4); + IChunkWrapper centerChunk = this.chunkArray[4]; + DhChunkPos centerPos = centerChunk.getChunkPos(); + if (centerPos.x == chunkX && centerPos.z == chunkZ) + { + return centerChunk; + } + + int offsetX = chunkX - centerPos.x; + if (offsetX < -1 || offsetX > 1) + { + return null; + } + + int offsetZ = chunkZ - centerPos.z; + if (offsetZ < -1 || offsetZ > 1) + { + return null; + } + + // equivalent to 4 + offsetX + (offsetZ * 3). + return this.chunkArray[4 + offsetX + offsetZ + (offsetZ << 1)]; + } + } + /** * Holds all potential {@link LightPos} objects a lighting task may need. * This is done so existing {@link LightPos} objects can be repurposed instead of destroyed, diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java index 64b8e9db3..452a7b97e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java @@ -79,7 +79,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb // TODO this logic isn't great and can cause a limit to how many threads could be used for world generation, // however it won't cause duplicate requests or concurrency issues, so it will be good enough for now. // A good long term fix may be to either: - // 1. allow the generator to deal with larger sections (let the generator threads split up larger tasks into smaller ones + // 1. allow the generator to deal with larger sections (let the generator threads split up larger tasks into smaller one // 2. batch requests better. instead of sending 4 individual tasks of detail level N, send 1 task of detail level n+1 private final ExecutorService queueingThread = ThreadUtil.makeSingleThreadPool("World Gen Queue"); private boolean generationQueueRunning = false; @@ -227,9 +227,6 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb catch (Exception e) { LOGGER.error("queueing exception: " + e.getMessage(), e); - } - finally - { this.generationQueueRunning = false; } }); @@ -377,7 +374,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb // don't log the shutdown exceptions if (!LodUtil.isInterruptOrReject(exception)) { - LOGGER.error("Error generating data for pos: " + DhSectionPos.toString(taskPos), exception); + LOGGER.error("Error generating data for section " + taskPos, exception); } newTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateFail())); @@ -387,11 +384,11 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb newTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateSuccess(DhSectionPos.encode(granularity, DhSectionPos.getX(taskPos), DhSectionPos.getZ(taskPos))))); } boolean worked = this.inProgressGenTasksByLodPos.remove(taskPos, newTaskGroup); - LodUtil.assertTrue(worked, "Unable to find in progress generator task with position ["+DhSectionPos.toString(taskPos)+"]"); + LodUtil.assertTrue(worked); } catch (Exception e) { - LOGGER.error("Unexpected error completing world gen task at pos: ["+DhSectionPos.toString(taskPos)+"].", e); + LOGGER.error("Unexpected error completing world gen task: "+taskPos, e); } }); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java index 1c3b1e08d..b556bdcff 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java @@ -19,28 +19,15 @@ package com.seibel.distanthorizons.core.level; -import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiChunkModifiedEvent; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; -import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; -import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; -import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.dataObjects.transformers.ChunkToLodBuilder; import com.seibel.distanthorizons.core.file.fullDatafile.DelayedFullDataSourceSaveCache; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; -import com.seibel.distanthorizons.core.render.renderer.generic.BeaconRenderHandler; -import com.seibel.distanthorizons.core.render.renderer.generic.CloudRenderHandler; -import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer; -import com.seibel.distanthorizons.core.render.renderer.generic.GenericRenderObjectFactory; -import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; import com.seibel.distanthorizons.core.sql.dto.ChunkHashDTO; -import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo; import com.seibel.distanthorizons.core.sql.repo.ChunkHashRepo; -import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import org.apache.logging.log4j.Logger; @@ -48,12 +35,8 @@ import org.jetbrains.annotations.Nullable; import java.io.File; import java.sql.SQLException; -import java.util.ArrayList; -import java.util.HashMap; import java.util.HashSet; -import java.util.List; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.atomic.AtomicInteger; public abstract class AbstractDhLevel implements IDhLevel { @@ -64,37 +47,21 @@ public abstract class AbstractDhLevel implements IDhLevel /** if this is null then the other handler is probably null too, but just in case */ @Nullable public ChunkHashRepo chunkHashRepo; - /** if this is null then the other handler is probably null too, but just in case */ - @Nullable - public BeaconBeamRepo beaconBeamRepo; protected final DelayedFullDataSourceSaveCache delayedFullDataSourceSaveCache = new DelayedFullDataSourceSaveCache(this::onDataSourceSave, 2_000); /** contains the {@link DhChunkPos} for each {@link DhSectionPos} that are queued to save via {@link AbstractDhLevel#delayedFullDataSourceSaveCache} */ protected final ConcurrentHashMap> updatedChunkPosSetBySectionPos = new ConcurrentHashMap<>(); - /** Will be null if clouds shouldn't be rendered for this level. */ - @Nullable - protected CloudRenderHandler cloudRenderHandler; - protected BeaconRenderHandler beaconRenderHandler; - //=============// // constructor // //=============// - protected AbstractDhLevel() - { - this.chunkToLodBuilder = new ChunkToLodBuilder(); - } + protected AbstractDhLevel() { this.chunkToLodBuilder = new ChunkToLodBuilder(); } - /** - * Creating the repos requires access to the level file, which isn't - * available at constructor time. - */ - protected void createAndSetSupportingRepos(File databaseFile) + protected void createAndSetChunkHashRepo(File databaseFile) { - // chunk hash ChunkHashRepo newChunkHashRepo = null; try { @@ -105,41 +72,6 @@ public abstract class AbstractDhLevel implements IDhLevel LOGGER.error("Unable to create [ChunkHashRepo], error: ["+e.getMessage()+"].", e); } this.chunkHashRepo = newChunkHashRepo; - - - // beacon beam - BeaconBeamRepo newBeaconBeamRepo = null; - try - { - newBeaconBeamRepo = new BeaconBeamRepo("jdbc:sqlite", databaseFile); - } - catch (SQLException e) - { - LOGGER.error("Unable to create [BeaconBeamRepo], error: ["+e.getMessage()+"].", e); - } - this.beaconBeamRepo = newBeaconBeamRepo; - } - - /** handles any setup that needs the repos to be created */ - protected void runRepoReliantSetup() - { - GenericObjectRenderer genericRenderer = this.getGenericRenderer(); - if (genericRenderer != null) - { - // only add clouds for certain dimension types - if (!this.getLevelWrapper().hasCeiling() - && !this.getLevelWrapper().getDimensionType().isTheEnd()) - { - this.cloudRenderHandler = new CloudRenderHandler(this, genericRenderer); - } - - - // shouldn't happen, but just in case - if (this.beaconBeamRepo != null) - { - this.beaconRenderHandler = new BeaconRenderHandler(this.beaconBeamRepo, genericRenderer); - } - } } @@ -194,13 +126,6 @@ public abstract class AbstractDhLevel implements IDhLevel } - - //=======// - // repos // - //=======// - - // chunk hash // - @Override public int getChunkHash(DhChunkPos pos) { @@ -223,55 +148,11 @@ public abstract class AbstractDhLevel implements IDhLevel - //=================// - // beacon handling // - //=================// - - @Override - public void setBeaconBeamsForChunk(DhChunkPos chunkPos, List newBeamList) - { - if (this.beaconRenderHandler != null) - { - this.beaconRenderHandler.setBeaconBeamsForChunk(chunkPos, newBeamList); - } - } - - @Override - public void loadBeaconBeamsInPos(long pos) - { - if (this.beaconRenderHandler != null) - { - this.beaconRenderHandler.loadBeaconBeamsInPos(pos); - } - } - @Override - public void unloadBeaconBeamsInPos(long pos) - { - if (this.beaconRenderHandler != null) - { - this.beaconRenderHandler.unloadBeaconBeamsInPos(pos); - } - } - - - //================// // base overrides // //================// @Override - public void close() - { - this.chunkToLodBuilder.close(); - - if (this.chunkHashRepo != null) - { - this.chunkHashRepo.close(); - } - if (this.beaconBeamRepo != null) - { - this.beaconBeamRepo.close(); - } - } + public void close() { this.chunkToLodBuilder.close(); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java b/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java index 99dd8fcf7..7ed8e1e43 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java @@ -27,12 +27,13 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.file.AbstractDataSourceHandler; import com.seibel.distanthorizons.core.file.fullDatafile.FullDataSourceProviderV2; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.render.LodQuadTree; import com.seibel.distanthorizons.core.render.RenderBufferHandler; -import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer; import com.seibel.distanthorizons.core.render.renderer.LodRenderer; import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; @@ -40,7 +41,9 @@ import org.apache.logging.log4j.Logger; import javax.annotation.WillNotClose; import java.io.Closeable; +import java.util.ArrayList; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.atomic.AtomicReference; public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.IDataSourceUpdateFunc @@ -53,14 +56,6 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I @WillNotClose public final FullDataSourceProviderV2 fullDataSourceProvider; public final AtomicReference ClientRenderStateRef = new AtomicReference<>(); - /** - * This is handled outside of the {@link ClientRenderState} to prevent destroying - * the {@link GenericObjectRenderer} when changing render distances or enabling/disabling rendering.

- * - * Destroying the {@link GenericObjectRenderer} would cause any existing bindings to be - * erroneously removed. - */ - public final GenericObjectRenderer genericRenderer = new GenericObjectRenderer(); @@ -113,7 +108,7 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I } clientRenderState.close(); - clientRenderState = new ClientRenderState(this.clientLevel, clientLevelWrapper, this.clientLevel.getFullDataProvider(), this.genericRenderer); + clientRenderState = new ClientRenderState(this.clientLevel, clientLevelWrapper, this.clientLevel.getFullDataProvider()); if (!this.ClientRenderStateRef.compareAndSet(null, clientRenderState)) { //FIXME: How to handle this? @@ -133,7 +128,7 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I } if (isBuffersDirty) { - clientRenderState.lodRenderer.bufferHandler.MarkAllBuffersDirty(); + clientRenderState.renderer.bufferHandler.MarkAllBuffersDirty(); } } @@ -146,7 +141,7 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I public boolean startRenderer(IClientLevelWrapper clientLevelWrapper) { // TODO why are we passing in a level wrapper? Our client level is already defined. - ClientRenderState ClientRenderState = new ClientRenderState(this.clientLevel, clientLevelWrapper, this.clientLevel.getFullDataProvider(), this.genericRenderer); + ClientRenderState ClientRenderState = new ClientRenderState(this.clientLevel, clientLevelWrapper, this.clientLevel.getFullDataProvider()); if (!this.ClientRenderStateRef.compareAndSet(null, ClientRenderState)) { LOGGER.warn("Failed to start renderer due to concurrency"); @@ -172,7 +167,7 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I // either the renderer hasn't been started yet, or is being reloaded return; } - ClientRenderState.lodRenderer.drawLods(ClientRenderState.clientLevelWrapper, renderEventParam, profiler); + ClientRenderState.renderer.drawLods(ClientRenderState.clientLevelWrapper, renderEventParam, profiler); } public void renderDeferred(DhApiRenderParam renderEventParam, IProfilerWrapper profiler) @@ -183,7 +178,7 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I // either the renderer hasn't been started yet, or is being reloaded return; } - ClientRenderState.lodRenderer.drawDeferredLods(ClientRenderState.clientLevelWrapper, renderEventParam, profiler); + ClientRenderState.renderer.drawDeferredLods(ClientRenderState.clientLevelWrapper, renderEventParam, profiler); } public void stopRenderer() @@ -280,25 +275,15 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I // helper classes // //================// - public static class ClientRenderState implements Closeable + public static class ClientRenderState { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); public final IClientLevelWrapper clientLevelWrapper; public final LodQuadTree quadtree; - public final RenderBufferHandler renderBufferHandler; - public final LodRenderer lodRenderer; + public final LodRenderer renderer; - - - //=============// - // constructor // - //=============// - - public ClientRenderState( - IDhClientLevel dhClientLevel, IClientLevelWrapper clientLevelWrapper, - FullDataSourceProviderV2 fullDataSourceProvider, - GenericObjectRenderer genericRenderer) + public ClientRenderState(IDhClientLevel dhClientLevel, IClientLevelWrapper clientLevelWrapper, FullDataSourceProviderV2 fullDataSourceProvider) { this.clientLevelWrapper = clientLevelWrapper; @@ -307,22 +292,17 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I 0, 0, fullDataSourceProvider); - this.renderBufferHandler = new RenderBufferHandler(this.quadtree); - this.lodRenderer = new LodRenderer(this.renderBufferHandler, genericRenderer); + RenderBufferHandler renderBufferHandler = new RenderBufferHandler(this.quadtree); + this.renderer = new LodRenderer(renderBufferHandler); } - //================// - // base overrides // - //================// - - @Override public void close() { LOGGER.info("Shutting down " + ClientRenderState.class.getSimpleName()); - this.lodRenderer.close(); + this.renderer.close(); this.quadtree.close(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index af69a3d73..9c7eda259 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -34,8 +34,6 @@ import com.seibel.distanthorizons.core.multiplayer.client.FullDataRefreshQueue; import com.seibel.distanthorizons.core.network.event.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos; -import com.seibel.distanthorizons.core.render.RenderBufferHandler; -import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; @@ -98,7 +96,6 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel LOGGER.warn("unable to create data folder."); } this.levelWrapper = clientLevelWrapper; - this.levelWrapper.setParentLevel(this); this.saveStructure = saveStructure; this.networkState = networkState; @@ -120,8 +117,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel this.clientside = new ClientLevelModule(this); - this.createAndSetSupportingRepos(this.dataFileHandler.repo.databaseFile); - this.runRepoReliantSetup(); + this.createAndSetChunkHashRepo(this.dataFileHandler.repo.databaseFile); if (enableRendering) { @@ -302,11 +298,10 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel this.eventSource.close(); } - this.levelWrapper.setParentLevel(null); this.clientside.close(); super.close(); this.dataFileHandler.close(); - LOGGER.info("Closed [" + DhClientLevel.class.getSimpleName() + "] for [" + this.levelWrapper + "]"); + LOGGER.info("Closed " + DhClientLevel.class.getSimpleName() + " for " + this.levelWrapper); } @Override @@ -319,15 +314,6 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel public boolean hasSkyLight() { return this.levelWrapper.hasSkyLight(); } - @Override - public GenericObjectRenderer getGenericRenderer() { return this.clientside.genericRenderer; } - @Override - public RenderBufferHandler getRenderBufferHandler() - { - ClientLevelModule.ClientRenderState renderState = this.clientside.ClientRenderStateRef.get(); - return (renderState != null) ? renderState.renderBufferHandler : null; - } - @Override public void onWorldGenTaskComplete(long pos) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java index 274bc8c39..42698d6d7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java @@ -24,13 +24,11 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSour import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.file.fullDatafile.FullDataSourceProviderV2; import com.seibel.distanthorizons.core.logging.f3.F3Screen; -import com.seibel.distanthorizons.core.render.RenderBufferHandler; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; -import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; @@ -68,11 +66,9 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev LOGGER.warn("unable to create data folder."); } this.serverLevelWrapper = serverLevelWrapper; - this.serverLevelWrapper.setParentLevel(this); this.serverside = new ServerLevelModule(this, saveStructure); this.clientside = new ClientLevelModule(this); - this.createAndSetSupportingRepos(this.serverside.fullDataFileHandler.repo.databaseFile); - this.runRepoReliantSetup(); + this.createAndSetChunkHashRepo(this.serverside.fullDataFileHandler.repo.databaseFile); LOGGER.info("Started " + DhClientServerLevel.class.getSimpleName() + " for " + serverLevelWrapper + " with saves at " + saveStructure); } @@ -106,7 +102,20 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev if (shouldDoWorldGen && !isWorldGenRunning) { // start world gen + + // create a new queue this.serverside.worldGenModule.startWorldGen(this.serverside.fullDataFileHandler, new ServerLevelModule.WorldGenState(this)); + + // TODO I think this used to queue the world gen + // is it still needed? + // populate the queue based on the current rendering tree + //ClientLevelModule.ClientRenderState renderState = this.clientside.ClientRenderStateRef.get(); + //Iterator> iterator = renderState.quadtree.leafNodeIterator(); + //while (iterator.hasNext()) + //{ + // QuadNode node = iterator.next(); + // //this.serverside.dataFileHandler.getAsync(node.sectionPos); + //} } else if (!shouldDoWorldGen && isWorldGenRunning) { @@ -151,7 +160,7 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev } @Override - public IClientLevelWrapper getClientLevelWrapper() { return MC_CLIENT.getWrappedClientLevel(); } + public IClientLevelWrapper getClientLevelWrapper() { return this.serverLevelWrapper.tryGetClientLevelWrapper(); } @Override public void clearRenderCache() @@ -223,16 +232,6 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev } - @Override - public GenericObjectRenderer getGenericRenderer() { return this.clientside.genericRenderer; } - @Override - public RenderBufferHandler getRenderBufferHandler() - { - ClientLevelModule.ClientRenderState renderState = this.clientside.ClientRenderStateRef.get(); - return (renderState != null) ? renderState.renderBufferHandler : null; - } - - //===============// // data handling // diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 7a84219f8..a4ee82e46 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -40,11 +40,9 @@ import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; -import com.seibel.distanthorizons.core.render.RenderBufferHandler; -import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; -import com.seibel.distanthorizons.core.util.math.Vec3d; +import com.seibel.distanthorizons.coreapi.util.math.Vec3d; import org.apache.logging.log4j.Logger; import java.text.MessageFormat; @@ -77,10 +75,6 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel private final ConcurrentMap requestGroupsByFutureId = new ConcurrentHashMap<>(); - //=============// - // constructor // - //=============// - public DhServerLevel(AbstractSaveStructure saveStructure, IServerLevelWrapper serverLevelWrapper, RemotePlayerConnectionHandler remotePlayerConnectionHandler) { if (saveStructure.getFullDataFolder(serverLevelWrapper).mkdirs()) @@ -89,8 +83,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel } this.serverLevelWrapper = serverLevelWrapper; this.serverside = new ServerLevelModule(this, saveStructure); - this.createAndSetSupportingRepos(this.serverside.fullDataFileHandler.repo.databaseFile); - this.runRepoReliantSetup(); + this.createAndSetChunkHashRepo(this.serverside.fullDataFileHandler.repo.databaseFile); LOGGER.info("Started DHLevel for {} with saves at {}", serverLevelWrapper, saveStructure); @@ -252,10 +245,6 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel this.worldGenPlayerCenteringQueue.add(serverPlayer); } - //=========// - // methods // - //=========// - public void removePlayer(IServerPlayerWrapper serverPlayer) { this.worldGenPlayerCenteringQueue.remove(serverPlayer); @@ -455,18 +444,6 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel } } - @Override - public GenericObjectRenderer getGenericRenderer() - { - // server-only levels don't support rendering - return null; - } - @Override - public RenderBufferHandler getRenderBufferHandler() - { - // server-only levels don't support rendering - return null; - } //===========// diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java index 15b392a6c..8f21f32ab 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java @@ -23,12 +23,8 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSour import com.seibel.distanthorizons.core.file.fullDatafile.FullDataSourceProviderV2; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.pos.DhChunkPos; -import com.seibel.distanthorizons.core.render.RenderBufferHandler; -import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer; -import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; -import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -48,10 +44,6 @@ public interface IDhLevel extends AutoCloseable void setChunkHash(DhChunkPos pos, int chunkHash); void updateChunkAsync(IChunkWrapper chunk); - void loadBeaconBeamsInPos(long pos); - void setBeaconBeamsForChunk(DhChunkPos chunkPos, List beamList); - void unloadBeaconBeamsInPos(long pos); - FullDataSourceProviderV2 getFullDataProvider(); AbstractSaveStructure getSaveStructure(); @@ -68,17 +60,5 @@ public interface IDhLevel extends AutoCloseable void addDebugMenuStringsToList(List messageList); - /** - * Will return null if the renderer isn't set up yet.
- * Not supported on the server-side. - */ - @Nullable - GenericObjectRenderer getGenericRenderer(); - /** - * Will return null if the renderer isn't set up yet.
- * Not supported on the server-side. - */ - @Nullable - RenderBufferHandler getRenderBufferHandler(); } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java b/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java index cc3b817cb..eacad1045 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java @@ -22,13 +22,14 @@ package com.seibel.distanthorizons.core.logging.f3; import com.seibel.distanthorizons.core.api.internal.SharedApi; import com.seibel.distanthorizons.core.level.IDhLevel; 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.coreapi.ModInfo; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.Nullable; +import java.lang.ref.WeakReference; import java.text.NumberFormat; import java.util.*; import java.util.concurrent.ThreadPoolExecutor; @@ -41,6 +42,23 @@ public class F3Screen + //============// + // properties // + //============// + + private static WeakReference renderBufferHandlerRef = new WeakReference<>(null); + public static void setRenderBufferHandler(@Nullable RenderBufferHandler renderBufferHandler) + { + if (renderBufferHandler != null && renderBufferHandlerRef.get() != null) + { + LOGGER.warn("multiple RenderBufferHandlers are active at once, the F3 menu may not be accurate."); + } + + renderBufferHandlerRef = new WeakReference<>(renderBufferHandler); + } + + + //=================// // injection point // //=================// @@ -88,28 +106,23 @@ public class F3Screen // chunk updates messageList.add(SharedApi.INSTANCE.getDebugMenuString()); messageList.add(""); + // rendering + RenderBufferHandler renderBufferHandler = renderBufferHandlerRef.get(); + if (renderBufferHandler != null) + { + messageList.add(renderBufferHandler.getVboRenderDebugMenuString()); + String showPassString = renderBufferHandler.getShadowPassRenderDebugMenuString(); + if (showPassString != null) + { + messageList.add(showPassString); + } + messageList.add(""); + } // world / levels world.addDebugMenuStringsToList(messageList); for (IDhLevel level : levelIterator) { level.addDebugMenuStringsToList(messageList); - // LOD rendering - RenderBufferHandler renderBufferHandler = level.getRenderBufferHandler(); - if (renderBufferHandler != null) - { - messageList.add(renderBufferHandler.getVboRenderDebugMenuString()); - String showPassString = renderBufferHandler.getShadowPassRenderDebugMenuString(); - if (showPassString != null) - { - messageList.add(showPassString); - } - } - // Generic rendering - GenericObjectRenderer genericRenderer = level.getGenericRenderer(); - if (genericRenderer != null) - { - messageList.add(genericRenderer.getVboRenderDebugMenuString()); - } } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhChunkPos.java b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhChunkPos.java index f81742b1f..fea474574 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhChunkPos.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhChunkPos.java @@ -19,8 +19,7 @@ package com.seibel.distanthorizons.core.pos; -import com.seibel.distanthorizons.core.util.LodUtil; -import com.seibel.distanthorizons.core.util.math.Vec3d; +import com.seibel.distanthorizons.coreapi.util.math.Vec3d; public class DhChunkPos { @@ -32,10 +31,6 @@ public class DhChunkPos - //==============// - // constructors // - //==============// - public DhChunkPos(int x, int z) { this.x = x; @@ -62,10 +57,6 @@ public class DhChunkPos - //=========// - // methods // - //=========// - public DhBlockPos center() { return new DhBlockPos(8 + this.x << 4, 0, 8 + this.z << 4); } public DhBlockPos corner() { return new DhBlockPos(this.x << 4, 0, this.z << 4); } @@ -79,17 +70,6 @@ public class DhChunkPos public DhBlockPos2D getMinBlockPos() { return new DhBlockPos2D(this.x << 4, this.z << 4); } - public boolean contains(DhBlockPos pos) - { - int minBlockX = this.getMinBlockX(); - int minBlockZ = this.getMinBlockZ(); - int maxBlockX = minBlockX + LodUtil.CHUNK_WIDTH; - int maxBlockZ = minBlockZ + LodUtil.CHUNK_WIDTH; - - return minBlockX <= pos.x && pos.x <= maxBlockX - && minBlockZ <= pos.z && pos.z <= maxBlockZ; - } - public long getLong() { return toLong(this.x, this.z); } public double distance(DhChunkPos other) @@ -97,11 +77,6 @@ public class DhChunkPos return Math.sqrt(Math.pow(x - other.x, 2) + Math.pow(z - other.z, 2)); } - - //================// - // base overrides // - //================// - @Override public boolean equals(Object obj) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java index e55072dbd..c37fac961 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java @@ -19,7 +19,6 @@ package com.seibel.distanthorizons.core.pos; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.enums.EDhDirection; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; @@ -102,32 +101,21 @@ public class DhSectionPos return data; } - /** Returns the section pos at the requested detail level containing the given BlockPos */ - public static long encodeContaining(byte outputSectionDetailLevel, DhBlockPos pos) + public static long encode(DhBlockPos pos) { return encodeBlockPos(pos.x, pos.z); } + public static long encode(DhBlockPos2D pos) { return encodeBlockPos(pos.x, pos.z); } + public static long encodeBlockPos(int blockX, int blockZ) { - int sectionPosX = getXOrZSectionPosFromChunkOrBlockPos(pos.x, false); - int sectionPosZ = getXOrZSectionPosFromChunkOrBlockPos(pos.z, false); - long blockPos = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, sectionPosX, sectionPosZ); - return convertToDetailLevel(blockPos, outputSectionDetailLevel); + long pos = encode(LodUtil.BLOCK_DETAIL_LEVEL, blockX, blockZ); + pos = convertToDetailLevel(pos, DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); + return pos; } - /** Returns the section pos at the requested detail level containing the given ChunkPos */ - public static long encodeContaining(byte outputSectionDetailLevel, DhChunkPos pos) + + public static long encode(DhChunkPos pos) { return encodeChunkPos(pos.x, pos.z); } + public static long encodeChunkPos(int chunkX, int chunkZ) { - int sectionPosX = getXOrZSectionPosFromChunkOrBlockPos(pos.x, true); - int sectionPosZ = getXOrZSectionPosFromChunkOrBlockPos(pos.z, true); - long blockPos = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, sectionPosX, sectionPosZ); - return convertToDetailLevel(blockPos, outputSectionDetailLevel); - } - private static int getXOrZSectionPosFromChunkOrBlockPos(int chunkXOrZPos, boolean isChunkPos) - { - int sectionPos = chunkXOrZPos; - int fullDataSourceWidth = isChunkPos ? FullDataSourceV2.NUMB_OF_CHUNKS_WIDE : (FullDataSourceV2.NUMB_OF_CHUNKS_WIDE * LodUtil.CHUNK_WIDTH); - - // negative positions start at -1 so the logic there is slightly different - sectionPos = (sectionPos < 0) - ? ((sectionPos + 1) / fullDataSourceWidth) - 1 - : (sectionPos / fullDataSourceWidth); - return sectionPos; + long pos = encode(LodUtil.CHUNK_DETAIL_LEVEL, chunkX, chunkZ); + pos = convertToDetailLevel(pos, DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL); + return pos; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/DhFrustumBounds.java b/core/src/main/java/com/seibel/distanthorizons/core/render/DhFrustumBounds.java index 0ef0b5f56..cf18751b8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/DhFrustumBounds.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/DhFrustumBounds.java @@ -1,9 +1,8 @@ package com.seibel.distanthorizons.core.render; import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiCullingFrustum; -import com.seibel.distanthorizons.api.objects.math.DhApiMat4f; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IOverrideInjector; -import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.coreapi.util.math.Mat4f; import org.joml.FrustumIntersection; import org.joml.Matrix4f; import org.joml.Matrix4fc; @@ -36,12 +35,12 @@ public class DhFrustumBounds implements IDhApiCullingFrustum //=========// @Override - public void update(int worldMinBlockY, int worldMaxBlockY, DhApiMat4f dhWorldViewProjection) + public void update(int worldMinBlockY, int worldMaxBlockY, Mat4f dhWorldViewProjection) { this.worldMinY = worldMinBlockY; this.worldMaxY = worldMaxBlockY; - Matrix4f worldViewProjection = new Matrix4f(Mat4f.createJomlMatrix(dhWorldViewProjection)); + Matrix4f worldViewProjection = new Matrix4f(dhWorldViewProjection.createJomlMatrix()); this.frustum.set(worldViewProjection); Matrix4fc matWorldViewProjectionInv = new Matrix4f(worldViewProjection).invert(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java index 0b24aa5c5..3c819d544 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java @@ -31,7 +31,6 @@ import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; -import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.util.objects.quadTree.QuadNode; @@ -330,10 +329,6 @@ public class LodQuadTree extends QuadTree implements IDebugRen } // all child positions are loaded, disable this section and enable its children. - if (renderSection.renderingEnabled) - { - this.level.unloadBeaconBeamsInPos(renderSection.pos); - } renderSection.renderingEnabled = false; // walk back down the tree and enable the child sections //TODO there are probably more efficient ways of doing this, but this will work for now @@ -388,7 +383,6 @@ public class LodQuadTree extends QuadTree implements IDebugRen if (!renderSection.renderingEnabled) { renderSection.renderingEnabled = true; - this.level.loadBeaconBeamsInPos(renderSection.pos); // delete/disable children, all of them will be a lower detail level than requested quadNode.deleteAllChildren((childRenderSection) -> diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java index bacd2eb7b..fbfd9a2ed 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java @@ -292,7 +292,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable } catch (Exception e) { - LOGGER.warn("Unable to get render source " + DhSectionPos.toString(this.pos) + ", error: " + e.getMessage(), e); + LOGGER.warn("Unable to get render source " + this.pos + ", error: " + e.getMessage(), e); this.renderSourceLoadingRefFuture = null; return null; } @@ -416,7 +416,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable } - this.level.unloadBeaconBeamsInPos(this.pos); if (this.renderBuffer != null) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/NeverCullFrustum.java b/core/src/main/java/com/seibel/distanthorizons/core/render/NeverCullFrustum.java index 611aa0ad6..7e10b0b44 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/NeverCullFrustum.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/NeverCullFrustum.java @@ -2,9 +2,8 @@ package com.seibel.distanthorizons.core.render; import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiCullingFrustum; import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiShadowCullingFrustum; -import com.seibel.distanthorizons.api.objects.math.DhApiMat4f; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IOverrideInjector; -import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.coreapi.util.math.Mat4f; /** * Dummy {@link IDhApiCullingFrustum} that allows everything through.
@@ -25,7 +24,7 @@ public class NeverCullFrustum implements IDhApiCullingFrustum, IDhApiShadowCulli //=========// @Override - public void update(int worldMinBlockY, int worldMaxBlockY, DhApiMat4f dhWorldViewProjection) { /* update isn't needed */ } + public void update(int worldMinBlockY, int worldMaxBlockY, Mat4f dhWorldViewProjection) { /* update isn't needed */ } @Override public boolean intersects(int lodBlockPosMinX, int lodBlockPosMinZ, int lodBlockWidth, int lodDetailLevel) { return true; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java index d02a238fb..f36280ae8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java @@ -41,9 +41,9 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRen import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IOverrideInjector; -import com.seibel.distanthorizons.core.util.math.Mat4f; -import com.seibel.distanthorizons.core.util.math.Vec3d; -import com.seibel.distanthorizons.core.util.math.Vec3f; +import com.seibel.distanthorizons.coreapi.util.math.Mat4f; +import com.seibel.distanthorizons.coreapi.util.math.Vec3d; +import com.seibel.distanthorizons.coreapi.util.math.Vec3f; import org.apache.logging.log4j.Logger; import org.joml.Matrix4f; import org.joml.Matrix4fc; @@ -100,6 +100,9 @@ public class RenderBufferHandler implements AutoCloseable { DhApi.overrides.bind(IDhApiShadowCullingFrustum.class, new NeverCullFrustum()); } + + + F3Screen.setRenderBufferHandler(this); } @@ -244,7 +247,7 @@ public class RenderBufferHandler implements AutoCloseable if (enableFrustumCulling) { int worldMinY = clientLevelWrapper.getMinHeight(); - int worldHeight = clientLevelWrapper.getMaxHeight(); + int worldHeight = clientLevelWrapper.getHeight(); Vec3d cameraPos = MC_RENDER.getCameraExactPosition(); @@ -414,6 +417,8 @@ public class RenderBufferHandler implements AutoCloseable renderSection.close(); } } + + F3Screen.setRenderBufferHandler(null); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/fog/LodFogConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/render/fog/LodFogConfig.java index 8e0873b93..be4adf73e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/fog/LodFogConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/fog/LodFogConfig.java @@ -173,7 +173,7 @@ public class LodFogConfig str.append("" + "float getNearFogThickness(float dist) \n" + "{ \n" + - " return linearFog(dist, uNearFogStart, uNearFogLength, 0.0, 1.0); \n" + + " return linearFog(dist, nearFogStart, nearFogLength, 0.0, 1.0); \n" + "} \n"); @@ -182,7 +182,7 @@ public class LodFogConfig str.append("\n" + "float getFarFogThickness(float dist) { return 0.0; } \n" + "float getHeightFogThickness(float dist) { return 0.0; } \n" + - "float calculateFarFogDepth(float horizontal, float dist, float uNearFogStart) { return 0.0; } \n" + + "float calculateFarFogDepth(float horizontal, float dist, float nearFogStart) { return 0.0; } \n" + "float calculateHeightFogDepth(float vertical, float realY) { return 0.0; } \n" + "float mixFogThickness(float near, float far, float height) \n" + "{ \n" + @@ -215,13 +215,13 @@ public class LodFogConfig "} \n"); - // Generate method: calculateFarFogDepth(float horizontal, float dist, float uNearFogStart); + // Generate method: calculateFarFogDepth(float horizontal, float dist, float nearFogStart); str.append("" + - "float calculateFarFogDepth(float horizontal, float dist, float uNearFogStart) \n" + + "float calculateFarFogDepth(float horizontal, float dist, float nearFogStart) \n" + "{ \n" + " return " + (heightFogMixMode == EDhApiHeightFogMixMode.BASIC ? - "(dist - uNearFogStart)/(1.0 - uNearFogStart);" : - "(horizontal - uNearFogStart)/(1.0 - uNearFogStart);") + + "(dist - nearFogStart)/(1.0 - nearFogStart);" : + "(horizontal - nearFogStart)/(1.0 - nearFogStart);") + "} \n"); // Generate method: float mixFogThickness(float near, float far, float height); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java index 4bbfbd6e8..ffe831b00 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java @@ -27,6 +27,7 @@ import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.util.objects.GLMessage; import com.seibel.distanthorizons.core.util.objects.GLMessageOutputStream; +import com.seibel.distanthorizons.core.util.objects.Pair; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.coreapi.ModInfo; import org.apache.logging.log4j.LogManager; @@ -39,6 +40,8 @@ import org.lwjgl.opengl.GLUtil; import java.io.PrintStream; import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import java.util.Arrays; import java.util.concurrent.ConcurrentLinkedQueue; /** @@ -73,9 +76,7 @@ public class GLProxy public boolean namedObjectSupported = false; // ~OpenGL 4.5 (UNUSED CURRENTLY) public boolean bufferStorageSupported = false; // ~OpenGL 4.4 - public boolean vertexAttributeBufferBindingSupported = false; // ~OpenGL 4.3 - public boolean instancedArraysSupported = false; - public boolean vertexAttribDivisorSupported = false; // OpenGL 3.3 or newer + public boolean VertexAttributeBufferBindingSupported = false; // ~OpenGL 4.3 private final EDhApiGpuUploadMethod preferredUploadMethod; @@ -90,14 +91,16 @@ public class GLProxy private GLProxy() throws IllegalStateException { // this must be created on minecraft's render context to work correctly - if (GLFW.glfwGetCurrentContext() == 0L) - { - throw new IllegalStateException(GLProxy.class.getSimpleName() + " was created outside the render thread!"); - } GL_LOGGER.info("Creating " + GLProxy.class.getSimpleName() + "... If this is the last message you see there must have been an OpenGL error."); GL_LOGGER.info("Lod Render OpenGL version [" + GL32.glGetString(GL32.GL_VERSION) + "]."); + // getting Minecraft's context has to be done on the render thread, + // where the GL context is + if (GLFW.glfwGetCurrentContext() == 0L) + { + throw new IllegalStateException(GLProxy.class.getSimpleName() + " was created outside the render thread!"); + } @@ -132,26 +135,21 @@ public class GLProxy // get GPU capabilities // //======================// + // Check if we can use the make-over version of Vertex Attribute, which is available in GL4.3 or after + this.VertexAttributeBufferBindingSupported = this.glCapabilities.glBindVertexBuffer != 0L; // Nullptr + // UNUSED currently // Check if we can use the named version of all calls, which is available in GL4.5 or after this.namedObjectSupported = this.glCapabilities.glNamedBufferData != 0L; //Nullptr + // get specific capabilities // Check if we can use the Buffer Storage, which is available in GL4.4 or after this.bufferStorageSupported = this.glCapabilities.glBufferStorage != 0L; // Nullptr if (!this.bufferStorageSupported) { - GL_LOGGER.info("This GPU doesn't support Buffer Storage (OpenGL 4.4), falling back to using other methods."); + GL_LOGGER.warn("This GPU doesn't support Buffer Storage (OpenGL 4.4), falling back to using other methods."); } - // Check if we can use the make-over version of Vertex Attribute, which is available in GL4.3 or after - this.vertexAttributeBufferBindingSupported = this.glCapabilities.glBindVertexBuffer != 0L; // Nullptr - - // used by instanced rendering - this.vertexAttribDivisorSupported = this.glCapabilities.OpenGL33; - // denotes if ARBInstancedArrays.glVertexAttribDivisorARB() is available or not - // can be used as a backup if MC didn't create a GL 3.3+ context - this.instancedArraysSupported = this.glCapabilities.GL_ARB_instanced_arrays; - // get the best automatic upload method String vendor = GL32.glGetString(GL32.GL_VENDOR).toUpperCase(); // example return: "NVIDIA CORPORATION" if (vendor.contains("NVIDIA") || vendor.contains("GEFORCE")) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/buffer/GLBuffer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/buffer/GLBuffer.java index bdfee1f4b..7d8608a83 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/buffer/GLBuffer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/buffer/GLBuffer.java @@ -20,7 +20,6 @@ package com.seibel.distanthorizons.core.render.glObject.buffer; import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod; -import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.render.glObject.GLProxy; import com.seibel.distanthorizons.core.util.LodUtil; @@ -141,10 +140,7 @@ public class GLBuffer implements AutoCloseable GL32.glDeleteBuffers(id); bufferCount.decrementAndGet(); - if (Config.Client.Advanced.Debugging.logBufferGarbageCollection.get()) - { - LOGGER.info("destroyed buffer [" + id + "], remaining: [" + BUFFER_ID_TO_PHANTOM.size() + "]"); - } + //LOGGER.info("destroyed buffer ["+id+"], remaining: ["+BUFFER_ID_TO_PHANTOM.size()+"]"); } }); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/shader/ShaderProgram.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/shader/ShaderProgram.java index fc821a461..8ade515eb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/shader/ShaderProgram.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/shader/ShaderProgram.java @@ -26,13 +26,12 @@ import java.util.Arrays; import java.util.List; import java.util.function.Supplier; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3i; import org.lwjgl.opengl.GL32; import org.lwjgl.system.MemoryStack; -import com.seibel.distanthorizons.core.util.math.Mat4f; -import com.seibel.distanthorizons.core.util.math.Vec3d; -import com.seibel.distanthorizons.core.util.math.Vec3f; +import com.seibel.distanthorizons.coreapi.util.math.Mat4f; +import com.seibel.distanthorizons.coreapi.util.math.Vec3d; +import com.seibel.distanthorizons.coreapi.util.math.Vec3f; /** @@ -155,7 +154,7 @@ public class ShaderProgram * @return Location of the Uniform * @throws RuntimeException if uniform not found */ - public int getUniformLocation(CharSequence name) throws RuntimeException + public int getUniformLocation(CharSequence name) { int i = GL32.glGetUniformLocation(id, name); if (i == -1) @@ -196,10 +195,11 @@ public class ShaderProgram { GL32.glUniform3f(location, value.x, value.y, value.z); } + /** Requires ShaderProgram binded. */ - public void setUniform(int location, DhApiVec3i value) + public void setUniform(int location, Vec3d value) { - GL32.glUniform3i(location, value.x, value.y, value.z); + GL32.glUniform3f(location, (float) value.x, (float) value.y, (float) value.z); } /** Requires ShaderProgram binded. */ diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/vertexAttribute/AbstractVertexAttribute.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/vertexAttribute/AbstractVertexAttribute.java index 3f27d12b6..3c7267c71 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/vertexAttribute/AbstractVertexAttribute.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/vertexAttribute/AbstractVertexAttribute.java @@ -48,7 +48,7 @@ public abstract class AbstractVertexAttribute public static AbstractVertexAttribute create() { - if (GLProxy.getInstance().vertexAttributeBufferBindingSupported) + if (GLProxy.getInstance().VertexAttributeBufferBindingSupported) { return new VertexAttributePostGL43(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java index 02243d36f..42d893b9d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java @@ -36,9 +36,9 @@ import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram; import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.AbstractVertexAttribute; import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.VertexPointer; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; -import com.seibel.distanthorizons.core.util.math.Mat4f; -import com.seibel.distanthorizons.core.util.math.Vec3d; -import com.seibel.distanthorizons.core.util.math.Vec3f; +import com.seibel.distanthorizons.coreapi.util.math.Mat4f; +import com.seibel.distanthorizons.coreapi.util.math.Vec3d; +import com.seibel.distanthorizons.coreapi.util.math.Vec3f; import org.apache.logging.log4j.LogManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -64,13 +64,13 @@ public class DebugRenderer // rendering setup private ShaderProgram basicShader; - private GLVertexBuffer vertexBuffer; - private GLElementBuffer outlineIndexBuffer; + private GLVertexBuffer boxBuffer; + private GLElementBuffer boxOutlineBuffer; private AbstractVertexAttribute va; private boolean init = false; // used when rendering - private Mat4f transformationMatrixThisFrame; + private Mat4f transformThiFrame; private Vec3f camPosFloatThisFrame; @@ -79,8 +79,8 @@ public class DebugRenderer - /** A box from 0,0,0 to 1,1,1 */ - private static final float[] BOX_VERTICES = { + // A box from 0,0,0 to 1,1,1 + private static final float[] box_vertices = { // Pos x y z 0, 0, 0, 1, 0, 0, @@ -92,7 +92,7 @@ public class DebugRenderer 0, 1, 1, }; - private static final int[] BOX_OUTLINE_INDICES = { + private static final int[] box_outline_indices = { 0, 1, 1, 2, 2, 3, @@ -115,47 +115,7 @@ public class DebugRenderer // constructor // //=============// - private DebugRenderer() { } - - public void init() - { - if (this.init) - { - return; - } - this.init = true; - - this.va = AbstractVertexAttribute.create(); - this.va.bind(); - // Pos - this.va.setVertexAttribute(0, 0, VertexPointer.addVec3Pointer(false)); - this.va.completeAndCheck(Float.BYTES * 3); - this.basicShader = new ShaderProgram("shaders/debug/vert.vert", "shaders/debug/frag.frag", - "fragColor", new String[]{"vPosition"}); - this.createBuffer(); - } - - private void createBuffer() - { - // box vertices - ByteBuffer boxVerticesBuffer = ByteBuffer.allocateDirect(BOX_VERTICES.length * Float.BYTES); - boxVerticesBuffer.order(ByteOrder.nativeOrder()); - boxVerticesBuffer.asFloatBuffer().put(BOX_VERTICES); - boxVerticesBuffer.rewind(); - this.vertexBuffer = new GLVertexBuffer(false); - this.vertexBuffer.bind(); - this.vertexBuffer.uploadBuffer(boxVerticesBuffer, 8, EDhApiGpuUploadMethod.DATA, BOX_VERTICES.length * Float.BYTES); - - - // outline vertex indexes - ByteBuffer boxOutlineBuffer = ByteBuffer.allocateDirect(BOX_OUTLINE_INDICES.length * Integer.BYTES); - boxOutlineBuffer.order(ByteOrder.nativeOrder()); - boxOutlineBuffer.asIntBuffer().put(BOX_OUTLINE_INDICES); - boxOutlineBuffer.rewind(); - this.outlineIndexBuffer = new GLElementBuffer(false); - this.outlineIndexBuffer.uploadBuffer(boxOutlineBuffer, EDhApiGpuUploadMethod.DATA, BOX_OUTLINE_INDICES.length * Integer.BYTES, GL32.GL_STATIC_DRAW); - - } + public DebugRenderer() { } @@ -181,13 +141,53 @@ public class DebugRenderer + + //===========// // rendering // //===========// + public void init() + { + if (this.init) + { + return; + } + + this.init = true; + this.va = AbstractVertexAttribute.create(); + this.va.bind(); + // Pos + this.va.setVertexAttribute(0, 0, VertexPointer.addVec3Pointer(false)); + this.va.completeAndCheck(Float.BYTES * 3); + this.basicShader = new ShaderProgram("shaders/debug/vert.vert", "shaders/debug/frag.frag", + "fragColor", new String[]{"vPosition"}); + this.createBuffer(); + } + + private void createBuffer() + { + ByteBuffer buffer = ByteBuffer.allocateDirect(box_vertices.length * Float.BYTES); + buffer.order(ByteOrder.nativeOrder()); + buffer.asFloatBuffer().put(box_vertices); + buffer.rewind(); + + this.boxBuffer = new GLVertexBuffer(false); + this.boxBuffer.bind(); + this.boxBuffer.uploadBuffer(buffer, 8, EDhApiGpuUploadMethod.DATA, box_vertices.length * Float.BYTES); + + buffer = ByteBuffer.allocateDirect(box_outline_indices.length * Integer.BYTES); + buffer.order(ByteOrder.nativeOrder()); + buffer.asIntBuffer().put(box_outline_indices); + buffer.rewind(); + + this.boxOutlineBuffer = new GLElementBuffer(false); + this.boxOutlineBuffer.uploadBuffer(buffer, EDhApiGpuUploadMethod.DATA, box_outline_indices.length * Integer.BYTES, GL32.GL_STATIC_DRAW); + } + public void render(Mat4f transform) { - this.transformationMatrixThisFrame = transform; + this.transformThiFrame = transform; Vec3d camPos = MC_RENDER.getCameraExactPosition(); this.camPosFloatThisFrame = new Vec3f((float) camPos.x, (float) camPos.y, (float) camPos.z); @@ -199,23 +199,22 @@ public class DebugRenderer this.basicShader.bind(); this.va.bind(); - this.va.bindBufferToAllBindingPoints(this.vertexBuffer.getId()); + this.va.bindBufferToAllBindingPoints(this.boxBuffer.getId()); - this.outlineIndexBuffer.bind(); + this.boxOutlineBuffer.bind(); this.rendererLists.render(this); BoxParticle head = null; while ((head = this.particles.poll()) != null && head.isDead(System.nanoTime())) - { /* remove dead particles */ } + { + } if (head != null) { - // re-add the popped off head this.particles.add(head); } - GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); for (BoxParticle particle : this.particles) { this.renderBox(particle.getBox()); @@ -227,13 +226,13 @@ public class DebugRenderer public void renderBox(Box box) { - Mat4f boxTransform = Mat4f.createTranslateMatrix(box.minPos.x - this.camPosFloatThisFrame.x, box.minPos.y - this.camPosFloatThisFrame.y, box.minPos.z - this.camPosFloatThisFrame.z); - boxTransform.multiply(Mat4f.createScaleMatrix(box.maxPos.x - box.minPos.x, box.maxPos.y - box.minPos.y, box.maxPos.z - box.minPos.z)); - Mat4f t = this.transformationMatrixThisFrame.copy(); + Mat4f boxTransform = Mat4f.createTranslateMatrix(box.a.x - this.camPosFloatThisFrame.x, box.a.y - this.camPosFloatThisFrame.y, box.a.z - this.camPosFloatThisFrame.z); + boxTransform.multiply(Mat4f.createScaleMatrix(box.b.x - box.a.x, box.b.y - box.a.y, box.b.z - box.a.z)); + Mat4f t = this.transformThiFrame.copy(); t.multiply(boxTransform); - this.basicShader.setUniform(this.basicShader.getUniformLocation("uTransform"), t); + this.basicShader.setUniform(this.basicShader.getUniformLocation("transform"), t); this.basicShader.setUniform(this.basicShader.getUniformLocation("uColor"), box.color); - GL32.glDrawElements(GL32.GL_LINES, BOX_OUTLINE_INDICES.length, GL32.GL_UNSIGNED_INT, 0); + GL32.glDrawElements(GL32.GL_LINES, box_outline_indices.length, GL32.GL_UNSIGNED_INT, 0); } @@ -244,25 +243,25 @@ public class DebugRenderer public static final class Box { - public Vec3f minPos; - public Vec3f maxPos; + public Vec3f a; + public Vec3f b; public Color color; - public Box(Vec3f minPos, Vec3f maxPos, Color color) + public Box(Vec3f a, Vec3f b, Color color) { - this.minPos = minPos; - this.maxPos = maxPos; + this.a = a; + this.b = b; this.color = color; } - public Box(Vec3f minPos, Vec3f maxPos, Color color, Vec3f margin) + public Box(Vec3f a, Vec3f b, Color color, Vec3f margin) { - this.minPos = minPos; - this.minPos.add(margin); - this.maxPos = maxPos; - this.maxPos.subtract(margin); + this.a = a; + this.a.add(margin); + this.b = b; + this.b.subtract(margin); this.color = color; } @@ -273,8 +272,8 @@ public class DebugRenderer float edge = pos.getBlockWidth() * marginPercent; Vec3f a = new Vec3f(blockMin.x + edge, minY, blockMin.z + edge); Vec3f b = new Vec3f(blockMax.x - edge, maxY, blockMax.z - edge); - this.minPos = a; - this.maxPos = b; + this.a = a; + this.b = b; this.color = color; } @@ -286,8 +285,8 @@ public class DebugRenderer float edge = pos.getBlockWidth() * marginPercent; Vec3f a = new Vec3f(blockMin.x + edge, hashY, blockMin.z + edge); Vec3f b = new Vec3f(blockMax.x - edge, hashY, blockMax.z - edge); - this.minPos = a; - this.maxPos = b; + this.a = a; + this.b = b; this.color = color; } @@ -335,7 +334,7 @@ public class DebugRenderer float percent = (now - this.startTime) / (float) this.duration; percent = (float) Math.pow(percent, 4); float yDiff = this.yChange * percent; - return new Box(new Vec3f(this.box.minPos.x, this.box.minPos.y + yDiff, this.box.minPos.z), new Vec3f(this.box.maxPos.x, this.box.maxPos.y + yDiff, this.box.maxPos.z), this.box.color); + return new Box(new Vec3f(this.box.a.x, this.box.a.y + yDiff, this.box.a.z), new Vec3f(this.box.b.x, this.box.b.y + yDiff, this.box.b.z), this.box.color); } public boolean isDead(long time) { return (time - this.startTime) > this.duration; } @@ -351,7 +350,7 @@ public class DebugRenderer public BoxWithLife(Box box, long ns, float yChange, Color deathColor) { this.box = box; - this.particaleOnClose = new BoxParticle(new Box(box.minPos, box.maxPos, deathColor), -1, ns, yChange); + this.particaleOnClose = new BoxParticle(new Box(box.a, box.b, deathColor), -1, ns, yChange); register(this, null); } @@ -361,7 +360,7 @@ public class DebugRenderer public BoxWithLife(Box box, double s, float yChange, Color deathColor) { this.box = box; - this.particaleOnClose = new BoxParticle(new Box(box.minPos, box.maxPos, deathColor), s, yChange); + this.particaleOnClose = new BoxParticle(new Box(box.a, box.b, deathColor), s, yChange); } public BoxWithLife(Box box, double s, float yChange) { this(box, s, yChange, box.color); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/FogRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/FogRenderer.java deleted file mode 100644 index 1f2f17491..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/FogRenderer.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.render.renderer; - -import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.render.glObject.GLState; -import com.seibel.distanthorizons.core.render.renderer.shaders.FogApplyShader; -import com.seibel.distanthorizons.core.render.renderer.shaders.FogShader; -import com.seibel.distanthorizons.core.util.math.Mat4f; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; -import org.lwjgl.opengl.GL32; - -import java.nio.ByteBuffer; - -/** - * Handles adding SSAO via {@link FogShader} and {@link FogApplyShader}.

- * - * {@link FogShader} - draws the Fog to a texture.
- * {@link FogApplyShader} - draws the Fog texture to DH's FrameBuffer.
- */ -public class FogRenderer -{ - public static FogRenderer INSTANCE = new FogRenderer(); - - private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); - - - private boolean init = false; - - private int width = -1; - private int height = -1; - private int fogFramebuffer = -1; - - private int fogTexture = -1; - - - - //=============// - // constructor // - //=============// - - private FogRenderer() { } - - public void init() - { - if (this.init) return; - this.init = true; - - FogShader.INSTANCE.init(); - FogApplyShader.INSTANCE.init(); - } - - private void createFramebuffer(int width, int height) - { - if (this.fogFramebuffer != -1) - { - GL32.glDeleteFramebuffers(this.fogFramebuffer); - this.fogFramebuffer = -1; - } - - if (this.fogTexture != -1) - { - GL32.glDeleteTextures(this.fogTexture); - this.fogTexture = -1; - } - - this.fogFramebuffer = GL32.glGenFramebuffers(); - GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.fogFramebuffer); - - this.fogTexture = GL32.glGenTextures(); - GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.fogTexture); - GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RGBA16, width, height, 0, GL32.GL_RGBA, GL32.GL_UNSIGNED_SHORT_4_4_4_4, (ByteBuffer) null); - GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_LINEAR); - GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_LINEAR); - GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.fogTexture, 0); - } - - - - //========// - // render // - //========// - - public void render(GLState primaryState, Mat4f projectionMatrix, float partialTicks) - { - GLState state = new GLState(); - this.init(); - - // resize the framebuffer if necessary - int width = MC_RENDER.getTargetFrameBufferViewportWidth(); - int height = MC_RENDER.getTargetFrameBufferViewportHeight(); - if (this.width != width || this.height != height) - { - this.width = width; - this.height = height; - this.createFramebuffer(width, height); - } - - FogShader.INSTANCE.frameBuffer = this.fogFramebuffer; - FogShader.INSTANCE.setProjectionMatrix(projectionMatrix); - FogShader.INSTANCE.render(partialTicks); - - // restored so we can write the SSAO texture to the main frame buffer - primaryState.restore(); - - FogApplyShader.INSTANCE.fogTexture = this.fogTexture; - FogApplyShader.INSTANCE.render(partialTicks); - - state.restore(); - } - - public void free() - { - FogShader.INSTANCE.free(); - FogApplyShader.INSTANCE.free(); - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DhTerrainShaderProgram.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderProgram.java similarity index 52% rename from core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DhTerrainShaderProgram.java rename to core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderProgram.java index 058b6b63e..5f95bb689 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DhTerrainShaderProgram.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderProgram.java @@ -21,7 +21,6 @@ package com.seibel.distanthorizons.core.render.renderer; import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiShaderProgram; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.render.glObject.GLProxy; import com.seibel.distanthorizons.core.render.glObject.shader.Shader; @@ -32,38 +31,39 @@ import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.VertexAtt import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.VertexPointer; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.RenderUtil; -import com.seibel.distanthorizons.core.util.math.Mat4f; -import com.seibel.distanthorizons.core.util.math.Vec3f; +import com.seibel.distanthorizons.coreapi.util.math.Mat4f; +import com.seibel.distanthorizons.coreapi.util.math.Vec3f; -/** - * Handles rendering the normal LOD terrain. - */ -public class DhTerrainShaderProgram extends ShaderProgram implements IDhApiShaderProgram +public class LodRenderProgram extends ShaderProgram implements IDhApiShaderProgram { + public static final String VERTEX_SHADER_PATH = "shaders/standard.vert"; + public static final String VERTEX_CURVE_SHADER_PATH = "shaders/curve.vert"; + public static final String FRAGMENT_SHADER_PATH = "shaders/flat_shaded.frag"; + public final AbstractVertexAttribute vao; // Uniforms - public final int uCombinedMatrix; - public final int uModelOffset; - public final int uWorldYOffset; + public final int combinedMatUniform; + public final int modelOffsetUniform; + public final int worldYOffsetUniform; - public final int uMircoOffset; + public final int mircoOffsetUniform; - public final int uEarthRadius; + public final int earthRadiusUniform; - public final int uLightMap; + public final int lightMapUniform; // Fog/Clip Uniforms - public final int uClipDistance; + public final int clipDistanceUniform; // Noise Uniforms - public final int uNoiseEnabled; - public final int uNoiseSteps; - public final int uNoiseIntensity; - public final int uNoiseDropoff; + public final int noiseEnabledUniform; + public final int noiseStepsUniform; + public final int noiseIntensityUniform; + public final int noiseDropoffUniform; // Debug Uniform - public final int uWhiteWorld; + public final int whiteWorldUniform; @@ -72,57 +72,50 @@ public class DhTerrainShaderProgram extends ShaderProgram implements IDhApiShade //=============// // This will bind AbstractVertexAttribute - public DhTerrainShaderProgram() + public LodRenderProgram() { - super( - () -> Shader.loadFile(Config.Client.Advanced.Graphics.AdvancedGraphics.earthCurveRatio.get() != 0 - ? "shaders/curve.vert" - : "shaders/standard.vert", + super(() -> Shader.loadFile(Config.Client.Advanced.Graphics.AdvancedGraphics.earthCurveRatio.get() != 0 ? VERTEX_CURVE_SHADER_PATH : VERTEX_SHADER_PATH, false, new StringBuilder()).toString(), - () -> Shader.loadFile("shaders/flat_shaded.frag", false, new StringBuilder()).toString(), + () -> Shader.loadFile(FRAGMENT_SHADER_PATH, false, new StringBuilder()).toString(), "fragColor", new String[]{"vPosition", "color"}); - this.uCombinedMatrix = this.getUniformLocation("uCombinedMatrix"); - this.uModelOffset = this.getUniformLocation("uModelOffset"); - this.uWorldYOffset = this.tryGetUniformLocation("uWorldYOffset"); - this.uMircoOffset = this.getUniformLocation("uMircoOffset"); - this.uEarthRadius = this.tryGetUniformLocation("uEarthRadius"); + combinedMatUniform = getUniformLocation("combinedMatrix"); + modelOffsetUniform = getUniformLocation("modelOffset"); + worldYOffsetUniform = tryGetUniformLocation("worldYOffset"); + mircoOffsetUniform = getUniformLocation("mircoOffset"); + earthRadiusUniform = tryGetUniformLocation("earthRadius"); - this.uLightMap = this.getUniformLocation("uLightMap"); + lightMapUniform = getUniformLocation("lightMap"); // Fog/Clip Uniforms - this.uClipDistance = this.getUniformLocation("uClipDistance"); + clipDistanceUniform = getUniformLocation("clipDistance"); // Noise Uniforms - this.uNoiseEnabled = this.getUniformLocation("uNoiseEnabled"); - this.uNoiseSteps = this.getUniformLocation("uNoiseSteps"); - this.uNoiseIntensity = this.getUniformLocation("uNoiseIntensity"); - this.uNoiseDropoff = this.getUniformLocation("uNoiseDropoff"); + noiseEnabledUniform = getUniformLocation("noiseEnabled"); + noiseStepsUniform = getUniformLocation("noiseSteps"); + noiseIntensityUniform = getUniformLocation("noiseIntensity"); + noiseDropoffUniform = getUniformLocation("noiseDropoff"); // Debug Uniform - this.uWhiteWorld = this.getUniformLocation("uWhiteWorld"); + whiteWorldUniform = getUniformLocation("whiteWorld"); // TODO: Add better use of the LODFormat thing int vertexByteCount = LodUtil.LOD_VERTEX_FORMAT.getByteSize(); - if (GLProxy.getInstance().vertexAttributeBufferBindingSupported) - { - this.vao = new VertexAttributePostGL43(); // also binds AbstractVertexAttribute - } + if (GLProxy.getInstance().VertexAttributeBufferBindingSupported) + vao = new VertexAttributePostGL43(); // also binds AbstractVertexAttribute else - { - this.vao = new VertexAttributePreGL43(); // also binds AbstractVertexAttribute - } - this.vao.bind(); + vao = new VertexAttributePreGL43(); // also binds AbstractVertexAttribute + vao.bind(); // TODO comment what each attribute represents - this.vao.setVertexAttribute(0, 0, VertexPointer.addUnsignedShortsPointer(4, false, true)); // 2+2+2+2 // TODO probably color, blockpos - this.vao.setVertexAttribute(0, 1, VertexPointer.addUnsignedBytesPointer(4, true, false)); // +4 // TODO ? - this.vao.setVertexAttribute(0, 2, VertexPointer.addUnsignedBytesPointer(4, true, true)); // +4 // TODO probably normal index and Iris block ID + vao.setVertexAttribute(0, 0, VertexPointer.addUnsignedShortsPointer(4, false, true)); // 2+2+2+2 // TODO probably color, blockpos + vao.setVertexAttribute(0, 1, VertexPointer.addUnsignedBytesPointer(4, true, false)); // +4 // TODO ? + vao.setVertexAttribute(0, 2, VertexPointer.addUnsignedBytesPointer(4, true, true)); // +4 // TODO probably normal index and Iris block ID try { - this.vao.completeAndCheck(vertexByteCount); + vao.completeAndCheck(vertexByteCount); } catch (RuntimeException e) { @@ -130,15 +123,15 @@ public class DhTerrainShaderProgram extends ShaderProgram implements IDhApiShade throw e; } - if (this.uEarthRadius != -1) this.setUniform(this.uEarthRadius, + if (earthRadiusUniform != -1) setUniform(earthRadiusUniform, /*6371KM*/ 6371000.0f / Config.Client.Advanced.Graphics.AdvancedGraphics.earthCurveRatio.get()); // Noise Uniforms - this.setUniform(this.uNoiseEnabled, Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseEnabled.get()); - this.setUniform(this.uNoiseSteps, Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseSteps.get()); - this.setUniform(this.uNoiseIntensity, Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseIntensity.get().floatValue()); - this.setUniform(this.uNoiseDropoff, Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseDropoff.get()); + setUniform(noiseEnabledUniform, Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseEnabled.get()); + setUniform(noiseStepsUniform, Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseSteps.get()); + setUniform(noiseIntensityUniform, Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseIntensity.get().floatValue()); + setUniform(noiseDropoffUniform, Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseDropoff.get()); } @@ -151,25 +144,27 @@ public class DhTerrainShaderProgram extends ShaderProgram implements IDhApiShade public void bind() { super.bind(); - this.vao.bind(); + vao.bind(); } @Override public void unbind() { super.unbind(); - this.vao.unbind(); + vao.unbind(); } @Override public void free() { - this.vao.free(); + vao.free(); super.free(); } @Override public void bindVertexBuffer(int vbo) { this.vao.bindBufferToAllBindingPoints(vbo); } + public void unbindVertexBuffer() { this.vao.unbindBuffersFromAllBindingPoint(); } + @Override public void fillUniformData(DhApiRenderParam renderParameters) { @@ -179,16 +174,16 @@ public class DhTerrainShaderProgram extends ShaderProgram implements IDhApiShade super.bind(); // uniforms - this.setUniform(this.uCombinedMatrix, combinedMatrix); - this.setUniform(this.uMircoOffset, 0.01f); // 0.01 block offset + setUniform(combinedMatUniform, combinedMatrix); + setUniform(mircoOffsetUniform, 0.01f); // 0.01 block offset // setUniform(skyLightUniform, skyLight); - this.setUniform(this.uLightMap, 0); // TODO this should probably be passed in + setUniform(lightMapUniform, 0); // TODO this should probably be passed in - if (this.uWorldYOffset != -1) this.setUniform(this.uWorldYOffset, (float) renderParameters.worldYOffset); + if (worldYOffsetUniform != -1) setUniform(worldYOffsetUniform, (float) renderParameters.worldYOffset); // Debug - this.setUniform(this.uWhiteWorld, Config.Client.Advanced.Debugging.enableWhiteWorld.get()); + setUniform(whiteWorldUniform, Config.Client.Advanced.Debugging.enableWhiteWorld.get()); // Clip Uniform float dhNearClipDistance = RenderUtil.getNearClipPlaneDistanceInBlocks(renderParameters.partialTicks); @@ -202,11 +197,11 @@ public class DhTerrainShaderProgram extends ShaderProgram implements IDhApiShade { dhNearClipDistance = 1.0f; } - this.setUniform(this.uClipDistance, dhNearClipDistance); + this.setUniform(this.clipDistanceUniform, dhNearClipDistance); } @Override - public void setModelOffsetPos(DhApiVec3f modelOffsetPos) { this.setUniform(this.uModelOffset, new Vec3f(modelOffsetPos)); } + public void setModelOffsetPos(Vec3f modelOffsetPos) { this.setUniform(this.modelOffsetUniform, modelOffsetPos); } @Override public int getId() { return this.id; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java index 2a6e2e688..6ea356fd8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java @@ -38,7 +38,6 @@ import com.seibel.distanthorizons.core.render.glObject.GLState; import com.seibel.distanthorizons.core.render.glObject.buffer.GLVertexBuffer; import com.seibel.distanthorizons.core.render.glObject.buffer.QuadElementBuffer; import com.seibel.distanthorizons.core.render.glObject.texture.*; -import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer; import com.seibel.distanthorizons.core.render.renderer.shaders.*; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; @@ -51,13 +50,16 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccess import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import com.seibel.distanthorizons.coreapi.DependencyInjection.OverrideInjector; -import com.seibel.distanthorizons.core.util.math.Mat4f; -import com.seibel.distanthorizons.core.util.math.Vec3d; -import com.seibel.distanthorizons.core.util.math.Vec3f; +import com.seibel.distanthorizons.coreapi.util.math.Mat4f; +import com.seibel.distanthorizons.coreapi.util.math.Vec3d; +import com.seibel.distanthorizons.coreapi.util.math.Vec3f; import org.apache.logging.log4j.LogManager; import org.lwjgl.opengl.GL32; import java.awt.*; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; import java.time.Duration; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; @@ -95,14 +97,90 @@ public class LodRenderer private int cachedWidth; private int cachedHeight; + + /** called by each {@link ColumnRenderBuffer} before rendering */ + public void setModelViewMatrixOffset(DhBlockPos pos, DhApiRenderParam renderEventParam) throws IllegalStateException + { + Vec3d cam = MC_RENDER.getCameraExactPosition(); + Vec3f modelPos = new Vec3f((float) (pos.x - cam.x), (float) (pos.y - cam.y), (float) (pos.z - cam.z)); + + + IDhApiShaderProgram shaderProgram = this.lodRenderProgram; + IDhApiShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class); + if (shaderProgramOverride != null && shaderProgram.overrideThisFrame()) + { + shaderProgram = shaderProgramOverride; + } + + if (!GL32.glIsProgram(shaderProgram.getId())) + { + throw new IllegalStateException("No GL program exists with the ID: [" + shaderProgram.getId() + "]. This either means a shader program was freed while it was still in use or was never created."); + } + + shaderProgram.bind(); + shaderProgram.setModelOffsetPos(modelPos); + + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeBufferRenderEvent.class, new DhApiBeforeBufferRenderEvent.EventParam(renderEventParam, modelPos)); + } + + public void drawVbo(GLVertexBuffer vbo) + { + //// can be uncommented to add additional debug validation to prevent crashes if invalid buffers are being created + //// shouldn't be used in production due to the performance hit + //if (GL32.glIsBuffer(vbo.getId())) + { + IDhApiShaderProgram shaderProgram = this.lodRenderProgram; + IDhApiShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class); + if (shaderProgramOverride != null && shaderProgram.overrideThisFrame()) + { + shaderProgram = shaderProgramOverride; + } + + + vbo.bind(); + shaderProgram.bindVertexBuffer(vbo.getId()); + GL32.glDrawElements(GL32.GL_TRIANGLES, (vbo.getVertexCount() / 4) * 6, // TODO what does the 4 and 6 here represent? + this.quadIBO.getType(), 0); + vbo.unbind(); + } + //else + //{ + // // will spam the log if uncommented, but helpful for validation + // //LOGGER.warn("Unable to draw VBO: "+vbo.getId()); + //} + } + + + public static class LagSpikeCatcher + { + long timer = System.nanoTime(); + + public LagSpikeCatcher() { } + + public void end(String source) + { + if (!ENABLE_DRAW_LAG_SPIKE_LOGGING) + { + return; + } + + this.timer = System.nanoTime() - this.timer; + if (this.timer > DRAW_LAG_SPIKE_THRESHOLD_NS) + { + //4 ms + EVENT_LOGGER.debug("NOTE: " + source + " took " + Duration.ofNanos(this.timer) + "!"); + } + + } + + } + private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); private final ReentrantLock setupLock = new ReentrantLock(); public final RenderBufferHandler bufferHandler; - public final GenericObjectRenderer genericObjectRenderer; - // The shader program IDhApiShaderProgram lodRenderProgram = null; @@ -127,10 +205,9 @@ public class LodRenderer // constructor // //=============// - public LodRenderer(RenderBufferHandler bufferHandler, GenericObjectRenderer genericObjectRenderer) + public LodRenderer(RenderBufferHandler bufferHandler) { this.bufferHandler = bufferHandler; - this.genericObjectRenderer = genericObjectRenderer; } private boolean rendererClosed = false; @@ -297,41 +374,16 @@ public class LodRenderer // Disable blending for opaque rendering GL32.glDisable(GL32.GL_BLEND); - - // terrain profiler.popPush("LOD Opaque"); ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderPassEvent.class, renderEventParam); + + // TODO: Directional culling this.bufferHandler.renderOpaque(this, renderEventParam); - // custom objects with SSAO - if (Config.Client.Advanced.Graphics.GenericRendering.enableRendering.get()) - { - profiler.popPush("Custom Objects"); - this.genericObjectRenderer.render(renderEventParam, profiler, true); - } - - - // SSAO if (Config.Client.Advanced.Graphics.Ssao.enabled.get()) { profiler.popPush("LOD SSAO"); - SSAORenderer.INSTANCE.render(minecraftGlState, new Mat4f(renderEventParam.dhProjectionMatrix), renderEventParam.partialTicks); - } - - - // custom objects without SSAO - if (Config.Client.Advanced.Graphics.GenericRendering.enableRendering.get()) - { - profiler.popPush("Custom Objects"); - this.genericObjectRenderer.render(renderEventParam, profiler, false); - } - - - //DarkShader.INSTANCE.render(partialTicks); // A test shader to make the world darker - - if (!deferTransparentRendering && Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled) - { - this.renderTransparentBuffers(profiler, renderEventParam, renderEventParam.partialTicks); + SSAORenderer.INSTANCE.render(minecraftGlState, renderEventParam.dhProjectionMatrix, renderEventParam.partialTicks); } @@ -342,9 +394,16 @@ public class LodRenderer Mat4f combinedMatrix = new Mat4f(renderEventParam.dhProjectionMatrix); combinedMatrix.multiply(renderEventParam.dhModelViewMatrix); - FogRenderer.INSTANCE.render(minecraftGlState, combinedMatrix, renderEventParam.partialTicks); + FogShader.INSTANCE.setModelViewProjectionMatrix(combinedMatrix); + FogShader.INSTANCE.render(renderEventParam.partialTicks); } + //DarkShader.INSTANCE.render(partialTicks); // A test shader to make the world darker + + if (!deferTransparentRendering && Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled) + { + this.renderTransparentBuffers(profiler, renderEventParam, renderEventParam.partialTicks); + } drawLagSpikeCatcher.end("LodDraw"); @@ -363,10 +422,9 @@ public class LodRenderer // Note: this can be very slow if a lot of boxes are being rendered DebugRenderer.INSTANCE.render(combinedMatrix); + profiler.popPush("LOD cleanup"); } - profiler.popPush("LOD cleanup"); - if (this.usingMcFrameBuffer) @@ -404,17 +462,6 @@ public class LodRenderer if (Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled) { this.renderTransparentBuffers(profiler, renderEventParam, renderEventParam.partialTicks); - - - if (Config.Client.Advanced.Graphics.Fog.drawMode.get() != EDhApiFogDrawMode.FOG_DISABLED) - { - profiler.popPush("LOD Fog"); - - Mat4f combinedMatrix = new Mat4f(renderEventParam.dhProjectionMatrix); - combinedMatrix.multiply(renderEventParam.dhModelViewMatrix); - - FogRenderer.INSTANCE.render(minecraftGlState, combinedMatrix, renderEventParam.partialTicks); - } } drawLagSpikeCatcher.end("LodTranslucentDraw"); @@ -469,56 +516,14 @@ public class LodRenderer this.bufferHandler.renderTransparent(this, renderEventParam); GL32.glDepthMask(true); // Apparently the depth mask state is stored in the FBO, so glState fails to restore it... - } - - /** called by each {@link ColumnRenderBuffer} before rendering */ - public void setModelViewMatrixOffset(DhBlockPos pos, DhApiRenderParam renderEventParam) throws IllegalStateException - { - Vec3d cam = MC_RENDER.getCameraExactPosition(); - Vec3f modelPos = new Vec3f((float) (pos.x - cam.x), (float) (pos.y - cam.y), (float) (pos.z - cam.z)); - - IDhApiShaderProgram shaderProgram = this.lodRenderProgram; - IDhApiShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class); - if (shaderProgramOverride != null && shaderProgram.overrideThisFrame()) + if (Config.Client.Advanced.Graphics.Fog.drawMode.get() != EDhApiFogDrawMode.FOG_DISABLED) { - shaderProgram = shaderProgramOverride; + profiler.popPush("LOD Fog"); + FogShader.INSTANCE.render(partialTicks); } - - shaderProgram.bind(); - shaderProgram.setModelOffsetPos(modelPos); - - ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeBufferRenderEvent.class, new DhApiBeforeBufferRenderEvent.EventParam(renderEventParam, modelPos)); } - public void drawVbo(GLVertexBuffer vbo) - { - //// can be uncommented to add additional debug validation to prevent crashes if invalid buffers are being created - //// shouldn't be used in production due to the performance hit - //if (GL32.glIsBuffer(vbo.getId())) - { - IDhApiShaderProgram shaderProgram = this.lodRenderProgram; - IDhApiShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class); - if (shaderProgramOverride != null && shaderProgram.overrideThisFrame()) - { - shaderProgram = shaderProgramOverride; - } - - - vbo.bind(); - shaderProgram.bindVertexBuffer(vbo.getId()); - GL32.glDrawElements(GL32.GL_TRIANGLES, (vbo.getVertexCount() / 4) * 6, // TODO what does the 4 and 6 here represent? - this.quadIBO.getType(), 0); - vbo.unbind(); - } - //else - //{ - // // will spam the log if uncommented, but helpful for validation - // //LOGGER.warn("Unable to draw VBO: "+vbo.getId()); - //} - } - - //=================// @@ -611,7 +616,6 @@ public class LodRenderer boolean renderWireframe = Config.Client.Advanced.Debugging.renderWireframe.get(); if (renderWireframe) { - // TODO fix GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_LINE); //GL32.glDisable(GL32.GL_CULL_FACE); } @@ -646,7 +650,7 @@ public class LodRenderer this.fogConfig = newFogConfig; this.lodRenderProgram.free(); - this.lodRenderProgram = new DhTerrainShaderProgram(); + this.lodRenderProgram = new LodRenderProgram(); FogShader.INSTANCE.free(); FogShader.INSTANCE = new FogShader(newFogConfig); @@ -686,7 +690,7 @@ public class LodRenderer EVENT_LOGGER.info("Setting up renderer"); this.isSetupComplete = true; - this.lodRenderProgram = new DhTerrainShaderProgram(); + this.lodRenderProgram = new LodRenderProgram(); if (ENABLE_IBO) { this.quadIBO = new QuadElementBuffer(); @@ -864,35 +868,4 @@ public class LodRenderer } } - - - //================// - // helper classes // - //================// - - public static class LagSpikeCatcher - { - long timer = System.nanoTime(); - - public LagSpikeCatcher() { } - - public void end(String source) - { - if (!ENABLE_DRAW_LAG_SPIKE_LOGGING) - { - return; - } - - this.timer = System.nanoTime() - this.timer; - if (this.timer > DRAW_LAG_SPIKE_THRESHOLD_NS) - { - //4 ms - EVENT_LOGGER.debug("NOTE: " + source + " took " + Duration.ofNanos(this.timer) + "!"); - } - - } - - } - - } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/SSAORenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/SSAORenderer.java index b9953cd31..77519aa8c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/SSAORenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/SSAORenderer.java @@ -24,7 +24,7 @@ import com.seibel.distanthorizons.core.render.glObject.GLState; import com.seibel.distanthorizons.core.render.renderer.shaders.SSAOApplyShader; import com.seibel.distanthorizons.core.render.renderer.shaders.SSAOShader; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; -import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.coreapi.util.math.Mat4f; import org.lwjgl.opengl.GL32; import java.nio.ByteBuffer; @@ -51,7 +51,6 @@ public class SSAORenderer private int ssaoTexture = -1; - //=============// // constructor // //=============// @@ -93,7 +92,6 @@ public class SSAORenderer } - //========// // render // //========// @@ -101,11 +99,12 @@ public class SSAORenderer public void render(GLState primaryState, Mat4f projectionMatrix, float partialTicks) { GLState state = new GLState(); - this.init(); - // resize the framebuffer if necessary + this.init(); + int width = MC_RENDER.getTargetFrameBufferViewportWidth(); int height = MC_RENDER.getTargetFrameBufferViewportHeight(); + if (this.width != width || this.height != height) { this.width = width; @@ -117,7 +116,6 @@ public class SSAORenderer SSAOShader.INSTANCE.setProjectionMatrix(projectionMatrix); SSAOShader.INSTANCE.render(partialTicks); - // restored so we can write the SSAO texture to the main frame buffer primaryState.restore(); SSAOApplyShader.INSTANCE.ssaoTexture = this.ssaoTexture; @@ -131,5 +129,4 @@ public class SSAORenderer SSAOShader.INSTANCE.free(); SSAOApplyShader.INSTANCE.free(); } - } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/BeaconRenderHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/BeaconRenderHandler.java deleted file mode 100644 index d1beb0701..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/BeaconRenderHandler.java +++ /dev/null @@ -1,241 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.render.renderer.generic; - -import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial; -import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; -import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; -import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; -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.DhBlockPos; -import com.seibel.distanthorizons.core.pos.DhChunkPos; -import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; -import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo; -import com.seibel.distanthorizons.core.util.LodUtil; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; -import com.seibel.distanthorizons.coreapi.ModInfo; -import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.NotNull; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; - -public class BeaconRenderHandler -{ - private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); - - private static final int BEAM_TOP_Y = 6_000; - - - /** if this is null then the other handler is probably null too, but just in case */ - private final BeaconBeamRepo beaconBeamRepo; - - private final IDhApiRenderableBoxGroup beaconBoxGroup; - private final HashMap beaconRefCountByBlockPos = new HashMap<>(); - - - - //=============// - // constructor // - //=============// - - public BeaconRenderHandler(@NotNull BeaconBeamRepo beaconBeamRepo, @NotNull GenericObjectRenderer renderer) - { - this.beaconBeamRepo = beaconBeamRepo; - - this.beaconBoxGroup = GenericRenderObjectFactory.INSTANCE.createAbsolutePositionedGroup(ModInfo.NAME+":Beacons", new ArrayList<>(0)); - this.beaconBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT); - this.beaconBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT); - this.beaconBoxGroup.setSsaoEnabled(false); - this.beaconBoxGroup.setShading(DhApiRenderableBoxGroupShading.getUnshaded()); - this.beaconBoxGroup.setPreRenderFunc((renderEventParam) -> this.beaconBoxGroup.setActive(Config.Client.Advanced.Graphics.GenericRendering.enableBeaconRendering.get())); - - renderer.add(this.beaconBoxGroup); - } - - - - //=========================// - // level loading/unloading // - //=========================// - - public void setBeaconBeamsForChunk(DhChunkPos chunkPos, List newBeamList) - { - // synchronized to prevent two threads from updating the same chunk at the same time - synchronized (this) - { - HashSet allPosSet = new HashSet<>(); - - // sort new beams - HashMap newBeamByPos = new HashMap<>(newBeamList.size()); - for (int i = 0; i < newBeamList.size(); i++) - { - BeaconBeamDTO beam = newBeamList.get(i); - newBeamByPos.put(beam.pos, beam); - allPosSet.add(beam.pos); - } - - // get existing beams - List existingBeamList = this.beaconBeamRepo.getAllBeamsForPos(chunkPos); - HashMap existingBeamByPos = new HashMap<>(existingBeamList.size()); - for (int i = 0; i < existingBeamList.size(); i++) - { - BeaconBeamDTO beam = existingBeamList.get(i); - existingBeamByPos.put(beam.pos, beam); - allPosSet.add(beam.pos); - } - - - - for (DhBlockPos beaconPos : allPosSet) - { - if (!chunkPos.contains(beaconPos)) - { - // don't update beacons outside the updated chunk - continue; - } - - BeaconBeamDTO existingBeam = existingBeamByPos.get(beaconPos); - BeaconBeamDTO newBeam = newBeamByPos.get(beaconPos); - - if (existingBeam != null && newBeam != null) - { - // beam still exists in chunk - if (!existingBeam.color.equals(newBeam.color)) - { - // beam colors were changed - this.beaconBeamRepo.save(newBeam); - this.updateBeaconColor(newBeam); - } - } - else if (existingBeam == null && newBeam != null) - { - // new beam found, add to DB - this.beaconBeamRepo.save(newBeam); - this.startRenderingBeacon(newBeam); - } - else if (existingBeam != null && newBeam == null) - { - // beam no longer exists at position, remove from DB - this.beaconBeamRepo.deleteWithKey(beaconPos); // TODO broken when updating adjacent chunks - this.stopRenderingBeaconAtPos(beaconPos); - } - - } - } - } - - public void loadBeaconBeamsInPos(long pos) - { - // get all beams in pos - List existingBeamList = this.beaconBeamRepo.getAllBeamsForPos(pos); - for (int i = 0; i < existingBeamList.size(); i++) - { - BeaconBeamDTO newBeam = existingBeamList.get(i); - this.startRenderingBeacon(newBeam); - } - } - - public void unloadBeaconBeamsInPos(long pos) - { - // get all beams in pos - List existingBeamList = this.beaconBeamRepo.getAllBeamsForPos(pos); - for (int i = 0; i < existingBeamList.size(); i++) - { - BeaconBeamDTO beam = existingBeamList.get(i); - this.stopRenderingBeaconAtPos(beam.pos); - } - } - - - - //=================// - // render handling // - //=================// - - private void startRenderingBeacon(BeaconBeamDTO beacon) - { - this.beaconRefCountByBlockPos.compute(beacon.pos, (beamPos, beaconRefCount) -> - { - if (beaconRefCount == null) { beaconRefCount = new AtomicInteger(); } - if (beaconRefCount.getAndIncrement() == 0) - { - DhApiRenderableBox beaconBox = new DhApiRenderableBox( - new DhApiVec3d(beacon.pos.x, beacon.pos.y+1, beacon.pos.z), - new DhApiVec3d(beacon.pos.x+1, BEAM_TOP_Y, beacon.pos.z+1), - beacon.color, - EDhApiBlockMaterial.ILLUMINATED - ); - - this.beaconBoxGroup.add(beaconBox); - this.beaconBoxGroup.triggerBoxChange(); - } - return beaconRefCount; - }); - } - - private void stopRenderingBeaconAtPos(DhBlockPos beaconPos) - { - this.beaconRefCountByBlockPos.compute(beaconPos, (pos, beaconRefCount) -> - { - if (beaconRefCount != null - && beaconRefCount.decrementAndGet() <= 0) - { - this.beaconBoxGroup.removeIf((box) -> - box.minPos.x == beaconPos.x - && box.minPos.y == beaconPos.y+1 // plus 1 because the beam starts above the beacon - && box.minPos.z == beaconPos.z - ); - this.beaconBoxGroup.triggerBoxChange(); - return null; - } - else - { - return beaconRefCount; - } - }); - } - - private void updateBeaconColor(BeaconBeamDTO newBeam) - { - DhBlockPos pos = newBeam.pos; - for (int i = 0; i < this.beaconBoxGroup.size(); i++) - { - DhApiRenderableBox box = this.beaconBoxGroup.get(i); - if (box.minPos.x == pos.x - && box.minPos.y == pos.y+1 // plus 1 because the beam starts above the beacon - && box.minPos.z == pos.z) - { - box.color = newBeam.color; - this.beaconBoxGroup.triggerBoxChange(); - break; - } - } - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/CloudRenderHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/CloudRenderHandler.java deleted file mode 100644 index 490522720..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/CloudRenderHandler.java +++ /dev/null @@ -1,403 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.render.renderer.generic; - -import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial; -import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; -import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; -import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; -import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.level.IDhLevel; -import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.util.LodUtil; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; -import com.seibel.distanthorizons.coreapi.ModInfo; -import org.apache.logging.log4j.Logger; - -import javax.imageio.ImageIO; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.io.FileNotFoundException; -import java.io.IOException; -import java.io.InputStream; -import java.util.*; - -public class CloudRenderHandler -{ - private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); - - private static final String CLOUD_RESOURCE_TEXTURE_PATH = "assets/distanthorizons/textures/clouds.png"; - // FIXME transparency sorting makes having transparent clouds impossible - private static final Color CLOUD_COLOR = new Color(255,255,255,255); - - private static final boolean DEBUG_BORDER_COLORS = false; - - /** - * How wide an individual box is.
- * Measured in blocks. - */ - private static final int CLOUD_BOX_WIDTH = 64; - /** measured in blocks */ - private static final int CLOUD_BOX_THICKNESS = 16; - - private final IDhApiRenderableBoxGroup[][] boxGroupByOffset = new IDhApiRenderableBoxGroup[3][3]; - private final IDhLevel level; - private final GenericObjectRenderer renderer; - - private float moveSpeedInBlocksPerSecond = 3.0f; - private boolean disabledWarningLogged = false; - - - - //=============// - // constructor // - //=============// - - public CloudRenderHandler(IDhLevel level, GenericObjectRenderer renderer) - { - this.level = level; - this.renderer = renderer; - - - - //=======================// - // get the cloud texture // - //=======================// - - // default to a single empty slot in case the texture is broken - boolean[][] cloudLocations = new boolean[1][1]; - try - { - cloudLocations = getCloudsFromTexture(); - } - catch (FileNotFoundException e) - { - LOGGER.error(e.getMessage(), e); - } - catch (IOException e) - { - LOGGER.error("Unexpected issue getting cloud texture, error: ["+e.getMessage()+"].", e); - } - - if (cloudLocations.length != 0 && - cloudLocations.length != cloudLocations[0].length) - { - LOGGER.warn("Non-square cloud texture found, some parts of the texture will be clipped off."); - } - - - - //===================// - // parse the texture // - //===================// - - int textureWidth = cloudLocations.length; - ArrayList boxList = new ArrayList<>(512); - for (int x = 0; x < textureWidth; x ++) - { - for (int z = 0; z < textureWidth; z ++) - { - if (cloudLocations[x][z]) - { - // start a new box in Z direction - int startZ = z; - int startX = x; - int endZ = startZ; - int endX = x+1; - - - - //==========================// - // merge in the Z direction // - //==========================// - - // Find the cloud's length in the Z direction - while (endZ < textureWidth - && cloudLocations[x][endZ]) - { - endZ++; - } - // update the z iterator so we can skip over everything included in this cloud - z = endZ - 1; - - - - //==========================// - // merge in the X direction // - //==========================// - - for (int currentX = startX + 1; currentX < textureWidth; currentX++) - { - boolean canMergeInXDir = true; - - // check if all locations in this column are true - for (int adjacentZ = startZ; adjacentZ < endZ; adjacentZ++) - { - if (!cloudLocations[currentX][adjacentZ]) - { - // at least one pixel in the texture is false, - // so we can't merge in this direction - canMergeInXDir = false; - break; - } - } - - - if (canMergeInXDir) - { - // mark the adjacent column as processed - for (int currentZ = startZ; currentZ < endZ; currentZ++) - { - // by flipping all the pixels in the adjacent column to false, - // we don't have to worry about adding another cloud - cloudLocations[currentX][currentZ] = false; - } - - endX = (currentX + 1); - } - else - { - break; - } - } - - - - //============================// - // Create the renderable box // - //============================// - - // endZ contains the last cloud index - // so the cloud now goes from startZ to endZ (inclusive) - int minXBlockPos = startX * CLOUD_BOX_WIDTH; - int minZBlockPos = startZ * CLOUD_BOX_WIDTH; - int maxXBlockPos = endX * CLOUD_BOX_WIDTH; - int maxZBlockPos = endZ * CLOUD_BOX_WIDTH; - - Color color = CLOUD_COLOR; - if (DEBUG_BORDER_COLORS) - { - // equals is included so the board is 2 blocks wide, it makes it easier to see - if (startX <= 1) { color = Color.RED; } - else if (startX >= textureWidth - 2) { color = Color.GREEN; } - if (startZ <= 1) { color = Color.BLUE; } - else if (endZ >= textureWidth - 2) { color = Color.BLACK; } - } - - DhApiRenderableBox box = new DhApiRenderableBox( - new DhApiVec3d(minXBlockPos, 0, minZBlockPos), - new DhApiVec3d(maxXBlockPos, CLOUD_BOX_THICKNESS, maxZBlockPos), - color, - EDhApiBlockMaterial.UNKNOWN - ); - boxList.add(box); - } - } - } - - - - //========================// - // create the renderables // - //========================// - - // slightly lighter shading than the default - DhApiRenderableBoxGroupShading cloudShading = DhApiRenderableBoxGroupShading.getUnshaded(); - cloudShading.north = cloudShading.south = 0.9f; - cloudShading.east = cloudShading.west = 0.8f; - cloudShading.top = 1.0f; - cloudShading.bottom = 0.7f; - - // 3x3 area so we clouds should always be overhead - for (int x = -1; x <= 1; x++) - { - for (int z = -1; z <= 1; z++) - { - IDhApiRenderableBoxGroup boxGroup = GenericRenderObjectFactory.INSTANCE.createRelativePositionedGroup( - ModInfo.NAME + ":Clouds", - new DhApiVec3d(0, 0, 0), // the offset will be set during rendering - boxList); - boxGroup.setBlockLight(LodUtil.MIN_MC_LIGHT); - boxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT); - boxGroup.setSsaoEnabled(false); - boxGroup.setShading(cloudShading); - - CloudParams params = new CloudParams(textureWidth, x, z); - boxGroup.setPreRenderFunc((renderParam) -> this.preRender(params)); - - renderer.add(boxGroup); - this.boxGroupByOffset[x+1][z+1] = boxGroup; - } - } - } - - private void preRender(CloudParams clouds) - { - IDhApiRenderableBoxGroup boxGroup = this.boxGroupByOffset[clouds.instanceOffsetX+1][clouds.instanceOffsetZ+1]; - - boolean renderClouds = Config.Client.Advanced.Graphics.GenericRendering.enableCloudRendering.get(); - boxGroup.setActive(renderClouds); - if(!renderClouds) - { - return; - } - - if (!this.renderer.getUseInstancedRendering()) - { - if (!this.disabledWarningLogged) - { - this.disabledWarningLogged = true; - LOGGER.warn("Instanced rendering unavailable, cloud rendering disabled."); - } - boxGroup.setActive(false); - return; - } - - - //================// - // cloud movement // - //================// - - long currentTime = System.currentTimeMillis(); - float deltaTime = (currentTime - clouds.lastFrameTime) / 1000.0f; // Delta time in seconds - clouds.lastFrameTime = currentTime; - - float deltaX = this.moveSpeedInBlocksPerSecond * deltaTime; - // negative delta is to match vanilla's cloud movement - clouds.xOffset -= deltaX; - // wrap the cloud around after reaching the edge - clouds.xOffset %= clouds.widthInBlocks; - - - - //============================// - // camera movement and offset // - //============================// - - // camera position - int cameraPosX = (int)MC_RENDER.getCameraExactPosition().x; - int cameraPosZ = (int)MC_RENDER.getCameraExactPosition().z; - // offset the camera position by negative 1 width when below zero to fix off-by-one errors in the negative direction - if (cameraPosX < 0) { cameraPosX -= clouds.widthInBlocks; } - if (cameraPosZ < 0) { cameraPosZ -= clouds.widthInBlocks; } - - // determine how many cloud instances away from the origin we are - int cloudInstanceOffsetX = cameraPosX / (int)clouds.widthInBlocks; - int cloudInstanceOffsetZ = cameraPosZ / (int)clouds.widthInBlocks; - - // calculate the new offset - float xOffset = (cloudInstanceOffsetX * clouds.widthInBlocks); - float zOffset = (cloudInstanceOffsetZ * clouds.widthInBlocks); - - - - //==============// - // update group // - //==============// - - boxGroup.setOriginBlockPos( - new DhApiVec3d( - clouds.xOffset + (clouds.instanceOffsetX * clouds.widthInBlocks) + xOffset + clouds.halfWidthInBlocks, - this.level.getLevelWrapper().getMaxHeight() + 200, - clouds.zOffset + (clouds.instanceOffsetZ * clouds.widthInBlocks) + zOffset + clouds.halfWidthInBlocks - ) - ); - } - - - - //==================// - // texture handling // - //==================// - - private static boolean[][] getCloudsFromTexture() throws FileNotFoundException, IOException - { - final ClassLoader loader = Thread.currentThread().getContextClassLoader(); - - boolean[][] whitePixels = null; - try(InputStream imageInputStream = loader.getResourceAsStream(CLOUD_RESOURCE_TEXTURE_PATH)) - { - if (imageInputStream == null) - { - throw new FileNotFoundException("Unable to find cloud texture at resource path: ["+CLOUD_RESOURCE_TEXTURE_PATH+"]."); - } - - BufferedImage image = ImageIO.read(imageInputStream); - - int width = image.getWidth(); - int height = image.getHeight(); - - whitePixels = new boolean[width][height]; - - for (int x = 0; x < width; x ++) - { - for (int z = 0; z < width; z ++) - { - Color color = new Color(image.getRGB(x,z)); - whitePixels[x][z] = color.equals(Color.WHITE); - } - } - } - - return whitePixels; - } - - - - //================// - // helper classes // - //================// - - private static class CloudParams - { - public final float textureWidth; - public final float widthInBlocks; - public final float halfWidthInBlocks; - - public final int instanceOffsetX; - public final int instanceOffsetZ; - - - public float xOffset = 0; - public float zOffset = 0; - - public long lastFrameTime = System.currentTimeMillis(); - - - - // constructor // - - public CloudParams(float textureWidth, int instanceOffsetX, int instanceOffsetZ) - { - this.textureWidth = textureWidth; - this.widthInBlocks = (this.textureWidth * CLOUD_BOX_WIDTH); - this.halfWidthInBlocks = this.widthInBlocks / 2; - - this.instanceOffsetX = instanceOffsetX; - this.instanceOffsetZ = instanceOffsetZ; - } - - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java deleted file mode 100644 index b3768548d..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java +++ /dev/null @@ -1,607 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.render.renderer.generic; - -import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod; -import com.seibel.distanthorizons.api.enums.config.EDhApiLoggerMode; -import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial; -import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiGenericObjectShaderProgram; -import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; -import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister; -import com.seibel.distanthorizons.api.methods.events.abstractEvents.*; -import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; -import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; -import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; -import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.logging.ConfigBasedSpamLogger; -import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.logging.f3.F3Screen; -import com.seibel.distanthorizons.core.render.glObject.GLProxy; -import com.seibel.distanthorizons.core.render.glObject.GLState; -import com.seibel.distanthorizons.core.render.glObject.buffer.GLElementBuffer; -import com.seibel.distanthorizons.core.render.glObject.buffer.GLVertexBuffer; -import com.seibel.distanthorizons.core.util.LodUtil; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; -import com.seibel.distanthorizons.core.util.math.Vec3d; -import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; -import com.seibel.distanthorizons.coreapi.DependencyInjection.OverrideInjector; -import com.seibel.distanthorizons.coreapi.ModInfo; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; -import org.lwjgl.opengl.ARBInstancedArrays; -import org.lwjgl.opengl.GL32; -import org.lwjgl.opengl.GL33; - -import java.awt.*; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; - -/** - * Handles rendering generic groups of {@link DhApiRenderableBox}. - * - * @see IDhApiCustomRenderRegister - * @see DhApiRenderableBox - */ -public class GenericObjectRenderer implements IDhApiCustomRenderRegister -{ - private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - public static final ConfigBasedSpamLogger SPAM_LOGGER = new ConfigBasedSpamLogger(LogManager.getLogger(GenericObjectRenderer.class), () -> EDhApiLoggerMode.LOG_ALL_TO_CHAT, 1); - - private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); - - /** - * Can be used to troubleshoot the renderer. - * If enabled several debug objects will render around (0,150,0). - */ - public static final boolean RENDER_DEBUG_OBJECTS = false; - - - // rendering setup - private boolean init = false; - - private IDhApiGenericObjectShaderProgram shaderProgram; - private GLVertexBuffer boxVertexBuffer; - private GLElementBuffer boxIndexBuffer; - - private boolean useInstancedRendering; - private boolean vertexAttribDivisorSupported; - private boolean instancedArraysSupported; - - - - private final ConcurrentHashMap boxGroupById = new ConcurrentHashMap<>(); - - - - /** A box from 0,0,0 to 1,1,1 */ - private static final float[] BOX_VERTICES = { - // Pos x y z - - // min X, vertical face - 0, 0, 0, - 1, 0, 0, - 1, 1, 0, - 0, 1, 0, - // max X, vertical face - 0, 1, 1, - 1, 1, 1, - 1, 0, 1, - 0, 0, 1, - - // min Z, vertical face - 0, 0, 1, - 0, 0, 0, - 0, 1, 0, - 0, 1, 1, - // max Z, vertical face - 1, 0, 1, - 1, 1, 1, - 1, 1, 0, - 1, 0, 0, - - // min Y, horizontal face - 0, 0, 1, - 1, 0, 1, - 1, 0, 0, - 0, 0, 0, - // max Y, horizontal face - 0, 1, 1, - 1, 1, 1, - 1, 1, 0, - 0, 1, 0, - }; - - private static final int[] BOX_INDICES = { - // min X, vertical face - 2, 1, 0, - 0, 3, 2, - // max X, vertical face - 6, 5, 4, - 4, 7, 6, - - // min Z, vertical face - 10, 9, 8, - 8, 11, 10, - // max Z, vertical face - 14, 13, 12, - 12, 15, 14, - - // min Y, horizontal face - 18, 17, 16, - 16, 19, 18, - // max Y, horizontal face - 20, 21, 22, - 22, 23, 20, - }; - - - - //=============// - // constructor // - //=============// - - public GenericObjectRenderer() { } - - public void init() - { - if (this.init) - { - return; - } - this.init = true; - - this.vertexAttribDivisorSupported = GLProxy.getInstance().vertexAttribDivisorSupported; - this.instancedArraysSupported = GLProxy.getInstance().instancedArraysSupported; - this.useInstancedRendering = this.vertexAttribDivisorSupported || this.instancedArraysSupported; - if (!this.useInstancedRendering) - { - LOGGER.warn("Instanced rendering not supported by this GPU, falling back to direct rendering. Generic object rendering will be slow."); - } - - this.shaderProgram = new GenericObjectShaderProgram(this.useInstancedRendering); - - - this.createBuffers(); - - if (RENDER_DEBUG_OBJECTS) - { - this.addGenericDebugObjects(); - } - } - private void createBuffers() - { - // box vertices - ByteBuffer boxVerticesBuffer = ByteBuffer.allocateDirect(BOX_VERTICES.length * Float.BYTES); - boxVerticesBuffer.order(ByteOrder.nativeOrder()); - boxVerticesBuffer.asFloatBuffer().put(BOX_VERTICES); - boxVerticesBuffer.rewind(); - this.boxVertexBuffer = new GLVertexBuffer(false); - this.boxVertexBuffer.bind(); - this.boxVertexBuffer.uploadBuffer(boxVerticesBuffer, 8, EDhApiGpuUploadMethod.DATA, BOX_VERTICES.length * Float.BYTES); - - - // box vertex indexes - ByteBuffer solidIndexBuffer = ByteBuffer.allocateDirect(BOX_INDICES.length * Integer.BYTES); - solidIndexBuffer.order(ByteOrder.nativeOrder()); - solidIndexBuffer.asIntBuffer().put(BOX_INDICES); - solidIndexBuffer.rewind(); - this.boxIndexBuffer = new GLElementBuffer(false); - this.boxIndexBuffer.uploadBuffer(solidIndexBuffer, EDhApiGpuUploadMethod.DATA, BOX_INDICES.length * Integer.BYTES, GL32.GL_STATIC_DRAW); - this.boxIndexBuffer.bind(); - - } - private void addGenericDebugObjects() - { - GenericRenderObjectFactory factory = GenericRenderObjectFactory.INSTANCE; - - - // single giant box - IDhApiRenderableBoxGroup singleGiantBoxGroup = factory.createForSingleBox( - ModInfo.NAME + ":CyanChunkBox", - new DhApiRenderableBox( - new DhApiVec3d(0,0,0), new DhApiVec3d(16,190,16), - new Color(Color.CYAN.getRed(), Color.CYAN.getGreen(), Color.CYAN.getBlue(), 125), - EDhApiBlockMaterial.WATER) - ); - singleGiantBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT); - singleGiantBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT); - this.add(singleGiantBoxGroup); - - - // single slender box - IDhApiRenderableBoxGroup singleTallBoxGroup = factory.createForSingleBox( - ModInfo.NAME + ":GreenBeacon", - new DhApiRenderableBox( - new DhApiVec3d(16,0,31), new DhApiVec3d(17,2000,32), - new Color(Color.GREEN.getRed(), Color.GREEN.getGreen(), Color.GREEN.getBlue(), 125), - EDhApiBlockMaterial.ILLUMINATED) - ); - singleTallBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT); - singleTallBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT); - this.add(singleTallBoxGroup); - - - // absolute box group - ArrayList absBoxList = new ArrayList<>(); - for (int i = 0; i < 18; i++) - { - absBoxList.add(new DhApiRenderableBox( - new DhApiVec3d(i,150+i,24), new DhApiVec3d(1+i,151+i,25), - new Color(Color.ORANGE.getRed(), Color.ORANGE.getGreen(), Color.ORANGE.getBlue()), - EDhApiBlockMaterial.LAVA - ) - ); - } - IDhApiRenderableBoxGroup absolutePosBoxGroup = factory.createAbsolutePositionedGroup(ModInfo.NAME + ":OrangeStairs", absBoxList); - this.add(absolutePosBoxGroup); - - - // relative box group - ArrayList relBoxList = new ArrayList<>(); - for (int i = 0; i < 8; i+=2) - { - relBoxList.add(new DhApiRenderableBox( - new DhApiVec3d(0,i,0), new DhApiVec3d(1,1+i,1), - new Color(Color.MAGENTA.getRed(), Color.MAGENTA.getGreen(), Color.MAGENTA.getBlue()), - EDhApiBlockMaterial.METAL - ) - ); - } - IDhApiRenderableBoxGroup relativePosBoxGroup = factory.createRelativePositionedGroup( - ModInfo.NAME + ":MovingMagentaGroup", - new DhApiVec3d(24, 140, 24), - relBoxList); - relativePosBoxGroup.setPreRenderFunc((event) -> - { - DhApiVec3d pos = relativePosBoxGroup.getOriginBlockPos(); - pos.x += event.partialTicks / 2; - pos.x %= 32; - relativePosBoxGroup.setOriginBlockPos(pos); - }); - this.add(relativePosBoxGroup); - - - // massive relative box group - ArrayList massRelBoxList = new ArrayList<>(); - for (int x = 0; x < 50*2; x+=2) - { - for (int z = 0; z < 50*2; z+=2) - { - massRelBoxList.add(new DhApiRenderableBox( - new DhApiVec3d(-x, 0, -z), new DhApiVec3d(1-x, 1, 1-z), - new Color(Color.RED.getRed(), Color.RED.getGreen(), Color.RED.getBlue()), - EDhApiBlockMaterial.TERRACOTTA - ) - ); - } - } - IDhApiRenderableBoxGroup massRelativePosBoxGroup = factory.createRelativePositionedGroup( - ModInfo.NAME + ":MassRedGroup", - new DhApiVec3d(-25, 140, 0), - massRelBoxList); - massRelativePosBoxGroup.setPreRenderFunc((event) -> - { - DhApiVec3d blockPos = massRelativePosBoxGroup.getOriginBlockPos(); - blockPos.y += event.partialTicks / 4; - if (blockPos.y > 150f) - { - blockPos.y = 140f; - - Color newColor = (massRelativePosBoxGroup.get(0).color == Color.RED) ? Color.RED.darker() : Color.RED; - massRelativePosBoxGroup.forEach((box) -> { box.color = newColor; }); - massRelativePosBoxGroup.triggerBoxChange(); - } - - massRelativePosBoxGroup.setOriginBlockPos(blockPos); - }); - this.add(massRelativePosBoxGroup); - } - - - - //==============// - // registration // - //==============// - - @Override - public void add(IDhApiRenderableBoxGroup iBoxGroup) throws IllegalArgumentException - { - if (!(iBoxGroup instanceof RenderableBoxGroup)) - { - throw new IllegalArgumentException("Box group must be of type ["+ RenderableBoxGroup.class.getSimpleName()+"], type received: ["+(iBoxGroup != null ? iBoxGroup.getClass() : "NULL")+"]."); - } - RenderableBoxGroup boxGroup = (RenderableBoxGroup) iBoxGroup; - - - long id = boxGroup.getId(); - if (this.boxGroupById.containsKey(id)) - { - throw new IllegalArgumentException("A box group with the ID [" + id + "] is already present."); - } - - this.boxGroupById.put(id, boxGroup); - } - - @Override - public IDhApiRenderableBoxGroup remove(long id) { return this.boxGroupById.remove(id); } - - public void clear() { this.boxGroupById.clear(); } - - - - //===========// - // rendering // - //===========// - - /** - * @param renderingWithSsao - * if true that means this render call is happening before the SSAO pass - * and any objects rendered in this pass will have SSAO applied to them. - */ - public void render(DhApiRenderParam renderEventParam, IProfilerWrapper profiler, boolean renderingWithSsao) - { - // render setup // - profiler.push("setup"); - - GLState glState = new GLState(); - this.init(); - - ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderSetupEvent.class, renderEventParam); - - GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); - GL32.glEnable(GL32.GL_DEPTH_TEST); - - GL32.glEnable(GL32.GL_BLEND); - GL32.glBlendEquation(GL32.GL_FUNC_ADD); - GL32.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA); - - IDhApiGenericObjectShaderProgram shaderProgram = this.shaderProgram; - IDhApiGenericObjectShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiGenericObjectShaderProgram.class); - if (shaderProgramOverride != null && shaderProgram.overrideThisFrame()) - { - shaderProgram = shaderProgramOverride; - } - - shaderProgram.bind(renderEventParam); - shaderProgram.bindVertexBuffer(this.boxVertexBuffer.getId()); - - this.boxIndexBuffer.bind(); - - Vec3d camPos = MC_RENDER.getCameraExactPosition(); - - - - // rendering // - - Collection boxList = this.boxGroupById.values(); - for (RenderableBoxGroup boxGroup : boxList) - { - // skip boxes that shouldn't render this pass - if (boxGroup.ssaoEnabled == renderingWithSsao) - { - profiler.popPush("render prep"); - boxGroup.preRender(renderEventParam); - - // ignore inactive groups - if (boxGroup.active) - { - boolean cancelRendering = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericObjectRenderEvent.class, new DhApiBeforeGenericObjectRenderEvent.EventParam(renderEventParam, boxGroup)); - if (!cancelRendering) - { - profiler.popPush("rendering"); - profiler.push(boxGroup.getResourceLocationNamespace()); - profiler.push(boxGroup.getResourceLocationPath()); - if (this.useInstancedRendering) - { - this.renderBoxGroupInstanced(shaderProgram, renderEventParam, boxGroup, camPos); - } - else - { - this.renderBoxGroupDirect(shaderProgram, renderEventParam, boxGroup, camPos); - } - profiler.pop(); // resource path - profiler.pop(); // resource namespace - - boxGroup.postRender(renderEventParam); - } - } - } - } - - - - // clean up // - profiler.popPush("cleanup"); - - ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderCleanupEvent.class, renderEventParam); - - shaderProgram.unbind(); - glState.restore(); - - profiler.pop(); - } - - - - //=====================// - // instanced rendering // - //=====================// - - private void renderBoxGroupInstanced(IDhApiGenericObjectShaderProgram shaderProgram, DhApiRenderParam renderEventParam, RenderableBoxGroup boxGroup, Vec3d camPos) - { - // update instance data // - - boxGroup.updateVertexAttributeData(); - - DhApiRenderableBoxGroupShading shading = boxGroup.shading; - if (shading == null) - { - shading = DhApiRenderableBoxGroupShading.getUnshaded(); - } - - shaderProgram.fillIndirectUniformData( - renderEventParam, - shading, boxGroup, - camPos); - - - - // Bind instance data // - - GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, boxGroup.instanceColorVbo); - GL32.glEnableVertexAttribArray(1); - GL32.glVertexAttribPointer(1, 4, GL32.GL_FLOAT, false, 4 * Float.BYTES, 0); - this.vertexAttribDivisor(1, 1); - - GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, boxGroup.instanceScaleVbo); - GL32.glEnableVertexAttribArray(2); - this.vertexAttribDivisor(2, 1); - GL32.glVertexAttribPointer(2, 3, GL32.GL_FLOAT, false, 3 * Float.BYTES, 0); - - GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, boxGroup.instanceChunkPosVbo); - GL32.glEnableVertexAttribArray(3); - this.vertexAttribDivisor(3, 1); - GL32.glVertexAttribIPointer(3, 3, GL32.GL_INT, 3 * Integer.BYTES, 0); - - GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, boxGroup.instanceSubChunkPosVbo); - GL32.glEnableVertexAttribArray(4); - this.vertexAttribDivisor(4, 1); - GL32.glVertexAttribPointer(4, 3, GL32.GL_FLOAT, false, 3 * Float.BYTES, 0); - - GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, boxGroup.instanceMaterialVbo); - GL32.glEnableVertexAttribArray(5); - this.vertexAttribDivisor(5, 1); - GL32.glVertexAttribIPointer(5, 1, GL32.GL_BYTE, Byte.BYTES, 0); - - - // Draw instanced - if (boxGroup.uploadedBoxCount > 0) - { - GL32.glDrawElementsInstanced(GL32.GL_TRIANGLES, BOX_INDICES.length, GL32.GL_UNSIGNED_INT, 0, boxGroup.uploadedBoxCount); - } - - - // Clean up - GL32.glDisableVertexAttribArray(1); - GL32.glDisableVertexAttribArray(2); - GL32.glDisableVertexAttribArray(3); - GL32.glDisableVertexAttribArray(4); - GL32.glDisableVertexAttribArray(5); - } - /** - * Clean way to handle both {@link GL33#glVertexAttribDivisor} and {@link ARBInstancedArrays#glVertexAttribDivisorARB} - * based on which one is supported. - */ - private void vertexAttribDivisor(int index, int divisor) - { - if (this.vertexAttribDivisorSupported) - { - GL33.glVertexAttribDivisor(index, divisor); - } - else if(this.instancedArraysSupported) - { - ARBInstancedArrays.glVertexAttribDivisorARB(index, divisor); - } - else - { - throw new IllegalStateException("Instanced rendering isn't supported by this machine. Direct rendering should have been used instead."); - } - } - - - - - //==================// - // direct rendering // - //==================// - - private void renderBoxGroupDirect(IDhApiGenericObjectShaderProgram shaderProgram, DhApiRenderParam renderEventParam, RenderableBoxGroup boxGroup, Vec3d camPos) - { - DhApiRenderableBoxGroupShading shading = boxGroup.shading; - if (shading == null) - { - shading = DhApiRenderableBoxGroupShading.getUnshaded(); - } - - shaderProgram.fillSharedDirectUniformData(renderEventParam, shading, boxGroup, camPos); - - for (DhApiRenderableBox box : boxGroup) - { - this.renderBox(shaderProgram, renderEventParam, boxGroup, box, camPos); - } - } - private void renderBox( - IDhApiGenericObjectShaderProgram shaderProgram, - DhApiRenderParam renderEventParam, - RenderableBoxGroup boxGroup, DhApiRenderableBox box, - Vec3d camPos) - { - shaderProgram.fillDirectUniformData(renderEventParam, boxGroup, box, camPos); - GL32.glDrawElements(GL32.GL_TRIANGLES, BOX_INDICES.length, GL32.GL_UNSIGNED_INT, 0); - } - - - - //=========// - // getters // - //=========// - - /** @throws IllegalStateException if {@link #init()} function hasn't been called yet */ - public boolean getUseInstancedRendering() throws IllegalStateException - { - if (!this.init) - { - throw new IllegalStateException("GL initialization hasn't been completed."); - } - - return this.useInstancedRendering; - } - - - - //=========// - // F3 menu // - //=========// - - public String getVboRenderDebugMenuString() - { - // get counts - int totalCount = this.boxGroupById.size(); - int activeCount = 0; - for (long key : this.boxGroupById.keySet()) - { - RenderableBoxGroup renderGroup = this.boxGroupById.get(key); - if (renderGroup.active) - { - activeCount++; - } - } - - - String totalCountText = F3Screen.NUMBER_FORMAT.format(totalCount); - String activeCountText = F3Screen.NUMBER_FORMAT.format(activeCount); - return LodUtil.formatLog("Generic Obj Count: " + activeCountText + "/" + totalCountText); - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectShaderProgram.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectShaderProgram.java deleted file mode 100644 index d021d04e0..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectShaderProgram.java +++ /dev/null @@ -1,229 +0,0 @@ -package com.seibel.distanthorizons.core.render.renderer.generic; - -import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiGenericObjectShaderProgram; -import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; -import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3i; -import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; -import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; -import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram; -import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.AbstractVertexAttribute; -import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.VertexPointer; -import com.seibel.distanthorizons.core.util.LodUtil; -import com.seibel.distanthorizons.core.util.math.Mat4f; -import com.seibel.distanthorizons.core.util.math.Vec3f; - -public class GenericObjectShaderProgram extends ShaderProgram implements IDhApiGenericObjectShaderProgram -{ - public static final String VERTEX_SHADER_INSTANCED_PATH = "shaders/genericObject/instanced/vert.vert"; - public static final String VERTEX_SHADER_DIRECT_PATH = "shaders/genericObject/direct/vert.vert"; - public static final String FRAGMENT_SHADER_INSTANCED_PATH = "shaders/genericObject/instanced/frag.frag"; - public static final String FRAGMENT_SHADER_DIRECT_PATH = "shaders/genericObject/direct/frag.frag"; - - public final AbstractVertexAttribute va; - - - // shader uniforms - private final int directShaderTransformUniform; - private final int directShaderColorUniform; - - private final int instancedShaderOffsetChunkUniform; - private final int instancedShaderOffsetSubChunkUniform; - private final int instancedShaderCameraChunkPosUniform; - private final int instancedShaderCameraSubChunkPosUniform; - private final int instancedShaderProjectionModelViewMatrixUniform; - - private final int lightMapUniform; - private final int skyLightUniform; - private final int blockLightUniform; - - private final int northShadingUniform; - private final int southShadingUniform; - private final int eastShadingUniform; - private final int westShadingUniform; - private final int topShadingUniform; - private final int bottomShadingUniform; - - - - //=============// - // constructor // - //=============// - - public GenericObjectShaderProgram(boolean useInstancedRendering) - { - super( - useInstancedRendering ? VERTEX_SHADER_INSTANCED_PATH : VERTEX_SHADER_DIRECT_PATH, - useInstancedRendering ? FRAGMENT_SHADER_INSTANCED_PATH : FRAGMENT_SHADER_DIRECT_PATH, - "fragColor", new String[]{"vPosition"}); - - this.va = AbstractVertexAttribute.create(); - this.va.bind(); - // Pos - this.va.setVertexAttribute(0, 0, VertexPointer.addVec3Pointer(false)); - this.va.completeAndCheck(Float.BYTES * 3); - - this.directShaderTransformUniform = this.tryGetUniformLocation("uTransform"); - this.directShaderColorUniform = this.tryGetUniformLocation("uColor"); - - this.instancedShaderOffsetChunkUniform = this.tryGetUniformLocation("uOffsetChunk"); - this.instancedShaderOffsetSubChunkUniform = this.tryGetUniformLocation("uOffsetSubChunk"); - this.instancedShaderCameraChunkPosUniform = this.tryGetUniformLocation("uCameraPosChunk"); - this.instancedShaderCameraSubChunkPosUniform = this.tryGetUniformLocation("uCameraPosSubChunk"); - this.instancedShaderProjectionModelViewMatrixUniform = this.tryGetUniformLocation("uProjectionMvm"); - - this.lightMapUniform = this.getUniformLocation("uLightMap"); - this.skyLightUniform = this.getUniformLocation("uSkyLight"); - this.blockLightUniform = this.getUniformLocation("uBlockLight"); - this.northShadingUniform = this.getUniformLocation("uNorthShading"); - this.southShadingUniform = this.getUniformLocation("uSouthShading"); - this.eastShadingUniform = this.getUniformLocation("uEastShading"); - this.westShadingUniform = this.getUniformLocation("uWestShading"); - this.topShadingUniform = this.getUniformLocation("uTopShading"); - this.bottomShadingUniform = this.getUniformLocation("uBottomShading"); - - } - - - - //=========// - // methods // - //=========// - - @Override - public void bind(DhApiRenderParam renderEventParam) - { - super.bind(); - this.va.bind(); - } - @Override - public void unbind() - { - super.unbind(); - this.va.unbind(); - } - - @Override - public void free() - { - this.va.free(); - super.free(); - } - - @Override - public void bindVertexBuffer(int vbo) { this.va.bindBufferToAllBindingPoints(vbo); } - - @Override - public void fillIndirectUniformData( - DhApiRenderParam renderParameters, - DhApiRenderableBoxGroupShading shading, IDhApiRenderableBoxGroup boxGroup, - DhApiVec3d camPos - ) - { - Mat4f projectionMvmMatrix = new Mat4f(renderParameters.dhProjectionMatrix); - projectionMvmMatrix.multiply(renderParameters.dhModelViewMatrix); - - super.bind(); - - - - - this.setUniform(this.instancedShaderOffsetChunkUniform, - new DhApiVec3i( - LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().x), - LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().y), - LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().z) - )); - this.setUniform(this.instancedShaderOffsetSubChunkUniform, - new Vec3f( - LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().x), - LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().y), - LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().z) - )); - - this.setUniform(this.instancedShaderCameraChunkPosUniform, - new DhApiVec3i( - LodUtil.getChunkPosFromDouble(camPos.x), - LodUtil.getChunkPosFromDouble(camPos.y), - LodUtil.getChunkPosFromDouble(camPos.z) - )); - this.setUniform(this.instancedShaderCameraSubChunkPosUniform, - new Vec3f( - LodUtil.getSubChunkPosFromDouble(camPos.x), - LodUtil.getSubChunkPosFromDouble(camPos.y), - LodUtil.getSubChunkPosFromDouble(camPos.z) - )); - - this.setUniform(this.instancedShaderProjectionModelViewMatrixUniform, projectionMvmMatrix); - - this.setUniform(this.lightMapUniform, 0); // TODO this should probably be passed in - this.setUniform(this.skyLightUniform, boxGroup.getSkyLight()); - this.setUniform(this.blockLightUniform, boxGroup.getBlockLight()); - - - this.setUniform(this.northShadingUniform, shading.north); - this.setUniform(this.southShadingUniform, shading.south); - this.setUniform(this.eastShadingUniform, shading.east); - this.setUniform(this.westShadingUniform, shading.west); - this.setUniform(this.topShadingUniform, shading.top); - this.setUniform(this.bottomShadingUniform, shading.bottom); - - - } - - - @Override - public void fillSharedDirectUniformData( - DhApiRenderParam renderParameters, - DhApiRenderableBoxGroupShading shading, IDhApiRenderableBoxGroup boxGroup, - DhApiVec3d camPos) - { - - this.setUniform(this.lightMapUniform, 0); // TODO this should probably be passed in - this.setUniform(this.skyLightUniform, boxGroup.getSkyLight()); - this.setUniform(this.blockLightUniform, boxGroup.getBlockLight()); - - - this.setUniform(this.northShadingUniform, shading.north); - this.setUniform(this.southShadingUniform, shading.south); - this.setUniform(this.eastShadingUniform, shading.east); - this.setUniform(this.westShadingUniform, shading.west); - this.setUniform(this.topShadingUniform, shading.top); - this.setUniform(this.bottomShadingUniform, shading.bottom); - - } - - public void fillDirectUniformData( - DhApiRenderParam renderParameters, - IDhApiRenderableBoxGroup boxGroup, DhApiRenderableBox box, - DhApiVec3d camPos) - { - Mat4f projectionMvmMatrix = new Mat4f(renderParameters.dhProjectionMatrix); - projectionMvmMatrix.multiply(renderParameters.dhModelViewMatrix); - - Mat4f boxTransform = Mat4f.createTranslateMatrix( - (float) (box.minPos.x + boxGroup.getOriginBlockPos().x - camPos.x), - (float) (box.minPos.y + boxGroup.getOriginBlockPos().y - camPos.y), - (float) (box.minPos.z + boxGroup.getOriginBlockPos().z - camPos.z)); - boxTransform.multiply(Mat4f.createScaleMatrix( - (float) (box.maxPos.x - box.minPos.x), - (float) (box.maxPos.y - box.minPos.y), - (float) (box.maxPos.z - box.minPos.z))); - projectionMvmMatrix.multiply(boxTransform); - this.setUniform(this.directShaderTransformUniform, projectionMvmMatrix); - - this.setUniform(this.directShaderColorUniform, box.color); - - } - - - - @Override - public int getId() { return this.id; } - - /** The base DH render program should always render */ - @Override - public boolean overrideThisFrame() { return true; } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericRenderObjectFactory.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericRenderObjectFactory.java deleted file mode 100644 index ca0590957..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericRenderObjectFactory.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.render.renderer.generic; - -import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderObjectFactory; -import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister; -import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; -import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; -import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.util.math.Vec3d; -import com.seibel.distanthorizons.core.util.math.Vec3f; -import org.apache.logging.log4j.Logger; - -import java.util.List; -import java.util.*; - -/** - * Handles creating {@link DhApiRenderableBox}. - * - * @see IDhApiCustomRenderRegister - * @see DhApiRenderableBox - */ -public class GenericRenderObjectFactory implements IDhApiCustomRenderObjectFactory -{ - private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - - public static final GenericRenderObjectFactory INSTANCE = new GenericRenderObjectFactory(); - - - - //=============// - // constructor // - //=============// - - private GenericRenderObjectFactory() { } - - - - //================// - // group creation // - //================// - - @Override - public IDhApiRenderableBoxGroup createForSingleBox(String resourceLocation, DhApiRenderableBox box) - { - ArrayList list = new ArrayList<>(); - list.add(box); - return this.createAbsolutePositionedGroup(resourceLocation, list); - } - - @Override - public IDhApiRenderableBoxGroup createRelativePositionedGroup(String resourceLocation, DhApiVec3d originBlockPos, List boxList) - { return new RenderableBoxGroup(resourceLocation, new DhApiVec3d(originBlockPos.x, originBlockPos.y, originBlockPos.z), boxList, true); } - - @Override - public IDhApiRenderableBoxGroup createAbsolutePositionedGroup(String resourceLocation, List boxList) - { return new RenderableBoxGroup(resourceLocation, new DhApiVec3d(0, 0, 0), boxList, false); } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/RenderableBoxGroup.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/RenderableBoxGroup.java deleted file mode 100644 index e02588c86..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/RenderableBoxGroup.java +++ /dev/null @@ -1,354 +0,0 @@ -package com.seibel.distanthorizons.core.render.renderer.generic; - -import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; -import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; -import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; -import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; -import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; -import com.seibel.distanthorizons.core.render.glObject.GLProxy; -import com.seibel.distanthorizons.core.util.LodUtil; -import org.jetbrains.annotations.Nullable; -import org.lwjgl.opengl.GL32; - -import java.awt.*; -import java.io.Closeable; -import java.util.*; -import java.util.List; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; -import java.util.function.Predicate; -import java.util.function.UnaryOperator; -import java.util.stream.Stream; - -public class RenderableBoxGroup - extends AbstractList - implements IDhApiRenderableBoxGroup, Closeable - { - public final static AtomicInteger NEXT_ID_ATOMIC_INT = new AtomicInteger(0); - - - - public final long id; - - public final String resourceLocationNamespace; - public final String resourceLocationPath; - - /** If false the boxes will be positioned relative to the level's origin */ - public final boolean positionBoxesRelativeToGroupOrigin; - - private final ArrayList boxList; - - private final DhApiVec3d originBlockPos; - - - public boolean active = true; - public boolean ssaoEnabled = true; - private boolean vertexDataDirty = true; - - public int skyLight = 15; - public int blockLight = 0; - public DhApiRenderableBoxGroupShading shading = DhApiRenderableBoxGroupShading.getDefaultShaded(); - - @Nullable - public Consumer beforeRenderFunc; - public Consumer afterRenderFunc; - - // instance data - public int instanceColorVbo = 0; - public int instanceMaterialVbo = 0; - public int instanceScaleVbo = 0; - public int instanceChunkPosVbo = 0; - public int instanceSubChunkPosVbo = 0; - - public int uploadedBoxCount = -1; - - - - // setters/getters // - - @Override - public long getId() { return this.id; } - - @Override - public String getResourceLocationNamespace() { return this.resourceLocationNamespace; } - @Override - public String getResourceLocationPath() { return this.resourceLocationPath; } - - @Override - public void setOriginBlockPos(DhApiVec3d pos) - { - this.originBlockPos.x = pos.x; - this.originBlockPos.y = pos.y; - this.originBlockPos.z = pos.z; - } - - @Override - public DhApiVec3d getOriginBlockPos() { return new DhApiVec3d(this.originBlockPos.x, this.originBlockPos.y, this.originBlockPos.z); } - - - @Override - public void setSkyLight(int skyLight) - { - if (skyLight < LodUtil.MIN_MC_LIGHT || skyLight > LodUtil.MAX_MC_LIGHT) - { - throw new IllegalArgumentException("Sky light ["+skyLight+"] must be between ["+LodUtil.MIN_MC_LIGHT+"] and ["+LodUtil.MAX_MC_LIGHT+"] (inclusive)."); - } - this.skyLight = skyLight; - } - @Override - public int getSkyLight() { return this.skyLight; } - - @Override - public void setBlockLight(int blockLight) - { - if (blockLight < LodUtil.MIN_MC_LIGHT || blockLight > LodUtil.MAX_MC_LIGHT) - { - throw new IllegalArgumentException("Block light ["+blockLight+"] must be between ["+LodUtil.MIN_MC_LIGHT+"] and ["+LodUtil.MAX_MC_LIGHT+"] (inclusive)."); - } - this.blockLight = blockLight; - } - @Override - public int getBlockLight() { return this.blockLight; } - - - - //=============// - // constructor // - //=============// - - public RenderableBoxGroup( - String resourceLocation, - DhApiVec3d originBlockPos, List boxList, - boolean positionBoxesRelativeToGroupOrigin) throws IllegalArgumentException - { - String[] splitResourceLocation = resourceLocation.split(":"); - if (splitResourceLocation.length != 2) - { - throw new IllegalArgumentException("Resource Location must be a string that's separated by a single colon, for example: [DistantHorizons:Beacons], your namespace ["+resourceLocation+"], contains ["+(splitResourceLocation.length-1)+"] colons."); - } - - this.resourceLocationNamespace = splitResourceLocation[0]; - this.resourceLocationPath = splitResourceLocation[1]; - - this.id = NEXT_ID_ATOMIC_INT.getAndIncrement(); - this.boxList = new ArrayList<>(boxList); - - this.originBlockPos = originBlockPos; - this.positionBoxesRelativeToGroupOrigin = positionBoxesRelativeToGroupOrigin; - } - - - - // methods // - - @Override - public boolean add(DhApiRenderableBox box) { return this.boxList.add(box); } - - @Override - public void setPreRenderFunc(Consumer func) { this.beforeRenderFunc = func; } - - @Override - public void setPostRenderFunc(Consumer func) { this.afterRenderFunc = func; } - - @Override - public void triggerBoxChange() { this.vertexDataDirty = true; } - - @Override - public void setActive(boolean active) { this.active = active; } - @Override - public boolean isActive() { return this.active; } - - @Override - public void setSsaoEnabled(boolean ssaoEnabled) { this.ssaoEnabled = ssaoEnabled; } - @Override - public boolean isSsaoEnabled() { return this.ssaoEnabled; } - - /** - * This is called before every frame, even if {@link this#isActive()} returns false.
- * {@link this#isActive()} can be changed at this point before the object is rendered to the frame. - */ - public void preRender(DhApiRenderParam renderEventParam) - { - if (this.beforeRenderFunc != null) - { - this.beforeRenderFunc.accept(renderEventParam); - } - } - /** - * Called after rendering is completed.
- * Can be used to handle any necessary cleanup. - */ - public void postRender(DhApiRenderParam renderEventParam) - { - if (this.afterRenderFunc != null) - { - this.afterRenderFunc.accept(renderEventParam); - } - } - - @Override - public void setShading(DhApiRenderableBoxGroupShading shading) { this.shading = shading; } - @Override - public DhApiRenderableBoxGroupShading getShading() { return this.shading; } - - - - //================// - // List Overrides // - //================// - - @Override - public DhApiRenderableBox get(int index) { return this.boxList.get(index); } - @Override - public int size() { return this.boxList.size(); } - @Override - public boolean removeIf(Predicate filter) { return this.boxList.removeIf(filter); } - @Override - public void replaceAll(UnaryOperator operator) { this.boxList.replaceAll(operator); } - @Override - public void sort(Comparator c) { this.boxList.sort(c); } - @Override - public void forEach(Consumer action) { this.boxList.forEach(action); } - @Override - public Spliterator spliterator() { return this.boxList.spliterator(); } - @Override - public Stream stream() { return this.boxList.stream(); } - @Override - public Stream parallelStream() { return this.boxList.parallelStream(); } - - - - //===================// - // vertex attributes // - //===================// - - /** Does nothing if the vertex data is already up-to-date */ - public void updateVertexAttributeData() - { - if (!this.vertexDataDirty) - { - return; - } - this.vertexDataDirty = false; - - if (this.instanceChunkPosVbo == 0) - { - this.instanceChunkPosVbo = GL32.glGenBuffers(); - this.instanceSubChunkPosVbo = GL32.glGenBuffers(); - this.instanceScaleVbo = GL32.glGenBuffers(); - this.instanceColorVbo = GL32.glGenBuffers(); - this.instanceMaterialVbo = GL32.glGenBuffers(); - } - - int boxCount = this.size(); - this.uploadedBoxCount = boxCount; - - - - // transformation / scaling // - int[] chunkPosData = new int[boxCount * 3]; - float[] subChunkPosData = new float[boxCount * 3]; - float[] scalingData = new float[boxCount * 3]; - for (int i = 0; i < boxCount; i++) - { - DhApiRenderableBox box = this.get(i); - - int dataIndex = i * 3; - - chunkPosData[dataIndex] = LodUtil.getChunkPosFromDouble(box.minPos.x); - chunkPosData[dataIndex + 1] = LodUtil.getChunkPosFromDouble(box.minPos.y); - chunkPosData[dataIndex + 2] = LodUtil.getChunkPosFromDouble(box.minPos.z); - - subChunkPosData[dataIndex] = LodUtil.getSubChunkPosFromDouble(box.minPos.x); - subChunkPosData[dataIndex + 1] = LodUtil.getSubChunkPosFromDouble(box.minPos.y); - subChunkPosData[dataIndex + 2] = LodUtil.getSubChunkPosFromDouble(box.minPos.z); - - scalingData[dataIndex] = (float) (box.maxPos.x - box.minPos.x); - scalingData[dataIndex + 1] = (float) (box.maxPos.y - box.minPos.y); - scalingData[dataIndex + 2] = (float) (box.maxPos.z - box.minPos.z); - - } - - - // colors/materials // - float[] colorData = new float[boxCount * 4]; - byte[] materialData = new byte[boxCount]; - for (int i = 0; i < boxCount; i++) - { - DhApiRenderableBox box = this.get(i); - Color color = box.color; - int colorIndex = i * 4; - colorData[colorIndex] = color.getRed() / 255.0f; - colorData[colorIndex + 1] = color.getGreen() / 255.0f; - colorData[colorIndex + 2] = color.getBlue() / 255.0f; - colorData[colorIndex + 3] = color.getAlpha() / 255.0f; - - materialData[i] = box.material; - } - - - // Upload transformation matrices - GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.instanceChunkPosVbo); - GL32.glBufferData(GL32.GL_ARRAY_BUFFER, chunkPosData ,GL32.GL_DYNAMIC_DRAW); - GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.instanceSubChunkPosVbo); - GL32.glBufferData(GL32.GL_ARRAY_BUFFER, subChunkPosData ,GL32.GL_DYNAMIC_DRAW); - GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.instanceScaleVbo); - GL32.glBufferData(GL32.GL_ARRAY_BUFFER, scalingData, GL32.GL_DYNAMIC_DRAW); - - // Upload colors - GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.instanceColorVbo); - GL32.glBufferData(GL32.GL_ARRAY_BUFFER, colorData, GL32.GL_DYNAMIC_DRAW); - - // Upload materials - GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.instanceMaterialVbo); - GL32.glBufferData(GL32.GL_ARRAY_BUFFER, colorData, GL32.GL_DYNAMIC_DRAW); - } - - - - //================// - // base overrides // - //================// - - @Override - public String toString() { return "ID:["+this.id+"], pos:["+this.originBlockPos.x+","+this.originBlockPos.y+","+this.originBlockPos.z+"], size:["+this.size()+"], active:["+this.active+"]"; } - - @Override - public void close() - { - GLProxy.getInstance().queueRunningOnRenderThread(() -> - { - if (this.instanceChunkPosVbo != 0) - { - GL32.glDeleteBuffers(this.instanceChunkPosVbo); - this.instanceChunkPosVbo = 0; - } - - if (this.instanceSubChunkPosVbo != 0) - { - GL32.glDeleteBuffers(this.instanceSubChunkPosVbo); - this.instanceSubChunkPosVbo = 0; - } - - if (this.instanceScaleVbo != 0) - { - GL32.glDeleteBuffers(this.instanceScaleVbo); - this.instanceScaleVbo = 0; - } - - if (this.instanceColorVbo != 0) - { - GL32.glDeleteBuffers(this.instanceColorVbo); - this.instanceColorVbo = 0; - } - - if (this.instanceMaterialVbo != 0) - { - GL32.glDeleteBuffers(this.instanceMaterialVbo); - this.instanceMaterialVbo = 0; - } - }); - } - - } - \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogApplyShader.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogApplyShader.java deleted file mode 100644 index 4906b75b2..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogApplyShader.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.render.renderer.shaders; - -import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram; -import com.seibel.distanthorizons.core.render.renderer.FogRenderer; -import com.seibel.distanthorizons.core.render.renderer.LodRenderer; -import com.seibel.distanthorizons.core.render.renderer.ScreenQuad; -import org.lwjgl.opengl.GL32; - -/** - * Draws the Fog texture onto DH's FrameBuffer.

- * - * See Also:
- * {@link FogRenderer} - Parent to this shader.
- * {@link FogShader} - draws the Fog texture.
- */ -public class FogApplyShader extends AbstractShaderRenderer -{ - public static FogApplyShader INSTANCE = new FogApplyShader(); - - public int fogTexture; - - // uniforms - public int colorTextureUniform; - public int depthTextureUniform; - - - - //=============// - // constructor // - //=============// - - @Override - public void onInit() - { - this.shader = new ShaderProgram( - "shaders/normal.vert", - "shaders/fog/apply.frag", - "fragColor", - new String[]{ "vPosition" }); - - // uniform setup - this.colorTextureUniform = this.shader.getUniformLocation("uColorTexture"); - this.depthTextureUniform = this.shader.getUniformLocation("uDepthTexture"); - - } - - - - //=============// - // render prep // - //=============// - - @Override - protected void onApplyUniforms(float partialTicks) - { - GL32.glActiveTexture(GL32.GL_TEXTURE0); - GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.fogTexture); - GL32.glUniform1i(this.colorTextureUniform, 0); - - GL32.glActiveTexture(GL32.GL_TEXTURE1); - GL32.glBindTexture(GL32.GL_TEXTURE_2D, LodRenderer.getActiveDepthTextureId()); - GL32.glUniform1i(this.depthTextureUniform, 1); - } - - - - //========// - // render // - //========// - - @Override - protected void onRender() - { - GL32.glEnable(GL32.GL_BLEND); - GL32.glBlendEquation(GL32.GL_FUNC_ADD); - GL32.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA); - - // apply the rendered Fog to DH's framebuffer - GL32.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, FogShader.INSTANCE.frameBuffer); - GL32.glBindFramebuffer(GL32.GL_DRAW_FRAMEBUFFER, LodRenderer.getActiveFramebufferId()); - - ScreenQuad.INSTANCE.render(); - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java index 5987af82e..e5a1fc255 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java @@ -31,7 +31,7 @@ import com.seibel.distanthorizons.core.render.renderer.ScreenQuad; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; -import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.coreapi.util.math.Mat4f; import org.lwjgl.opengl.GL32; import java.awt.*; @@ -44,31 +44,24 @@ public class FogShader extends AbstractShaderRenderer private static final IVersionConstants VERSION_CONSTANTS = SingletonInjector.INSTANCE.get(IVersionConstants.class); - public int frameBuffer; - private final LodFogConfig fogConfig; private Mat4f inverseMvmProjMatrix; + public int gInvertedModelViewProjectionUniform; + public int gDepthMapUniform; + + // Fog Uniforms + public int fogColorUniform; + public int fogScaleUniform; + public int fogVerticalScaleUniform; + public int nearFogStartUniform; + public int nearFogLengthUniform; + public int fullFogModeUniform; - // Uniforms - public int uFogColor; - public int uFogScale; - public int uFogVerticalScale; - public int uNearFogStart; - public int uNearFogLength; - public int uFullFogMode; - - /** Inverted Model View Projection matrix */ - public int uInvMvmProj; - public int uDepthMap; - - - - //=============// - // constructor // - //=============// - - public FogShader(LodFogConfig fogConfig) { this.fogConfig = fogConfig; } + public FogShader(LodFogConfig fogConfig) + { + this.fogConfig = fogConfig; + } @Override public void onInit() @@ -83,47 +76,49 @@ public class FogShader extends AbstractShaderRenderer // all uniforms should be tryGet... // because disabling fog can cause the GLSL to optimize out most (if not all) uniforms - this.uDepthMap = this.shader.getUniformLocation("uDepthMap"); - this.uInvMvmProj = this.shader.getUniformLocation("uInvMvmProj"); + this.gInvertedModelViewProjectionUniform = this.shader.tryGetUniformLocation("gInvMvmProj"); + this.gDepthMapUniform = this.shader.tryGetUniformLocation("gDepthMap"); // Fog uniforms - this.uFogScale = this.shader.tryGetUniformLocation("uFogScale"); - this.uFogVerticalScale = this.shader.tryGetUniformLocation("uFogVerticalScale"); - this.uFogColor = this.shader.tryGetUniformLocation("uFogColor"); - this.uFullFogMode = this.shader.tryGetUniformLocation("uFullFogMode"); + this.fogColorUniform = this.shader.tryGetUniformLocation("fogColor"); + this.fullFogModeUniform = this.shader.tryGetUniformLocation("fullFogMode"); + this.fogScaleUniform = this.shader.tryGetUniformLocation("fogScale"); + this.fogVerticalScaleUniform = this.shader.tryGetUniformLocation("fogVerticalScale"); // near fog - this.uNearFogStart = this.shader.tryGetUniformLocation("uNearFogStart"); - this.uNearFogLength = this.shader.tryGetUniformLocation("uNearFogLength"); - + this.nearFogStartUniform = this.shader.tryGetUniformLocation("nearFogStart"); + this.nearFogLengthUniform = this.shader.tryGetUniformLocation("nearFogLength"); } - - - //=============// - // render prep // - //=============// - @Override protected void onApplyUniforms(float partialTicks) { - if (this.inverseMvmProjMatrix != null) - { - this.shader.setUniform(this.uInvMvmProj, this.inverseMvmProjMatrix); - } + this.shader.setUniform(this.gInvertedModelViewProjectionUniform, this.inverseMvmProjMatrix); int lodDrawDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get() * LodUtil.CHUNK_WIDTH; + int vanillaDrawDistance = MC_RENDER.getRenderDistance() * LodUtil.CHUNK_WIDTH; + vanillaDrawDistance += LodUtil.CHUNK_WIDTH * 2; // Give it a 2 chunk boundary for near fog. + + // bind the depth buffer + if (this.gDepthMapUniform != -1) + { + GL32.glActiveTexture(GL32.GL_TEXTURE1); + GL32.glBindTexture(GL32.GL_TEXTURE_2D, LodRenderer.getActiveDepthTextureId()); + GL32.glUniform1i(this.gDepthMapUniform, 1); + } // Fog - if (this.uFullFogMode != -1) this.shader.setUniform(this.uFullFogMode, MC_RENDER.isFogStateSpecial() ? 1 : 0); - if (this.uFogColor != -1) this.shader.setUniform(this.uFogColor, MC_RENDER.isFogStateSpecial() ? this.getSpecialFogColor(partialTicks) : this.getFogColor(partialTicks)); + if (this.fullFogModeUniform != -1) this.shader.setUniform(this.fullFogModeUniform, MC_RENDER.isFogStateSpecial() ? 1 : 0); + if (this.fogColorUniform != -1) this.shader.setUniform(this.fogColorUniform, MC_RENDER.isFogStateSpecial() ? this.getSpecialFogColor(partialTicks) : this.getFogColor(partialTicks)); - float nearFogStart = (VERSION_CONSTANTS.isVanillaRenderedChunkSquare() ? (float) Math.sqrt(2.0) : 1.0f) / lodDrawDistance; - if (this.uNearFogStart != -1) this.shader.setUniform(this.uNearFogStart, nearFogStart); - if (this.uNearFogLength != -1) this.shader.setUniform(this.uNearFogLength, 0.0f); - if (this.uFogScale != -1) this.shader.setUniform(this.uFogScale, 1.f / lodDrawDistance); - if (this.uFogVerticalScale != -1) this.shader.setUniform(this.uFogVerticalScale, 1.f / MC.getWrappedClientLevel().getMaxHeight()); + float nearFogLen = vanillaDrawDistance * 0.2f / lodDrawDistance; + float nearFogStart = vanillaDrawDistance * (VERSION_CONSTANTS.isVanillaRenderedChunkSquare() ? (float) Math.sqrt(2.0) : 1.0f) / lodDrawDistance; + if (this.nearFogStartUniform != -1) this.shader.setUniform(this.nearFogStartUniform, nearFogStart); + if (this.nearFogLengthUniform != -1) this.shader.setUniform(this.nearFogLengthUniform, nearFogLen); + if (this.fogScaleUniform != -1) this.shader.setUniform(this.fogScaleUniform, 1.f / lodDrawDistance); + if (this.fogVerticalScaleUniform != -1) this.shader.setUniform(this.fogVerticalScaleUniform, 1.f / MC.getWrappedClientLevel().getHeight()); } + private Color getFogColor(float partialTicks) { Color fogColor; @@ -139,34 +134,29 @@ public class FogShader extends AbstractShaderRenderer return fogColor; } + private Color getSpecialFogColor(float partialTicks) { return MC_RENDER.getSpecialFogColor(partialTicks); } - public void setProjectionMatrix(Mat4f projectionMatrix) + public void setModelViewProjectionMatrix(Mat4f combinedModelViewProjectionMatrix) { - this.inverseMvmProjMatrix = new Mat4f(projectionMatrix); + this.inverseMvmProjMatrix = new Mat4f(combinedModelViewProjectionMatrix); this.inverseMvmProjMatrix.invert(); } - - - //========// - // render // - //========// - @Override protected void onRender() { GLState state = new GLState(); - GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.frameBuffer); - GL32.glDisable(GL32.GL_SCISSOR_TEST); GL32.glDisable(GL32.GL_DEPTH_TEST); - GL32.glDisable(GL32.GL_BLEND); + GL32.glDisable(GL32.GL_SCISSOR_TEST); + + GL32.glEnable(GL32.GL_BLEND); + GL32.glBlendEquation(GL32.GL_FUNC_ADD); + GL32.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ONE); GL32.glActiveTexture(GL32.GL_TEXTURE0); - GL32.glBindTexture(GL32.GL_TEXTURE_2D, LodRenderer.getActiveDepthTextureId()); - GL32.glUniform1i(this.uDepthMap, 0); - + GL32.glBindTexture(GL32.GL_TEXTURE_2D, LodRenderer.getActiveColorTextureId()); ScreenQuad.INSTANCE.render(); state.restore(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOApplyShader.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOApplyShader.java index 43da8521f..eb871c92b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOApplyShader.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOApplyShader.java @@ -50,11 +50,6 @@ public class SSAOApplyShader extends AbstractShaderRenderer public int gFarUniform; - - //=============// - // constructor // - //=============// - @Override public void onInit() { @@ -73,12 +68,6 @@ public class SSAOApplyShader extends AbstractShaderRenderer this.gFarUniform = this.shader.tryGetUniformLocation("gFar"); } - - - //=============// - // render prep // - //=============// - @Override protected void onApplyUniforms(float partialTicks) { @@ -113,7 +102,6 @@ public class SSAOApplyShader extends AbstractShaderRenderer } - //========// // render // //========// diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOShader.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOShader.java index 05834e159..867f891e0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOShader.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOShader.java @@ -24,7 +24,7 @@ import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram; import com.seibel.distanthorizons.core.render.renderer.LodRenderer; import com.seibel.distanthorizons.core.render.renderer.SSAORenderer; import com.seibel.distanthorizons.core.render.renderer.ScreenQuad; -import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.coreapi.util.math.Mat4f; import org.lwjgl.opengl.GL32; /** @@ -38,13 +38,11 @@ public class SSAOShader extends AbstractShaderRenderer { public static SSAOShader INSTANCE = new SSAOShader(); - public int frameBuffer; private Mat4f projection; private Mat4f invertedProjection; - // uniforms public int gProjUniform; public int gInvProjUniform; @@ -56,17 +54,11 @@ public class SSAOShader extends AbstractShaderRenderer public int gDepthMapUniform; - - //=============// - // constructor // - //=============// - @Override public void onInit() { this.shader = new ShaderProgram("shaders/normal.vert", "shaders/ssao/ao.frag", - "fragColor", new String[]{ "vPosition" } - ); + "fragColor", new String[]{"vPosition"}); // uniform setup this.gProjUniform = this.shader.getUniformLocation("gProj"); @@ -79,12 +71,6 @@ public class SSAOShader extends AbstractShaderRenderer this.gDepthMapUniform = this.shader.getUniformLocation("gDepthMap"); } - - - //=============// - // render prep // - //=============// - public void setProjectionMatrix(Mat4f projectionMatrix) { this.projection = projectionMatrix; @@ -122,7 +108,6 @@ public class SSAOShader extends AbstractShaderRenderer } - //========// // render // //========// diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/BeaconBeamDTO.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/BeaconBeamDTO.java deleted file mode 100644 index 452af3873..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/BeaconBeamDTO.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.sql.dto; - -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; -import com.seibel.distanthorizons.core.pos.DhBlockPos; -import com.seibel.distanthorizons.core.pos.DhBlockPos; - -import java.awt.*; - -/** handles storing {@link FullDataSourceV2}'s in the database. */ -public class BeaconBeamDTO implements IBaseDTO -{ - public DhBlockPos pos; - public Color color; - - - - //=============// - // constructor // - //=============// - - public BeaconBeamDTO(DhBlockPos pos, Color color) - { - this.pos = pos; - this.color = color; - } - - - - //===========// - // overrides // - //===========// - - @Override - public DhBlockPos getKey() { return this.pos; } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/AbstractDhRepo.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/AbstractDhRepo.java index de29b49bb..34de259dc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/AbstractDhRepo.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/AbstractDhRepo.java @@ -426,36 +426,6 @@ public abstract class AbstractDhRepo> implemen } } - /** can be used to make sure everything is closed when the world closes */ - public static void closeAllConnections() - { - LOGGER.info("Closing all ["+ACTIVE_CONNECTION_STRINGS_BY_REPO.size()+"] database connections..."); - for (String connectionString : ACTIVE_CONNECTION_STRINGS_BY_REPO.values()) - { - try - { - Connection connection = CONNECTIONS_BY_CONNECTION_STRING.remove(connectionString); - if (connection != null) - { - if (!connection.isClosed()) - { - LOGGER.info("Closing database connection: [" + connectionString + "]"); - connection.close(); - } - else - { - LOGGER.warn("Attempting to close already closed database connection: [" + connectionString + "]"); - } - } - } - catch(SQLException e) - { - // connection close failed. - LOGGER.error("Unable to close the connection ["+connectionString+"], error: ["+e.getMessage()+"]"); - } - } - } - @Override public void close() { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/BeaconBeamRepo.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/BeaconBeamRepo.java deleted file mode 100644 index 9616a79b2..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/BeaconBeamRepo.java +++ /dev/null @@ -1,196 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.sql.repo; - -import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.pos.DhBlockPos; -import com.seibel.distanthorizons.core.pos.DhChunkPos; -import com.seibel.distanthorizons.core.pos.DhSectionPos; -import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; -import com.seibel.distanthorizons.core.util.LodUtil; -import org.apache.logging.log4j.Logger; - -import java.awt.*; -import java.io.File; -import java.sql.PreparedStatement; -import java.sql.SQLException; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; - -public class BeaconBeamRepo extends AbstractDhRepo -{ - private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - - - - //=============// - // constructor // - //=============// - - public BeaconBeamRepo(String databaseType, File databaseFile) throws SQLException - { - super(databaseType, databaseFile, BeaconBeamDTO.class); - } - - - - //===========// - // overrides // - //===========// - - @Override - public String getTableName() { return "BeaconBeam"; } - - @Override - public String createWhereStatement(DhBlockPos pos) { return "BlockPosX = "+pos.x+" AND BlockPosY = "+pos.y+" AND BlockPosZ = "+pos.z; } - - - - //=======================// - // repo required methods // - //=======================// - - @Override - public BeaconBeamDTO convertDictionaryToDto(Map objectMap) throws ClassCastException - { - int posX = (Integer) objectMap.get("BlockPosX"); - int posY = (Integer) objectMap.get("BlockPosY"); - int posZ = (Integer) objectMap.get("BlockPosZ"); - - int red = (Integer) objectMap.get("ColorR"); - int green = (Integer) objectMap.get("ColorG"); - int blue = (Integer) objectMap.get("ColorB"); - - - BeaconBeamDTO dto = new BeaconBeamDTO(new DhBlockPos(posX, posY, posZ), new Color(red, green, blue)); - return dto; - } - - @Override - public PreparedStatement createInsertStatement(BeaconBeamDTO dto) throws SQLException - { - String sql = - "INSERT INTO "+this.getTableName() + " (\n" + - " BlockPosX, BlockPosY, BlockPosZ, \n" + - " ColorR, ColorG, ColorB, \n" + - " LastModifiedUnixDateTime, CreatedUnixDateTime) \n" + - "VALUES( \n" + - " ?, ?, ?, \n" + - " ?, ?, ?, \n" + - " ?, ? \n" + - ");"; - PreparedStatement statement = this.createPreparedStatement(sql); - - int i = 1; - statement.setObject(i++, dto.pos.x); - statement.setObject(i++, dto.pos.y); - statement.setObject(i++, dto.pos.z); - - statement.setObject(i++, dto.color.getRed()); - statement.setObject(i++, dto.color.getGreen()); - statement.setObject(i++, dto.color.getBlue()); - - statement.setObject(i++, System.currentTimeMillis()); // last modified unix time - statement.setObject(i++, System.currentTimeMillis()); // created unix time - - return statement; - } - - @Override - public PreparedStatement createUpdateStatement(BeaconBeamDTO dto) throws SQLException - { - String sql = - "UPDATE "+this.getTableName()+" \n" + - "SET \n" + - " ColorR = ?, ColorG = ?, ColorB = ?, \n" + - " LastModifiedUnixDateTime = ? \n" + - "WHERE BlockPosX = ? AND BlockPosY = ? AND BlockPosZ = ?"; - PreparedStatement statement = this.createPreparedStatement(sql); - - int i = 1; - statement.setObject(i++, dto.color.getRed()); - statement.setObject(i++, dto.color.getGreen()); - statement.setObject(i++, dto.color.getBlue()); - - statement.setObject(i++, System.currentTimeMillis()); // last modified unix time - - statement.setObject(i++, dto.pos.x); - statement.setObject(i++, dto.pos.y); - statement.setObject(i++, dto.pos.z); - - return statement; - } - - - - //====================// - // additional methods // - //====================// - - public List getAllBeamsForPos(DhChunkPos chunkPos) - { - int minBlockX = chunkPos.getMinBlockX(); - int minBlockZ = chunkPos.getMinBlockZ(); - int maxBlockX = minBlockX + LodUtil.CHUNK_WIDTH; - int maxBlockZ = minBlockZ + LodUtil.CHUNK_WIDTH; - - return this.getAllBeamsInBlockPosRange( - minBlockX, minBlockZ, - maxBlockX, maxBlockZ - ); - } - - public List getAllBeamsForPos(long pos) - { - int minBlockX = DhSectionPos.getMinCornerBlockX(pos); - int minBlockZ = DhSectionPos.getMinCornerBlockZ(pos); - int maxBlockX = minBlockX + DhSectionPos.getBlockWidth(pos); - int maxBlockZ = minBlockZ + DhSectionPos.getBlockWidth(pos); - - return this.getAllBeamsInBlockPosRange( - minBlockX, minBlockZ, - maxBlockX, maxBlockZ - ); - } - - public List getAllBeamsInBlockPosRange( - int minBlockX, int minBlockZ, - int maxBlockX, int maxBlockZ - ) - { - List> objectMapList = this.queryDictionary( - "SELECT * " + - "FROM "+this.getTableName()+" " + - "WHERE " + - minBlockX+" <= BlockPosX AND BlockPosX <= "+maxBlockX+" AND " + - minBlockZ+" <= BlockPosZ AND BlockPosZ <= "+maxBlockZ); - - ArrayList beamList = new ArrayList<>(); - for (Map objectMap : objectMapList) - { - beamList.add(this.convertDictionaryToDto(objectMap)); - } - - return beamList; - } - - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/ColorUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/ColorUtil.java index 6e4b3acc9..222fcbb9b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/ColorUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/ColorUtil.java @@ -19,8 +19,6 @@ package com.seibel.distanthorizons.core.util; -import java.awt.*; - /** * Handles the bit-wise math used when * dealing with colors stored as integers. @@ -214,7 +212,4 @@ public class ColorUtil ",B:" + getBlue(color); } - public static Color toColorObjRGB(int color) { return new Color(getRed(color), getGreen(color), getBlue(color)); } - public static Color toColorObjRGBA(int color) { return new Color(getRed(color), getGreen(color), getBlue(color), getAlpha(color)); } - } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/LodUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/LodUtil.java index ad9de0b90..7b43cf6a8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/LodUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/LodUtil.java @@ -19,9 +19,7 @@ package com.seibel.distanthorizons.core.util; -import java.util.Arrays; import java.util.Iterator; -import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletionException; import java.util.concurrent.RejectedExecutionException; @@ -35,7 +33,6 @@ import com.seibel.distanthorizons.core.render.vertexFormat.DefaultLodVertexForma import com.seibel.distanthorizons.core.render.vertexFormat.LodVertexFormat; import com.seibel.distanthorizons.core.util.gridList.EdgeDistanceBooleanGrid; import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException; -import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWrapper; @@ -69,6 +66,9 @@ public class LodUtil + /** The maximum number of LODs that can be rendered vertically */ + public static final int MAX_NUMBER_OF_VERTICAL_LODS = 32; + /** * alpha used when drawing chunks in debug mode */ @@ -123,23 +123,6 @@ public class LodUtil /** lowest possible light level handled by Minecraft */ public static final byte MIN_MC_LIGHT = 0; - /** the opacity value returned by {@link IBlockStateWrapper#getOpacity()} if a block is fully transparent */ - public static final int BLOCK_FULLY_TRANSPARENT = 0; - /** the opacity value returned by {@link IBlockStateWrapper#getOpacity()} if a block is fully opaque */ - public static final int BLOCK_FULLY_OPAQUE = 16; - - /** - * List of every block that can be used in a beacon's base.
- * Should be all lowercase - */ - public static final List BEACON_BASE_BLOCK_NAME_LIST = Arrays.asList( - "iron_block", - "gold_block", - "diamond_block", - "emerald_block", - "netherite_block" - ); - /** @@ -253,15 +236,6 @@ public class LodUtil renderDist * 2 + 1); } - /** Returns the chunk int position for the given double position */ - public static int getChunkPosFromDouble(double value) { return (int) Math.floor(value / CHUNK_WIDTH); } - /** Returns the float position inside the chunk for the given double position */ - public static float getSubChunkPosFromDouble(double value) - { - double chunkPos = Math.floor(value / CHUNK_WIDTH); - return (float) (value - chunkPos * CHUNK_WIDTH); - } - // True if the requested threshold pass, or false otherwise // For details, see: diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/RenderDataPointUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/RenderDataPointUtil.java index 757aec211..72bb1864f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/RenderDataPointUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/RenderDataPointUtil.java @@ -245,8 +245,8 @@ public class RenderDataPointUtil " Y-:" + getYMin(dataPoint) + " argb:" + getAlpha(dataPoint) + " " + getRed(dataPoint) + " " + - getGreen(dataPoint) + " " + - getBlue(dataPoint) + + getBlue(dataPoint) + " " + + getGreen(dataPoint) + " BL:" + getLightBlock(dataPoint) + " SL:" + getLightSky(dataPoint) + " BID:" + getBlockMaterialId(dataPoint); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/RenderUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/RenderUtil.java index 4888f5673..e1d6c7ed3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/RenderUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/RenderUtil.java @@ -30,8 +30,8 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftCli import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.coreapi.util.MathUtil; -import com.seibel.distanthorizons.core.util.math.Mat4f; -import com.seibel.distanthorizons.core.util.math.Vec3f; +import com.seibel.distanthorizons.coreapi.util.math.Mat4f; +import com.seibel.distanthorizons.coreapi.util.math.Vec3f; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; /** @@ -244,7 +244,7 @@ public class RenderUtil // if the player is a significant distance above the work, increase the // near clip plane to fix Z imprecision issues int playerHeight = MC.getPlayerBlockPos().y; - int levelMaxHeight = level.getMaxHeight(); + int levelMaxHeight = level.getHeight(); if (playerHeight > levelMaxHeight + 1_000) { return playerHeight - (levelMaxHeight + 1000); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/math/Mat4f.java b/core/src/main/java/com/seibel/distanthorizons/core/util/math/Mat4f.java deleted file mode 100644 index 27c8bbb97..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/math/Mat4f.java +++ /dev/null @@ -1,249 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.util.math; - -import com.seibel.distanthorizons.api.objects.math.DhApiMat4f; -import org.joml.Matrix4f; -import org.joml.Matrix4fc; - -import java.nio.FloatBuffer; - -/** - * An (almost) exact copy of Minecraft's 1.16.5 - * implementation of a 4x4 float matrix. - * - * @author James Seibel - * @version 11-11-2021 - */ -public class Mat4f extends DhApiMat4f -{ - - //==============// - // constructors // - //==============// - - public Mat4f() { /* all values are 0 */ } - - public Mat4f(DhApiMat4f sourceMatrix) { super(sourceMatrix); } - - /** Expects the values of the input buffer to be in row major order (AKA rows then columns) */ - public Mat4f(FloatBuffer buffer) { this(buffer.array()); } - /** Expects the values of the input array to be in row major order (AKA rows then columns) */ - public Mat4f(float[] values) { super(values); } - - public Mat4f(Matrix4fc sourceMatrix) { this(convertJomlMatrixToArray(sourceMatrix)); } - private static float[] convertJomlMatrixToArray(Matrix4fc sourceMatrix) - { - FloatBuffer buffer = FloatBuffer.allocate(16); - - buffer.put(bufferIndex(0, 0), sourceMatrix.m00()); - buffer.put(bufferIndex(0, 1), sourceMatrix.m01()); - buffer.put(bufferIndex(0, 2), sourceMatrix.m02()); - buffer.put(bufferIndex(0, 3), sourceMatrix.m03()); - - buffer.put(bufferIndex(1, 0), sourceMatrix.m10()); - buffer.put(bufferIndex(1, 1), sourceMatrix.m11()); - buffer.put(bufferIndex(1, 2), sourceMatrix.m12()); - buffer.put(bufferIndex(1, 3), sourceMatrix.m13()); - - buffer.put(bufferIndex(2, 0), sourceMatrix.m20()); - buffer.put(bufferIndex(2, 1), sourceMatrix.m21()); - buffer.put(bufferIndex(2, 2), sourceMatrix.m22()); - buffer.put(bufferIndex(2, 3), sourceMatrix.m23()); - - buffer.put(bufferIndex(3, 0), sourceMatrix.m30()); - buffer.put(bufferIndex(3, 1), sourceMatrix.m31()); - buffer.put(bufferIndex(3, 2), sourceMatrix.m32()); - buffer.put(bufferIndex(3, 3), sourceMatrix.m33()); - - return buffer.array(); - } - private static int bufferIndex(int xIndex, int zIndex) { return (zIndex * 4) + xIndex; } - - - public void store(FloatBuffer floatBuffer) - { - floatBuffer.put(bufferIndex(0, 0), this.m00); - floatBuffer.put(bufferIndex(0, 1), this.m01); - floatBuffer.put(bufferIndex(0, 2), this.m02); - floatBuffer.put(bufferIndex(0, 3), this.m03); - floatBuffer.put(bufferIndex(1, 0), this.m10); - floatBuffer.put(bufferIndex(1, 1), this.m11); - floatBuffer.put(bufferIndex(1, 2), this.m12); - floatBuffer.put(bufferIndex(1, 3), this.m13); - floatBuffer.put(bufferIndex(2, 0), this.m20); - floatBuffer.put(bufferIndex(2, 1), this.m21); - floatBuffer.put(bufferIndex(2, 2), this.m22); - floatBuffer.put(bufferIndex(2, 3), this.m23); - floatBuffer.put(bufferIndex(3, 0), this.m30); - floatBuffer.put(bufferIndex(3, 1), this.m31); - floatBuffer.put(bufferIndex(3, 2), this.m32); - floatBuffer.put(bufferIndex(3, 3), this.m33); - } - - - public static Matrix4f createJomlMatrix(DhApiMat4f matrix) - { - return new Matrix4f( - matrix.m00, matrix.m10, matrix.m20, matrix.m30, - matrix.m01, matrix.m11, matrix.m21, matrix.m31, - matrix.m02, matrix.m12, matrix.m22, matrix.m32, - matrix.m03, matrix.m13, matrix.m23, matrix.m33 - ); - } - public Matrix4f createJomlMatrix() - { - return new Matrix4f( - this.m00, this.m10, this.m20, this.m30, - this.m01, this.m11, this.m21, this.m31, - this.m02, this.m12, this.m22, this.m32, - this.m03, this.m13, this.m23, this.m33 - ); - } - - - - //=========// - // methods // - //=========// - - public static Mat4f perspective(double fov, float widthHeightRatio, float nearClipPlane, float farClipPlane) - { - float f = (float) (1.0D / Math.tan(fov * ((float) Math.PI / 180F) / 2.0D)); - Mat4f matrix = new Mat4f(); - matrix.m00 = f / widthHeightRatio; - matrix.m11 = f; - matrix.m22 = (farClipPlane + nearClipPlane) / (nearClipPlane - farClipPlane); - matrix.m32 = -1.0F; - matrix.m23 = 2.0F * farClipPlane * nearClipPlane / (nearClipPlane - farClipPlane); - return matrix; - } - - /** originally "translate" from Minecraft's MatrixStack */ - public void multiplyTranslationMatrix(double x, double y, double z) - { multiply(createTranslateMatrix((float) x, (float) y, (float) z)); } - - public static Mat4f createScaleMatrix(float x, float y, float z) - { - Mat4f matrix = new Mat4f(); - matrix.m00 = x; - matrix.m11 = y; - matrix.m22 = z; - matrix.m33 = 1.0F; - return matrix; - } - - public static Mat4f createTranslateMatrix(float x, float y, float z) - { - Mat4f matrix = new Mat4f(); - matrix.m00 = 1.0F; - matrix.m11 = 1.0F; - matrix.m22 = 1.0F; - matrix.m33 = 1.0F; - matrix.m03 = x; - matrix.m13 = y; - matrix.m23 = z; - return matrix; - } - - - - //===============// - // Forge methods // - //===============// - - public void set(DhApiMat4f mat) - { - this.m00 = mat.m00; - this.m01 = mat.m01; - this.m02 = mat.m02; - this.m03 = mat.m03; - this.m10 = mat.m10; - this.m11 = mat.m11; - this.m12 = mat.m12; - this.m13 = mat.m13; - this.m20 = mat.m20; - this.m21 = mat.m21; - this.m22 = mat.m22; - this.m23 = mat.m23; - this.m30 = mat.m30; - this.m31 = mat.m31; - this.m32 = mat.m32; - this.m33 = mat.m33; - } - - public void add(DhApiMat4f other) - { - m00 += other.m00; - m01 += other.m01; - m02 += other.m02; - m03 += other.m03; - m10 += other.m10; - m11 += other.m11; - m12 += other.m12; - m13 += other.m13; - m20 += other.m20; - m21 += other.m21; - m22 += other.m22; - m23 += other.m23; - m30 += other.m30; - m31 += other.m31; - m32 += other.m32; - m33 += other.m33; - } - - public void multiplyBackward(DhApiMat4f other) - { - DhApiMat4f copy = other.copy(); - copy.multiply(this); - this.set(copy); - } - - public void setTranslation(float x, float y, float z) - { - this.m00 = 1.0F; - this.m11 = 1.0F; - this.m22 = 1.0F; - this.m33 = 1.0F; - this.m03 = x; - this.m13 = y; - this.m23 = z; - } - - /** - * Changes the values that store the clipping planes. - * Formula for calculating matrix values is the same that OpenGL uses when making matrices. - * - * @param nearClip New near clipping plane value. - * @param farClip New far clipping plane value. - */ - public void setClipPlanes(float nearClip, float farClip) - { - //convert to matrix values, formula copied from a textbook / openGL specification. - float matNearClip = -((farClip + nearClip) / (farClip - nearClip)); - float matFarClip = -((2 * farClip * nearClip) / (farClip - nearClip)); - //set new values for the clip planes. - this.m22 = matNearClip; - this.m23 = matFarClip; - } - - public Mat4f copy() { return new Mat4f(this); } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/math/Vec3f.java b/core/src/main/java/com/seibel/distanthorizons/core/util/math/Vec3f.java deleted file mode 100644 index 11f4b8f46..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/math/Vec3f.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.util.math; - -import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; -import com.seibel.distanthorizons.core.pos.DhBlockPos2D; -import com.seibel.distanthorizons.coreapi.util.MathUtil; - -/** - * A (almost) exact copy of Minecraft's 1.16.5 - * implementation of a 3 element float vector. - * - * @author James Seibel - * @version 11-11-2021 - */ -public class Vec3f extends DhApiVec3f -{ - //==============// - // constructors // - //==============// - - public Vec3f() { this(0,0,0); } - - public Vec3f(float x, float y, float z) - { - this.x = x; - this.y = y; - this.z = z; - } - - public Vec3f(DhApiVec3f pos) - { - this.x = pos.x; - this.y = pos.y; - this.z = pos.z; - } - - public Vec3f(Vec3d pos) - { - this.x = (float) pos.x; - this.y = (float) pos.y; - this.z = (float) pos.z; - } - - - - - //==============// - // math methods // - //==============// - - public void mul(float scalar) - { - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; - } - - public void mul(float x, float y, float z) - { - this.x *= x; - this.y *= y; - this.z *= z; - } - - public void clamp(float min, float max) - { - this.x = MathUtil.clamp(min, this.x, max); - this.y = MathUtil.clamp(min, this.y, max); - this.z = MathUtil.clamp(min, this.z, max); - } - - public void add(float x, float y, float z) - { - this.x += x; - this.y += y; - this.z += z; - } - - public void add(Vec3f vector) - { - this.x += vector.x; - this.y += vector.y; - this.z += vector.z; - } - - public void subtract(Vec3f vector) - { - this.x -= vector.x; - this.y -= vector.y; - this.z -= vector.z; - } - - public float dotProduct(Vec3f vector) { return this.x * vector.x + this.y * vector.y + this.z * vector.z; } - - /** @return true if normalization had to be done */ - public boolean normalize() - { - float squaredSum = this.x * this.x + this.y * this.y + this.z * this.z; - if (squaredSum < 1.0E-5D) - { - return false; - } - else - { - float f1 = MathUtil.fastInvSqrt(squaredSum); - this.x *= f1; - this.y *= f1; - this.z *= f1; - return true; - } - } - - public void crossProduct(Vec3f vector) - { - float f = this.x; - float f1 = this.y; - float f2 = this.z; - float f3 = vector.x; - float f4 = vector.y; - float f5 = vector.z; - this.x = f1 * f5 - f2 * f4; - this.y = f2 * f3 - f * f5; - this.z = f * f4 - f1 * f3; - } - - public static float getManhattanDistance(DhApiVec3f a, DhApiVec3f b) - { - return Math.abs(a.x - b.x) - + Math.abs(a.y - b.y) - + Math.abs(a.z - b.z); - } - - public static double getDistance(DhApiVec3f a, DhApiVec3f b) - { - return Math.sqrt(Math.pow(a.x - b.x, 2) - + Math.pow(a.y - b.y, 2) - + Math.pow(a.z - b.z, 2)); - } - - - - //==============// - // misc methods // - //==============// - - public void set(float x, float y, float z) - { - this.x = x; - this.y = y; - this.z = z; - } - - public Vec3f copy() { return new Vec3f(this.x, this.y, this.z); } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhApiWorldProxy.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhApiWorldProxy.java index 477dae008..88893d9ac 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhApiWorldProxy.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhApiWorldProxy.java @@ -50,18 +50,10 @@ public class DhApiWorldProxy implements IDhApiWorldProxy - //=============// - // constructor // - //=============// - private DhApiWorldProxy() { } - //=========// - // methods // - //=========// - @Override public boolean worldLoaded() { return SharedApi.getAbstractDhWorld() != null; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java index 0fdaf3c7a..1471cfabb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java @@ -98,7 +98,6 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor } level.startRenderer(clientLevelWrapper); - clientLevelWrapper.setParentLevel(level); return level; }); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java index 427310ece..139949cc4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java @@ -82,17 +82,6 @@ public interface IWrapperFactory extends IDhApiWrapperFactory, IBindable * Generally this contains blocks like: air, barriers, light blocks, etc. */ HashSet getRendererIgnoredBlocks(ILevelWrapper levelWrapper); - /** - * Returns the set of {@link IBlockStateWrapper}'s that shouldn't be rendered in caves.
- * Generally this contains blocks like: air, rails, glow lichen, etc. - */ - HashSet getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper); - - /** clears the cached values */ - void resetRendererIgnoredCaveBlocks(); - /** clears the cached values */ - void resetRendererIgnoredBlocksSet(); - /** * Specifically designed to be used with the API. diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java index 36ad21767..8f8b11ce2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java @@ -20,13 +20,44 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.block; import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper; -import com.seibel.distanthorizons.core.util.LodUtil; - -import java.awt.*; /** A Minecraft version independent way of handling Blocks. */ public interface IBlockStateWrapper extends IDhApiBlockStateWrapper { + //===========// + // constants // + //===========// + + int FULLY_TRANSPARENT = 0; + int FULLY_OPAQUE = 16; + + /** contains the indices used by Iris to determine how different block types should be rendered */ + class IrisBlockMaterial + { + public static final byte UNKOWN = 0; + public static final byte LEAVES = 1; + public static final byte STONE = 2; + public static final byte WOOD = 3; + public static final byte METAL = 4; + public static final byte DIRT = 5; + public static final byte LAVA = 6; + public static final byte DEEPSLATE = 7; + public static final byte SNOW = 8; + public static final byte SAND = 9; + public static final byte TERRACOTTA = 10; + public static final byte NETHER_STONE = 11; + public static final byte WATER = 12; + public static final byte GRASS = 13; + + /** shouldn't normally be needed, but just in case */ + public static final byte AIR = 14; + public static final byte ILLUMINATED = 15; // Max value + } + + + + + //=========// // methods // //=========// @@ -37,19 +68,13 @@ public interface IBlockStateWrapper extends IDhApiBlockStateWrapper * Returning a value of 0 means the block is completely transparent. getAllActiveBeacons(ArrayList neighbourChunkList) - { - ArrayList beaconBeamList = new ArrayList<>(); - - AdjacentChunkHolder adjacentChunkHolder = new AdjacentChunkHolder(this, neighbourChunkList); - - // since beacons emit light we can check only the positions that are emitting light - ArrayList blockPosList = this.getBlockLightPosList(); - for (int i = 0; i < blockPosList.size(); i++) - { - DhBlockPos pos = blockPosList.get(i); - DhBlockPos relPos = pos.convertToChunkRelativePos(); - - - IBlockStateWrapper block = this.getBlockState(relPos); - if (block.isBeaconBlock()) - { - Color beaconColor = getBeaconColor(pos, adjacentChunkHolder); - if (beaconColor != null) - { - BeaconBeamDTO beam = new BeaconBeamDTO(blockPosList.get(i), beaconColor); - beaconBeamList.add(beam); - } - } - } - - return beaconBeamList; - } - /** @return Null if the position isn't valid for a beacon beam. */ - @Nullable - static Color getBeaconColor(DhBlockPos beaconPos, AdjacentChunkHolder chunkHolder) - { - DhBlockPos beaconRelPos = beaconPos.convertToChunkRelativePos(); - DhBlockPos baseRelPos = new DhBlockPos(0, beaconRelPos.y-1, 0); - - - - //===========================// - // check for the base blocks // - //===========================// - - for (int x = -1; x<= 1; x++) - { - for (int z = -1; z <= 1; z++) - { - baseRelPos.x = beaconRelPos.x + x; - baseRelPos.z = beaconRelPos.z + z; - baseRelPos.mutateToChunkRelativePos(baseRelPos); - - IChunkWrapper chunk = chunkHolder.getByBlockPos(beaconPos.x + x, beaconPos.z + z); - if (chunk != null) - { - IBlockStateWrapper block = chunk.getBlockState(baseRelPos.x, baseRelPos.y, baseRelPos.z); - if (!block.isBeaconBaseBlock()) - { - return null; - } - } - } - } - - - - //=========================// - // get the beacon color // - // and check for occlusion // - //=========================// - - int red = 0; - int green = 0; - int blue = 0; - boolean glassBlockFound = false; - - IChunkWrapper centerChunk = chunkHolder.getByBlockPos(beaconPos.x, beaconPos.z); - int maxY = centerChunk.getMaxNonEmptyHeight(); - for (int y = beaconRelPos.y+1; y <= maxY; y++) - { - IBlockStateWrapper block = centerChunk.getBlockState(beaconRelPos.x, y, beaconRelPos.z); - if (!block.isAir() && block.getOpacity() == LodUtil.BLOCK_FULLY_OPAQUE) - { - return null; - } - - if (block.isGlassBlock() - // ignore invisible blocks (which have pure black as their map color, luckily black stained-glass is actually extremely dark gray) - && !block.getMapColor().equals(Color.BLACK)) - { - red += block.getMapColor().getRed(); - green += block.getMapColor().getGreen(); - blue += block.getMapColor().getBlue(); - - if (glassBlockFound) - { - red /= 2; - green /= 2; - blue /= 2; - } - glassBlockFound = true; - } - } - - return glassBlockFound ? new Color(red, green, blue) : Color.WHITE; - } - } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftClientWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftClientWrapper.java index a4190044d..bedacffc2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftClientWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftClientWrapper.java @@ -62,8 +62,6 @@ public interface IMinecraftClientWrapper extends IBindable boolean hasSinglePlayerServer(); boolean clientConnectedToDedicatedServer(); - /** for use with the Replay mod */ - boolean connectedToReplay(); String getCurrentServerName(); String getCurrentServerIp(); @@ -92,6 +90,10 @@ public interface IMinecraftClientWrapper extends IBindable */ IClientLevelWrapper getWrappedClientLevel(boolean bypassMultiverse); + /** Please move over to getInstallationDirectory() within the IMinecraftSharedWrapper */ + @Deprecated + File getGameDirectory(); + IProfilerWrapper getProfiler(); /** Returns all worlds available to the server */ diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftRenderWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftRenderWrapper.java index 2fc860772..42d355520 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftRenderWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftRenderWrapper.java @@ -28,9 +28,9 @@ import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable; -import com.seibel.distanthorizons.core.util.math.Mat4f; -import com.seibel.distanthorizons.core.util.math.Vec3d; -import com.seibel.distanthorizons.core.util.math.Vec3f; +import com.seibel.distanthorizons.coreapi.util.math.Mat4f; +import com.seibel.distanthorizons.coreapi.util.math.Vec3d; +import com.seibel.distanthorizons.coreapi.util.math.Vec3f; import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants; import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java index 4d2dd13cf..f33d53354 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java @@ -21,7 +21,7 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.misc; import com.seibel.distanthorizons.api.interfaces.IDhApiUnsafeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; -import com.seibel.distanthorizons.core.util.math.Vec3d; +import com.seibel.distanthorizons.coreapi.util.math.Vec3d; import java.net.SocketAddress; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IClientLevelWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IClientLevelWrapper.java index c22c4a413..7cdd14bbf 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IClientLevelWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IClientLevelWrapper.java @@ -19,8 +19,6 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.world; -import com.seibel.distanthorizons.core.level.IDhClientLevel; -import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import org.jetbrains.annotations.Nullable; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java index aed5804a6..d9cea7b95 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java @@ -20,7 +20,6 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.world; import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper; -import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; @@ -48,7 +47,8 @@ public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable boolean hasSkyLight(); @Override - int getMaxHeight(); + int getHeight(); + @Override default int getMinHeight() { return 0; } @@ -65,8 +65,4 @@ public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable /** Fired when the level is being unloaded. Doesn't unload the level. */ void onUnload(); - // TODO I don't like the circular reference, can we merge the level wrapper and DhLevels? - @Deprecated - void setParentLevel(IDhLevel parentLevel); - } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java index a9a762709..b4ae28dc8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java @@ -26,6 +26,9 @@ import static com.seibel.distanthorizons.core.config.Config.Client.Advanced.Mult public interface IServerLevelWrapper extends ILevelWrapper { + @Nullable + IClientLevelWrapper tryGetClientLevelWrapper(); + File getSaveFolder(); default String getKeyedLevelDimensionName() diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index 31598ea31..83b75513a 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -89,7 +89,7 @@ "distanthorizons.config.client.advanced.graphics.quality.maxHorizontalResolution.@tooltip": "The maximum detail LODs are rendered at.\n\n§6Fastest:§r Chunk\n§6Fanciest:§r Block", "distanthorizons.config.client.advanced.graphics.quality.lodChunkRenderDistanceRadius": - "LOD Chunk Render Distance Radius", + "LOD Render Distance Radius", "distanthorizons.config.client.advanced.graphics.quality.lodChunkRenderDistanceRadius.@tooltip": "Distant Horizons' render distance, measured in chunks. \n\nNote: this is a best effort number. \nThe render distance may be above or below this number \ndepending on your other graphic settings.", "distanthorizons.config.client.advanced.graphics.quality.verticalQuality": @@ -309,23 +309,6 @@ "Grass Side Rendering", "distanthorizons.config.client.advanced.graphics.advancedGraphics.grassSideRendering.@tooltip": "How should the sides and bottom of grass block LODs render?", - - - "distanthorizons.config.client.advanced.graphics.genericRendering": - "Generic Rendering", - - "distanthorizons.config.client.advanced.graphics.genericRendering.enableRendering": - "Enable Rendering", - "distanthorizons.config.client.advanced.graphics.genericRendering.enableRendering.@tooltip": - "If true non terrain objects will be rendered in DH's terrain. \nThis includes beacon beams and clouds.", - "distanthorizons.config.client.advanced.graphics.genericRendering.enableBeaconRendering": - "Enable Beacon Rendering", - "distanthorizons.config.client.advanced.graphics.genericRendering.enableBeaconRendering.@tooltip": - "If true LOD beacon beams will be rendered.", - "distanthorizons.config.client.advanced.graphics.genericRendering.enableCloudRendering": - "Enable Cloud Rendering", - "distanthorizons.config.client.advanced.graphics.genericRendering.enableCloudRendering.@tooltip": - "If true LOD clouds will be rendered.", "distanthorizons.config.client.advanced.worldGenerator": @@ -365,10 +348,6 @@ "Only Use DH Lighting Engine", "distanthorizons.config.client.advanced.lodBuilding.onlyUseDhLightingEngine.@tooltip": "If false LODs will be lit by Minecraft's lighting engine when possible \nand fall back to the DH lighting engine only when necessary. \n\nIf true LODs will only be lit using Distant Horizons' lighting engine. \n\nGenerally it is best to leave this disabled and should only be enabled \nif there are lighting issues or for debugging.", - "distanthorizons.config.client.advanced.lodBuilding.pullLightingForPregeneratedChunks": - "Pull Lighting For Pre-generated Chunks", - "distanthorizons.config.client.advanced.lodBuilding.pullLightingForPregeneratedChunks.@tooltip": - "If true LOD generation for pre-existing chunks will attempt to pull the lighting data \nsaved in Minecraft's Region files. \nIf false DH will pull in chunks without lighting and re-light them. \n\nSetting this to true will result in faster LOD generation \nfor already generated worlds, but is broken by most lighting mods. \n\nSet this to false if LODs are black.", "distanthorizons.config.client.advanced.lodBuilding.dataCompression": "Data Compression", "distanthorizons.config.client.advanced.lodBuilding.dataCompression.@tooltip": @@ -379,14 +358,7 @@ "How should block data be compressed when creating LOD data? \nThis setting will only affect new or updated LOD data, \nany data already generated when this setting is changed will be \nunaffected until it is modified or re-loaded. \n\nMost Accurate: Merge Same Blocks \nHighest Compression: Visually Equal", "distanthorizons.config.client.advanced.lodBuilding.showMigrationChatWarning": "Log Migration In Chat", - "distanthorizons.config.client.advanced.lodBuilding.ignoredRenderBlockCsv": - "Ignored Block CSV", - "distanthorizons.config.client.advanced.lodBuilding.ignoredRenderBlockCsv.@tooltip": - "A comma separated list of block resource locations that won't be rendered by DH. \nNote: air is always included in this list.", - "distanthorizons.config.client.advanced.lodBuilding.ignoredRenderCaveBlockCsv": - "Ignored Cave Block CSV", - "distanthorizons.config.client.advanced.lodBuilding.ignoredRenderCaveBlockCsv.@tooltip": - "A comma separated list of block resource locations that shouldn't be rendered \nif they are in a 0 sky light underground area. \nNote: air is always included in this list.", + "distanthorizons.config.client.advanced.multiplayer": "Multiplayer", @@ -420,7 +392,7 @@ "distanthorizons.config.client.advanced.multiplayer.serverFolderNameMode": "Server Folder Mode", "distanthorizons.config.client.advanced.multiplayer.serverFolderNameMode.@tooltip": - "Determines the folder format for local multiplayer data.\n\n§6Name Only:§r\nUses the server browser name. Ex: \"Minecraft Server\"\n§6IP Only:§r\n\"192.168.1.40\"\n§6Name IP:§r\n\"Minecraft Server, IP 192.168.1.40\"\n§6Name, IP, Port:§r\n\"Minecraft Server, IP 192.168.1.40:25565\"\n§6Name, IP, Port, MC Version:§r\n\"Minecraft Server, IP 192.168.1.40:25565, GameVersion 1.18.1\"\n\n§c§lCaution:§r changing while connected to a multiplayer server may cause glitches.", + "Determines the folder format for local multiplayer data.\n\n§6Name Only:§r\nUses the server browser name. Ex: \"Minecraft Server\"\n§6Name IP:§r\n\"Minecraft Server, IP 192.168.1.40\"\n§6Name, IP, Port:§r\n\"Minecraft Server, IP 192.168.1.40:25565\"\n§6Name, IP, Port, MC Version:§r\n\"Minecraft Server, IP 192.168.1.40:25565, GameVersion 1.18.1\"\n\n§c§lCaution:§r changing while connected to a multiplayer server may cause glitches.", "distanthorizons.config.client.advanced.multiplayer.multiverseSimilarityRequiredPercent": "Multiverse Required Similarity %", "distanthorizons.config.client.advanced.multiplayer.multiverseSimilarityRequiredPercent.@tooltip": @@ -502,11 +474,7 @@ "distanthorizons.config.client.advanced.debugging.showOverlappingQuadErrors": "Show overlapping quad errors", "distanthorizons.config.client.advanced.debugging.showOverlappingQuadErrors.@tooltip": - "If true overlapping quads will be rendered as bright red for easy identification. \nIf false the quads will be rendered normally.", - "distanthorizons.config.client.advanced.debugging.logBufferGarbageCollection": - "Log Buffer Garbage Collection", - "distanthorizons.config.client.advanced.debugging.logBufferGarbageCollection.@tooltip": - "If true OpenGL Buffer garbage collection will be logged \nthis also includes the number of live buffers. ", + "f true overlapping quads will be rendered as bright red for easy identification. \nIf false the quads will be rendered normally.", "distanthorizons.config.client.advanced.debugging.allowUnsafeValues": "Allow Unsafe UI Values", "distanthorizons.config.client.advanced.debugging.allowUnsafeValues.@tooltip": @@ -579,8 +547,6 @@ "Network Events", "distanthorizons.config.client.advanced.logging.showLowMemoryWarningOnStartup": "Show Low Memory Warning", - "distanthorizons.config.client.advanced.logging.showReplayWarningOnStartup": - "Show Replay Warning", diff --git a/core/src/main/resources/assets/distanthorizons/textures/clouds - original.png b/core/src/main/resources/assets/distanthorizons/textures/clouds - original.png deleted file mode 100644 index 825f4814e3fab7899db0e991087484a908873d5f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6214 zcmV-M7`f+(P)Px#1ZP1_K>z@;j|==^1poj522e~?MF0Q*|NsA`*`M72000SaNLh0L01m?j~gZYoErBeyQB=cj_p-0I=rLVQ)}o zX;6sM?D;ZSxmyPL8$XLp<>oVCn8;hlxgU}{YnO7-aJ30Pm&!L>086(&v>*ZaTTf2i z1WW_;x2ojbp#vBqFaZ7H{i6U}4m=>X-|>M`YbH}SX^ zabDL|LB5VokLw92$f8Yu10&lKPTR-LAJj=I00NoF6#!)IU|mX`zAnTf0H&Kojqi^P zo~64l0jJ+={zqwg%;;kjr0 zVm@IVRo_rz8+n0MzzP6+b9E?iL@_)fLSMSVNQv-^{c7~E7)f;mr?C5KvQJiiX5RYE4Y~7NwGfkD z(=8-W%v*mU3Gw_}0B7Qe*RU(*{3vO09*%yfMrp#ojrS1~Y0S8UF4vLSodmr5JhwU3 zF*K1lbNlN@dkQ-`DsVU<(}KX5&sESk{~E573K^_MCAx9^AR~8`e%2WIMwt*Y`35&^ zqy`|(3#UynNOYkJRjxixl7=CqyJ^WMDyj~EluN{A9h^5u_^g=-L0iiY^2COhD7GX; zg^xdfQvy_7d+keHmsnnMa*hk z08L&@b&LxDH(Up>5OD8o+!|R?>TZ7ZII|Zq3iC&rrZ_CNU-$H@UnnI8%W(GL-D(&b z`^7osCJEP$=|r+#YNB7SL+Zn{z$NXjtTk7f>{pI#v8HZGE%L7cafSjfA!Pp0=Oaxm z_0~J-@UYgMv`9WzG7ayF^glC?)&ZyiKrm10bi<*G39oZvFxB_c3H1pLFKz` z+oh9&vW{PxNJm}fdBHbk5I;1>1NDL(fs{Oz#cBG()m|f?OlDFw4r)=x0l33b2Y(>Y zU_%B#$fqbPW#zLH7$(QlB!b21+fRonRsd=j!u`CF*eNH6&c%b~IwZSx+o*OaB1i&P zybiJjM71#=P4u!x6vXr=+PogxCYDHbvd50KU9L1SR+)fSd}?X@0bnJ7XdUvl(bHTe zor>a5_DJT0)m7R5XH1SljMhOg*NgH@QHNNn6lyC=xK#}zYY^*8HSWa(N>)w@X!E-M zA**Aem)lbp8Y@^AvYN3&oa3by<@Y&9iJ*_VJbKoWY)m0Y;vWa;!ZFCt&$1)KZXSRO zca4Z+XthwXkIOHEA8T0^z!aweSY0%8r7wvUu&(AnM0TrK>lQ{MxHNq3~1wP0KZG0G(Ir%h1M>Nyfv_PIJ|b5_eFwQtyTbSk_G!s04nwx z3zHEV(JW=5bUU(#YJ!mx+O+9zQL4K=);kOr+&%#mK0yd7h|)KYjBAHM=F2i2NZoKD zSJ(isZ{zh~z&`O?+QWGidfDVyiV)TXDS*XF)?706D(00O18i)u<4HJMdUf)Y_k9_)9QKcxVHV!ybkmz3gMQfI_ZouVwS6(1TR&KO3*5 zQMvyr0iGMQtks=$eDx;NNW02W+Ljj<is)bzMZqxsuudC`9hv(WF1+8(z0iaH}N9 zHZk_C2CRMetilPHSKwx8YXPv;#?c6K9iVkqbz}w)r0&LO5vuX0`e7}y-0Q|u0?-*m z_))>`SyAms*l!`q3IO>Q@N&4&FRDQRbe=9lb&f@kDpuTBx$S%~AbuHsO zcTF`w0kE54No`Lm8kaPufL<3+NNZV61hcWf+oqhXjT$27d28AsT#9Qg6x6-+?3OQ^ zkB5HpeiTO%7BhpKAU)noxTu-{rrZ?P{hd=M!OKLIyeuj5iJG!^7~e~7FLy#MZa}Lj z$34e^UBc|!{uPq*w%q=4x6)xVgHwB3c2OEaEhDk;WJ-FBn2G(-lAn3vv2GHAOqN{Js{+d8?CCPJa3PK&vm)pN@Q<9 zw{-O+LOb^!CtdoVsjNo;xF7vVxhzNMnCC6VpV(v5Aie<^7d;7#dTQM!S^?A@n)>G>osf?h6%p%3mpmU=)9~h-x6n?Xk5B~Ry|7gee2Fb$qbc%; zlb8>2d8J$3eja}mT+Z$}%WOIW7H|Sf(khkWJZ`senVvLByCntA2QL;E%_e?+tD2O` z$RTL|?uZ}){0qgkVw?ZB*5+8#uUOP3rLK`@$*ATv z(GQti8(JOO^6eNxcg~P)2c(C$AqG7o!JGl$%?Swta!+?Op#ac&yPfK3zGNYH!t`|w zbX8eG#JXb2s8kVMo{_(>kA&vlGWcyiB7sy$Wf2Hm09Xdd5l3!5a|rSRK%rW55#IHN z03h-vaqk}9D7Ca>XLtJOhhy-#KFgjdZ8(kgWSmt(a?3FiDN${KwRXb}GbD9lD#u8u z2i}_z#19#SN|LR4v|&8i@aBFP^0@_N4{BuRcQ5BtJIBCraP9oHDvLjgbvom_1R$Rd zGHIBQMc28fjHJ%fn9mp++yrz63qy?Np>gPkx*$Coht1KQ@FFumdWghJ*i1)`knon! zTEsTslzSBlQss4a5oC!V;>{oEG3Qhn}eSq$Xv7`jEGT8GV zXb|7%g#$hOoKYdv)tfPd#Q*Q7I`Uy1@Hy{AU!*i`2HwXa!re*Q8lpRI&sr^ z<3bEPG$N?c5ghXv4`ryia^OpV(HZQYIRBf!1WVG=93%;g5TfbL<az5f3v!Kgl7-%s9IgXB;hF&<6xS_vHcG7vyvRj_siUH@|rN z6Y@#7v7>qaG$qehNO_uzK=~zs@6yO~JK82iOkNDE)&cuxY@>?!DLq+g!`d@2sM;8j zyf~~i!Mj*S{-h?R$Qq}NKx%#0idMWBGjGpb-%^SV00sae)Wl8!*iM($9;XH9a5Eex zzx@q>7X|F$bPs&;^HGOK99i0Y0pR;wKM|C~4GLB10+da+Q=LW9s5sLf>q`qEa9{ms zY~w+B01e4 z*n4Fvk4ho&ZvFP&2mPWS;iHTlEhwZYz)R9A3uC*}uK?J1+>3o$>4q&+r`mxCsEP8Z zXYz))GY`oWk&s?!fj%V=*;3UxCrH%l?MTUh?$n3mz`_-Q_=22}v|R}C*H%x2a?K=* zD6D4H0-fJ&4Ls18yDw{r6i4A{0`S*9nK{!9K-HllNx2X0Nf~w0$)r>UwO2TyGjAH* z0l->u&ht=@yteR;OCK^R&L@p|J~VV>?+FZ7{7^@@7JG%2F4imOGO>PF$7i-E zYeTQD=p=bL@IqY%-+ZkIR(}1}PEC=Z`d15!u|iq_+GBsu)|~NkOQ};KQCtvf>fgI` z_-!{&;=on01HcPBYh!1yh+wiT*R^s1<(%sEbhzK$s>U~_$in=nA44}uc~R|W4Hi>6>CXa1B`FD@vq{D@gZaV25d0?YTR-T;$t;<+t&vk- zE!i4J^`Csma-Y0}te4?KWfI4)YMF9AGz{EdT@vr4xDaZId0Z3}m$$I=7Er82r_UdA z+6>SMMV5f#%ABwUL*vLMRBNitrhf_tYL}GKHB!pl1kj5;T;||==(C)Dy2C2~WorGE z8jn?&+hW-T>|QLq0)rL%=Q$2*pu_gC?}g{(=>-I8t3J~K+lQ2I>Z#MwY7eqfW<-w$ zg4i4fC;dIFuS&`O@~y);34oOJRN|>aG57&ln`mNT&2THzAyZsCJ+T4c@Mm3(^h@bB zzgvU*`0GcK7t$NJze6W==6skJ1nwj>C&kNq^Hu=*lg~Sqcn2WeYRi{5%y__p&$+Jw zkUehHSBVsGN;Vi&eUhG?YngMu)LdVO7w*vCA(R>%6>);i%k?u0*>#HW2mt$IRnb5L zqVk=S+KfVke#i?fQwRK@0jB+;YwbUJw5m@i7DL{=Xrmz0e#8`Y)un#uYe+i@On#@3u7b(^VJkOQi|r3Euv#R zeIvrnu^O*&h%-@|ML|sG6?LbPVs~T!cGV4@=bdpxL{(wzjd0c?;#nI6+vEv)wQ0Kn zKxpr?%y|Xays$a5`WJeS+P7p_ClR{53P@AHye%Bl@zo?6FS`P`J~0pzHkQHIDnGd! zdm$B4pOn@lwX3Nnxc&<9pq7sheWS7*Im(X-WH*n8HRx>ihYn+nc1tFw9Ctqt0O-fkHuGM_*9p|ON|%hYzAxy;6FatpF2qa$kWX_ z6xof`iasz-2%ei?0q}fr6rl3ncmsN=V)}PWl+!-Bcy@dneS;|{eS{drvv+Nj7pCuL z&?Fe&C4d_MSzWEFXyB}QN9kj8^*IH@*Jl}&_Ch7$Y>&$%iWY-4LbqyX%=-FIByh^8 zY9C5QG)-wOc(*640McLAeJ|Fc#d1Y;Qdt>6OK9$UW9_443HN>doI6SDoc5tqy?gd^ zl7D`$UTXI@0#v%{-D_abIE>uOEV}i_J@JH~o+)?{t#z6}*d7$&L{%jYX8>rZX-h0c zj|)_eGZC=FJAEFDA{vJ3%ByXVOOeJ>Y&oG=$0n)S4FL8pFl+!AO=6s6BDF%R)ybim zl4Yum&kr^L$A({ywOG~C9T7t4BWC+I0I}A*^(Ko$nc(u0)WwIjvV{8Yt7*1n_tHA8 zmrp-7+Vo_`4A6wZ^enfq6PJ)*F7a-7YBYHS%t_OlpRweK@?hBd0&?W964+K;)Vy_> zuO3`yhaM`g$=N}7(#zoDIAXIbo3`||`auHMaJn!CPcf5sxljN}B;^|AD_8mD%{HR^ z#+<|*HS8t7M|>;b>OWJ1P1dybrWHQYf@}I)ByAJO=t;>9a156p83}G?0T*>$@^XpZ)+Z z{cKv+^#=gBapew*36{rR050JIK5Hw7kZd%xV@Y6SV; zVbgLWtO_KWEt8;~RhXq+oM0t%AdD0IsYM(uYqx1z8ZPhN;yjP#at{w0;P5TG$&5(z z#4}R41M5$Ge;_BiBnLpRO~oG`JCv5prF=jL7XIzM*k z?;qiyz7h)nLtp{ETPtbtS9Ed}eC&sHPNM&YGTT7C?+ie1;I{CceK%DuQ&}$DmVTEW z@_bD-cLsZ7wAyWp`4TsZpXYxL0Mgof3Mao_mRR*+HKhwEIpQigT{<6O zwL^6@BdthMA${8FB=l@tach~r7;I4i$p0bE9(W{a-Nx`>yCxDrC?AF!YcE1=ydCwK zPfcLiic>|AE&#C9$Y1Aj7 z&SNT@knU4!X2h=*Pb)LUPx#1ZP1_K>z@;j|==^1poj522e~?MF0Q*|NsA`*`M72000SaNLh0L01m+b01m+c zxRGn^000JbNklm|!Z{X8PS!as$9&;GKf0rme**}XApF1rSYoCB0<4Xy2H4#T&er=p4*)2J0et?0 zY-L=QEYsOg4840{m>z_T{x`{0I#BV$uZPm6G=RUcfbtagE8X%rrgpiu@IPe0D!%~G z6P)_LX+h3cDnC{Z48VMg{;5VbM-p+9TsUU~KD71Ik~Z27%uI>Xz`6oUd4; z#1~^|%QUD5jb>iT4ZuRyCVPCvvB?M^mBmTMpiFtdu$vF6f`V=_L3my$oi1~07%7KE za`=29X)tBG0{UE#+^3(@Xt~H$UM?Z~UVo6{qZZedvl9S^XfAi8kDbP3p$9*F&0?a( znMnYC1U!qeYKK_XE%Sb6yh0ZH7BcV9e^W!tNaaw(Ai)pgW{4l=#E9_S_93}ZgG@E; zi_xtxAXLEFqM1Ih`bOcg`)>HSXTl`I_p1*InvezsY@s&+PGu}TYpkaG(zK=Kd#Dl# zK=cA&j$bU3cVTf5d1RIy>(aD#%yd&h63|}gVtl&mezm=_8wB7ID_0*<- zPZlD6-2iqaAwk%Q;AyG_>hi>WtDHus>Et3@KE5-~eMZ?KhHB|jA+Kef5mso|oXDzP z>W-qt^XNIdzZj^-Qda?&+)7u2(K==NrsiPk&8!vIXU(T`s2V!NGvZLgDyR6;#WEd~ zY2}ewa9?R1*t8l1e}yL>rp(3S^NMzUC&EI&{yqvz%qF|n2AklEX-A|U7K8T*nqh)_ z56INi{J66GE2{T5{1pHL03CMHJ65&a2Jo}IiZk8Bl?97p_x(ppbphxd z&LsE%sQrv206)aG)3l#pnN`xq5|7x5@-P~GJ~Fa0)rWMOPj&UmG=Ulm+Al~coZ}kX zWPc^l;O8}+&QFRVGOh`WvKMy^SwLeyUj4uK(;I;84Z=fXLM}jIf}bS{2L)ke$bo81 zdYx+AAsE@<2zhFY=C>Q|#g%AB9q%e_G+xZUo}Z?w>Bja+9msG+DC;s@EmepzsUVvK zNV5#N_9QuO1Lf=yE`>5-6nTKBMdU7lWv|}diVxV7f-+iw z5kwCFWRDVqPtjU1cm2_j9_m?=}-cpYF%jB_aFkFKmP0S%Qp1&?O24t7j1 z&I(_~99(cB~%A4yl&D4M)`N4`a_RVworD{GjMQ>m=@y00000NkvXXu0mjfIJ!ks diff --git a/core/src/main/resources/shaders/curve.vert b/core/src/main/resources/shaders/curve.vert index 8b8c96755..03876a40c 100644 --- a/core/src/main/resources/shaders/curve.vert +++ b/core/src/main/resources/shaders/curve.vert @@ -8,17 +8,17 @@ out vec4 vertexColor; out vec3 vertexWorldPos; out float vertexYPos; -uniform bool uWhiteWorld; +uniform bool whiteWorld; -uniform mat4 uCombinedMatrix; -uniform vec3 uModelOffset; -uniform float uWorldYOffset; +uniform mat4 combinedMatrix; +uniform vec3 modelOffset; +uniform float worldYOffset; -uniform int uWorldSkyLight; -uniform sampler2D uLightMap; -uniform float uMircoOffset; +uniform int worldSkyLight; +uniform sampler2D lightMap; +uniform float mircoOffset; -uniform float uEarthRadius; +uniform float earthRadius; /** * TODO in the future this and standard.vert should be merged together to prevent inconsistencies between the two @@ -35,9 +35,9 @@ void main() { vPos = vPosition; // This is so it can be passed to the fragment shader - vertexWorldPos = vPosition.xyz + uModelOffset; + vertexWorldPos = vPosition.xyz + modelOffset; - vertexYPos = vPosition.y + uWorldYOffset; + vertexYPos = vPosition.y + worldYOffset; uint meta = vPosition.a; @@ -46,11 +46,11 @@ void main() // 0b01 = positive offset // 0b11 = negative offset // format is: 0b00zzyyxx - float mx = (mirco & 1u) != 0u ? uMircoOffset : 0.0; + float mx = (mirco & 1u) != 0u ? mircoOffset : 0.0; mx = (mirco & 2u) != 0u ? -mx : mx; - float my = (mirco & 4u) != 0u ? uMircoOffset : 0.0; + float my = (mirco & 4u) != 0u ? mircoOffset : 0.0; my = (mirco & 8u) != 0u ? -my : my; - float mz = (mirco & 16u) != 0u ? uMircoOffset : 0.0; + float mz = (mirco & 16u) != 0u ? mircoOffset : 0.0; mz = (mirco & 32u) != 0u ? -mz : mz; vertexWorldPos.x += mx; vertexWorldPos.y += my; @@ -58,7 +58,7 @@ void main() // vertex transformation logic - stduhpf - float localRadius = uEarthRadius + vertexYPos; + float localRadius = earthRadius + vertexYPos; float phi = length(vertexWorldPos.xz) / localRadius; vertexWorldPos.y += (cos(phi) - 1.0) * localRadius; vertexWorldPos.xz = vertexWorldPos.xz * sin(phi) / phi; @@ -68,12 +68,12 @@ void main() float light2 = (mod(float(lights), 16.0) + 0.5) / 16.0; float light = (float(lights / 16u) + 0.5) / 16.0; - vertexColor = vec4(texture(uLightMap, vec2(light, light2)).xyz, 1.0); + vertexColor = vec4(texture(lightMap, vec2(light, light2)).xyz, 1.0); - if (!uWhiteWorld) + if (!whiteWorld) { vertexColor *= color; } - gl_Position = uCombinedMatrix * vec4(vertexWorldPos, 1.0); + gl_Position = combinedMatrix * vec4(vertexWorldPos, 1.0); } diff --git a/core/src/main/resources/shaders/debug/frag.frag b/core/src/main/resources/shaders/debug/frag.frag index 16f8de33f..1f22d751c 100644 --- a/core/src/main/resources/shaders/debug/frag.frag +++ b/core/src/main/resources/shaders/debug/frag.frag @@ -1,7 +1,6 @@ #version 150 core uniform vec4 uColor; - out vec4 fragColor; void main() diff --git a/core/src/main/resources/shaders/debug/vert.vert b/core/src/main/resources/shaders/debug/vert.vert index befc43200..e32ec6e80 100644 --- a/core/src/main/resources/shaders/debug/vert.vert +++ b/core/src/main/resources/shaders/debug/vert.vert @@ -1,10 +1,10 @@ #version 150 core -uniform mat4 uTransform; +uniform mat4 transform; in vec3 vPosition; void main() { - gl_Position = uTransform * vec4(vPosition, 1.0); + gl_Position = transform * vec4(vPosition, 1.0); } \ No newline at end of file diff --git a/core/src/main/resources/shaders/flat_shaded.frag b/core/src/main/resources/shaders/flat_shaded.frag index 0bfe32461..e6d7a9407 100644 --- a/core/src/main/resources/shaders/flat_shaded.frag +++ b/core/src/main/resources/shaders/flat_shaded.frag @@ -9,13 +9,13 @@ out vec4 fragColor; // Fog/Clip Uniforms -uniform float uClipDistance = 0.0; +uniform float clipDistance = 0.0; // Noise Uniforms -uniform bool uNoiseEnabled; -uniform int uNoiseSteps; -uniform float uNoiseIntensity; -uniform int uNoiseDropoff; +uniform bool noiseEnabled; +uniform int noiseSteps; +uniform float noiseIntensity; +uniform int noiseDropoff; // The random functions for diffrent dimentions @@ -38,13 +38,13 @@ void applyNoise(inout vec4 fragColor, const in float viewDist) // This bit of code is required to fix the vertex position problem cus of floats in the verted world position varuable vec3 fixedVPos = vPos.xyz + vertexNormal * 0.001; - float noiseAmplification = uNoiseIntensity * 0.01; + float noiseAmplification = noiseIntensity * 0.01; float lum = (fragColor.r + fragColor.g + fragColor.b) / 3.0; noiseAmplification = (1.0 - pow(lum * 2.0 - 1.0, 2.0)) * noiseAmplification; // Lessen the effect on depending on how dark the object is, equasion for this is -(2x-1)^{2}+1 noiseAmplification *= fragColor.a; // The effect would lessen on transparent objects // Random value for each position - float randomValue = rand(quantize(fixedVPos, uNoiseSteps)) + float randomValue = rand(quantize(fixedVPos, noiseSteps)) * 2.0 * noiseAmplification - noiseAmplification; // Modifies the color @@ -52,8 +52,8 @@ void applyNoise(inout vec4 fragColor, const in float viewDist) vec3 newCol = fragColor.rgb + (1.0 - fragColor.rgb) * randomValue; newCol = clamp(newCol, 0.0, 1.0); - if (uNoiseDropoff != 0) { - float distF = min(viewDist / uNoiseDropoff, 1.0); + if (noiseDropoff != 0) { + float distF = min(viewDist / noiseDropoff, 1.0); newCol = mix(newCol, fragColor.rgb, distF); // The further away it gets, the less noise gets applied } @@ -67,12 +67,12 @@ void main() fragColor = vertexColor; float viewDist = length(vertexWorldPos); - if (viewDist < uClipDistance && uClipDistance > 0.0) + if (viewDist < clipDistance && clipDistance > 0.0) { discard; } - if (uNoiseEnabled) + if (noiseEnabled) { applyNoise(fragColor, viewDist); } diff --git a/core/src/main/resources/shaders/fog/apply.frag b/core/src/main/resources/shaders/fog/apply.frag deleted file mode 100644 index 7241d1161..000000000 --- a/core/src/main/resources/shaders/fog/apply.frag +++ /dev/null @@ -1,24 +0,0 @@ -#version 150 core - -in vec2 TexCoord; - -out vec4 fragColor; - -uniform sampler2D uColorTexture; -uniform sampler2D uDepthTexture; - - - -void main() -{ - fragColor = vec4(1.0); - - float fragmentDepth = textureLod(uDepthTexture, TexCoord, 0).r; - - // a fragment depth of "1" means the fragment wasn't drawn to, - // only update fragments that were drawn to - if (fragmentDepth != 1) - { - fragColor = texture(uColorTexture, TexCoord); - } -} diff --git a/core/src/main/resources/shaders/fog/fog.frag b/core/src/main/resources/shaders/fog/fog.frag index eed4c6b6b..04eb3f7c1 100644 --- a/core/src/main/resources/shaders/fog/fog.frag +++ b/core/src/main/resources/shaders/fog/fog.frag @@ -3,17 +3,17 @@ in vec2 TexCoord; out vec4 fragColor; -uniform sampler2D uDepthMap; +uniform sampler2D gDepthMap; // inverted model view matrix and projection matrix -uniform mat4 uInvMvmProj; +uniform mat4 gInvMvmProj; -uniform float uFogScale; -uniform float uFogVerticalScale; -uniform vec4 uFogColor; -uniform int uFullFogMode; +uniform float fogScale; +uniform float fogVerticalScale; +uniform float nearFogStart; +uniform float nearFogLength; +uniform int fullFogMode; -uniform float uNearFogStart; -uniform float uNearFogLength; +uniform vec4 fogColor; /* ========MARCO DEFINED BY RUNTIME CODE GEN========= @@ -53,7 +53,7 @@ vec3 calcViewPosition(float fragmentDepth) { vec4 ndc = vec4(TexCoord.xy, fragmentDepth, 1.0); ndc.xyz = ndc.xyz * 2.0 - 1.0; - vec4 eyeCoord = uInvMvmProj * ndc; + vec4 eyeCoord = gInvMvmProj * ndc; return eyeCoord.xyz / eyeCoord.w; } @@ -66,19 +66,19 @@ vec3 calcViewPosition(float fragmentDepth) { void main() { float vertexYPos = 100.0f; - float fragmentDepth = texture(uDepthMap, TexCoord).r; - fragColor = vec4(uFogColor.rgb, 0.0); + float fragmentDepth = texture(gDepthMap, TexCoord).r; + fragColor = vec4(fogColor.rgb, 0.0); // a fragment depth of "1" means the fragment wasn't drawn to, // we only want to apply Fog to LODs, not to the sky outside the LODs if (fragmentDepth < 1.0) { - if (uFullFogMode == 0) { + if (fullFogMode == 0) { // render fog based on distance from the camera vec3 vertexWorldPos = calcViewPosition(fragmentDepth); - float horizontalDist = length(vertexWorldPos.xz) * uFogScale; - float heightDist = calculateHeightFogDepth(vertexWorldPos.y, vertexYPos) * uFogVerticalScale; - float farDist = calculateFarFogDepth(horizontalDist, length(vertexWorldPos.xyz) * uFogScale, uNearFogStart); + float horizontalDist = length(vertexWorldPos.xz) * fogScale; + float heightDist = calculateHeightFogDepth(vertexWorldPos.y, vertexYPos) * fogVerticalScale; + float farDist = calculateFarFogDepth(horizontalDist, length(vertexWorldPos.xyz) * fogScale, nearFogStart); float nearFogThickness = getNearFogThickness(horizontalDist); float farFogThickness = getFarFogThickness(farDist); @@ -89,7 +89,7 @@ void main() float dither = InterleavedGradientNoise(gl_FragCoord.xy) - 0.5; fragColor.a += dither / 255.0; } - else if (uFullFogMode == 1) { + else if (fullFogMode == 1) { // render everything with the fog color fragColor.a = 1.0; } @@ -101,7 +101,7 @@ void main() // a uniform we don't have to worry about GLSL optimizing away different // options when testing, causing a bunch of headaches if we just want to render the screen red. - float depthValue = textureLod(uDepthMap, TexCoord, 0).r; + float depthValue = textureLod(gDepthMap, TexCoord, 0).r; fragColor.rgb = vec3(depthValue); // Convert depth value to grayscale color fragColor.a = 1.0; } diff --git a/core/src/main/resources/shaders/genericObject/direct/frag.frag b/core/src/main/resources/shaders/genericObject/direct/frag.frag deleted file mode 100644 index c3d2ac8c4..000000000 --- a/core/src/main/resources/shaders/genericObject/direct/frag.frag +++ /dev/null @@ -1,10 +0,0 @@ -#version 150 core - -in vec4 fColor; - -out vec4 fragColor; - -void main() -{ - fragColor = fColor; -} \ No newline at end of file diff --git a/core/src/main/resources/shaders/genericObject/direct/vert.vert b/core/src/main/resources/shaders/genericObject/direct/vert.vert deleted file mode 100644 index 55cf5cad7..000000000 --- a/core/src/main/resources/shaders/genericObject/direct/vert.vert +++ /dev/null @@ -1,41 +0,0 @@ -#version 150 core - -uniform mat4 uTransform; -uniform vec4 uColor; -uniform int uSkyLight; -uniform int uBlockLight; -uniform sampler2D uLightMap; - -uniform float uNorthShading; -uniform float uSouthShading; -uniform float uEastShading; -uniform float uWestShading; -uniform float uTopShading; -uniform float uBottomShading; - - -in vec3 vPosition; - -out vec4 fColor; - -void main() -{ - gl_Position = uTransform * vec4(vPosition, 1.0); - - - float blockLight = (float(uBlockLight)+0.5) / 16.0; - float skyLight = (float(uSkyLight)+0.5) / 16.0; - vec4 lightColor = vec4(texture(uLightMap, vec2(blockLight, skyLight)).xyz, 1.0); - - - fColor = lightColor * uColor; - - // apply directional shading - if (gl_VertexID >= 0 && gl_VertexID < 4) { fColor.rgb *= uNorthShading; } - else if (gl_VertexID >= 4 && gl_VertexID < 8) { fColor.rgb *= uSouthShading; } - else if (gl_VertexID >= 8 && gl_VertexID < 12) { fColor.rgb *= uWestShading; } - else if (gl_VertexID >= 12 && gl_VertexID < 16) { fColor.rgb *= uEastShading; } - else if (gl_VertexID >= 16 && gl_VertexID < 20) { fColor.rgb *= uBottomShading; } - else if (gl_VertexID >= 20 && gl_VertexID < 24) { fColor.rgb *= uTopShading; } - -} \ No newline at end of file diff --git a/core/src/main/resources/shaders/genericObject/instanced/frag.frag b/core/src/main/resources/shaders/genericObject/instanced/frag.frag deleted file mode 100644 index c3d2ac8c4..000000000 --- a/core/src/main/resources/shaders/genericObject/instanced/frag.frag +++ /dev/null @@ -1,10 +0,0 @@ -#version 150 core - -in vec4 fColor; - -out vec4 fragColor; - -void main() -{ - fragColor = fColor; -} \ No newline at end of file diff --git a/core/src/main/resources/shaders/genericObject/instanced/vert.vert b/core/src/main/resources/shaders/genericObject/instanced/vert.vert deleted file mode 100644 index a7d9a6a96..000000000 --- a/core/src/main/resources/shaders/genericObject/instanced/vert.vert +++ /dev/null @@ -1,66 +0,0 @@ -#version 330 core - -layout (location = 1) in vec4 aColor; -layout (location = 2) in vec3 aScale; -layout (location = 3) in ivec3 aTranslateChunk; -layout (location = 4) in vec3 aTranslateSubChunk; -layout (location = 5) in int aMaterial; - -uniform ivec3 uOffsetChunk; -uniform vec3 uOffsetSubChunk; -uniform ivec3 uCameraPosChunk; -uniform vec3 uCameraPosSubChunk; - -uniform mat4 uProjectionMvm; -uniform int uSkyLight; -uniform int uBlockLight; -uniform sampler2D uLightMap; - -uniform float uNorthShading; -uniform float uSouthShading; -uniform float uEastShading; -uniform float uWestShading; -uniform float uTopShading; -uniform float uBottomShading; - - -in vec3 vPosition; - -out vec4 fColor; - -void main() -{ - // aTranslate - moves the vertex to the boxGroup's relative position - // uOffset - moves the vertex to the boxGroup's world position - // uCameraPos - moves the vertex into camera space - vec3 trans = (aTranslateChunk + uOffsetChunk - uCameraPosChunk) * 16.0f; - // separate float and int values are to fix percission loss at extreme distances from the origin (IE 10,000,000+) - // luckily large translate values minus large cameraPos generally equal values that cleanly fit in a float - trans += (aTranslateSubChunk + uOffsetSubChunk - uCameraPosSubChunk); - - // combination translation and scaling matrix - mat4 transform = mat4( - aScale.x, 0.0, 0.0, 0.0, - 0.0, aScale.y, 0.0, 0.0, - 0.0, 0.0, aScale.z, 0.0, - trans.x, trans.y, trans.z, 1.0 - ); - - gl_Position = uProjectionMvm * transform * vec4(vPosition, 1.0); - - float blockLight = (float(uBlockLight)+0.5) / 16.0; - float skyLight = (float(uSkyLight)+0.5) / 16.0; - vec4 lightColor = vec4(texture(uLightMap, vec2(blockLight, skyLight)).xyz, 1.0); - - - fColor = lightColor * aColor; - - // apply directional shading - if (gl_VertexID >= 0 && gl_VertexID < 4) { fColor.rgb *= uNorthShading; } - else if (gl_VertexID >= 4 && gl_VertexID < 8) { fColor.rgb *= uSouthShading; } - else if (gl_VertexID >= 8 && gl_VertexID < 12) { fColor.rgb *= uWestShading; } - else if (gl_VertexID >= 12 && gl_VertexID < 16) { fColor.rgb *= uEastShading; } - else if (gl_VertexID >= 16 && gl_VertexID < 20) { fColor.rgb *= uBottomShading; } - else if (gl_VertexID >= 20 && gl_VertexID < 24) { fColor.rgb *= uTopShading; } - -} diff --git a/core/src/main/resources/shaders/noise/noise.frag b/core/src/main/resources/shaders/noise/noise.frag index e34018619..1156311fc 100644 --- a/core/src/main/resources/shaders/noise/noise.frag +++ b/core/src/main/resources/shaders/noise/noise.frag @@ -7,9 +7,9 @@ out vec4 fragColor; uniform float distanceScale; -uniform int uNoiseSteps; -uniform float uNoiseIntensity; -uniform float uNoiseDropoff; +uniform int noiseSteps; +uniform float noiseIntensity; +uniform float noiseDropoff; @@ -43,12 +43,12 @@ void main() { vec3 fixedVPos = vPos.xyz - vertexNormal * 0.001; - float noiseAmplification = uNoiseIntensity / 100; + float noiseAmplification = noiseIntensity / 100; noiseAmplification = (-1 * pow(2*((vertexColor.x + vertexColor.y + vertexColor.z) / 3) - 1, 2) + 1) * noiseAmplification; // Lessen the effect on depending on how dark the object is, equasion for this is -(2x-1)^{2}+1 noiseAmplification *= vertexColor.w; // The effect would lessen on transparent objects // Random value for each position - float randomValue = rand(quantize(fixedVPos.xyz, uNoiseSteps)) + float randomValue = rand(quantize(fixedVPos.xyz, noiseSteps)) * 2.0 * noiseAmplification - noiseAmplification; @@ -57,7 +57,7 @@ void main() { vec3 newCol = (1.0 - vertexColor.rgb) * randomValue; // Clamps it and turns it back into a vec4 - float distA = length(vertexWorldPos) * distanceScale * uNoiseDropoff; + float distA = length(vertexWorldPos) * distanceScale * noiseDropoff; fragColor = clamp(vec4(newCol.rgb, distA), 0.0, 1.0); // The further away it gets, the less noise gets applied // The further away it gets, the less noise gets applied diff --git a/core/src/main/resources/shaders/normal.vert b/core/src/main/resources/shaders/normal.vert index a7fc6cd33..e6a28aa8c 100644 --- a/core/src/main/resources/shaders/normal.vert +++ b/core/src/main/resources/shaders/normal.vert @@ -1,7 +1,6 @@ #version 150 core in vec2 vPosition; - out vec2 TexCoord; /** diff --git a/core/src/main/resources/shaders/standard.vert b/core/src/main/resources/shaders/standard.vert index bcff6d9c8..316559b6e 100644 --- a/core/src/main/resources/shaders/standard.vert +++ b/core/src/main/resources/shaders/standard.vert @@ -8,15 +8,15 @@ out vec4 vertexColor; out vec3 vertexWorldPos; out float vertexYPos; -uniform bool uWhiteWorld; +uniform bool whiteWorld; -uniform mat4 uCombinedMatrix; -uniform vec3 uModelOffset; -uniform float uWorldYOffset; +uniform mat4 combinedMatrix; +uniform vec3 modelOffset; +uniform float worldYOffset; -uniform int uWorldSkyLight; -uniform sampler2D uLightMap; -uniform float uMircoOffset; +uniform int worldSkyLight; +uniform sampler2D lightMap; +uniform float mircoOffset; /** @@ -33,9 +33,9 @@ void main() { vPos = vPosition; // This is so it can be passed to the fragment shader - vertexWorldPos = vPosition.xyz + uModelOffset; + vertexWorldPos = vPosition.xyz + modelOffset; - vertexYPos = vPosition.y + uWorldYOffset; + vertexYPos = vPosition.y + worldYOffset; uint meta = vPosition.a; @@ -44,23 +44,23 @@ void main() // 0b01 = positive offset // 0b11 = negative offset // format is: 0b00zzyyxx - float mx = (mirco & 1u)!=0u ? uMircoOffset : 0.0; + float mx = (mirco & 1u)!=0u ? mircoOffset : 0.0; mx = (mirco & 2u)!=0u ? -mx : mx; - float my = (mirco & 4u)!=0u ? uMircoOffset : 0.0; + float my = (mirco & 4u)!=0u ? mircoOffset : 0.0; my = (mirco & 8u)!=0u ? -my : my; - float mz = (mirco & 16u)!=0u ? uMircoOffset : 0.0; + float mz = (mirco & 16u)!=0u ? mircoOffset : 0.0; mz = (mirco & 32u)!=0u ? -mz : mz; uint lights = meta & 0xFFu; float light2 = (mod(float(lights), 16.0)+0.5) / 16.0; float light = (float(lights/16u)+0.5) / 16.0; - vertexColor = vec4(texture(uLightMap, vec2(light, light2)).xyz, 1.0); + vertexColor = vec4(texture(lightMap, vec2(light, light2)).xyz, 1.0); - if (!uWhiteWorld) + if (!whiteWorld) { vertexColor *= color; } - gl_Position = uCombinedMatrix * vec4(vertexWorldPos + vec3(mx, 0, mz), 1.0); + gl_Position = combinedMatrix * vec4(vertexWorldPos + vec3(mx, 0, mz), 1.0); } diff --git a/core/src/main/resources/sqlScripts/0070-sqlite-createBeaconBeamTable.sql b/core/src/main/resources/sqlScripts/0070-sqlite-createBeaconBeamTable.sql deleted file mode 100644 index 48a5040a6..000000000 --- a/core/src/main/resources/sqlScripts/0070-sqlite-createBeaconBeamTable.sql +++ /dev/null @@ -1,16 +0,0 @@ - -CREATE TABLE BeaconBeam( - -- compound primary key - BlockPosX INT NOT NULL - ,BlockPosY INT NOT NULL - ,BlockPosZ INT NOT NULL - - ,ColorR INT NOT NULL - ,ColorG INT NOT NULL - ,ColorB INT NOT NULL - - ,LastModifiedUnixDateTime BIGINT NOT NULL -- in GMT 0 - ,CreatedUnixDateTime BIGINT NOT NULL -- in GMT 0 - - ,PRIMARY KEY (BlockPosX, BlockPosY, BlockPosZ) -); diff --git a/core/src/main/resources/sqlScripts/scriptList.txt b/core/src/main/resources/sqlScripts/scriptList.txt index 7c6ed02ea..52027a061 100644 --- a/core/src/main/resources/sqlScripts/scriptList.txt +++ b/core/src/main/resources/sqlScripts/scriptList.txt @@ -6,4 +6,3 @@ 0040-sqlite-removeRenderCache.sql 0050-sqlite-addApplyToParentIndex.sql 0060-sqlite-createChunkHashTable.sql -0070-sqlite-createBeaconBeamTable.sql diff --git a/core/src/test/java/testItems/lightingEngine/LightingTestBlockStateWrapper.java b/core/src/test/java/testItems/lightingEngine/LightingTestBlockStateWrapper.java index a46f50fc3..1ecea8ab5 100644 --- a/core/src/test/java/testItems/lightingEngine/LightingTestBlockStateWrapper.java +++ b/core/src/test/java/testItems/lightingEngine/LightingTestBlockStateWrapper.java @@ -22,8 +22,6 @@ package testItems.lightingEngine; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import tests.LightingEngineTest; -import java.awt.*; - /** * @see LightingEngineTest * @see LightingTestChunkWrapper @@ -104,18 +102,6 @@ public class LightingTestBlockStateWrapper implements IBlockStateWrapper public boolean isLiquid() { throw new UnsupportedOperationException("Not Implemented"); } @Override - public byte getMaterialId() { throw new UnsupportedOperationException("Not Implemented"); } - - @Override - public boolean isBeaconBlock() { throw new UnsupportedOperationException("Not Implemented"); } - @Override - public boolean isBeaconBaseBlock() { throw new UnsupportedOperationException("Not Implemented"); } - - @Override - public Color getMapColor() { throw new UnsupportedOperationException("Not Implemented"); } - @Override - public boolean isGlassBlock() { throw new UnsupportedOperationException("Not Implemented"); } - - + public byte getIrisBlockMaterialId() { throw new UnsupportedOperationException("Not Implemented"); } } diff --git a/core/src/test/java/testItems/lightingEngine/LightingTestChunkWrapper.java b/core/src/test/java/testItems/lightingEngine/LightingTestChunkWrapper.java index 8317a6509..30be0d596 100644 --- a/core/src/test/java/testItems/lightingEngine/LightingTestChunkWrapper.java +++ b/core/src/test/java/testItems/lightingEngine/LightingTestChunkWrapper.java @@ -90,7 +90,7 @@ public class LightingTestChunkWrapper implements IChunkWrapper IBlockStateWrapper block = chunkWrapper.getBlockState(x,y,z); int opacity = block.getOpacity(); - if (opacity >= LodUtil.BLOCK_FULLY_OPAQUE) + if (opacity >= IBlockStateWrapper.FULLY_OPAQUE) { opacity = 3; } diff --git a/core/src/test/java/testItems/worldGeneratorInjection/objects/LevelWrapperTest.java b/core/src/test/java/testItems/worldGeneratorInjection/objects/LevelWrapperTest.java index 4990c892e..b22178b55 100644 --- a/core/src/test/java/testItems/worldGeneratorInjection/objects/LevelWrapperTest.java +++ b/core/src/test/java/testItems/worldGeneratorInjection/objects/LevelWrapperTest.java @@ -20,7 +20,6 @@ package testItems.worldGeneratorInjection.objects; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType; -import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister; import com.seibel.distanthorizons.api.interfaces.world.IDhApiDimensionTypeWrapper; import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper; @@ -51,14 +50,9 @@ public class LevelWrapperTest implements IDhApiLevelWrapper public boolean hasSkyLight() { return false; } @Override - public int getMaxHeight() { return 0; } + public int getHeight() { return 0; } @Override public int getMinHeight() { return IDhApiLevelWrapper.super.getMinHeight(); } - @Override - public IDhApiCustomRenderRegister getRenderRegister() { return null; } - - - } \ No newline at end of file diff --git a/core/src/test/java/tests/DhSectionPosTest.java b/core/src/test/java/tests/DhSectionPosTest.java index 14f29a068..c0585a60e 100644 --- a/core/src/test/java/tests/DhSectionPosTest.java +++ b/core/src/test/java/tests/DhSectionPosTest.java @@ -191,19 +191,41 @@ public class DhSectionPosTest // origin pos // DhBlockPos originBlockPos = new DhBlockPos(0, 0, 0); - long originSectionPos = DhSectionPos.encodeContaining(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, originBlockPos); - assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 0, 0), originSectionPos); + long originsectionPos = DhSectionPos.encode(originBlockPos); + assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 0, 0), originsectionPos); // offset pos // long offsetSectionPos; DhBlockPos offsetBlockPos = new DhBlockPos(1000, 0, 42000); - offsetSectionPos = DhSectionPos.encodeContaining(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, offsetBlockPos); + offsetSectionPos = DhSectionPos.encode(offsetBlockPos); assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 15, 656), offsetSectionPos); offsetBlockPos = new DhBlockPos(-987654, 0, 46); - offsetSectionPos = DhSectionPos.encodeContaining(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, offsetBlockPos); + offsetSectionPos = DhSectionPos.encode(offsetBlockPos); + assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, -15433, 0), offsetSectionPos); + + } + + @Test + public void createFromBlockPos2D() + { + // origin pos // + + DhBlockPos2D originBlockPos = new DhBlockPos2D(0, 0); + long originSectionPos = DhSectionPos.encode(originBlockPos); + assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 0, 0), originSectionPos); + + + // offset pos // + + DhBlockPos2D offsetBlockPos = new DhBlockPos2D(1000, 42000); + long offsetSectionPos = DhSectionPos.encode(offsetBlockPos); + assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 15, 656), offsetSectionPos); + + offsetBlockPos = new DhBlockPos2D(-987654, 46); + offsetSectionPos = DhSectionPos.encode(offsetBlockPos); assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, -15433, 0), offsetSectionPos); } @@ -214,18 +236,18 @@ public class DhSectionPosTest // origin pos // DhChunkPos originChunkPos = new DhChunkPos(0,0); - long originSectionPos = DhSectionPos.encodeContaining(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL, originChunkPos); + long originSectionPos = DhSectionPos.encode(originChunkPos); assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL, 0, 0), originSectionPos); // offset pos // DhChunkPos offsetChunkPos = new DhChunkPos(1000, 42000); - long offsetSectionPos = DhSectionPos.encodeContaining(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL, offsetChunkPos); + long offsetSectionPos = DhSectionPos.encode(offsetChunkPos); assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL, 15, 656), offsetSectionPos); offsetChunkPos = new DhChunkPos(-987654, 46); - offsetSectionPos = DhSectionPos.encodeContaining(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL, offsetChunkPos); + offsetSectionPos = DhSectionPos.encode(offsetChunkPos); assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL, -15433, 0), offsetSectionPos); } From ccbb071704ed95b094e4bd350e26aabe76e1d6f9 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 28 Jul 2024 17:05:58 +0500 Subject: [PATCH 174/205] Reapply "Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core into serverside" This reverts commit d26cb410482b6884ef70824796a4b9913ee21504. --- .../com/seibel/distanthorizons/api/DhApi.java | 8 + .../enums/rendering/EDhApiBlockMaterial.java | 67 ++ .../block/IDhApiBlockStateWrapper.java | 15 +- .../client/IDhApiGenericRenderingConfig.java | 46 ++ .../config/client/IDhApiGraphicsConfig.java | 1 + .../data/IDhApiTerrainDataCache.java | 21 + .../data/IDhApiTerrainDataRepo.java | 81 ++- .../factories/IDhApiWrapperFactory.java | 35 +- .../rendering/IDhApiCullingFrustum.java | 4 +- .../IDhApiGenericObjectShaderProgram.java | 80 +++ .../rendering/IDhApiShaderProgram.java | 7 +- .../rendering/IDhApiShadowCullingFrustum.java | 4 - .../AbstractDhApiChunkWorldGenerator.java | 49 +- .../worldGenerator/IDhApiWorldGenerator.java | 1 + .../IDhApiCustomRenderObjectFactory.java | 67 ++ .../render/IDhApiCustomRenderRegister.java | 30 + .../render/IDhApiRenderableBoxGroup.java | 98 +++ .../api/interfaces/util/IDhApiCopyable.java | 18 + .../interfaces/world/IDhApiLevelWrapper.java | 9 +- .../abstractEvents/DhApiAfterRenderEvent.java | 15 +- .../DhApiBeforeBufferRenderEvent.java | 18 +- .../DhApiBeforeGenericObjectRenderEvent.java | 97 +++ .../DhApiBeforeGenericRenderCleanupEvent.java | 47 ++ .../DhApiBeforeGenericRenderSetupEvent.java | 50 ++ .../DhApiBeforeRenderCleanupEvent.java | 2 +- .../DhApiChunkModifiedEvent.java | 12 +- .../DhApiColorDepthTextureCreatedEvent.java | 13 +- .../abstractEvents/DhApiLevelLoadEvent.java | 6 +- .../abstractEvents/DhApiLevelUnloadEvent.java | 7 +- .../events/interfaces/IDhApiEventParam.java | 13 + .../DhApiRenderParam.java | 45 +- .../api/objects/data/DhApiChunk.java | 8 +- .../api/objects/data/DhApiRaycastResult.java | 3 +- .../objects/data/DhApiTerrainDataPoint.java | 8 +- .../objects/math/DhApiMat4f.java} | 486 ++++---------- .../api/objects/math/DhApiVec3d.java | 94 +++ .../api/objects/math/DhApiVec3f.java | 98 +++ .../objects/render/DhApiRenderableBox.java | 52 ++ .../DhApiRenderableBoxGroupShading.java | 93 +++ .../DependencyInjection/ApiEventInjector.java | 22 +- .../distanthorizons/coreapi/ModInfo.java | 4 +- .../interfaces/config/IConfigEntry.java | 3 +- .../coreapi/util/math/Vec3f.java | 263 -------- .../distanthorizons/core/Initializer.java | 2 + .../client/DhApiGenericRenderingConfig.java | 46 ++ .../config/client/DhApiGraphicsConfig.java | 10 +- .../methods/data/DhApiTerrainDataCache.java | 87 +++ .../methods/data/DhApiTerrainDataRepo.java | 233 ++++--- .../core/api/internal/ClientApi.java | 42 +- .../core/api/internal/ServerApi.java | 2 +- .../core/api/internal/SharedApi.java | 53 +- .../distanthorizons/core/config/Config.java | 134 +++- .../core/config/types/ConfigEntry.java | 28 +- .../core/config/types/ConfigLinkedEntry.java | 1 + .../fullData/sources/FullDataSourceV2.java | 2 + .../render/ColumnRenderSource.java | 2 +- .../render/bufferBuilding/ColumnBox.java | 5 +- .../bufferBuilding/ColumnRenderBuffer.java | 7 +- .../ColumnRenderBufferBuilder.java | 28 +- .../bufferBuilding/CubicLodTemplate.java | 36 +- .../render/bufferBuilding/LodQuadBuilder.java | 11 +- .../FullDataToRenderDataTransformer.java | 218 ++++--- .../transformers/LodDataBuilder.java | 67 +- .../core/enums/EDhDirection.java | 2 +- .../structure/ClientOnlySaveStructure.java | 24 +- .../SubDimensionLevelMatcher.java | 4 +- .../core/generation/AdjacentChunkHolder.java | 117 ++++ .../core/generation/DhLightingEngine.java | 61 +- .../core/generation/WorldGenerationQueue.java | 11 +- .../core/level/AbstractDhLevel.java | 125 +++- .../core/level/ClientLevelModule.java | 50 +- .../core/level/DhClientLevel.java | 18 +- .../core/level/DhClientServerLevel.java | 31 +- .../core/level/DhServerLevel.java | 27 +- .../distanthorizons/core/level/IDhLevel.java | 20 + .../core/logging/f3/F3Screen.java | 49 +- .../distanthorizons/core/pos/DhChunkPos.java | 27 +- .../core/pos/DhSectionPos.java | 36 +- .../core/render/DhFrustumBounds.java | 7 +- .../core/render/LodQuadTree.java | 6 + .../core/render/LodRenderSection.java | 3 +- .../core/render/NeverCullFrustum.java | 5 +- .../core/render/RenderBufferHandler.java | 13 +- .../core/render/fog/LodFogConfig.java | 12 +- .../core/render/glObject/GLProxy.java | 32 +- .../core/render/glObject/buffer/GLBuffer.java | 6 +- .../render/glObject/shader/ShaderProgram.java | 14 +- .../AbstractVertexAttribute.java | 2 +- .../core/render/renderer/DebugRenderer.java | 155 ++--- ...ogram.java => DhTerrainShaderProgram.java} | 123 ++-- .../core/render/renderer/FogRenderer.java | 135 ++++ .../core/render/renderer/LodRenderer.java | 237 ++++--- .../core/render/renderer/SSAORenderer.java | 11 +- .../renderer/generic/BeaconRenderHandler.java | 241 +++++++ .../renderer/generic/CloudRenderHandler.java | 403 ++++++++++++ .../generic/GenericObjectRenderer.java | 607 ++++++++++++++++++ .../generic/GenericObjectShaderProgram.java | 229 +++++++ .../generic/GenericRenderObjectFactory.java | 78 +++ .../renderer/generic/RenderableBoxGroup.java | 354 ++++++++++ .../renderer/shaders/FogApplyShader.java | 104 +++ .../render/renderer/shaders/FogShader.java | 116 ++-- .../renderer/shaders/SSAOApplyShader.java | 12 + .../render/renderer/shaders/SSAOShader.java | 19 +- .../core/sql/dto/BeaconBeamDTO.java | 55 ++ .../core/sql/repo/AbstractDhRepo.java | 30 + .../core/sql/repo/BeaconBeamRepo.java | 196 ++++++ .../distanthorizons/core/util/ColorUtil.java | 5 + .../distanthorizons/core/util/LodUtil.java | 32 +- .../core/util/RenderDataPointUtil.java | 4 +- .../distanthorizons/core/util/RenderUtil.java | 6 +- .../distanthorizons/core/util/math/Mat4f.java | 249 +++++++ .../core}/util/math/Vec3d.java | 139 +--- .../distanthorizons/core/util/math/Vec3f.java | 173 +++++ .../core}/util/math/Vec3i.java | 2 +- .../core/world/DhApiWorldProxy.java | 8 + .../core/world/DhClientServerWorld.java | 1 + .../wrapperInterfaces/IWrapperFactory.java | 11 + .../block/IBlockStateWrapper.java | 49 +- .../chunk/IChunkWrapper.java | 109 ++++ .../minecraft/IMinecraftClientWrapper.java | 6 +- .../minecraft/IMinecraftRenderWrapper.java | 6 +- .../misc/IServerPlayerWrapper.java | 2 +- .../world/IClientLevelWrapper.java | 2 + .../world/ILevelWrapper.java | 8 +- .../world/IServerLevelWrapper.java | 3 - .../assets/distanthorizons/lang/en_us.json | 42 +- .../textures/clouds - original.png | Bin 0 -> 6214 bytes .../distanthorizons/textures/clouds.png | Bin 0 -> 1777 bytes core/src/main/resources/shaders/curve.vert | 34 +- .../main/resources/shaders/debug/frag.frag | 1 + .../main/resources/shaders/debug/vert.vert | 4 +- .../main/resources/shaders/flat_shaded.frag | 22 +- .../src/main/resources/shaders/fog/apply.frag | 24 + core/src/main/resources/shaders/fog/fog.frag | 34 +- .../shaders/genericObject/direct/frag.frag | 10 + .../shaders/genericObject/direct/vert.vert | 41 ++ .../shaders/genericObject/instanced/frag.frag | 10 + .../shaders/genericObject/instanced/vert.vert | 66 ++ .../main/resources/shaders/noise/noise.frag | 12 +- core/src/main/resources/shaders/normal.vert | 1 + core/src/main/resources/shaders/standard.vert | 30 +- .../0070-sqlite-createBeaconBeamTable.sql | 16 + .../main/resources/sqlScripts/scriptList.txt | 1 + .../LightingTestBlockStateWrapper.java | 16 +- .../LightingTestChunkWrapper.java | 2 +- .../objects/LevelWrapperTest.java | 8 +- .../src/test/java/tests/DhSectionPosTest.java | 36 +- 147 files changed, 6344 insertions(+), 1802 deletions(-) create mode 100644 api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiBlockMaterial.java create mode 100644 api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGenericRenderingConfig.java create mode 100644 api/src/main/java/com/seibel/distanthorizons/api/interfaces/data/IDhApiTerrainDataCache.java create mode 100644 api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiGenericObjectShaderProgram.java create mode 100644 api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiCustomRenderObjectFactory.java create mode 100644 api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiCustomRenderRegister.java create mode 100644 api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiRenderableBoxGroup.java create mode 100644 api/src/main/java/com/seibel/distanthorizons/api/interfaces/util/IDhApiCopyable.java create mode 100644 api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeGenericObjectRenderEvent.java create mode 100644 api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeGenericRenderCleanupEvent.java create mode 100644 api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeGenericRenderSetupEvent.java create mode 100644 api/src/main/java/com/seibel/distanthorizons/api/methods/events/interfaces/IDhApiEventParam.java rename api/src/main/java/com/seibel/distanthorizons/{coreapi/util/math/Mat4f.java => api/objects/math/DhApiMat4f.java} (57%) create mode 100644 api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiVec3d.java create mode 100644 api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiVec3f.java create mode 100644 api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBox.java create mode 100644 api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBoxGroupShading.java delete mode 100644 api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Vec3f.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGenericRenderingConfig.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataCache.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/generation/AdjacentChunkHolder.java rename core/src/main/java/com/seibel/distanthorizons/core/render/renderer/{LodRenderProgram.java => DhTerrainShaderProgram.java} (52%) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/render/renderer/FogRenderer.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/BeaconRenderHandler.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/CloudRenderHandler.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectShaderProgram.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericRenderObjectFactory.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/RenderableBoxGroup.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogApplyShader.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/sql/dto/BeaconBeamDTO.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/sql/repo/BeaconBeamRepo.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/util/math/Mat4f.java rename {api/src/main/java/com/seibel/distanthorizons/coreapi => core/src/main/java/com/seibel/distanthorizons/core}/util/math/Vec3d.java (50%) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/util/math/Vec3f.java rename {api/src/main/java/com/seibel/distanthorizons/coreapi => core/src/main/java/com/seibel/distanthorizons/core}/util/math/Vec3i.java (98%) create mode 100644 core/src/main/resources/assets/distanthorizons/textures/clouds - original.png create mode 100644 core/src/main/resources/assets/distanthorizons/textures/clouds.png create mode 100644 core/src/main/resources/shaders/fog/apply.frag create mode 100644 core/src/main/resources/shaders/genericObject/direct/frag.frag create mode 100644 core/src/main/resources/shaders/genericObject/direct/vert.vert create mode 100644 core/src/main/resources/shaders/genericObject/instanced/frag.frag create mode 100644 core/src/main/resources/shaders/genericObject/instanced/vert.vert create mode 100644 core/src/main/resources/sqlScripts/0070-sqlite-createBeaconBeamTable.sql diff --git a/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java b/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java index 80ae01702..21995fced 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/DhApi.java @@ -23,7 +23,9 @@ import com.seibel.distanthorizons.api.interfaces.events.IDhApiEventInjector; import com.seibel.distanthorizons.api.interfaces.factories.IDhApiWrapperFactory; import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable; import com.seibel.distanthorizons.api.interfaces.override.worldGenerator.IDhApiWorldGeneratorOverrideRegister; +import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderObjectFactory; import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderProxy; +import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiAfterDhInitEvent; import com.seibel.distanthorizons.api.methods.override.DhApiWorldGeneratorOverrideRegister; import com.seibel.distanthorizons.coreapi.ModInfo; @@ -127,6 +129,12 @@ public class DhApi */ public static IDhApiWrapperFactory wrapperFactory = null; + /** + * Used to create custom renderable objects.
+ * These objects can be added to the renderer in {@link IDhApiLevelWrapper} + * @since API 3.0.0 + */ + public static IDhApiCustomRenderObjectFactory customRenderObjectFactory = null; } diff --git a/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiBlockMaterial.java b/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiBlockMaterial.java new file mode 100644 index 000000000..dfedf3278 --- /dev/null +++ b/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiBlockMaterial.java @@ -0,0 +1,67 @@ +package com.seibel.distanthorizons.api.enums.rendering; + +/** + * contains the indices used by shaders to determine + * how different block types should be rendered.

+ * + * UNKNOWN,
+ * LEAVES,
+ * STONE,
+ * WOOD,
+ * METAL,
+ * DIRT,
+ * LAVA,
+ * DEEPSLATE,
+ * SNOW,
+ * SAND,
+ * TERRACOTTA,
+ * NETHER_STONE,
+ * WATER,
+ * GRASS,
+ * AIR,
+ * ILLUMINATED,
+ * + * @author James Seibel + * @since API 3.0.0 + * @version 2024-7-11 + */ +public enum EDhApiBlockMaterial +{ + UNKNOWN(0), + LEAVES(1), + STONE(2), + WOOD(3), + METAL(4), + DIRT(5), + LAVA(6), + DEEPSLATE(7), + SNOW(8), + SAND(9), + TERRACOTTA(10), + NETHER_STONE(11), + WATER(12), + GRASS(13), + /** shouldn't normally be needed, but just in case */ + AIR(14), + ILLUMINATED(15); // Max value + + + + public final byte index; + + EDhApiBlockMaterial(int index) { this.index = (byte)index;} + + public static EDhApiBlockMaterial getFromIndex(int index) + { + for(EDhApiBlockMaterial material : EDhApiBlockMaterial.values()) + { + if (material.index == index) + { + return material; + } + } + + return EDhApiBlockMaterial.UNKNOWN; + } + +} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/block/IDhApiBlockStateWrapper.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/block/IDhApiBlockStateWrapper.java index 682b04f06..8a71a090f 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/block/IDhApiBlockStateWrapper.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/block/IDhApiBlockStateWrapper.java @@ -30,12 +30,21 @@ import com.seibel.distanthorizons.api.interfaces.IDhApiUnsafeWrapper; */ public interface IDhApiBlockStateWrapper extends IDhApiUnsafeWrapper { + /** @since API 1.0.0 */ boolean isAir(); + /** @since API 1.0.0 */ boolean isSolid(); + /** @since API 1.0.0 */ boolean isLiquid(); - // TODO: - // boolean hasNoCollision(); - // boolean noFaceIsFullFace(); + /** + * Returns the full serialized form of the given block + * as defined by DH's serialization methods. + * @since API 3.0.0 + */ + String getSerialString(); + /** @since API 3.0.0 */ + byte getMaterialId(); + } diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGenericRenderingConfig.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGenericRenderingConfig.java new file mode 100644 index 000000000..3209b07ba --- /dev/null +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGenericRenderingConfig.java @@ -0,0 +1,46 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.api.interfaces.config.client; + +import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigGroup; +import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue; + +/** + * Distant Horizons' generic rendering configuration.

+ * + * @author James Seibel + * @version 2024-7-11 + * @since API 3.0.0 + */ +public interface IDhApiGenericRenderingConfig extends IDhApiConfigGroup +{ + /** + * If enabled DH will render generic objects into its terrain pass.
+ * This includes: clouds, beacons, and API added objects. + */ + IDhApiConfigValue renderingEnabled(); + + /** If enabled DH will render beacon beams. */ + IDhApiConfigValue beaconRenderingEnabled(); + + /** If enabled DH will render clouds. */ + IDhApiConfigValue cloudRenderingEnabled(); + +} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGraphicsConfig.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGraphicsConfig.java index d7c2792f4..f69869aeb 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGraphicsConfig.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiGraphicsConfig.java @@ -41,6 +41,7 @@ public interface IDhApiGraphicsConfig extends IDhApiConfigGroup IDhApiFogConfig fog(); IDhApiAmbientOcclusionConfig ambientOcclusion(); IDhApiNoiseTextureConfig noiseTexture(); + IDhApiGenericRenderingConfig genericRendering(); diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/data/IDhApiTerrainDataCache.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/data/IDhApiTerrainDataCache.java new file mode 100644 index 000000000..a57a8a56e --- /dev/null +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/data/IDhApiTerrainDataCache.java @@ -0,0 +1,21 @@ +package com.seibel.distanthorizons.api.interfaces.data; + +/** + * Can be used to drastically speed up repeat read operations in {@link IDhApiTerrainDataRepo}. + * + * @see IDhApiTerrainDataRepo + * + * @author James Seibel + * @version 2024-7-14 + * @since API 3.0.0 + */ +public interface IDhApiTerrainDataCache +{ + /** + * Removes any data that's currently stored in this cache. + * This cane be done to free up memory or invalidate + * the cache so fresh data can be pulled in. + */ + void clear(); + +} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/data/IDhApiTerrainDataRepo.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/data/IDhApiTerrainDataRepo.java index 2a4f81e24..45f8c1d72 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/data/IDhApiTerrainDataRepo.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/data/IDhApiTerrainDataRepo.java @@ -29,6 +29,8 @@ import com.seibel.distanthorizons.api.objects.data.DhApiTerrainDataPoint; /** * Used to interface with Distant Horizons' terrain data. * + * @see IDhApiTerrainDataCache + * * @author James Seibel * @version 2023-6-22 * @since API 1.0.0 @@ -40,29 +42,50 @@ public interface IDhApiTerrainDataRepo // getters // //=========// - /** Returns the terrain datapoint at the given block position, at/or containing the given Y position. */ - DhApiResult getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ); - /** Returns every datapoint in the column located at the given block X and Z position top to bottom. */ - DhApiResult getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ); + /** @see IDhApiTerrainDataRepo#getSingleDataPointAtBlockPos(IDhApiLevelWrapper, int, int, int, IDhApiTerrainDataCache) */ + default DhApiResult getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ) { return this.getSingleDataPointAtBlockPos(levelWrapper, blockPosX, blockPosY, blockPosZ, null); } + /** + * Returns the terrain datapoint at the given block position, at/or containing the given Y position. + * @since API 3.0.0 + */ + DhApiResult getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ, IDhApiTerrainDataCache dataCache); + /** @see IDhApiTerrainDataRepo#getColumnDataAtBlockPos(IDhApiLevelWrapper, int, int, IDhApiTerrainDataCache) */ + default DhApiResult getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ) { return this.getColumnDataAtBlockPos(levelWrapper, blockPosX, blockPosZ, null); } + /** + * Returns every datapoint in the column located at the given block X and Z position top to bottom. + * @since API 3.0.0 + */ + DhApiResult getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ, IDhApiTerrainDataCache dataCache); + + /** @see IDhApiTerrainDataRepo#getAllTerrainDataAtChunkPos(IDhApiLevelWrapper, int, int, IDhApiTerrainDataCache) */ + default DhApiResult getAllTerrainDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ) { return this.getAllTerrainDataAtChunkPos(levelWrapper, chunkPosX, chunkPosZ, null); } /** * Returns every datapoint in the given chunk's X and Z position.

* * The returned array is ordered: [relativeBlockX][relativeBlockZ][columnIndex]
* RelativeBlockX/Z are relative to the block position closest to negative infinity in the chunk's position.
* The column data is ordered from top to bottom. Note: each column may have a different number of values.
+ * + * @since API 3.0.0 */ - DhApiResult getAllTerrainDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ); + DhApiResult getAllTerrainDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ, IDhApiTerrainDataCache dataCache); + /** @see IDhApiTerrainDataRepo#getAllTerrainDataAtRegionPos(IDhApiLevelWrapper, int, int, IDhApiTerrainDataCache) */ + default DhApiResult getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ) { return this.getAllTerrainDataAtRegionPos(levelWrapper, regionPosX, regionPosZ, null); } /** * Returns every datapoint in the given region's X and Z position.

* * The returned array is ordered: [relativeBlockX][relativeBlockZ][columnIndex]
* RelativeBlockX/Z are relative to the block position closest to negative infinity in the region's position.
* The column data is ordered from top to bottom. Note: each column may have a different number of values.
+ * + * @since API 3.0.0 */ - DhApiResult getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ); + DhApiResult getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ, IDhApiTerrainDataCache dataCache); + /** @see IDhApiTerrainDataRepo#getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper, byte, int, int, IDhApiTerrainDataCache) */ + default DhApiResult getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ) { return this.getAllTerrainDataAtDetailLevelAndPos(levelWrapper, detailLevel, posX, posZ, null); } /** * Returns every datapoint in the column located at the given detail level and X/Z position.
* This can be used to return terrain data for non-standard sizes (IE 2x2 blocks or 2x2 chunks). @@ -71,20 +94,42 @@ public interface IDhApiTerrainDataRepo * Every increase doubles the width of the returned area.
* Example values: 0 = block, 1 = 2x2 blocks, 2 = 4x4 blocks, ... 4 = chunk (16x16 blocks), ... 9 = region (512x512 blocks)
* See {@link EDhApiDetailLevel} for more information. + * + * @since API 3.0.0 */ - DhApiResult getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ); + DhApiResult getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ, IDhApiTerrainDataCache dataCache); + + + + /** @see IDhApiTerrainDataRepo#raycast(IDhApiLevelWrapper, double, double, double, float, float, float, int, IDhApiTerrainDataCache) */ + default DhApiResult raycast( + IDhApiLevelWrapper levelWrapper, + double rayOriginX, double rayOriginY, double rayOriginZ, + float rayDirectionX, float rayDirectionY, float rayDirectionZ, + int maxRayBlockLength) + { + return this.raycast( + levelWrapper, + rayOriginX, rayOriginY, rayOriginZ, + rayDirectionX, rayDirectionY, rayDirectionZ, + maxRayBlockLength, + null); + } /** * Returns the datapoint and position of the LOD * at the end of the given ray.

* * Will return "success" with a null datapoint if the ray reaches the max length without finding any data. + * + * @since API 3.0.0 */ DhApiResult raycast( IDhApiLevelWrapper levelWrapper, double rayOriginX, double rayOriginY, double rayOriginZ, float rayDirectionX, float rayDirectionY, float rayDirectionZ, - int maxRayBlockLength); + int maxRayBlockLength, + IDhApiTerrainDataCache dataCache); @@ -98,15 +143,27 @@ public interface IDhApiTerrainDataRepo * Notes:
* - Only works if the given {@link IDhApiLevelWrapper} points to a loaded level.
* - If the player travels to this chunk, or the chunk is updated in some other way; your data will be replaced - * by whatever the current chunk is.
- * - This method may not update the LOD data immediately. Any other chunks have - * been queued to update, they will be handled first. + * by whatever the current chunk is.
+ * - This method may not update the LOD data immediately. Any other chunks that have + * been queued to update will be handled first. * * @param levelWrapper the level wrapper that the chunk should be saved to. * @param chunkObjectArray see {@link IDhApiWorldGenerator#generateChunks} for what objects are expected. * @throws ClassCastException if chunkObjectArray doesn't contain the right objects. * The exception will contain the expected object(s). */ - public DhApiResult overwriteChunkDataAsync(IDhApiLevelWrapper levelWrapper, Object[] chunkObjectArray) throws ClassCastException; + DhApiResult overwriteChunkDataAsync(IDhApiLevelWrapper levelWrapper, Object[] chunkObjectArray) throws ClassCastException; + + + + //=========// + // helpers // + //=========// + + /** + * @return a {@link IDhApiTerrainDataCache} backed by {@link java.lang.ref.SoftReference}'s. + * @since API 3.0.0 + */ + IDhApiTerrainDataCache getSoftCache(); } diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/factories/IDhApiWrapperFactory.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/factories/IDhApiWrapperFactory.java index 524742f04..ce3e70662 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/factories/IDhApiWrapperFactory.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/factories/IDhApiWrapperFactory.java @@ -79,11 +79,34 @@ public interface IDhApiWrapperFactory - ///** - // * Specifically designed to be used with the API. - // * - // * @throws ClassCastException with instructions on expected objects if the object couldn't be cast - // */ - //IChunkWrapper createChunkWrapper(Object[] objectArray) throws ClassCastException; + /** + * Constructs a {@link IDhApiBiomeWrapper} for use by other DhApi methods. + * + * @param resourceLocationString example: "minecraft:plains" + * + * @param levelWrapper Expects a {@link IDhApiLevelWrapper} returned by one of DH's {@link DhApi.Delayed#worldProxy} methods.
+ * A custom implementation of {@link IDhApiLevelWrapper} will not be accepted. + * + * @throws IOException if the resourceLocationString wasn't able to be parsed or converted into a valid {@link IDhApiBiomeWrapper} + * @throws ClassCastException if the wrong levelWrapper type was given + * + * @since API 3.0.0 + */ + IDhApiBiomeWrapper getBiomeWrapper(String resourceLocationString, IDhApiLevelWrapper levelWrapper) throws IOException, ClassCastException; + + /** + * Constructs a {@link IDhApiBlockStateWrapper} for use by other DhApi methods. + * This returns the default blockstate for the given resource location. + * + * @param resourceLocationString examples: "minecraft:bedrock", "minecraft:stone", "minecraft:grass_block" + * @param levelWrapper Expects a {@link IDhApiBlockStateWrapper} returned by one of DH's {@link DhApi.Delayed#worldProxy} methods.
+ * A custom implementation of {@link IDhApiBlockStateWrapper} will not be accepted. + * + * @throws IOException if the resourceLocationString wasn't able to be parsed or converted into a valid {@link IDhApiBlockStateWrapper} + * @throws ClassCastException if the wrong levelWrapper type was given + * + * @since API 3.0.0 + */ + IDhApiBlockStateWrapper getDefaultBlockStateWrapper(String resourceLocationString, IDhApiLevelWrapper levelWrapper) throws IOException, ClassCastException; } diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiCullingFrustum.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiCullingFrustum.java index 44106b82a..33cebacd2 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiCullingFrustum.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiCullingFrustum.java @@ -21,7 +21,7 @@ package com.seibel.distanthorizons.api.interfaces.override.rendering; import com.seibel.distanthorizons.api.enums.EDhApiDetailLevel; import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable; -import com.seibel.distanthorizons.coreapi.util.math.Mat4f; +import com.seibel.distanthorizons.api.objects.math.DhApiMat4f; /** * Used to determine if a LOD should be rendered or is outside the @@ -41,7 +41,7 @@ public interface IDhApiCullingFrustum extends IDhApiOverrideable * @param worldMaxBlockY the highest block position this level allows. * @param worldViewProjection the projection matrix used in this render pass. */ - void update(int worldMinBlockY, int worldMaxBlockY, Mat4f worldViewProjection); + void update(int worldMinBlockY, int worldMaxBlockY, DhApiMat4f worldViewProjection); /** * returns true if the LOD bounds intersect this frustum diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiGenericObjectShaderProgram.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiGenericObjectShaderProgram.java new file mode 100644 index 000000000..ce1d6fcde --- /dev/null +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiGenericObjectShaderProgram.java @@ -0,0 +1,80 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.api.interfaces.override.rendering; + +import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable; +import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; + +/** + * @see IDhApiShaderProgram + * + * @author James Seibel + * @version 2024-7-11 + * @since API 3.0.0 + */ +public interface IDhApiGenericObjectShaderProgram extends IDhApiOverrideable +{ + + /** + * If this method is called that means this program has the highest priority as defined by {@link IDhApiOverrideable#getPriority()} + * and gets to decide if it wants to be used to render this frame or not.

+ * + * If this method returns true then this program will be used for this frame.
+ * If this returns false then the default DH {@link IDhApiGenericObjectShaderProgram} will be used instead. + */ + boolean overrideThisFrame(); + + /** @return the OpenGL ID for this shader program */ + int getId(); + + /** Free any OpenGL objects owned by this program. */ + void free(); + + /** Runs any necessary binding this program needs so rendering can be done. */ + void bind(DhApiRenderParam renderEventParam); + /** Runs any necessary unbinding this program needs so rendering can be done by another program. */ + void unbind(); + + /** Binds the given Vertex Buffer Object to this shader program for rendering. */ + void bindVertexBuffer(int vbo); + + + /** sets up the necessary uniforms for rendering */ + void fillIndirectUniformData( + DhApiRenderParam renderParameters, + DhApiRenderableBoxGroupShading shading, IDhApiRenderableBoxGroup boxGroup, + DhApiVec3d camPos); + + /** sets up the necessary uniforms for rendering */ + void fillSharedDirectUniformData( + DhApiRenderParam renderParameters, + DhApiRenderableBoxGroupShading shading, IDhApiRenderableBoxGroup boxGroup, + DhApiVec3d camPos); + void fillDirectUniformData( + DhApiRenderParam renderParameters, + IDhApiRenderableBoxGroup boxGroup, DhApiRenderableBox box, + DhApiVec3d camPos); + + +} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiShaderProgram.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiShaderProgram.java index 6f329121d..6844715cf 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiShaderProgram.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiShaderProgram.java @@ -21,10 +21,11 @@ package com.seibel.distanthorizons.api.interfaces.override.rendering; import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; -import com.seibel.distanthorizons.coreapi.util.math.Mat4f; -import com.seibel.distanthorizons.coreapi.util.math.Vec3f; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; /** + * @see IDhApiGenericObjectShaderProgram + * * @author James Seibel * @version 2024-1-24 * @since API 2.0.0 @@ -57,7 +58,7 @@ public interface IDhApiShaderProgram extends IDhApiOverrideable void fillUniformData(DhApiRenderParam renderParameters); /** sets the vec3 that all DH verticies should be offset by when rendering */ - void setModelOffsetPos(Vec3f modelPos); + void setModelOffsetPos(DhApiVec3f modelPos); /** Binds the given Vertex Buffer Object to this shader program for rendering. */ void bindVertexBuffer(int vbo); diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiShadowCullingFrustum.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiShadowCullingFrustum.java index 5748d06d9..564c27838 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiShadowCullingFrustum.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/rendering/IDhApiShadowCullingFrustum.java @@ -19,10 +19,6 @@ package com.seibel.distanthorizons.api.interfaces.override.rendering; -import com.seibel.distanthorizons.api.enums.EDhApiDetailLevel; -import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable; -import com.seibel.distanthorizons.coreapi.util.math.Mat4f; - /** * The culling frustum used during Distant Horizons' shadow pass * if another mod has enabled Distant Horizons' shadow diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/AbstractDhApiChunkWorldGenerator.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/AbstractDhApiChunkWorldGenerator.java index e34fe2ecb..09e71d706 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/AbstractDhApiChunkWorldGenerator.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/AbstractDhApiChunkWorldGenerator.java @@ -22,6 +22,8 @@ package com.seibel.distanthorizons.api.interfaces.override.worldGenerator; import com.seibel.distanthorizons.api.enums.EDhApiDetailLevel; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode; import com.seibel.distanthorizons.api.interfaces.override.IDhApiOverrideable; +import com.seibel.distanthorizons.api.objects.data.DhApiChunk; +import com.seibel.distanthorizons.api.objects.data.DhApiTerrainDataPoint; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import java.io.Closeable; @@ -77,13 +79,41 @@ public abstract class AbstractDhApiChunkWorldGenerator implements Closeable, IDh }, worldGeneratorThreadPool); } + @Override + public final CompletableFuture generateApiChunks( + int chunkPosMinX, + int chunkPosMinZ, + byte granularity, + byte targetDataDetail, + EDhApiDistantGeneratorMode generatorMode, + ExecutorService worldGeneratorThreadPool, + Consumer resultConsumer + ) + { + return CompletableFuture.runAsync(() -> + { + // TODO what does this mean? + int genChunkWidth = BitShiftUtil.powerOfTwo(granularity - 4); + + for (int chunkX = chunkPosMinX; chunkX < chunkPosMinX + genChunkWidth; chunkX++) + { + for (int chunkZ = chunkPosMinZ; chunkZ < chunkPosMinZ + genChunkWidth; chunkZ++) + { + DhApiChunk apiChunk = this.generateApiChunk(chunkX, chunkZ, generatorMode); + resultConsumer.accept(apiChunk); + } + } + }, worldGeneratorThreadPool); + } + + /** * This method is called to generate terrain over a given area * from a thread defined by Distant Horizons.

* * @param chunkPosX the chunk X position in the level (not to be confused with the chunk's BlockPos in the level) * @param chunkPosZ the chunk Z position in the level (not to be confused with the chunk's BlockPos in the level) - * @param generatorMode how far into the world gen pipeline this method run. See {@link EDhApiDistantGeneratorMode} for additional documentation. + * @param generatorMode how far into the world gen pipeline this method should run. See {@link EDhApiDistantGeneratorMode} for additional documentation. * * @return See {@link IDhApiWorldGenerator#generateChunks(int, int, byte, byte, EDhApiDistantGeneratorMode, ExecutorService, Consumer) IDhApiWorldGenerator.generateChunks} * for the list of Object's this method should return along with additional documentation. @@ -92,4 +122,21 @@ public abstract class AbstractDhApiChunkWorldGenerator implements Closeable, IDh */ public abstract Object[] generateChunk(int chunkPosX, int chunkPosZ, EDhApiDistantGeneratorMode generatorMode); + /** + * This method is called to generate terrain over a given area + * from a thread defined by Distant Horizons.

+ * + * @param chunkPosX the chunk X position in the level (not to be confused with the chunk's BlockPos in the level) + * @param chunkPosZ the chunk Z position in the level (not to be confused with the chunk's BlockPos in the level) + * @param generatorMode how far into the world gen pipeline this method should run. See {@link EDhApiDistantGeneratorMode} for additional documentation. + * + * @return A {@link DhApiChunk} with the generated {@link DhApiTerrainDataPoint} including air blocks. + * Note: if air blocks aren't included with the proper lighting, lower detail levels will appear as black/unlit. + * + * @see IDhApiWorldGenerator#generateApiChunks(int, int, byte, byte, EDhApiDistantGeneratorMode, ExecutorService, Consumer) + * + * @since API 3.0.0 + */ + public abstract DhApiChunk generateApiChunk(int chunkPosX, int chunkPosZ, EDhApiDistantGeneratorMode generatorMode); + } diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/IDhApiWorldGenerator.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/IDhApiWorldGenerator.java index eb1831973..7861040cc 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/IDhApiWorldGenerator.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/IDhApiWorldGenerator.java @@ -154,6 +154,7 @@ public interface IDhApiWorldGenerator extends Closeable, IDhApiOverrideable * * After the {@link DhApiChunk} has been generated, it should be passed into the * resultConsumer's {@link Consumer#accept(Object)} method. + * Note: if air blocks aren't included in the with the {@link DhApiChunk} with proper lighting, lower detail levels will appear as black/unlit. * * @implNote the default implementation of this method throws an {@link UnsupportedOperationException}, * and must be overridden when {@link #getReturnType()} returns {@link EDhApiWorldGeneratorReturnType#API_CHUNKS}. diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiCustomRenderObjectFactory.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiCustomRenderObjectFactory.java new file mode 100644 index 000000000..334c3edd1 --- /dev/null +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiCustomRenderObjectFactory.java @@ -0,0 +1,67 @@ +package com.seibel.distanthorizons.api.interfaces.render; + +import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; + +import java.util.List; + +/** + * Handles creating + * {@link IDhApiRenderableBoxGroup} objects, + * which can be added via a {@link IDhApiCustomRenderRegister}. + * + * @see IDhApiCustomRenderRegister + * @see IDhApiRenderableBoxGroup + * + * @author James Seibel + * @version 2024-7-3 + * @since API 3.0.0 + */ +public interface IDhApiCustomRenderObjectFactory +{ + /** + * Creates a {@link IDhApiRenderableBoxGroup} from for the given {@link DhApiRenderableBox} + * where the box is positioned relative to the level's origin. + * + * @param resourceLocation A colon separated Resource Location string, similar to vanilla Minecraft, for example: "DistantHorizons:Clouds" + * + * @see DhApiRenderableBox + * @see IDhApiRenderableBoxGroup#getResourceLocationNamespace() + * @see IDhApiRenderableBoxGroup#getResourceLocationPath() + * + * @throws IllegalArgumentException if resourceLocation is null, isn't separated by a colon, or has multiple colons. + */ + IDhApiRenderableBoxGroup createForSingleBox(String resourceLocation, DhApiRenderableBox cube) throws IllegalArgumentException; + + /** + * Creates a {@link IDhApiRenderableBoxGroup} from the given list of {@link DhApiRenderableBox} where each + * one is positioned relative to given originBlockPos, which in turn is relative to the level's origin. + * + * @param resourceLocation A colon separated Resource Location string, similar to vanilla Minecraft, for example: "DistantHorizons:Clouds" + * @param originBlockPos The starting position for this {@link IDhApiRenderableBoxGroup}, can be changed during runtime. + * + * + * @see DhApiRenderableBox + * @see IDhApiRenderableBoxGroup#getResourceLocationNamespace() + * @see IDhApiRenderableBoxGroup#getResourceLocationPath() + * + * @throws IllegalArgumentException if resourceLocation is null, isn't separated by a colon, or has multiple colons. + */ + IDhApiRenderableBoxGroup createRelativePositionedGroup(String resourceLocation, DhApiVec3d originBlockPos, List cubeList); + + /** + * Creates a {@link IDhApiRenderableBoxGroup} from the given list of {@link DhApiRenderableBox} where each + * one is positioned relative to the level's origin. + * + * @param resourceLocation A colon separated Resource Location string, similar to vanilla Minecraft, for example: "DistantHorizons:Clouds" + * + * @see DhApiRenderableBox + * @see IDhApiRenderableBoxGroup#getResourceLocationNamespace() + * @see IDhApiRenderableBoxGroup#getResourceLocationPath() + * + * @throws IllegalArgumentException if resourceLocation is null, isn't separated by a colon, or has multiple colons. + */ + IDhApiRenderableBoxGroup createAbsolutePositionedGroup(String resourceLocation, List cubeList); + +} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiCustomRenderRegister.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiCustomRenderRegister.java new file mode 100644 index 000000000..c156c0c8c --- /dev/null +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiCustomRenderRegister.java @@ -0,0 +1,30 @@ +package com.seibel.distanthorizons.api.interfaces.render; + +import com.seibel.distanthorizons.api.DhApi; +import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper; +import com.seibel.distanthorizons.api.interfaces.world.IDhApiWorldProxy; + +/** + * Handles adding and removing + * {@link IDhApiRenderableBoxGroup} objects, + * from DH's renderer.

+ * + * Can be accessed in + * {@link DhApi.Delayed#worldProxy} -> {@link IDhApiLevelWrapper}. + * + * @see IDhApiCustomRenderObjectFactory + * @see IDhApiRenderableBoxGroup + * @see IDhApiWorldProxy + * @see IDhApiLevelWrapper + * + * @author James Seibel + * @version 2024-7-3 + * @since API 3.0.0 + */ +public interface IDhApiCustomRenderRegister +{ + void add(IDhApiRenderableBoxGroup cubeGroup) throws IllegalArgumentException; + + IDhApiRenderableBoxGroup remove(long id); + +} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiRenderableBoxGroup.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiRenderableBoxGroup.java new file mode 100644 index 000000000..399ba1571 --- /dev/null +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/render/IDhApiRenderableBoxGroup.java @@ -0,0 +1,98 @@ +package com.seibel.distanthorizons.api.interfaces.render; + +import com.seibel.distanthorizons.api.enums.config.EDhApiLodShading; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3i; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; + +import java.util.List; +import java.util.function.Consumer; + +/** + * A list of {@link DhApiRenderableBox}'s that + * can be rendered to DH's terrain pass. + * + * @see DhApiRenderableBox + * + * @author James Seibel + * @version 2024-6-30 + * @since API 3.0.0 + */ +public interface IDhApiRenderableBoxGroup extends List +{ + /** + * A unique numerical ID used by DH during rendering. + * This can also be used to bind/unbind specific {@link IDhApiRenderableBoxGroup}'s from the renderer. + * @return the ID for this specific group + */ + long getId(); + + /** + * Used to determine which mods have added what to the DH renderer. + * This can be used both by the F3 pie chart so you as a mod developer can profile your code + * or by shader developers who want to render your objects differently.

+ * + * Should be used the same as a vanilla Minecraft ResourceLocation. + * For example if your mod named "Heavy Thunder" adds additional clouds named "Storm Front", + * your Resource Location would be something like "HeavyThunder:StormFront" + * and this method would return "HeavyThunder". + */ + String getResourceLocationNamespace(); + /** + * Used to determine what type of object mods have added what to the DH renderer. + * This can be used both by the F3 pie chart so you as a mod developer can profile your code + * or by shader developers who want to render your objects differently.

+ * + * Should be used the same as a vanilla Minecraft ResourceLocation. + * For example if your mod named "Heavy Thunder" adds additional clouds named "Storm Front", + * your Resource Location would be something like "HeavyThunder:StormFront" + * and this method would return "StormFront". + */ + String getResourceLocationPath(); + + /** Sets whether this group should render or not. */ + void setActive(boolean active); + /** @return if active this group will render. */ + boolean isActive(); + + /** Sets whether this group should render with Screen Space Ambient Occlusioning. */ + void setSsaoEnabled(boolean ssaoEnabled); + /** @return if active this group will render with Screen Space Ambient Occlusioning. */ + boolean isSsaoEnabled(); + + /** Sets where this group will render in the level. */ + void setOriginBlockPos(DhApiVec3d pos); + /** @return the block position in the level that all {@see DhApiRenderableBox} will render relative to. */ + DhApiVec3d getOriginBlockPos(); + + /** + * Called right before this group is rendered.
+ * This is a good place to change the origin or notify of any box changes. + */ + void setPreRenderFunc(Consumer renderEventParam); + void setPostRenderFunc(Consumer renderEventParam); // TODO name? + + /** + * If a cube's color, position, or other property is changed this method + * must be called for those changes to render.

+ * + * Note: changing the group's position via {@link #setOriginBlockPos} doesn't + * require calling this method. + */ + void triggerBoxChange(); + + /** Only accepts values between 0 and 15 */ + void setSkyLight(int skyLight); + int getSkyLight(); + + /** Only accepts values between 0 and 15 */ + void setBlockLight(int blockLight); + int getBlockLight(); + + void setShading(DhApiRenderableBoxGroupShading shading); + DhApiRenderableBoxGroupShading getShading(); + +} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/util/IDhApiCopyable.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/util/IDhApiCopyable.java new file mode 100644 index 000000000..4a86357ef --- /dev/null +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/util/IDhApiCopyable.java @@ -0,0 +1,18 @@ +package com.seibel.distanthorizons.api.interfaces.util; + +/** + * Used for objects that need deep clones.
+ * Replacement for {@link Cloneable}. + * + * @see Cloneable + * + * @author James Seibel + * @version 2024-7-12 + * @since API 3.0.0 + */ +public interface IDhApiCopyable +{ + /** Returns a deep clone of all parameters whenever possible. */ + IDhApiCopyable copy(); + +} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java index b846f1880..1c159e746 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java @@ -21,6 +21,7 @@ package com.seibel.distanthorizons.api.interfaces.world; import com.seibel.distanthorizons.api.interfaces.IDhApiUnsafeWrapper; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType; +import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister; /** * Can be either a Server or Client level.
@@ -43,7 +44,7 @@ public interface IDhApiLevelWrapper extends IDhApiUnsafeWrapper boolean hasSkyLight(); /** Returns the max block height of the level(?) */ - int getHeight(); + int getMaxHeight(); /** * Returns the lowest possible block position for the level.
@@ -51,4 +52,10 @@ public interface IDhApiLevelWrapper extends IDhApiUnsafeWrapper */ default int getMinHeight() { return 0; } + /** + * Will return null if called on the server, + * or if called before the renderer has been set up. + */ + IDhApiCustomRenderRegister getRenderRegister(); + } \ No newline at end of file diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiAfterRenderEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiAfterRenderEvent.java index 05da17b96..7bc6dab51 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiAfterRenderEvent.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiAfterRenderEvent.java @@ -26,17 +26,20 @@ import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhAp /** * Fired after Distant Horizons finishes rendering a frame.
* At this point DH will have also finished cleaning up any modifications it - * did to the OpenGL state, so the state should be back to Minecraft's defaults. + * did to the OpenGL state, so the state should be back to Minecraft's defaults.

* + * Note: as of API v 3.0.0 no {@link DhApiRenderParam} is included in this event + * because the specific parameters may change + * depending on whether deferred rendering is enabled or not. + * * @author James Seibel - * @version 2024-1-31 - * @see DhApiRenderParam + * @version 2024-7-14 * @since API 1.0.0 */ -public abstract class DhApiAfterRenderEvent implements IDhApiEvent +public abstract class DhApiAfterRenderEvent implements IDhApiEvent { /** Fired after Distant Horizons finishes rendering fake chunks. */ - public abstract void afterRender(DhApiEventParam event); + public abstract void afterRender(DhApiEventParam event); //=========================// @@ -44,6 +47,6 @@ public abstract class DhApiAfterRenderEvent implements IDhApiEvent event) { this.afterRender(event); } + public final void fireEvent(DhApiEventParam event) { this.afterRender(event); } } \ No newline at end of file diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeBufferRenderEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeBufferRenderEvent.java index 7716fda0c..01b8fcaa1 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeBufferRenderEvent.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeBufferRenderEvent.java @@ -20,9 +20,10 @@ package com.seibel.distanthorizons.api.methods.events.abstractEvents; import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; +import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; -import com.seibel.distanthorizons.coreapi.util.math.Vec3f; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; /** * Called before Distant Horizons starts rendering a buffer.
@@ -52,20 +53,29 @@ public abstract class DhApiBeforeBufferRenderEvent implements IDhApiEvent. + */ + +package com.seibel.distanthorizons.api.methods.events.abstractEvents; + +import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; +import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper; +import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiCancelableEvent; +import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiCancelableEventParam; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; + +/** + * Called before Distant Horizons starts rendering a generic object.
+ * Canceling this event will prevent the triggering {@link IDhApiRenderableBoxGroup} from rendering this frame. + * + * @author James Seibel + * @version 2024-7-11 + * @since API 3.0.0 + */ +public abstract class DhApiBeforeGenericObjectRenderEvent implements IDhApiCancelableEvent +{ + /** Fired before Distant Horizons renders a generic object. */ + public abstract void beforeRender(DhApiCancelableEventParam event); + + + //=========================// + // internal DH API methods // + //=========================// + + @Override + public final void fireEvent(DhApiCancelableEventParam input) { this.beforeRender(input); } + + + //==================// + // parameter object // + //==================// + + public static class EventParam extends DhApiRenderParam implements IDhApiEventParam + { + public final long boxGroupId; + public final String resourceLocationNamespace; + public final String resourceLocationPath; + + + public EventParam( + DhApiRenderParam renderParam, + IDhApiRenderableBoxGroup boxGroup + ) + { + super(renderParam); + + this.boxGroupId = boxGroup.getId(); + this.resourceLocationNamespace = boxGroup.getResourceLocationNamespace(); + this.resourceLocationPath = boxGroup.getResourceLocationPath(); + } + public EventParam( + DhApiRenderParam renderParam, + long boxGroupId, String resourceLocationNamespace, String resourceLocationPath + ) + { + super(renderParam); + + this.boxGroupId = boxGroupId; + this.resourceLocationNamespace = resourceLocationNamespace; + this.resourceLocationPath = resourceLocationPath; + } + + + + @Override + public EventParam copy() + { + return new EventParam( + this, + this.boxGroupId, this.resourceLocationNamespace, this.resourceLocationPath + ); + } + } + +} \ No newline at end of file diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeGenericRenderCleanupEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeGenericRenderCleanupEvent.java new file mode 100644 index 000000000..e9c6bfbd4 --- /dev/null +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeGenericRenderCleanupEvent.java @@ -0,0 +1,47 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.api.methods.events.abstractEvents; + +import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; + +/** + * Called before Distant Horizons starts the cleanup process done after rendering generic objects.
+ * This is called after all generic objects have finished rendering. + * + * @author James Seibel + * @version 2024-7-13 + * @since API 3.0.0 + */ +public abstract class DhApiBeforeGenericRenderCleanupEvent implements IDhApiEvent +{ + /** Fired before Distant Horizons starts the cleanup process once rendering has finished. */ + public abstract void beforeCleanup(DhApiEventParam event); + + + //=========================// + // internal DH API methods // + //=========================// + + @Override + public final void fireEvent(DhApiEventParam event) { this.beforeCleanup(event); } + +} \ No newline at end of file diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeGenericRenderSetupEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeGenericRenderSetupEvent.java new file mode 100644 index 000000000..2dea2942c --- /dev/null +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeGenericRenderSetupEvent.java @@ -0,0 +1,50 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.api.methods.events.abstractEvents; + +import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; + +/** + * Called before Distant Horizons has started setting up OpenGL objects for rendering generic objects.
+ * If you want to modify already bound DH OpenGL objects try using {@link DhApiBeforeGenericObjectRenderEvent}. + * + * @author James Seibel + * @version 2024-7-12 + * @since API 3.0.0 + * + * @see DhApiBeforeGenericObjectRenderEvent + */ +public abstract class DhApiBeforeGenericRenderSetupEvent implements IDhApiEvent +{ + /** Fired before Distant Horizons has started setting up OpenGL objects for rendering generic objects. */ + public abstract void beforeSetup(DhApiEventParam input); + + + //=========================// + // internal DH API methods // + //=========================// + + @Override + public final void fireEvent(DhApiEventParam input) { this.beforeSetup(input); } + + +} \ No newline at end of file diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeRenderCleanupEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeRenderCleanupEvent.java index b8eb08568..85533fa35 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeRenderCleanupEvent.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiBeforeRenderCleanupEvent.java @@ -33,7 +33,7 @@ import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhAp */ public abstract class DhApiBeforeRenderCleanupEvent implements IDhApiEvent { - /** Fired before Distant Horizons renders LODs. */ + /** Fired before Distant Horizons starts the cleanup process once rendering has finished. */ public abstract void beforeCleanup(DhApiEventParam event); diff --git a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiChunkModifiedEvent.java b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiChunkModifiedEvent.java index 6afe312cf..abc7bd0b7 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiChunkModifiedEvent.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/methods/events/abstractEvents/DhApiChunkModifiedEvent.java @@ -22,6 +22,7 @@ package com.seibel.distanthorizons.api.methods.events.abstractEvents; import com.seibel.distanthorizons.api.interfaces.data.IDhApiTerrainDataRepo; import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper; import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; +import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; /** @@ -53,7 +54,7 @@ public abstract class DhApiChunkModifiedEvent implements IDhApiEvent> dataPoints; @@ -52,12 +52,12 @@ public class DhApiChunk // constructors // //==============// - public DhApiChunk(int chunkPosX, int chunkPosZ, int topYBlockPos, int bottomYBlockPos) + public DhApiChunk(int chunkPosX, int chunkPosZ, int bottomYBlockPos, int topYBlockPos) { this.chunkPosX = chunkPosX; this.chunkPosZ = chunkPosZ; - this.topYBlockPos = topYBlockPos; this.bottomYBlockPos = bottomYBlockPos; + this.topYBlockPos = topYBlockPos; // populate the array to prevent null pointers this.dataPoints = new ArrayList<>(16 * 16); // 256 diff --git a/api/src/main/java/com/seibel/distanthorizons/api/objects/data/DhApiRaycastResult.java b/api/src/main/java/com/seibel/distanthorizons/api/objects/data/DhApiRaycastResult.java index c54866bf0..ad3f182d2 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/objects/data/DhApiRaycastResult.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/objects/data/DhApiRaycastResult.java @@ -20,7 +20,6 @@ package com.seibel.distanthorizons.api.objects.data; import com.seibel.distanthorizons.api.objects.math.DhApiVec3i; -import com.seibel.distanthorizons.coreapi.util.math.Vec3i; /** * Holds a single datapoint of terrain data @@ -47,7 +46,7 @@ public class DhApiRaycastResult - public DhApiRaycastResult(DhApiTerrainDataPoint dataPoint, Vec3i blockPos) + public DhApiRaycastResult(DhApiTerrainDataPoint dataPoint, DhApiVec3i blockPos) { this.dataPoint = dataPoint; this.pos = blockPos; diff --git a/api/src/main/java/com/seibel/distanthorizons/api/objects/data/DhApiTerrainDataPoint.java b/api/src/main/java/com/seibel/distanthorizons/api/objects/data/DhApiTerrainDataPoint.java index 0c70dfbfb..ab2cb9f01 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/objects/data/DhApiTerrainDataPoint.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/objects/data/DhApiTerrainDataPoint.java @@ -26,7 +26,7 @@ import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper; * Holds a single datapoint of terrain data. * * @author James Seibel - * @version 2022-11-13 + * @version 2024-7-20 * @since API 1.0.0 */ public class DhApiTerrainDataPoint @@ -42,22 +42,22 @@ public class DhApiTerrainDataPoint public final int blockLightLevel; public final int skyLightLevel; - public final int topYBlockPos; public final int bottomYBlockPos; + public final int topYBlockPos; public final IDhApiBlockStateWrapper blockStateWrapper; public final IDhApiBiomeWrapper biomeWrapper; - public DhApiTerrainDataPoint(byte detailLevel, int blockLightLevel, int skyLightLevel, int topYBlockPos, int bottomYBlockPos, IDhApiBlockStateWrapper blockStateWrapper, IDhApiBiomeWrapper biomeWrapper) + public DhApiTerrainDataPoint(byte detailLevel, int blockLightLevel, int skyLightLevel, int bottomYBlockPos, int topYBlockPos, IDhApiBlockStateWrapper blockStateWrapper, IDhApiBiomeWrapper biomeWrapper) { this.detailLevel = detailLevel; this.blockLightLevel = blockLightLevel; this.skyLightLevel = skyLightLevel; - this.topYBlockPos = topYBlockPos; this.bottomYBlockPos = bottomYBlockPos; + this.topYBlockPos = topYBlockPos; this.blockStateWrapper = blockStateWrapper; this.biomeWrapper = biomeWrapper; diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Mat4f.java b/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiMat4f.java similarity index 57% rename from api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Mat4f.java rename to api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiMat4f.java index 9d24e9f41..2c05ae708 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Mat4f.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiMat4f.java @@ -17,46 +17,55 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.coreapi.util.math; +package com.seibel.distanthorizons.api.objects.math; -import org.joml.Matrix4f; -import org.joml.Matrix4fc; - -import java.nio.FloatBuffer; +import com.seibel.distanthorizons.api.interfaces.util.IDhApiCopyable; /** * An (almost) exact copy of Minecraft's 1.16.5 - * implementation of a 4x4 float matrix. + * implementation of a 4x4 float matrix.

* + * + * m00, m10, m20, m30,
+ * m01, m11, m21, m31,
+ * m02, m12, m22, m32,
+ * m03, m13, m23, m33
+ *
+ * * @author James Seibel - * @version 11-11-2021 + * @version 2024-6-30 */ -public class Mat4f +public class DhApiMat4f implements IDhApiCopyable { - private float m00; - private float m01; - private float m02; - private float m03; - private float m10; - private float m11; - private float m12; - private float m13; - private float m20; - private float m21; - private float m22; - private float m23; - private float m30; - private float m31; - private float m32; - private float m33; + public float m00; + public float m01; + public float m02; + public float m03; + + public float m10; + public float m11; + public float m12; + public float m13; + + public float m20; + public float m21; + public float m22; + public float m23; + + public float m30; + public float m31; + public float m32; + public float m33; - public Mat4f() - { - - } - public Mat4f(Mat4f sourceMatrix) + //==============// + // constructors // + //==============// + + public DhApiMat4f() { /* all values are 0 */ } + + public DhApiMat4f(DhApiMat4f sourceMatrix) { this.m00 = sourceMatrix.m00; this.m01 = sourceMatrix.m01; @@ -76,163 +85,36 @@ public class Mat4f this.m33 = sourceMatrix.m33; } - public Mat4f(Matrix4fc sourceMatrix) { this(convertJomlMatrixToArray(sourceMatrix)); } - private static float[] convertJomlMatrixToArray(Matrix4fc sourceMatrix) + /** Expects the values of the input array to be in row major order (AKA rows then columns) */ + public DhApiMat4f(float[] values) { - FloatBuffer buffer = FloatBuffer.allocate(16); + m00 = values[0]; + m01 = values[1]; + m02 = values[2]; + m03 = values[3]; - buffer.put(bufferIndex(0, 0), sourceMatrix.m00()); - buffer.put(bufferIndex(0, 1), sourceMatrix.m01()); - buffer.put(bufferIndex(0, 2), sourceMatrix.m02()); - buffer.put(bufferIndex(0, 3), sourceMatrix.m03()); - buffer.put(bufferIndex(1, 0), sourceMatrix.m10()); - buffer.put(bufferIndex(1, 1), sourceMatrix.m11()); - buffer.put(bufferIndex(1, 2), sourceMatrix.m12()); - buffer.put(bufferIndex(1, 3), sourceMatrix.m13()); - buffer.put(bufferIndex(2, 0), sourceMatrix.m20()); - buffer.put(bufferIndex(2, 1), sourceMatrix.m21()); - buffer.put(bufferIndex(2, 2), sourceMatrix.m22()); - buffer.put(bufferIndex(2, 3), sourceMatrix.m23()); - buffer.put(bufferIndex(3, 0), sourceMatrix.m30()); - buffer.put(bufferIndex(3, 1), sourceMatrix.m31()); - buffer.put(bufferIndex(3, 2), sourceMatrix.m32()); - buffer.put(bufferIndex(3, 3), sourceMatrix.m33()); + m10 = values[4]; + m11 = values[5]; + m12 = values[6]; + m13 = values[7]; - return buffer.array(); - } - - /* Quaternions are not currently needed/implemented - public Matrix4float(Quaternion p_i48104_1_) - { - float f = p_i48104_1_.i(); - float f1 = p_i48104_1_.j(); - float f2 = p_i48104_1_.k(); - float f3 = p_i48104_1_.r(); - float f4 = 2.0F * f * f; - float f5 = 2.0F * f1 * f1; - float f6 = 2.0F * f2 * f2; - this.m00 = 1.0F - f5 - f6; - this.m11 = 1.0F - f6 - f4; - this.m22 = 1.0F - f4 - f5; - this.m33 = 1.0F; - float f7 = f * f1; - float f8 = f1 * f2; - float f9 = f2 * f; - float f10 = f * f3; - float f11 = f1 * f3; - float f12 = f2 * f3; - this.m10 = 2.0F * (f7 + f12); - this.m01 = 2.0F * (f7 - f12); - this.m20 = 2.0F * (f9 - f11); - this.m02 = 2.0F * (f9 + f11); - this.m21 = 2.0F * (f8 + f10); - this.m12 = 2.0F * (f8 - f10); - } - */ - - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - else if (obj != null && this.getClass() == obj.getClass()) - { - Mat4f otherMatrix = (Mat4f) obj; - return Float.compare(otherMatrix.m00, this.m00) == 0 - && Float.compare(otherMatrix.m01, this.m01) == 0 - && Float.compare(otherMatrix.m02, this.m02) == 0 - && Float.compare(otherMatrix.m03, this.m03) == 0 - && Float.compare(otherMatrix.m10, this.m10) == 0 - && Float.compare(otherMatrix.m11, this.m11) == 0 - && Float.compare(otherMatrix.m12, this.m12) == 0 - && Float.compare(otherMatrix.m13, this.m13) == 0 - && Float.compare(otherMatrix.m20, this.m20) == 0 - && Float.compare(otherMatrix.m21, this.m21) == 0 - && Float.compare(otherMatrix.m22, this.m22) == 0 - && Float.compare(otherMatrix.m23, this.m23) == 0 - && Float.compare(otherMatrix.m30, this.m30) == 0 - && Float.compare(otherMatrix.m31, this.m31) == 0 - && Float.compare(otherMatrix.m32, this.m32) == 0 - && Float.compare(otherMatrix.m33, this.m33) == 0; - } - else - { - return false; - } - } - - @Override - public int hashCode() - { - int i = this.m00 != 0.0F ? Float.floatToIntBits(this.m00) : 0; - i = 31 * i + (this.m01 != 0.0F ? Float.floatToIntBits(this.m01) : 0); - i = 31 * i + (this.m02 != 0.0F ? Float.floatToIntBits(this.m02) : 0); - i = 31 * i + (this.m03 != 0.0F ? Float.floatToIntBits(this.m03) : 0); - i = 31 * i + (this.m10 != 0.0F ? Float.floatToIntBits(this.m10) : 0); - i = 31 * i + (this.m11 != 0.0F ? Float.floatToIntBits(this.m11) : 0); - i = 31 * i + (this.m12 != 0.0F ? Float.floatToIntBits(this.m12) : 0); - i = 31 * i + (this.m13 != 0.0F ? Float.floatToIntBits(this.m13) : 0); - i = 31 * i + (this.m20 != 0.0F ? Float.floatToIntBits(this.m20) : 0); - i = 31 * i + (this.m21 != 0.0F ? Float.floatToIntBits(this.m21) : 0); - i = 31 * i + (this.m22 != 0.0F ? Float.floatToIntBits(this.m22) : 0); - i = 31 * i + (this.m23 != 0.0F ? Float.floatToIntBits(this.m23) : 0); - i = 31 * i + (this.m30 != 0.0F ? Float.floatToIntBits(this.m30) : 0); - i = 31 * i + (this.m31 != 0.0F ? Float.floatToIntBits(this.m31) : 0); - i = 31 * i + (this.m32 != 0.0F ? Float.floatToIntBits(this.m32) : 0); - return 31 * i + (this.m33 != 0.0F ? Float.floatToIntBits(this.m33) : 0); + m20 = values[8]; + m21 = values[9]; + m22 = values[10]; + m23 = values[11]; + + m30 = values[12]; + m31 = values[13]; + m32 = values[14]; + m33 = values[15]; } - @Override - public String toString() - { - return "Matrix4f:\n" + - this.m00 + " " + this.m01 + " " + this.m02 + " " + this.m03 + "\n" + - this.m10 + " " + this.m11 + " " + this.m12 + " " + this.m13 + "\n" + - this.m20 + " " + this.m21 + " " + this.m22 + " " + this.m23 + "\n" + - this.m30 + " " + this.m31 + " " + this.m32 + " " + this.m33 + "\n"; - } - public void store(FloatBuffer floatBuffer) - { - floatBuffer.put(bufferIndex(0, 0), this.m00); - floatBuffer.put(bufferIndex(0, 1), this.m01); - floatBuffer.put(bufferIndex(0, 2), this.m02); - floatBuffer.put(bufferIndex(0, 3), this.m03); - floatBuffer.put(bufferIndex(1, 0), this.m10); - floatBuffer.put(bufferIndex(1, 1), this.m11); - floatBuffer.put(bufferIndex(1, 2), this.m12); - floatBuffer.put(bufferIndex(1, 3), this.m13); - floatBuffer.put(bufferIndex(2, 0), this.m20); - floatBuffer.put(bufferIndex(2, 1), this.m21); - floatBuffer.put(bufferIndex(2, 2), this.m22); - floatBuffer.put(bufferIndex(2, 3), this.m23); - floatBuffer.put(bufferIndex(3, 0), this.m30); - floatBuffer.put(bufferIndex(3, 1), this.m31); - floatBuffer.put(bufferIndex(3, 2), this.m32); - floatBuffer.put(bufferIndex(3, 3), this.m33); - } - - public Matrix4f createJomlMatrix() - { - return new Matrix4f( - this.m00, this.m10, this.m20, this.m30, - this.m01, this.m11, this.m21, this.m31, - this.m02, this.m12, this.m22, this.m32, - this.m03, this.m13, this.m23, this.m33 - ); - } - - - private static int bufferIndex(int xIndex, int zIndex) - { - return (zIndex * 4) + xIndex; - } - + //=========// + // methods // + //=========// public void setIdentity() { @@ -341,7 +223,7 @@ public class Mat4f } } - public void multiply(Mat4f multMatrix) + public void multiply(DhApiMat4f multMatrix) { float f = this.m00 * multMatrix.m00 + this.m01 * multMatrix.m10 + this.m02 * multMatrix.m20 + this.m03 * multMatrix.m30; float f1 = this.m00 * multMatrix.m01 + this.m01 * multMatrix.m11 + this.m02 * multMatrix.m21 + this.m03 * multMatrix.m31; @@ -377,13 +259,6 @@ public class Mat4f this.m33 = f15; } - /* Quaternions aren't currently needed/implemented - public void multiply(Quaternion p_226596_1_) - { - this.multiply(new Matrix4f(p_226596_1_)); - } - */ - public void multiply(float scalar) { this.m00 *= scalar; @@ -404,81 +279,6 @@ public class Mat4f this.m33 *= scalar; } - public static Mat4f perspective(double fov, float widthHeightRatio, float nearClipPlane, float farClipPlane) - { - float f = (float) (1.0D / Math.tan(fov * ((float) Math.PI / 180F) / 2.0D)); - Mat4f matrix = new Mat4f(); - matrix.m00 = f / widthHeightRatio; - matrix.m11 = f; - matrix.m22 = (farClipPlane + nearClipPlane) / (nearClipPlane - farClipPlane); - matrix.m32 = -1.0F; - matrix.m23 = 2.0F * farClipPlane * nearClipPlane / (nearClipPlane - farClipPlane); - return matrix; - } - - - /* not currently needed/implemented - * Also the parameter names should be double checked as they may be incorrect - public static Matrix4Float orthographic(float left, float right, float top, float bottom) - { - Matrix4Float matrix4f = new Matrix4Float(); - matrix4f.m00 = 2.0F / left; - matrix4f.m11 = 2.0F / right; - float f = bottom - top; - matrix4f.m22 = -2.0F / f; - matrix4f.m33 = 1.0F; - matrix4f.m03 = -1.0F; - matrix4f.m13 = -1.0F; - matrix4f.m23 = -(bottom + top) / f; - return matrix4f; - } - */ - - /** - * TODO: what kind of translation is this? - * and how is this different from "multiplyTranslationMatrix"? - * Answer: This is faster and direct (but only if this is pure translation matrix without rotate) - */ - public void translate(Vec3f vec) - { - this.m03 += vec.x; - this.m13 += vec.y; - this.m23 += vec.z; - } - - /** originally "translate" from Minecraft's MatrixStack */ - public void multiplyTranslationMatrix(double x, double y, double z) - { - multiply(createTranslateMatrix((float) x, (float) y, (float) z)); - } - - public Mat4f copy() - { - return new Mat4f(this); - } - - public static Mat4f createScaleMatrix(float x, float y, float z) - { - Mat4f matrix = new Mat4f(); - matrix.m00 = x; - matrix.m11 = y; - matrix.m22 = z; - matrix.m33 = 1.0F; - return matrix; - } - - public static Mat4f createTranslateMatrix(float x, float y, float z) - { - Mat4f matrix = new Mat4f(); - matrix.m00 = 1.0F; - matrix.m11 = 1.0F; - matrix.m22 = 1.0F; - matrix.m33 = 1.0F; - matrix.m03 = x; - matrix.m13 = y; - matrix.m23 = z; - return matrix; - } @@ -487,6 +287,8 @@ public class Mat4f // methods // //==================// + private static int getArrayIndex(int xIndex, int zIndex) { return (zIndex * 4) + xIndex; } + /** Returns the values of this matrix in row major order (AKA rows then columns) */ public float[] getValuesAsArray() { @@ -513,113 +315,77 @@ public class Mat4f }; } - public Vec3f asNonNormalizedLookForwardVector() + + + //================// + // base overrides // + //================// + + @Override + public boolean equals(Object obj) { - return new Vec3f(this.m02, this.m12, this.m22); + if (this == obj) + { + return true; + } + else if (obj != null && this.getClass() == obj.getClass()) + { + DhApiMat4f otherMatrix = (DhApiMat4f) obj; + return Float.compare(otherMatrix.m00, this.m00) == 0 + && Float.compare(otherMatrix.m01, this.m01) == 0 + && Float.compare(otherMatrix.m02, this.m02) == 0 + && Float.compare(otherMatrix.m03, this.m03) == 0 + && Float.compare(otherMatrix.m10, this.m10) == 0 + && Float.compare(otherMatrix.m11, this.m11) == 0 + && Float.compare(otherMatrix.m12, this.m12) == 0 + && Float.compare(otherMatrix.m13, this.m13) == 0 + && Float.compare(otherMatrix.m20, this.m20) == 0 + && Float.compare(otherMatrix.m21, this.m21) == 0 + && Float.compare(otherMatrix.m22, this.m22) == 0 + && Float.compare(otherMatrix.m23, this.m23) == 0 + && Float.compare(otherMatrix.m30, this.m30) == 0 + && Float.compare(otherMatrix.m31, this.m31) == 0 + && Float.compare(otherMatrix.m32, this.m32) == 0 + && Float.compare(otherMatrix.m33, this.m33) == 0; + } + else + { + return false; + } } - //===============// - // Forge methods // - //===============// - - public Mat4f(float[] values) + @Override + public int hashCode() { - m00 = values[0]; - m01 = values[1]; - m02 = values[2]; - m03 = values[3]; - m10 = values[4]; - m11 = values[5]; - m12 = values[6]; - m13 = values[7]; - m20 = values[8]; - m21 = values[9]; - m22 = values[10]; - m23 = values[11]; - m30 = values[12]; - m31 = values[13]; - m32 = values[14]; - m33 = values[15]; + int i = this.m00 != 0.0F ? Float.floatToIntBits(this.m00) : 0; + i = 31 * i + (this.m01 != 0.0F ? Float.floatToIntBits(this.m01) : 0); + i = 31 * i + (this.m02 != 0.0F ? Float.floatToIntBits(this.m02) : 0); + i = 31 * i + (this.m03 != 0.0F ? Float.floatToIntBits(this.m03) : 0); + i = 31 * i + (this.m10 != 0.0F ? Float.floatToIntBits(this.m10) : 0); + i = 31 * i + (this.m11 != 0.0F ? Float.floatToIntBits(this.m11) : 0); + i = 31 * i + (this.m12 != 0.0F ? Float.floatToIntBits(this.m12) : 0); + i = 31 * i + (this.m13 != 0.0F ? Float.floatToIntBits(this.m13) : 0); + i = 31 * i + (this.m20 != 0.0F ? Float.floatToIntBits(this.m20) : 0); + i = 31 * i + (this.m21 != 0.0F ? Float.floatToIntBits(this.m21) : 0); + i = 31 * i + (this.m22 != 0.0F ? Float.floatToIntBits(this.m22) : 0); + i = 31 * i + (this.m23 != 0.0F ? Float.floatToIntBits(this.m23) : 0); + i = 31 * i + (this.m30 != 0.0F ? Float.floatToIntBits(this.m30) : 0); + i = 31 * i + (this.m31 != 0.0F ? Float.floatToIntBits(this.m31) : 0); + i = 31 * i + (this.m32 != 0.0F ? Float.floatToIntBits(this.m32) : 0); + return 31 * i + (this.m33 != 0.0F ? Float.floatToIntBits(this.m33) : 0); } - public Mat4f(FloatBuffer buffer) + @Override + public String toString() { - this(buffer.array()); + return "Matrix4f:\n" + + this.m00 + " " + this.m01 + " " + this.m02 + " " + this.m03 + "\n" + + this.m10 + " " + this.m11 + " " + this.m12 + " " + this.m13 + "\n" + + this.m20 + " " + this.m21 + " " + this.m22 + " " + this.m23 + "\n" + + this.m30 + " " + this.m31 + " " + this.m32 + " " + this.m33 + "\n"; } - public void set(Mat4f mat) - { - this.m00 = mat.m00; - this.m01 = mat.m01; - this.m02 = mat.m02; - this.m03 = mat.m03; - this.m10 = mat.m10; - this.m11 = mat.m11; - this.m12 = mat.m12; - this.m13 = mat.m13; - this.m20 = mat.m20; - this.m21 = mat.m21; - this.m22 = mat.m22; - this.m23 = mat.m23; - this.m30 = mat.m30; - this.m31 = mat.m31; - this.m32 = mat.m32; - this.m33 = mat.m33; - } - - public void add(Mat4f other) - { - m00 += other.m00; - m01 += other.m01; - m02 += other.m02; - m03 += other.m03; - m10 += other.m10; - m11 += other.m11; - m12 += other.m12; - m13 += other.m13; - m20 += other.m20; - m21 += other.m21; - m22 += other.m22; - m23 += other.m23; - m30 += other.m30; - m31 += other.m31; - m32 += other.m32; - m33 += other.m33; - } - - public void multiplyBackward(Mat4f other) - { - Mat4f copy = other.copy(); - copy.multiply(this); - this.set(copy); - } - - public void setTranslation(float x, float y, float z) - { - this.m00 = 1.0F; - this.m11 = 1.0F; - this.m22 = 1.0F; - this.m33 = 1.0F; - this.m03 = x; - this.m13 = y; - this.m23 = z; - } - - /** - * Changes the values that store the clipping planes. - * Formula for calculating matrix values is the same that OpenGL uses when making matrices. - * - * @param nearClip New near clipping plane value. - * @param farClip New far clipping plane value. - */ - public void setClipPlanes(float nearClip, float farClip) - { - //convert to matrix values, formula copied from a textbook / openGL specification. - float matNearClip = -((farClip + nearClip) / (farClip - nearClip)); - float matFarClip = -((2 * farClip * nearClip) / (farClip - nearClip)); - //set new values for the clip planes. - this.m22 = matNearClip; - this.m23 = matFarClip; - } + @Override + public DhApiMat4f copy() { return new DhApiMat4f(this); } } diff --git a/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiVec3d.java b/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiVec3d.java new file mode 100644 index 000000000..9e1344262 --- /dev/null +++ b/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiVec3d.java @@ -0,0 +1,94 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.api.objects.math; + +/** + * Often used to store block positions or any other + * position in 3D space. + * + * @author James Seibel + * @version 2024-7-9 + * @since API 3.0.0 + */ +public class DhApiVec3d +{ + public double x; + public double y; + public double z; + + + + /** creates a Vec3 at (0,0,0) */ + public DhApiVec3d() + { + this.x = 0; + this.y = 0; + this.z = 0; + } + + public DhApiVec3d(double x, double y, double z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else if (obj != null && this.getClass() == obj.getClass()) + { + DhApiVec3d Vec3d = (DhApiVec3d) obj; + if (Double.compare(Vec3d.x, this.x) != 0) + { + return false; + } + else if (Double.compare(Vec3d.y, this.y) != 0) + { + return false; + } + else + { + return Double.compare(Vec3d.z, this.z) == 0; + } + } + else + { + return false; + } + } + + @Override + public int hashCode() + { + long i = Double.doubleToLongBits(this.x); + i = 31 * i + Double.doubleToLongBits(this.y); + i = 31 * i + Double.doubleToLongBits(this.z); + return Long.hashCode(i); + } + + @Override + public String toString() { return "[" + this.x + ", " + this.y + ", " + this.z + "]"; } + +} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiVec3f.java b/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiVec3f.java new file mode 100644 index 000000000..8120b149a --- /dev/null +++ b/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiVec3f.java @@ -0,0 +1,98 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.api.objects.math; + +import com.seibel.distanthorizons.api.interfaces.util.IDhApiCopyable; + +/** + * Often used to store block positions or any other + * position in 3D space. + * + * @author James Seibel + * @version 2024-6-3 + * @since API 2.2.0 + */ +public class DhApiVec3f implements IDhApiCopyable +{ + public float x; + public float y; + public float z; + + + + /** creates a Vec3 at (0,0,0) */ + public DhApiVec3f() + { + this.x = 0; + this.y = 0; + this.z = 0; + } + + public DhApiVec3f(float x, float y, float z) + { + this.x = x; + this.y = y; + this.z = z; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + else if (obj != null && this.getClass() == obj.getClass()) + { + DhApiVec3f Vec3f = (DhApiVec3f) obj; + if (Float.compare(Vec3f.x, this.x) != 0) + { + return false; + } + else if (Float.compare(Vec3f.y, this.y) != 0) + { + return false; + } + else + { + return Float.compare(Vec3f.z, this.z) == 0; + } + } + else + { + return false; + } + } + + @Override + public int hashCode() + { + int i = Float.floatToIntBits(this.x); + i = 31 * i + Float.floatToIntBits(this.y); + return 31 * i + Float.floatToIntBits(this.z); + } + + @Override + public String toString() { return "[" + this.x + ", " + this.y + ", " + this.z + "]"; } + + @Override + public DhApiVec3f copy() { return new DhApiVec3f(this.x, this.y, this.z); } + +} diff --git a/api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBox.java b/api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBox.java new file mode 100644 index 000000000..d3411dc15 --- /dev/null +++ b/api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBox.java @@ -0,0 +1,52 @@ +package com.seibel.distanthorizons.api.objects.render; + + +import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial; +import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; + +import java.awt.*; + +/** + * @see IDhApiRenderableBoxGroup + * + * @author James Seibel + * @version 2024-6-30 + * @since API 3.0.0 + */ +public class DhApiRenderableBox +{ + /** the position closest to (-inf,-inf) */ + public DhApiVec3d minPos; + /** the position closest to (+inf,+inf) */ + public DhApiVec3d maxPos; + + public Color color; + public byte material; + + + + //==============// + // constructors // + //==============// + + public DhApiRenderableBox(DhApiVec3d minPos, float width, Color color, EDhApiBlockMaterial material) + { + this(minPos, new DhApiVec3d( + minPos.x + width, + minPos.y + width, + minPos.z + width + ), color, material); + } + + public DhApiRenderableBox(DhApiVec3d minPos, DhApiVec3d maxPos, Color color, EDhApiBlockMaterial material) + { + this.minPos = minPos; + this.maxPos = maxPos; + this.color = color; + this.material = material.index; + } + +} + diff --git a/api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBoxGroupShading.java b/api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBoxGroupShading.java new file mode 100644 index 000000000..396f73bc5 --- /dev/null +++ b/api/src/main/java/com/seibel/distanthorizons/api/objects/render/DhApiRenderableBoxGroupShading.java @@ -0,0 +1,93 @@ +package com.seibel.distanthorizons.api.objects.render; + + +import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; + +import java.awt.*; + +/** + * @see IDhApiRenderableBoxGroup + * + * Shading values are multiplied against the color for each direction, + * for example:
+ * A shading value of 1.0 indicates the color is unchanged.
+ * A shading value of 0.0 changes the color to black.
+ * + * @author James Seibel + * @version 2024-7-7 + * @since API 3.0.0 + */ +public class DhApiRenderableBoxGroupShading +{ + /** negative X */ + public float north = 1.0f; + /** positive X */ + public float south = 1.0f; + + /** positive X */ + public float east = 1.0f; + /** negative X */ + public float west = 1.0f; + + /** positive Y */ + public float top = 1.0f; + /** negative Y */ + public float bottom = 1.0f; + + + + //==============// + // constructors // + //==============// + + public static DhApiRenderableBoxGroupShading getDefaultShaded() + { + DhApiRenderableBoxGroupShading shading = new DhApiRenderableBoxGroupShading(); + shading.setDefaultShaded(); + return shading; + } + + public static DhApiRenderableBoxGroupShading getUnshaded() + { + DhApiRenderableBoxGroupShading shading = new DhApiRenderableBoxGroupShading(); + shading.setUnshaded(); + return shading; + } + + + + //=========// + // methods // + //=========// + + /** + * Directions will have different brightness similar to Minecraft blocks.
+ * This is a good default for un-lit objects. + */ + public void setDefaultShaded() + { + this.north = 0.8f; + this.south = 0.8f; + this.east = 0.6f; + this.west = 0.6f; + this.top = 1.0f; + this.bottom = 0.5f; + } + + /** + * All directions render with the same brightness.
+ * This is best used for glowing objects like beacons. + */ + public void setUnshaded() + { + this.north = 1.0f; + this.south = 1.0f; + this.east = 1.0f; + this.west = 1.0f; + this.top = 1.0f; + this.bottom = 1.0f; + } + +} + diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/DependencyInjection/ApiEventInjector.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/DependencyInjection/ApiEventInjector.java index e6d49f611..386ab2aef 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/DependencyInjection/ApiEventInjector.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/DependencyInjection/ApiEventInjector.java @@ -22,6 +22,7 @@ package com.seibel.distanthorizons.coreapi.DependencyInjection; import com.seibel.distanthorizons.api.interfaces.events.IDhApiEventInjector; import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiCancelableEvent; import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEvent; +import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiEventParam; import com.seibel.distanthorizons.api.methods.events.interfaces.IDhApiOneTimeEvent; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiCancelableEventParam; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; @@ -140,7 +141,26 @@ public class ApiEventInjector extends DependencyInjector implements // fire each event and record if any of them // request to cancel the event. - DhApiEventParam eventParam = createEventParamWrapper(event, eventInput); + + // attempt to clone the event input if possible + // this is done to reduce the likely hood that one event listener + // will make change the event parameter for other listeners + T input = eventInput; + if (eventInput instanceof IDhApiEventParam) + { + try + { + //noinspection unchecked + input = (T) ((IDhApiEventParam) eventInput).copy(); + } + catch (Exception e) + { + LOGGER.error("Unable to clone event parameter ["+eventInput.getClass().getSimpleName()+"], error: ["+e.getMessage()+"].", e); + } + } + + + DhApiEventParam eventParam = createEventParamWrapper(event, input); event.fireEvent(eventParam); if (eventParam instanceof DhApiCancelableEventParam) diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java index a382dca0d..526c5e10e 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java @@ -47,9 +47,9 @@ public final class ModInfo public static boolean IS_DEV_BUILD = VERSION.toLowerCase().contains("dev"); /** This version should only be updated when breaking changes are introduced to the DH API */ - public static final int API_MAJOR_VERSION = 2; + public static final int API_MAJOR_VERSION = 3; /** This version should be updated whenever new methods are added to the DH API */ - public static final int API_MINOR_VERSION = 1; + public static final int API_MINOR_VERSION = 0; /** This version should be updated whenever non-breaking fixes are added to the DH API */ public static final int API_PATH_VERSION = 0; diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/interfaces/config/IConfigEntry.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/interfaces/config/IConfigEntry.java index a31745a8e..3297a1a26 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/interfaces/config/IConfigEntry.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/interfaces/config/IConfigEntry.java @@ -67,10 +67,11 @@ public interface IConfigEntry * Checks if the option is valid * * 0 == valid + * 2 == invalid * 1 == number too high * -1 == number too low */ - byte isValid(); + byte isValid(); // TODO replace with an enum /** Checks if a value is valid */ byte isValid(T value); diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Vec3f.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Vec3f.java deleted file mode 100644 index b45ba98be..000000000 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Vec3f.java +++ /dev/null @@ -1,263 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.coreapi.util.math; - -import com.seibel.distanthorizons.coreapi.util.MathUtil; - -/** - * A (almost) exact copy of Minecraft's 1.16.5 - * implementation of a 3 element float vector. - * - * @author James Seibel - * @version 11-11-2021 - */ -public class Vec3f -{ - public static Vec3f XNeg = new Vec3f(-1.0F, 0.0F, 0.0F); - public static Vec3f XPos = new Vec3f(1.0F, 0.0F, 0.0F); - public static Vec3f YNeg = new Vec3f(0.0F, -1.0F, 0.0F); - public static Vec3f YPos = new Vec3f(0.0F, 1.0F, 0.0F); - public static Vec3f ZNeg = new Vec3f(0.0F, 0.0F, -1.0F); - public static Vec3f ZPos = new Vec3f(0.0F, 0.0F, 1.0F); - - - public float x; - public float y; - public float z; - - - - public Vec3f() - { - - } - - public Vec3f(float x, float y, float z) - { - this.x = x; - this.y = y; - this.z = z; - } - - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - else if (obj != null && this.getClass() == obj.getClass()) - { - Vec3f Vec3f = (Vec3f) obj; - if (Float.compare(Vec3f.x, this.x) != 0) - { - return false; - } - else if (Float.compare(Vec3f.y, this.y) != 0) - { - return false; - } - else - { - return Float.compare(Vec3f.z, this.z) == 0; - } - } - else - { - return false; - } - } - - @Override - public int hashCode() - { - int i = Float.floatToIntBits(this.x); - i = 31 * i + Float.floatToIntBits(this.y); - return 31 * i + Float.floatToIntBits(this.z); - } - - public void mul(float scalar) - { - this.x *= scalar; - this.y *= scalar; - this.z *= scalar; - } - - public void mul(float x, float y, float z) - { - this.x *= x; - this.y *= y; - this.z *= z; - } - - public void clamp(float min, float max) - { - this.x = MathUtil.clamp(min, this.x, max); - this.y = MathUtil.clamp(min, this.y, max); - this.z = MathUtil.clamp(min, this.z, max); - } - - public void set(float x, float y, float z) - { - this.x = x; - this.y = y; - this.z = z; - } - - public void add(float x, float y, float z) - { - this.x += x; - this.y += y; - this.z += z; - } - - public void add(Vec3f vector) - { - this.x += vector.x; - this.y += vector.y; - this.z += vector.z; - } - - public void subtract(Vec3f vector) - { - this.x -= vector.x; - this.y -= vector.y; - this.z -= vector.z; - } - - public float dotProduct(Vec3f vector) - { - return this.x * vector.x + this.y * vector.y + this.z * vector.z; - } - - /** Returns true if normalization had to be done */ - public boolean normalize() - { - float squaredSum = this.x * this.x + this.y * this.y + this.z * this.z; - if (squaredSum < 1.0E-5D) - { - return false; - } - else - { - float f1 = MathUtil.fastInvSqrt(squaredSum); - this.x *= f1; - this.y *= f1; - this.z *= f1; - return true; - } - } - - public void crossProduct(Vec3f vector) - { - float f = this.x; - float f1 = this.y; - float f2 = this.z; - float f3 = vector.x; - float f4 = vector.y; - float f5 = vector.z; - this.x = f1 * f5 - f2 * f4; - this.y = f2 * f3 - f * f5; - this.z = f * f4 - f1 * f3; - } - - /* Matrix3f is not currently needed/implemented - public void transform(Matrix3f p_229188_1_) - { - float f = this.x; - float f1 = this.y; - float f2 = this.z; - this.x = p_229188_1_.m00 * f + p_229188_1_.m01 * f1 + p_229188_1_.m02 * f2; - this.y = p_229188_1_.m10 * f + p_229188_1_.m11 * f1 + p_229188_1_.m12 * f2; - this.z = p_229188_1_.m20 * f + p_229188_1_.m21 * f1 + p_229188_1_.m22 * f2; - } - */ - - /* Quaternions are not currently needed/implemented - public void transform(Quaternion p_214905_1_) - { - Quaternion quaternion = new Quaternion(p_214905_1_); - quaternion.mul(new Quaternion(this.x(), this.y(), this.z(), 0.0F)); - Quaternion quaternion1 = new Quaternion(p_214905_1_); - quaternion1.conj(); - quaternion.mul(quaternion1); - this.set(quaternion.i(), quaternion.j(), quaternion.k()); - } - */ - - /* not currently needed - * percent may actually be partial ticks (which is available when rendering) - public void linearInterp(Vec3f resultingVector, float percent) - { - float f = 1.0F - percent; - this.x = this.x * f + resultingVector.x * percent; - this.y = this.y * f + resultingVector.y * percent; - this.z = this.z * f + resultingVector.z * percent; - } - */ - - /* Quaternions are not currently needed/implemented - public Quaternion rotation(float p_229193_1_) - { - return new Quaternion(this, p_229193_1_, false); - } - - - @OnlyIn(Dist.CLIENT) - public Quaternion rotationDegrees(float p_229187_1_) - { - return new Quaternion(this, p_229187_1_, true); - } - */ - - public Vec3f copy() - { - return new Vec3f(this.x, this.y, this.z); - } - - /* not currently needed/implemented - public void map(Float2FloatFunction p_229191_1_) - { - this.x = p_229191_1_.get(this.x); - this.y = p_229191_1_.get(this.y); - this.z = p_229191_1_.get(this.z); - } - */ - - @Override - public String toString() - { - return "[" + this.x + ", " + this.y + ", " + this.z + "]"; - } - - // Forge start - public Vec3f(float[] values) - { - set(values); - } - - public void set(float[] values) - { - this.x = values[0]; - this.y = values[1]; - this.z = values[2]; - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/Initializer.java b/core/src/main/java/com/seibel/distanthorizons/core/Initializer.java index 3ef5e3325..3bed23eed 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/Initializer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/Initializer.java @@ -20,6 +20,7 @@ package com.seibel.distanthorizons.core; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.render.renderer.generic.GenericRenderObjectFactory; import com.seibel.distanthorizons.core.sql.DatabaseUpdater; import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; @@ -96,6 +97,7 @@ public class Initializer DhApi.Delayed.terrainRepo = DhApiTerrainDataRepo.INSTANCE; DhApi.Delayed.worldProxy = DhApiWorldProxy.INSTANCE; DhApi.Delayed.renderProxy = DhApiRenderProxy.INSTANCE; + DhApi.Delayed.customRenderObjectFactory = GenericRenderObjectFactory.INSTANCE; DhApi.Delayed.wrapperFactory = SingletonInjector.INSTANCE.get(IWrapperFactory.class); if (DhApi.Delayed.wrapperFactory == null) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGenericRenderingConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGenericRenderingConfig.java new file mode 100644 index 000000000..f7fded3e5 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGenericRenderingConfig.java @@ -0,0 +1,46 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.api.external.methods.config.client; + +import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue; +import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiGenericRenderingConfig; +import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiNoiseTextureConfig; +import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue; +import com.seibel.distanthorizons.core.config.Config; + +public class DhApiGenericRenderingConfig implements IDhApiGenericRenderingConfig +{ + public static DhApiGenericRenderingConfig INSTANCE = new DhApiGenericRenderingConfig(); + + private DhApiGenericRenderingConfig() { } + + + + @Override + public IDhApiConfigValue renderingEnabled() + { return new DhApiConfigValue(Config.Client.Advanced.Graphics.GenericRendering.enableRendering); } + @Override + public IDhApiConfigValue beaconRenderingEnabled() + { return new DhApiConfigValue(Config.Client.Advanced.Graphics.GenericRendering.enableBeaconRendering); } + @Override + public IDhApiConfigValue cloudRenderingEnabled() + { return new DhApiConfigValue(Config.Client.Advanced.Graphics.GenericRendering.enableCloudRendering); } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGraphicsConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGraphicsConfig.java index 27c1d48eb..ef4f66c74 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGraphicsConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiGraphicsConfig.java @@ -22,10 +22,7 @@ package com.seibel.distanthorizons.core.api.external.methods.config.client; import com.seibel.distanthorizons.api.enums.config.*; import com.seibel.distanthorizons.api.enums.rendering.EDhApiTransparency; import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue; -import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiAmbientOcclusionConfig; -import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiFogConfig; -import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiGraphicsConfig; -import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiNoiseTextureConfig; +import com.seibel.distanthorizons.api.interfaces.config.client.*; import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue; import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode; import com.seibel.distanthorizons.core.config.Config; @@ -42,9 +39,14 @@ public class DhApiGraphicsConfig implements IDhApiGraphicsConfig // inner layers // //==============// + @Override public IDhApiFogConfig fog() { return DhApiFogConfig.INSTANCE; } + @Override public IDhApiAmbientOcclusionConfig ambientOcclusion() { return DhApiAmbientOcclusionConfig.INSTANCE; } + @Override public IDhApiNoiseTextureConfig noiseTexture() { return DhApiNoiseTextureConfig.INSTANCE; } + @Override + public IDhApiGenericRenderingConfig genericRendering() { return DhApiGenericRenderingConfig.INSTANCE; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataCache.java b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataCache.java new file mode 100644 index 000000000..37cabfb97 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataCache.java @@ -0,0 +1,87 @@ +package com.seibel.distanthorizons.core.api.external.methods.data; + +import com.seibel.distanthorizons.api.interfaces.data.IDhApiTerrainDataCache; +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; +import it.unimi.dsi.fastutil.longs.Long2ReferenceOpenHashMap; +import it.unimi.dsi.fastutil.longs.LongSet; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.Nullable; + +import java.lang.ref.SoftReference; + +public class DhApiTerrainDataCache implements IDhApiTerrainDataCache +{ + private final Object modificationLock = new Object(); + private Long2ReferenceOpenHashMap> posToFullDataRef = new Long2ReferenceOpenHashMap<>(); + + private static final Logger LOGGER = LogManager.getLogger(DhApiTerrainDataCache.class.getSimpleName()); + + + + //==================// + // internal methods // + //==================// + + public void add(long pos, FullDataSourceV2 dataSource) + { + synchronized (this.modificationLock) + { + this.posToFullDataRef.put(pos, new SoftReference<>(dataSource)); + } + } + + @Nullable + public FullDataSourceV2 get(long pos) + { + synchronized (this.modificationLock) + { + SoftReference ref = this.posToFullDataRef.get(pos); + if (ref != null) + { + return ref.get(); + } + else + { + return null; + } + } + } + + + + //=============// + // API methods // + //=============// + + @Override + public void clear() + { + synchronized (this.modificationLock) + { + LongSet keySet = this.posToFullDataRef.keySet(); + for (long pos : keySet) + { + SoftReference dataRef = this.posToFullDataRef.remove(pos); + if (dataRef != null) + { + FullDataSourceV2 dataSource = dataRef.get(); + if (dataSource != null) + { + try + { + dataSource.close(); + } + catch (Exception e) + { + LOGGER.warn("Unable to close data source, error: [" + e.getMessage() + "].", e); + } + } + } + } + } + } + + + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataRepo.java b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataRepo.java index d08989ad8..cf7d337c0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataRepo.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/data/DhApiTerrainDataRepo.java @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.api.external.methods.data; +import com.seibel.distanthorizons.api.interfaces.data.IDhApiTerrainDataCache; import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper; import com.seibel.distanthorizons.api.objects.DhApiResult; import com.seibel.distanthorizons.api.objects.data.DhApiRaycastResult; @@ -35,6 +36,7 @@ import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.FullDataPointUtil; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.RayCastUtil; +import com.seibel.distanthorizons.core.util.math.Vec3f; import com.seibel.distanthorizons.core.world.AbstractDhWorld; import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; @@ -43,12 +45,12 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRen import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; -import com.seibel.distanthorizons.coreapi.util.math.Vec3d; -import com.seibel.distanthorizons.coreapi.util.math.Vec3f; -import com.seibel.distanthorizons.coreapi.util.math.Vec3i; +import com.seibel.distanthorizons.core.util.math.Vec3d; +import com.seibel.distanthorizons.core.util.math.Vec3i; import it.unimi.dsi.fastutil.longs.LongArrayList; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.concurrent.ExecutionException; @@ -56,9 +58,6 @@ import java.util.concurrent.ExecutionException; /** * Allows interfacing with the terrain data Distant Horizons has stored. - * - * @author James Seibel - * @version 2022-11-19 */ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo { @@ -68,12 +67,15 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo // debugging values private static volatile boolean debugThreadRunning = false; - private static String currentDebugBiomeName = ""; - private static int currentDebugBlockColorInt = -1; + private static DhApiTerrainDataCache debugDataCache = new DhApiTerrainDataCache(); private static DhApiVec3i currentDebugVec3i = new Vec3i(); + //=============// + // constructor // + //=============// + private DhApiTerrainDataRepo() { @@ -86,41 +88,32 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo //================// @Override - public DhApiResult getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ) - { - return getTerrainDataAtBlockYPos(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), blockPosY); - } + public DhApiResult getSingleDataPointAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosY, int blockPosZ, @Nullable IDhApiTerrainDataCache dataCache) + { return getTerrainDataAtBlockYPos(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), blockPosY, dataCache); } @Override - public DhApiResult getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ) - { - return getTerrainDataColumnArray(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), null); - } + public DhApiResult getColumnDataAtBlockPos(IDhApiLevelWrapper levelWrapper, int blockPosX, int blockPosZ, @Nullable IDhApiTerrainDataCache dataCache) + { return getTerrainDataColumnArray(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), null, dataCache); } @Override - public DhApiResult getAllTerrainDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ) - { - return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPosX, chunkPosZ)); - } + public DhApiResult getAllTerrainDataAtChunkPos(IDhApiLevelWrapper levelWrapper, int chunkPosX, int chunkPosZ, @Nullable IDhApiTerrainDataCache dataCache) + { return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(LodUtil.CHUNK_DETAIL_LEVEL, chunkPosX, chunkPosZ), dataCache); } @Override - public DhApiResult getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ) - { - return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(LodUtil.REGION_DETAIL_LEVEL, regionPosX, regionPosZ)); - } + public DhApiResult getAllTerrainDataAtRegionPos(IDhApiLevelWrapper levelWrapper, int regionPosX, int regionPosZ, @Nullable IDhApiTerrainDataCache dataCache) + { return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(LodUtil.REGION_DETAIL_LEVEL, regionPosX, regionPosZ), dataCache); } @Override - public DhApiResult getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ) - { - return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(detailLevel, posX, posZ)); - } + public DhApiResult getAllTerrainDataAtDetailLevelAndPos(IDhApiLevelWrapper levelWrapper, byte detailLevel, int posX, int posZ, @Nullable IDhApiTerrainDataCache dataCache) + { return getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, new DhLodPos(detailLevel, posX, posZ), dataCache); } + // private getters // /** Returns a single API terrain datapoint that contains the given Y block position */ - private static DhApiResult getTerrainDataAtBlockYPos(IDhApiLevelWrapper levelWrapper, DhLodPos requestedColumnPos, Integer blockYPos) + private static DhApiResult getTerrainDataAtBlockYPos(IDhApiLevelWrapper levelWrapper, DhLodPos requestedColumnPos, Integer blockYPos, @Nullable IDhApiTerrainDataCache dataCache) { - DhApiResult result = getTerrainDataColumnArray(levelWrapper, requestedColumnPos, blockYPos); + DhApiResult result = getTerrainDataColumnArray(levelWrapper, requestedColumnPos, blockYPos, dataCache); if (result.success && result.payload.length > 0) { return DhApiResult.createSuccess(result.message, result.payload[0]); @@ -140,7 +133,9 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo * * will stop and return the in progress data if any errors are encountered. */ - private static DhApiResult getTerrainDataOverAreaForPositionDetailLevel(IDhApiLevelWrapper levelWrapper, DhLodPos requestedAreaPos) + private static DhApiResult getTerrainDataOverAreaForPositionDetailLevel( + IDhApiLevelWrapper levelWrapper, DhLodPos requestedAreaPos, + @Nullable IDhApiTerrainDataCache dataCache) { DhLodPos startingBlockPos = requestedAreaPos.getCornerLodPos(LodUtil.BLOCK_DETAIL_LEVEL); int widthOfAreaInBlocks = BitShiftUtil.powerOfTwo(requestedAreaPos.detailLevel); @@ -154,7 +149,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo for (int z = 0; z < widthOfAreaInBlocks; z++) { DhLodPos blockColumnPos = new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, startingBlockPos.x + x, startingBlockPos.z + z); - DhApiResult result = getTerrainDataColumnArray(levelWrapper, blockColumnPos, null); + DhApiResult result = getTerrainDataColumnArray(levelWrapper, blockColumnPos, null, dataCache); if (result.success) { returnArray[x][z] = result.payload; @@ -177,8 +172,15 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo * If the ApiResult is successful there will be an array of data.
* The returned array will be empty if no data could be retrieved. */ - private static DhApiResult getTerrainDataColumnArray(IDhApiLevelWrapper levelWrapper, DhLodPos requestedColumnPos, Integer nullableBlockYPos) + private static DhApiResult getTerrainDataColumnArray( + IDhApiLevelWrapper levelWrapper, + DhLodPos requestedColumnPos, Integer nullableBlockYPos, + @Nullable IDhApiTerrainDataCache apiDataCache) { + //============// + // validation // + //============// + AbstractDhWorld currentWorld = SharedApi.getAbstractDhWorld(); if (currentWorld == null) { @@ -194,6 +196,15 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo ILevelWrapper coreLevelWrapper = (ILevelWrapper) levelWrapper; + if (!(apiDataCache instanceof DhApiTerrainDataCache)) + { + // custom level wrappers aren't supported, + // the API user must get a level wrapper from our code somewhere + return DhApiResult.createFail("Unsupported [" + IDhApiTerrainDataCache.class.getSimpleName() + "] implementation, only the core class [" + DhApiTerrainDataCache.class.getSimpleName() + "] is a valid parameter."); + } + DhApiTerrainDataCache dataCache = (DhApiTerrainDataCache) apiDataCache; + + IDhLevel level = currentWorld.getLevel(coreLevelWrapper); if (level == null) { @@ -209,70 +220,96 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo DhLodPos relativePos = requestedColumnPos.getDhSectionRelativePositionForDetailLevel(); + + //=====================// + // get the data source // + //=====================// + try { - // attempt to get/generate the data source for this section - FullDataSourceV2 dataSource = level.getFullDataProvider().getAsync(sectionPos).get(); + FullDataSourceV2 dataSource = null; + + // try using the cached data if possible + if (dataCache != null) + { + dataSource = dataCache.get(sectionPos); + } + if (dataSource == null) { - return DhApiResult.createFail("Unable to find/generate any data at the " + DhSectionPos.class.getSimpleName() + " [" + DhSectionPos.toString(sectionPos) + "]."); - } - else - { - // attempt to get the LOD data from the data source - FullDataPointIdMap mapping = dataSource.mapping; - LongArrayList dataColumn = dataSource.get(relativePos.x, relativePos.z); - if (dataColumn != null) + // attempt to get/generate the data source for this section + dataSource = level.getFullDataProvider().getAsync(sectionPos).get(); + if (dataSource == null) { - int dataColumnIndexCount = dataColumn.size(); - DhApiTerrainDataPoint[] returnArray = new DhApiTerrainDataPoint[dataColumnIndexCount]; - long dataPoint; + return DhApiResult.createFail("Unable to find/generate any data at the " + DhSectionPos.class.getSimpleName() + " [" + DhSectionPos.toString(sectionPos) + "]."); + } + dataCache.add(sectionPos, dataSource); + } + + + + //===============================// + // get LOD data from data source // + //===============================// + + FullDataPointIdMap mapping = dataSource.mapping; + LongArrayList dataColumn = dataSource.get(relativePos.x, relativePos.z); + if (dataColumn != null) + { + int dataColumnIndexCount = dataColumn.size(); + DhApiTerrainDataPoint[] returnArray = new DhApiTerrainDataPoint[dataColumnIndexCount]; + long dataPoint; + + boolean getSpecificYCoordinate = nullableBlockYPos != null; + int levelMinimumHeight = levelWrapper.getMinHeight(); + + + // search for a datapoint that contains the block y position + for (int i = 0; i < dataColumnIndexCount; i++) + { + dataPoint = dataColumn.getLong(i); - boolean getSpecificYCoordinate = nullableBlockYPos != null; - int levelMinimumHeight = levelWrapper.getMinHeight(); - - - // search for a datapoint that contains the block y position - for (int i = 0; i < dataColumnIndexCount; i++) + if (!getSpecificYCoordinate) { - dataPoint = dataColumn.getLong(i); - - if (!getSpecificYCoordinate) + // if we aren't look for a specific datapoint, add each datapoint to the return array + returnArray[i] = generateApiDatapoint(levelWrapper, mapping, requestedDetailLevel, dataPoint); + } + else + { + // we are looking for a specific datapoint, + // don't look at null ones + if (dataPoint != 0) { - // if we aren't look for a specific datapoint, add each datapoint to the return array - returnArray[i] = generateApiDatapoint(levelWrapper, mapping, requestedDetailLevel, dataPoint); - } - else - { - // we are looking for a specific datapoint, - // don't look at null ones - if (dataPoint != 0) + int requestedY = nullableBlockYPos; + int bottomY = FullDataPointUtil.getBottomY(dataPoint) + levelMinimumHeight; + int height = FullDataPointUtil.getHeight(dataPoint); + int topY = bottomY + height; + + // does this datapoint contain the requested Y position? + if (bottomY <= requestedY && requestedY < topY) // blockPositions start from the bottom of the block, thus "<=" for bottomY, just "<" for topY { - int requestedY = nullableBlockYPos; - int bottomY = FullDataPointUtil.getBottomY(dataPoint) + levelMinimumHeight; - int height = FullDataPointUtil.getHeight(dataPoint); - int topY = bottomY + height; - - // does this datapoint contain the requested Y position? - if (bottomY <= requestedY && requestedY < topY) // blockPositions start from the bottom of the block, thus "<=" for bottomY, just "<" for topY - { - // this datapoint contains the requested block position, return it - DhApiTerrainDataPoint apiTerrainData = generateApiDatapoint(levelWrapper, mapping, requestedDetailLevel, dataPoint); - return DhApiResult.createSuccess(new DhApiTerrainDataPoint[]{apiTerrainData}); - } + // this datapoint contains the requested block position, return it + DhApiTerrainDataPoint apiTerrainData = generateApiDatapoint(levelWrapper, mapping, requestedDetailLevel, dataPoint); + return DhApiResult.createSuccess(new DhApiTerrainDataPoint[]{apiTerrainData}); } } } - - // return all collected data - return DhApiResult.createSuccess(returnArray); } - // the requested data wasn't present in this column (and/or the column wasn't able to be accessed/generated) - return DhApiResult.createSuccess(new DhApiTerrainDataPoint[0]); + // return all collected data + return DhApiResult.createSuccess(returnArray); } + + // the requested data wasn't present in this column (and/or the column wasn't able to be accessed/generated) + return DhApiResult.createSuccess(new DhApiTerrainDataPoint[0]); } catch (InterruptedException | ExecutionException e) + { + // shouldn't normally happen, but just in case + LOGGER.error("getTerrainDataColumnArray operation canceled. Error: [" + e.getMessage() + "]", e); + return DhApiResult.createFail("Operation cancled before it could complete: [" + e.getMessage() + "]."); + } + catch (Exception e) { // shouldn't normally happen, but just in case LOGGER.error("Unexpected exception in getTerrainDataColumnArray. Error: [" + e.getMessage() + "]", e); @@ -306,9 +343,11 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo IDhApiLevelWrapper levelWrapper, double rayOriginX, double rayOriginY, double rayOriginZ, float rayDirectionX, float rayDirectionY, float rayDirectionZ, - int maxRayBlockLength) + int maxRayBlockLength, + @Nullable + IDhApiTerrainDataCache dataCache) { - return this.raycastLodData(levelWrapper, new Vec3d(rayOriginX, rayOriginY, rayOriginZ), new Vec3f(rayDirectionX, rayDirectionY, rayDirectionZ), maxRayBlockLength); + return this.raycastLodData(levelWrapper, new Vec3d(rayOriginX, rayOriginY, rayOriginZ), new Vec3f(rayDirectionX, rayDirectionY, rayDirectionZ), maxRayBlockLength, dataCache); } /** @@ -317,12 +356,17 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo * Works by walking through the world and attempting to get the LOD
* data present at each step. */ - private DhApiResult raycastLodData(IDhApiLevelWrapper levelWrapper, Vec3d rayOrigin, Vec3f rayDirection, int maxRayBlockLength) + private DhApiResult raycastLodData( + IDhApiLevelWrapper levelWrapper, + Vec3d rayOrigin, Vec3f rayDirection, + int maxRayBlockLength, + @Nullable + IDhApiTerrainDataCache dataCache) { rayDirection.normalize(); int minBlockHeight = levelWrapper.getMinHeight(); - int maxBlockHeight = levelWrapper.getHeight(); + int maxBlockHeight = levelWrapper.getMaxHeight(); @@ -346,7 +390,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo for (Vec3i columnPos : columnPositions) { // check each column - DhApiResult result = this.getColumnDataAtBlockPos(levelWrapper, columnPos.x, columnPos.z); + DhApiResult result = this.getColumnDataAtBlockPos(levelWrapper, columnPos.x, columnPos.z, dataCache); if (!result.success) { // if there was an error, stop and return it @@ -414,7 +458,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo */ private static ArrayList getIntersectingColumnsAtPosition(Vec3i rayEndingPos, Vec3f rayDirection) { - ArrayList returnList = new ArrayList(9); + ArrayList returnList = new ArrayList<>(9); for (int x = -1; x <= 1; x++) { @@ -469,6 +513,15 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo + //=============// + // API helpers // + //=============// + + @Override + public IDhApiTerrainDataCache getSoftCache() { return new DhApiTerrainDataCache(); } + + + //===============// // debug methods // //===============// @@ -485,15 +538,15 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo Thread thread = new Thread(() -> { try { - DhApiResult single = getTerrainDataAtBlockYPos(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), blockPosY); - DhApiResult column = getTerrainDataColumnArray(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), null); + DhApiResult single = getTerrainDataAtBlockYPos(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), blockPosY, debugDataCache); + DhApiResult column = getTerrainDataColumnArray(levelWrapper, new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ), null, debugDataCache); DhLodPos chunkPos = new DhLodPos(LodUtil.BLOCK_DETAIL_LEVEL, blockPosX, blockPosZ).convertToDetailLevel(LodUtil.CHUNK_DETAIL_LEVEL); - DhApiResult area = getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, chunkPos); + DhApiResult area = getTerrainDataOverAreaForPositionDetailLevel(levelWrapper, chunkPos, debugDataCache); IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); - DhApiResult rayCast = INSTANCE.raycastLodData(levelWrapper, MC_RENDER.getCameraExactPosition(), MC_RENDER.getLookAtVector(), 1000); + DhApiResult rayCast = INSTANCE.raycastLodData(levelWrapper, MC_RENDER.getCameraExactPosition(), MC_RENDER.getLookAtVector(), 1000, debugDataCache); if (rayCast.payload != null && !rayCast.payload.pos.equals(currentDebugVec3i)) { currentDebugVec3i = rayCast.payload.pos; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index 2e438978a..ec4df1ad7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -23,6 +23,12 @@ import com.seibel.distanthorizons.api.DhApi; 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.pos.DhChunkPos; +import com.seibel.distanthorizons.core.render.DhApiRenderProxy; +import com.seibel.distanthorizons.core.util.objects.Pair; +import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; +import com.seibel.distanthorizons.core.level.IDhClientLevel; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.network.messages.NetworkMessage; import com.seibel.distanthorizons.core.network.session.Session; @@ -30,17 +36,14 @@ 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.IDhClientLevel; import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.logging.ConfigBasedSpamLogger; import com.seibel.distanthorizons.core.logging.SpamReducedLogger; -import com.seibel.distanthorizons.core.pos.DhChunkPos; -import com.seibel.distanthorizons.core.render.DhApiRenderProxy; +import com.seibel.distanthorizons.core.util.math.Mat4f; import com.seibel.distanthorizons.core.render.glObject.GLProxy; import com.seibel.distanthorizons.core.render.renderer.TestRenderer; import com.seibel.distanthorizons.core.util.RenderUtil; -import com.seibel.distanthorizons.core.util.objects.Pair; import com.seibel.distanthorizons.core.world.AbstractDhWorld; import com.seibel.distanthorizons.core.world.DhClientServerWorld; import com.seibel.distanthorizons.core.world.DhClientWorld; @@ -49,13 +52,12 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; -import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; -import com.seibel.distanthorizons.coreapi.util.math.Mat4f; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; import org.lwjgl.glfw.GLFW; +import java.io.File; import java.util.HashMap; import java.util.HashSet; import java.util.Queue; @@ -124,9 +126,28 @@ public class ClientApi public synchronized void onClientOnlyConnected() { // only continue if the client is connected to a different server - if (MC.clientConnectedToDedicatedServer()) + boolean connectedToServer = MC.clientConnectedToDedicatedServer(); + boolean connectedToReplay = MC.connectedToReplay(); + if (connectedToServer || connectedToReplay) { - LOGGER.info("Client on ClientOnly mode connecting."); + if (connectedToServer) + { + LOGGER.info("Client on ClientOnly mode connecting."); + } + else + { + LOGGER.info("Replay on ClientServer mode connecting."); + + if (Config.Client.Advanced.Logging.showReplayWarningOnStartup.get()) + { + MC.sendChatMessage("\u00A76" + "Distant Horizons: Replay detected." + "\u00A7r"); // gold color + MC.sendChatMessage("DH may behave strangely or have missing functionality."); + MC.sendChatMessage("In order to use pre-generated LODs, put your DH database(s) in:"); + MC.sendChatMessage("\u00A77"+".Minecraft" + File.separator + ClientOnlySaveStructure.SERVER_DATA_FOLDER_NAME + File.separator + ClientOnlySaveStructure.REPLAY_SERVER_FOLDER_NAME + File.separator + "DIMENSION_NAME"+"\u00A7r"); // light gray color + MC.sendChatMessage("This can be disabled in DH's config under Advanced -> Logging."); + MC.sendChatMessage(""); + } + } // firing after clientLevelLoadEvent // TODO if level has prepped to load it should fire level load event @@ -447,7 +468,7 @@ public class ClientApi if (!DhApi.Delayed.renderProxy.getDeferTransparentRendering()) { - ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterRenderEvent.class, renderEventParam); + ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterRenderEvent.class, null); } } else if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DEBUG) @@ -468,7 +489,7 @@ public class ClientApi if (DhApi.Delayed.renderProxy.getDeferTransparentRendering()) { - ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterRenderEvent.class, renderEventParam); + ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterRenderEvent.class, null); } } } @@ -479,6 +500,7 @@ public class ClientApi MC.sendChatMessage("\u00A74\u00A7l\u00A7uERROR: Distant Horizons renderer has encountered an exception!"); MC.sendChatMessage("\u00A74Renderer is now disabled to prevent further issues."); + MC.sendChatMessage("\u00A74Please restart your game to re-enable Distant Horizons' LOD rendering."); MC.sendChatMessage("\u00A74Exception detail: " + e); } finally diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java index 7f16944bf..0c811b0a0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java @@ -138,7 +138,7 @@ public class ServerApi //=======================// public void serverChunkLoadEvent(IChunkWrapper chunkWrapper, ILevelWrapper level) { SharedApi.INSTANCE.applyChunkUpdate(chunkWrapper, level, false); } - public void serverChunkSaveEvent(IChunkWrapper chunkWrapper, ILevelWrapper level) { SharedApi.INSTANCE.applyChunkUpdate(chunkWrapper, level, false); } + public void serverChunkSaveEvent(IChunkWrapper chunkWrapper, ILevelWrapper level) { SharedApi.INSTANCE.applyChunkUpdate(chunkWrapper, level, true); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java index 91ed0745b..26b1afa9b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java @@ -29,7 +29,8 @@ import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; -import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; +import com.seibel.distanthorizons.core.sql.repo.AbstractDhRepo; import com.seibel.distanthorizons.core.util.TimerUtil; import com.seibel.distanthorizons.core.util.objects.Pair; import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; @@ -56,7 +57,7 @@ public class SharedApi private static final Set UPDATING_CHUNK_POS_SET = ConcurrentHashMap.newKeySet(); /** how many chunks can be queued for updating per thread, used to prevent updates from infinitely pilling up if the user flys around extremely fast */ private static final int MAX_UPDATING_CHUNK_COUNT_PER_THREAD = 500; - private static final int MIN_MS_BETWEEN_OVERLOADED_LOG_MESSAGE = 5_000; + private static final int MIN_MS_BETWEEN_OVERLOADED_LOG_MESSAGE = 30_000; private static final Timer CHUNK_UPDATE_TIMER = TimerUtil.CreateTimer("ChunkUpdateTimer"); @@ -101,7 +102,9 @@ public class SharedApi { DebugRenderer.clearRenderables(); MC_RENDER.clearTargetFrameBuffer(); - // needs to be closed on world shutdown to clear out un-processed chunks + // shouldn't be necessary, but if we missed closing one of the connections this should make sure they're all closed + AbstractDhRepo.closeAllConnections(); + // needs to be closed on world shutdown to clear out un-processed chunks UPDATING_CHUNK_POS_SET.clear(); } @@ -138,7 +141,8 @@ public class SharedApi * Used to prevent getting a full chunk from MC if it isn't necessary.
* This is important since asking MC for a chunk is slow and may block the render thread. */ - public static boolean isChunkAtBlockPosAlreadyUpdating(int blockPosX, int blockPosZ) { return UPDATING_CHUNK_POS_SET.contains(new DhChunkPos(new DhBlockPos2D(blockPosX, blockPosZ))); } + public static boolean isChunkAtBlockPosAlreadyUpdating(int blockPosX, int blockPosZ) + { return UPDATING_CHUNK_POS_SET.contains(new DhChunkPos(new DhBlockPos2D(blockPosX, blockPosZ))); } /** handles both block place and break events */ @@ -215,7 +219,13 @@ public class SharedApi if (msBetweenLastLog >= MIN_MS_BETWEEN_OVERLOADED_LOG_MESSAGE) { lastOverloadedLogMessageMsTime = System.currentTimeMillis(); - LOGGER.warn("Too many chunks queued for updating, max queue count ["+maxQueueCount+"] (["+MAX_UPDATING_CHUNK_COUNT_PER_THREAD+"] per thread). This may result in holes in your LODs. Please move through the world slower, decrease your vanilla render distance, slow down your world pre-generator, or increase the CPU load config."); + + String message = "Distant Horizons overloaded, too many chunks queued for updating. " + + "\nThis may result in holes in your LODs. " + + "\nPlease move through the world slower, decrease your vanilla render distance, slow down your world pre-generator, or increase the Distant Horizons' CPU load config. " + + "\nMax queue count ["+maxQueueCount+"] (["+MAX_UPDATING_CHUNK_COUNT_PER_THREAD+"] per thread)."; + ClientApi.INSTANCE.showChatMessageNextFrame(message); + LOGGER.warn(message); } return; @@ -310,6 +320,18 @@ public class SharedApi } + // having a list of the nearby chunks is needed for lighting and beacon generation + ArrayList nearbyChunkList; + if (neighbourChunkList != null) + { + nearbyChunkList = neighbourChunkList; + } + else + { + nearbyChunkList = new ArrayList<>(1); + nearbyChunkList.add(chunkWrapper); + } + // Save or populate the chunk wrapper's lighting // this is done so we don't have to worry about MC unloading the lighting data for this chunk @@ -319,7 +341,7 @@ public class SharedApi try { // If MC's lighting engine isn't thread safe this may cause the server thread to lag - chunkWrapper.bakeDhLightingUsingMcLightingEngine(); // TODO handle unlit chunks, would pulling in the chunk from disk be a good idea? Look at ChunkLoader in the world gen code for an example + chunkWrapper.bakeDhLightingUsingMcLightingEngine(); } catch (IllegalStateException e) { @@ -329,21 +351,16 @@ public class SharedApi else { // generate the chunk's lighting, using neighboring chunks if present - - ArrayList nearbyChunkList; - if (neighbourChunkList != null) - { - nearbyChunkList = neighbourChunkList; - } - else - { - nearbyChunkList = new ArrayList<>(1); - nearbyChunkList.add(chunkWrapper); - } - DhLightingEngine.INSTANCE.lightChunk(chunkWrapper, nearbyChunkList, dhLevel.hasSkyLight() ? 15 : 0); } + + + // get this chunk's active beacons + List beaconBeamList = chunkWrapper.getAllActiveBeacons(nearbyChunkList); + dhLevel.setBeaconBeamsForChunk(chunkWrapper.getChunkPos(), beaconBeamList); + + dhLevel.updateChunkAsync(chunkWrapper); dhLevel.setChunkHash(chunkWrapper.getChunkPos(), newChunkHash); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 9233e59af..f865f9ec7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -20,17 +20,20 @@ package com.seibel.distanthorizons.core.config; +import com.seibel.distanthorizons.api.DhApi; import com.seibel.distanthorizons.api.enums.config.*; import com.seibel.distanthorizons.api.enums.config.quickOptions.*; import com.seibel.distanthorizons.api.enums.rendering.*; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode; import com.seibel.distanthorizons.core.config.eventHandlers.*; import com.seibel.distanthorizons.core.config.eventHandlers.presets.*; +import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener; import com.seibel.distanthorizons.core.config.types.*; import com.seibel.distanthorizons.core.config.types.enums.*; 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.IMinecraftSharedWrapper; import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.util.StringUtil; @@ -136,6 +139,7 @@ public class Config public static ConfigCategory fog = new ConfigCategory.Builder().set(Fog.class).build(); public static ConfigCategory ssao = new ConfigCategory.Builder().set(Ssao.class).build(); public static ConfigCategory noiseTextureSettings = new ConfigCategory.Builder().set(NoiseTextureSettings.class).build(); + public static ConfigCategory genericRendering = new ConfigCategory.Builder().set(GenericRendering.class).build(); public static ConfigCategory advancedGraphics = new ConfigCategory.Builder().set(AdvancedGraphics.class).build(); @@ -288,7 +292,7 @@ public class Config private static final Double FOG_RANGE_MAX = Math.sqrt(2.0); public static ConfigEntry farFogStart = new ConfigEntry.Builder() - .setMinDefaultMax(FOG_RANGE_MIN, 0.0, FOG_RANGE_MAX) + .setMinDefaultMax(FOG_RANGE_MIN, 0.4, FOG_RANGE_MAX) .comment("" + "At what distance should the far fog start? \n" + "\n" @@ -519,7 +523,8 @@ public class Config public static ConfigEntry noiseSteps = new ConfigEntry.Builder() .setMinDefaultMax(1, 4, null) .comment("" - + "How many steps of noise should be applied to LODs?") + + "How many steps of noise should be applied to LODs?" + + "") .build(); public static ConfigEntry noiseIntensity = new ConfigEntry.Builder() // TODO: Make this a float (the ClassicConfigGUI doesn't support floats) @@ -532,7 +537,34 @@ public class Config .setMinDefaultMax(0, 1024, null) .comment("" + "Defines how far should the noise texture render before it fades away. (in blocks) \n" - + "Set to 0 to disable noise from fading away") + + "Set to 0 to disable noise from fading away \n" + + "") + .build(); + + } + + public static class GenericRendering + { + public static ConfigEntry enableRendering = new ConfigEntry.Builder() + .set(true) + .comment("" + + "If true non terrain objects will be rendered in DH's terrain. \n" + + "This includes beacon beams and clouds. \n" + + "") + .build(); + + public static ConfigEntry enableBeaconRendering = new ConfigEntry.Builder() + .set(true) + .comment("" + + "If true LOD beacon beams will be rendered. \n" + + "") + .build(); + + public static ConfigEntry enableCloudRendering = new ConfigEntry.Builder() + .set(true) + .comment("" + + "If true LOD clouds will be rendered. \n" + + "") .build(); } @@ -589,8 +621,10 @@ public class Config + " does not have a ceiling.") .build(); + @Deprecated public static ConfigEntry caveCullingHeight = new ConfigEntry.Builder() .setMinDefaultMax(-4096, 40, 4096) + .setAppearance(EConfigEntryAppearance.ONLY_IN_API) .comment("" + "At what Y value should cave culling start?") .build(); @@ -761,6 +795,20 @@ public class Config + "") .build(); + public static ConfigEntry pullLightingForPregeneratedChunks = new ConfigEntry.Builder() + .set(false) + .comment("" + + "If true LOD generation for pre-existing chunks will attempt to pull the lighting data \n" + + "saved in Minecraft's Region files. \n" + + "If false DH will pull in chunks without lighting and re-light them. \n" + + " \n" + + "Setting this to true will result in faster LOD generation \n" + + "for already generated worlds, but is broken by most lighting mods. \n" + + " \n" + + "Set this to false if LODs are black. \n" + + "") + .build(); + public static ConfigEntry dataCompression = new ConfigEntry.Builder() .set(EDhApiDataCompressionMode.LZMA2) .comment("" @@ -809,13 +857,47 @@ public class Config + "") .build(); - //public static ConfigEntry showMigrationChatWarning = new ConfigEntry.Builder() - // .set(true) - // .comment("" - // + "Determines if a message should be displayed in the chat when LOD migration starts. \n" - // + "") - // .build(); + public static ConfigEntry ignoredRenderBlockCsv = new ConfigEntry.Builder() + .set("minecraft:barrier,minecraft:structure_void,minecraft:light,minecraft:tripwire") + .comment("" + + "A comma separated list of block resource locations that won't be rendered by DH. \n" + + "Note: air is always included in this list. \n" + + "") + .build(); + public static ConfigEntry ignoredRenderCaveBlockCsv = new ConfigEntry.Builder() + .set("minecraft:glow_lichen,minecraft:rail,minecraft:water,minecraft:lava,minecraft:bubble_column") + .comment("" + + "A comma separated list of block resource locations that shouldn't be rendered \n" + + "if they are in a 0 sky light underground area. \n" + + "Note: air is always included in this list. \n" + + "") + .build(); + + static + { + ignoredRenderBlockCsv.addListener(new ConfigChangeListener(Config.Client.Advanced.LodBuilding.ignoredRenderBlockCsv, + (blockCsv) -> + { + IWrapperFactory wrapperFactory = SingletonInjector.INSTANCE.get(IWrapperFactory.class); + if (wrapperFactory != null) + { + wrapperFactory.resetRendererIgnoredBlocksSet(); + DhApi.Delayed.renderProxy.clearRenderDataCache(); + } + })); + + ignoredRenderCaveBlockCsv.addListener(new ConfigChangeListener(Config.Client.Advanced.LodBuilding.ignoredRenderCaveBlockCsv, + (blockCsv) -> + { + IWrapperFactory wrapperFactory = SingletonInjector.INSTANCE.get(IWrapperFactory.class); + if (wrapperFactory != null) + { + wrapperFactory.resetRendererIgnoredCaveBlocks(); + DhApi.Delayed.renderProxy.clearRenderDataCache(); + } + })); + } } public static class Multiplayer @@ -1192,7 +1274,7 @@ public class Config // TODO default to error chat and info file public static ConfigEntry logWorldGenEvent = new ConfigEntry.Builder() .setServersideShortName("logWorldGenEvent") - .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_FILE) + .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT) .comment("" + "If enabled, the mod will log information about the world generation process. \n" + "This can be useful for debugging.") @@ -1200,7 +1282,7 @@ public class Config public static ConfigEntry logWorldGenPerformance = new ConfigEntry.Builder() .setServersideShortName("logWorldGenPerformance") - .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_FILE) + .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT) .comment("" + "If enabled, the mod will log performance about the world generation process. \n" + "This can be useful for debugging.") @@ -1208,7 +1290,7 @@ public class Config public static ConfigEntry logWorldGenLoadEvent = new ConfigEntry.Builder() .setServersideShortName("logWorldGenPerformance") - .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_FILE) + .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT) .comment("" + "If enabled, the mod will log information about the world generation process. \n" + "This can be useful for debugging.") @@ -1216,21 +1298,21 @@ public class Config public static ConfigEntry logLodBuilderEvent = new ConfigEntry.Builder() .setServersideShortName("logLodBuilderEvent") - .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE) + .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT) .comment("" + "If enabled, the mod will log information about the LOD generation process. \n" + "This can be useful for debugging.") .build(); public static ConfigEntry logRendererBufferEvent = new ConfigEntry.Builder() - .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE) + .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT) .comment("" + "If enabled, the mod will log information about the renderer buffer process. \n" + "This can be useful for debugging.") .build(); public static ConfigEntry logRendererGLEvent = new ConfigEntry.Builder() - .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE) + .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT) .comment("" + "If enabled, the mod will log information about the renderer OpenGL process. \n" + "This can be useful for debugging.") @@ -1238,7 +1320,7 @@ public class Config public static ConfigEntry logFileReadWriteEvent = new ConfigEntry.Builder() .setServersideShortName("logFileReadWriteEvent") - .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE) + .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT) .comment("" + "If enabled, the mod will log information about file read/write operations. \n" + "This can be useful for debugging.") @@ -1246,7 +1328,7 @@ public class Config public static ConfigEntry logFileSubDimEvent = new ConfigEntry.Builder() .setServersideShortName("logFileSubDimEvent") - .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE) + .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT) .comment("" + "If enabled, the mod will log information about file sub-dimension operations. \n" + "This can be useful for debugging.") @@ -1254,7 +1336,7 @@ public class Config public static ConfigEntry logNetworkEvent = new ConfigEntry.Builder() .setServersideShortName("logNetworkEvent") - .set(EDhApiLoggerMode.LOG_WARNING_TO_CHAT_AND_INFO_TO_FILE) + .set(EDhApiLoggerMode.LOG_ERROR_TO_CHAT) .comment("" + "If enabled, the mod will log information about network operations. \n" + "This can be useful for debugging.") @@ -1268,6 +1350,13 @@ public class Config + "memory allocated to run DH well.") .build(); + public static ConfigEntry showReplayWarningOnStartup = new ConfigEntry.Builder() + .set(true) + .comment("" + + "If enabled, a chat message will be displayed when a replay is started \n" + + "giving some basic information about how DH will function.") + .build(); + } public static class Debugging @@ -1335,6 +1424,15 @@ public class Config + "") .build(); + public static ConfigEntry logBufferGarbageCollection = new ConfigEntry.Builder() + .set(false) + .comment("" + + "If true OpenGL Buffer garbage collection will be logged \n" + + "this also includes the number of live buffers. \n" + + "") + .build(); + + // Note: This will reset on game restart, and should have a warning on the tooltip public static ConfigEntry allowUnsafeValues = new ConfigEntry.Builder() .set(false) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java index 5a877f9f7..b982c621d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java @@ -260,20 +260,38 @@ public class ConfigEntry extends AbstractConfigType> implem public byte isValid(T value, T min, T max) { if (this.configBase.disableMinMax) + { return 0; - - if (value == null || this.value == null || value.getClass() != this.value.getClass()) // If the 2 variables aren't the same type then it will be invalid + } + else if (min == null && max == null) + { + // no validation is needed for this field + return 0; + } + else if (value == null || this.value == null + || value.getClass() != this.value.getClass()) + { + // If the 2 variables aren't the same type then it will be invalid return 2; - if (Number.class.isAssignableFrom(value.getClass())) - { // Only check min max if it is a number + } + else if (Number.class.isAssignableFrom(value.getClass())) + { + // Only check min max if it is a number if (max != null && NumberUtil.greaterThan((Number) value, (Number) max)) + { return 1; + } if (min != null && NumberUtil.lessThan((Number) value, (Number) min)) + { return -1; + } return 0; } - return 0; + else + { + return 0; + } } /** This should normally not be called since set() automatically calls this */ diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigLinkedEntry.java b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigLinkedEntry.java index bac72896c..5483f7769 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigLinkedEntry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigLinkedEntry.java @@ -27,6 +27,7 @@ import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryAppearance * * @author coolGi */ +@Deprecated // FIXME doesn't work with localization public class ConfigLinkedEntry extends AbstractConfigType, ConfigLinkedEntry> { public ConfigLinkedEntry(AbstractConfigType value) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/FullDataSourceV2.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/FullDataSourceV2.java index d10f2361e..87519bc3f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/FullDataSourceV2.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/FullDataSourceV2.java @@ -62,6 +62,8 @@ public class FullDataSourceV2 implements IDataSource /** measured in data columns */ public static final int WIDTH = 64; + /** how many chunks wide this datasource is. */ + public static final int NUMB_OF_CHUNKS_WIDE = WIDTH / LodUtil.CHUNK_WIDTH; public static final byte DATA_FORMAT_VERSION = 1; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderSource.java index 9d3407c46..a00fb750d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/ColumnRenderSource.java @@ -182,7 +182,7 @@ public class ColumnRenderSource implements IDataSource EDhApiWorldGenerationStep worldGenStep = inputFullDataSource.getWorldGenStepAtRelativePos(x, z); if (dataColumn != null && worldGenStep != EDhApiWorldGenerationStep.EMPTY) { - FullDataToRenderDataTransformer.convertColumnData( + FullDataToRenderDataTransformer.updateRenderDataViewWithFullDataColumn( level, inputFullDataSource.mapping, minBlockPos.x + x, minBlockPos.z + z, diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnBox.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnBox.java index edf2a4c67..0cc51e422 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnBox.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnBox.java @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding; +import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.enums.EDhDirection; import com.seibel.distanthorizons.core.util.ColorUtil; @@ -475,10 +476,10 @@ public class ColumnBox throw new RuntimeException("Loop error"); if (previousAdjDepth > adjYMax) { - if (irisBlockMaterialId == IBlockStateWrapper.IrisBlockMaterial.GRASS) + if (irisBlockMaterialId == EDhApiBlockMaterial.GRASS.index) { // this LOD is underneath another, grass will never show here - irisBlockMaterialId = IBlockStateWrapper.IrisBlockMaterial.DIRT; + irisBlockMaterialId = EDhApiBlockMaterial.DIRT.index; } builder.addQuadAdj(direction, x, adjYMax, z, horizontalWidth, (short) (previousAdjDepth - adjYMax), color, irisBlockMaterialId, diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java index 8762bec32..8a95bc2c7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java @@ -50,8 +50,11 @@ public class ColumnRenderBuffer implements AutoCloseable private static final long MAX_BUFFER_UPLOAD_TIMEOUT_NANOSECONDS = 1_000_000; - public static final int QUADS_BYTE_SIZE = LodUtil.LOD_VERTEX_FORMAT.getByteSize() * 4; // TODO what does the 4 represent - public static final int MAX_QUADS_PER_BUFFER = (1024 * 1024 * 1) / QUADS_BYTE_SIZE; // TODO what do these multiples represent? + /** number of bytes a single quad takes */ + public static final int QUADS_BYTE_SIZE = LodUtil.LOD_VERTEX_FORMAT.getByteSize() * 4; + /** how big a single VBO can be in bytes */ + public static final int MAX_VBO_BYTE_SIZE = 10 * 1024 * 1024; // 10 MB + public static final int MAX_QUADS_PER_BUFFER = MAX_VBO_BYTE_SIZE / QUADS_BYTE_SIZE; public static final int FULL_SIZED_BUFFER = MAX_QUADS_PER_BUFFER * QUADS_BYTE_SIZE; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java index 02c064408..64c1abd02 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java @@ -54,11 +54,6 @@ public class ColumnRenderBufferBuilder private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - public static final int MAX_NUMBER_OF_CONCURRENT_CALLS_PER_THREAD = 3; - public static int maxNumberOfConcurrentCalls = MAX_NUMBER_OF_CONCURRENT_CALLS_PER_THREAD; - - - //==============// // vbo building // @@ -87,33 +82,14 @@ public class ColumnRenderBufferBuilder { boolean enableTransparency = Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled; - //EVENT_LOGGER.trace("RenderRegion start QuadBuild @ " + renderSource.sectionPos); - boolean enableSkyLightCulling = - Config.Client.Advanced.Graphics.AdvancedGraphics.enableCaveCulling.get() - && ( - // dimensions with a ceiling will be all caves so we don't want cave culling - !clientLevel.getLevelWrapper().hasCeiling() - // the end has a lot of overhangs with 0 lighting above the void, which look broken with - // the current cave culling logic (this could probably be improved, but just skipping it works best for now) - && !clientLevel.getLevelWrapper().getDimensionType().isTheEnd() - // FIXME temporary fix - // Cave culling is currently broken for any detail level above 0 - && DhSectionPos.getDetailLevel(renderSource.pos) == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL - ); - - int skyLightCullingBelow = Config.Client.Advanced.Graphics.AdvancedGraphics.caveCullingHeight.get(); - // FIXME: Clamp also to the max world height. - skyLightCullingBelow = Math.max(skyLightCullingBelow, clientLevel.getMinY()); - - long builderStartTime = System.currentTimeMillis(); - LodQuadBuilder builder = new LodQuadBuilder(enableSkyLightCulling, (short) (skyLightCullingBelow - clientLevel.getMinY()), enableTransparency, clientLevel.getClientLevelWrapper()); + LodQuadBuilder builder = new LodQuadBuilder(enableTransparency, clientLevel.getClientLevelWrapper()); makeLodRenderData(builder, renderSource, adjData); long builderEndTime = System.currentTimeMillis(); long buildMs = builderEndTime - builderStartTime; - LOGGER.debug("RenderRegion end QuadBuild @ " + renderSource.pos + " took: " + buildMs); + //LOGGER.debug("RenderRegion end QuadBuild @ " + renderSource.pos + " took: " + buildMs); return builder; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/CubicLodTemplate.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/CubicLodTemplate.java index 87b2e7720..a45fb2ec4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/CubicLodTemplate.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/CubicLodTemplate.java @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding; +import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.render.ColumnRenderSource; import com.seibel.distanthorizons.core.pos.DhLodPos; @@ -95,53 +96,54 @@ public class CubicLodTemplate } case SHOW_BLOCK_MATERIAL: { - switch (blockMaterialId) + + switch (EDhApiBlockMaterial.getFromIndex(blockMaterialId)) { - case IBlockStateWrapper.IrisBlockMaterial.UNKOWN: - case IBlockStateWrapper.IrisBlockMaterial.AIR: // shouldn't normally be rendered, but just in case + case UNKNOWN: + case AIR: // shouldn't normally be rendered, but just in case color = ColorUtil.HOT_PINK; break; - case IBlockStateWrapper.IrisBlockMaterial.LEAVES: + case LEAVES: color = ColorUtil.DARK_GREEN; break; - case IBlockStateWrapper.IrisBlockMaterial.STONE: + case STONE: color = ColorUtil.GRAY; break; - case IBlockStateWrapper.IrisBlockMaterial.WOOD: + case WOOD: color = ColorUtil.BROWN; break; - case IBlockStateWrapper.IrisBlockMaterial.METAL: + case METAL: color = ColorUtil.DARK_GRAY; break; - case IBlockStateWrapper.IrisBlockMaterial.DIRT: + case DIRT: color = ColorUtil.LIGHT_BROWN; break; - case IBlockStateWrapper.IrisBlockMaterial.LAVA: + case LAVA: color = ColorUtil.ORANGE; break; - case IBlockStateWrapper.IrisBlockMaterial.DEEPSLATE: + case DEEPSLATE: color = ColorUtil.BLACK; break; - case IBlockStateWrapper.IrisBlockMaterial.SNOW: + case SNOW: color = ColorUtil.WHITE; break; - case IBlockStateWrapper.IrisBlockMaterial.SAND: + case SAND: color = ColorUtil.TAN; break; - case IBlockStateWrapper.IrisBlockMaterial.TERRACOTTA: + case TERRACOTTA: color = ColorUtil.DARK_ORANGE; break; - case IBlockStateWrapper.IrisBlockMaterial.NETHER_STONE: + case NETHER_STONE: color = ColorUtil.DARK_RED; break; - case IBlockStateWrapper.IrisBlockMaterial.WATER: + case WATER: color = ColorUtil.BLUE; break; - case IBlockStateWrapper.IrisBlockMaterial.GRASS: + case GRASS: color = ColorUtil.GREEN; break; - case IBlockStateWrapper.IrisBlockMaterial.ILLUMINATED: + case ILLUMINATED: color = ColorUtil.YELLOW; break; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodQuadBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodQuadBuilder.java index 07744dcb9..ab3605bdd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodQuadBuilder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/LodQuadBuilder.java @@ -24,6 +24,7 @@ import java.nio.ByteOrder; import java.util.*; import com.seibel.distanthorizons.api.enums.config.EDhApiGrassSideRendering; +import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial; import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; @@ -50,7 +51,9 @@ public class LodQuadBuilder private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); + @Deprecated public final boolean skipQuadsWithZeroSkylight; + @Deprecated public final short skyLightCullingBelow; @SuppressWarnings("unchecked") @@ -122,7 +125,7 @@ public class LodQuadBuilder // constructor // //=============// - public LodQuadBuilder(boolean enableSkylightCulling, short skyLightCullingBelow, boolean doTransparency, IClientLevelWrapper clientLevelWrapper) + public LodQuadBuilder(boolean doTransparency, IClientLevelWrapper clientLevelWrapper) { this.doTransparency = doTransparency; for (int i = 0; i < 6; i++) @@ -131,8 +134,8 @@ public class LodQuadBuilder this.transparentQuads[i] = new ArrayList<>(); } - this.skipQuadsWithZeroSkylight = enableSkylightCulling; - this.skyLightCullingBelow = skyLightCullingBelow; + this.skipQuadsWithZeroSkylight = false; + this.skyLightCullingBelow = 0; this.clientLevelWrapper = clientLevelWrapper; this.debugRenderingMode = Config.Client.Advanced.Debugging.debugRendering.get(); @@ -273,7 +276,7 @@ public class LodQuadBuilder int color = quad.color; // use custom side color logic for grass blocks - if (quad.irisBlockMaterialId == IBlockStateWrapper.IrisBlockMaterial.GRASS) + if (quad.irisBlockMaterialId == EDhApiBlockMaterial.GRASS.index) { // only use dirt colors if debug rendering is disabled if (this.debugRenderingMode == EDhApiDebugRendering.OFF) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java index 6f3117a6b..5c658e232 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java @@ -20,6 +20,7 @@ package com.seibel.distanthorizons.core.dataObjects.transformers; import com.seibel.distanthorizons.api.enums.config.EDhApiBlocksToAvoid; +import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; @@ -32,6 +33,7 @@ import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.ColorUtil; import com.seibel.distanthorizons.core.util.FullDataPointUtil; +import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.RenderDataPointUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; @@ -109,74 +111,85 @@ public class FullDataToRenderDataTransformer } columnSource.markNotEmpty(); + int baseX = DhSectionPos.getMinCornerBlockX(pos); + int baseZ = DhSectionPos.getMinCornerBlockZ(pos); - if (dataDetail == columnSource.getDataDetailLevel()) + for (int x = 0; x < DhSectionPos.getWidthCountForLowerDetailedSection(pos, dataDetail); x++) { - int baseX = DhSectionPos.getMinCornerBlockX(pos); - int baseZ = DhSectionPos.getMinCornerBlockZ(pos); - - for (int x = 0; x < DhSectionPos.getWidthCountForLowerDetailedSection(pos, dataDetail); x++) + for (int z = 0; z < DhSectionPos.getWidthCountForLowerDetailedSection(pos, dataDetail); z++) { - for (int z = 0; z < DhSectionPos.getWidthCountForLowerDetailedSection(pos, dataDetail); z++) - { - throwIfThreadInterrupted(); - - ColumnArrayView columnArrayView = columnSource.getVerticalDataPointView(x, z); - LongArrayList dataColumn = fullDataSource.get(x, z); - convertColumnData(level, fullDataSource.mapping, baseX + x, baseZ + z, columnArrayView, dataColumn); - } + throwIfThreadInterrupted(); + + ColumnArrayView columnArrayView = columnSource.getVerticalDataPointView(x, z); + LongArrayList dataColumn = fullDataSource.get(x, z); + updateRenderDataViewWithFullDataColumn(level, fullDataSource.mapping, baseX + x, baseZ + z, columnArrayView, dataColumn); } - - columnSource.fillDebugFlag(0, 0, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.DebugSourceFlag.FULL); - - } - else - { - throw new UnsupportedOperationException("To be implemented"); - //FIXME: Implement different size creation of renderData } + + columnSource.fillDebugFlag(0, 0, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.SECTION_SIZE, ColumnRenderSource.DebugSourceFlag.FULL); + return columnSource; } - - - //================// - // helper methods // - //================// - - /** - * Called in loops that may run for an extended period of time.
- * This is necessary to allow canceling these transformers since running - * them after the client has left a given world will throw exceptions. - */ - private static void throwIfThreadInterrupted() throws InterruptedException + /** Updates the given {@link ColumnArrayView} to match the incoming Full data {@link LongArrayList} */ + public static void updateRenderDataViewWithFullDataColumn( + IDhClientLevel level, + FullDataPointIdMap fullDataMapping, int blockX, int blockZ, + ColumnArrayView columnArrayView, + LongArrayList fullDataColumn) { - if (Thread.interrupted()) + if (fullDataColumn == null || fullDataColumn.size() == 0) { - throw new InterruptedException(FullDataToRenderDataTransformer.class.getSimpleName() + " task interrupted."); + return; + } + + int dataTotalLength = fullDataColumn.size(); + if (dataTotalLength > columnArrayView.verticalSize()) + { + ColumnArrayView totalColumnData = new ColumnArrayView(new LongArrayList(new long[dataTotalLength]), dataTotalLength, 0, dataTotalLength); + iterateAndConvert(level, fullDataMapping, blockX, blockZ, totalColumnData, fullDataColumn); + columnArrayView.changeVerticalSizeFrom(totalColumnData); + } + else + { + iterateAndConvert(level, fullDataMapping, blockX, blockZ, columnArrayView, fullDataColumn); //Directly use the arrayView since it fits. } } - - - // TODO what does this mean? private static void iterateAndConvert( - IDhClientLevel level, FullDataPointIdMap fullDataMapping, - int blockX, int blockZ, + IDhClientLevel level, FullDataPointIdMap fullDataMapping, + int blockX, int blockZ, ColumnArrayView renderColumnData, LongArrayList fullColumnData) { - boolean avoidSolidBlocks = (Config.Client.Advanced.Graphics.Quality.blocksToIgnore.get() == EDhApiBlocksToAvoid.NON_COLLIDING); + boolean ignoreNonCollidingBlocks = (Config.Client.Advanced.Graphics.Quality.blocksToIgnore.get() == EDhApiBlocksToAvoid.NON_COLLIDING); boolean colorBelowWithAvoidedBlocks = Config.Client.Advanced.Graphics.Quality.tintWithAvoidedBlocks.get(); HashSet blockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredBlocks(level.getLevelWrapper()); + HashSet caveBlockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredCaveBlocks(level.getLevelWrapper()); + + boolean caveCullingEnabled = + Config.Client.Advanced.Graphics.AdvancedGraphics.enableCaveCulling.get() + && ( + // dimensions with a ceiling will be all caves so we don't want cave culling + !level.getLevelWrapper().hasCeiling() + // the end has a lot of overhangs with 0 lighting above the void, which look broken with + // the current cave culling logic (this could probably be improved, but just skipping it works best for now) + && !level.getLevelWrapper().getDimensionType().isTheEnd() + ); boolean isVoid = true; + int colorToApplyToNextBlock = -1; int lastColor = 0; int lastBottom = -10000; + int skylightToApplyToNextBlock = -1; int blocklightToApplyToNextBlock = -1; int columnOffset = 0; + IBiomeWrapper biome = null; + IBlockStateWrapper block = null; + + // goes from the top down for (int i = 0; i < fullColumnData.size(); i++) { @@ -187,23 +200,6 @@ public class FullDataToRenderDataTransformer int blockLight = FullDataPointUtil.getBlockLight(fullData); int skyLight = FullDataPointUtil.getSkyLight(fullData); - // TODO how should corrupted data be handled? - // TODO why is the full data corrupted in the first place? FullDataPointUtil hasn't been changed in a long time, could one of the full data point objects be corrupted? - // TODO if either of these happen the ID might also be invalid - //if (bottomY + blockHeight > 300) - //{ - // // this data point is too tall, it's probably a monolith - // int k = 0; - // throw new RuntimeException(); - //} - //if (light > 16 || light < 0) - //{ - // // light is out of range - // throw new RuntimeException(); - //} - - IBiomeWrapper biome; - IBlockStateWrapper block; try { biome = fullDataMapping.getBiomeWrapper(id); @@ -228,28 +224,72 @@ public class FullDataToRenderDataTransformer } - if (blockStatesToIgnore.contains(block)) + //====================// + // ignored block and // + // cave culling check // + //====================// + + boolean ignoreBlock = blockStatesToIgnore.contains(block); + boolean caveBlock = caveBlockStatesToIgnore.contains(block); + if (caveBlock) { - // Don't render: air, barriers, light blocks, etc. + if (caveCullingEnabled + // assume this data point is underground if it has no sky-light + && skyLight == LodUtil.MIN_MC_LIGHT + // cave culling shouldn't happen when at the top of the world + && columnOffset != 0 + // cave culling can't happen when at the bottom of the world + && columnOffset != fullColumnData.size()) + { + // we need to get the next sky/block lights because + // the air block here will always have a light of 0/0 due to only the top of the LOD's light being saved. + long nextFullData = fullColumnData.getLong(i+1); + int nextSkyLight = FullDataPointUtil.getSkyLight(nextFullData); + + if (nextSkyLight == LodUtil.MIN_MC_LIGHT + && ColorUtil.getAlpha(lastColor) == 255) + { + // replace the previous block with new bottom + long columnData = renderColumnData.get(columnOffset - 1); + columnData = RenderDataPointUtil.setYMin(columnData, bottomY); + renderColumnData.set(columnOffset - 1, columnData); + } + + continue; + } + + + if (ignoreBlock) + { + // this is a merged block and a cave block, so it should never be rendered + continue; + } + } + else if (ignoreBlock) + { + // this is an ignored block, but shouldn't be merged like a cave block continue; } - // solid block check - if (avoidSolidBlocks && !block.isSolid() && !block.isLiquid() && block.getOpacity() != IBlockStateWrapper.FULLY_OPAQUE) + //===================// + // solid block check // + //===================// + + if (ignoreNonCollidingBlocks && !block.isSolid() && !block.isLiquid() && block.getOpacity() != LodUtil.BLOCK_FULLY_OPAQUE) { if (colorBelowWithAvoidedBlocks) { int tempColor = level.computeBaseColor(new DhBlockPos(blockX, bottomY + level.getMinY(), blockZ), biome, block); - if (ColorUtil.getAlpha(tempColor) == 0) + // don't transfer the color when alpha is 0 + if (ColorUtil.getAlpha(tempColor) != 0) { - //make sure to not transfer the color when alpha is 0 - continue; + // don't transfer alpha if for some reason grass is semi transparent + colorToApplyToNextBlock = ColorUtil.setAlpha(tempColor,255); + + skylightToApplyToNextBlock = skyLight; + blocklightToApplyToNextBlock = blockLight; } - //mare sure to not trnasfer alpha if for some reason grass is semi transparent - colorToApplyToNextBlock = ColorUtil.setAlpha(tempColor,255); - skylightToApplyToNextBlock = skyLight; - blocklightToApplyToNextBlock = blockLight; } // don't add this block @@ -271,11 +311,11 @@ public class FullDataToRenderDataTransformer skyLight = skylightToApplyToNextBlock; blockLight = blocklightToApplyToNextBlock; } - - //check if they share a top-bottom face and if they have same collor + + //check if they share a top-bottom face and if they have same color if (color == lastColor && bottomY + blockHeight == lastBottom && columnOffset > 0) { - //replace the previus block with new bottom + //replace the previous block with new bottom long columnData = renderColumnData.get(columnOffset - 1); columnData = RenderDataPointUtil.setYMin(columnData, bottomY); renderColumnData.set(columnOffset - 1, columnData); @@ -284,13 +324,13 @@ public class FullDataToRenderDataTransformer { // add the block isVoid = false; - long columnData = RenderDataPointUtil.createDataPoint(bottomY + blockHeight, bottomY, color, skyLight, blockLight, block.getIrisBlockMaterialId()); + long columnData = RenderDataPointUtil.createDataPoint(bottomY + blockHeight, bottomY, color, skyLight, blockLight, block.getMaterialId()); renderColumnData.set(columnOffset, columnData); columnOffset++; } lastBottom = bottomY; lastColor = color; - + } @@ -300,24 +340,22 @@ public class FullDataToRenderDataTransformer } } - // TODO what does this mean? - public static void convertColumnData(IDhClientLevel level, FullDataPointIdMap fullDataMapping, int blockX, int blockZ, ColumnArrayView columnArrayView, LongArrayList fullDataColumn) + + + //================// + // helper methods // + //================// + + /** + * Called in loops that may run for an extended period of time.
+ * This is necessary to allow canceling these transformers since running + * them after the client has left a given world will throw exceptions. + */ + private static void throwIfThreadInterrupted() throws InterruptedException { - if (fullDataColumn == null || fullDataColumn.size() == 0) + if (Thread.interrupted()) { - return; - } - - int dataTotalLength = fullDataColumn.size(); - if (dataTotalLength > columnArrayView.verticalSize()) - { - ColumnArrayView totalColumnData = new ColumnArrayView(new LongArrayList(new long[dataTotalLength]), dataTotalLength, 0, dataTotalLength); - iterateAndConvert(level, fullDataMapping, blockX, blockZ, totalColumnData, fullDataColumn); - columnArrayView.changeVerticalSizeFrom(totalColumnData); - } - else - { - iterateAndConvert(level, fullDataMapping, blockX, blockZ, columnArrayView, fullDataColumn); //Directly use the arrayView since it fits. + throw new InterruptedException(FullDataToRenderDataTransformer.class.getSimpleName() + " task interrupted."); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/LodDataBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/LodDataBuilder.java index 5f3001621..917aad07e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/LodDataBuilder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/LodDataBuilder.java @@ -31,7 +31,6 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.enums.EDhDirection; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhBlockPos; -import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.FullDataPointUtil; import com.seibel.distanthorizons.core.util.LodUtil; @@ -47,8 +46,6 @@ public class LodDataBuilder { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final IBlockStateWrapper AIR = SingletonInjector.INSTANCE.get(IWrapperFactory.class).getAirBlockStateWrapper(); - /** how many chunks wide the {@link FullDataSourceV2} is. */ - private static final int NUMB_OF_CHUNKS_WIDE = FullDataSourceV2.WIDTH / LodUtil.CHUNK_WIDTH; private static boolean getTopErrorLogged = false; @@ -67,12 +64,8 @@ public class LodDataBuilder - // get the section position - int sectionPosX = chunkWrapper.getChunkPos().x; - // negative positions start at -1 so the logic there is slightly different - sectionPosX = (sectionPosX < 0) ? ((sectionPosX + 1) / NUMB_OF_CHUNKS_WIDE) - 1 : (sectionPosX / NUMB_OF_CHUNKS_WIDE); - int sectionPosZ = chunkWrapper.getChunkPos().z; - sectionPosZ = (sectionPosZ < 0) ? ((sectionPosZ + 1) / NUMB_OF_CHUNKS_WIDE) - 1 : (sectionPosZ / NUMB_OF_CHUNKS_WIDE); + int sectionPosX = getXOrZSectionPosFromChunkPos(chunkWrapper.getChunkPos().x); + int sectionPosZ = getXOrZSectionPosFromChunkPos(chunkWrapper.getChunkPos().z); long pos = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, sectionPosX, sectionPosZ); FullDataSourceV2 dataSource = FullDataSourceV2.createEmpty(pos); @@ -98,30 +91,30 @@ public class LodDataBuilder // -3 -> 1 // -4 -> 0 --- // -5 -> 3 - chunkOffsetX = ((chunkOffsetX) % NUMB_OF_CHUNKS_WIDE); + chunkOffsetX = ((chunkOffsetX) % FullDataSourceV2.NUMB_OF_CHUNKS_WIDE); if (chunkOffsetX != 0) { - chunkOffsetX += NUMB_OF_CHUNKS_WIDE; + chunkOffsetX += FullDataSourceV2.NUMB_OF_CHUNKS_WIDE; } } else { - chunkOffsetX %= NUMB_OF_CHUNKS_WIDE; + chunkOffsetX %= FullDataSourceV2.NUMB_OF_CHUNKS_WIDE; } chunkOffsetX *= LodUtil.CHUNK_WIDTH; int chunkOffsetZ = chunkWrapper.getChunkPos().z; if (chunkWrapper.getChunkPos().z < 0) { - chunkOffsetZ = ((chunkOffsetZ) % NUMB_OF_CHUNKS_WIDE); + chunkOffsetZ = ((chunkOffsetZ) % FullDataSourceV2.NUMB_OF_CHUNKS_WIDE); if (chunkOffsetZ != 0) { - chunkOffsetZ += NUMB_OF_CHUNKS_WIDE; + chunkOffsetZ += FullDataSourceV2.NUMB_OF_CHUNKS_WIDE; } } else { - chunkOffsetZ %= NUMB_OF_CHUNKS_WIDE; + chunkOffsetZ %= FullDataSourceV2.NUMB_OF_CHUNKS_WIDE; } chunkOffsetZ *= LodUtil.CHUNK_WIDTH; @@ -212,6 +205,7 @@ public class LodDataBuilder longs.add(FullDataPointUtil.encode(mappedId, lastY - y, y + 1 - chunkWrapper.getMinBuildHeight(), blockLight, skyLight)); biome = newBiome; blockState = newBlockState; + mappedId = dataSource.mapping.addIfNotPresentAndGetId(biome, blockState); blockLight = newBlockLight; skyLight = newSkyLight; @@ -296,19 +290,28 @@ public class LodDataBuilder // this block isn't on a chunk boundary, check if it is next to a transparent/air block IBlockStateWrapper blockState = chunkWrapper.getBlockState(testBlockPos); - return blockState.isAir() || blockState.getOpacity() != IBlockStateWrapper.FULLY_OPAQUE; + return blockState.isAir() || blockState.getOpacity() != LodUtil.BLOCK_FULLY_OPAQUE; } /** @throws ClassCastException if an API user returns the wrong object type(s) */ - public static FullDataSourceV2 createFromApiChunkData(DhApiChunk dataPoints) throws ClassCastException, DataCorruptedException + public static FullDataSourceV2 createFromApiChunkData(DhApiChunk apiChunk) throws ClassCastException, DataCorruptedException { - FullDataSourceV2 accessor = FullDataSourceV2.createEmpty(DhSectionPos.encode(new DhChunkPos(dataPoints.chunkPosX, dataPoints.chunkPosZ))); - for (int relZ = 0; relZ < LodUtil.CHUNK_WIDTH; relZ++) + // get the section position + int sectionPosX = getXOrZSectionPosFromChunkPos(apiChunk.chunkPosX); + int sectionPosZ = getXOrZSectionPosFromChunkPos(apiChunk.chunkPosZ); + long pos = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, sectionPosX, sectionPosZ); + + // chunk relative block position in the data source + int relSourceBlockX = Math.floorMod(apiChunk.chunkPosX, 4) * LodUtil.CHUNK_WIDTH; + int relSourceBlockZ = Math.floorMod(apiChunk.chunkPosZ, 4) * LodUtil.CHUNK_WIDTH; + + FullDataSourceV2 dataSource = FullDataSourceV2.createEmpty(pos); + for (int relBlockZ = 0; relBlockZ < LodUtil.CHUNK_WIDTH; relBlockZ++) { - for (int relX = 0; relX < LodUtil.CHUNK_WIDTH; relX++) + for (int relBlockX = 0; relBlockX < LodUtil.CHUNK_WIDTH; relBlockX++) { - List columnDataPoints = dataPoints.getDataPoints(relX, relZ); + List columnDataPoints = apiChunk.getDataPoints(relBlockX, relBlockZ); // this null check does 2 nice things at the same time: @@ -322,7 +325,7 @@ public class LodDataBuilder { DhApiTerrainDataPoint dataPoint = columnDataPoints.get(index); - int id = accessor.mapping.addIfNotPresentAndGetId( + int id = dataSource.mapping.addIfNotPresentAndGetId( (IBiomeWrapper) (dataPoint.biomeWrapper), (IBlockStateWrapper) (dataPoint.blockStateWrapper) ); @@ -330,7 +333,7 @@ public class LodDataBuilder packedDataPoints.set(index, FullDataPointUtil.encode( id, dataPoint.topYBlockPos - dataPoint.bottomYBlockPos, - dataPoint.bottomYBlockPos - dataPoints.topYBlockPos, + dataPoint.bottomYBlockPos - apiChunk.bottomYBlockPos, (byte) (dataPoint.blockLightLevel), (byte) (dataPoint.skyLightLevel) )); @@ -338,11 +341,14 @@ public class LodDataBuilder // TODO add the ability for API users to define a different compression mode // or add a "unkown" compression mode - accessor.setSingleColumn(packedDataPoints, relX, relZ, EDhApiWorldGenerationStep.LIGHT, EDhApiWorldCompressionMode.MERGE_SAME_BLOCKS); + dataSource.setSingleColumn( + packedDataPoints, + relBlockX + relSourceBlockX, relBlockZ + relSourceBlockZ, + EDhApiWorldGenerationStep.LIGHT, EDhApiWorldCompressionMode.MERGE_SAME_BLOCKS); + dataSource.isEmpty = false; } } - - return accessor; + return dataSource; } @@ -353,4 +359,13 @@ public class LodDataBuilder public static boolean canGenerateLodFromChunk(IChunkWrapper chunk) { return chunk != null && chunk.isLightCorrect(); } + public static int getXOrZSectionPosFromChunkPos(int chunkXOrZPos) + { + // get the section position + int sectionPos = chunkXOrZPos; + // negative positions start at -1 so the logic there is slightly different + sectionPos = (sectionPos < 0) ? ((sectionPos + 1) / FullDataSourceV2.NUMB_OF_CHUNKS_WIDE) - 1 : (sectionPos / FullDataSourceV2.NUMB_OF_CHUNKS_WIDE); + return sectionPos; + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/enums/EDhDirection.java b/core/src/main/java/com/seibel/distanthorizons/core/enums/EDhDirection.java index ff7cebd50..757200b18 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/enums/EDhDirection.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/enums/EDhDirection.java @@ -25,7 +25,7 @@ import java.util.Map; import java.util.function.Predicate; import java.util.stream.Collectors; -import com.seibel.distanthorizons.coreapi.util.math.Vec3i; +import com.seibel.distanthorizons.core.util.math.Vec3i; /** * An (almost) exact copy of Minecraft's diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java b/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java index 7c9c36308..628aabf80 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java @@ -41,13 +41,17 @@ import java.util.*; */ public class ClientOnlySaveStructure extends AbstractSaveStructure { - final File folder; - private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); - private static final IMinecraftSharedWrapper MC_SHARED = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class); + public static final String SERVER_DATA_FOLDER_NAME = "Distant_Horizons_server_data"; + public static final String REPLAY_SERVER_FOLDER_NAME = "REPLAY"; public static final String INVALID_FILE_CHARACTERS_REGEX = "[\\\\/:*?\"<>|]"; - SubDimensionLevelMatcher subDimMatcher = null; - final HashMap levelWrapperToFileMap = new HashMap<>(); + private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); + private static final IMinecraftSharedWrapper MC_SHARED = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class); + + + private SubDimensionLevelMatcher subDimMatcher = null; + private final File folder; + private final HashMap levelWrapperToFileMap = new HashMap<>(); @@ -237,7 +241,7 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure private static String getSaveStructureFolderPath() { String path = MC_SHARED.getInstallationDirectory().getPath() + File.separatorChar - + "Distant_Horizons_server_data" + File.separatorChar + + SERVER_DATA_FOLDER_NAME + File.separatorChar + getServerFolderName(); return path; } @@ -245,6 +249,14 @@ public class ClientOnlySaveStructure extends AbstractSaveStructure /** Generated from the server the client is currently connected to. */ private static String getServerFolderName() { + // if connected to a replay we won't have any server info + // use the dedicated replay server folder + if (MC_CLIENT.connectedToReplay()) + { + return REPLAY_SERVER_FOLDER_NAME; + } + + // parse the current server's IP ParsedIp parsedIp = new ParsedIp(MC_CLIENT.getCurrentServerIp()); String serverIpCleaned = parsedIp.ip.replaceAll(INVALID_FILE_CHARACTERS_REGEX, ""); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java index 6263bc23c..841b0b2f8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java @@ -190,7 +190,7 @@ public class SubDimensionLevelMatcher implements AutoCloseable } FullDataSourceV2 newChunkSizedFullDataView = FullDataSourceV2.createFromChunk(newlyLoadedChunk); // convert to a data source for easier comparing - FullDataSourceV2 newDataSource = FullDataSourceV2.createEmpty(DhSectionPos.encode(this.playerData.playerBlockPos)); + FullDataSourceV2 newDataSource = FullDataSourceV2.createEmpty(DhSectionPos.encodeContaining(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, this.playerData.playerBlockPos)); newDataSource.update(newChunkSizedFullDataView); @@ -215,7 +215,7 @@ public class SubDimensionLevelMatcher implements AutoCloseable // get the data source to compare against try (IDhLevel tempLevel = new DhClientLevel(new ClientOnlySaveStructure(), this.currentClientLevel, testLevelFolder, false, null)) { - testFullDataSource = tempLevel.getFullDataProvider().getAsync(DhSectionPos.encode(this.playerData.playerBlockPos)).join(); + testFullDataSource = tempLevel.getFullDataProvider().getAsync(DhSectionPos.encodeContaining(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, this.playerData.playerBlockPos)).join(); if (testFullDataSource == null) { continue; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/AdjacentChunkHolder.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/AdjacentChunkHolder.java new file mode 100644 index 000000000..a3d5603e3 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/AdjacentChunkHolder.java @@ -0,0 +1,117 @@ +package com.seibel.distanthorizons.core.generation; + +import com.seibel.distanthorizons.core.pos.DhChunkPos; +import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; +import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.HashSet; + +/** holds adjacent chunks without having to create new Pos objects */ +public class AdjacentChunkHolder +{ + final IChunkWrapper[] chunkArray = new IChunkWrapper[9]; + + + //==============// + // constructors // + //==============// + + public AdjacentChunkHolder(IChunkWrapper centerWrapper) { this.chunkArray[4] = centerWrapper; } + + public AdjacentChunkHolder(IChunkWrapper centerWrapper, @NotNull ArrayList nearbyChunkList) + { + this.chunkArray[4] = centerWrapper; + + DhChunkPos centerChunkPos = centerWrapper.getChunkPos(); + + // generate the list of chunk pos we need, + // currently a 3x3 grid + HashSet requestedAdjacentPositions = new HashSet<>(9); + for (int xOffset = -1; xOffset <= 1; xOffset++) + { + for (int zOffset = -1; zOffset <= 1; zOffset++) + { + DhChunkPos adjacentPos = new DhChunkPos(centerChunkPos.x + xOffset, centerChunkPos.z + zOffset); + requestedAdjacentPositions.add(adjacentPos); + } + } + + for (int chunkIndex = 0; chunkIndex < nearbyChunkList.size(); chunkIndex++) // using iterators in high traffic areas can cause GC issues due to allocating a bunch of iterators, use an indexed for-loop instead + { + IChunkWrapper chunk = nearbyChunkList.get(chunkIndex); + if (chunk != null && requestedAdjacentPositions.contains(chunk.getChunkPos())) + { + // remove the newly found position + requestedAdjacentPositions.remove(chunk.getChunkPos()); + + // add the adjacent chunk + this.add(chunk); + } + + if (requestedAdjacentPositions.isEmpty()) + { + // we found every chunk we needed, we don't need to keep iterating + break; + } + } + } + + + + //=========// + // methods // + //=========// + + public void add(IChunkWrapper centerWrapper) + { + DhChunkPos centerPos = this.chunkArray[4].getChunkPos(); + DhChunkPos offsetPos = centerWrapper.getChunkPos(); + + int offsetX = offsetPos.x - centerPos.x; + if (offsetX < -1 || offsetX > 1) + { + return; + } + + int offsetZ = offsetPos.z - centerPos.z; + if (offsetZ < -1 || offsetZ > 1) + { + return; + } + + // equivalent to 4 + offsetX + (offsetZ * 3). + this.chunkArray[4 + offsetX + offsetZ + (offsetZ << 1)] = centerWrapper; + } + + public IChunkWrapper getByBlockPos(int blockX, int blockZ) + { + int chunkX = BitShiftUtil.divideByPowerOfTwo(blockX, 4); + int chunkZ = BitShiftUtil.divideByPowerOfTwo(blockZ, 4); + IChunkWrapper centerChunk = this.chunkArray[4]; + DhChunkPos centerPos = centerChunk.getChunkPos(); + if (centerPos.x == chunkX && centerPos.z == chunkZ) + { + return centerChunk; + } + + int offsetX = chunkX - centerPos.x; + if (offsetX < -1 || offsetX > 1) + { + return null; + } + + int offsetZ = chunkZ - centerPos.z; + if (offsetZ < -1 || offsetZ > 1) + { + return null; + } + + // equivalent to 4 + offsetX + (offsetZ * 3). + return this.chunkArray[4 + offsetX + offsetZ + (offsetZ << 1)]; + } + + +} + \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/DhLightingEngine.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/DhLightingEngine.java index 6648f62ee..c160ef482 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/DhLightingEngine.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/DhLightingEngine.java @@ -26,7 +26,6 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; -import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import org.apache.logging.log4j.Logger; import java.util.*; @@ -152,7 +151,7 @@ public class DhLightingEngine for (int y = maxY; y >= minY; y--) { IBlockStateWrapper block = chunk.getBlockState(relX, y, relZ); - if (block != null && block.getOpacity() != IBlockStateWrapper.FULLY_TRANSPARENT) + if (block != null && block.getOpacity() != LodUtil.BLOCK_FULLY_TRANSPARENT) { // keep moving down until we find a non-transparent block break; @@ -306,64 +305,6 @@ public class DhLightingEngine } - /** holds the adjacent chunks without having to create new Pos objects */ - private static class AdjacentChunkHolder - { - final IChunkWrapper[] chunkArray = new IChunkWrapper[9]; - - - public AdjacentChunkHolder(IChunkWrapper centerWrapper) { this.chunkArray[4] = centerWrapper; } - - - public void add(IChunkWrapper centerWrapper) - { - DhChunkPos centerPos = this.chunkArray[4].getChunkPos(); - DhChunkPos offsetPos = centerWrapper.getChunkPos(); - - int offsetX = offsetPos.x - centerPos.x; - if (offsetX < -1 || offsetX > 1) - { - return; - } - - int offsetZ = offsetPos.z - centerPos.z; - if (offsetZ < -1 || offsetZ > 1) - { - return; - } - - // equivalent to 4 + offsetX + (offsetZ * 3). - this.chunkArray[4 + offsetX + offsetZ + (offsetZ << 1)] = centerWrapper; - } - - public IChunkWrapper getByBlockPos(int blockX, int blockZ) - { - int chunkX = BitShiftUtil.divideByPowerOfTwo(blockX, 4); - int chunkZ = BitShiftUtil.divideByPowerOfTwo(blockZ, 4); - IChunkWrapper centerChunk = this.chunkArray[4]; - DhChunkPos centerPos = centerChunk.getChunkPos(); - if (centerPos.x == chunkX && centerPos.z == chunkZ) - { - return centerChunk; - } - - int offsetX = chunkX - centerPos.x; - if (offsetX < -1 || offsetX > 1) - { - return null; - } - - int offsetZ = chunkZ - centerPos.z; - if (offsetZ < -1 || offsetZ > 1) - { - return null; - } - - // equivalent to 4 + offsetX + (offsetZ * 3). - return this.chunkArray[4 + offsetX + offsetZ + (offsetZ << 1)]; - } - } - /** * Holds all potential {@link LightPos} objects a lighting task may need. * This is done so existing {@link LightPos} objects can be repurposed instead of destroyed, diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java index 452a7b97e..64b8e9db3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java @@ -79,7 +79,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb // TODO this logic isn't great and can cause a limit to how many threads could be used for world generation, // however it won't cause duplicate requests or concurrency issues, so it will be good enough for now. // A good long term fix may be to either: - // 1. allow the generator to deal with larger sections (let the generator threads split up larger tasks into smaller one + // 1. allow the generator to deal with larger sections (let the generator threads split up larger tasks into smaller ones // 2. batch requests better. instead of sending 4 individual tasks of detail level N, send 1 task of detail level n+1 private final ExecutorService queueingThread = ThreadUtil.makeSingleThreadPool("World Gen Queue"); private boolean generationQueueRunning = false; @@ -227,6 +227,9 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb catch (Exception e) { LOGGER.error("queueing exception: " + e.getMessage(), e); + } + finally + { this.generationQueueRunning = false; } }); @@ -374,7 +377,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb // don't log the shutdown exceptions if (!LodUtil.isInterruptOrReject(exception)) { - LOGGER.error("Error generating data for section " + taskPos, exception); + LOGGER.error("Error generating data for pos: " + DhSectionPos.toString(taskPos), exception); } newTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateFail())); @@ -384,11 +387,11 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb newTaskGroup.group.worldGenTasks.forEach(worldGenTask -> worldGenTask.future.complete(WorldGenResult.CreateSuccess(DhSectionPos.encode(granularity, DhSectionPos.getX(taskPos), DhSectionPos.getZ(taskPos))))); } boolean worked = this.inProgressGenTasksByLodPos.remove(taskPos, newTaskGroup); - LodUtil.assertTrue(worked); + LodUtil.assertTrue(worked, "Unable to find in progress generator task with position ["+DhSectionPos.toString(taskPos)+"]"); } catch (Exception e) { - LOGGER.error("Unexpected error completing world gen task: "+taskPos, e); + LOGGER.error("Unexpected error completing world gen task at pos: ["+DhSectionPos.toString(taskPos)+"].", e); } }); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java index b556bdcff..1c3b1e08d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhLevel.java @@ -19,15 +19,28 @@ package com.seibel.distanthorizons.core.level; +import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiChunkModifiedEvent; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; +import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.dataObjects.transformers.ChunkToLodBuilder; import com.seibel.distanthorizons.core.file.fullDatafile.DelayedFullDataSourceSaveCache; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.render.renderer.generic.BeaconRenderHandler; +import com.seibel.distanthorizons.core.render.renderer.generic.CloudRenderHandler; +import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer; +import com.seibel.distanthorizons.core.render.renderer.generic.GenericRenderObjectFactory; +import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; import com.seibel.distanthorizons.core.sql.dto.ChunkHashDTO; +import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo; import com.seibel.distanthorizons.core.sql.repo.ChunkHashRepo; +import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import org.apache.logging.log4j.Logger; @@ -35,8 +48,12 @@ import org.jetbrains.annotations.Nullable; import java.io.File; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; public abstract class AbstractDhLevel implements IDhLevel { @@ -47,21 +64,37 @@ public abstract class AbstractDhLevel implements IDhLevel /** if this is null then the other handler is probably null too, but just in case */ @Nullable public ChunkHashRepo chunkHashRepo; + /** if this is null then the other handler is probably null too, but just in case */ + @Nullable + public BeaconBeamRepo beaconBeamRepo; protected final DelayedFullDataSourceSaveCache delayedFullDataSourceSaveCache = new DelayedFullDataSourceSaveCache(this::onDataSourceSave, 2_000); /** contains the {@link DhChunkPos} for each {@link DhSectionPos} that are queued to save via {@link AbstractDhLevel#delayedFullDataSourceSaveCache} */ protected final ConcurrentHashMap> updatedChunkPosSetBySectionPos = new ConcurrentHashMap<>(); + /** Will be null if clouds shouldn't be rendered for this level. */ + @Nullable + protected CloudRenderHandler cloudRenderHandler; + protected BeaconRenderHandler beaconRenderHandler; + //=============// // constructor // //=============// - protected AbstractDhLevel() { this.chunkToLodBuilder = new ChunkToLodBuilder(); } + protected AbstractDhLevel() + { + this.chunkToLodBuilder = new ChunkToLodBuilder(); + } - protected void createAndSetChunkHashRepo(File databaseFile) + /** + * Creating the repos requires access to the level file, which isn't + * available at constructor time. + */ + protected void createAndSetSupportingRepos(File databaseFile) { + // chunk hash ChunkHashRepo newChunkHashRepo = null; try { @@ -72,6 +105,41 @@ public abstract class AbstractDhLevel implements IDhLevel LOGGER.error("Unable to create [ChunkHashRepo], error: ["+e.getMessage()+"].", e); } this.chunkHashRepo = newChunkHashRepo; + + + // beacon beam + BeaconBeamRepo newBeaconBeamRepo = null; + try + { + newBeaconBeamRepo = new BeaconBeamRepo("jdbc:sqlite", databaseFile); + } + catch (SQLException e) + { + LOGGER.error("Unable to create [BeaconBeamRepo], error: ["+e.getMessage()+"].", e); + } + this.beaconBeamRepo = newBeaconBeamRepo; + } + + /** handles any setup that needs the repos to be created */ + protected void runRepoReliantSetup() + { + GenericObjectRenderer genericRenderer = this.getGenericRenderer(); + if (genericRenderer != null) + { + // only add clouds for certain dimension types + if (!this.getLevelWrapper().hasCeiling() + && !this.getLevelWrapper().getDimensionType().isTheEnd()) + { + this.cloudRenderHandler = new CloudRenderHandler(this, genericRenderer); + } + + + // shouldn't happen, but just in case + if (this.beaconBeamRepo != null) + { + this.beaconRenderHandler = new BeaconRenderHandler(this.beaconBeamRepo, genericRenderer); + } + } } @@ -126,6 +194,13 @@ public abstract class AbstractDhLevel implements IDhLevel } + + //=======// + // repos // + //=======// + + // chunk hash // + @Override public int getChunkHash(DhChunkPos pos) { @@ -148,11 +223,55 @@ public abstract class AbstractDhLevel implements IDhLevel + //=================// + // beacon handling // + //=================// + + @Override + public void setBeaconBeamsForChunk(DhChunkPos chunkPos, List newBeamList) + { + if (this.beaconRenderHandler != null) + { + this.beaconRenderHandler.setBeaconBeamsForChunk(chunkPos, newBeamList); + } + } + + @Override + public void loadBeaconBeamsInPos(long pos) + { + if (this.beaconRenderHandler != null) + { + this.beaconRenderHandler.loadBeaconBeamsInPos(pos); + } + } + @Override + public void unloadBeaconBeamsInPos(long pos) + { + if (this.beaconRenderHandler != null) + { + this.beaconRenderHandler.unloadBeaconBeamsInPos(pos); + } + } + + + //================// // base overrides // //================// @Override - public void close() { this.chunkToLodBuilder.close(); } + public void close() + { + this.chunkToLodBuilder.close(); + + if (this.chunkHashRepo != null) + { + this.chunkHashRepo.close(); + } + if (this.beaconBeamRepo != null) + { + this.beaconBeamRepo.close(); + } + } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java b/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java index 7ed8e1e43..99dd8fcf7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/ClientLevelModule.java @@ -27,13 +27,12 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.file.AbstractDataSourceHandler; import com.seibel.distanthorizons.core.file.fullDatafile.FullDataSourceProviderV2; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.render.LodQuadTree; import com.seibel.distanthorizons.core.render.RenderBufferHandler; +import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer; import com.seibel.distanthorizons.core.render.renderer.LodRenderer; import com.seibel.distanthorizons.core.util.LodUtil; -import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; @@ -41,9 +40,7 @@ import org.apache.logging.log4j.Logger; import javax.annotation.WillNotClose; import java.io.Closeable; -import java.util.ArrayList; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.atomic.AtomicReference; public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.IDataSourceUpdateFunc @@ -56,6 +53,14 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I @WillNotClose public final FullDataSourceProviderV2 fullDataSourceProvider; public final AtomicReference ClientRenderStateRef = new AtomicReference<>(); + /** + * This is handled outside of the {@link ClientRenderState} to prevent destroying + * the {@link GenericObjectRenderer} when changing render distances or enabling/disabling rendering.

+ * + * Destroying the {@link GenericObjectRenderer} would cause any existing bindings to be + * erroneously removed. + */ + public final GenericObjectRenderer genericRenderer = new GenericObjectRenderer(); @@ -108,7 +113,7 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I } clientRenderState.close(); - clientRenderState = new ClientRenderState(this.clientLevel, clientLevelWrapper, this.clientLevel.getFullDataProvider()); + clientRenderState = new ClientRenderState(this.clientLevel, clientLevelWrapper, this.clientLevel.getFullDataProvider(), this.genericRenderer); if (!this.ClientRenderStateRef.compareAndSet(null, clientRenderState)) { //FIXME: How to handle this? @@ -128,7 +133,7 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I } if (isBuffersDirty) { - clientRenderState.renderer.bufferHandler.MarkAllBuffersDirty(); + clientRenderState.lodRenderer.bufferHandler.MarkAllBuffersDirty(); } } @@ -141,7 +146,7 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I public boolean startRenderer(IClientLevelWrapper clientLevelWrapper) { // TODO why are we passing in a level wrapper? Our client level is already defined. - ClientRenderState ClientRenderState = new ClientRenderState(this.clientLevel, clientLevelWrapper, this.clientLevel.getFullDataProvider()); + ClientRenderState ClientRenderState = new ClientRenderState(this.clientLevel, clientLevelWrapper, this.clientLevel.getFullDataProvider(), this.genericRenderer); if (!this.ClientRenderStateRef.compareAndSet(null, ClientRenderState)) { LOGGER.warn("Failed to start renderer due to concurrency"); @@ -167,7 +172,7 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I // either the renderer hasn't been started yet, or is being reloaded return; } - ClientRenderState.renderer.drawLods(ClientRenderState.clientLevelWrapper, renderEventParam, profiler); + ClientRenderState.lodRenderer.drawLods(ClientRenderState.clientLevelWrapper, renderEventParam, profiler); } public void renderDeferred(DhApiRenderParam renderEventParam, IProfilerWrapper profiler) @@ -178,7 +183,7 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I // either the renderer hasn't been started yet, or is being reloaded return; } - ClientRenderState.renderer.drawDeferredLods(ClientRenderState.clientLevelWrapper, renderEventParam, profiler); + ClientRenderState.lodRenderer.drawDeferredLods(ClientRenderState.clientLevelWrapper, renderEventParam, profiler); } public void stopRenderer() @@ -275,15 +280,25 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I // helper classes // //================// - public static class ClientRenderState + public static class ClientRenderState implements Closeable { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); public final IClientLevelWrapper clientLevelWrapper; public final LodQuadTree quadtree; - public final LodRenderer renderer; + public final RenderBufferHandler renderBufferHandler; + public final LodRenderer lodRenderer; - public ClientRenderState(IDhClientLevel dhClientLevel, IClientLevelWrapper clientLevelWrapper, FullDataSourceProviderV2 fullDataSourceProvider) + + + //=============// + // constructor // + //=============// + + public ClientRenderState( + IDhClientLevel dhClientLevel, IClientLevelWrapper clientLevelWrapper, + FullDataSourceProviderV2 fullDataSourceProvider, + GenericObjectRenderer genericRenderer) { this.clientLevelWrapper = clientLevelWrapper; @@ -292,17 +307,22 @@ public class ClientLevelModule implements Closeable, AbstractDataSourceHandler.I 0, 0, fullDataSourceProvider); - RenderBufferHandler renderBufferHandler = new RenderBufferHandler(this.quadtree); - this.renderer = new LodRenderer(renderBufferHandler); + this.renderBufferHandler = new RenderBufferHandler(this.quadtree); + this.lodRenderer = new LodRenderer(this.renderBufferHandler, genericRenderer); } + //================// + // base overrides // + //================// + + @Override public void close() { LOGGER.info("Shutting down " + ClientRenderState.class.getSimpleName()); - this.renderer.close(); + this.lodRenderer.close(); this.quadtree.close(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 9c7eda259..af69a3d73 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -34,6 +34,8 @@ import com.seibel.distanthorizons.core.multiplayer.client.FullDataRefreshQueue; import com.seibel.distanthorizons.core.network.event.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos; +import com.seibel.distanthorizons.core.render.RenderBufferHandler; +import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; @@ -96,6 +98,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel LOGGER.warn("unable to create data folder."); } this.levelWrapper = clientLevelWrapper; + this.levelWrapper.setParentLevel(this); this.saveStructure = saveStructure; this.networkState = networkState; @@ -117,7 +120,8 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel this.clientside = new ClientLevelModule(this); - this.createAndSetChunkHashRepo(this.dataFileHandler.repo.databaseFile); + this.createAndSetSupportingRepos(this.dataFileHandler.repo.databaseFile); + this.runRepoReliantSetup(); if (enableRendering) { @@ -298,10 +302,11 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel this.eventSource.close(); } + this.levelWrapper.setParentLevel(null); this.clientside.close(); super.close(); this.dataFileHandler.close(); - LOGGER.info("Closed " + DhClientLevel.class.getSimpleName() + " for " + this.levelWrapper); + LOGGER.info("Closed [" + DhClientLevel.class.getSimpleName() + "] for [" + this.levelWrapper + "]"); } @Override @@ -314,6 +319,15 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel public boolean hasSkyLight() { return this.levelWrapper.hasSkyLight(); } + @Override + public GenericObjectRenderer getGenericRenderer() { return this.clientside.genericRenderer; } + @Override + public RenderBufferHandler getRenderBufferHandler() + { + ClientLevelModule.ClientRenderState renderState = this.clientside.ClientRenderStateRef.get(); + return (renderState != null) ? renderState.renderBufferHandler : null; + } + @Override public void onWorldGenTaskComplete(long pos) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java index 42698d6d7..274bc8c39 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java @@ -24,11 +24,13 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSour import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.file.fullDatafile.FullDataSourceProviderV2; import com.seibel.distanthorizons.core.logging.f3.F3Screen; +import com.seibel.distanthorizons.core.render.RenderBufferHandler; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; +import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; @@ -66,9 +68,11 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev LOGGER.warn("unable to create data folder."); } this.serverLevelWrapper = serverLevelWrapper; + this.serverLevelWrapper.setParentLevel(this); this.serverside = new ServerLevelModule(this, saveStructure); this.clientside = new ClientLevelModule(this); - this.createAndSetChunkHashRepo(this.serverside.fullDataFileHandler.repo.databaseFile); + this.createAndSetSupportingRepos(this.serverside.fullDataFileHandler.repo.databaseFile); + this.runRepoReliantSetup(); LOGGER.info("Started " + DhClientServerLevel.class.getSimpleName() + " for " + serverLevelWrapper + " with saves at " + saveStructure); } @@ -102,20 +106,7 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev if (shouldDoWorldGen && !isWorldGenRunning) { // start world gen - - // create a new queue this.serverside.worldGenModule.startWorldGen(this.serverside.fullDataFileHandler, new ServerLevelModule.WorldGenState(this)); - - // TODO I think this used to queue the world gen - // is it still needed? - // populate the queue based on the current rendering tree - //ClientLevelModule.ClientRenderState renderState = this.clientside.ClientRenderStateRef.get(); - //Iterator> iterator = renderState.quadtree.leafNodeIterator(); - //while (iterator.hasNext()) - //{ - // QuadNode node = iterator.next(); - // //this.serverside.dataFileHandler.getAsync(node.sectionPos); - //} } else if (!shouldDoWorldGen && isWorldGenRunning) { @@ -160,7 +151,7 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev } @Override - public IClientLevelWrapper getClientLevelWrapper() { return this.serverLevelWrapper.tryGetClientLevelWrapper(); } + public IClientLevelWrapper getClientLevelWrapper() { return MC_CLIENT.getWrappedClientLevel(); } @Override public void clearRenderCache() @@ -232,6 +223,16 @@ public class DhClientServerLevel extends AbstractDhLevel implements IDhClientLev } + @Override + public GenericObjectRenderer getGenericRenderer() { return this.clientside.genericRenderer; } + @Override + public RenderBufferHandler getRenderBufferHandler() + { + ClientLevelModule.ClientRenderState renderState = this.clientside.ClientRenderStateRef.get(); + return (renderState != null) ? renderState.renderBufferHandler : null; + } + + //===============// // data handling // diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index a4ee82e46..7a84219f8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -40,9 +40,11 @@ import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; +import com.seibel.distanthorizons.core.render.RenderBufferHandler; +import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; -import com.seibel.distanthorizons.coreapi.util.math.Vec3d; +import com.seibel.distanthorizons.core.util.math.Vec3d; import org.apache.logging.log4j.Logger; import java.text.MessageFormat; @@ -75,6 +77,10 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel private final ConcurrentMap requestGroupsByFutureId = new ConcurrentHashMap<>(); + //=============// + // constructor // + //=============// + public DhServerLevel(AbstractSaveStructure saveStructure, IServerLevelWrapper serverLevelWrapper, RemotePlayerConnectionHandler remotePlayerConnectionHandler) { if (saveStructure.getFullDataFolder(serverLevelWrapper).mkdirs()) @@ -83,7 +89,8 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel } this.serverLevelWrapper = serverLevelWrapper; this.serverside = new ServerLevelModule(this, saveStructure); - this.createAndSetChunkHashRepo(this.serverside.fullDataFileHandler.repo.databaseFile); + this.createAndSetSupportingRepos(this.serverside.fullDataFileHandler.repo.databaseFile); + this.runRepoReliantSetup(); LOGGER.info("Started DHLevel for {} with saves at {}", serverLevelWrapper, saveStructure); @@ -245,6 +252,10 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel this.worldGenPlayerCenteringQueue.add(serverPlayer); } + //=========// + // methods // + //=========// + public void removePlayer(IServerPlayerWrapper serverPlayer) { this.worldGenPlayerCenteringQueue.remove(serverPlayer); @@ -444,6 +455,18 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel } } + @Override + public GenericObjectRenderer getGenericRenderer() + { + // server-only levels don't support rendering + return null; + } + @Override + public RenderBufferHandler getRenderBufferHandler() + { + // server-only levels don't support rendering + return null; + } //===========// diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java index 8f21f32ab..15b392a6c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhLevel.java @@ -23,8 +23,12 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSour import com.seibel.distanthorizons.core.file.fullDatafile.FullDataSourceProviderV2; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.pos.DhChunkPos; +import com.seibel.distanthorizons.core.render.RenderBufferHandler; +import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer; +import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; +import org.jetbrains.annotations.Nullable; import java.util.List; import java.util.concurrent.CompletableFuture; @@ -44,6 +48,10 @@ public interface IDhLevel extends AutoCloseable void setChunkHash(DhChunkPos pos, int chunkHash); void updateChunkAsync(IChunkWrapper chunk); + void loadBeaconBeamsInPos(long pos); + void setBeaconBeamsForChunk(DhChunkPos chunkPos, List beamList); + void unloadBeaconBeamsInPos(long pos); + FullDataSourceProviderV2 getFullDataProvider(); AbstractSaveStructure getSaveStructure(); @@ -60,5 +68,17 @@ public interface IDhLevel extends AutoCloseable void addDebugMenuStringsToList(List messageList); + /** + * Will return null if the renderer isn't set up yet.
+ * Not supported on the server-side. + */ + @Nullable + GenericObjectRenderer getGenericRenderer(); + /** + * Will return null if the renderer isn't set up yet.
+ * Not supported on the server-side. + */ + @Nullable + RenderBufferHandler getRenderBufferHandler(); } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java b/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java index eacad1045..cc3b817cb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java @@ -22,14 +22,13 @@ package com.seibel.distanthorizons.core.logging.f3; import com.seibel.distanthorizons.core.api.internal.SharedApi; import com.seibel.distanthorizons.core.level.IDhLevel; 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.coreapi.ModInfo; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import org.jetbrains.annotations.Nullable; -import java.lang.ref.WeakReference; import java.text.NumberFormat; import java.util.*; import java.util.concurrent.ThreadPoolExecutor; @@ -42,23 +41,6 @@ public class F3Screen - //============// - // properties // - //============// - - private static WeakReference renderBufferHandlerRef = new WeakReference<>(null); - public static void setRenderBufferHandler(@Nullable RenderBufferHandler renderBufferHandler) - { - if (renderBufferHandler != null && renderBufferHandlerRef.get() != null) - { - LOGGER.warn("multiple RenderBufferHandlers are active at once, the F3 menu may not be accurate."); - } - - renderBufferHandlerRef = new WeakReference<>(renderBufferHandler); - } - - - //=================// // injection point // //=================// @@ -106,23 +88,28 @@ public class F3Screen // chunk updates messageList.add(SharedApi.INSTANCE.getDebugMenuString()); messageList.add(""); - // rendering - RenderBufferHandler renderBufferHandler = renderBufferHandlerRef.get(); - if (renderBufferHandler != null) - { - messageList.add(renderBufferHandler.getVboRenderDebugMenuString()); - String showPassString = renderBufferHandler.getShadowPassRenderDebugMenuString(); - if (showPassString != null) - { - messageList.add(showPassString); - } - messageList.add(""); - } // world / levels world.addDebugMenuStringsToList(messageList); for (IDhLevel level : levelIterator) { level.addDebugMenuStringsToList(messageList); + // LOD rendering + RenderBufferHandler renderBufferHandler = level.getRenderBufferHandler(); + if (renderBufferHandler != null) + { + messageList.add(renderBufferHandler.getVboRenderDebugMenuString()); + String showPassString = renderBufferHandler.getShadowPassRenderDebugMenuString(); + if (showPassString != null) + { + messageList.add(showPassString); + } + } + // Generic rendering + GenericObjectRenderer genericRenderer = level.getGenericRenderer(); + if (genericRenderer != null) + { + messageList.add(genericRenderer.getVboRenderDebugMenuString()); + } } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhChunkPos.java b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhChunkPos.java index fea474574..f81742b1f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhChunkPos.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhChunkPos.java @@ -19,7 +19,8 @@ package com.seibel.distanthorizons.core.pos; -import com.seibel.distanthorizons.coreapi.util.math.Vec3d; +import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.util.math.Vec3d; public class DhChunkPos { @@ -31,6 +32,10 @@ public class DhChunkPos + //==============// + // constructors // + //==============// + public DhChunkPos(int x, int z) { this.x = x; @@ -57,6 +62,10 @@ public class DhChunkPos + //=========// + // methods // + //=========// + public DhBlockPos center() { return new DhBlockPos(8 + this.x << 4, 0, 8 + this.z << 4); } public DhBlockPos corner() { return new DhBlockPos(this.x << 4, 0, this.z << 4); } @@ -70,6 +79,17 @@ public class DhChunkPos public DhBlockPos2D getMinBlockPos() { return new DhBlockPos2D(this.x << 4, this.z << 4); } + public boolean contains(DhBlockPos pos) + { + int minBlockX = this.getMinBlockX(); + int minBlockZ = this.getMinBlockZ(); + int maxBlockX = minBlockX + LodUtil.CHUNK_WIDTH; + int maxBlockZ = minBlockZ + LodUtil.CHUNK_WIDTH; + + return minBlockX <= pos.x && pos.x <= maxBlockX + && minBlockZ <= pos.z && pos.z <= maxBlockZ; + } + public long getLong() { return toLong(this.x, this.z); } public double distance(DhChunkPos other) @@ -77,6 +97,11 @@ public class DhChunkPos return Math.sqrt(Math.pow(x - other.x, 2) + Math.pow(z - other.z, 2)); } + + //================// + // base overrides // + //================// + @Override public boolean equals(Object obj) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java index c37fac961..e55072dbd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.pos; +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.enums.EDhDirection; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; @@ -101,21 +102,32 @@ public class DhSectionPos return data; } - public static long encode(DhBlockPos pos) { return encodeBlockPos(pos.x, pos.z); } - public static long encode(DhBlockPos2D pos) { return encodeBlockPos(pos.x, pos.z); } - public static long encodeBlockPos(int blockX, int blockZ) + /** Returns the section pos at the requested detail level containing the given BlockPos */ + public static long encodeContaining(byte outputSectionDetailLevel, DhBlockPos pos) { - long pos = encode(LodUtil.BLOCK_DETAIL_LEVEL, blockX, blockZ); - pos = convertToDetailLevel(pos, DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL); - return pos; + int sectionPosX = getXOrZSectionPosFromChunkOrBlockPos(pos.x, false); + int sectionPosZ = getXOrZSectionPosFromChunkOrBlockPos(pos.z, false); + long blockPos = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, sectionPosX, sectionPosZ); + return convertToDetailLevel(blockPos, outputSectionDetailLevel); } - - public static long encode(DhChunkPos pos) { return encodeChunkPos(pos.x, pos.z); } - public static long encodeChunkPos(int chunkX, int chunkZ) + /** Returns the section pos at the requested detail level containing the given ChunkPos */ + public static long encodeContaining(byte outputSectionDetailLevel, DhChunkPos pos) { - long pos = encode(LodUtil.CHUNK_DETAIL_LEVEL, chunkX, chunkZ); - pos = convertToDetailLevel(pos, DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL); - return pos; + int sectionPosX = getXOrZSectionPosFromChunkOrBlockPos(pos.x, true); + int sectionPosZ = getXOrZSectionPosFromChunkOrBlockPos(pos.z, true); + long blockPos = DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, sectionPosX, sectionPosZ); + return convertToDetailLevel(blockPos, outputSectionDetailLevel); + } + private static int getXOrZSectionPosFromChunkOrBlockPos(int chunkXOrZPos, boolean isChunkPos) + { + int sectionPos = chunkXOrZPos; + int fullDataSourceWidth = isChunkPos ? FullDataSourceV2.NUMB_OF_CHUNKS_WIDE : (FullDataSourceV2.NUMB_OF_CHUNKS_WIDE * LodUtil.CHUNK_WIDTH); + + // negative positions start at -1 so the logic there is slightly different + sectionPos = (sectionPos < 0) + ? ((sectionPos + 1) / fullDataSourceWidth) - 1 + : (sectionPos / fullDataSourceWidth); + return sectionPos; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/DhFrustumBounds.java b/core/src/main/java/com/seibel/distanthorizons/core/render/DhFrustumBounds.java index cf18751b8..0ef0b5f56 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/DhFrustumBounds.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/DhFrustumBounds.java @@ -1,8 +1,9 @@ package com.seibel.distanthorizons.core.render; import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiCullingFrustum; +import com.seibel.distanthorizons.api.objects.math.DhApiMat4f; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IOverrideInjector; -import com.seibel.distanthorizons.coreapi.util.math.Mat4f; +import com.seibel.distanthorizons.core.util.math.Mat4f; import org.joml.FrustumIntersection; import org.joml.Matrix4f; import org.joml.Matrix4fc; @@ -35,12 +36,12 @@ public class DhFrustumBounds implements IDhApiCullingFrustum //=========// @Override - public void update(int worldMinBlockY, int worldMaxBlockY, Mat4f dhWorldViewProjection) + public void update(int worldMinBlockY, int worldMaxBlockY, DhApiMat4f dhWorldViewProjection) { this.worldMinY = worldMinBlockY; this.worldMaxY = worldMaxBlockY; - Matrix4f worldViewProjection = new Matrix4f(dhWorldViewProjection.createJomlMatrix()); + Matrix4f worldViewProjection = new Matrix4f(Mat4f.createJomlMatrix(dhWorldViewProjection)); this.frustum.set(worldViewProjection); Matrix4fc matWorldViewProjectionInv = new Matrix4f(worldViewProjection).invert(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java index 3c819d544..0b24aa5c5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java @@ -31,6 +31,7 @@ import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; +import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.util.objects.quadTree.QuadNode; @@ -329,6 +330,10 @@ public class LodQuadTree extends QuadTree implements IDebugRen } // all child positions are loaded, disable this section and enable its children. + if (renderSection.renderingEnabled) + { + this.level.unloadBeaconBeamsInPos(renderSection.pos); + } renderSection.renderingEnabled = false; // walk back down the tree and enable the child sections //TODO there are probably more efficient ways of doing this, but this will work for now @@ -383,6 +388,7 @@ public class LodQuadTree extends QuadTree implements IDebugRen if (!renderSection.renderingEnabled) { renderSection.renderingEnabled = true; + this.level.loadBeaconBeamsInPos(renderSection.pos); // delete/disable children, all of them will be a lower detail level than requested quadNode.deleteAllChildren((childRenderSection) -> diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java index fbfd9a2ed..bacd2eb7b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java @@ -292,7 +292,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable } catch (Exception e) { - LOGGER.warn("Unable to get render source " + this.pos + ", error: " + e.getMessage(), e); + LOGGER.warn("Unable to get render source " + DhSectionPos.toString(this.pos) + ", error: " + e.getMessage(), e); this.renderSourceLoadingRefFuture = null; return null; } @@ -416,6 +416,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable } + this.level.unloadBeaconBeamsInPos(this.pos); if (this.renderBuffer != null) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/NeverCullFrustum.java b/core/src/main/java/com/seibel/distanthorizons/core/render/NeverCullFrustum.java index 7e10b0b44..611aa0ad6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/NeverCullFrustum.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/NeverCullFrustum.java @@ -2,8 +2,9 @@ package com.seibel.distanthorizons.core.render; import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiCullingFrustum; import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiShadowCullingFrustum; +import com.seibel.distanthorizons.api.objects.math.DhApiMat4f; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IOverrideInjector; -import com.seibel.distanthorizons.coreapi.util.math.Mat4f; +import com.seibel.distanthorizons.core.util.math.Mat4f; /** * Dummy {@link IDhApiCullingFrustum} that allows everything through.
@@ -24,7 +25,7 @@ public class NeverCullFrustum implements IDhApiCullingFrustum, IDhApiShadowCulli //=========// @Override - public void update(int worldMinBlockY, int worldMaxBlockY, Mat4f dhWorldViewProjection) { /* update isn't needed */ } + public void update(int worldMinBlockY, int worldMaxBlockY, DhApiMat4f dhWorldViewProjection) { /* update isn't needed */ } @Override public boolean intersects(int lodBlockPosMinX, int lodBlockPosMinZ, int lodBlockWidth, int lodDetailLevel) { return true; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java index f36280ae8..d02a238fb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/RenderBufferHandler.java @@ -41,9 +41,9 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRen import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IOverrideInjector; -import com.seibel.distanthorizons.coreapi.util.math.Mat4f; -import com.seibel.distanthorizons.coreapi.util.math.Vec3d; -import com.seibel.distanthorizons.coreapi.util.math.Vec3f; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.util.math.Vec3d; +import com.seibel.distanthorizons.core.util.math.Vec3f; import org.apache.logging.log4j.Logger; import org.joml.Matrix4f; import org.joml.Matrix4fc; @@ -100,9 +100,6 @@ public class RenderBufferHandler implements AutoCloseable { DhApi.overrides.bind(IDhApiShadowCullingFrustum.class, new NeverCullFrustum()); } - - - F3Screen.setRenderBufferHandler(this); } @@ -247,7 +244,7 @@ public class RenderBufferHandler implements AutoCloseable if (enableFrustumCulling) { int worldMinY = clientLevelWrapper.getMinHeight(); - int worldHeight = clientLevelWrapper.getHeight(); + int worldHeight = clientLevelWrapper.getMaxHeight(); Vec3d cameraPos = MC_RENDER.getCameraExactPosition(); @@ -417,8 +414,6 @@ public class RenderBufferHandler implements AutoCloseable renderSection.close(); } } - - F3Screen.setRenderBufferHandler(null); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/fog/LodFogConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/render/fog/LodFogConfig.java index be4adf73e..8e0873b93 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/fog/LodFogConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/fog/LodFogConfig.java @@ -173,7 +173,7 @@ public class LodFogConfig str.append("" + "float getNearFogThickness(float dist) \n" + "{ \n" + - " return linearFog(dist, nearFogStart, nearFogLength, 0.0, 1.0); \n" + + " return linearFog(dist, uNearFogStart, uNearFogLength, 0.0, 1.0); \n" + "} \n"); @@ -182,7 +182,7 @@ public class LodFogConfig str.append("\n" + "float getFarFogThickness(float dist) { return 0.0; } \n" + "float getHeightFogThickness(float dist) { return 0.0; } \n" + - "float calculateFarFogDepth(float horizontal, float dist, float nearFogStart) { return 0.0; } \n" + + "float calculateFarFogDepth(float horizontal, float dist, float uNearFogStart) { return 0.0; } \n" + "float calculateHeightFogDepth(float vertical, float realY) { return 0.0; } \n" + "float mixFogThickness(float near, float far, float height) \n" + "{ \n" + @@ -215,13 +215,13 @@ public class LodFogConfig "} \n"); - // Generate method: calculateFarFogDepth(float horizontal, float dist, float nearFogStart); + // Generate method: calculateFarFogDepth(float horizontal, float dist, float uNearFogStart); str.append("" + - "float calculateFarFogDepth(float horizontal, float dist, float nearFogStart) \n" + + "float calculateFarFogDepth(float horizontal, float dist, float uNearFogStart) \n" + "{ \n" + " return " + (heightFogMixMode == EDhApiHeightFogMixMode.BASIC ? - "(dist - nearFogStart)/(1.0 - nearFogStart);" : - "(horizontal - nearFogStart)/(1.0 - nearFogStart);") + + "(dist - uNearFogStart)/(1.0 - uNearFogStart);" : + "(horizontal - uNearFogStart)/(1.0 - uNearFogStart);") + "} \n"); // Generate method: float mixFogThickness(float near, float far, float height); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java index ffe831b00..4bbfbd6e8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java @@ -27,7 +27,6 @@ import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.util.objects.GLMessage; import com.seibel.distanthorizons.core.util.objects.GLMessageOutputStream; -import com.seibel.distanthorizons.core.util.objects.Pair; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.coreapi.ModInfo; import org.apache.logging.log4j.LogManager; @@ -40,8 +39,6 @@ import org.lwjgl.opengl.GLUtil; import java.io.PrintStream; import java.lang.invoke.MethodHandles; -import java.util.ArrayList; -import java.util.Arrays; import java.util.concurrent.ConcurrentLinkedQueue; /** @@ -76,7 +73,9 @@ public class GLProxy public boolean namedObjectSupported = false; // ~OpenGL 4.5 (UNUSED CURRENTLY) public boolean bufferStorageSupported = false; // ~OpenGL 4.4 - public boolean VertexAttributeBufferBindingSupported = false; // ~OpenGL 4.3 + public boolean vertexAttributeBufferBindingSupported = false; // ~OpenGL 4.3 + public boolean instancedArraysSupported = false; + public boolean vertexAttribDivisorSupported = false; // OpenGL 3.3 or newer private final EDhApiGpuUploadMethod preferredUploadMethod; @@ -91,17 +90,15 @@ public class GLProxy private GLProxy() throws IllegalStateException { // this must be created on minecraft's render context to work correctly - - GL_LOGGER.info("Creating " + GLProxy.class.getSimpleName() + "... If this is the last message you see there must have been an OpenGL error."); - GL_LOGGER.info("Lod Render OpenGL version [" + GL32.glGetString(GL32.GL_VERSION) + "]."); - - // getting Minecraft's context has to be done on the render thread, - // where the GL context is if (GLFW.glfwGetCurrentContext() == 0L) { throw new IllegalStateException(GLProxy.class.getSimpleName() + " was created outside the render thread!"); } + GL_LOGGER.info("Creating " + GLProxy.class.getSimpleName() + "... If this is the last message you see there must have been an OpenGL error."); + GL_LOGGER.info("Lod Render OpenGL version [" + GL32.glGetString(GL32.GL_VERSION) + "]."); + + //============================// @@ -135,21 +132,26 @@ public class GLProxy // get GPU capabilities // //======================// - // Check if we can use the make-over version of Vertex Attribute, which is available in GL4.3 or after - this.VertexAttributeBufferBindingSupported = this.glCapabilities.glBindVertexBuffer != 0L; // Nullptr - // UNUSED currently // Check if we can use the named version of all calls, which is available in GL4.5 or after this.namedObjectSupported = this.glCapabilities.glNamedBufferData != 0L; //Nullptr - // get specific capabilities // Check if we can use the Buffer Storage, which is available in GL4.4 or after this.bufferStorageSupported = this.glCapabilities.glBufferStorage != 0L; // Nullptr if (!this.bufferStorageSupported) { - GL_LOGGER.warn("This GPU doesn't support Buffer Storage (OpenGL 4.4), falling back to using other methods."); + GL_LOGGER.info("This GPU doesn't support Buffer Storage (OpenGL 4.4), falling back to using other methods."); } + // Check if we can use the make-over version of Vertex Attribute, which is available in GL4.3 or after + this.vertexAttributeBufferBindingSupported = this.glCapabilities.glBindVertexBuffer != 0L; // Nullptr + + // used by instanced rendering + this.vertexAttribDivisorSupported = this.glCapabilities.OpenGL33; + // denotes if ARBInstancedArrays.glVertexAttribDivisorARB() is available or not + // can be used as a backup if MC didn't create a GL 3.3+ context + this.instancedArraysSupported = this.glCapabilities.GL_ARB_instanced_arrays; + // get the best automatic upload method String vendor = GL32.glGetString(GL32.GL_VENDOR).toUpperCase(); // example return: "NVIDIA CORPORATION" if (vendor.contains("NVIDIA") || vendor.contains("GEFORCE")) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/buffer/GLBuffer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/buffer/GLBuffer.java index 7d8608a83..bdfee1f4b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/buffer/GLBuffer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/buffer/GLBuffer.java @@ -20,6 +20,7 @@ package com.seibel.distanthorizons.core.render.glObject.buffer; import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod; +import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.render.glObject.GLProxy; import com.seibel.distanthorizons.core.util.LodUtil; @@ -140,7 +141,10 @@ public class GLBuffer implements AutoCloseable GL32.glDeleteBuffers(id); bufferCount.decrementAndGet(); - //LOGGER.info("destroyed buffer ["+id+"], remaining: ["+BUFFER_ID_TO_PHANTOM.size()+"]"); + if (Config.Client.Advanced.Debugging.logBufferGarbageCollection.get()) + { + LOGGER.info("destroyed buffer [" + id + "], remaining: [" + BUFFER_ID_TO_PHANTOM.size() + "]"); + } } }); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/shader/ShaderProgram.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/shader/ShaderProgram.java index 8ade515eb..fc821a461 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/shader/ShaderProgram.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/shader/ShaderProgram.java @@ -26,12 +26,13 @@ import java.util.Arrays; import java.util.List; import java.util.function.Supplier; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3i; import org.lwjgl.opengl.GL32; import org.lwjgl.system.MemoryStack; -import com.seibel.distanthorizons.coreapi.util.math.Mat4f; -import com.seibel.distanthorizons.coreapi.util.math.Vec3d; -import com.seibel.distanthorizons.coreapi.util.math.Vec3f; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.util.math.Vec3d; +import com.seibel.distanthorizons.core.util.math.Vec3f; /** @@ -154,7 +155,7 @@ public class ShaderProgram * @return Location of the Uniform * @throws RuntimeException if uniform not found */ - public int getUniformLocation(CharSequence name) + public int getUniformLocation(CharSequence name) throws RuntimeException { int i = GL32.glGetUniformLocation(id, name); if (i == -1) @@ -195,11 +196,10 @@ public class ShaderProgram { GL32.glUniform3f(location, value.x, value.y, value.z); } - /** Requires ShaderProgram binded. */ - public void setUniform(int location, Vec3d value) + public void setUniform(int location, DhApiVec3i value) { - GL32.glUniform3f(location, (float) value.x, (float) value.y, (float) value.z); + GL32.glUniform3i(location, value.x, value.y, value.z); } /** Requires ShaderProgram binded. */ diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/vertexAttribute/AbstractVertexAttribute.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/vertexAttribute/AbstractVertexAttribute.java index 3c7267c71..3f27d12b6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/vertexAttribute/AbstractVertexAttribute.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/vertexAttribute/AbstractVertexAttribute.java @@ -48,7 +48,7 @@ public abstract class AbstractVertexAttribute public static AbstractVertexAttribute create() { - if (GLProxy.getInstance().VertexAttributeBufferBindingSupported) + if (GLProxy.getInstance().vertexAttributeBufferBindingSupported) { return new VertexAttributePostGL43(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java index 42d893b9d..02243d36f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DebugRenderer.java @@ -36,9 +36,9 @@ import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram; import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.AbstractVertexAttribute; import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.VertexPointer; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; -import com.seibel.distanthorizons.coreapi.util.math.Mat4f; -import com.seibel.distanthorizons.coreapi.util.math.Vec3d; -import com.seibel.distanthorizons.coreapi.util.math.Vec3f; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.util.math.Vec3d; +import com.seibel.distanthorizons.core.util.math.Vec3f; import org.apache.logging.log4j.LogManager; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -64,13 +64,13 @@ public class DebugRenderer // rendering setup private ShaderProgram basicShader; - private GLVertexBuffer boxBuffer; - private GLElementBuffer boxOutlineBuffer; + private GLVertexBuffer vertexBuffer; + private GLElementBuffer outlineIndexBuffer; private AbstractVertexAttribute va; private boolean init = false; // used when rendering - private Mat4f transformThiFrame; + private Mat4f transformationMatrixThisFrame; private Vec3f camPosFloatThisFrame; @@ -79,8 +79,8 @@ public class DebugRenderer - // A box from 0,0,0 to 1,1,1 - private static final float[] box_vertices = { + /** A box from 0,0,0 to 1,1,1 */ + private static final float[] BOX_VERTICES = { // Pos x y z 0, 0, 0, 1, 0, 0, @@ -92,7 +92,7 @@ public class DebugRenderer 0, 1, 1, }; - private static final int[] box_outline_indices = { + private static final int[] BOX_OUTLINE_INDICES = { 0, 1, 1, 2, 2, 3, @@ -115,7 +115,47 @@ public class DebugRenderer // constructor // //=============// - public DebugRenderer() { } + private DebugRenderer() { } + + public void init() + { + if (this.init) + { + return; + } + this.init = true; + + this.va = AbstractVertexAttribute.create(); + this.va.bind(); + // Pos + this.va.setVertexAttribute(0, 0, VertexPointer.addVec3Pointer(false)); + this.va.completeAndCheck(Float.BYTES * 3); + this.basicShader = new ShaderProgram("shaders/debug/vert.vert", "shaders/debug/frag.frag", + "fragColor", new String[]{"vPosition"}); + this.createBuffer(); + } + + private void createBuffer() + { + // box vertices + ByteBuffer boxVerticesBuffer = ByteBuffer.allocateDirect(BOX_VERTICES.length * Float.BYTES); + boxVerticesBuffer.order(ByteOrder.nativeOrder()); + boxVerticesBuffer.asFloatBuffer().put(BOX_VERTICES); + boxVerticesBuffer.rewind(); + this.vertexBuffer = new GLVertexBuffer(false); + this.vertexBuffer.bind(); + this.vertexBuffer.uploadBuffer(boxVerticesBuffer, 8, EDhApiGpuUploadMethod.DATA, BOX_VERTICES.length * Float.BYTES); + + + // outline vertex indexes + ByteBuffer boxOutlineBuffer = ByteBuffer.allocateDirect(BOX_OUTLINE_INDICES.length * Integer.BYTES); + boxOutlineBuffer.order(ByteOrder.nativeOrder()); + boxOutlineBuffer.asIntBuffer().put(BOX_OUTLINE_INDICES); + boxOutlineBuffer.rewind(); + this.outlineIndexBuffer = new GLElementBuffer(false); + this.outlineIndexBuffer.uploadBuffer(boxOutlineBuffer, EDhApiGpuUploadMethod.DATA, BOX_OUTLINE_INDICES.length * Integer.BYTES, GL32.GL_STATIC_DRAW); + + } @@ -141,53 +181,13 @@ public class DebugRenderer - - //===========// // rendering // //===========// - public void init() - { - if (this.init) - { - return; - } - - this.init = true; - this.va = AbstractVertexAttribute.create(); - this.va.bind(); - // Pos - this.va.setVertexAttribute(0, 0, VertexPointer.addVec3Pointer(false)); - this.va.completeAndCheck(Float.BYTES * 3); - this.basicShader = new ShaderProgram("shaders/debug/vert.vert", "shaders/debug/frag.frag", - "fragColor", new String[]{"vPosition"}); - this.createBuffer(); - } - - private void createBuffer() - { - ByteBuffer buffer = ByteBuffer.allocateDirect(box_vertices.length * Float.BYTES); - buffer.order(ByteOrder.nativeOrder()); - buffer.asFloatBuffer().put(box_vertices); - buffer.rewind(); - - this.boxBuffer = new GLVertexBuffer(false); - this.boxBuffer.bind(); - this.boxBuffer.uploadBuffer(buffer, 8, EDhApiGpuUploadMethod.DATA, box_vertices.length * Float.BYTES); - - buffer = ByteBuffer.allocateDirect(box_outline_indices.length * Integer.BYTES); - buffer.order(ByteOrder.nativeOrder()); - buffer.asIntBuffer().put(box_outline_indices); - buffer.rewind(); - - this.boxOutlineBuffer = new GLElementBuffer(false); - this.boxOutlineBuffer.uploadBuffer(buffer, EDhApiGpuUploadMethod.DATA, box_outline_indices.length * Integer.BYTES, GL32.GL_STATIC_DRAW); - } - public void render(Mat4f transform) { - this.transformThiFrame = transform; + this.transformationMatrixThisFrame = transform; Vec3d camPos = MC_RENDER.getCameraExactPosition(); this.camPosFloatThisFrame = new Vec3f((float) camPos.x, (float) camPos.y, (float) camPos.z); @@ -199,22 +199,23 @@ public class DebugRenderer this.basicShader.bind(); this.va.bind(); - this.va.bindBufferToAllBindingPoints(this.boxBuffer.getId()); + this.va.bindBufferToAllBindingPoints(this.vertexBuffer.getId()); - this.boxOutlineBuffer.bind(); + this.outlineIndexBuffer.bind(); this.rendererLists.render(this); BoxParticle head = null; while ((head = this.particles.poll()) != null && head.isDead(System.nanoTime())) - { - } + { /* remove dead particles */ } if (head != null) { + // re-add the popped off head this.particles.add(head); } + GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); for (BoxParticle particle : this.particles) { this.renderBox(particle.getBox()); @@ -226,13 +227,13 @@ public class DebugRenderer public void renderBox(Box box) { - Mat4f boxTransform = Mat4f.createTranslateMatrix(box.a.x - this.camPosFloatThisFrame.x, box.a.y - this.camPosFloatThisFrame.y, box.a.z - this.camPosFloatThisFrame.z); - boxTransform.multiply(Mat4f.createScaleMatrix(box.b.x - box.a.x, box.b.y - box.a.y, box.b.z - box.a.z)); - Mat4f t = this.transformThiFrame.copy(); + Mat4f boxTransform = Mat4f.createTranslateMatrix(box.minPos.x - this.camPosFloatThisFrame.x, box.minPos.y - this.camPosFloatThisFrame.y, box.minPos.z - this.camPosFloatThisFrame.z); + boxTransform.multiply(Mat4f.createScaleMatrix(box.maxPos.x - box.minPos.x, box.maxPos.y - box.minPos.y, box.maxPos.z - box.minPos.z)); + Mat4f t = this.transformationMatrixThisFrame.copy(); t.multiply(boxTransform); - this.basicShader.setUniform(this.basicShader.getUniformLocation("transform"), t); + this.basicShader.setUniform(this.basicShader.getUniformLocation("uTransform"), t); this.basicShader.setUniform(this.basicShader.getUniformLocation("uColor"), box.color); - GL32.glDrawElements(GL32.GL_LINES, box_outline_indices.length, GL32.GL_UNSIGNED_INT, 0); + GL32.glDrawElements(GL32.GL_LINES, BOX_OUTLINE_INDICES.length, GL32.GL_UNSIGNED_INT, 0); } @@ -243,25 +244,25 @@ public class DebugRenderer public static final class Box { - public Vec3f a; - public Vec3f b; + public Vec3f minPos; + public Vec3f maxPos; public Color color; - public Box(Vec3f a, Vec3f b, Color color) + public Box(Vec3f minPos, Vec3f maxPos, Color color) { - this.a = a; - this.b = b; + this.minPos = minPos; + this.maxPos = maxPos; this.color = color; } - public Box(Vec3f a, Vec3f b, Color color, Vec3f margin) + public Box(Vec3f minPos, Vec3f maxPos, Color color, Vec3f margin) { - this.a = a; - this.a.add(margin); - this.b = b; - this.b.subtract(margin); + this.minPos = minPos; + this.minPos.add(margin); + this.maxPos = maxPos; + this.maxPos.subtract(margin); this.color = color; } @@ -272,8 +273,8 @@ public class DebugRenderer float edge = pos.getBlockWidth() * marginPercent; Vec3f a = new Vec3f(blockMin.x + edge, minY, blockMin.z + edge); Vec3f b = new Vec3f(blockMax.x - edge, maxY, blockMax.z - edge); - this.a = a; - this.b = b; + this.minPos = a; + this.maxPos = b; this.color = color; } @@ -285,8 +286,8 @@ public class DebugRenderer float edge = pos.getBlockWidth() * marginPercent; Vec3f a = new Vec3f(blockMin.x + edge, hashY, blockMin.z + edge); Vec3f b = new Vec3f(blockMax.x - edge, hashY, blockMax.z - edge); - this.a = a; - this.b = b; + this.minPos = a; + this.maxPos = b; this.color = color; } @@ -334,7 +335,7 @@ public class DebugRenderer float percent = (now - this.startTime) / (float) this.duration; percent = (float) Math.pow(percent, 4); float yDiff = this.yChange * percent; - return new Box(new Vec3f(this.box.a.x, this.box.a.y + yDiff, this.box.a.z), new Vec3f(this.box.b.x, this.box.b.y + yDiff, this.box.b.z), this.box.color); + return new Box(new Vec3f(this.box.minPos.x, this.box.minPos.y + yDiff, this.box.minPos.z), new Vec3f(this.box.maxPos.x, this.box.maxPos.y + yDiff, this.box.maxPos.z), this.box.color); } public boolean isDead(long time) { return (time - this.startTime) > this.duration; } @@ -350,7 +351,7 @@ public class DebugRenderer public BoxWithLife(Box box, long ns, float yChange, Color deathColor) { this.box = box; - this.particaleOnClose = new BoxParticle(new Box(box.a, box.b, deathColor), -1, ns, yChange); + this.particaleOnClose = new BoxParticle(new Box(box.minPos, box.maxPos, deathColor), -1, ns, yChange); register(this, null); } @@ -360,7 +361,7 @@ public class DebugRenderer public BoxWithLife(Box box, double s, float yChange, Color deathColor) { this.box = box; - this.particaleOnClose = new BoxParticle(new Box(box.a, box.b, deathColor), s, yChange); + this.particaleOnClose = new BoxParticle(new Box(box.minPos, box.maxPos, deathColor), s, yChange); } public BoxWithLife(Box box, double s, float yChange) { this(box, s, yChange, box.color); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderProgram.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DhTerrainShaderProgram.java similarity index 52% rename from core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderProgram.java rename to core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DhTerrainShaderProgram.java index 5f95bb689..058b6b63e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderProgram.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/DhTerrainShaderProgram.java @@ -21,6 +21,7 @@ package com.seibel.distanthorizons.core.render.renderer; import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiShaderProgram; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.render.glObject.GLProxy; import com.seibel.distanthorizons.core.render.glObject.shader.Shader; @@ -31,39 +32,38 @@ import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.VertexAtt import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.VertexPointer; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.RenderUtil; -import com.seibel.distanthorizons.coreapi.util.math.Mat4f; -import com.seibel.distanthorizons.coreapi.util.math.Vec3f; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.util.math.Vec3f; -public class LodRenderProgram extends ShaderProgram implements IDhApiShaderProgram +/** + * Handles rendering the normal LOD terrain. + */ +public class DhTerrainShaderProgram extends ShaderProgram implements IDhApiShaderProgram { - public static final String VERTEX_SHADER_PATH = "shaders/standard.vert"; - public static final String VERTEX_CURVE_SHADER_PATH = "shaders/curve.vert"; - public static final String FRAGMENT_SHADER_PATH = "shaders/flat_shaded.frag"; - public final AbstractVertexAttribute vao; // Uniforms - public final int combinedMatUniform; - public final int modelOffsetUniform; - public final int worldYOffsetUniform; + public final int uCombinedMatrix; + public final int uModelOffset; + public final int uWorldYOffset; - public final int mircoOffsetUniform; + public final int uMircoOffset; - public final int earthRadiusUniform; + public final int uEarthRadius; - public final int lightMapUniform; + public final int uLightMap; // Fog/Clip Uniforms - public final int clipDistanceUniform; + public final int uClipDistance; // Noise Uniforms - public final int noiseEnabledUniform; - public final int noiseStepsUniform; - public final int noiseIntensityUniform; - public final int noiseDropoffUniform; + public final int uNoiseEnabled; + public final int uNoiseSteps; + public final int uNoiseIntensity; + public final int uNoiseDropoff; // Debug Uniform - public final int whiteWorldUniform; + public final int uWhiteWorld; @@ -72,50 +72,57 @@ public class LodRenderProgram extends ShaderProgram implements IDhApiShaderProgr //=============// // This will bind AbstractVertexAttribute - public LodRenderProgram() + public DhTerrainShaderProgram() { - super(() -> Shader.loadFile(Config.Client.Advanced.Graphics.AdvancedGraphics.earthCurveRatio.get() != 0 ? VERTEX_CURVE_SHADER_PATH : VERTEX_SHADER_PATH, + super( + () -> Shader.loadFile(Config.Client.Advanced.Graphics.AdvancedGraphics.earthCurveRatio.get() != 0 + ? "shaders/curve.vert" + : "shaders/standard.vert", false, new StringBuilder()).toString(), - () -> Shader.loadFile(FRAGMENT_SHADER_PATH, false, new StringBuilder()).toString(), + () -> Shader.loadFile("shaders/flat_shaded.frag", false, new StringBuilder()).toString(), "fragColor", new String[]{"vPosition", "color"}); - combinedMatUniform = getUniformLocation("combinedMatrix"); - modelOffsetUniform = getUniformLocation("modelOffset"); - worldYOffsetUniform = tryGetUniformLocation("worldYOffset"); - mircoOffsetUniform = getUniformLocation("mircoOffset"); - earthRadiusUniform = tryGetUniformLocation("earthRadius"); + this.uCombinedMatrix = this.getUniformLocation("uCombinedMatrix"); + this.uModelOffset = this.getUniformLocation("uModelOffset"); + this.uWorldYOffset = this.tryGetUniformLocation("uWorldYOffset"); + this.uMircoOffset = this.getUniformLocation("uMircoOffset"); + this.uEarthRadius = this.tryGetUniformLocation("uEarthRadius"); - lightMapUniform = getUniformLocation("lightMap"); + this.uLightMap = this.getUniformLocation("uLightMap"); // Fog/Clip Uniforms - clipDistanceUniform = getUniformLocation("clipDistance"); + this.uClipDistance = this.getUniformLocation("uClipDistance"); // Noise Uniforms - noiseEnabledUniform = getUniformLocation("noiseEnabled"); - noiseStepsUniform = getUniformLocation("noiseSteps"); - noiseIntensityUniform = getUniformLocation("noiseIntensity"); - noiseDropoffUniform = getUniformLocation("noiseDropoff"); + this.uNoiseEnabled = this.getUniformLocation("uNoiseEnabled"); + this.uNoiseSteps = this.getUniformLocation("uNoiseSteps"); + this.uNoiseIntensity = this.getUniformLocation("uNoiseIntensity"); + this.uNoiseDropoff = this.getUniformLocation("uNoiseDropoff"); // Debug Uniform - whiteWorldUniform = getUniformLocation("whiteWorld"); + this.uWhiteWorld = this.getUniformLocation("uWhiteWorld"); // TODO: Add better use of the LODFormat thing int vertexByteCount = LodUtil.LOD_VERTEX_FORMAT.getByteSize(); - if (GLProxy.getInstance().VertexAttributeBufferBindingSupported) - vao = new VertexAttributePostGL43(); // also binds AbstractVertexAttribute + if (GLProxy.getInstance().vertexAttributeBufferBindingSupported) + { + this.vao = new VertexAttributePostGL43(); // also binds AbstractVertexAttribute + } else - vao = new VertexAttributePreGL43(); // also binds AbstractVertexAttribute - vao.bind(); + { + this.vao = new VertexAttributePreGL43(); // also binds AbstractVertexAttribute + } + this.vao.bind(); // TODO comment what each attribute represents - vao.setVertexAttribute(0, 0, VertexPointer.addUnsignedShortsPointer(4, false, true)); // 2+2+2+2 // TODO probably color, blockpos - vao.setVertexAttribute(0, 1, VertexPointer.addUnsignedBytesPointer(4, true, false)); // +4 // TODO ? - vao.setVertexAttribute(0, 2, VertexPointer.addUnsignedBytesPointer(4, true, true)); // +4 // TODO probably normal index and Iris block ID + this.vao.setVertexAttribute(0, 0, VertexPointer.addUnsignedShortsPointer(4, false, true)); // 2+2+2+2 // TODO probably color, blockpos + this.vao.setVertexAttribute(0, 1, VertexPointer.addUnsignedBytesPointer(4, true, false)); // +4 // TODO ? + this.vao.setVertexAttribute(0, 2, VertexPointer.addUnsignedBytesPointer(4, true, true)); // +4 // TODO probably normal index and Iris block ID try { - vao.completeAndCheck(vertexByteCount); + this.vao.completeAndCheck(vertexByteCount); } catch (RuntimeException e) { @@ -123,15 +130,15 @@ public class LodRenderProgram extends ShaderProgram implements IDhApiShaderProgr throw e; } - if (earthRadiusUniform != -1) setUniform(earthRadiusUniform, + if (this.uEarthRadius != -1) this.setUniform(this.uEarthRadius, /*6371KM*/ 6371000.0f / Config.Client.Advanced.Graphics.AdvancedGraphics.earthCurveRatio.get()); // Noise Uniforms - setUniform(noiseEnabledUniform, Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseEnabled.get()); - setUniform(noiseStepsUniform, Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseSteps.get()); - setUniform(noiseIntensityUniform, Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseIntensity.get().floatValue()); - setUniform(noiseDropoffUniform, Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseDropoff.get()); + this.setUniform(this.uNoiseEnabled, Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseEnabled.get()); + this.setUniform(this.uNoiseSteps, Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseSteps.get()); + this.setUniform(this.uNoiseIntensity, Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseIntensity.get().floatValue()); + this.setUniform(this.uNoiseDropoff, Config.Client.Advanced.Graphics.NoiseTextureSettings.noiseDropoff.get()); } @@ -144,27 +151,25 @@ public class LodRenderProgram extends ShaderProgram implements IDhApiShaderProgr public void bind() { super.bind(); - vao.bind(); + this.vao.bind(); } @Override public void unbind() { super.unbind(); - vao.unbind(); + this.vao.unbind(); } @Override public void free() { - vao.free(); + this.vao.free(); super.free(); } @Override public void bindVertexBuffer(int vbo) { this.vao.bindBufferToAllBindingPoints(vbo); } - public void unbindVertexBuffer() { this.vao.unbindBuffersFromAllBindingPoint(); } - @Override public void fillUniformData(DhApiRenderParam renderParameters) { @@ -174,16 +179,16 @@ public class LodRenderProgram extends ShaderProgram implements IDhApiShaderProgr super.bind(); // uniforms - setUniform(combinedMatUniform, combinedMatrix); - setUniform(mircoOffsetUniform, 0.01f); // 0.01 block offset + this.setUniform(this.uCombinedMatrix, combinedMatrix); + this.setUniform(this.uMircoOffset, 0.01f); // 0.01 block offset // setUniform(skyLightUniform, skyLight); - setUniform(lightMapUniform, 0); // TODO this should probably be passed in + this.setUniform(this.uLightMap, 0); // TODO this should probably be passed in - if (worldYOffsetUniform != -1) setUniform(worldYOffsetUniform, (float) renderParameters.worldYOffset); + if (this.uWorldYOffset != -1) this.setUniform(this.uWorldYOffset, (float) renderParameters.worldYOffset); // Debug - setUniform(whiteWorldUniform, Config.Client.Advanced.Debugging.enableWhiteWorld.get()); + this.setUniform(this.uWhiteWorld, Config.Client.Advanced.Debugging.enableWhiteWorld.get()); // Clip Uniform float dhNearClipDistance = RenderUtil.getNearClipPlaneDistanceInBlocks(renderParameters.partialTicks); @@ -197,11 +202,11 @@ public class LodRenderProgram extends ShaderProgram implements IDhApiShaderProgr { dhNearClipDistance = 1.0f; } - this.setUniform(this.clipDistanceUniform, dhNearClipDistance); + this.setUniform(this.uClipDistance, dhNearClipDistance); } @Override - public void setModelOffsetPos(Vec3f modelOffsetPos) { this.setUniform(this.modelOffsetUniform, modelOffsetPos); } + public void setModelOffsetPos(DhApiVec3f modelOffsetPos) { this.setUniform(this.uModelOffset, new Vec3f(modelOffsetPos)); } @Override public int getId() { return this.id; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/FogRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/FogRenderer.java new file mode 100644 index 000000000..1f2f17491 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/FogRenderer.java @@ -0,0 +1,135 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.render.renderer; + +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.render.glObject.GLState; +import com.seibel.distanthorizons.core.render.renderer.shaders.FogApplyShader; +import com.seibel.distanthorizons.core.render.renderer.shaders.FogShader; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import org.lwjgl.opengl.GL32; + +import java.nio.ByteBuffer; + +/** + * Handles adding SSAO via {@link FogShader} and {@link FogApplyShader}.

+ * + * {@link FogShader} - draws the Fog to a texture.
+ * {@link FogApplyShader} - draws the Fog texture to DH's FrameBuffer.
+ */ +public class FogRenderer +{ + public static FogRenderer INSTANCE = new FogRenderer(); + + private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + + + private boolean init = false; + + private int width = -1; + private int height = -1; + private int fogFramebuffer = -1; + + private int fogTexture = -1; + + + + //=============// + // constructor // + //=============// + + private FogRenderer() { } + + public void init() + { + if (this.init) return; + this.init = true; + + FogShader.INSTANCE.init(); + FogApplyShader.INSTANCE.init(); + } + + private void createFramebuffer(int width, int height) + { + if (this.fogFramebuffer != -1) + { + GL32.glDeleteFramebuffers(this.fogFramebuffer); + this.fogFramebuffer = -1; + } + + if (this.fogTexture != -1) + { + GL32.glDeleteTextures(this.fogTexture); + this.fogTexture = -1; + } + + this.fogFramebuffer = GL32.glGenFramebuffers(); + GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.fogFramebuffer); + + this.fogTexture = GL32.glGenTextures(); + GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.fogTexture); + GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RGBA16, width, height, 0, GL32.GL_RGBA, GL32.GL_UNSIGNED_SHORT_4_4_4_4, (ByteBuffer) null); + GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_LINEAR); + GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_LINEAR); + GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.fogTexture, 0); + } + + + + //========// + // render // + //========// + + public void render(GLState primaryState, Mat4f projectionMatrix, float partialTicks) + { + GLState state = new GLState(); + this.init(); + + // resize the framebuffer if necessary + int width = MC_RENDER.getTargetFrameBufferViewportWidth(); + int height = MC_RENDER.getTargetFrameBufferViewportHeight(); + if (this.width != width || this.height != height) + { + this.width = width; + this.height = height; + this.createFramebuffer(width, height); + } + + FogShader.INSTANCE.frameBuffer = this.fogFramebuffer; + FogShader.INSTANCE.setProjectionMatrix(projectionMatrix); + FogShader.INSTANCE.render(partialTicks); + + // restored so we can write the SSAO texture to the main frame buffer + primaryState.restore(); + + FogApplyShader.INSTANCE.fogTexture = this.fogTexture; + FogApplyShader.INSTANCE.render(partialTicks); + + state.restore(); + } + + public void free() + { + FogShader.INSTANCE.free(); + FogApplyShader.INSTANCE.free(); + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java index 6ea356fd8..2a6e2e688 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java @@ -38,6 +38,7 @@ import com.seibel.distanthorizons.core.render.glObject.GLState; import com.seibel.distanthorizons.core.render.glObject.buffer.GLVertexBuffer; import com.seibel.distanthorizons.core.render.glObject.buffer.QuadElementBuffer; import com.seibel.distanthorizons.core.render.glObject.texture.*; +import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer; import com.seibel.distanthorizons.core.render.renderer.shaders.*; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; @@ -50,16 +51,13 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccess import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import com.seibel.distanthorizons.coreapi.DependencyInjection.OverrideInjector; -import com.seibel.distanthorizons.coreapi.util.math.Mat4f; -import com.seibel.distanthorizons.coreapi.util.math.Vec3d; -import com.seibel.distanthorizons.coreapi.util.math.Vec3f; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.util.math.Vec3d; +import com.seibel.distanthorizons.core.util.math.Vec3f; import org.apache.logging.log4j.LogManager; import org.lwjgl.opengl.GL32; import java.awt.*; -import java.io.File; -import java.io.FileWriter; -import java.io.IOException; import java.time.Duration; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; @@ -97,90 +95,14 @@ public class LodRenderer private int cachedWidth; private int cachedHeight; - - /** called by each {@link ColumnRenderBuffer} before rendering */ - public void setModelViewMatrixOffset(DhBlockPos pos, DhApiRenderParam renderEventParam) throws IllegalStateException - { - Vec3d cam = MC_RENDER.getCameraExactPosition(); - Vec3f modelPos = new Vec3f((float) (pos.x - cam.x), (float) (pos.y - cam.y), (float) (pos.z - cam.z)); - - - IDhApiShaderProgram shaderProgram = this.lodRenderProgram; - IDhApiShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class); - if (shaderProgramOverride != null && shaderProgram.overrideThisFrame()) - { - shaderProgram = shaderProgramOverride; - } - - if (!GL32.glIsProgram(shaderProgram.getId())) - { - throw new IllegalStateException("No GL program exists with the ID: [" + shaderProgram.getId() + "]. This either means a shader program was freed while it was still in use or was never created."); - } - - shaderProgram.bind(); - shaderProgram.setModelOffsetPos(modelPos); - - ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeBufferRenderEvent.class, new DhApiBeforeBufferRenderEvent.EventParam(renderEventParam, modelPos)); - } - - public void drawVbo(GLVertexBuffer vbo) - { - //// can be uncommented to add additional debug validation to prevent crashes if invalid buffers are being created - //// shouldn't be used in production due to the performance hit - //if (GL32.glIsBuffer(vbo.getId())) - { - IDhApiShaderProgram shaderProgram = this.lodRenderProgram; - IDhApiShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class); - if (shaderProgramOverride != null && shaderProgram.overrideThisFrame()) - { - shaderProgram = shaderProgramOverride; - } - - - vbo.bind(); - shaderProgram.bindVertexBuffer(vbo.getId()); - GL32.glDrawElements(GL32.GL_TRIANGLES, (vbo.getVertexCount() / 4) * 6, // TODO what does the 4 and 6 here represent? - this.quadIBO.getType(), 0); - vbo.unbind(); - } - //else - //{ - // // will spam the log if uncommented, but helpful for validation - // //LOGGER.warn("Unable to draw VBO: "+vbo.getId()); - //} - } - - - public static class LagSpikeCatcher - { - long timer = System.nanoTime(); - - public LagSpikeCatcher() { } - - public void end(String source) - { - if (!ENABLE_DRAW_LAG_SPIKE_LOGGING) - { - return; - } - - this.timer = System.nanoTime() - this.timer; - if (this.timer > DRAW_LAG_SPIKE_THRESHOLD_NS) - { - //4 ms - EVENT_LOGGER.debug("NOTE: " + source + " took " + Duration.ofNanos(this.timer) + "!"); - } - - } - - } - private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); private final ReentrantLock setupLock = new ReentrantLock(); public final RenderBufferHandler bufferHandler; + public final GenericObjectRenderer genericObjectRenderer; + // The shader program IDhApiShaderProgram lodRenderProgram = null; @@ -205,9 +127,10 @@ public class LodRenderer // constructor // //=============// - public LodRenderer(RenderBufferHandler bufferHandler) + public LodRenderer(RenderBufferHandler bufferHandler, GenericObjectRenderer genericObjectRenderer) { this.bufferHandler = bufferHandler; + this.genericObjectRenderer = genericObjectRenderer; } private boolean rendererClosed = false; @@ -374,16 +297,41 @@ public class LodRenderer // Disable blending for opaque rendering GL32.glDisable(GL32.GL_BLEND); + + // terrain profiler.popPush("LOD Opaque"); ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderPassEvent.class, renderEventParam); - - // TODO: Directional culling this.bufferHandler.renderOpaque(this, renderEventParam); + // custom objects with SSAO + if (Config.Client.Advanced.Graphics.GenericRendering.enableRendering.get()) + { + profiler.popPush("Custom Objects"); + this.genericObjectRenderer.render(renderEventParam, profiler, true); + } + + + // SSAO if (Config.Client.Advanced.Graphics.Ssao.enabled.get()) { profiler.popPush("LOD SSAO"); - SSAORenderer.INSTANCE.render(minecraftGlState, renderEventParam.dhProjectionMatrix, renderEventParam.partialTicks); + SSAORenderer.INSTANCE.render(minecraftGlState, new Mat4f(renderEventParam.dhProjectionMatrix), renderEventParam.partialTicks); + } + + + // custom objects without SSAO + if (Config.Client.Advanced.Graphics.GenericRendering.enableRendering.get()) + { + profiler.popPush("Custom Objects"); + this.genericObjectRenderer.render(renderEventParam, profiler, false); + } + + + //DarkShader.INSTANCE.render(partialTicks); // A test shader to make the world darker + + if (!deferTransparentRendering && Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled) + { + this.renderTransparentBuffers(profiler, renderEventParam, renderEventParam.partialTicks); } @@ -394,16 +342,9 @@ public class LodRenderer Mat4f combinedMatrix = new Mat4f(renderEventParam.dhProjectionMatrix); combinedMatrix.multiply(renderEventParam.dhModelViewMatrix); - FogShader.INSTANCE.setModelViewProjectionMatrix(combinedMatrix); - FogShader.INSTANCE.render(renderEventParam.partialTicks); + FogRenderer.INSTANCE.render(minecraftGlState, combinedMatrix, renderEventParam.partialTicks); } - //DarkShader.INSTANCE.render(partialTicks); // A test shader to make the world darker - - if (!deferTransparentRendering && Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled) - { - this.renderTransparentBuffers(profiler, renderEventParam, renderEventParam.partialTicks); - } drawLagSpikeCatcher.end("LodDraw"); @@ -422,9 +363,10 @@ public class LodRenderer // Note: this can be very slow if a lot of boxes are being rendered DebugRenderer.INSTANCE.render(combinedMatrix); - profiler.popPush("LOD cleanup"); } + profiler.popPush("LOD cleanup"); + if (this.usingMcFrameBuffer) @@ -462,6 +404,17 @@ public class LodRenderer if (Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled) { this.renderTransparentBuffers(profiler, renderEventParam, renderEventParam.partialTicks); + + + if (Config.Client.Advanced.Graphics.Fog.drawMode.get() != EDhApiFogDrawMode.FOG_DISABLED) + { + profiler.popPush("LOD Fog"); + + Mat4f combinedMatrix = new Mat4f(renderEventParam.dhProjectionMatrix); + combinedMatrix.multiply(renderEventParam.dhModelViewMatrix); + + FogRenderer.INSTANCE.render(minecraftGlState, combinedMatrix, renderEventParam.partialTicks); + } } drawLagSpikeCatcher.end("LodTranslucentDraw"); @@ -516,14 +469,56 @@ public class LodRenderer this.bufferHandler.renderTransparent(this, renderEventParam); GL32.glDepthMask(true); // Apparently the depth mask state is stored in the FBO, so glState fails to restore it... - - if (Config.Client.Advanced.Graphics.Fog.drawMode.get() != EDhApiFogDrawMode.FOG_DISABLED) - { - profiler.popPush("LOD Fog"); - FogShader.INSTANCE.render(partialTicks); - } } + /** called by each {@link ColumnRenderBuffer} before rendering */ + public void setModelViewMatrixOffset(DhBlockPos pos, DhApiRenderParam renderEventParam) throws IllegalStateException + { + Vec3d cam = MC_RENDER.getCameraExactPosition(); + Vec3f modelPos = new Vec3f((float) (pos.x - cam.x), (float) (pos.y - cam.y), (float) (pos.z - cam.z)); + + + IDhApiShaderProgram shaderProgram = this.lodRenderProgram; + IDhApiShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class); + if (shaderProgramOverride != null && shaderProgram.overrideThisFrame()) + { + shaderProgram = shaderProgramOverride; + } + + shaderProgram.bind(); + shaderProgram.setModelOffsetPos(modelPos); + + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeBufferRenderEvent.class, new DhApiBeforeBufferRenderEvent.EventParam(renderEventParam, modelPos)); + } + + public void drawVbo(GLVertexBuffer vbo) + { + //// can be uncommented to add additional debug validation to prevent crashes if invalid buffers are being created + //// shouldn't be used in production due to the performance hit + //if (GL32.glIsBuffer(vbo.getId())) + { + IDhApiShaderProgram shaderProgram = this.lodRenderProgram; + IDhApiShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class); + if (shaderProgramOverride != null && shaderProgram.overrideThisFrame()) + { + shaderProgram = shaderProgramOverride; + } + + + vbo.bind(); + shaderProgram.bindVertexBuffer(vbo.getId()); + GL32.glDrawElements(GL32.GL_TRIANGLES, (vbo.getVertexCount() / 4) * 6, // TODO what does the 4 and 6 here represent? + this.quadIBO.getType(), 0); + vbo.unbind(); + } + //else + //{ + // // will spam the log if uncommented, but helpful for validation + // //LOGGER.warn("Unable to draw VBO: "+vbo.getId()); + //} + } + + //=================// @@ -616,6 +611,7 @@ public class LodRenderer boolean renderWireframe = Config.Client.Advanced.Debugging.renderWireframe.get(); if (renderWireframe) { + // TODO fix GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_LINE); //GL32.glDisable(GL32.GL_CULL_FACE); } @@ -650,7 +646,7 @@ public class LodRenderer this.fogConfig = newFogConfig; this.lodRenderProgram.free(); - this.lodRenderProgram = new LodRenderProgram(); + this.lodRenderProgram = new DhTerrainShaderProgram(); FogShader.INSTANCE.free(); FogShader.INSTANCE = new FogShader(newFogConfig); @@ -690,7 +686,7 @@ public class LodRenderer EVENT_LOGGER.info("Setting up renderer"); this.isSetupComplete = true; - this.lodRenderProgram = new LodRenderProgram(); + this.lodRenderProgram = new DhTerrainShaderProgram(); if (ENABLE_IBO) { this.quadIBO = new QuadElementBuffer(); @@ -868,4 +864,35 @@ public class LodRenderer } } + + + //================// + // helper classes // + //================// + + public static class LagSpikeCatcher + { + long timer = System.nanoTime(); + + public LagSpikeCatcher() { } + + public void end(String source) + { + if (!ENABLE_DRAW_LAG_SPIKE_LOGGING) + { + return; + } + + this.timer = System.nanoTime() - this.timer; + if (this.timer > DRAW_LAG_SPIKE_THRESHOLD_NS) + { + //4 ms + EVENT_LOGGER.debug("NOTE: " + source + " took " + Duration.ofNanos(this.timer) + "!"); + } + + } + + } + + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/SSAORenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/SSAORenderer.java index 77519aa8c..b9953cd31 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/SSAORenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/SSAORenderer.java @@ -24,7 +24,7 @@ import com.seibel.distanthorizons.core.render.glObject.GLState; import com.seibel.distanthorizons.core.render.renderer.shaders.SSAOApplyShader; import com.seibel.distanthorizons.core.render.renderer.shaders.SSAOShader; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; -import com.seibel.distanthorizons.coreapi.util.math.Mat4f; +import com.seibel.distanthorizons.core.util.math.Mat4f; import org.lwjgl.opengl.GL32; import java.nio.ByteBuffer; @@ -51,6 +51,7 @@ public class SSAORenderer private int ssaoTexture = -1; + //=============// // constructor // //=============// @@ -92,6 +93,7 @@ public class SSAORenderer } + //========// // render // //========// @@ -99,12 +101,11 @@ public class SSAORenderer public void render(GLState primaryState, Mat4f projectionMatrix, float partialTicks) { GLState state = new GLState(); - this.init(); - + + // resize the framebuffer if necessary int width = MC_RENDER.getTargetFrameBufferViewportWidth(); int height = MC_RENDER.getTargetFrameBufferViewportHeight(); - if (this.width != width || this.height != height) { this.width = width; @@ -116,6 +117,7 @@ public class SSAORenderer SSAOShader.INSTANCE.setProjectionMatrix(projectionMatrix); SSAOShader.INSTANCE.render(partialTicks); + // restored so we can write the SSAO texture to the main frame buffer primaryState.restore(); SSAOApplyShader.INSTANCE.ssaoTexture = this.ssaoTexture; @@ -129,4 +131,5 @@ public class SSAORenderer SSAOShader.INSTANCE.free(); SSAOApplyShader.INSTANCE.free(); } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/BeaconRenderHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/BeaconRenderHandler.java new file mode 100644 index 000000000..d1beb0701 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/BeaconRenderHandler.java @@ -0,0 +1,241 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.render.renderer.generic; + +import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial; +import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; +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.DhBlockPos; +import com.seibel.distanthorizons.core.pos.DhChunkPos; +import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; +import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo; +import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import com.seibel.distanthorizons.coreapi.ModInfo; +import org.apache.logging.log4j.Logger; +import org.jetbrains.annotations.NotNull; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; + +public class BeaconRenderHandler +{ + private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + + private static final int BEAM_TOP_Y = 6_000; + + + /** if this is null then the other handler is probably null too, but just in case */ + private final BeaconBeamRepo beaconBeamRepo; + + private final IDhApiRenderableBoxGroup beaconBoxGroup; + private final HashMap beaconRefCountByBlockPos = new HashMap<>(); + + + + //=============// + // constructor // + //=============// + + public BeaconRenderHandler(@NotNull BeaconBeamRepo beaconBeamRepo, @NotNull GenericObjectRenderer renderer) + { + this.beaconBeamRepo = beaconBeamRepo; + + this.beaconBoxGroup = GenericRenderObjectFactory.INSTANCE.createAbsolutePositionedGroup(ModInfo.NAME+":Beacons", new ArrayList<>(0)); + this.beaconBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT); + this.beaconBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT); + this.beaconBoxGroup.setSsaoEnabled(false); + this.beaconBoxGroup.setShading(DhApiRenderableBoxGroupShading.getUnshaded()); + this.beaconBoxGroup.setPreRenderFunc((renderEventParam) -> this.beaconBoxGroup.setActive(Config.Client.Advanced.Graphics.GenericRendering.enableBeaconRendering.get())); + + renderer.add(this.beaconBoxGroup); + } + + + + //=========================// + // level loading/unloading // + //=========================// + + public void setBeaconBeamsForChunk(DhChunkPos chunkPos, List newBeamList) + { + // synchronized to prevent two threads from updating the same chunk at the same time + synchronized (this) + { + HashSet allPosSet = new HashSet<>(); + + // sort new beams + HashMap newBeamByPos = new HashMap<>(newBeamList.size()); + for (int i = 0; i < newBeamList.size(); i++) + { + BeaconBeamDTO beam = newBeamList.get(i); + newBeamByPos.put(beam.pos, beam); + allPosSet.add(beam.pos); + } + + // get existing beams + List existingBeamList = this.beaconBeamRepo.getAllBeamsForPos(chunkPos); + HashMap existingBeamByPos = new HashMap<>(existingBeamList.size()); + for (int i = 0; i < existingBeamList.size(); i++) + { + BeaconBeamDTO beam = existingBeamList.get(i); + existingBeamByPos.put(beam.pos, beam); + allPosSet.add(beam.pos); + } + + + + for (DhBlockPos beaconPos : allPosSet) + { + if (!chunkPos.contains(beaconPos)) + { + // don't update beacons outside the updated chunk + continue; + } + + BeaconBeamDTO existingBeam = existingBeamByPos.get(beaconPos); + BeaconBeamDTO newBeam = newBeamByPos.get(beaconPos); + + if (existingBeam != null && newBeam != null) + { + // beam still exists in chunk + if (!existingBeam.color.equals(newBeam.color)) + { + // beam colors were changed + this.beaconBeamRepo.save(newBeam); + this.updateBeaconColor(newBeam); + } + } + else if (existingBeam == null && newBeam != null) + { + // new beam found, add to DB + this.beaconBeamRepo.save(newBeam); + this.startRenderingBeacon(newBeam); + } + else if (existingBeam != null && newBeam == null) + { + // beam no longer exists at position, remove from DB + this.beaconBeamRepo.deleteWithKey(beaconPos); // TODO broken when updating adjacent chunks + this.stopRenderingBeaconAtPos(beaconPos); + } + + } + } + } + + public void loadBeaconBeamsInPos(long pos) + { + // get all beams in pos + List existingBeamList = this.beaconBeamRepo.getAllBeamsForPos(pos); + for (int i = 0; i < existingBeamList.size(); i++) + { + BeaconBeamDTO newBeam = existingBeamList.get(i); + this.startRenderingBeacon(newBeam); + } + } + + public void unloadBeaconBeamsInPos(long pos) + { + // get all beams in pos + List existingBeamList = this.beaconBeamRepo.getAllBeamsForPos(pos); + for (int i = 0; i < existingBeamList.size(); i++) + { + BeaconBeamDTO beam = existingBeamList.get(i); + this.stopRenderingBeaconAtPos(beam.pos); + } + } + + + + //=================// + // render handling // + //=================// + + private void startRenderingBeacon(BeaconBeamDTO beacon) + { + this.beaconRefCountByBlockPos.compute(beacon.pos, (beamPos, beaconRefCount) -> + { + if (beaconRefCount == null) { beaconRefCount = new AtomicInteger(); } + if (beaconRefCount.getAndIncrement() == 0) + { + DhApiRenderableBox beaconBox = new DhApiRenderableBox( + new DhApiVec3d(beacon.pos.x, beacon.pos.y+1, beacon.pos.z), + new DhApiVec3d(beacon.pos.x+1, BEAM_TOP_Y, beacon.pos.z+1), + beacon.color, + EDhApiBlockMaterial.ILLUMINATED + ); + + this.beaconBoxGroup.add(beaconBox); + this.beaconBoxGroup.triggerBoxChange(); + } + return beaconRefCount; + }); + } + + private void stopRenderingBeaconAtPos(DhBlockPos beaconPos) + { + this.beaconRefCountByBlockPos.compute(beaconPos, (pos, beaconRefCount) -> + { + if (beaconRefCount != null + && beaconRefCount.decrementAndGet() <= 0) + { + this.beaconBoxGroup.removeIf((box) -> + box.minPos.x == beaconPos.x + && box.minPos.y == beaconPos.y+1 // plus 1 because the beam starts above the beacon + && box.minPos.z == beaconPos.z + ); + this.beaconBoxGroup.triggerBoxChange(); + return null; + } + else + { + return beaconRefCount; + } + }); + } + + private void updateBeaconColor(BeaconBeamDTO newBeam) + { + DhBlockPos pos = newBeam.pos; + for (int i = 0; i < this.beaconBoxGroup.size(); i++) + { + DhApiRenderableBox box = this.beaconBoxGroup.get(i); + if (box.minPos.x == pos.x + && box.minPos.y == pos.y+1 // plus 1 because the beam starts above the beacon + && box.minPos.z == pos.z) + { + box.color = newBeam.color; + this.beaconBoxGroup.triggerBoxChange(); + break; + } + } + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/CloudRenderHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/CloudRenderHandler.java new file mode 100644 index 000000000..490522720 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/CloudRenderHandler.java @@ -0,0 +1,403 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.render.renderer.generic; + +import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial; +import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.level.IDhLevel; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import com.seibel.distanthorizons.coreapi.ModInfo; +import org.apache.logging.log4j.Logger; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.*; + +public class CloudRenderHandler +{ + private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + + private static final String CLOUD_RESOURCE_TEXTURE_PATH = "assets/distanthorizons/textures/clouds.png"; + // FIXME transparency sorting makes having transparent clouds impossible + private static final Color CLOUD_COLOR = new Color(255,255,255,255); + + private static final boolean DEBUG_BORDER_COLORS = false; + + /** + * How wide an individual box is.
+ * Measured in blocks. + */ + private static final int CLOUD_BOX_WIDTH = 64; + /** measured in blocks */ + private static final int CLOUD_BOX_THICKNESS = 16; + + private final IDhApiRenderableBoxGroup[][] boxGroupByOffset = new IDhApiRenderableBoxGroup[3][3]; + private final IDhLevel level; + private final GenericObjectRenderer renderer; + + private float moveSpeedInBlocksPerSecond = 3.0f; + private boolean disabledWarningLogged = false; + + + + //=============// + // constructor // + //=============// + + public CloudRenderHandler(IDhLevel level, GenericObjectRenderer renderer) + { + this.level = level; + this.renderer = renderer; + + + + //=======================// + // get the cloud texture // + //=======================// + + // default to a single empty slot in case the texture is broken + boolean[][] cloudLocations = new boolean[1][1]; + try + { + cloudLocations = getCloudsFromTexture(); + } + catch (FileNotFoundException e) + { + LOGGER.error(e.getMessage(), e); + } + catch (IOException e) + { + LOGGER.error("Unexpected issue getting cloud texture, error: ["+e.getMessage()+"].", e); + } + + if (cloudLocations.length != 0 && + cloudLocations.length != cloudLocations[0].length) + { + LOGGER.warn("Non-square cloud texture found, some parts of the texture will be clipped off."); + } + + + + //===================// + // parse the texture // + //===================// + + int textureWidth = cloudLocations.length; + ArrayList boxList = new ArrayList<>(512); + for (int x = 0; x < textureWidth; x ++) + { + for (int z = 0; z < textureWidth; z ++) + { + if (cloudLocations[x][z]) + { + // start a new box in Z direction + int startZ = z; + int startX = x; + int endZ = startZ; + int endX = x+1; + + + + //==========================// + // merge in the Z direction // + //==========================// + + // Find the cloud's length in the Z direction + while (endZ < textureWidth + && cloudLocations[x][endZ]) + { + endZ++; + } + // update the z iterator so we can skip over everything included in this cloud + z = endZ - 1; + + + + //==========================// + // merge in the X direction // + //==========================// + + for (int currentX = startX + 1; currentX < textureWidth; currentX++) + { + boolean canMergeInXDir = true; + + // check if all locations in this column are true + for (int adjacentZ = startZ; adjacentZ < endZ; adjacentZ++) + { + if (!cloudLocations[currentX][adjacentZ]) + { + // at least one pixel in the texture is false, + // so we can't merge in this direction + canMergeInXDir = false; + break; + } + } + + + if (canMergeInXDir) + { + // mark the adjacent column as processed + for (int currentZ = startZ; currentZ < endZ; currentZ++) + { + // by flipping all the pixels in the adjacent column to false, + // we don't have to worry about adding another cloud + cloudLocations[currentX][currentZ] = false; + } + + endX = (currentX + 1); + } + else + { + break; + } + } + + + + //============================// + // Create the renderable box // + //============================// + + // endZ contains the last cloud index + // so the cloud now goes from startZ to endZ (inclusive) + int minXBlockPos = startX * CLOUD_BOX_WIDTH; + int minZBlockPos = startZ * CLOUD_BOX_WIDTH; + int maxXBlockPos = endX * CLOUD_BOX_WIDTH; + int maxZBlockPos = endZ * CLOUD_BOX_WIDTH; + + Color color = CLOUD_COLOR; + if (DEBUG_BORDER_COLORS) + { + // equals is included so the board is 2 blocks wide, it makes it easier to see + if (startX <= 1) { color = Color.RED; } + else if (startX >= textureWidth - 2) { color = Color.GREEN; } + if (startZ <= 1) { color = Color.BLUE; } + else if (endZ >= textureWidth - 2) { color = Color.BLACK; } + } + + DhApiRenderableBox box = new DhApiRenderableBox( + new DhApiVec3d(minXBlockPos, 0, minZBlockPos), + new DhApiVec3d(maxXBlockPos, CLOUD_BOX_THICKNESS, maxZBlockPos), + color, + EDhApiBlockMaterial.UNKNOWN + ); + boxList.add(box); + } + } + } + + + + //========================// + // create the renderables // + //========================// + + // slightly lighter shading than the default + DhApiRenderableBoxGroupShading cloudShading = DhApiRenderableBoxGroupShading.getUnshaded(); + cloudShading.north = cloudShading.south = 0.9f; + cloudShading.east = cloudShading.west = 0.8f; + cloudShading.top = 1.0f; + cloudShading.bottom = 0.7f; + + // 3x3 area so we clouds should always be overhead + for (int x = -1; x <= 1; x++) + { + for (int z = -1; z <= 1; z++) + { + IDhApiRenderableBoxGroup boxGroup = GenericRenderObjectFactory.INSTANCE.createRelativePositionedGroup( + ModInfo.NAME + ":Clouds", + new DhApiVec3d(0, 0, 0), // the offset will be set during rendering + boxList); + boxGroup.setBlockLight(LodUtil.MIN_MC_LIGHT); + boxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT); + boxGroup.setSsaoEnabled(false); + boxGroup.setShading(cloudShading); + + CloudParams params = new CloudParams(textureWidth, x, z); + boxGroup.setPreRenderFunc((renderParam) -> this.preRender(params)); + + renderer.add(boxGroup); + this.boxGroupByOffset[x+1][z+1] = boxGroup; + } + } + } + + private void preRender(CloudParams clouds) + { + IDhApiRenderableBoxGroup boxGroup = this.boxGroupByOffset[clouds.instanceOffsetX+1][clouds.instanceOffsetZ+1]; + + boolean renderClouds = Config.Client.Advanced.Graphics.GenericRendering.enableCloudRendering.get(); + boxGroup.setActive(renderClouds); + if(!renderClouds) + { + return; + } + + if (!this.renderer.getUseInstancedRendering()) + { + if (!this.disabledWarningLogged) + { + this.disabledWarningLogged = true; + LOGGER.warn("Instanced rendering unavailable, cloud rendering disabled."); + } + boxGroup.setActive(false); + return; + } + + + //================// + // cloud movement // + //================// + + long currentTime = System.currentTimeMillis(); + float deltaTime = (currentTime - clouds.lastFrameTime) / 1000.0f; // Delta time in seconds + clouds.lastFrameTime = currentTime; + + float deltaX = this.moveSpeedInBlocksPerSecond * deltaTime; + // negative delta is to match vanilla's cloud movement + clouds.xOffset -= deltaX; + // wrap the cloud around after reaching the edge + clouds.xOffset %= clouds.widthInBlocks; + + + + //============================// + // camera movement and offset // + //============================// + + // camera position + int cameraPosX = (int)MC_RENDER.getCameraExactPosition().x; + int cameraPosZ = (int)MC_RENDER.getCameraExactPosition().z; + // offset the camera position by negative 1 width when below zero to fix off-by-one errors in the negative direction + if (cameraPosX < 0) { cameraPosX -= clouds.widthInBlocks; } + if (cameraPosZ < 0) { cameraPosZ -= clouds.widthInBlocks; } + + // determine how many cloud instances away from the origin we are + int cloudInstanceOffsetX = cameraPosX / (int)clouds.widthInBlocks; + int cloudInstanceOffsetZ = cameraPosZ / (int)clouds.widthInBlocks; + + // calculate the new offset + float xOffset = (cloudInstanceOffsetX * clouds.widthInBlocks); + float zOffset = (cloudInstanceOffsetZ * clouds.widthInBlocks); + + + + //==============// + // update group // + //==============// + + boxGroup.setOriginBlockPos( + new DhApiVec3d( + clouds.xOffset + (clouds.instanceOffsetX * clouds.widthInBlocks) + xOffset + clouds.halfWidthInBlocks, + this.level.getLevelWrapper().getMaxHeight() + 200, + clouds.zOffset + (clouds.instanceOffsetZ * clouds.widthInBlocks) + zOffset + clouds.halfWidthInBlocks + ) + ); + } + + + + //==================// + // texture handling // + //==================// + + private static boolean[][] getCloudsFromTexture() throws FileNotFoundException, IOException + { + final ClassLoader loader = Thread.currentThread().getContextClassLoader(); + + boolean[][] whitePixels = null; + try(InputStream imageInputStream = loader.getResourceAsStream(CLOUD_RESOURCE_TEXTURE_PATH)) + { + if (imageInputStream == null) + { + throw new FileNotFoundException("Unable to find cloud texture at resource path: ["+CLOUD_RESOURCE_TEXTURE_PATH+"]."); + } + + BufferedImage image = ImageIO.read(imageInputStream); + + int width = image.getWidth(); + int height = image.getHeight(); + + whitePixels = new boolean[width][height]; + + for (int x = 0; x < width; x ++) + { + for (int z = 0; z < width; z ++) + { + Color color = new Color(image.getRGB(x,z)); + whitePixels[x][z] = color.equals(Color.WHITE); + } + } + } + + return whitePixels; + } + + + + //================// + // helper classes // + //================// + + private static class CloudParams + { + public final float textureWidth; + public final float widthInBlocks; + public final float halfWidthInBlocks; + + public final int instanceOffsetX; + public final int instanceOffsetZ; + + + public float xOffset = 0; + public float zOffset = 0; + + public long lastFrameTime = System.currentTimeMillis(); + + + + // constructor // + + public CloudParams(float textureWidth, int instanceOffsetX, int instanceOffsetZ) + { + this.textureWidth = textureWidth; + this.widthInBlocks = (this.textureWidth * CLOUD_BOX_WIDTH); + this.halfWidthInBlocks = this.widthInBlocks / 2; + + this.instanceOffsetX = instanceOffsetX; + this.instanceOffsetZ = instanceOffsetZ; + } + + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java new file mode 100644 index 000000000..b3768548d --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java @@ -0,0 +1,607 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.render.renderer.generic; + +import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod; +import com.seibel.distanthorizons.api.enums.config.EDhApiLoggerMode; +import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial; +import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiGenericObjectShaderProgram; +import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; +import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister; +import com.seibel.distanthorizons.api.methods.events.abstractEvents.*; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.logging.ConfigBasedSpamLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.logging.f3.F3Screen; +import com.seibel.distanthorizons.core.render.glObject.GLProxy; +import com.seibel.distanthorizons.core.render.glObject.GLState; +import com.seibel.distanthorizons.core.render.glObject.buffer.GLElementBuffer; +import com.seibel.distanthorizons.core.render.glObject.buffer.GLVertexBuffer; +import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; +import com.seibel.distanthorizons.core.util.math.Vec3d; +import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; +import com.seibel.distanthorizons.coreapi.DependencyInjection.OverrideInjector; +import com.seibel.distanthorizons.coreapi.ModInfo; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.lwjgl.opengl.ARBInstancedArrays; +import org.lwjgl.opengl.GL32; +import org.lwjgl.opengl.GL33; + +import java.awt.*; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Handles rendering generic groups of {@link DhApiRenderableBox}. + * + * @see IDhApiCustomRenderRegister + * @see DhApiRenderableBox + */ +public class GenericObjectRenderer implements IDhApiCustomRenderRegister +{ + private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + public static final ConfigBasedSpamLogger SPAM_LOGGER = new ConfigBasedSpamLogger(LogManager.getLogger(GenericObjectRenderer.class), () -> EDhApiLoggerMode.LOG_ALL_TO_CHAT, 1); + + private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + + /** + * Can be used to troubleshoot the renderer. + * If enabled several debug objects will render around (0,150,0). + */ + public static final boolean RENDER_DEBUG_OBJECTS = false; + + + // rendering setup + private boolean init = false; + + private IDhApiGenericObjectShaderProgram shaderProgram; + private GLVertexBuffer boxVertexBuffer; + private GLElementBuffer boxIndexBuffer; + + private boolean useInstancedRendering; + private boolean vertexAttribDivisorSupported; + private boolean instancedArraysSupported; + + + + private final ConcurrentHashMap boxGroupById = new ConcurrentHashMap<>(); + + + + /** A box from 0,0,0 to 1,1,1 */ + private static final float[] BOX_VERTICES = { + // Pos x y z + + // min X, vertical face + 0, 0, 0, + 1, 0, 0, + 1, 1, 0, + 0, 1, 0, + // max X, vertical face + 0, 1, 1, + 1, 1, 1, + 1, 0, 1, + 0, 0, 1, + + // min Z, vertical face + 0, 0, 1, + 0, 0, 0, + 0, 1, 0, + 0, 1, 1, + // max Z, vertical face + 1, 0, 1, + 1, 1, 1, + 1, 1, 0, + 1, 0, 0, + + // min Y, horizontal face + 0, 0, 1, + 1, 0, 1, + 1, 0, 0, + 0, 0, 0, + // max Y, horizontal face + 0, 1, 1, + 1, 1, 1, + 1, 1, 0, + 0, 1, 0, + }; + + private static final int[] BOX_INDICES = { + // min X, vertical face + 2, 1, 0, + 0, 3, 2, + // max X, vertical face + 6, 5, 4, + 4, 7, 6, + + // min Z, vertical face + 10, 9, 8, + 8, 11, 10, + // max Z, vertical face + 14, 13, 12, + 12, 15, 14, + + // min Y, horizontal face + 18, 17, 16, + 16, 19, 18, + // max Y, horizontal face + 20, 21, 22, + 22, 23, 20, + }; + + + + //=============// + // constructor // + //=============// + + public GenericObjectRenderer() { } + + public void init() + { + if (this.init) + { + return; + } + this.init = true; + + this.vertexAttribDivisorSupported = GLProxy.getInstance().vertexAttribDivisorSupported; + this.instancedArraysSupported = GLProxy.getInstance().instancedArraysSupported; + this.useInstancedRendering = this.vertexAttribDivisorSupported || this.instancedArraysSupported; + if (!this.useInstancedRendering) + { + LOGGER.warn("Instanced rendering not supported by this GPU, falling back to direct rendering. Generic object rendering will be slow."); + } + + this.shaderProgram = new GenericObjectShaderProgram(this.useInstancedRendering); + + + this.createBuffers(); + + if (RENDER_DEBUG_OBJECTS) + { + this.addGenericDebugObjects(); + } + } + private void createBuffers() + { + // box vertices + ByteBuffer boxVerticesBuffer = ByteBuffer.allocateDirect(BOX_VERTICES.length * Float.BYTES); + boxVerticesBuffer.order(ByteOrder.nativeOrder()); + boxVerticesBuffer.asFloatBuffer().put(BOX_VERTICES); + boxVerticesBuffer.rewind(); + this.boxVertexBuffer = new GLVertexBuffer(false); + this.boxVertexBuffer.bind(); + this.boxVertexBuffer.uploadBuffer(boxVerticesBuffer, 8, EDhApiGpuUploadMethod.DATA, BOX_VERTICES.length * Float.BYTES); + + + // box vertex indexes + ByteBuffer solidIndexBuffer = ByteBuffer.allocateDirect(BOX_INDICES.length * Integer.BYTES); + solidIndexBuffer.order(ByteOrder.nativeOrder()); + solidIndexBuffer.asIntBuffer().put(BOX_INDICES); + solidIndexBuffer.rewind(); + this.boxIndexBuffer = new GLElementBuffer(false); + this.boxIndexBuffer.uploadBuffer(solidIndexBuffer, EDhApiGpuUploadMethod.DATA, BOX_INDICES.length * Integer.BYTES, GL32.GL_STATIC_DRAW); + this.boxIndexBuffer.bind(); + + } + private void addGenericDebugObjects() + { + GenericRenderObjectFactory factory = GenericRenderObjectFactory.INSTANCE; + + + // single giant box + IDhApiRenderableBoxGroup singleGiantBoxGroup = factory.createForSingleBox( + ModInfo.NAME + ":CyanChunkBox", + new DhApiRenderableBox( + new DhApiVec3d(0,0,0), new DhApiVec3d(16,190,16), + new Color(Color.CYAN.getRed(), Color.CYAN.getGreen(), Color.CYAN.getBlue(), 125), + EDhApiBlockMaterial.WATER) + ); + singleGiantBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT); + singleGiantBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT); + this.add(singleGiantBoxGroup); + + + // single slender box + IDhApiRenderableBoxGroup singleTallBoxGroup = factory.createForSingleBox( + ModInfo.NAME + ":GreenBeacon", + new DhApiRenderableBox( + new DhApiVec3d(16,0,31), new DhApiVec3d(17,2000,32), + new Color(Color.GREEN.getRed(), Color.GREEN.getGreen(), Color.GREEN.getBlue(), 125), + EDhApiBlockMaterial.ILLUMINATED) + ); + singleTallBoxGroup.setSkyLight(LodUtil.MAX_MC_LIGHT); + singleTallBoxGroup.setBlockLight(LodUtil.MAX_MC_LIGHT); + this.add(singleTallBoxGroup); + + + // absolute box group + ArrayList absBoxList = new ArrayList<>(); + for (int i = 0; i < 18; i++) + { + absBoxList.add(new DhApiRenderableBox( + new DhApiVec3d(i,150+i,24), new DhApiVec3d(1+i,151+i,25), + new Color(Color.ORANGE.getRed(), Color.ORANGE.getGreen(), Color.ORANGE.getBlue()), + EDhApiBlockMaterial.LAVA + ) + ); + } + IDhApiRenderableBoxGroup absolutePosBoxGroup = factory.createAbsolutePositionedGroup(ModInfo.NAME + ":OrangeStairs", absBoxList); + this.add(absolutePosBoxGroup); + + + // relative box group + ArrayList relBoxList = new ArrayList<>(); + for (int i = 0; i < 8; i+=2) + { + relBoxList.add(new DhApiRenderableBox( + new DhApiVec3d(0,i,0), new DhApiVec3d(1,1+i,1), + new Color(Color.MAGENTA.getRed(), Color.MAGENTA.getGreen(), Color.MAGENTA.getBlue()), + EDhApiBlockMaterial.METAL + ) + ); + } + IDhApiRenderableBoxGroup relativePosBoxGroup = factory.createRelativePositionedGroup( + ModInfo.NAME + ":MovingMagentaGroup", + new DhApiVec3d(24, 140, 24), + relBoxList); + relativePosBoxGroup.setPreRenderFunc((event) -> + { + DhApiVec3d pos = relativePosBoxGroup.getOriginBlockPos(); + pos.x += event.partialTicks / 2; + pos.x %= 32; + relativePosBoxGroup.setOriginBlockPos(pos); + }); + this.add(relativePosBoxGroup); + + + // massive relative box group + ArrayList massRelBoxList = new ArrayList<>(); + for (int x = 0; x < 50*2; x+=2) + { + for (int z = 0; z < 50*2; z+=2) + { + massRelBoxList.add(new DhApiRenderableBox( + new DhApiVec3d(-x, 0, -z), new DhApiVec3d(1-x, 1, 1-z), + new Color(Color.RED.getRed(), Color.RED.getGreen(), Color.RED.getBlue()), + EDhApiBlockMaterial.TERRACOTTA + ) + ); + } + } + IDhApiRenderableBoxGroup massRelativePosBoxGroup = factory.createRelativePositionedGroup( + ModInfo.NAME + ":MassRedGroup", + new DhApiVec3d(-25, 140, 0), + massRelBoxList); + massRelativePosBoxGroup.setPreRenderFunc((event) -> + { + DhApiVec3d blockPos = massRelativePosBoxGroup.getOriginBlockPos(); + blockPos.y += event.partialTicks / 4; + if (blockPos.y > 150f) + { + blockPos.y = 140f; + + Color newColor = (massRelativePosBoxGroup.get(0).color == Color.RED) ? Color.RED.darker() : Color.RED; + massRelativePosBoxGroup.forEach((box) -> { box.color = newColor; }); + massRelativePosBoxGroup.triggerBoxChange(); + } + + massRelativePosBoxGroup.setOriginBlockPos(blockPos); + }); + this.add(massRelativePosBoxGroup); + } + + + + //==============// + // registration // + //==============// + + @Override + public void add(IDhApiRenderableBoxGroup iBoxGroup) throws IllegalArgumentException + { + if (!(iBoxGroup instanceof RenderableBoxGroup)) + { + throw new IllegalArgumentException("Box group must be of type ["+ RenderableBoxGroup.class.getSimpleName()+"], type received: ["+(iBoxGroup != null ? iBoxGroup.getClass() : "NULL")+"]."); + } + RenderableBoxGroup boxGroup = (RenderableBoxGroup) iBoxGroup; + + + long id = boxGroup.getId(); + if (this.boxGroupById.containsKey(id)) + { + throw new IllegalArgumentException("A box group with the ID [" + id + "] is already present."); + } + + this.boxGroupById.put(id, boxGroup); + } + + @Override + public IDhApiRenderableBoxGroup remove(long id) { return this.boxGroupById.remove(id); } + + public void clear() { this.boxGroupById.clear(); } + + + + //===========// + // rendering // + //===========// + + /** + * @param renderingWithSsao + * if true that means this render call is happening before the SSAO pass + * and any objects rendered in this pass will have SSAO applied to them. + */ + public void render(DhApiRenderParam renderEventParam, IProfilerWrapper profiler, boolean renderingWithSsao) + { + // render setup // + profiler.push("setup"); + + GLState glState = new GLState(); + this.init(); + + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderSetupEvent.class, renderEventParam); + + GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); + GL32.glEnable(GL32.GL_DEPTH_TEST); + + GL32.glEnable(GL32.GL_BLEND); + GL32.glBlendEquation(GL32.GL_FUNC_ADD); + GL32.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA); + + IDhApiGenericObjectShaderProgram shaderProgram = this.shaderProgram; + IDhApiGenericObjectShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiGenericObjectShaderProgram.class); + if (shaderProgramOverride != null && shaderProgram.overrideThisFrame()) + { + shaderProgram = shaderProgramOverride; + } + + shaderProgram.bind(renderEventParam); + shaderProgram.bindVertexBuffer(this.boxVertexBuffer.getId()); + + this.boxIndexBuffer.bind(); + + Vec3d camPos = MC_RENDER.getCameraExactPosition(); + + + + // rendering // + + Collection boxList = this.boxGroupById.values(); + for (RenderableBoxGroup boxGroup : boxList) + { + // skip boxes that shouldn't render this pass + if (boxGroup.ssaoEnabled == renderingWithSsao) + { + profiler.popPush("render prep"); + boxGroup.preRender(renderEventParam); + + // ignore inactive groups + if (boxGroup.active) + { + boolean cancelRendering = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericObjectRenderEvent.class, new DhApiBeforeGenericObjectRenderEvent.EventParam(renderEventParam, boxGroup)); + if (!cancelRendering) + { + profiler.popPush("rendering"); + profiler.push(boxGroup.getResourceLocationNamespace()); + profiler.push(boxGroup.getResourceLocationPath()); + if (this.useInstancedRendering) + { + this.renderBoxGroupInstanced(shaderProgram, renderEventParam, boxGroup, camPos); + } + else + { + this.renderBoxGroupDirect(shaderProgram, renderEventParam, boxGroup, camPos); + } + profiler.pop(); // resource path + profiler.pop(); // resource namespace + + boxGroup.postRender(renderEventParam); + } + } + } + } + + + + // clean up // + profiler.popPush("cleanup"); + + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderCleanupEvent.class, renderEventParam); + + shaderProgram.unbind(); + glState.restore(); + + profiler.pop(); + } + + + + //=====================// + // instanced rendering // + //=====================// + + private void renderBoxGroupInstanced(IDhApiGenericObjectShaderProgram shaderProgram, DhApiRenderParam renderEventParam, RenderableBoxGroup boxGroup, Vec3d camPos) + { + // update instance data // + + boxGroup.updateVertexAttributeData(); + + DhApiRenderableBoxGroupShading shading = boxGroup.shading; + if (shading == null) + { + shading = DhApiRenderableBoxGroupShading.getUnshaded(); + } + + shaderProgram.fillIndirectUniformData( + renderEventParam, + shading, boxGroup, + camPos); + + + + // Bind instance data // + + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, boxGroup.instanceColorVbo); + GL32.glEnableVertexAttribArray(1); + GL32.glVertexAttribPointer(1, 4, GL32.GL_FLOAT, false, 4 * Float.BYTES, 0); + this.vertexAttribDivisor(1, 1); + + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, boxGroup.instanceScaleVbo); + GL32.glEnableVertexAttribArray(2); + this.vertexAttribDivisor(2, 1); + GL32.glVertexAttribPointer(2, 3, GL32.GL_FLOAT, false, 3 * Float.BYTES, 0); + + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, boxGroup.instanceChunkPosVbo); + GL32.glEnableVertexAttribArray(3); + this.vertexAttribDivisor(3, 1); + GL32.glVertexAttribIPointer(3, 3, GL32.GL_INT, 3 * Integer.BYTES, 0); + + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, boxGroup.instanceSubChunkPosVbo); + GL32.glEnableVertexAttribArray(4); + this.vertexAttribDivisor(4, 1); + GL32.glVertexAttribPointer(4, 3, GL32.GL_FLOAT, false, 3 * Float.BYTES, 0); + + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, boxGroup.instanceMaterialVbo); + GL32.glEnableVertexAttribArray(5); + this.vertexAttribDivisor(5, 1); + GL32.glVertexAttribIPointer(5, 1, GL32.GL_BYTE, Byte.BYTES, 0); + + + // Draw instanced + if (boxGroup.uploadedBoxCount > 0) + { + GL32.glDrawElementsInstanced(GL32.GL_TRIANGLES, BOX_INDICES.length, GL32.GL_UNSIGNED_INT, 0, boxGroup.uploadedBoxCount); + } + + + // Clean up + GL32.glDisableVertexAttribArray(1); + GL32.glDisableVertexAttribArray(2); + GL32.glDisableVertexAttribArray(3); + GL32.glDisableVertexAttribArray(4); + GL32.glDisableVertexAttribArray(5); + } + /** + * Clean way to handle both {@link GL33#glVertexAttribDivisor} and {@link ARBInstancedArrays#glVertexAttribDivisorARB} + * based on which one is supported. + */ + private void vertexAttribDivisor(int index, int divisor) + { + if (this.vertexAttribDivisorSupported) + { + GL33.glVertexAttribDivisor(index, divisor); + } + else if(this.instancedArraysSupported) + { + ARBInstancedArrays.glVertexAttribDivisorARB(index, divisor); + } + else + { + throw new IllegalStateException("Instanced rendering isn't supported by this machine. Direct rendering should have been used instead."); + } + } + + + + + //==================// + // direct rendering // + //==================// + + private void renderBoxGroupDirect(IDhApiGenericObjectShaderProgram shaderProgram, DhApiRenderParam renderEventParam, RenderableBoxGroup boxGroup, Vec3d camPos) + { + DhApiRenderableBoxGroupShading shading = boxGroup.shading; + if (shading == null) + { + shading = DhApiRenderableBoxGroupShading.getUnshaded(); + } + + shaderProgram.fillSharedDirectUniformData(renderEventParam, shading, boxGroup, camPos); + + for (DhApiRenderableBox box : boxGroup) + { + this.renderBox(shaderProgram, renderEventParam, boxGroup, box, camPos); + } + } + private void renderBox( + IDhApiGenericObjectShaderProgram shaderProgram, + DhApiRenderParam renderEventParam, + RenderableBoxGroup boxGroup, DhApiRenderableBox box, + Vec3d camPos) + { + shaderProgram.fillDirectUniformData(renderEventParam, boxGroup, box, camPos); + GL32.glDrawElements(GL32.GL_TRIANGLES, BOX_INDICES.length, GL32.GL_UNSIGNED_INT, 0); + } + + + + //=========// + // getters // + //=========// + + /** @throws IllegalStateException if {@link #init()} function hasn't been called yet */ + public boolean getUseInstancedRendering() throws IllegalStateException + { + if (!this.init) + { + throw new IllegalStateException("GL initialization hasn't been completed."); + } + + return this.useInstancedRendering; + } + + + + //=========// + // F3 menu // + //=========// + + public String getVboRenderDebugMenuString() + { + // get counts + int totalCount = this.boxGroupById.size(); + int activeCount = 0; + for (long key : this.boxGroupById.keySet()) + { + RenderableBoxGroup renderGroup = this.boxGroupById.get(key); + if (renderGroup.active) + { + activeCount++; + } + } + + + String totalCountText = F3Screen.NUMBER_FORMAT.format(totalCount); + String activeCountText = F3Screen.NUMBER_FORMAT.format(activeCount); + return LodUtil.formatLog("Generic Obj Count: " + activeCountText + "/" + totalCountText); + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectShaderProgram.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectShaderProgram.java new file mode 100644 index 000000000..d021d04e0 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectShaderProgram.java @@ -0,0 +1,229 @@ +package com.seibel.distanthorizons.core.render.renderer.generic; + +import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiGenericObjectShaderProgram; +import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3i; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; +import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram; +import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.AbstractVertexAttribute; +import com.seibel.distanthorizons.core.render.glObject.vertexAttribute.VertexPointer; +import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.util.math.Vec3f; + +public class GenericObjectShaderProgram extends ShaderProgram implements IDhApiGenericObjectShaderProgram +{ + public static final String VERTEX_SHADER_INSTANCED_PATH = "shaders/genericObject/instanced/vert.vert"; + public static final String VERTEX_SHADER_DIRECT_PATH = "shaders/genericObject/direct/vert.vert"; + public static final String FRAGMENT_SHADER_INSTANCED_PATH = "shaders/genericObject/instanced/frag.frag"; + public static final String FRAGMENT_SHADER_DIRECT_PATH = "shaders/genericObject/direct/frag.frag"; + + public final AbstractVertexAttribute va; + + + // shader uniforms + private final int directShaderTransformUniform; + private final int directShaderColorUniform; + + private final int instancedShaderOffsetChunkUniform; + private final int instancedShaderOffsetSubChunkUniform; + private final int instancedShaderCameraChunkPosUniform; + private final int instancedShaderCameraSubChunkPosUniform; + private final int instancedShaderProjectionModelViewMatrixUniform; + + private final int lightMapUniform; + private final int skyLightUniform; + private final int blockLightUniform; + + private final int northShadingUniform; + private final int southShadingUniform; + private final int eastShadingUniform; + private final int westShadingUniform; + private final int topShadingUniform; + private final int bottomShadingUniform; + + + + //=============// + // constructor // + //=============// + + public GenericObjectShaderProgram(boolean useInstancedRendering) + { + super( + useInstancedRendering ? VERTEX_SHADER_INSTANCED_PATH : VERTEX_SHADER_DIRECT_PATH, + useInstancedRendering ? FRAGMENT_SHADER_INSTANCED_PATH : FRAGMENT_SHADER_DIRECT_PATH, + "fragColor", new String[]{"vPosition"}); + + this.va = AbstractVertexAttribute.create(); + this.va.bind(); + // Pos + this.va.setVertexAttribute(0, 0, VertexPointer.addVec3Pointer(false)); + this.va.completeAndCheck(Float.BYTES * 3); + + this.directShaderTransformUniform = this.tryGetUniformLocation("uTransform"); + this.directShaderColorUniform = this.tryGetUniformLocation("uColor"); + + this.instancedShaderOffsetChunkUniform = this.tryGetUniformLocation("uOffsetChunk"); + this.instancedShaderOffsetSubChunkUniform = this.tryGetUniformLocation("uOffsetSubChunk"); + this.instancedShaderCameraChunkPosUniform = this.tryGetUniformLocation("uCameraPosChunk"); + this.instancedShaderCameraSubChunkPosUniform = this.tryGetUniformLocation("uCameraPosSubChunk"); + this.instancedShaderProjectionModelViewMatrixUniform = this.tryGetUniformLocation("uProjectionMvm"); + + this.lightMapUniform = this.getUniformLocation("uLightMap"); + this.skyLightUniform = this.getUniformLocation("uSkyLight"); + this.blockLightUniform = this.getUniformLocation("uBlockLight"); + this.northShadingUniform = this.getUniformLocation("uNorthShading"); + this.southShadingUniform = this.getUniformLocation("uSouthShading"); + this.eastShadingUniform = this.getUniformLocation("uEastShading"); + this.westShadingUniform = this.getUniformLocation("uWestShading"); + this.topShadingUniform = this.getUniformLocation("uTopShading"); + this.bottomShadingUniform = this.getUniformLocation("uBottomShading"); + + } + + + + //=========// + // methods // + //=========// + + @Override + public void bind(DhApiRenderParam renderEventParam) + { + super.bind(); + this.va.bind(); + } + @Override + public void unbind() + { + super.unbind(); + this.va.unbind(); + } + + @Override + public void free() + { + this.va.free(); + super.free(); + } + + @Override + public void bindVertexBuffer(int vbo) { this.va.bindBufferToAllBindingPoints(vbo); } + + @Override + public void fillIndirectUniformData( + DhApiRenderParam renderParameters, + DhApiRenderableBoxGroupShading shading, IDhApiRenderableBoxGroup boxGroup, + DhApiVec3d camPos + ) + { + Mat4f projectionMvmMatrix = new Mat4f(renderParameters.dhProjectionMatrix); + projectionMvmMatrix.multiply(renderParameters.dhModelViewMatrix); + + super.bind(); + + + + + this.setUniform(this.instancedShaderOffsetChunkUniform, + new DhApiVec3i( + LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().x), + LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().y), + LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().z) + )); + this.setUniform(this.instancedShaderOffsetSubChunkUniform, + new Vec3f( + LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().x), + LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().y), + LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().z) + )); + + this.setUniform(this.instancedShaderCameraChunkPosUniform, + new DhApiVec3i( + LodUtil.getChunkPosFromDouble(camPos.x), + LodUtil.getChunkPosFromDouble(camPos.y), + LodUtil.getChunkPosFromDouble(camPos.z) + )); + this.setUniform(this.instancedShaderCameraSubChunkPosUniform, + new Vec3f( + LodUtil.getSubChunkPosFromDouble(camPos.x), + LodUtil.getSubChunkPosFromDouble(camPos.y), + LodUtil.getSubChunkPosFromDouble(camPos.z) + )); + + this.setUniform(this.instancedShaderProjectionModelViewMatrixUniform, projectionMvmMatrix); + + this.setUniform(this.lightMapUniform, 0); // TODO this should probably be passed in + this.setUniform(this.skyLightUniform, boxGroup.getSkyLight()); + this.setUniform(this.blockLightUniform, boxGroup.getBlockLight()); + + + this.setUniform(this.northShadingUniform, shading.north); + this.setUniform(this.southShadingUniform, shading.south); + this.setUniform(this.eastShadingUniform, shading.east); + this.setUniform(this.westShadingUniform, shading.west); + this.setUniform(this.topShadingUniform, shading.top); + this.setUniform(this.bottomShadingUniform, shading.bottom); + + + } + + + @Override + public void fillSharedDirectUniformData( + DhApiRenderParam renderParameters, + DhApiRenderableBoxGroupShading shading, IDhApiRenderableBoxGroup boxGroup, + DhApiVec3d camPos) + { + + this.setUniform(this.lightMapUniform, 0); // TODO this should probably be passed in + this.setUniform(this.skyLightUniform, boxGroup.getSkyLight()); + this.setUniform(this.blockLightUniform, boxGroup.getBlockLight()); + + + this.setUniform(this.northShadingUniform, shading.north); + this.setUniform(this.southShadingUniform, shading.south); + this.setUniform(this.eastShadingUniform, shading.east); + this.setUniform(this.westShadingUniform, shading.west); + this.setUniform(this.topShadingUniform, shading.top); + this.setUniform(this.bottomShadingUniform, shading.bottom); + + } + + public void fillDirectUniformData( + DhApiRenderParam renderParameters, + IDhApiRenderableBoxGroup boxGroup, DhApiRenderableBox box, + DhApiVec3d camPos) + { + Mat4f projectionMvmMatrix = new Mat4f(renderParameters.dhProjectionMatrix); + projectionMvmMatrix.multiply(renderParameters.dhModelViewMatrix); + + Mat4f boxTransform = Mat4f.createTranslateMatrix( + (float) (box.minPos.x + boxGroup.getOriginBlockPos().x - camPos.x), + (float) (box.minPos.y + boxGroup.getOriginBlockPos().y - camPos.y), + (float) (box.minPos.z + boxGroup.getOriginBlockPos().z - camPos.z)); + boxTransform.multiply(Mat4f.createScaleMatrix( + (float) (box.maxPos.x - box.minPos.x), + (float) (box.maxPos.y - box.minPos.y), + (float) (box.maxPos.z - box.minPos.z))); + projectionMvmMatrix.multiply(boxTransform); + this.setUniform(this.directShaderTransformUniform, projectionMvmMatrix); + + this.setUniform(this.directShaderColorUniform, box.color); + + } + + + + @Override + public int getId() { return this.id; } + + /** The base DH render program should always render */ + @Override + public boolean overrideThisFrame() { return true; } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericRenderObjectFactory.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericRenderObjectFactory.java new file mode 100644 index 000000000..ca0590957 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericRenderObjectFactory.java @@ -0,0 +1,78 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.render.renderer.generic; + +import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderObjectFactory; +import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister; +import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.util.math.Vec3d; +import com.seibel.distanthorizons.core.util.math.Vec3f; +import org.apache.logging.log4j.Logger; + +import java.util.List; +import java.util.*; + +/** + * Handles creating {@link DhApiRenderableBox}. + * + * @see IDhApiCustomRenderRegister + * @see DhApiRenderableBox + */ +public class GenericRenderObjectFactory implements IDhApiCustomRenderObjectFactory +{ + private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + + public static final GenericRenderObjectFactory INSTANCE = new GenericRenderObjectFactory(); + + + + //=============// + // constructor // + //=============// + + private GenericRenderObjectFactory() { } + + + + //================// + // group creation // + //================// + + @Override + public IDhApiRenderableBoxGroup createForSingleBox(String resourceLocation, DhApiRenderableBox box) + { + ArrayList list = new ArrayList<>(); + list.add(box); + return this.createAbsolutePositionedGroup(resourceLocation, list); + } + + @Override + public IDhApiRenderableBoxGroup createRelativePositionedGroup(String resourceLocation, DhApiVec3d originBlockPos, List boxList) + { return new RenderableBoxGroup(resourceLocation, new DhApiVec3d(originBlockPos.x, originBlockPos.y, originBlockPos.z), boxList, true); } + + @Override + public IDhApiRenderableBoxGroup createAbsolutePositionedGroup(String resourceLocation, List boxList) + { return new RenderableBoxGroup(resourceLocation, new DhApiVec3d(0, 0, 0), boxList, false); } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/RenderableBoxGroup.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/RenderableBoxGroup.java new file mode 100644 index 000000000..e02588c86 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/RenderableBoxGroup.java @@ -0,0 +1,354 @@ +package com.seibel.distanthorizons.core.render.renderer.generic; + +import com.seibel.distanthorizons.api.interfaces.render.IDhApiRenderableBoxGroup; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; +import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; +import com.seibel.distanthorizons.core.render.glObject.GLProxy; +import com.seibel.distanthorizons.core.util.LodUtil; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.opengl.GL32; + +import java.awt.*; +import java.io.Closeable; +import java.util.*; +import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import java.util.function.Predicate; +import java.util.function.UnaryOperator; +import java.util.stream.Stream; + +public class RenderableBoxGroup + extends AbstractList + implements IDhApiRenderableBoxGroup, Closeable + { + public final static AtomicInteger NEXT_ID_ATOMIC_INT = new AtomicInteger(0); + + + + public final long id; + + public final String resourceLocationNamespace; + public final String resourceLocationPath; + + /** If false the boxes will be positioned relative to the level's origin */ + public final boolean positionBoxesRelativeToGroupOrigin; + + private final ArrayList boxList; + + private final DhApiVec3d originBlockPos; + + + public boolean active = true; + public boolean ssaoEnabled = true; + private boolean vertexDataDirty = true; + + public int skyLight = 15; + public int blockLight = 0; + public DhApiRenderableBoxGroupShading shading = DhApiRenderableBoxGroupShading.getDefaultShaded(); + + @Nullable + public Consumer beforeRenderFunc; + public Consumer afterRenderFunc; + + // instance data + public int instanceColorVbo = 0; + public int instanceMaterialVbo = 0; + public int instanceScaleVbo = 0; + public int instanceChunkPosVbo = 0; + public int instanceSubChunkPosVbo = 0; + + public int uploadedBoxCount = -1; + + + + // setters/getters // + + @Override + public long getId() { return this.id; } + + @Override + public String getResourceLocationNamespace() { return this.resourceLocationNamespace; } + @Override + public String getResourceLocationPath() { return this.resourceLocationPath; } + + @Override + public void setOriginBlockPos(DhApiVec3d pos) + { + this.originBlockPos.x = pos.x; + this.originBlockPos.y = pos.y; + this.originBlockPos.z = pos.z; + } + + @Override + public DhApiVec3d getOriginBlockPos() { return new DhApiVec3d(this.originBlockPos.x, this.originBlockPos.y, this.originBlockPos.z); } + + + @Override + public void setSkyLight(int skyLight) + { + if (skyLight < LodUtil.MIN_MC_LIGHT || skyLight > LodUtil.MAX_MC_LIGHT) + { + throw new IllegalArgumentException("Sky light ["+skyLight+"] must be between ["+LodUtil.MIN_MC_LIGHT+"] and ["+LodUtil.MAX_MC_LIGHT+"] (inclusive)."); + } + this.skyLight = skyLight; + } + @Override + public int getSkyLight() { return this.skyLight; } + + @Override + public void setBlockLight(int blockLight) + { + if (blockLight < LodUtil.MIN_MC_LIGHT || blockLight > LodUtil.MAX_MC_LIGHT) + { + throw new IllegalArgumentException("Block light ["+blockLight+"] must be between ["+LodUtil.MIN_MC_LIGHT+"] and ["+LodUtil.MAX_MC_LIGHT+"] (inclusive)."); + } + this.blockLight = blockLight; + } + @Override + public int getBlockLight() { return this.blockLight; } + + + + //=============// + // constructor // + //=============// + + public RenderableBoxGroup( + String resourceLocation, + DhApiVec3d originBlockPos, List boxList, + boolean positionBoxesRelativeToGroupOrigin) throws IllegalArgumentException + { + String[] splitResourceLocation = resourceLocation.split(":"); + if (splitResourceLocation.length != 2) + { + throw new IllegalArgumentException("Resource Location must be a string that's separated by a single colon, for example: [DistantHorizons:Beacons], your namespace ["+resourceLocation+"], contains ["+(splitResourceLocation.length-1)+"] colons."); + } + + this.resourceLocationNamespace = splitResourceLocation[0]; + this.resourceLocationPath = splitResourceLocation[1]; + + this.id = NEXT_ID_ATOMIC_INT.getAndIncrement(); + this.boxList = new ArrayList<>(boxList); + + this.originBlockPos = originBlockPos; + this.positionBoxesRelativeToGroupOrigin = positionBoxesRelativeToGroupOrigin; + } + + + + // methods // + + @Override + public boolean add(DhApiRenderableBox box) { return this.boxList.add(box); } + + @Override + public void setPreRenderFunc(Consumer func) { this.beforeRenderFunc = func; } + + @Override + public void setPostRenderFunc(Consumer func) { this.afterRenderFunc = func; } + + @Override + public void triggerBoxChange() { this.vertexDataDirty = true; } + + @Override + public void setActive(boolean active) { this.active = active; } + @Override + public boolean isActive() { return this.active; } + + @Override + public void setSsaoEnabled(boolean ssaoEnabled) { this.ssaoEnabled = ssaoEnabled; } + @Override + public boolean isSsaoEnabled() { return this.ssaoEnabled; } + + /** + * This is called before every frame, even if {@link this#isActive()} returns false.
+ * {@link this#isActive()} can be changed at this point before the object is rendered to the frame. + */ + public void preRender(DhApiRenderParam renderEventParam) + { + if (this.beforeRenderFunc != null) + { + this.beforeRenderFunc.accept(renderEventParam); + } + } + /** + * Called after rendering is completed.
+ * Can be used to handle any necessary cleanup. + */ + public void postRender(DhApiRenderParam renderEventParam) + { + if (this.afterRenderFunc != null) + { + this.afterRenderFunc.accept(renderEventParam); + } + } + + @Override + public void setShading(DhApiRenderableBoxGroupShading shading) { this.shading = shading; } + @Override + public DhApiRenderableBoxGroupShading getShading() { return this.shading; } + + + + //================// + // List Overrides // + //================// + + @Override + public DhApiRenderableBox get(int index) { return this.boxList.get(index); } + @Override + public int size() { return this.boxList.size(); } + @Override + public boolean removeIf(Predicate filter) { return this.boxList.removeIf(filter); } + @Override + public void replaceAll(UnaryOperator operator) { this.boxList.replaceAll(operator); } + @Override + public void sort(Comparator c) { this.boxList.sort(c); } + @Override + public void forEach(Consumer action) { this.boxList.forEach(action); } + @Override + public Spliterator spliterator() { return this.boxList.spliterator(); } + @Override + public Stream stream() { return this.boxList.stream(); } + @Override + public Stream parallelStream() { return this.boxList.parallelStream(); } + + + + //===================// + // vertex attributes // + //===================// + + /** Does nothing if the vertex data is already up-to-date */ + public void updateVertexAttributeData() + { + if (!this.vertexDataDirty) + { + return; + } + this.vertexDataDirty = false; + + if (this.instanceChunkPosVbo == 0) + { + this.instanceChunkPosVbo = GL32.glGenBuffers(); + this.instanceSubChunkPosVbo = GL32.glGenBuffers(); + this.instanceScaleVbo = GL32.glGenBuffers(); + this.instanceColorVbo = GL32.glGenBuffers(); + this.instanceMaterialVbo = GL32.glGenBuffers(); + } + + int boxCount = this.size(); + this.uploadedBoxCount = boxCount; + + + + // transformation / scaling // + int[] chunkPosData = new int[boxCount * 3]; + float[] subChunkPosData = new float[boxCount * 3]; + float[] scalingData = new float[boxCount * 3]; + for (int i = 0; i < boxCount; i++) + { + DhApiRenderableBox box = this.get(i); + + int dataIndex = i * 3; + + chunkPosData[dataIndex] = LodUtil.getChunkPosFromDouble(box.minPos.x); + chunkPosData[dataIndex + 1] = LodUtil.getChunkPosFromDouble(box.minPos.y); + chunkPosData[dataIndex + 2] = LodUtil.getChunkPosFromDouble(box.minPos.z); + + subChunkPosData[dataIndex] = LodUtil.getSubChunkPosFromDouble(box.minPos.x); + subChunkPosData[dataIndex + 1] = LodUtil.getSubChunkPosFromDouble(box.minPos.y); + subChunkPosData[dataIndex + 2] = LodUtil.getSubChunkPosFromDouble(box.minPos.z); + + scalingData[dataIndex] = (float) (box.maxPos.x - box.minPos.x); + scalingData[dataIndex + 1] = (float) (box.maxPos.y - box.minPos.y); + scalingData[dataIndex + 2] = (float) (box.maxPos.z - box.minPos.z); + + } + + + // colors/materials // + float[] colorData = new float[boxCount * 4]; + byte[] materialData = new byte[boxCount]; + for (int i = 0; i < boxCount; i++) + { + DhApiRenderableBox box = this.get(i); + Color color = box.color; + int colorIndex = i * 4; + colorData[colorIndex] = color.getRed() / 255.0f; + colorData[colorIndex + 1] = color.getGreen() / 255.0f; + colorData[colorIndex + 2] = color.getBlue() / 255.0f; + colorData[colorIndex + 3] = color.getAlpha() / 255.0f; + + materialData[i] = box.material; + } + + + // Upload transformation matrices + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.instanceChunkPosVbo); + GL32.glBufferData(GL32.GL_ARRAY_BUFFER, chunkPosData ,GL32.GL_DYNAMIC_DRAW); + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.instanceSubChunkPosVbo); + GL32.glBufferData(GL32.GL_ARRAY_BUFFER, subChunkPosData ,GL32.GL_DYNAMIC_DRAW); + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.instanceScaleVbo); + GL32.glBufferData(GL32.GL_ARRAY_BUFFER, scalingData, GL32.GL_DYNAMIC_DRAW); + + // Upload colors + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.instanceColorVbo); + GL32.glBufferData(GL32.GL_ARRAY_BUFFER, colorData, GL32.GL_DYNAMIC_DRAW); + + // Upload materials + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, this.instanceMaterialVbo); + GL32.glBufferData(GL32.GL_ARRAY_BUFFER, colorData, GL32.GL_DYNAMIC_DRAW); + } + + + + //================// + // base overrides // + //================// + + @Override + public String toString() { return "ID:["+this.id+"], pos:["+this.originBlockPos.x+","+this.originBlockPos.y+","+this.originBlockPos.z+"], size:["+this.size()+"], active:["+this.active+"]"; } + + @Override + public void close() + { + GLProxy.getInstance().queueRunningOnRenderThread(() -> + { + if (this.instanceChunkPosVbo != 0) + { + GL32.glDeleteBuffers(this.instanceChunkPosVbo); + this.instanceChunkPosVbo = 0; + } + + if (this.instanceSubChunkPosVbo != 0) + { + GL32.glDeleteBuffers(this.instanceSubChunkPosVbo); + this.instanceSubChunkPosVbo = 0; + } + + if (this.instanceScaleVbo != 0) + { + GL32.glDeleteBuffers(this.instanceScaleVbo); + this.instanceScaleVbo = 0; + } + + if (this.instanceColorVbo != 0) + { + GL32.glDeleteBuffers(this.instanceColorVbo); + this.instanceColorVbo = 0; + } + + if (this.instanceMaterialVbo != 0) + { + GL32.glDeleteBuffers(this.instanceMaterialVbo); + this.instanceMaterialVbo = 0; + } + }); + } + + } + \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogApplyShader.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogApplyShader.java new file mode 100644 index 000000000..4906b75b2 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogApplyShader.java @@ -0,0 +1,104 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.render.renderer.shaders; + +import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram; +import com.seibel.distanthorizons.core.render.renderer.FogRenderer; +import com.seibel.distanthorizons.core.render.renderer.LodRenderer; +import com.seibel.distanthorizons.core.render.renderer.ScreenQuad; +import org.lwjgl.opengl.GL32; + +/** + * Draws the Fog texture onto DH's FrameBuffer.

+ * + * See Also:
+ * {@link FogRenderer} - Parent to this shader.
+ * {@link FogShader} - draws the Fog texture.
+ */ +public class FogApplyShader extends AbstractShaderRenderer +{ + public static FogApplyShader INSTANCE = new FogApplyShader(); + + public int fogTexture; + + // uniforms + public int colorTextureUniform; + public int depthTextureUniform; + + + + //=============// + // constructor // + //=============// + + @Override + public void onInit() + { + this.shader = new ShaderProgram( + "shaders/normal.vert", + "shaders/fog/apply.frag", + "fragColor", + new String[]{ "vPosition" }); + + // uniform setup + this.colorTextureUniform = this.shader.getUniformLocation("uColorTexture"); + this.depthTextureUniform = this.shader.getUniformLocation("uDepthTexture"); + + } + + + + //=============// + // render prep // + //=============// + + @Override + protected void onApplyUniforms(float partialTicks) + { + GL32.glActiveTexture(GL32.GL_TEXTURE0); + GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.fogTexture); + GL32.glUniform1i(this.colorTextureUniform, 0); + + GL32.glActiveTexture(GL32.GL_TEXTURE1); + GL32.glBindTexture(GL32.GL_TEXTURE_2D, LodRenderer.getActiveDepthTextureId()); + GL32.glUniform1i(this.depthTextureUniform, 1); + } + + + + //========// + // render // + //========// + + @Override + protected void onRender() + { + GL32.glEnable(GL32.GL_BLEND); + GL32.glBlendEquation(GL32.GL_FUNC_ADD); + GL32.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA); + + // apply the rendered Fog to DH's framebuffer + GL32.glBindFramebuffer(GL32.GL_READ_FRAMEBUFFER, FogShader.INSTANCE.frameBuffer); + GL32.glBindFramebuffer(GL32.GL_DRAW_FRAMEBUFFER, LodRenderer.getActiveFramebufferId()); + + ScreenQuad.INSTANCE.render(); + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java index e5a1fc255..5987af82e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java @@ -31,7 +31,7 @@ import com.seibel.distanthorizons.core.render.renderer.ScreenQuad; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; -import com.seibel.distanthorizons.coreapi.util.math.Mat4f; +import com.seibel.distanthorizons.core.util.math.Mat4f; import org.lwjgl.opengl.GL32; import java.awt.*; @@ -44,24 +44,31 @@ public class FogShader extends AbstractShaderRenderer private static final IVersionConstants VERSION_CONSTANTS = SingletonInjector.INSTANCE.get(IVersionConstants.class); + public int frameBuffer; + private final LodFogConfig fogConfig; private Mat4f inverseMvmProjMatrix; - public int gInvertedModelViewProjectionUniform; - public int gDepthMapUniform; - - // Fog Uniforms - public int fogColorUniform; - public int fogScaleUniform; - public int fogVerticalScaleUniform; - public int nearFogStartUniform; - public int nearFogLengthUniform; - public int fullFogModeUniform; - public FogShader(LodFogConfig fogConfig) - { - this.fogConfig = fogConfig; - } + // Uniforms + public int uFogColor; + public int uFogScale; + public int uFogVerticalScale; + public int uNearFogStart; + public int uNearFogLength; + public int uFullFogMode; + + /** Inverted Model View Projection matrix */ + public int uInvMvmProj; + public int uDepthMap; + + + + //=============// + // constructor // + //=============// + + public FogShader(LodFogConfig fogConfig) { this.fogConfig = fogConfig; } @Override public void onInit() @@ -76,49 +83,47 @@ public class FogShader extends AbstractShaderRenderer // all uniforms should be tryGet... // because disabling fog can cause the GLSL to optimize out most (if not all) uniforms - this.gInvertedModelViewProjectionUniform = this.shader.tryGetUniformLocation("gInvMvmProj"); - this.gDepthMapUniform = this.shader.tryGetUniformLocation("gDepthMap"); + this.uDepthMap = this.shader.getUniformLocation("uDepthMap"); + this.uInvMvmProj = this.shader.getUniformLocation("uInvMvmProj"); // Fog uniforms - this.fogColorUniform = this.shader.tryGetUniformLocation("fogColor"); - this.fullFogModeUniform = this.shader.tryGetUniformLocation("fullFogMode"); - this.fogScaleUniform = this.shader.tryGetUniformLocation("fogScale"); - this.fogVerticalScaleUniform = this.shader.tryGetUniformLocation("fogVerticalScale"); + this.uFogScale = this.shader.tryGetUniformLocation("uFogScale"); + this.uFogVerticalScale = this.shader.tryGetUniformLocation("uFogVerticalScale"); + this.uFogColor = this.shader.tryGetUniformLocation("uFogColor"); + this.uFullFogMode = this.shader.tryGetUniformLocation("uFullFogMode"); // near fog - this.nearFogStartUniform = this.shader.tryGetUniformLocation("nearFogStart"); - this.nearFogLengthUniform = this.shader.tryGetUniformLocation("nearFogLength"); + this.uNearFogStart = this.shader.tryGetUniformLocation("uNearFogStart"); + this.uNearFogLength = this.shader.tryGetUniformLocation("uNearFogLength"); + } + + + //=============// + // render prep // + //=============// + @Override protected void onApplyUniforms(float partialTicks) { - this.shader.setUniform(this.gInvertedModelViewProjectionUniform, this.inverseMvmProjMatrix); - - int lodDrawDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get() * LodUtil.CHUNK_WIDTH; - int vanillaDrawDistance = MC_RENDER.getRenderDistance() * LodUtil.CHUNK_WIDTH; - vanillaDrawDistance += LodUtil.CHUNK_WIDTH * 2; // Give it a 2 chunk boundary for near fog. - - // bind the depth buffer - if (this.gDepthMapUniform != -1) + if (this.inverseMvmProjMatrix != null) { - GL32.glActiveTexture(GL32.GL_TEXTURE1); - GL32.glBindTexture(GL32.GL_TEXTURE_2D, LodRenderer.getActiveDepthTextureId()); - GL32.glUniform1i(this.gDepthMapUniform, 1); + this.shader.setUniform(this.uInvMvmProj, this.inverseMvmProjMatrix); } - // Fog - if (this.fullFogModeUniform != -1) this.shader.setUniform(this.fullFogModeUniform, MC_RENDER.isFogStateSpecial() ? 1 : 0); - if (this.fogColorUniform != -1) this.shader.setUniform(this.fogColorUniform, MC_RENDER.isFogStateSpecial() ? this.getSpecialFogColor(partialTicks) : this.getFogColor(partialTicks)); + int lodDrawDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get() * LodUtil.CHUNK_WIDTH; - float nearFogLen = vanillaDrawDistance * 0.2f / lodDrawDistance; - float nearFogStart = vanillaDrawDistance * (VERSION_CONSTANTS.isVanillaRenderedChunkSquare() ? (float) Math.sqrt(2.0) : 1.0f) / lodDrawDistance; - if (this.nearFogStartUniform != -1) this.shader.setUniform(this.nearFogStartUniform, nearFogStart); - if (this.nearFogLengthUniform != -1) this.shader.setUniform(this.nearFogLengthUniform, nearFogLen); - if (this.fogScaleUniform != -1) this.shader.setUniform(this.fogScaleUniform, 1.f / lodDrawDistance); - if (this.fogVerticalScaleUniform != -1) this.shader.setUniform(this.fogVerticalScaleUniform, 1.f / MC.getWrappedClientLevel().getHeight()); + // Fog + if (this.uFullFogMode != -1) this.shader.setUniform(this.uFullFogMode, MC_RENDER.isFogStateSpecial() ? 1 : 0); + if (this.uFogColor != -1) this.shader.setUniform(this.uFogColor, MC_RENDER.isFogStateSpecial() ? this.getSpecialFogColor(partialTicks) : this.getFogColor(partialTicks)); + + float nearFogStart = (VERSION_CONSTANTS.isVanillaRenderedChunkSquare() ? (float) Math.sqrt(2.0) : 1.0f) / lodDrawDistance; + if (this.uNearFogStart != -1) this.shader.setUniform(this.uNearFogStart, nearFogStart); + if (this.uNearFogLength != -1) this.shader.setUniform(this.uNearFogLength, 0.0f); + if (this.uFogScale != -1) this.shader.setUniform(this.uFogScale, 1.f / lodDrawDistance); + if (this.uFogVerticalScale != -1) this.shader.setUniform(this.uFogVerticalScale, 1.f / MC.getWrappedClientLevel().getMaxHeight()); } - private Color getFogColor(float partialTicks) { Color fogColor; @@ -134,29 +139,34 @@ public class FogShader extends AbstractShaderRenderer return fogColor; } - private Color getSpecialFogColor(float partialTicks) { return MC_RENDER.getSpecialFogColor(partialTicks); } - public void setModelViewProjectionMatrix(Mat4f combinedModelViewProjectionMatrix) + public void setProjectionMatrix(Mat4f projectionMatrix) { - this.inverseMvmProjMatrix = new Mat4f(combinedModelViewProjectionMatrix); + this.inverseMvmProjMatrix = new Mat4f(projectionMatrix); this.inverseMvmProjMatrix.invert(); } + + + //========// + // render // + //========// + @Override protected void onRender() { GLState state = new GLState(); - GL32.glDisable(GL32.GL_DEPTH_TEST); + GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.frameBuffer); GL32.glDisable(GL32.GL_SCISSOR_TEST); - - GL32.glEnable(GL32.GL_BLEND); - GL32.glBlendEquation(GL32.GL_FUNC_ADD); - GL32.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ONE); + GL32.glDisable(GL32.GL_DEPTH_TEST); + GL32.glDisable(GL32.GL_BLEND); GL32.glActiveTexture(GL32.GL_TEXTURE0); - GL32.glBindTexture(GL32.GL_TEXTURE_2D, LodRenderer.getActiveColorTextureId()); + GL32.glBindTexture(GL32.GL_TEXTURE_2D, LodRenderer.getActiveDepthTextureId()); + GL32.glUniform1i(this.uDepthMap, 0); + ScreenQuad.INSTANCE.render(); state.restore(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOApplyShader.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOApplyShader.java index eb871c92b..43da8521f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOApplyShader.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOApplyShader.java @@ -50,6 +50,11 @@ public class SSAOApplyShader extends AbstractShaderRenderer public int gFarUniform; + + //=============// + // constructor // + //=============// + @Override public void onInit() { @@ -68,6 +73,12 @@ public class SSAOApplyShader extends AbstractShaderRenderer this.gFarUniform = this.shader.tryGetUniformLocation("gFar"); } + + + //=============// + // render prep // + //=============// + @Override protected void onApplyUniforms(float partialTicks) { @@ -102,6 +113,7 @@ public class SSAOApplyShader extends AbstractShaderRenderer } + //========// // render // //========// diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOShader.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOShader.java index 867f891e0..05834e159 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOShader.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/SSAOShader.java @@ -24,7 +24,7 @@ import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram; import com.seibel.distanthorizons.core.render.renderer.LodRenderer; import com.seibel.distanthorizons.core.render.renderer.SSAORenderer; import com.seibel.distanthorizons.core.render.renderer.ScreenQuad; -import com.seibel.distanthorizons.coreapi.util.math.Mat4f; +import com.seibel.distanthorizons.core.util.math.Mat4f; import org.lwjgl.opengl.GL32; /** @@ -38,11 +38,13 @@ public class SSAOShader extends AbstractShaderRenderer { public static SSAOShader INSTANCE = new SSAOShader(); + public int frameBuffer; private Mat4f projection; private Mat4f invertedProjection; + // uniforms public int gProjUniform; public int gInvProjUniform; @@ -54,11 +56,17 @@ public class SSAOShader extends AbstractShaderRenderer public int gDepthMapUniform; + + //=============// + // constructor // + //=============// + @Override public void onInit() { this.shader = new ShaderProgram("shaders/normal.vert", "shaders/ssao/ao.frag", - "fragColor", new String[]{"vPosition"}); + "fragColor", new String[]{ "vPosition" } + ); // uniform setup this.gProjUniform = this.shader.getUniformLocation("gProj"); @@ -71,6 +79,12 @@ public class SSAOShader extends AbstractShaderRenderer this.gDepthMapUniform = this.shader.getUniformLocation("gDepthMap"); } + + + //=============// + // render prep // + //=============// + public void setProjectionMatrix(Mat4f projectionMatrix) { this.projection = projectionMatrix; @@ -108,6 +122,7 @@ public class SSAOShader extends AbstractShaderRenderer } + //========// // render // //========// diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/BeaconBeamDTO.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/BeaconBeamDTO.java new file mode 100644 index 000000000..452af3873 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/dto/BeaconBeamDTO.java @@ -0,0 +1,55 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.sql.dto; + +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; +import com.seibel.distanthorizons.core.pos.DhBlockPos; +import com.seibel.distanthorizons.core.pos.DhBlockPos; + +import java.awt.*; + +/** handles storing {@link FullDataSourceV2}'s in the database. */ +public class BeaconBeamDTO implements IBaseDTO +{ + public DhBlockPos pos; + public Color color; + + + + //=============// + // constructor // + //=============// + + public BeaconBeamDTO(DhBlockPos pos, Color color) + { + this.pos = pos; + this.color = color; + } + + + + //===========// + // overrides // + //===========// + + @Override + public DhBlockPos getKey() { return this.pos; } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/AbstractDhRepo.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/AbstractDhRepo.java index 34de259dc..de29b49bb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/AbstractDhRepo.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/AbstractDhRepo.java @@ -426,6 +426,36 @@ public abstract class AbstractDhRepo> implemen } } + /** can be used to make sure everything is closed when the world closes */ + public static void closeAllConnections() + { + LOGGER.info("Closing all ["+ACTIVE_CONNECTION_STRINGS_BY_REPO.size()+"] database connections..."); + for (String connectionString : ACTIVE_CONNECTION_STRINGS_BY_REPO.values()) + { + try + { + Connection connection = CONNECTIONS_BY_CONNECTION_STRING.remove(connectionString); + if (connection != null) + { + if (!connection.isClosed()) + { + LOGGER.info("Closing database connection: [" + connectionString + "]"); + connection.close(); + } + else + { + LOGGER.warn("Attempting to close already closed database connection: [" + connectionString + "]"); + } + } + } + catch(SQLException e) + { + // connection close failed. + LOGGER.error("Unable to close the connection ["+connectionString+"], error: ["+e.getMessage()+"]"); + } + } + } + @Override public void close() { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/BeaconBeamRepo.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/BeaconBeamRepo.java new file mode 100644 index 000000000..9616a79b2 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/BeaconBeamRepo.java @@ -0,0 +1,196 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.sql.repo; + +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.pos.DhBlockPos; +import com.seibel.distanthorizons.core.pos.DhChunkPos; +import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; +import com.seibel.distanthorizons.core.util.LodUtil; +import org.apache.logging.log4j.Logger; + +import java.awt.*; +import java.io.File; +import java.sql.PreparedStatement; +import java.sql.SQLException; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +public class BeaconBeamRepo extends AbstractDhRepo +{ + private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + + + + //=============// + // constructor // + //=============// + + public BeaconBeamRepo(String databaseType, File databaseFile) throws SQLException + { + super(databaseType, databaseFile, BeaconBeamDTO.class); + } + + + + //===========// + // overrides // + //===========// + + @Override + public String getTableName() { return "BeaconBeam"; } + + @Override + public String createWhereStatement(DhBlockPos pos) { return "BlockPosX = "+pos.x+" AND BlockPosY = "+pos.y+" AND BlockPosZ = "+pos.z; } + + + + //=======================// + // repo required methods // + //=======================// + + @Override + public BeaconBeamDTO convertDictionaryToDto(Map objectMap) throws ClassCastException + { + int posX = (Integer) objectMap.get("BlockPosX"); + int posY = (Integer) objectMap.get("BlockPosY"); + int posZ = (Integer) objectMap.get("BlockPosZ"); + + int red = (Integer) objectMap.get("ColorR"); + int green = (Integer) objectMap.get("ColorG"); + int blue = (Integer) objectMap.get("ColorB"); + + + BeaconBeamDTO dto = new BeaconBeamDTO(new DhBlockPos(posX, posY, posZ), new Color(red, green, blue)); + return dto; + } + + @Override + public PreparedStatement createInsertStatement(BeaconBeamDTO dto) throws SQLException + { + String sql = + "INSERT INTO "+this.getTableName() + " (\n" + + " BlockPosX, BlockPosY, BlockPosZ, \n" + + " ColorR, ColorG, ColorB, \n" + + " LastModifiedUnixDateTime, CreatedUnixDateTime) \n" + + "VALUES( \n" + + " ?, ?, ?, \n" + + " ?, ?, ?, \n" + + " ?, ? \n" + + ");"; + PreparedStatement statement = this.createPreparedStatement(sql); + + int i = 1; + statement.setObject(i++, dto.pos.x); + statement.setObject(i++, dto.pos.y); + statement.setObject(i++, dto.pos.z); + + statement.setObject(i++, dto.color.getRed()); + statement.setObject(i++, dto.color.getGreen()); + statement.setObject(i++, dto.color.getBlue()); + + statement.setObject(i++, System.currentTimeMillis()); // last modified unix time + statement.setObject(i++, System.currentTimeMillis()); // created unix time + + return statement; + } + + @Override + public PreparedStatement createUpdateStatement(BeaconBeamDTO dto) throws SQLException + { + String sql = + "UPDATE "+this.getTableName()+" \n" + + "SET \n" + + " ColorR = ?, ColorG = ?, ColorB = ?, \n" + + " LastModifiedUnixDateTime = ? \n" + + "WHERE BlockPosX = ? AND BlockPosY = ? AND BlockPosZ = ?"; + PreparedStatement statement = this.createPreparedStatement(sql); + + int i = 1; + statement.setObject(i++, dto.color.getRed()); + statement.setObject(i++, dto.color.getGreen()); + statement.setObject(i++, dto.color.getBlue()); + + statement.setObject(i++, System.currentTimeMillis()); // last modified unix time + + statement.setObject(i++, dto.pos.x); + statement.setObject(i++, dto.pos.y); + statement.setObject(i++, dto.pos.z); + + return statement; + } + + + + //====================// + // additional methods // + //====================// + + public List getAllBeamsForPos(DhChunkPos chunkPos) + { + int minBlockX = chunkPos.getMinBlockX(); + int minBlockZ = chunkPos.getMinBlockZ(); + int maxBlockX = minBlockX + LodUtil.CHUNK_WIDTH; + int maxBlockZ = minBlockZ + LodUtil.CHUNK_WIDTH; + + return this.getAllBeamsInBlockPosRange( + minBlockX, minBlockZ, + maxBlockX, maxBlockZ + ); + } + + public List getAllBeamsForPos(long pos) + { + int minBlockX = DhSectionPos.getMinCornerBlockX(pos); + int minBlockZ = DhSectionPos.getMinCornerBlockZ(pos); + int maxBlockX = minBlockX + DhSectionPos.getBlockWidth(pos); + int maxBlockZ = minBlockZ + DhSectionPos.getBlockWidth(pos); + + return this.getAllBeamsInBlockPosRange( + minBlockX, minBlockZ, + maxBlockX, maxBlockZ + ); + } + + public List getAllBeamsInBlockPosRange( + int minBlockX, int minBlockZ, + int maxBlockX, int maxBlockZ + ) + { + List> objectMapList = this.queryDictionary( + "SELECT * " + + "FROM "+this.getTableName()+" " + + "WHERE " + + minBlockX+" <= BlockPosX AND BlockPosX <= "+maxBlockX+" AND " + + minBlockZ+" <= BlockPosZ AND BlockPosZ <= "+maxBlockZ); + + ArrayList beamList = new ArrayList<>(); + for (Map objectMap : objectMapList) + { + beamList.add(this.convertDictionaryToDto(objectMap)); + } + + return beamList; + } + + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/ColorUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/ColorUtil.java index 222fcbb9b..6e4b3acc9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/ColorUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/ColorUtil.java @@ -19,6 +19,8 @@ package com.seibel.distanthorizons.core.util; +import java.awt.*; + /** * Handles the bit-wise math used when * dealing with colors stored as integers. @@ -212,4 +214,7 @@ public class ColorUtil ",B:" + getBlue(color); } + public static Color toColorObjRGB(int color) { return new Color(getRed(color), getGreen(color), getBlue(color)); } + public static Color toColorObjRGBA(int color) { return new Color(getRed(color), getGreen(color), getBlue(color), getAlpha(color)); } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/LodUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/LodUtil.java index 7b43cf6a8..ad9de0b90 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/LodUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/LodUtil.java @@ -19,7 +19,9 @@ package com.seibel.distanthorizons.core.util; +import java.util.Arrays; import java.util.Iterator; +import java.util.List; import java.util.concurrent.CancellationException; import java.util.concurrent.CompletionException; import java.util.concurrent.RejectedExecutionException; @@ -33,6 +35,7 @@ import com.seibel.distanthorizons.core.render.vertexFormat.DefaultLodVertexForma import com.seibel.distanthorizons.core.render.vertexFormat.LodVertexFormat; import com.seibel.distanthorizons.core.util.gridList.EdgeDistanceBooleanGrid; import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException; +import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWrapper; @@ -66,9 +69,6 @@ public class LodUtil - /** The maximum number of LODs that can be rendered vertically */ - public static final int MAX_NUMBER_OF_VERTICAL_LODS = 32; - /** * alpha used when drawing chunks in debug mode */ @@ -123,6 +123,23 @@ public class LodUtil /** lowest possible light level handled by Minecraft */ public static final byte MIN_MC_LIGHT = 0; + /** the opacity value returned by {@link IBlockStateWrapper#getOpacity()} if a block is fully transparent */ + public static final int BLOCK_FULLY_TRANSPARENT = 0; + /** the opacity value returned by {@link IBlockStateWrapper#getOpacity()} if a block is fully opaque */ + public static final int BLOCK_FULLY_OPAQUE = 16; + + /** + * List of every block that can be used in a beacon's base.
+ * Should be all lowercase + */ + public static final List BEACON_BASE_BLOCK_NAME_LIST = Arrays.asList( + "iron_block", + "gold_block", + "diamond_block", + "emerald_block", + "netherite_block" + ); + /** @@ -236,6 +253,15 @@ public class LodUtil renderDist * 2 + 1); } + /** Returns the chunk int position for the given double position */ + public static int getChunkPosFromDouble(double value) { return (int) Math.floor(value / CHUNK_WIDTH); } + /** Returns the float position inside the chunk for the given double position */ + public static float getSubChunkPosFromDouble(double value) + { + double chunkPos = Math.floor(value / CHUNK_WIDTH); + return (float) (value - chunkPos * CHUNK_WIDTH); + } + // True if the requested threshold pass, or false otherwise // For details, see: diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/RenderDataPointUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/RenderDataPointUtil.java index 72bb1864f..757aec211 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/RenderDataPointUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/RenderDataPointUtil.java @@ -245,8 +245,8 @@ public class RenderDataPointUtil " Y-:" + getYMin(dataPoint) + " argb:" + getAlpha(dataPoint) + " " + getRed(dataPoint) + " " + - getBlue(dataPoint) + " " + - getGreen(dataPoint) + + getGreen(dataPoint) + " " + + getBlue(dataPoint) + " BL:" + getLightBlock(dataPoint) + " SL:" + getLightSky(dataPoint) + " BID:" + getBlockMaterialId(dataPoint); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/RenderUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/RenderUtil.java index e1d6c7ed3..4888f5673 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/RenderUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/RenderUtil.java @@ -30,8 +30,8 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftCli import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.coreapi.util.MathUtil; -import com.seibel.distanthorizons.coreapi.util.math.Mat4f; -import com.seibel.distanthorizons.coreapi.util.math.Vec3f; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.util.math.Vec3f; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; /** @@ -244,7 +244,7 @@ public class RenderUtil // if the player is a significant distance above the work, increase the // near clip plane to fix Z imprecision issues int playerHeight = MC.getPlayerBlockPos().y; - int levelMaxHeight = level.getHeight(); + int levelMaxHeight = level.getMaxHeight(); if (playerHeight > levelMaxHeight + 1_000) { return playerHeight - (levelMaxHeight + 1000); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/math/Mat4f.java b/core/src/main/java/com/seibel/distanthorizons/core/util/math/Mat4f.java new file mode 100644 index 000000000..27c8bbb97 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/math/Mat4f.java @@ -0,0 +1,249 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.util.math; + +import com.seibel.distanthorizons.api.objects.math.DhApiMat4f; +import org.joml.Matrix4f; +import org.joml.Matrix4fc; + +import java.nio.FloatBuffer; + +/** + * An (almost) exact copy of Minecraft's 1.16.5 + * implementation of a 4x4 float matrix. + * + * @author James Seibel + * @version 11-11-2021 + */ +public class Mat4f extends DhApiMat4f +{ + + //==============// + // constructors // + //==============// + + public Mat4f() { /* all values are 0 */ } + + public Mat4f(DhApiMat4f sourceMatrix) { super(sourceMatrix); } + + /** Expects the values of the input buffer to be in row major order (AKA rows then columns) */ + public Mat4f(FloatBuffer buffer) { this(buffer.array()); } + /** Expects the values of the input array to be in row major order (AKA rows then columns) */ + public Mat4f(float[] values) { super(values); } + + public Mat4f(Matrix4fc sourceMatrix) { this(convertJomlMatrixToArray(sourceMatrix)); } + private static float[] convertJomlMatrixToArray(Matrix4fc sourceMatrix) + { + FloatBuffer buffer = FloatBuffer.allocate(16); + + buffer.put(bufferIndex(0, 0), sourceMatrix.m00()); + buffer.put(bufferIndex(0, 1), sourceMatrix.m01()); + buffer.put(bufferIndex(0, 2), sourceMatrix.m02()); + buffer.put(bufferIndex(0, 3), sourceMatrix.m03()); + + buffer.put(bufferIndex(1, 0), sourceMatrix.m10()); + buffer.put(bufferIndex(1, 1), sourceMatrix.m11()); + buffer.put(bufferIndex(1, 2), sourceMatrix.m12()); + buffer.put(bufferIndex(1, 3), sourceMatrix.m13()); + + buffer.put(bufferIndex(2, 0), sourceMatrix.m20()); + buffer.put(bufferIndex(2, 1), sourceMatrix.m21()); + buffer.put(bufferIndex(2, 2), sourceMatrix.m22()); + buffer.put(bufferIndex(2, 3), sourceMatrix.m23()); + + buffer.put(bufferIndex(3, 0), sourceMatrix.m30()); + buffer.put(bufferIndex(3, 1), sourceMatrix.m31()); + buffer.put(bufferIndex(3, 2), sourceMatrix.m32()); + buffer.put(bufferIndex(3, 3), sourceMatrix.m33()); + + return buffer.array(); + } + private static int bufferIndex(int xIndex, int zIndex) { return (zIndex * 4) + xIndex; } + + + public void store(FloatBuffer floatBuffer) + { + floatBuffer.put(bufferIndex(0, 0), this.m00); + floatBuffer.put(bufferIndex(0, 1), this.m01); + floatBuffer.put(bufferIndex(0, 2), this.m02); + floatBuffer.put(bufferIndex(0, 3), this.m03); + floatBuffer.put(bufferIndex(1, 0), this.m10); + floatBuffer.put(bufferIndex(1, 1), this.m11); + floatBuffer.put(bufferIndex(1, 2), this.m12); + floatBuffer.put(bufferIndex(1, 3), this.m13); + floatBuffer.put(bufferIndex(2, 0), this.m20); + floatBuffer.put(bufferIndex(2, 1), this.m21); + floatBuffer.put(bufferIndex(2, 2), this.m22); + floatBuffer.put(bufferIndex(2, 3), this.m23); + floatBuffer.put(bufferIndex(3, 0), this.m30); + floatBuffer.put(bufferIndex(3, 1), this.m31); + floatBuffer.put(bufferIndex(3, 2), this.m32); + floatBuffer.put(bufferIndex(3, 3), this.m33); + } + + + public static Matrix4f createJomlMatrix(DhApiMat4f matrix) + { + return new Matrix4f( + matrix.m00, matrix.m10, matrix.m20, matrix.m30, + matrix.m01, matrix.m11, matrix.m21, matrix.m31, + matrix.m02, matrix.m12, matrix.m22, matrix.m32, + matrix.m03, matrix.m13, matrix.m23, matrix.m33 + ); + } + public Matrix4f createJomlMatrix() + { + return new Matrix4f( + this.m00, this.m10, this.m20, this.m30, + this.m01, this.m11, this.m21, this.m31, + this.m02, this.m12, this.m22, this.m32, + this.m03, this.m13, this.m23, this.m33 + ); + } + + + + //=========// + // methods // + //=========// + + public static Mat4f perspective(double fov, float widthHeightRatio, float nearClipPlane, float farClipPlane) + { + float f = (float) (1.0D / Math.tan(fov * ((float) Math.PI / 180F) / 2.0D)); + Mat4f matrix = new Mat4f(); + matrix.m00 = f / widthHeightRatio; + matrix.m11 = f; + matrix.m22 = (farClipPlane + nearClipPlane) / (nearClipPlane - farClipPlane); + matrix.m32 = -1.0F; + matrix.m23 = 2.0F * farClipPlane * nearClipPlane / (nearClipPlane - farClipPlane); + return matrix; + } + + /** originally "translate" from Minecraft's MatrixStack */ + public void multiplyTranslationMatrix(double x, double y, double z) + { multiply(createTranslateMatrix((float) x, (float) y, (float) z)); } + + public static Mat4f createScaleMatrix(float x, float y, float z) + { + Mat4f matrix = new Mat4f(); + matrix.m00 = x; + matrix.m11 = y; + matrix.m22 = z; + matrix.m33 = 1.0F; + return matrix; + } + + public static Mat4f createTranslateMatrix(float x, float y, float z) + { + Mat4f matrix = new Mat4f(); + matrix.m00 = 1.0F; + matrix.m11 = 1.0F; + matrix.m22 = 1.0F; + matrix.m33 = 1.0F; + matrix.m03 = x; + matrix.m13 = y; + matrix.m23 = z; + return matrix; + } + + + + //===============// + // Forge methods // + //===============// + + public void set(DhApiMat4f mat) + { + this.m00 = mat.m00; + this.m01 = mat.m01; + this.m02 = mat.m02; + this.m03 = mat.m03; + this.m10 = mat.m10; + this.m11 = mat.m11; + this.m12 = mat.m12; + this.m13 = mat.m13; + this.m20 = mat.m20; + this.m21 = mat.m21; + this.m22 = mat.m22; + this.m23 = mat.m23; + this.m30 = mat.m30; + this.m31 = mat.m31; + this.m32 = mat.m32; + this.m33 = mat.m33; + } + + public void add(DhApiMat4f other) + { + m00 += other.m00; + m01 += other.m01; + m02 += other.m02; + m03 += other.m03; + m10 += other.m10; + m11 += other.m11; + m12 += other.m12; + m13 += other.m13; + m20 += other.m20; + m21 += other.m21; + m22 += other.m22; + m23 += other.m23; + m30 += other.m30; + m31 += other.m31; + m32 += other.m32; + m33 += other.m33; + } + + public void multiplyBackward(DhApiMat4f other) + { + DhApiMat4f copy = other.copy(); + copy.multiply(this); + this.set(copy); + } + + public void setTranslation(float x, float y, float z) + { + this.m00 = 1.0F; + this.m11 = 1.0F; + this.m22 = 1.0F; + this.m33 = 1.0F; + this.m03 = x; + this.m13 = y; + this.m23 = z; + } + + /** + * Changes the values that store the clipping planes. + * Formula for calculating matrix values is the same that OpenGL uses when making matrices. + * + * @param nearClip New near clipping plane value. + * @param farClip New far clipping plane value. + */ + public void setClipPlanes(float nearClip, float farClip) + { + //convert to matrix values, formula copied from a textbook / openGL specification. + float matNearClip = -((farClip + nearClip) / (farClip - nearClip)); + float matFarClip = -((2 * farClip * nearClip) / (farClip - nearClip)); + //set new values for the clip planes. + this.m22 = matNearClip; + this.m23 = matFarClip; + } + + public Mat4f copy() { return new Mat4f(this); } + +} diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Vec3d.java b/core/src/main/java/com/seibel/distanthorizons/core/util/math/Vec3d.java similarity index 50% rename from api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Vec3d.java rename to core/src/main/java/com/seibel/distanthorizons/core/util/math/Vec3d.java index a1374d8de..57911612f 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Vec3d.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/math/Vec3d.java @@ -17,8 +17,9 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.coreapi.util.math; +package com.seibel.distanthorizons.core.util.math; +import com.seibel.distanthorizons.api.objects.math.DhApiVec3d; import com.seibel.distanthorizons.coreapi.util.MathUtil; /** @@ -29,7 +30,7 @@ import com.seibel.distanthorizons.coreapi.util.MathUtil; * @author James Seibel * @version 11-18-2021 */ -public class Vec3d +public class Vec3d extends DhApiVec3d { public static Vec3d XNeg = new Vec3d(-1.0F, 0.0F, 0.0F); public static Vec3d XPos = new Vec3d(1.0F, 0.0F, 0.0F); @@ -40,16 +41,13 @@ public class Vec3d public static final Vec3d ZERO_VECTOR = new Vec3d(0.0D, 0.0D, 0.0D); - public double x; - public double y; - public double z; + //==============// + // constructors // + //==============// - public Vec3d() - { - - } + public Vec3d() { } public Vec3d(double x, double y, double z) { @@ -58,47 +56,11 @@ public class Vec3d this.z = z; } - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - else if (obj != null && this.getClass() == obj.getClass()) - { - Vec3d Vec3f = (Vec3d) obj; - if (Double.compare(Vec3f.x, this.x) != 0) - { - return false; - } - else if (Double.compare(Vec3f.y, this.y) != 0) - { - return false; - } - else - { - return Double.compare(Vec3f.z, this.z) == 0; - } - } - else - { - return false; - } - } - @Override - public int hashCode() - { - long longVal = Double.doubleToLongBits(this.x); - - int intVal = (int) (longVal ^ longVal >>> 32); - longVal = Double.doubleToLongBits(this.y); - intVal = 31 * intVal + (int) (longVal ^ longVal >>> 32); - longVal = Double.doubleToLongBits(this.z); - - return 31 * intVal + (int) (longVal ^ longVal >>> 32); - } + + //=========// + // methods // + //=========// public void mul(double scalar) { @@ -149,10 +111,7 @@ public class Vec3d this.z -= vector.z; } - public double dotProduct(Vec3d vector) - { - return this.x * vector.x + this.y * vector.y + this.z * vector.z; - } + public double dotProduct(Vec3d vector) { return this.x * vector.x + this.y * vector.y + this.z * vector.z; } public Vec3d normalize() { @@ -173,80 +132,10 @@ public class Vec3d this.z = f * f4 - f1 * f3; } - /* Matrix3f is not currently needed/implemented - public void transform(Matrix3f p_229188_1_) - { - double f = this.x; - double f1 = this.y; - double f2 = this.z; - this.x = p_229188_1_.m00 * f + p_229188_1_.m01 * f1 + p_229188_1_.m02 * f2; - this.y = p_229188_1_.m10 * f + p_229188_1_.m11 * f1 + p_229188_1_.m12 * f2; - this.z = p_229188_1_.m20 * f + p_229188_1_.m21 * f1 + p_229188_1_.m22 * f2; - } - */ - - /* Quaternions are not currently needed/implemented - public void transform(Quaternion p_214905_1_) - { - Quaternion quaternion = new Quaternion(p_214905_1_); - quaternion.mul(new Quaternion(this.x(), this.y(), this.z(), 0.0F)); - Quaternion quaternion1 = new Quaternion(p_214905_1_); - quaternion1.conj(); - quaternion.mul(quaternion1); - this.set(quaternion.i(), quaternion.j(), quaternion.k()); - } - */ - - /* not currently needed - * percent may actually be partial ticks (which is available when rendering) - public void linearInterp(Vec3f resultingVector, double percent) - { - double f = 1.0F - percent; - this.x = this.x * f + resultingVector.x * percent; - this.y = this.y * f + resultingVector.y * percent; - this.z = this.z * f + resultingVector.z * percent; - } - */ - - /* Quaternions are not currently needed/implemented - public Quaternion rotation(double p_229193_1_) - { - return new Quaternion(this, p_229193_1_, false); - } - - - @OnlyIn(Dist.CLIENT) - public Quaternion rotationDegrees(double p_229187_1_) - { - return new Quaternion(this, p_229187_1_, true); - } - */ - - public Vec3d copy() - { - return new Vec3d(this.x, this.y, this.z); - } - - /* not currently needed/implemented - public void map(double2doubleFunction p_229191_1_) - { - this.x = p_229191_1_.get(this.x); - this.y = p_229191_1_.get(this.y); - this.z = p_229191_1_.get(this.z); - } - */ - - @Override - public String toString() - { - return "[" + this.x + ", " + this.y + ", " + this.z + "]"; - } + public Vec3d copy() { return new Vec3d(this.x, this.y, this.z); } // Forge start - public Vec3d(double[] values) - { - set(values); - } + public Vec3d(double[] values) { this.set(values); } public void set(double[] values) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/math/Vec3f.java b/core/src/main/java/com/seibel/distanthorizons/core/util/math/Vec3f.java new file mode 100644 index 000000000..11f4b8f46 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/math/Vec3f.java @@ -0,0 +1,173 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.util.math; + +import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; +import com.seibel.distanthorizons.core.pos.DhBlockPos2D; +import com.seibel.distanthorizons.coreapi.util.MathUtil; + +/** + * A (almost) exact copy of Minecraft's 1.16.5 + * implementation of a 3 element float vector. + * + * @author James Seibel + * @version 11-11-2021 + */ +public class Vec3f extends DhApiVec3f +{ + //==============// + // constructors // + //==============// + + public Vec3f() { this(0,0,0); } + + public Vec3f(float x, float y, float z) + { + this.x = x; + this.y = y; + this.z = z; + } + + public Vec3f(DhApiVec3f pos) + { + this.x = pos.x; + this.y = pos.y; + this.z = pos.z; + } + + public Vec3f(Vec3d pos) + { + this.x = (float) pos.x; + this.y = (float) pos.y; + this.z = (float) pos.z; + } + + + + + //==============// + // math methods // + //==============// + + public void mul(float scalar) + { + this.x *= scalar; + this.y *= scalar; + this.z *= scalar; + } + + public void mul(float x, float y, float z) + { + this.x *= x; + this.y *= y; + this.z *= z; + } + + public void clamp(float min, float max) + { + this.x = MathUtil.clamp(min, this.x, max); + this.y = MathUtil.clamp(min, this.y, max); + this.z = MathUtil.clamp(min, this.z, max); + } + + public void add(float x, float y, float z) + { + this.x += x; + this.y += y; + this.z += z; + } + + public void add(Vec3f vector) + { + this.x += vector.x; + this.y += vector.y; + this.z += vector.z; + } + + public void subtract(Vec3f vector) + { + this.x -= vector.x; + this.y -= vector.y; + this.z -= vector.z; + } + + public float dotProduct(Vec3f vector) { return this.x * vector.x + this.y * vector.y + this.z * vector.z; } + + /** @return true if normalization had to be done */ + public boolean normalize() + { + float squaredSum = this.x * this.x + this.y * this.y + this.z * this.z; + if (squaredSum < 1.0E-5D) + { + return false; + } + else + { + float f1 = MathUtil.fastInvSqrt(squaredSum); + this.x *= f1; + this.y *= f1; + this.z *= f1; + return true; + } + } + + public void crossProduct(Vec3f vector) + { + float f = this.x; + float f1 = this.y; + float f2 = this.z; + float f3 = vector.x; + float f4 = vector.y; + float f5 = vector.z; + this.x = f1 * f5 - f2 * f4; + this.y = f2 * f3 - f * f5; + this.z = f * f4 - f1 * f3; + } + + public static float getManhattanDistance(DhApiVec3f a, DhApiVec3f b) + { + return Math.abs(a.x - b.x) + + Math.abs(a.y - b.y) + + Math.abs(a.z - b.z); + } + + public static double getDistance(DhApiVec3f a, DhApiVec3f b) + { + return Math.sqrt(Math.pow(a.x - b.x, 2) + + Math.pow(a.y - b.y, 2) + + Math.pow(a.z - b.z, 2)); + } + + + + //==============// + // misc methods // + //==============// + + public void set(float x, float y, float z) + { + this.x = x; + this.y = y; + this.z = z; + } + + public Vec3f copy() { return new Vec3f(this.x, this.y, this.z); } + +} diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Vec3i.java b/core/src/main/java/com/seibel/distanthorizons/core/util/math/Vec3i.java similarity index 98% rename from api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Vec3i.java rename to core/src/main/java/com/seibel/distanthorizons/core/util/math/Vec3i.java index c85fdea51..bd4d609e1 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/util/math/Vec3i.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/math/Vec3i.java @@ -17,7 +17,7 @@ * along with this program. If not, see . */ -package com.seibel.distanthorizons.coreapi.util.math; +package com.seibel.distanthorizons.core.util.math; import com.seibel.distanthorizons.coreapi.util.MathUtil; import com.seibel.distanthorizons.api.objects.math.DhApiVec3i; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhApiWorldProxy.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhApiWorldProxy.java index 88893d9ac..477dae008 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhApiWorldProxy.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhApiWorldProxy.java @@ -50,10 +50,18 @@ public class DhApiWorldProxy implements IDhApiWorldProxy + //=============// + // constructor // + //=============// + private DhApiWorldProxy() { } + //=========// + // methods // + //=========// + @Override public boolean worldLoaded() { return SharedApi.getAbstractDhWorld() != null; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java index 1471cfabb..0fdaf3c7a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java @@ -98,6 +98,7 @@ public class DhClientServerWorld extends AbstractDhWorld implements IDhClientWor } level.startRenderer(clientLevelWrapper); + clientLevelWrapper.setParentLevel(level); return level; }); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java index 139949cc4..427310ece 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java @@ -82,6 +82,17 @@ public interface IWrapperFactory extends IDhApiWrapperFactory, IBindable * Generally this contains blocks like: air, barriers, light blocks, etc. */ HashSet getRendererIgnoredBlocks(ILevelWrapper levelWrapper); + /** + * Returns the set of {@link IBlockStateWrapper}'s that shouldn't be rendered in caves.
+ * Generally this contains blocks like: air, rails, glow lichen, etc. + */ + HashSet getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper); + + /** clears the cached values */ + void resetRendererIgnoredCaveBlocks(); + /** clears the cached values */ + void resetRendererIgnoredBlocksSet(); + /** * Specifically designed to be used with the API. diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java index 8f8b11ce2..36ad21767 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java @@ -20,44 +20,13 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.block; import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper; +import com.seibel.distanthorizons.core.util.LodUtil; + +import java.awt.*; /** A Minecraft version independent way of handling Blocks. */ public interface IBlockStateWrapper extends IDhApiBlockStateWrapper { - //===========// - // constants // - //===========// - - int FULLY_TRANSPARENT = 0; - int FULLY_OPAQUE = 16; - - /** contains the indices used by Iris to determine how different block types should be rendered */ - class IrisBlockMaterial - { - public static final byte UNKOWN = 0; - public static final byte LEAVES = 1; - public static final byte STONE = 2; - public static final byte WOOD = 3; - public static final byte METAL = 4; - public static final byte DIRT = 5; - public static final byte LAVA = 6; - public static final byte DEEPSLATE = 7; - public static final byte SNOW = 8; - public static final byte SAND = 9; - public static final byte TERRACOTTA = 10; - public static final byte NETHER_STONE = 11; - public static final byte WATER = 12; - public static final byte GRASS = 13; - - /** shouldn't normally be needed, but just in case */ - public static final byte AIR = 14; - public static final byte ILLUMINATED = 15; // Max value - } - - - - - //=========// // methods // //=========// @@ -68,13 +37,19 @@ public interface IBlockStateWrapper extends IDhApiBlockStateWrapper * Returning a value of 0 means the block is completely transparent. getAllActiveBeacons(ArrayList neighbourChunkList) + { + ArrayList beaconBeamList = new ArrayList<>(); + + AdjacentChunkHolder adjacentChunkHolder = new AdjacentChunkHolder(this, neighbourChunkList); + + // since beacons emit light we can check only the positions that are emitting light + ArrayList blockPosList = this.getBlockLightPosList(); + for (int i = 0; i < blockPosList.size(); i++) + { + DhBlockPos pos = blockPosList.get(i); + DhBlockPos relPos = pos.convertToChunkRelativePos(); + + + IBlockStateWrapper block = this.getBlockState(relPos); + if (block.isBeaconBlock()) + { + Color beaconColor = getBeaconColor(pos, adjacentChunkHolder); + if (beaconColor != null) + { + BeaconBeamDTO beam = new BeaconBeamDTO(blockPosList.get(i), beaconColor); + beaconBeamList.add(beam); + } + } + } + + return beaconBeamList; + } + /** @return Null if the position isn't valid for a beacon beam. */ + @Nullable + static Color getBeaconColor(DhBlockPos beaconPos, AdjacentChunkHolder chunkHolder) + { + DhBlockPos beaconRelPos = beaconPos.convertToChunkRelativePos(); + DhBlockPos baseRelPos = new DhBlockPos(0, beaconRelPos.y-1, 0); + + + + //===========================// + // check for the base blocks // + //===========================// + + for (int x = -1; x<= 1; x++) + { + for (int z = -1; z <= 1; z++) + { + baseRelPos.x = beaconRelPos.x + x; + baseRelPos.z = beaconRelPos.z + z; + baseRelPos.mutateToChunkRelativePos(baseRelPos); + + IChunkWrapper chunk = chunkHolder.getByBlockPos(beaconPos.x + x, beaconPos.z + z); + if (chunk != null) + { + IBlockStateWrapper block = chunk.getBlockState(baseRelPos.x, baseRelPos.y, baseRelPos.z); + if (!block.isBeaconBaseBlock()) + { + return null; + } + } + } + } + + + + //=========================// + // get the beacon color // + // and check for occlusion // + //=========================// + + int red = 0; + int green = 0; + int blue = 0; + boolean glassBlockFound = false; + + IChunkWrapper centerChunk = chunkHolder.getByBlockPos(beaconPos.x, beaconPos.z); + int maxY = centerChunk.getMaxNonEmptyHeight(); + for (int y = beaconRelPos.y+1; y <= maxY; y++) + { + IBlockStateWrapper block = centerChunk.getBlockState(beaconRelPos.x, y, beaconRelPos.z); + if (!block.isAir() && block.getOpacity() == LodUtil.BLOCK_FULLY_OPAQUE) + { + return null; + } + + if (block.isGlassBlock() + // ignore invisible blocks (which have pure black as their map color, luckily black stained-glass is actually extremely dark gray) + && !block.getMapColor().equals(Color.BLACK)) + { + red += block.getMapColor().getRed(); + green += block.getMapColor().getGreen(); + blue += block.getMapColor().getBlue(); + + if (glassBlockFound) + { + red /= 2; + green /= 2; + blue /= 2; + } + glassBlockFound = true; + } + } + + return glassBlockFound ? new Color(red, green, blue) : Color.WHITE; + } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftClientWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftClientWrapper.java index bedacffc2..a4190044d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftClientWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftClientWrapper.java @@ -62,6 +62,8 @@ public interface IMinecraftClientWrapper extends IBindable boolean hasSinglePlayerServer(); boolean clientConnectedToDedicatedServer(); + /** for use with the Replay mod */ + boolean connectedToReplay(); String getCurrentServerName(); String getCurrentServerIp(); @@ -90,10 +92,6 @@ public interface IMinecraftClientWrapper extends IBindable */ IClientLevelWrapper getWrappedClientLevel(boolean bypassMultiverse); - /** Please move over to getInstallationDirectory() within the IMinecraftSharedWrapper */ - @Deprecated - File getGameDirectory(); - IProfilerWrapper getProfiler(); /** Returns all worlds available to the server */ diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftRenderWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftRenderWrapper.java index 42d355520..2fc860772 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftRenderWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftRenderWrapper.java @@ -28,9 +28,9 @@ import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable; -import com.seibel.distanthorizons.coreapi.util.math.Mat4f; -import com.seibel.distanthorizons.coreapi.util.math.Vec3d; -import com.seibel.distanthorizons.coreapi.util.math.Vec3f; +import com.seibel.distanthorizons.core.util.math.Mat4f; +import com.seibel.distanthorizons.core.util.math.Vec3d; +import com.seibel.distanthorizons.core.util.math.Vec3f; import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants; import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java index f33d53354..4d2dd13cf 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java @@ -21,7 +21,7 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.misc; import com.seibel.distanthorizons.api.interfaces.IDhApiUnsafeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; -import com.seibel.distanthorizons.coreapi.util.math.Vec3d; +import com.seibel.distanthorizons.core.util.math.Vec3d; import java.net.SocketAddress; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IClientLevelWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IClientLevelWrapper.java index 7cdd14bbf..c22c4a413 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IClientLevelWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IClientLevelWrapper.java @@ -19,6 +19,8 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.world; +import com.seibel.distanthorizons.core.level.IDhClientLevel; +import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import org.jetbrains.annotations.Nullable; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java index d9cea7b95..aed5804a6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java @@ -20,6 +20,7 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.world; import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper; +import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; @@ -47,8 +48,7 @@ public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable boolean hasSkyLight(); @Override - int getHeight(); - + int getMaxHeight(); @Override default int getMinHeight() { return 0; } @@ -65,4 +65,8 @@ public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable /** Fired when the level is being unloaded. Doesn't unload the level. */ void onUnload(); + // TODO I don't like the circular reference, can we merge the level wrapper and DhLevels? + @Deprecated + void setParentLevel(IDhLevel parentLevel); + } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java index b4ae28dc8..a9a762709 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java @@ -26,9 +26,6 @@ import static com.seibel.distanthorizons.core.config.Config.Client.Advanced.Mult public interface IServerLevelWrapper extends ILevelWrapper { - @Nullable - IClientLevelWrapper tryGetClientLevelWrapper(); - File getSaveFolder(); default String getKeyedLevelDimensionName() diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index 83b75513a..31598ea31 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -89,7 +89,7 @@ "distanthorizons.config.client.advanced.graphics.quality.maxHorizontalResolution.@tooltip": "The maximum detail LODs are rendered at.\n\n§6Fastest:§r Chunk\n§6Fanciest:§r Block", "distanthorizons.config.client.advanced.graphics.quality.lodChunkRenderDistanceRadius": - "LOD Render Distance Radius", + "LOD Chunk Render Distance Radius", "distanthorizons.config.client.advanced.graphics.quality.lodChunkRenderDistanceRadius.@tooltip": "Distant Horizons' render distance, measured in chunks. \n\nNote: this is a best effort number. \nThe render distance may be above or below this number \ndepending on your other graphic settings.", "distanthorizons.config.client.advanced.graphics.quality.verticalQuality": @@ -309,6 +309,23 @@ "Grass Side Rendering", "distanthorizons.config.client.advanced.graphics.advancedGraphics.grassSideRendering.@tooltip": "How should the sides and bottom of grass block LODs render?", + + + "distanthorizons.config.client.advanced.graphics.genericRendering": + "Generic Rendering", + + "distanthorizons.config.client.advanced.graphics.genericRendering.enableRendering": + "Enable Rendering", + "distanthorizons.config.client.advanced.graphics.genericRendering.enableRendering.@tooltip": + "If true non terrain objects will be rendered in DH's terrain. \nThis includes beacon beams and clouds.", + "distanthorizons.config.client.advanced.graphics.genericRendering.enableBeaconRendering": + "Enable Beacon Rendering", + "distanthorizons.config.client.advanced.graphics.genericRendering.enableBeaconRendering.@tooltip": + "If true LOD beacon beams will be rendered.", + "distanthorizons.config.client.advanced.graphics.genericRendering.enableCloudRendering": + "Enable Cloud Rendering", + "distanthorizons.config.client.advanced.graphics.genericRendering.enableCloudRendering.@tooltip": + "If true LOD clouds will be rendered.", "distanthorizons.config.client.advanced.worldGenerator": @@ -348,6 +365,10 @@ "Only Use DH Lighting Engine", "distanthorizons.config.client.advanced.lodBuilding.onlyUseDhLightingEngine.@tooltip": "If false LODs will be lit by Minecraft's lighting engine when possible \nand fall back to the DH lighting engine only when necessary. \n\nIf true LODs will only be lit using Distant Horizons' lighting engine. \n\nGenerally it is best to leave this disabled and should only be enabled \nif there are lighting issues or for debugging.", + "distanthorizons.config.client.advanced.lodBuilding.pullLightingForPregeneratedChunks": + "Pull Lighting For Pre-generated Chunks", + "distanthorizons.config.client.advanced.lodBuilding.pullLightingForPregeneratedChunks.@tooltip": + "If true LOD generation for pre-existing chunks will attempt to pull the lighting data \nsaved in Minecraft's Region files. \nIf false DH will pull in chunks without lighting and re-light them. \n\nSetting this to true will result in faster LOD generation \nfor already generated worlds, but is broken by most lighting mods. \n\nSet this to false if LODs are black.", "distanthorizons.config.client.advanced.lodBuilding.dataCompression": "Data Compression", "distanthorizons.config.client.advanced.lodBuilding.dataCompression.@tooltip": @@ -358,7 +379,14 @@ "How should block data be compressed when creating LOD data? \nThis setting will only affect new or updated LOD data, \nany data already generated when this setting is changed will be \nunaffected until it is modified or re-loaded. \n\nMost Accurate: Merge Same Blocks \nHighest Compression: Visually Equal", "distanthorizons.config.client.advanced.lodBuilding.showMigrationChatWarning": "Log Migration In Chat", - + "distanthorizons.config.client.advanced.lodBuilding.ignoredRenderBlockCsv": + "Ignored Block CSV", + "distanthorizons.config.client.advanced.lodBuilding.ignoredRenderBlockCsv.@tooltip": + "A comma separated list of block resource locations that won't be rendered by DH. \nNote: air is always included in this list.", + "distanthorizons.config.client.advanced.lodBuilding.ignoredRenderCaveBlockCsv": + "Ignored Cave Block CSV", + "distanthorizons.config.client.advanced.lodBuilding.ignoredRenderCaveBlockCsv.@tooltip": + "A comma separated list of block resource locations that shouldn't be rendered \nif they are in a 0 sky light underground area. \nNote: air is always included in this list.", "distanthorizons.config.client.advanced.multiplayer": "Multiplayer", @@ -392,7 +420,7 @@ "distanthorizons.config.client.advanced.multiplayer.serverFolderNameMode": "Server Folder Mode", "distanthorizons.config.client.advanced.multiplayer.serverFolderNameMode.@tooltip": - "Determines the folder format for local multiplayer data.\n\n§6Name Only:§r\nUses the server browser name. Ex: \"Minecraft Server\"\n§6Name IP:§r\n\"Minecraft Server, IP 192.168.1.40\"\n§6Name, IP, Port:§r\n\"Minecraft Server, IP 192.168.1.40:25565\"\n§6Name, IP, Port, MC Version:§r\n\"Minecraft Server, IP 192.168.1.40:25565, GameVersion 1.18.1\"\n\n§c§lCaution:§r changing while connected to a multiplayer server may cause glitches.", + "Determines the folder format for local multiplayer data.\n\n§6Name Only:§r\nUses the server browser name. Ex: \"Minecraft Server\"\n§6IP Only:§r\n\"192.168.1.40\"\n§6Name IP:§r\n\"Minecraft Server, IP 192.168.1.40\"\n§6Name, IP, Port:§r\n\"Minecraft Server, IP 192.168.1.40:25565\"\n§6Name, IP, Port, MC Version:§r\n\"Minecraft Server, IP 192.168.1.40:25565, GameVersion 1.18.1\"\n\n§c§lCaution:§r changing while connected to a multiplayer server may cause glitches.", "distanthorizons.config.client.advanced.multiplayer.multiverseSimilarityRequiredPercent": "Multiverse Required Similarity %", "distanthorizons.config.client.advanced.multiplayer.multiverseSimilarityRequiredPercent.@tooltip": @@ -474,7 +502,11 @@ "distanthorizons.config.client.advanced.debugging.showOverlappingQuadErrors": "Show overlapping quad errors", "distanthorizons.config.client.advanced.debugging.showOverlappingQuadErrors.@tooltip": - "f true overlapping quads will be rendered as bright red for easy identification. \nIf false the quads will be rendered normally.", + "If true overlapping quads will be rendered as bright red for easy identification. \nIf false the quads will be rendered normally.", + "distanthorizons.config.client.advanced.debugging.logBufferGarbageCollection": + "Log Buffer Garbage Collection", + "distanthorizons.config.client.advanced.debugging.logBufferGarbageCollection.@tooltip": + "If true OpenGL Buffer garbage collection will be logged \nthis also includes the number of live buffers. ", "distanthorizons.config.client.advanced.debugging.allowUnsafeValues": "Allow Unsafe UI Values", "distanthorizons.config.client.advanced.debugging.allowUnsafeValues.@tooltip": @@ -547,6 +579,8 @@ "Network Events", "distanthorizons.config.client.advanced.logging.showLowMemoryWarningOnStartup": "Show Low Memory Warning", + "distanthorizons.config.client.advanced.logging.showReplayWarningOnStartup": + "Show Replay Warning", diff --git a/core/src/main/resources/assets/distanthorizons/textures/clouds - original.png b/core/src/main/resources/assets/distanthorizons/textures/clouds - original.png new file mode 100644 index 0000000000000000000000000000000000000000..825f4814e3fab7899db0e991087484a908873d5f GIT binary patch literal 6214 zcmV-M7`f+(P)Px#1ZP1_K>z@;j|==^1poj522e~?MF0Q*|NsA`*`M72000SaNLh0L01m?j~gZYoErBeyQB=cj_p-0I=rLVQ)}o zX;6sM?D;ZSxmyPL8$XLp<>oVCn8;hlxgU}{YnO7-aJ30Pm&!L>086(&v>*ZaTTf2i z1WW_;x2ojbp#vBqFaZ7H{i6U}4m=>X-|>M`YbH}SX^ zabDL|LB5VokLw92$f8Yu10&lKPTR-LAJj=I00NoF6#!)IU|mX`zAnTf0H&Kojqi^P zo~64l0jJ+={zqwg%;;kjr0 zVm@IVRo_rz8+n0MzzP6+b9E?iL@_)fLSMSVNQv-^{c7~E7)f;mr?C5KvQJiiX5RYE4Y~7NwGfkD z(=8-W%v*mU3Gw_}0B7Qe*RU(*{3vO09*%yfMrp#ojrS1~Y0S8UF4vLSodmr5JhwU3 zF*K1lbNlN@dkQ-`DsVU<(}KX5&sESk{~E573K^_MCAx9^AR~8`e%2WIMwt*Y`35&^ zqy`|(3#UynNOYkJRjxixl7=CqyJ^WMDyj~EluN{A9h^5u_^g=-L0iiY^2COhD7GX; zg^xdfQvy_7d+keHmsnnMa*hk z08L&@b&LxDH(Up>5OD8o+!|R?>TZ7ZII|Zq3iC&rrZ_CNU-$H@UnnI8%W(GL-D(&b z`^7osCJEP$=|r+#YNB7SL+Zn{z$NXjtTk7f>{pI#v8HZGE%L7cafSjfA!Pp0=Oaxm z_0~J-@UYgMv`9WzG7ayF^glC?)&ZyiKrm10bi<*G39oZvFxB_c3H1pLFKz` z+oh9&vW{PxNJm}fdBHbk5I;1>1NDL(fs{Oz#cBG()m|f?OlDFw4r)=x0l33b2Y(>Y zU_%B#$fqbPW#zLH7$(QlB!b21+fRonRsd=j!u`CF*eNH6&c%b~IwZSx+o*OaB1i&P zybiJjM71#=P4u!x6vXr=+PogxCYDHbvd50KU9L1SR+)fSd}?X@0bnJ7XdUvl(bHTe zor>a5_DJT0)m7R5XH1SljMhOg*NgH@QHNNn6lyC=xK#}zYY^*8HSWa(N>)w@X!E-M zA**Aem)lbp8Y@^AvYN3&oa3by<@Y&9iJ*_VJbKoWY)m0Y;vWa;!ZFCt&$1)KZXSRO zca4Z+XthwXkIOHEA8T0^z!aweSY0%8r7wvUu&(AnM0TrK>lQ{MxHNq3~1wP0KZG0G(Ir%h1M>Nyfv_PIJ|b5_eFwQtyTbSk_G!s04nwx z3zHEV(JW=5bUU(#YJ!mx+O+9zQL4K=);kOr+&%#mK0yd7h|)KYjBAHM=F2i2NZoKD zSJ(isZ{zh~z&`O?+QWGidfDVyiV)TXDS*XF)?706D(00O18i)u<4HJMdUf)Y_k9_)9QKcxVHV!ybkmz3gMQfI_ZouVwS6(1TR&KO3*5 zQMvyr0iGMQtks=$eDx;NNW02W+Ljj<is)bzMZqxsuudC`9hv(WF1+8(z0iaH}N9 zHZk_C2CRMetilPHSKwx8YXPv;#?c6K9iVkqbz}w)r0&LO5vuX0`e7}y-0Q|u0?-*m z_))>`SyAms*l!`q3IO>Q@N&4&FRDQRbe=9lb&f@kDpuTBx$S%~AbuHsO zcTF`w0kE54No`Lm8kaPufL<3+NNZV61hcWf+oqhXjT$27d28AsT#9Qg6x6-+?3OQ^ zkB5HpeiTO%7BhpKAU)noxTu-{rrZ?P{hd=M!OKLIyeuj5iJG!^7~e~7FLy#MZa}Lj z$34e^UBc|!{uPq*w%q=4x6)xVgHwB3c2OEaEhDk;WJ-FBn2G(-lAn3vv2GHAOqN{Js{+d8?CCPJa3PK&vm)pN@Q<9 zw{-O+LOb^!CtdoVsjNo;xF7vVxhzNMnCC6VpV(v5Aie<^7d;7#dTQM!S^?A@n)>G>osf?h6%p%3mpmU=)9~h-x6n?Xk5B~Ry|7gee2Fb$qbc%; zlb8>2d8J$3eja}mT+Z$}%WOIW7H|Sf(khkWJZ`senVvLByCntA2QL;E%_e?+tD2O` z$RTL|?uZ}){0qgkVw?ZB*5+8#uUOP3rLK`@$*ATv z(GQti8(JOO^6eNxcg~P)2c(C$AqG7o!JGl$%?Swta!+?Op#ac&yPfK3zGNYH!t`|w zbX8eG#JXb2s8kVMo{_(>kA&vlGWcyiB7sy$Wf2Hm09Xdd5l3!5a|rSRK%rW55#IHN z03h-vaqk}9D7Ca>XLtJOhhy-#KFgjdZ8(kgWSmt(a?3FiDN${KwRXb}GbD9lD#u8u z2i}_z#19#SN|LR4v|&8i@aBFP^0@_N4{BuRcQ5BtJIBCraP9oHDvLjgbvom_1R$Rd zGHIBQMc28fjHJ%fn9mp++yrz63qy?Np>gPkx*$Coht1KQ@FFumdWghJ*i1)`knon! zTEsTslzSBlQss4a5oC!V;>{oEG3Qhn}eSq$Xv7`jEGT8GV zXb|7%g#$hOoKYdv)tfPd#Q*Q7I`Uy1@Hy{AU!*i`2HwXa!re*Q8lpRI&sr^ z<3bEPG$N?c5ghXv4`ryia^OpV(HZQYIRBf!1WVG=93%;g5TfbL<az5f3v!Kgl7-%s9IgXB;hF&<6xS_vHcG7vyvRj_siUH@|rN z6Y@#7v7>qaG$qehNO_uzK=~zs@6yO~JK82iOkNDE)&cuxY@>?!DLq+g!`d@2sM;8j zyf~~i!Mj*S{-h?R$Qq}NKx%#0idMWBGjGpb-%^SV00sae)Wl8!*iM($9;XH9a5Eex zzx@q>7X|F$bPs&;^HGOK99i0Y0pR;wKM|C~4GLB10+da+Q=LW9s5sLf>q`qEa9{ms zY~w+B01e4 z*n4Fvk4ho&ZvFP&2mPWS;iHTlEhwZYz)R9A3uC*}uK?J1+>3o$>4q&+r`mxCsEP8Z zXYz))GY`oWk&s?!fj%V=*;3UxCrH%l?MTUh?$n3mz`_-Q_=22}v|R}C*H%x2a?K=* zD6D4H0-fJ&4Ls18yDw{r6i4A{0`S*9nK{!9K-HllNx2X0Nf~w0$)r>UwO2TyGjAH* z0l->u&ht=@yteR;OCK^R&L@p|J~VV>?+FZ7{7^@@7JG%2F4imOGO>PF$7i-E zYeTQD=p=bL@IqY%-+ZkIR(}1}PEC=Z`d15!u|iq_+GBsu)|~NkOQ};KQCtvf>fgI` z_-!{&;=on01HcPBYh!1yh+wiT*R^s1<(%sEbhzK$s>U~_$in=nA44}uc~R|W4Hi>6>CXa1B`FD@vq{D@gZaV25d0?YTR-T;$t;<+t&vk- zE!i4J^`Csma-Y0}te4?KWfI4)YMF9AGz{EdT@vr4xDaZId0Z3}m$$I=7Er82r_UdA z+6>SMMV5f#%ABwUL*vLMRBNitrhf_tYL}GKHB!pl1kj5;T;||==(C)Dy2C2~WorGE z8jn?&+hW-T>|QLq0)rL%=Q$2*pu_gC?}g{(=>-I8t3J~K+lQ2I>Z#MwY7eqfW<-w$ zg4i4fC;dIFuS&`O@~y);34oOJRN|>aG57&ln`mNT&2THzAyZsCJ+T4c@Mm3(^h@bB zzgvU*`0GcK7t$NJze6W==6skJ1nwj>C&kNq^Hu=*lg~Sqcn2WeYRi{5%y__p&$+Jw zkUehHSBVsGN;Vi&eUhG?YngMu)LdVO7w*vCA(R>%6>);i%k?u0*>#HW2mt$IRnb5L zqVk=S+KfVke#i?fQwRK@0jB+;YwbUJw5m@i7DL{=Xrmz0e#8`Y)un#uYe+i@On#@3u7b(^VJkOQi|r3Euv#R zeIvrnu^O*&h%-@|ML|sG6?LbPVs~T!cGV4@=bdpxL{(wzjd0c?;#nI6+vEv)wQ0Kn zKxpr?%y|Xays$a5`WJeS+P7p_ClR{53P@AHye%Bl@zo?6FS`P`J~0pzHkQHIDnGd! zdm$B4pOn@lwX3Nnxc&<9pq7sheWS7*Im(X-WH*n8HRx>ihYn+nc1tFw9Ctqt0O-fkHuGM_*9p|ON|%hYzAxy;6FatpF2qa$kWX_ z6xof`iasz-2%ei?0q}fr6rl3ncmsN=V)}PWl+!-Bcy@dneS;|{eS{drvv+Nj7pCuL z&?Fe&C4d_MSzWEFXyB}QN9kj8^*IH@*Jl}&_Ch7$Y>&$%iWY-4LbqyX%=-FIByh^8 zY9C5QG)-wOc(*640McLAeJ|Fc#d1Y;Qdt>6OK9$UW9_443HN>doI6SDoc5tqy?gd^ zl7D`$UTXI@0#v%{-D_abIE>uOEV}i_J@JH~o+)?{t#z6}*d7$&L{%jYX8>rZX-h0c zj|)_eGZC=FJAEFDA{vJ3%ByXVOOeJ>Y&oG=$0n)S4FL8pFl+!AO=6s6BDF%R)ybim zl4Yum&kr^L$A({ywOG~C9T7t4BWC+I0I}A*^(Ko$nc(u0)WwIjvV{8Yt7*1n_tHA8 zmrp-7+Vo_`4A6wZ^enfq6PJ)*F7a-7YBYHS%t_OlpRweK@?hBd0&?W964+K;)Vy_> zuO3`yhaM`g$=N}7(#zoDIAXIbo3`||`auHMaJn!CPcf5sxljN}B;^|AD_8mD%{HR^ z#+<|*HS8t7M|>;b>OWJ1P1dybrWHQYf@}I)ByAJO=t;>9a156p83}G?0T*>$@^XpZ)+Z z{cKv+^#=gBapew*36{rR050JIK5Hw7kZd%xV@Y6SV; zVbgLWtO_KWEt8;~RhXq+oM0t%AdD0IsYM(uYqx1z8ZPhN;yjP#at{w0;P5TG$&5(z z#4}R41M5$Ge;_BiBnLpRO~oG`JCv5prF=jL7XIzM*k z?;qiyz7h)nLtp{ETPtbtS9Ed}eC&sHPNM&YGTT7C?+ie1;I{CceK%DuQ&}$DmVTEW z@_bD-cLsZ7wAyWp`4TsZpXYxL0Mgof3Mao_mRR*+HKhwEIpQigT{<6O zwL^6@BdthMA${8FB=l@tach~r7;I4i$p0bE9(W{a-Nx`>yCxDrC?AF!YcE1=ydCwK zPfcLiic>|AE&#C9$Y1Aj7 z&SNT@knU4!X2h=*Pb)LUPx#1ZP1_K>z@;j|==^1poj522e~?MF0Q*|NsA`*`M72000SaNLh0L01m+b01m+c zxRGn^000JbNklm|!Z{X8PS!as$9&;GKf0rme**}XApF1rSYoCB0<4Xy2H4#T&er=p4*)2J0et?0 zY-L=QEYsOg4840{m>z_T{x`{0I#BV$uZPm6G=RUcfbtagE8X%rrgpiu@IPe0D!%~G z6P)_LX+h3cDnC{Z48VMg{;5VbM-p+9TsUU~KD71Ik~Z27%uI>Xz`6oUd4; z#1~^|%QUD5jb>iT4ZuRyCVPCvvB?M^mBmTMpiFtdu$vF6f`V=_L3my$oi1~07%7KE za`=29X)tBG0{UE#+^3(@Xt~H$UM?Z~UVo6{qZZedvl9S^XfAi8kDbP3p$9*F&0?a( znMnYC1U!qeYKK_XE%Sb6yh0ZH7BcV9e^W!tNaaw(Ai)pgW{4l=#E9_S_93}ZgG@E; zi_xtxAXLEFqM1Ih`bOcg`)>HSXTl`I_p1*InvezsY@s&+PGu}TYpkaG(zK=Kd#Dl# zK=cA&j$bU3cVTf5d1RIy>(aD#%yd&h63|}gVtl&mezm=_8wB7ID_0*<- zPZlD6-2iqaAwk%Q;AyG_>hi>WtDHus>Et3@KE5-~eMZ?KhHB|jA+Kef5mso|oXDzP z>W-qt^XNIdzZj^-Qda?&+)7u2(K==NrsiPk&8!vIXU(T`s2V!NGvZLgDyR6;#WEd~ zY2}ewa9?R1*t8l1e}yL>rp(3S^NMzUC&EI&{yqvz%qF|n2AklEX-A|U7K8T*nqh)_ z56INi{J66GE2{T5{1pHL03CMHJ65&a2Jo}IiZk8Bl?97p_x(ppbphxd z&LsE%sQrv206)aG)3l#pnN`xq5|7x5@-P~GJ~Fa0)rWMOPj&UmG=Ulm+Al~coZ}kX zWPc^l;O8}+&QFRVGOh`WvKMy^SwLeyUj4uK(;I;84Z=fXLM}jIf}bS{2L)ke$bo81 zdYx+AAsE@<2zhFY=C>Q|#g%AB9q%e_G+xZUo}Z?w>Bja+9msG+DC;s@EmepzsUVvK zNV5#N_9QuO1Lf=yE`>5-6nTKBMdU7lWv|}diVxV7f-+iw z5kwCFWRDVqPtjU1cm2_j9_m?=}-cpYF%jB_aFkFKmP0S%Qp1&?O24t7j1 z&I(_~99(cB~%A4yl&D4M)`N4`a_RVworD{GjMQ>m=@y00000NkvXXu0mjfIJ!ks literal 0 HcmV?d00001 diff --git a/core/src/main/resources/shaders/curve.vert b/core/src/main/resources/shaders/curve.vert index 03876a40c..8b8c96755 100644 --- a/core/src/main/resources/shaders/curve.vert +++ b/core/src/main/resources/shaders/curve.vert @@ -8,17 +8,17 @@ out vec4 vertexColor; out vec3 vertexWorldPos; out float vertexYPos; -uniform bool whiteWorld; +uniform bool uWhiteWorld; -uniform mat4 combinedMatrix; -uniform vec3 modelOffset; -uniform float worldYOffset; +uniform mat4 uCombinedMatrix; +uniform vec3 uModelOffset; +uniform float uWorldYOffset; -uniform int worldSkyLight; -uniform sampler2D lightMap; -uniform float mircoOffset; +uniform int uWorldSkyLight; +uniform sampler2D uLightMap; +uniform float uMircoOffset; -uniform float earthRadius; +uniform float uEarthRadius; /** * TODO in the future this and standard.vert should be merged together to prevent inconsistencies between the two @@ -35,9 +35,9 @@ void main() { vPos = vPosition; // This is so it can be passed to the fragment shader - vertexWorldPos = vPosition.xyz + modelOffset; + vertexWorldPos = vPosition.xyz + uModelOffset; - vertexYPos = vPosition.y + worldYOffset; + vertexYPos = vPosition.y + uWorldYOffset; uint meta = vPosition.a; @@ -46,11 +46,11 @@ void main() // 0b01 = positive offset // 0b11 = negative offset // format is: 0b00zzyyxx - float mx = (mirco & 1u) != 0u ? mircoOffset : 0.0; + float mx = (mirco & 1u) != 0u ? uMircoOffset : 0.0; mx = (mirco & 2u) != 0u ? -mx : mx; - float my = (mirco & 4u) != 0u ? mircoOffset : 0.0; + float my = (mirco & 4u) != 0u ? uMircoOffset : 0.0; my = (mirco & 8u) != 0u ? -my : my; - float mz = (mirco & 16u) != 0u ? mircoOffset : 0.0; + float mz = (mirco & 16u) != 0u ? uMircoOffset : 0.0; mz = (mirco & 32u) != 0u ? -mz : mz; vertexWorldPos.x += mx; vertexWorldPos.y += my; @@ -58,7 +58,7 @@ void main() // vertex transformation logic - stduhpf - float localRadius = earthRadius + vertexYPos; + float localRadius = uEarthRadius + vertexYPos; float phi = length(vertexWorldPos.xz) / localRadius; vertexWorldPos.y += (cos(phi) - 1.0) * localRadius; vertexWorldPos.xz = vertexWorldPos.xz * sin(phi) / phi; @@ -68,12 +68,12 @@ void main() float light2 = (mod(float(lights), 16.0) + 0.5) / 16.0; float light = (float(lights / 16u) + 0.5) / 16.0; - vertexColor = vec4(texture(lightMap, vec2(light, light2)).xyz, 1.0); + vertexColor = vec4(texture(uLightMap, vec2(light, light2)).xyz, 1.0); - if (!whiteWorld) + if (!uWhiteWorld) { vertexColor *= color; } - gl_Position = combinedMatrix * vec4(vertexWorldPos, 1.0); + gl_Position = uCombinedMatrix * vec4(vertexWorldPos, 1.0); } diff --git a/core/src/main/resources/shaders/debug/frag.frag b/core/src/main/resources/shaders/debug/frag.frag index 1f22d751c..16f8de33f 100644 --- a/core/src/main/resources/shaders/debug/frag.frag +++ b/core/src/main/resources/shaders/debug/frag.frag @@ -1,6 +1,7 @@ #version 150 core uniform vec4 uColor; + out vec4 fragColor; void main() diff --git a/core/src/main/resources/shaders/debug/vert.vert b/core/src/main/resources/shaders/debug/vert.vert index e32ec6e80..befc43200 100644 --- a/core/src/main/resources/shaders/debug/vert.vert +++ b/core/src/main/resources/shaders/debug/vert.vert @@ -1,10 +1,10 @@ #version 150 core -uniform mat4 transform; +uniform mat4 uTransform; in vec3 vPosition; void main() { - gl_Position = transform * vec4(vPosition, 1.0); + gl_Position = uTransform * vec4(vPosition, 1.0); } \ No newline at end of file diff --git a/core/src/main/resources/shaders/flat_shaded.frag b/core/src/main/resources/shaders/flat_shaded.frag index e6d7a9407..0bfe32461 100644 --- a/core/src/main/resources/shaders/flat_shaded.frag +++ b/core/src/main/resources/shaders/flat_shaded.frag @@ -9,13 +9,13 @@ out vec4 fragColor; // Fog/Clip Uniforms -uniform float clipDistance = 0.0; +uniform float uClipDistance = 0.0; // Noise Uniforms -uniform bool noiseEnabled; -uniform int noiseSteps; -uniform float noiseIntensity; -uniform int noiseDropoff; +uniform bool uNoiseEnabled; +uniform int uNoiseSteps; +uniform float uNoiseIntensity; +uniform int uNoiseDropoff; // The random functions for diffrent dimentions @@ -38,13 +38,13 @@ void applyNoise(inout vec4 fragColor, const in float viewDist) // This bit of code is required to fix the vertex position problem cus of floats in the verted world position varuable vec3 fixedVPos = vPos.xyz + vertexNormal * 0.001; - float noiseAmplification = noiseIntensity * 0.01; + float noiseAmplification = uNoiseIntensity * 0.01; float lum = (fragColor.r + fragColor.g + fragColor.b) / 3.0; noiseAmplification = (1.0 - pow(lum * 2.0 - 1.0, 2.0)) * noiseAmplification; // Lessen the effect on depending on how dark the object is, equasion for this is -(2x-1)^{2}+1 noiseAmplification *= fragColor.a; // The effect would lessen on transparent objects // Random value for each position - float randomValue = rand(quantize(fixedVPos, noiseSteps)) + float randomValue = rand(quantize(fixedVPos, uNoiseSteps)) * 2.0 * noiseAmplification - noiseAmplification; // Modifies the color @@ -52,8 +52,8 @@ void applyNoise(inout vec4 fragColor, const in float viewDist) vec3 newCol = fragColor.rgb + (1.0 - fragColor.rgb) * randomValue; newCol = clamp(newCol, 0.0, 1.0); - if (noiseDropoff != 0) { - float distF = min(viewDist / noiseDropoff, 1.0); + if (uNoiseDropoff != 0) { + float distF = min(viewDist / uNoiseDropoff, 1.0); newCol = mix(newCol, fragColor.rgb, distF); // The further away it gets, the less noise gets applied } @@ -67,12 +67,12 @@ void main() fragColor = vertexColor; float viewDist = length(vertexWorldPos); - if (viewDist < clipDistance && clipDistance > 0.0) + if (viewDist < uClipDistance && uClipDistance > 0.0) { discard; } - if (noiseEnabled) + if (uNoiseEnabled) { applyNoise(fragColor, viewDist); } diff --git a/core/src/main/resources/shaders/fog/apply.frag b/core/src/main/resources/shaders/fog/apply.frag new file mode 100644 index 000000000..7241d1161 --- /dev/null +++ b/core/src/main/resources/shaders/fog/apply.frag @@ -0,0 +1,24 @@ +#version 150 core + +in vec2 TexCoord; + +out vec4 fragColor; + +uniform sampler2D uColorTexture; +uniform sampler2D uDepthTexture; + + + +void main() +{ + fragColor = vec4(1.0); + + float fragmentDepth = textureLod(uDepthTexture, TexCoord, 0).r; + + // a fragment depth of "1" means the fragment wasn't drawn to, + // only update fragments that were drawn to + if (fragmentDepth != 1) + { + fragColor = texture(uColorTexture, TexCoord); + } +} diff --git a/core/src/main/resources/shaders/fog/fog.frag b/core/src/main/resources/shaders/fog/fog.frag index 04eb3f7c1..eed4c6b6b 100644 --- a/core/src/main/resources/shaders/fog/fog.frag +++ b/core/src/main/resources/shaders/fog/fog.frag @@ -3,17 +3,17 @@ in vec2 TexCoord; out vec4 fragColor; -uniform sampler2D gDepthMap; +uniform sampler2D uDepthMap; // inverted model view matrix and projection matrix -uniform mat4 gInvMvmProj; +uniform mat4 uInvMvmProj; -uniform float fogScale; -uniform float fogVerticalScale; -uniform float nearFogStart; -uniform float nearFogLength; -uniform int fullFogMode; +uniform float uFogScale; +uniform float uFogVerticalScale; +uniform vec4 uFogColor; +uniform int uFullFogMode; -uniform vec4 fogColor; +uniform float uNearFogStart; +uniform float uNearFogLength; /* ========MARCO DEFINED BY RUNTIME CODE GEN========= @@ -53,7 +53,7 @@ vec3 calcViewPosition(float fragmentDepth) { vec4 ndc = vec4(TexCoord.xy, fragmentDepth, 1.0); ndc.xyz = ndc.xyz * 2.0 - 1.0; - vec4 eyeCoord = gInvMvmProj * ndc; + vec4 eyeCoord = uInvMvmProj * ndc; return eyeCoord.xyz / eyeCoord.w; } @@ -66,19 +66,19 @@ vec3 calcViewPosition(float fragmentDepth) { void main() { float vertexYPos = 100.0f; - float fragmentDepth = texture(gDepthMap, TexCoord).r; - fragColor = vec4(fogColor.rgb, 0.0); + float fragmentDepth = texture(uDepthMap, TexCoord).r; + fragColor = vec4(uFogColor.rgb, 0.0); // a fragment depth of "1" means the fragment wasn't drawn to, // we only want to apply Fog to LODs, not to the sky outside the LODs if (fragmentDepth < 1.0) { - if (fullFogMode == 0) { + if (uFullFogMode == 0) { // render fog based on distance from the camera vec3 vertexWorldPos = calcViewPosition(fragmentDepth); - float horizontalDist = length(vertexWorldPos.xz) * fogScale; - float heightDist = calculateHeightFogDepth(vertexWorldPos.y, vertexYPos) * fogVerticalScale; - float farDist = calculateFarFogDepth(horizontalDist, length(vertexWorldPos.xyz) * fogScale, nearFogStart); + float horizontalDist = length(vertexWorldPos.xz) * uFogScale; + float heightDist = calculateHeightFogDepth(vertexWorldPos.y, vertexYPos) * uFogVerticalScale; + float farDist = calculateFarFogDepth(horizontalDist, length(vertexWorldPos.xyz) * uFogScale, uNearFogStart); float nearFogThickness = getNearFogThickness(horizontalDist); float farFogThickness = getFarFogThickness(farDist); @@ -89,7 +89,7 @@ void main() float dither = InterleavedGradientNoise(gl_FragCoord.xy) - 0.5; fragColor.a += dither / 255.0; } - else if (fullFogMode == 1) { + else if (uFullFogMode == 1) { // render everything with the fog color fragColor.a = 1.0; } @@ -101,7 +101,7 @@ void main() // a uniform we don't have to worry about GLSL optimizing away different // options when testing, causing a bunch of headaches if we just want to render the screen red. - float depthValue = textureLod(gDepthMap, TexCoord, 0).r; + float depthValue = textureLod(uDepthMap, TexCoord, 0).r; fragColor.rgb = vec3(depthValue); // Convert depth value to grayscale color fragColor.a = 1.0; } diff --git a/core/src/main/resources/shaders/genericObject/direct/frag.frag b/core/src/main/resources/shaders/genericObject/direct/frag.frag new file mode 100644 index 000000000..c3d2ac8c4 --- /dev/null +++ b/core/src/main/resources/shaders/genericObject/direct/frag.frag @@ -0,0 +1,10 @@ +#version 150 core + +in vec4 fColor; + +out vec4 fragColor; + +void main() +{ + fragColor = fColor; +} \ No newline at end of file diff --git a/core/src/main/resources/shaders/genericObject/direct/vert.vert b/core/src/main/resources/shaders/genericObject/direct/vert.vert new file mode 100644 index 000000000..55cf5cad7 --- /dev/null +++ b/core/src/main/resources/shaders/genericObject/direct/vert.vert @@ -0,0 +1,41 @@ +#version 150 core + +uniform mat4 uTransform; +uniform vec4 uColor; +uniform int uSkyLight; +uniform int uBlockLight; +uniform sampler2D uLightMap; + +uniform float uNorthShading; +uniform float uSouthShading; +uniform float uEastShading; +uniform float uWestShading; +uniform float uTopShading; +uniform float uBottomShading; + + +in vec3 vPosition; + +out vec4 fColor; + +void main() +{ + gl_Position = uTransform * vec4(vPosition, 1.0); + + + float blockLight = (float(uBlockLight)+0.5) / 16.0; + float skyLight = (float(uSkyLight)+0.5) / 16.0; + vec4 lightColor = vec4(texture(uLightMap, vec2(blockLight, skyLight)).xyz, 1.0); + + + fColor = lightColor * uColor; + + // apply directional shading + if (gl_VertexID >= 0 && gl_VertexID < 4) { fColor.rgb *= uNorthShading; } + else if (gl_VertexID >= 4 && gl_VertexID < 8) { fColor.rgb *= uSouthShading; } + else if (gl_VertexID >= 8 && gl_VertexID < 12) { fColor.rgb *= uWestShading; } + else if (gl_VertexID >= 12 && gl_VertexID < 16) { fColor.rgb *= uEastShading; } + else if (gl_VertexID >= 16 && gl_VertexID < 20) { fColor.rgb *= uBottomShading; } + else if (gl_VertexID >= 20 && gl_VertexID < 24) { fColor.rgb *= uTopShading; } + +} \ No newline at end of file diff --git a/core/src/main/resources/shaders/genericObject/instanced/frag.frag b/core/src/main/resources/shaders/genericObject/instanced/frag.frag new file mode 100644 index 000000000..c3d2ac8c4 --- /dev/null +++ b/core/src/main/resources/shaders/genericObject/instanced/frag.frag @@ -0,0 +1,10 @@ +#version 150 core + +in vec4 fColor; + +out vec4 fragColor; + +void main() +{ + fragColor = fColor; +} \ No newline at end of file diff --git a/core/src/main/resources/shaders/genericObject/instanced/vert.vert b/core/src/main/resources/shaders/genericObject/instanced/vert.vert new file mode 100644 index 000000000..a7d9a6a96 --- /dev/null +++ b/core/src/main/resources/shaders/genericObject/instanced/vert.vert @@ -0,0 +1,66 @@ +#version 330 core + +layout (location = 1) in vec4 aColor; +layout (location = 2) in vec3 aScale; +layout (location = 3) in ivec3 aTranslateChunk; +layout (location = 4) in vec3 aTranslateSubChunk; +layout (location = 5) in int aMaterial; + +uniform ivec3 uOffsetChunk; +uniform vec3 uOffsetSubChunk; +uniform ivec3 uCameraPosChunk; +uniform vec3 uCameraPosSubChunk; + +uniform mat4 uProjectionMvm; +uniform int uSkyLight; +uniform int uBlockLight; +uniform sampler2D uLightMap; + +uniform float uNorthShading; +uniform float uSouthShading; +uniform float uEastShading; +uniform float uWestShading; +uniform float uTopShading; +uniform float uBottomShading; + + +in vec3 vPosition; + +out vec4 fColor; + +void main() +{ + // aTranslate - moves the vertex to the boxGroup's relative position + // uOffset - moves the vertex to the boxGroup's world position + // uCameraPos - moves the vertex into camera space + vec3 trans = (aTranslateChunk + uOffsetChunk - uCameraPosChunk) * 16.0f; + // separate float and int values are to fix percission loss at extreme distances from the origin (IE 10,000,000+) + // luckily large translate values minus large cameraPos generally equal values that cleanly fit in a float + trans += (aTranslateSubChunk + uOffsetSubChunk - uCameraPosSubChunk); + + // combination translation and scaling matrix + mat4 transform = mat4( + aScale.x, 0.0, 0.0, 0.0, + 0.0, aScale.y, 0.0, 0.0, + 0.0, 0.0, aScale.z, 0.0, + trans.x, trans.y, trans.z, 1.0 + ); + + gl_Position = uProjectionMvm * transform * vec4(vPosition, 1.0); + + float blockLight = (float(uBlockLight)+0.5) / 16.0; + float skyLight = (float(uSkyLight)+0.5) / 16.0; + vec4 lightColor = vec4(texture(uLightMap, vec2(blockLight, skyLight)).xyz, 1.0); + + + fColor = lightColor * aColor; + + // apply directional shading + if (gl_VertexID >= 0 && gl_VertexID < 4) { fColor.rgb *= uNorthShading; } + else if (gl_VertexID >= 4 && gl_VertexID < 8) { fColor.rgb *= uSouthShading; } + else if (gl_VertexID >= 8 && gl_VertexID < 12) { fColor.rgb *= uWestShading; } + else if (gl_VertexID >= 12 && gl_VertexID < 16) { fColor.rgb *= uEastShading; } + else if (gl_VertexID >= 16 && gl_VertexID < 20) { fColor.rgb *= uBottomShading; } + else if (gl_VertexID >= 20 && gl_VertexID < 24) { fColor.rgb *= uTopShading; } + +} diff --git a/core/src/main/resources/shaders/noise/noise.frag b/core/src/main/resources/shaders/noise/noise.frag index 1156311fc..e34018619 100644 --- a/core/src/main/resources/shaders/noise/noise.frag +++ b/core/src/main/resources/shaders/noise/noise.frag @@ -7,9 +7,9 @@ out vec4 fragColor; uniform float distanceScale; -uniform int noiseSteps; -uniform float noiseIntensity; -uniform float noiseDropoff; +uniform int uNoiseSteps; +uniform float uNoiseIntensity; +uniform float uNoiseDropoff; @@ -43,12 +43,12 @@ void main() { vec3 fixedVPos = vPos.xyz - vertexNormal * 0.001; - float noiseAmplification = noiseIntensity / 100; + float noiseAmplification = uNoiseIntensity / 100; noiseAmplification = (-1 * pow(2*((vertexColor.x + vertexColor.y + vertexColor.z) / 3) - 1, 2) + 1) * noiseAmplification; // Lessen the effect on depending on how dark the object is, equasion for this is -(2x-1)^{2}+1 noiseAmplification *= vertexColor.w; // The effect would lessen on transparent objects // Random value for each position - float randomValue = rand(quantize(fixedVPos.xyz, noiseSteps)) + float randomValue = rand(quantize(fixedVPos.xyz, uNoiseSteps)) * 2.0 * noiseAmplification - noiseAmplification; @@ -57,7 +57,7 @@ void main() { vec3 newCol = (1.0 - vertexColor.rgb) * randomValue; // Clamps it and turns it back into a vec4 - float distA = length(vertexWorldPos) * distanceScale * noiseDropoff; + float distA = length(vertexWorldPos) * distanceScale * uNoiseDropoff; fragColor = clamp(vec4(newCol.rgb, distA), 0.0, 1.0); // The further away it gets, the less noise gets applied // The further away it gets, the less noise gets applied diff --git a/core/src/main/resources/shaders/normal.vert b/core/src/main/resources/shaders/normal.vert index e6a28aa8c..a7fc6cd33 100644 --- a/core/src/main/resources/shaders/normal.vert +++ b/core/src/main/resources/shaders/normal.vert @@ -1,6 +1,7 @@ #version 150 core in vec2 vPosition; + out vec2 TexCoord; /** diff --git a/core/src/main/resources/shaders/standard.vert b/core/src/main/resources/shaders/standard.vert index 316559b6e..bcff6d9c8 100644 --- a/core/src/main/resources/shaders/standard.vert +++ b/core/src/main/resources/shaders/standard.vert @@ -8,15 +8,15 @@ out vec4 vertexColor; out vec3 vertexWorldPos; out float vertexYPos; -uniform bool whiteWorld; +uniform bool uWhiteWorld; -uniform mat4 combinedMatrix; -uniform vec3 modelOffset; -uniform float worldYOffset; +uniform mat4 uCombinedMatrix; +uniform vec3 uModelOffset; +uniform float uWorldYOffset; -uniform int worldSkyLight; -uniform sampler2D lightMap; -uniform float mircoOffset; +uniform int uWorldSkyLight; +uniform sampler2D uLightMap; +uniform float uMircoOffset; /** @@ -33,9 +33,9 @@ void main() { vPos = vPosition; // This is so it can be passed to the fragment shader - vertexWorldPos = vPosition.xyz + modelOffset; + vertexWorldPos = vPosition.xyz + uModelOffset; - vertexYPos = vPosition.y + worldYOffset; + vertexYPos = vPosition.y + uWorldYOffset; uint meta = vPosition.a; @@ -44,23 +44,23 @@ void main() // 0b01 = positive offset // 0b11 = negative offset // format is: 0b00zzyyxx - float mx = (mirco & 1u)!=0u ? mircoOffset : 0.0; + float mx = (mirco & 1u)!=0u ? uMircoOffset : 0.0; mx = (mirco & 2u)!=0u ? -mx : mx; - float my = (mirco & 4u)!=0u ? mircoOffset : 0.0; + float my = (mirco & 4u)!=0u ? uMircoOffset : 0.0; my = (mirco & 8u)!=0u ? -my : my; - float mz = (mirco & 16u)!=0u ? mircoOffset : 0.0; + float mz = (mirco & 16u)!=0u ? uMircoOffset : 0.0; mz = (mirco & 32u)!=0u ? -mz : mz; uint lights = meta & 0xFFu; float light2 = (mod(float(lights), 16.0)+0.5) / 16.0; float light = (float(lights/16u)+0.5) / 16.0; - vertexColor = vec4(texture(lightMap, vec2(light, light2)).xyz, 1.0); + vertexColor = vec4(texture(uLightMap, vec2(light, light2)).xyz, 1.0); - if (!whiteWorld) + if (!uWhiteWorld) { vertexColor *= color; } - gl_Position = combinedMatrix * vec4(vertexWorldPos + vec3(mx, 0, mz), 1.0); + gl_Position = uCombinedMatrix * vec4(vertexWorldPos + vec3(mx, 0, mz), 1.0); } diff --git a/core/src/main/resources/sqlScripts/0070-sqlite-createBeaconBeamTable.sql b/core/src/main/resources/sqlScripts/0070-sqlite-createBeaconBeamTable.sql new file mode 100644 index 000000000..48a5040a6 --- /dev/null +++ b/core/src/main/resources/sqlScripts/0070-sqlite-createBeaconBeamTable.sql @@ -0,0 +1,16 @@ + +CREATE TABLE BeaconBeam( + -- compound primary key + BlockPosX INT NOT NULL + ,BlockPosY INT NOT NULL + ,BlockPosZ INT NOT NULL + + ,ColorR INT NOT NULL + ,ColorG INT NOT NULL + ,ColorB INT NOT NULL + + ,LastModifiedUnixDateTime BIGINT NOT NULL -- in GMT 0 + ,CreatedUnixDateTime BIGINT NOT NULL -- in GMT 0 + + ,PRIMARY KEY (BlockPosX, BlockPosY, BlockPosZ) +); diff --git a/core/src/main/resources/sqlScripts/scriptList.txt b/core/src/main/resources/sqlScripts/scriptList.txt index 52027a061..7c6ed02ea 100644 --- a/core/src/main/resources/sqlScripts/scriptList.txt +++ b/core/src/main/resources/sqlScripts/scriptList.txt @@ -6,3 +6,4 @@ 0040-sqlite-removeRenderCache.sql 0050-sqlite-addApplyToParentIndex.sql 0060-sqlite-createChunkHashTable.sql +0070-sqlite-createBeaconBeamTable.sql diff --git a/core/src/test/java/testItems/lightingEngine/LightingTestBlockStateWrapper.java b/core/src/test/java/testItems/lightingEngine/LightingTestBlockStateWrapper.java index 1ecea8ab5..a46f50fc3 100644 --- a/core/src/test/java/testItems/lightingEngine/LightingTestBlockStateWrapper.java +++ b/core/src/test/java/testItems/lightingEngine/LightingTestBlockStateWrapper.java @@ -22,6 +22,8 @@ package testItems.lightingEngine; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import tests.LightingEngineTest; +import java.awt.*; + /** * @see LightingEngineTest * @see LightingTestChunkWrapper @@ -102,6 +104,18 @@ public class LightingTestBlockStateWrapper implements IBlockStateWrapper public boolean isLiquid() { throw new UnsupportedOperationException("Not Implemented"); } @Override - public byte getIrisBlockMaterialId() { throw new UnsupportedOperationException("Not Implemented"); } + public byte getMaterialId() { throw new UnsupportedOperationException("Not Implemented"); } + + @Override + public boolean isBeaconBlock() { throw new UnsupportedOperationException("Not Implemented"); } + @Override + public boolean isBeaconBaseBlock() { throw new UnsupportedOperationException("Not Implemented"); } + + @Override + public Color getMapColor() { throw new UnsupportedOperationException("Not Implemented"); } + @Override + public boolean isGlassBlock() { throw new UnsupportedOperationException("Not Implemented"); } + + } diff --git a/core/src/test/java/testItems/lightingEngine/LightingTestChunkWrapper.java b/core/src/test/java/testItems/lightingEngine/LightingTestChunkWrapper.java index 30be0d596..8317a6509 100644 --- a/core/src/test/java/testItems/lightingEngine/LightingTestChunkWrapper.java +++ b/core/src/test/java/testItems/lightingEngine/LightingTestChunkWrapper.java @@ -90,7 +90,7 @@ public class LightingTestChunkWrapper implements IChunkWrapper IBlockStateWrapper block = chunkWrapper.getBlockState(x,y,z); int opacity = block.getOpacity(); - if (opacity >= IBlockStateWrapper.FULLY_OPAQUE) + if (opacity >= LodUtil.BLOCK_FULLY_OPAQUE) { opacity = 3; } diff --git a/core/src/test/java/testItems/worldGeneratorInjection/objects/LevelWrapperTest.java b/core/src/test/java/testItems/worldGeneratorInjection/objects/LevelWrapperTest.java index b22178b55..4990c892e 100644 --- a/core/src/test/java/testItems/worldGeneratorInjection/objects/LevelWrapperTest.java +++ b/core/src/test/java/testItems/worldGeneratorInjection/objects/LevelWrapperTest.java @@ -20,6 +20,7 @@ package testItems.worldGeneratorInjection.objects; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType; +import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister; import com.seibel.distanthorizons.api.interfaces.world.IDhApiDimensionTypeWrapper; import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper; @@ -50,9 +51,14 @@ public class LevelWrapperTest implements IDhApiLevelWrapper public boolean hasSkyLight() { return false; } @Override - public int getHeight() { return 0; } + public int getMaxHeight() { return 0; } @Override public int getMinHeight() { return IDhApiLevelWrapper.super.getMinHeight(); } + @Override + public IDhApiCustomRenderRegister getRenderRegister() { return null; } + + + } \ No newline at end of file diff --git a/core/src/test/java/tests/DhSectionPosTest.java b/core/src/test/java/tests/DhSectionPosTest.java index c0585a60e..14f29a068 100644 --- a/core/src/test/java/tests/DhSectionPosTest.java +++ b/core/src/test/java/tests/DhSectionPosTest.java @@ -191,41 +191,19 @@ public class DhSectionPosTest // origin pos // DhBlockPos originBlockPos = new DhBlockPos(0, 0, 0); - long originsectionPos = DhSectionPos.encode(originBlockPos); - assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 0, 0), originsectionPos); + long originSectionPos = DhSectionPos.encodeContaining(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, originBlockPos); + assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 0, 0), originSectionPos); // offset pos // long offsetSectionPos; DhBlockPos offsetBlockPos = new DhBlockPos(1000, 0, 42000); - offsetSectionPos = DhSectionPos.encode(offsetBlockPos); + offsetSectionPos = DhSectionPos.encodeContaining(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, offsetBlockPos); assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 15, 656), offsetSectionPos); offsetBlockPos = new DhBlockPos(-987654, 0, 46); - offsetSectionPos = DhSectionPos.encode(offsetBlockPos); - assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, -15433, 0), offsetSectionPos); - - } - - @Test - public void createFromBlockPos2D() - { - // origin pos // - - DhBlockPos2D originBlockPos = new DhBlockPos2D(0, 0); - long originSectionPos = DhSectionPos.encode(originBlockPos); - assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 0, 0), originSectionPos); - - - // offset pos // - - DhBlockPos2D offsetBlockPos = new DhBlockPos2D(1000, 42000); - long offsetSectionPos = DhSectionPos.encode(offsetBlockPos); - assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, 15, 656), offsetSectionPos); - - offsetBlockPos = new DhBlockPos2D(-987654, 46); - offsetSectionPos = DhSectionPos.encode(offsetBlockPos); + offsetSectionPos = DhSectionPos.encodeContaining(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, offsetBlockPos); assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, -15433, 0), offsetSectionPos); } @@ -236,18 +214,18 @@ public class DhSectionPosTest // origin pos // DhChunkPos originChunkPos = new DhChunkPos(0,0); - long originSectionPos = DhSectionPos.encode(originChunkPos); + long originSectionPos = DhSectionPos.encodeContaining(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL, originChunkPos); assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL, 0, 0), originSectionPos); // offset pos // DhChunkPos offsetChunkPos = new DhChunkPos(1000, 42000); - long offsetSectionPos = DhSectionPos.encode(offsetChunkPos); + long offsetSectionPos = DhSectionPos.encodeContaining(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL, offsetChunkPos); assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL, 15, 656), offsetSectionPos); offsetChunkPos = new DhChunkPos(-987654, 46); - offsetSectionPos = DhSectionPos.encode(offsetChunkPos); + offsetSectionPos = DhSectionPos.encodeContaining(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL, offsetChunkPos); assertSectionPosEqual(DhSectionPos.encode(DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL, -15433, 0), offsetSectionPos); } From 32abe15b09e848b12098ed42f71ea8772bbaab56 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 28 Jul 2024 20:19:38 +0500 Subject: [PATCH 175/205] Add build number on F3 screen --- .../com/seibel/distanthorizons/core/logging/f3/F3Screen.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java b/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java index cc3b817cb..3cba37e27 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java @@ -20,6 +20,7 @@ package com.seibel.distanthorizons.core.logging.f3; import com.seibel.distanthorizons.core.api.internal.SharedApi; +import com.seibel.distanthorizons.core.jar.ModJarInfo; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.render.RenderBufferHandler; import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer; @@ -79,6 +80,10 @@ public class F3Screen messageList.add(""); messageList.add(ModInfo.READABLE_NAME+": "+ModInfo.VERSION); + if (ModInfo.IS_DEV_BUILD) + { + messageList.add("Build: " + ModJarInfo.Git_Commit.substring(0, 8) + " (" + ModJarInfo.Git_Branch + ")"); + } messageList.add(""); // thread pools messageList.add(getThreadPoolStatString("World Gen", worldGenPool));//"World Gen Tasks: 40/5304, (in progress: 7)"); From e5033a0c0f68779340682cde6879f2c84c74b0ca Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 29 Jul 2024 13:10:04 +0500 Subject: [PATCH 176/205] Use FullDataPayload instead of reusing messages --- .../core/level/DhClientLevel.java | 2 +- .../core/level/DhServerLevel.java | 19 ++-- .../client/AbstractFullDataRequestQueue.java | 4 +- .../client/ClientNetworkState.java | 6 +- .../FullDataPartialUpdateMessage.java | 32 ++---- .../messages/fullData/FullDataPayload.java | 103 ++++++++++++++++++ .../FullDataSourceResponseMessage.java | 34 ++---- .../fullData/IFullDataPayloadMessage.java | 82 -------------- 8 files changed, 137 insertions(+), 145 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPayload.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/IFullDataPayloadMessage.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 02260a3d3..50aa62a7b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -139,7 +139,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel { try { - FullDataSourceV2DTO dataSourceDto = this.networkState.decodeDataSourceAndReleaseBuffer(msg); + FullDataSourceV2DTO dataSourceDto = this.networkState.decodeDataSourceAndReleaseBuffer(msg.payload); if (!msg.isSameLevelAs(this.levelWrapper)) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 7a84219f8..c2b8290bb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -28,6 +28,7 @@ import com.seibel.distanthorizons.core.multiplayer.server.RemotePlayerConnection import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; import com.seibel.distanthorizons.core.network.messages.ILevelRelatedMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPayload; import com.seibel.distanthorizons.core.network.messages.requests.CancelMessage; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceResponseMessage; @@ -173,9 +174,9 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel { rateLimiterSet.loginDataSyncRCLimiter.release(); - FullDataSourceResponseMessage responseMessage = new FullDataSourceResponseMessage(fullDataSource); - responseMessage.splitIntoChunks(FULL_DATA_CHUNK_SIZE, msg.session::sendMessage); - msg.sendResponse(responseMessage); + FullDataPayload payload = new FullDataPayload(fullDataSource); + payload.acceptInChunkMessages(FULL_DATA_CHUNK_SIZE, msg.session::sendMessage); + msg.sendResponse(new FullDataSourceResponseMessage(payload)); }, executor); } })); @@ -289,7 +290,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel } CompletableFuture.runAsync(() -> { - try (FullDataSourceResponseMessage response = new FullDataSourceResponseMessage(requestGroup.fullDataSource)) + try (FullDataPayload payload = new FullDataPayload(requestGroup.fullDataSource)) { for (FullDataSourceRequestMessage msg : requestGroup.requestMessages.values()) { @@ -302,8 +303,8 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel } serverPlayerState.getRateLimiterSet(this).fullDataRequestConcurrencyLimiter.release(); - response.splitIntoChunks(FULL_DATA_CHUNK_SIZE, msg.session::sendMessage); - msg.sendResponse(response.retain()); + payload.acceptInChunkMessages(FULL_DATA_CHUNK_SIZE, msg.session::sendMessage); + msg.sendResponse(new FullDataSourceResponseMessage(payload.retain())); } } }, executor); @@ -326,7 +327,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel } CompletableFuture.runAsync(() -> { - try (FullDataPartialUpdateMessage updateMessage = new FullDataPartialUpdateMessage(this.serverLevelWrapper, data)) + try (FullDataPayload payload = new FullDataPayload(data)) { for (ServerPlayerState serverPlayerState : this.remotePlayerConnectionHandler.getConnectedPlayers()) { @@ -345,8 +346,8 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel if (distanceFromPlayer >= serverPlayerState.serverPlayer().getViewDistance() && distanceFromPlayer <= serverPlayerState.config.getRenderDistanceRadius()) { - updateMessage.splitIntoChunks(FULL_DATA_CHUNK_SIZE, serverPlayerState.session::sendMessage); - serverPlayerState.session.sendMessage(updateMessage.retain()); + payload.acceptInChunkMessages(FULL_DATA_CHUNK_SIZE, serverPlayerState.session::sendMessage); + serverPlayerState.session.sendMessage(new FullDataPartialUpdateMessage(this.serverLevelWrapper, payload.retain())); } } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index 5f5e2eaf4..7455b9b15 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -190,9 +190,9 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, throw throwable; } - if (response.dtoBufferId != null) + if (response.payload != null) { - FullDataSourceV2DTO dataSourceDto = this.networkState.decodeDataSourceAndReleaseBuffer(response); + FullDataSourceV2DTO dataSourceDto = this.networkState.decodeDataSourceAndReleaseBuffer(response.payload); FullDataSourceV2 fullDataSource = dataSourceDto.createPooledDataSource(this.level.getLevelWrapper()); entry.chunkDataConsumer.accept(fullDataSource); FullDataSourceV2.DATA_SOURCE_POOL.returnPooledDataSource(fullDataSource); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index b84263111..850920e7e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -10,7 +10,7 @@ import com.seibel.distanthorizons.core.network.event.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.event.CloseEvent; import com.seibel.distanthorizons.core.network.messages.base.RemotePlayerConfigMessage; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataChunkMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.IFullDataPayloadMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPayload; import com.seibel.distanthorizons.core.network.session.Session; import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; import io.netty.buffer.ByteBufAllocator; @@ -86,9 +86,9 @@ public class ClientNetworkState implements Closeable }); } - public FullDataSourceV2DTO decodeDataSourceAndReleaseBuffer(IFullDataPayloadMessage msg) + public FullDataSourceV2DTO decodeDataSourceAndReleaseBuffer(FullDataPayload msg) { - CompositeByteBuf composite = this.fullDataBuffers.remove(msg.getDtoBufferId()); + CompositeByteBuf composite = this.fullDataBuffers.remove(msg.dtoBufferId); Objects.requireNonNull(composite); try diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java index 973d5560a..29bd25403 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java @@ -20,40 +20,26 @@ package com.seibel.distanthorizons.core.network.messages.fullData; import com.google.common.base.MoreObjects; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; +import com.seibel.distanthorizons.core.network.INetworkObject; import com.seibel.distanthorizons.core.network.messages.ILevelRelatedMessage; import com.seibel.distanthorizons.core.network.messages.NetworkMessage; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import io.netty.buffer.ByteBuf; -import org.jetbrains.annotations.Nullable; -import java.util.Objects; - -public class FullDataPartialUpdateMessage extends NetworkMessage implements ILevelRelatedMessage, IFullDataPayloadMessage +public class FullDataPartialUpdateMessage extends NetworkMessage implements ILevelRelatedMessage { private String levelName; @Override public String getLevelName() { return this.levelName; } - @Nullable - public Integer dtoBufferId; - @Override - public int getDtoBufferId() { return Objects.requireNonNull(this.dtoBufferId); } - @Override - public void setDtoBufferId(int bufferId) { this.dtoBufferId = bufferId; } - - public ByteBuf dtoBuffer; - @Override - public ByteBuf getDtoBuffer() { return this.dtoBuffer; } - @Override - public void setDtoBuffer(ByteBuf buffer) { this.dtoBuffer = buffer; } + public FullDataPayload payload; public FullDataPartialUpdateMessage() { } - public FullDataPartialUpdateMessage(IServerLevelWrapper level, FullDataSourceV2 fullDataSource) + public FullDataPartialUpdateMessage(IServerLevelWrapper level, FullDataPayload payload) { this.levelName = level.getKeyedLevelDimensionName(); - this.createCompressedDtoBuffer(fullDataSource); + this.payload = payload; } @Override @@ -63,15 +49,14 @@ public class FullDataPartialUpdateMessage extends NetworkMessage implements ILev public void encode(ByteBuf out) { this.writeString(this.levelName, out); - out.writeInt(Objects.requireNonNull(this.dtoBufferId)); - this.dtoBuffer.release(); + this.payload.encode(out); } @Override public void decode(ByteBuf in) { this.levelName = this.readString(in); - this.dtoBufferId = in.readInt(); + this.payload = INetworkObject.decodeToInstance(new FullDataPayload(), in); } @@ -80,8 +65,7 @@ public class FullDataPartialUpdateMessage extends NetworkMessage implements ILev { return super.toStringHelper() .add("levelName", this.levelName) - .add("dtoBufferId", this.dtoBufferId) - .add("dtoBuffer", this.dtoBuffer); + .add("payload", this.payload); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPayload.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPayload.java new file mode 100644 index 000000000..fbc830b1a --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPayload.java @@ -0,0 +1,103 @@ +package com.seibel.distanthorizons.core.network.messages.fullData; + +import com.google.common.base.MoreObjects; +import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; +import com.seibel.distanthorizons.core.network.INetworkObject; +import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.ByteBufAllocator; +import org.jetbrains.annotations.NotNull; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Objects; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; + + +public class FullDataPayload implements INetworkObject, Closeable +{ + private static final AtomicInteger lastBufferId = new AtomicInteger(); + + public int dtoBufferId; + public ByteBuf dtoBuffer; + + + public FullDataPayload() { } + public FullDataPayload(@NotNull FullDataSourceV2 fullDataSource) + { + Objects.requireNonNull(fullDataSource); + + this.dtoBufferId = lastBufferId.getAndIncrement(); + + try + { + EDhApiDataCompressionMode compressionMode = Config.Client.Advanced.LodBuilding.dataCompression.get(); + FullDataSourceV2DTO dataSourceDto = FullDataSourceV2DTO.CreateFromDataSource(fullDataSource, compressionMode); + + this.dtoBuffer = ByteBufAllocator.DEFAULT.buffer(); + dataSourceDto.encode(this.dtoBuffer); + } + catch (IOException e) + { + throw new RuntimeException(e); + } + } + + + public void acceptInChunkMessages(int chunkSize, Consumer chunkMessageConsumer) + { + for (int chunkNum = 0; ; chunkNum++) + { + int offset = chunkNum * chunkSize; + + int actualChunkSize = Math.min(this.dtoBuffer.writerIndex() - offset, chunkSize); + if (actualChunkSize <= 0) + { + break; + } + + FullDataChunkMessage chunk = new FullDataChunkMessage(this.dtoBufferId, chunkNum == 0, this.dtoBuffer.slice(offset, actualChunkSize)); + chunkMessageConsumer.accept(chunk); + } + } + + + public FullDataPayload retain() + { + this.dtoBuffer.retain(); + return this; + } + + @Override public void close() + { + this.dtoBuffer.release(); + } + + + @Override + public void encode(ByteBuf out) + { + out.writeInt(this.dtoBufferId); + this.dtoBuffer.release(); + } + + @Override + public void decode(ByteBuf in) + { + this.dtoBufferId = in.readInt(); + } + + + @Override + public String toString() + { + return MoreObjects.toStringHelper(this) + .add("dtoBufferId", this.dtoBufferId) + .add("dtoBuffer", this.dtoBuffer) + .toString(); + } + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java index 9cfc5fea4..a60fd5982 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceResponseMessage.java @@ -20,56 +20,43 @@ package com.seibel.distanthorizons.core.network.messages.fullData; import com.google.common.base.MoreObjects; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; +import com.seibel.distanthorizons.core.network.INetworkObject; import com.seibel.distanthorizons.core.network.messages.TrackableMessage; import io.netty.buffer.ByteBuf; import org.jetbrains.annotations.Nullable; -import java.util.Objects; - /** * Response message, containing the requested full data source, * or nothing if requested in updates-only mode and the data was not updated. */ -public class FullDataSourceResponseMessage extends TrackableMessage implements IFullDataPayloadMessage +public class FullDataSourceResponseMessage extends TrackableMessage { @Nullable - public Integer dtoBufferId; - @Override - public int getDtoBufferId() { return Objects.requireNonNull(this.dtoBufferId); } - @Override - public void setDtoBufferId(int bufferId) { this.dtoBufferId = bufferId; } - - public ByteBuf dtoBuffer; - @Override - public ByteBuf getDtoBuffer() { return this.dtoBuffer; } - @Override - public void setDtoBuffer(ByteBuf buffer) { this.dtoBuffer = buffer; } + public FullDataPayload payload; public FullDataSourceResponseMessage() { } - public FullDataSourceResponseMessage(@Nullable FullDataSourceV2 fullDataSource) + public FullDataSourceResponseMessage(@Nullable FullDataPayload payload) { - if (fullDataSource != null) + if (payload != null) { - this.createCompressedDtoBuffer(fullDataSource); + this.payload = payload; } } @Override public void encode0(ByteBuf out) { - if (this.writeOptional(out, this.dtoBufferId)) + if (this.writeOptional(out, this.payload)) { - out.writeInt(this.dtoBufferId); - this.dtoBuffer.release(); + this.payload.encode(out); } } @Override public void decode0(ByteBuf in) { - this.dtoBufferId = this.readOptional(in, in::readInt); + this.payload = this.readOptional(in, () -> INetworkObject.decodeToInstance(new FullDataPayload(), in)); } @@ -77,8 +64,7 @@ public class FullDataSourceResponseMessage extends TrackableMessage implements I public MoreObjects.ToStringHelper toStringHelper() { return super.toStringHelper() - .add("dtoBufferId", this.dtoBufferId) - .add("dtoBuffer", this.dtoBuffer); + .add("payload", this.payload); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/IFullDataPayloadMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/IFullDataPayloadMessage.java deleted file mode 100644 index a631e0b76..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/IFullDataPayloadMessage.java +++ /dev/null @@ -1,82 +0,0 @@ -package com.seibel.distanthorizons.core.network.messages.fullData; - -import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; -import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; -import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; -import io.netty.buffer.ByteBuf; -import io.netty.buffer.ByteBufAllocator; - -import java.io.Closeable; -import java.io.IOException; -import java.util.Objects; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; - - -public interface IFullDataPayloadMessage> extends Closeable -{ - AtomicInteger lastBufferId = new AtomicInteger(); - - int getDtoBufferId(); - void setDtoBufferId(int bufferId); - - ByteBuf getDtoBuffer(); - void setDtoBuffer(ByteBuf buffer); - - - default void createCompressedDtoBuffer(FullDataSourceV2 fullDataSource) - { - Objects.requireNonNull(fullDataSource); - - int bufferId = lastBufferId.getAndIncrement(); - this.setDtoBufferId(bufferId); - - try - { - EDhApiDataCompressionMode compressionMode = Config.Client.Advanced.LodBuilding.dataCompression.get(); - FullDataSourceV2DTO dataSourceDto = FullDataSourceV2DTO.CreateFromDataSource(fullDataSource, compressionMode); - - ByteBuf buffer = ByteBufAllocator.DEFAULT.buffer(); - dataSourceDto.encode(buffer); - this.setDtoBuffer(buffer); - } - catch (IOException e) - { - throw new RuntimeException(e); - } - } - - default void splitIntoChunks(int chunkSize, Consumer chunkMessageConsumer) - { - int bufferId = this.getDtoBufferId(); - ByteBuf dtoBuffer = this.getDtoBuffer(); - - for (int chunkNum = 0; ; chunkNum++) - { - int offset = chunkNum * chunkSize; - - int actualChunkSize = Math.min(dtoBuffer.writerIndex() - offset, chunkSize); - if (actualChunkSize <= 0) - { - break; - } - - FullDataChunkMessage chunk = new FullDataChunkMessage(bufferId, chunkNum == 0, dtoBuffer.slice(offset, actualChunkSize)); - chunkMessageConsumer.accept(chunk); - } - } - - default T retain() - { - this.getDtoBuffer().retain(); - //noinspection unchecked - return (T)this; - } - - default void close() - { - this.getDtoBuffer().release(); - } - -} From 93b57ae2e1d768bea880df0e2c1e6b149cc900ff Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 30 Jul 2024 10:52:27 +0500 Subject: [PATCH 177/205] Increase defaults for network compression threads --- .../presets/ThreadPresetConfigEventHandler.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/ThreadPresetConfigEventHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/ThreadPresetConfigEventHandler.java index bcdbb45c9..08542169c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/ThreadPresetConfigEventHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/ThreadPresetConfigEventHandler.java @@ -32,6 +32,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.List; +@SuppressWarnings("FieldCanBeLocal") public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHandler { public static final ThreadPresetConfigEventHandler INSTANCE = new ThreadPresetConfigEventHandler(); @@ -128,24 +129,24 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan }}); - public static int getNetworkCompressionDefaultThreadCount() { return getThreadCountByPercent(0.1); } + public static int getNetworkCompressionDefaultThreadCount() { return getThreadCountByPercent(0.3); } private final ConfigEntryWithPresetOptions networkCompressionThreadCount = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.numberOfNetworkCompressionThreads, new HashMap() {{ this.put(EDhApiThreadPreset.MINIMAL_IMPACT, 1); this.put(EDhApiThreadPreset.LOW_IMPACT, getNetworkCompressionDefaultThreadCount()); - this.put(EDhApiThreadPreset.BALANCED, getThreadCountByPercent(0.2)); - this.put(EDhApiThreadPreset.AGGRESSIVE, getThreadCountByPercent(0.4)); - this.put(EDhApiThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, getThreadCountByPercent(0.6)); + this.put(EDhApiThreadPreset.BALANCED, getThreadCountByPercent(0.4)); + this.put(EDhApiThreadPreset.AGGRESSIVE, getThreadCountByPercent(0.6)); + this.put(EDhApiThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, getThreadCountByPercent(0.8)); }}); - public static double getNetworkCompressionDefaultRunTimeRatio() { return 0.25; } + public static double getNetworkCompressionDefaultRunTimeRatio() { return 0.5; } private final ConfigEntryWithPresetOptions networkCompressionRunTime = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.runTimeRatioForNetworkCompressionThreads, new HashMap() {{ - this.put(EDhApiThreadPreset.MINIMAL_IMPACT, 0.1); + this.put(EDhApiThreadPreset.MINIMAL_IMPACT, 0.25); this.put(EDhApiThreadPreset.LOW_IMPACT, getNetworkCompressionDefaultRunTimeRatio()); - this.put(EDhApiThreadPreset.BALANCED, 0.5); - this.put(EDhApiThreadPreset.AGGRESSIVE, 0.75); + this.put(EDhApiThreadPreset.BALANCED, 0.75); + this.put(EDhApiThreadPreset.AGGRESSIVE, 1.0); this.put(EDhApiThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, 1.0); }}); From e62c6a5c55b7d2c99892b6ea2eb6bb9e068acd2a Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Thu, 1 Aug 2024 22:19:10 +0500 Subject: [PATCH 178/205] [skip ci] Prevent disconnects on encode/decode/handle errors --- .../api/internal/ClientPluginChannelApi.java | 2 +- .../client/ClientNetworkState.java | 2 +- .../multiplayer/server/ServerPlayerState.java | 2 +- .../core/network/event/CloseEvent.java | 11 ++------ .../core/network/event/InternalEvent.java | 20 ++++++++++++++ .../network/event/NetworkEventSource.java | 26 ++++++++++--------- .../network/event/ProtocolErrorEvent.java | 21 +++++++++++++++ .../event/ScopedNetworkEventSource.java | 4 +-- .../core/network/session/Session.java | 4 +-- 9 files changed, 64 insertions(+), 28 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/event/InternalEvent.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/event/ProtocolErrorEvent.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index 688d2f809..29ffee347 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -52,7 +52,7 @@ public class ClientPluginChannelApi Objects.requireNonNull(session); this.session = session; session.registerHandler(CurrentLevelKeyMessage.class, this::onCurrentLevelKeyMessage); - session.registerHandler(CloseEvent.class, this::onClose); + session.registerHandler(CloseEvent.class, false, this::onClose); } private void onCurrentLevelKeyMessage(CurrentLevelKeyMessage msg) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index 850920e7e..d7f18154b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -63,7 +63,7 @@ public class ClientNetworkState implements Closeable this.configReceived = true; }); - this.session.registerHandler(CloseEvent.class, msg -> + this.session.registerHandler(CloseEvent.class, false, msg -> { this.configReceived = false; }); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index 6a3c04502..d3a15c1e4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -53,7 +53,7 @@ public class ServerPlayerState this.session.sendMessage(new RemotePlayerConfigMessage(this.config)); }); - this.session.registerHandler(CloseEvent.class, event -> { + this.session.registerHandler(CloseEvent.class, false, event -> { // Noop }); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/CloseEvent.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/CloseEvent.java index 8ee84fbfb..cbd3a1a31 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/event/CloseEvent.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/CloseEvent.java @@ -1,16 +1,9 @@ package com.seibel.distanthorizons.core.network.event; -import com.seibel.distanthorizons.core.network.messages.NetworkMessage; -import io.netty.buffer.ByteBuf; - /** - * This is not a "real" message, and only used to indicate a disconnection. + * This event is used to indicate a disconnect. */ -public class CloseEvent extends NetworkMessage +public class CloseEvent extends InternalEvent { - @Override - public void encode(ByteBuf out) { throw new UnsupportedOperationException(this.getClass().getSimpleName() + " is not a real message, and cannot be sent."); } - @Override - public void decode(ByteBuf in) { throw new UnsupportedOperationException(this.getClass().getSimpleName() + " is not a real message, and cannot be received."); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/InternalEvent.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/InternalEvent.java new file mode 100644 index 000000000..12bcf834e --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/InternalEvent.java @@ -0,0 +1,20 @@ +package com.seibel.distanthorizons.core.network.event; + +import com.seibel.distanthorizons.core.network.messages.NetworkMessage; +import io.netty.buffer.ByteBuf; + +public abstract class InternalEvent extends NetworkMessage +{ + @Override + public void encode(ByteBuf out) + { + throw new UnsupportedOperationException(this.getClass().getSimpleName() + " is an internal event, and cannot be sent."); + } + + @Override + public void decode(ByteBuf in) + { + throw new UnsupportedOperationException(this.getClass().getSimpleName() + " is an internal event, and cannot be received."); + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java index f1c0f3dd5..49e21e60f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java @@ -108,20 +108,22 @@ public abstract class NetworkEventSource } } - public abstract void registerHandler(Class handlerClass, Consumer handlerImplementation); - - protected void registerHandler(NetworkEventSource instance, Class handlerClass, Consumer handlerImplementation) + public abstract void registerHandler(Class handlerClass, boolean throwIfMessageNotRegistered, Consumer handlerImplementation); + + public final void registerHandler(Class handlerClass, Consumer handlerImplementation) { + this.registerHandler(handlerClass, true, handlerImplementation); + } + + protected final void registerHandler(NetworkEventSource instance, Class handlerClass, boolean throwIfMessageNotRegistered, Consumer handlerImplementation) + { + if (throwIfMessageNotRegistered) + { + MessageRegistry.INSTANCE.getMessageId(handlerClass); + } + //noinspection unchecked - this.handlers.computeIfAbsent(handlerClass, missingHandlerClass -> - { - // Will throw if the handler class is not found - if (missingHandlerClass != CloseEvent.class) - { - MessageRegistry.INSTANCE.getMessageId(missingHandlerClass); - } - return new ConcurrentHashMap<>(); - }) + this.handlers.computeIfAbsent(handlerClass, missingHandlerClass -> new ConcurrentHashMap<>()) .computeIfAbsent(instance, _instance -> ConcurrentHashMap.newKeySet()) .add((Consumer) handlerImplementation); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/ProtocolErrorEvent.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/ProtocolErrorEvent.java new file mode 100644 index 000000000..be6ff4272 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/ProtocolErrorEvent.java @@ -0,0 +1,21 @@ +package com.seibel.distanthorizons.core.network.event; + +import com.seibel.distanthorizons.core.network.messages.NetworkMessage; +import org.jetbrains.annotations.Nullable; + +/** + * This event is used to indicate that encoding or decoding of a message threw an exception. + */ +public class ProtocolErrorEvent extends InternalEvent +{ + public final Throwable throwable; + @Nullable + public final NetworkMessage message; + + public ProtocolErrorEvent(Throwable throwable, @Nullable NetworkMessage message) + { + this.throwable = throwable; + this.message = message; + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/ScopedNetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/ScopedNetworkEventSource.java index cf690aeb0..84d4b7a01 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/event/ScopedNetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/ScopedNetworkEventSource.java @@ -35,14 +35,14 @@ public final class ScopedNetworkEventSource extends NetworkEventSource } @Override - public void registerHandler(Class handlerClass, Consumer handlerImplementation) + public void registerHandler(Class handlerClass, boolean throwIfMessageNotRegistered, Consumer handlerImplementation) { if (this.isClosed) { return; } - this.parent.registerHandler(this, handlerClass, this::handleMessage); + this.parent.registerHandler(this, handlerClass, throwIfMessageNotRegistered, this::handleMessage); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java b/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java index 3527536be..953dce569 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java @@ -66,14 +66,14 @@ public class Session extends NetworkEventSource } @Override - public void registerHandler(Class handlerClass, Consumer handlerImplementation) + public void registerHandler(Class handlerClass, boolean throwIfMessageNotRegistered, Consumer handlerImplementation) { if (this.closeReason.get() != null) { return; } - this.registerHandler(this, handlerClass, handlerImplementation); + this.registerHandler(this, handlerClass, throwIfMessageNotRegistered, handlerImplementation); } public CompletableFuture sendRequest(TrackableMessage msg, Class responseClass) From 9d1173344422717af1525894c2e59dc3598da05c Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 3 Aug 2024 15:42:43 +0500 Subject: [PATCH 179/205] Fix dimension switching (untested) --- .../distanthorizons/core/api/internal/ClientApi.java | 2 +- .../core/api/internal/ClientPluginChannelApi.java | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index ec4df1ad7..90b21502f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -235,7 +235,7 @@ public class ClientApi AbstractDhWorld world = SharedApi.getAbstractDhWorld(); if (world != null) { - if (!this.pluginChannelApi.allowLevelAutoload()) + if (!this.pluginChannelApi.allowLevelLoading(level)) { LOGGER.info("Levels in this connection are managed by the server, skipping auto-load."); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index 688d2f809..bcf74a98c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -27,24 +27,24 @@ public class ClientPluginChannelApi private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); private static final IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class); + private final Consumer levelLoadHandler; private final Consumer levelUnloadHandler; - private final Consumer multiverseLevelLoadHandler; @Nullable public Session session; - public boolean allowLevelAutoload() + public boolean allowLevelLoading(IClientLevelWrapper level) { - return (KEYED_CLIENT_LEVEL_MANAGER.isEnabled() && KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel() != null) + return (KEYED_CLIENT_LEVEL_MANAGER.isEnabled() && level instanceof IServerKeyedClientLevel) || !KEYED_CLIENT_LEVEL_MANAGER.isEnabled(); } public ClientPluginChannelApi(Consumer levelLoadHandler, Consumer levelUnloadHandler) { + this.levelLoadHandler = levelLoadHandler; this.levelUnloadHandler = levelUnloadHandler; - this.multiverseLevelLoadHandler = levelLoadHandler; } public void onJoin(@NonNull Session session) @@ -90,7 +90,7 @@ public class ClientPluginChannelApi { LOGGER.info("Loading level with key: " + msg.levelKey); IServerKeyedClientLevel keyedLevel = KEYED_CLIENT_LEVEL_MANAGER.setServerKeyedLevel(clientLevel, msg.levelKey); - this.multiverseLevelLoadHandler.accept(keyedLevel); + this.levelLoadHandler.accept(keyedLevel); } }); } From 82c5de7dfe68b0ae422122b537d6e08741cb3951 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 5 Aug 2024 14:51:17 +0500 Subject: [PATCH 180/205] Fix Neoforge not being able to connect to vanilla servers --- .../core/network/session/Session.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java b/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java index 3527536be..a9c0e6a35 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java @@ -86,15 +86,25 @@ public class Session extends NetworkEventSource public void sendMessage(NetworkMessage message) { + if (this.closeReason.get() != null) return; LOGGER.debug("Sending message: {}", message); - if (this.serverPlayer != null) + try { - PACKET_SENDER.sendPluginPacketServer(this.serverPlayer, message); + if (this.serverPlayer != null) + { + PACKET_SENDER.sendPluginPacketServer(this.serverPlayer, message); + } + else + { + PACKET_SENDER.sendPluginPacketClient(message); + } } - else + catch (Throwable throwable) { - PACKET_SENDER.sendPluginPacketClient(message); + LOGGER.info("Failed to send a message", throwable); + LOGGER.info("Message: {}", message); + this.close(throwable); } } From 71e4cd62729ce6a98e36ef3cc5b37b5c81db95fd Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 6 Aug 2024 23:12:13 +0500 Subject: [PATCH 181/205] Fix real-time updates --- .../core/multiplayer/client/ClientNetworkState.java | 6 ++++++ .../core/network/event/NetworkEventSource.java | 2 +- .../core/network/event/ScopedNetworkEventSource.java | 10 +++++++++- .../core/network/messages/NetworkMessage.java | 2 -- .../core/network/messages/TrackableMessage.java | 3 ++- .../messages/fullData/FullDataChunkMessage.java | 3 --- .../fullData/FullDataPartialUpdateMessage.java | 2 -- 7 files changed, 18 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index 850920e7e..130a5ca73 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -10,6 +10,7 @@ import com.seibel.distanthorizons.core.network.event.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.event.CloseEvent; import com.seibel.distanthorizons.core.network.messages.base.RemotePlayerConfigMessage; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataChunkMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPayload; import com.seibel.distanthorizons.core.network.session.Session; import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; @@ -84,6 +85,11 @@ public class ClientNetworkState implements Closeable composite.addComponent(true, msg.buffer); LOGGER.debug("Full data buffer {}: {}", msg.bufferId, composite); }); + + this.session.registerHandler(FullDataPartialUpdateMessage.class, msg -> + { + // Dummy handler to prevent unhandled message warnings + }); } public FullDataSourceV2DTO decodeDataSourceAndReleaseBuffer(FullDataPayload msg) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java index f1c0f3dd5..16a07eeff 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java @@ -102,7 +102,7 @@ public abstract class NetworkEventSource } } - if (!handled && ModInfo.IS_DEV_BUILD && message.warnWhenUnhandled()) + if (!handled && ModInfo.IS_DEV_BUILD) { LOGGER.warn("Unhandled message: " + message); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/ScopedNetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/ScopedNetworkEventSource.java index cf690aeb0..3d9ff50a8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/event/ScopedNetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/ScopedNetworkEventSource.java @@ -29,11 +29,15 @@ public final class ScopedNetworkEventSource extends NetworkEventSource public final NetworkEventSource parent; private boolean isClosed = false; + private final Consumer actualHandleMessageStable = this::handleMessage; + + public ScopedNetworkEventSource(NetworkEventSource parent) { this.parent = parent; } + @Override public void registerHandler(Class handlerClass, Consumer handlerImplementation) { @@ -42,9 +46,13 @@ public final class ScopedNetworkEventSource extends NetworkEventSource return; } - this.parent.registerHandler(this, handlerClass, this::handleMessage); + //noinspection unchecked + this.parent.registerHandler(this, handlerClass, (Consumer) this.actualHandleMessageStable); + + super.registerHandler(this, handlerClass, handlerImplementation); } + @Override public void close() { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/NetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/NetworkMessage.java index 64e684075..b71a298cf 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/NetworkMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/NetworkMessage.java @@ -10,8 +10,6 @@ public abstract class NetworkMessage implements INetworkObject public Session session = null; public IServerPlayerWrapper serverPlayer() { return this.session.serverPlayer; } - public boolean warnWhenUnhandled() { return true; } - public Session getConnection() { return this.session; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/TrackableMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/TrackableMessage.java index dc0d76bf2..5cc6166d1 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/TrackableMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/TrackableMessage.java @@ -39,7 +39,8 @@ public abstract class TrackableMessage extends NetworkMessage public long futureId = lastId.getAndIncrement() | ((Objects.requireNonNull(SharedApi.getEnvironment()) == EWorldEnvironment.Server_Only ? 1 : 0) << 31); - public void setSession(Session session) + @Override + public void setSession(Session session) { super.setSession(session); this.futureId |= (long) session.id << 32; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataChunkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataChunkMessage.java index 000715c74..fb9ff4c03 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataChunkMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataChunkMessage.java @@ -39,9 +39,6 @@ public class FullDataChunkMessage extends NetworkMessage } - @Override - public boolean warnWhenUnhandled() { return false; } - @Override public void encode(ByteBuf out) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java index 29bd25403..9612d222b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPartialUpdateMessage.java @@ -42,8 +42,6 @@ public class FullDataPartialUpdateMessage extends NetworkMessage implements ILev this.payload = payload; } - @Override - public boolean warnWhenUnhandled() { return false; } @Override public void encode(ByteBuf out) From ba3677b6419aaa53fed3045cbc8f3fc18c66ce54 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 10 Aug 2024 19:49:33 +0500 Subject: [PATCH 182/205] Make error handling somewhat work --- .../core/api/internal/ClientApi.java | 1 - .../core/level/DhServerLevel.java | 10 +-- .../server/RemotePlayerConnectionHandler.java | 5 +- .../network/event/NetworkEventSource.java | 2 +- .../network/messages/MessageRegistry.java | 2 +- .../core/network/messages/NetworkMessage.java | 8 +-- .../network/messages/TrackableMessage.java | 2 +- .../messages/base/CodecCrashMessage.java | 62 +++++++++++++++++++ .../core/network/session/Session.java | 15 ++++- 9 files changed, 87 insertions(+), 20 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CodecCrashMessage.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index 90b21502f..e6cec2d17 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -359,7 +359,6 @@ public class ClientApi Session session = this.pluginChannelApi.session; if (session != null) { - message.setSession(session); session.tryHandleMessage(message); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index c2b8290bb..437c4a930 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -175,7 +175,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel rateLimiterSet.loginDataSyncRCLimiter.release(); FullDataPayload payload = new FullDataPayload(fullDataSource); - payload.acceptInChunkMessages(FULL_DATA_CHUNK_SIZE, msg.session::sendMessage); + payload.acceptInChunkMessages(FULL_DATA_CHUNK_SIZE, msg.getSession()::sendMessage); msg.sendResponse(new FullDataSourceResponseMessage(payload)); }, executor); } @@ -227,8 +227,8 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel } // If player is not in this dimension and handling multiple dimensions at once is not allowed - assert msg.session.serverPlayer != null; - if (msg.session.serverPlayer.getLevel() != this.getLevelWrapper()) + assert msg.getSession().serverPlayer != null; + if (msg.getSession().serverPlayer.getLevel() != this.getLevelWrapper()) { // If the message can be replied to - reply with error, otherwise just ignore if (msg instanceof TrackableMessage) @@ -236,7 +236,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel ((TrackableMessage) msg).sendResponse(new InvalidLevelException(MessageFormat.format( "Generation not allowed. Requested dimension: {0}, player dimension: {1}, handler dimension: {2}", ((ILevelRelatedMessage) msg).getLevelName(), - msg.session.serverPlayer.getLevel().getDimensionName(), + msg.getSession().serverPlayer.getLevel().getDimensionName(), this.getLevelWrapper().getDimensionName() ))); } @@ -303,7 +303,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel } serverPlayerState.getRateLimiterSet(this).fullDataRequestConcurrencyLimiter.release(); - payload.acceptInChunkMessages(FULL_DATA_CHUNK_SIZE, msg.session::sendMessage); + payload.acceptInChunkMessages(FULL_DATA_CHUNK_SIZE, msg.getSession()::sendMessage); msg.sendResponse(new FullDataSourceResponseMessage(payload.retain())); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java index be1c9798f..92b15d501 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/RemotePlayerConnectionHandler.java @@ -20,9 +20,7 @@ public class RemotePlayerConnectionHandler ServerPlayerState playerState = this.connectedPlayers.get(player); if (playerState != null) { - Session session = playerState.session; - message.setSession(session); - session.tryHandleMessage(message); + playerState.session.tryHandleMessage(message); } else { @@ -51,7 +49,6 @@ public class RemotePlayerConnectionHandler Session session = state.session; for (NetworkMessage message : queuedMessages) { - message.setSession(session); session.tryHandleMessage(message); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java index c4dfd5288..73059d19a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java @@ -104,7 +104,7 @@ public abstract class NetworkEventSource if (!handled && ModInfo.IS_DEV_BUILD) { - LOGGER.warn("Unhandled message: " + message); + LOGGER.warn("Unhandled message: {}", message); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/MessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/MessageRegistry.java index aa4bcd2c3..1487c30da 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/MessageRegistry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/MessageRegistry.java @@ -70,7 +70,7 @@ public class MessageRegistry - protected void registerMessage(Class clazz, Supplier supplier) + public void registerMessage(Class clazz, Supplier supplier) { int id = this.idToSupplier.size() + 1; this.idToSupplier.put(id, supplier); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/NetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/NetworkMessage.java index b71a298cf..e0b2c85f1 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/NetworkMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/NetworkMessage.java @@ -7,20 +7,16 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapp public abstract class NetworkMessage implements INetworkObject { - public Session session = null; + private Session session = null; public IServerPlayerWrapper serverPlayer() { return this.session.serverPlayer; } - public Session getConnection() + public Session getSession() { return this.session; } public void setSession(Session connection) { - if (this.session != null) - { - throw new IllegalStateException("Session object cannot be changed after initialization."); - } this.session = connection; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/TrackableMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/TrackableMessage.java index 5cc6166d1..e46a15d82 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/TrackableMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/TrackableMessage.java @@ -49,7 +49,7 @@ public abstract class TrackableMessage extends NetworkMessage public void sendResponse(TrackableMessage responseMessage) { responseMessage.futureId = this.futureId; - this.session.sendMessage(responseMessage); + this.getSession().sendMessage(responseMessage); } public void sendResponse(Exception e) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CodecCrashMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CodecCrashMessage.java new file mode 100644 index 000000000..0f59ecba0 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/CodecCrashMessage.java @@ -0,0 +1,62 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2023 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.core.network.messages.base; + +import com.google.common.base.MoreObjects; +import com.seibel.distanthorizons.core.network.messages.NetworkMessage; +import io.netty.buffer.ByteBuf; + +public class CodecCrashMessage extends NetworkMessage +{ + public ECrashPhase crashPhase; + + public CodecCrashMessage() { } + public CodecCrashMessage(ECrashPhase crashPhase) { this.crashPhase = crashPhase; } + + @Override + public void encode(ByteBuf out) + { + if (this.crashPhase == ECrashPhase.ENCODE) + { + throw new RuntimeException("encode force crash"); + } + } + + @Override + public void decode(ByteBuf in) + { + throw new RuntimeException("decode force crash"); + } + + + @Override + public MoreObjects.ToStringHelper toStringHelper() + { + return super.toStringHelper() + .add("crashPhase", this.crashPhase); + } + + public enum ECrashPhase + { + ENCODE, + DECODE + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java b/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java index 16386bc70..c85e70f66 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java @@ -7,6 +7,7 @@ import com.seibel.distanthorizons.core.network.event.NetworkEventSource; import com.seibel.distanthorizons.core.network.event.CloseEvent; import com.seibel.distanthorizons.core.network.messages.NetworkMessage; import com.seibel.distanthorizons.core.network.messages.TrackableMessage; +import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import org.apache.logging.log4j.LogManager; @@ -42,6 +43,11 @@ public class Session extends NetworkEventSource public Session(@Nullable IServerPlayerWrapper serverPlayer) { this.serverPlayer = serverPlayer; + + this.registerHandler(CloseReasonMessage.class, msg -> + { + this.close(new SessionClosedException(msg.reason)); + }); } @@ -52,6 +58,8 @@ public class Session extends NetworkEventSource return; } + message.setSession(this); + try { LOGGER.debug("Received message: {}", message); @@ -86,8 +94,13 @@ public class Session extends NetworkEventSource public void sendMessage(NetworkMessage message) { - if (this.closeReason.get() != null) return; + if (this.closeReason.get() != null) + { + return; + } + LOGGER.debug("Sending message: {}", message); + message.setSession(this); try { From 606c1579587568855971434279912d984e66f4b2 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 10 Aug 2024 23:26:04 +0500 Subject: [PATCH 183/205] Make encode/decode error handling work correctly --- .../api/internal/ClientPluginChannelApi.java | 4 +++- .../multiplayer/client/ClientNetworkState.java | 2 +- .../multiplayer/server/ServerPlayerState.java | 4 ++-- .../core/network/event/NetworkEventSource.java | 13 ++++--------- .../core/network/event/ProtocolErrorEvent.java | 8 +++++--- .../network/event/ScopedNetworkEventSource.java | 6 +++--- .../core/network/messages/MessageRegistry.java | 10 ++++++++++ .../core/network/session/Session.java | 17 ++++++++++++++--- 8 files changed, 42 insertions(+), 22 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index a516a8138..d5b781fd8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -52,11 +52,13 @@ public class ClientPluginChannelApi Objects.requireNonNull(session); this.session = session; session.registerHandler(CurrentLevelKeyMessage.class, this::onCurrentLevelKeyMessage); - session.registerHandler(CloseEvent.class, false, this::onClose); + session.registerHandler(CloseEvent.class, this::onClose); } private void onCurrentLevelKeyMessage(CurrentLevelKeyMessage msg) { + // prefix@namespace:path + // 1-50 characters in total, all parts except namespace can be omitted if (!msg.levelKey.matches("^(?=.{1,50}$)([a-zA-Z0-9-_]+@)?[a-zA-Z0-9-_]+(:[a-zA-Z0-9-_]+)?$")) { throw new IllegalArgumentException("Server sent invalid level key."); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index 091475f97..130a5ca73 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -64,7 +64,7 @@ public class ClientNetworkState implements Closeable this.configReceived = true; }); - this.session.registerHandler(CloseEvent.class, false, msg -> + this.session.registerHandler(CloseEvent.class, msg -> { this.configReceived = false; }); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index d3a15c1e4..68c90ddde 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -53,8 +53,8 @@ public class ServerPlayerState this.session.sendMessage(new RemotePlayerConfigMessage(this.config)); }); - this.session.registerHandler(CloseEvent.class, false, event -> { - // Noop + this.session.registerHandler(CloseEvent.class, event -> { + // No-op. removes "Unhandled message" log entries }); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java index 73059d19a..8d63dfa38 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java @@ -108,16 +108,11 @@ public abstract class NetworkEventSource } } - public abstract void registerHandler(Class handlerClass, boolean throwIfMessageNotRegistered, Consumer handlerImplementation); - - public final void registerHandler(Class handlerClass, Consumer handlerImplementation) + public abstract void registerHandler(Class handlerClass, Consumer handlerImplementation); + + protected final void registerHandler(NetworkEventSource instance, Class handlerClass, Consumer handlerImplementation) { - this.registerHandler(handlerClass, true, handlerImplementation); - } - - protected final void registerHandler(NetworkEventSource instance, Class handlerClass, boolean throwIfMessageNotRegistered, Consumer handlerImplementation) - { - if (throwIfMessageNotRegistered) + if (!InternalEvent.class.isAssignableFrom(handlerClass)) { MessageRegistry.INSTANCE.getMessageId(handlerClass); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/ProtocolErrorEvent.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/ProtocolErrorEvent.java index be6ff4272..e3e167174 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/event/ProtocolErrorEvent.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/ProtocolErrorEvent.java @@ -8,14 +8,16 @@ import org.jetbrains.annotations.Nullable; */ public class ProtocolErrorEvent extends InternalEvent { - public final Throwable throwable; + public final Throwable reason; @Nullable public final NetworkMessage message; + public final boolean replyWithCloseReason; - public ProtocolErrorEvent(Throwable throwable, @Nullable NetworkMessage message) + public ProtocolErrorEvent(Throwable reason, @Nullable NetworkMessage message, boolean replyWithCloseReason) { - this.throwable = throwable; + this.reason = reason; this.message = message; + this.replyWithCloseReason = replyWithCloseReason; } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/ScopedNetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/ScopedNetworkEventSource.java index ddd7da67f..3d9ff50a8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/event/ScopedNetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/ScopedNetworkEventSource.java @@ -39,7 +39,7 @@ public final class ScopedNetworkEventSource extends NetworkEventSource @Override - public void registerHandler(Class handlerClass, boolean throwIfMessageNotRegistered, Consumer handlerImplementation) + public void registerHandler(Class handlerClass, Consumer handlerImplementation) { if (this.isClosed) { @@ -47,9 +47,9 @@ public final class ScopedNetworkEventSource extends NetworkEventSource } //noinspection unchecked - this.parent.registerHandler(this, handlerClass, throwIfMessageNotRegistered, (Consumer) this.actualHandleMessageStable); + this.parent.registerHandler(this, handlerClass, (Consumer) this.actualHandleMessageStable); - super.registerHandler(this, handlerClass, throwIfMessageNotRegistered, handlerImplementation); + super.registerHandler(this, handlerClass, handlerImplementation); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/MessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/MessageRegistry.java index 1487c30da..e0b8a7a85 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/MessageRegistry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/MessageRegistry.java @@ -21,6 +21,7 @@ package com.seibel.distanthorizons.core.network.messages; import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; +import com.seibel.distanthorizons.core.network.messages.base.CodecCrashMessage; import com.seibel.distanthorizons.core.network.messages.base.CurrentLevelKeyMessage; import com.seibel.distanthorizons.core.network.messages.base.RemotePlayerConfigMessage; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataChunkMessage; @@ -30,6 +31,7 @@ import com.seibel.distanthorizons.core.network.messages.requests.ExceptionMessag import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceResponseMessage; +import com.seibel.distanthorizons.coreapi.ModInfo; import java.util.HashMap; import java.util.Map; @@ -37,6 +39,7 @@ import java.util.function.Supplier; public class MessageRegistry { + public static final boolean DEBUG_ENABLE_CODEC_CRASH_MESSAGE = ModInfo.IS_DEV_BUILD; public static final MessageRegistry INSTANCE = new MessageRegistry(); private final Map> idToSupplier = new HashMap<>(); @@ -49,6 +52,7 @@ public class MessageRegistry // Note: Messages must have parameterless constructors // When the communication is about to be stopped, either side can send this message + // There may be messages after this, but they should be ignored if it's possible this.registerMessage(CloseReasonMessage.class, CloseReasonMessage::new); // Level keys @@ -66,6 +70,12 @@ public class MessageRegistry this.registerMessage(FullDataSourceResponseMessage.class, FullDataSourceResponseMessage::new); this.registerMessage(FullDataPartialUpdateMessage.class, FullDataPartialUpdateMessage::new); this.registerMessage(FullDataChunkMessage.class, FullDataChunkMessage::new); + + // Debug messages are always last, and not included into release builds. + if (DEBUG_ENABLE_CODEC_CRASH_MESSAGE) + { + this.registerMessage(CodecCrashMessage.class, CodecCrashMessage::new); + } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java b/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java index c85e70f66..6bd0215ed 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java @@ -5,6 +5,7 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.network.event.NetworkEventSource; import com.seibel.distanthorizons.core.network.event.CloseEvent; +import com.seibel.distanthorizons.core.network.event.ProtocolErrorEvent; import com.seibel.distanthorizons.core.network.messages.NetworkMessage; import com.seibel.distanthorizons.core.network.messages.TrackableMessage; import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage; @@ -48,6 +49,16 @@ public class Session extends NetworkEventSource { this.close(new SessionClosedException(msg.reason)); }); + + this.registerHandler(ProtocolErrorEvent.class, event -> + { + if (event.replyWithCloseReason) + { + this.sendMessage(new CloseReasonMessage("Internal error on other side")); + } + + this.close(event.reason); + }); } @@ -68,20 +79,20 @@ public class Session extends NetworkEventSource catch (Throwable e) { LOGGER.error("Failed to handle the message. New messages will be ignored.", e); - LOGGER.error("Message: " + message); + LOGGER.error("Message: {}", message); this.close(); } } @Override - public void registerHandler(Class handlerClass, boolean throwIfMessageNotRegistered, Consumer handlerImplementation) + public void registerHandler(Class handlerClass, Consumer handlerImplementation) { if (this.closeReason.get() != null) { return; } - this.registerHandler(this, handlerClass, throwIfMessageNotRegistered, handlerImplementation); + this.registerHandler(this, handlerClass, handlerImplementation); } public CompletableFuture sendRequest(TrackableMessage msg, Class responseClass) From 8c3e8136be9767d0fb9f8c96c248bd940d41d3f1 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 10 Aug 2024 23:53:22 +0500 Subject: [PATCH 184/205] Do not clear keyed level on close event --- .../core/api/internal/ClientPluginChannelApi.java | 2 +- .../core/multiplayer/client/ClientNetworkState.java | 2 +- .../core/multiplayer/server/ServerPlayerState.java | 2 +- .../core/network/event/NetworkEventSource.java | 1 + .../core/network/event/{ => internal}/CloseEvent.java | 2 +- .../core/network/event/{ => internal}/InternalEvent.java | 2 +- .../core/network/event/{ => internal}/ProtocolErrorEvent.java | 2 +- .../seibel/distanthorizons/core/network/session/Session.java | 4 ++-- 8 files changed, 9 insertions(+), 8 deletions(-) rename core/src/main/java/com/seibel/distanthorizons/core/network/event/{ => internal}/CloseEvent.java (62%) rename core/src/main/java/com/seibel/distanthorizons/core/network/event/{ => internal}/InternalEvent.java (89%) rename core/src/main/java/com/seibel/distanthorizons/core/network/event/{ => internal}/ProtocolErrorEvent.java (90%) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index d5b781fd8..e6fd29ad8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -5,7 +5,7 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager; import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.network.event.CloseEvent; +import com.seibel.distanthorizons.core.network.event.internal.CloseEvent; import com.seibel.distanthorizons.core.network.messages.base.CurrentLevelKeyMessage; import com.seibel.distanthorizons.core.network.session.Session; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index 130a5ca73..571611e67 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -7,7 +7,7 @@ import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; import com.seibel.distanthorizons.core.network.INetworkObject; import com.seibel.distanthorizons.core.network.event.ScopedNetworkEventSource; -import com.seibel.distanthorizons.core.network.event.CloseEvent; +import com.seibel.distanthorizons.core.network.event.internal.CloseEvent; import com.seibel.distanthorizons.core.network.messages.base.RemotePlayerConfigMessage; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataChunkMessage; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index 68c90ddde..2d798bf86 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -6,7 +6,7 @@ import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; import com.seibel.distanthorizons.core.network.messages.base.CurrentLevelKeyMessage; import com.seibel.distanthorizons.core.network.messages.base.RemotePlayerConfigMessage; -import com.seibel.distanthorizons.core.network.event.CloseEvent; +import com.seibel.distanthorizons.core.network.event.internal.CloseEvent; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage; import com.seibel.distanthorizons.core.network.session.Session; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java index 8d63dfa38..cfb7fae52 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/NetworkEventSource.java @@ -22,6 +22,7 @@ package com.seibel.distanthorizons.core.network.event; import com.google.common.cache.CacheBuilder; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; +import com.seibel.distanthorizons.core.network.event.internal.InternalEvent; import com.seibel.distanthorizons.core.network.messages.NetworkMessage; import com.seibel.distanthorizons.core.network.messages.TrackableMessage; import com.seibel.distanthorizons.core.network.messages.MessageRegistry; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/CloseEvent.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/internal/CloseEvent.java similarity index 62% rename from core/src/main/java/com/seibel/distanthorizons/core/network/event/CloseEvent.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/event/internal/CloseEvent.java index cbd3a1a31..93458c331 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/event/CloseEvent.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/internal/CloseEvent.java @@ -1,4 +1,4 @@ -package com.seibel.distanthorizons.core.network.event; +package com.seibel.distanthorizons.core.network.event.internal; /** * This event is used to indicate a disconnect. diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/InternalEvent.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/internal/InternalEvent.java similarity index 89% rename from core/src/main/java/com/seibel/distanthorizons/core/network/event/InternalEvent.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/event/internal/InternalEvent.java index 12bcf834e..09c8b8855 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/event/InternalEvent.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/internal/InternalEvent.java @@ -1,4 +1,4 @@ -package com.seibel.distanthorizons.core.network.event; +package com.seibel.distanthorizons.core.network.event.internal; import com.seibel.distanthorizons.core.network.messages.NetworkMessage; import io.netty.buffer.ByteBuf; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/ProtocolErrorEvent.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/internal/ProtocolErrorEvent.java similarity index 90% rename from core/src/main/java/com/seibel/distanthorizons/core/network/event/ProtocolErrorEvent.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/event/internal/ProtocolErrorEvent.java index e3e167174..01a2acaa6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/event/ProtocolErrorEvent.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/internal/ProtocolErrorEvent.java @@ -1,4 +1,4 @@ -package com.seibel.distanthorizons.core.network.event; +package com.seibel.distanthorizons.core.network.event.internal; import com.seibel.distanthorizons.core.network.messages.NetworkMessage; import org.jetbrains.annotations.Nullable; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java b/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java index 6bd0215ed..e38a45823 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/session/Session.java @@ -4,8 +4,8 @@ import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.network.event.NetworkEventSource; -import com.seibel.distanthorizons.core.network.event.CloseEvent; -import com.seibel.distanthorizons.core.network.event.ProtocolErrorEvent; +import com.seibel.distanthorizons.core.network.event.internal.CloseEvent; +import com.seibel.distanthorizons.core.network.event.internal.ProtocolErrorEvent; import com.seibel.distanthorizons.core.network.messages.NetworkMessage; import com.seibel.distanthorizons.core.network.messages.TrackableMessage; import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage; From b5e2019d28540e5b79c682b430befbadcec24df8 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Thu, 15 Aug 2024 15:45:31 +0500 Subject: [PATCH 185/205] Add logging of request group lifecycle --- .../core/level/DhServerLevel.java | 46 +++++++++++-------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index bcc701439..bd1ef565e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -19,40 +19,40 @@ package com.seibel.distanthorizons.core.level; +import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.file.fullDatafile.FullDataSourceProviderV2; -import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; -import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState; +import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.multiplayer.server.RemotePlayerConnectionHandler; +import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState; import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; import com.seibel.distanthorizons.core.network.messages.ILevelRelatedMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPayload; -import com.seibel.distanthorizons.core.network.messages.requests.CancelMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceResponseMessage; -import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage; import com.seibel.distanthorizons.core.network.messages.NetworkMessage; import com.seibel.distanthorizons.core.network.messages.TrackableMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPayload; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage; +import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceResponseMessage; +import com.seibel.distanthorizons.core.network.messages.requests.CancelMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; -import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhSectionPos; -import com.seibel.distanthorizons.core.util.LodUtil; -import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; -import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.render.RenderBufferHandler; import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer; +import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.core.util.math.Vec3d; +import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; +import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; -import com.seibel.distanthorizons.core.util.math.Vec3d; +import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +import javax.annotation.CheckForNull; import java.text.MessageFormat; import java.util.List; -import java.util.concurrent.CompletableFuture; - -import javax.annotation.CheckForNull; import java.util.Map; import java.util.concurrent.*; import java.util.function.Consumer; @@ -60,6 +60,9 @@ import java.util.function.Consumer; public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + private static final ConfigBasedLogger NETWORK_LOGGER = new ConfigBasedLogger(LogManager.getLogger(), + () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); + public static final int FULL_DATA_CHUNK_SIZE = 1048000; // 576 bytes left for other contents public final ServerLevelModule serverside; @@ -125,6 +128,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel { DataSourceRequestGroup newGroup = new DataSourceRequestGroup(); this.tryFulfillDataSourceRequestGroup(newGroup, pos); + NETWORK_LOGGER.debug("[{}] Created request group for pos {}", this.serverLevelWrapper.getDimensionName(), pos); return newGroup; }); @@ -201,6 +205,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel FullDataSourceRequestMessage requestMessage = requestGroup.requestMessages.remove(msg.futureId); if (requestGroup.requestMessages.isEmpty()) { + NETWORK_LOGGER.debug("[{}] Cancelled request group {}", this.serverLevelWrapper.getDimensionName(), requestMessage.sectionPos); this.requestGroupsByPos.remove(requestMessage.sectionPos); this.serverside.fullDataFileHandler.removeRetrievalRequestIf(pos -> pos == requestMessage.sectionPos); } @@ -248,15 +253,14 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel }; } - public void addPlayer(IServerPlayerWrapper serverPlayer) - { - this.worldGenPlayerCenteringQueue.add(serverPlayer); - } - //=========// // methods // //=========// + public void addPlayer(IServerPlayerWrapper serverPlayer) + { + this.worldGenPlayerCenteringQueue.add(serverPlayer); + } public void removePlayer(IServerPlayerWrapper serverPlayer) { this.worldGenPlayerCenteringQueue.remove(serverPlayer); @@ -275,6 +279,8 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel continue; } + NETWORK_LOGGER.debug("[{}] Fulfilled request group {}", this.serverLevelWrapper.getDimensionName(), entry.getKey()); + // Make this group unavailable for adding into this.requestGroupsByPos.remove(entry.getKey()); requestGroup.requestRemoveSemaphore.acquireUninterruptibly(Short.MAX_VALUE); From 81e6f55dbfb61299aa79a75112b751481a2da131 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 19 Aug 2024 17:55:59 +0500 Subject: [PATCH 186/205] Use same packet resource for all versions --- .../main/java/com/seibel/distanthorizons/coreapi/ModInfo.java | 1 - .../distanthorizons/core/network/messages/NetworkMessage.java | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java index 526c5e10e..54ab66f10 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java @@ -33,7 +33,6 @@ public final class ModInfo // region Protocol versions // Incremented every time any packets are added, changed or removed, with a few exceptions. public static final int PROTOCOL_VERSION = 3; - public static final String PLUGIN_CHANNEL_PATH = "main"; public static final String WRAPPER_PACKET_PATH = "message"; // endregion diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/NetworkMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/NetworkMessage.java index e0b2c85f1..f6034b59a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/NetworkMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/NetworkMessage.java @@ -15,9 +15,9 @@ public abstract class NetworkMessage implements INetworkObject return this.session; } - public void setSession(Session connection) + public void setSession(Session session) { - this.session = connection; + this.session = session; } From f17c3fa267b7c5f2a8223d42cdda6fd050732449 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 23 Aug 2024 14:16:20 +0500 Subject: [PATCH 187/205] "Fix" buffer release errors in FullDataPayload --- .../core/level/DhServerLevel.java | 62 +++++++++---------- .../core/network/INetworkObject.java | 2 +- .../messages/fullData/FullDataPayload.java | 31 +++++----- 3 files changed, 46 insertions(+), 49 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index bd1ef565e..e14272c28 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -294,22 +294,20 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel } CompletableFuture.runAsync(() -> { - try (FullDataPayload payload = new FullDataPayload(requestGroup.fullDataSource)) + FullDataPayload payload = new FullDataPayload(requestGroup.fullDataSource); + for (FullDataSourceRequestMessage msg : requestGroup.requestMessages.values()) { - for (FullDataSourceRequestMessage msg : requestGroup.requestMessages.values()) + this.requestGroupsByFutureId.remove(msg.futureId); + + ServerPlayerState serverPlayerState = this.remotePlayerConnectionHandler.getConnectedPlayer(msg.serverPlayer()); + if (serverPlayerState == null) { - this.requestGroupsByFutureId.remove(msg.futureId); - - ServerPlayerState serverPlayerState = this.remotePlayerConnectionHandler.getConnectedPlayer(msg.serverPlayer()); - if (serverPlayerState == null) - { - continue; - } - - serverPlayerState.getRateLimiterSet(this).fullDataRequestConcurrencyLimiter.release(); - payload.acceptInChunkMessages(FULL_DATA_CHUNK_SIZE, msg.getSession()::sendMessage); - msg.sendResponse(new FullDataSourceResponseMessage(payload.retain())); + continue; } + + serverPlayerState.getRateLimiterSet(this).fullDataRequestConcurrencyLimiter.release(); + payload.acceptInChunkMessages(FULL_DATA_CHUNK_SIZE, msg.getSession()::sendMessage); + msg.sendResponse(new FullDataSourceResponseMessage(payload)); } }, executor); } @@ -331,28 +329,26 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel } CompletableFuture.runAsync(() -> { - try (FullDataPayload payload = new FullDataPayload(data)) + FullDataPayload payload = new FullDataPayload(data); + for (ServerPlayerState serverPlayerState : this.remotePlayerConnectionHandler.getConnectedPlayers()) { - for (ServerPlayerState serverPlayerState : this.remotePlayerConnectionHandler.getConnectedPlayers()) + if (serverPlayerState.serverPlayer().getLevel() != this.serverLevelWrapper) { - if (serverPlayerState.serverPlayer().getLevel() != this.serverLevelWrapper) - { - continue; - } - - if (!serverPlayerState.config.isRealTimeUpdatesEnabled()) - { - continue; - } - - Vec3d playerPosition = serverPlayerState.serverPlayer().getPosition(); - int distanceFromPlayer = DhSectionPos.getManhattanBlockDistance(data.getPos(), new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)) / 16; - if (distanceFromPlayer >= serverPlayerState.serverPlayer().getViewDistance() && - distanceFromPlayer <= serverPlayerState.config.getRenderDistanceRadius()) - { - payload.acceptInChunkMessages(FULL_DATA_CHUNK_SIZE, serverPlayerState.session::sendMessage); - serverPlayerState.session.sendMessage(new FullDataPartialUpdateMessage(this.serverLevelWrapper, payload.retain())); - } + continue; + } + + if (!serverPlayerState.config.isRealTimeUpdatesEnabled()) + { + continue; + } + + Vec3d playerPosition = serverPlayerState.serverPlayer().getPosition(); + int distanceFromPlayer = DhSectionPos.getManhattanBlockDistance(data.getPos(), new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)) / 16; + if (distanceFromPlayer >= serverPlayerState.serverPlayer().getViewDistance() && + distanceFromPlayer <= serverPlayerState.config.getRenderDistanceRadius()) + { + payload.acceptInChunkMessages(FULL_DATA_CHUNK_SIZE, serverPlayerState.session::sendMessage); + serverPlayerState.session.sendMessage(new FullDataPartialUpdateMessage(this.serverLevelWrapper, payload)); } } }, executor); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/INetworkObject.java b/core/src/main/java/com/seibel/distanthorizons/core/network/INetworkObject.java index 4d81250e1..dccbb43b6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/INetworkObject.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/INetworkObject.java @@ -75,7 +75,7 @@ public interface INetworkObject default String readString(ByteBuf inputByteBuf) { int length = inputByteBuf.readUnsignedShort(); - return inputByteBuf.readBytes(length).toString(StandardCharsets.UTF_8); + return inputByteBuf.readSlice(length).toString(StandardCharsets.UTF_8); } default void writeCollection(ByteBuf outputByteBuf, Collection collection) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPayload.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPayload.java index fbc830b1a..32ccc0220 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPayload.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataPayload.java @@ -6,21 +6,26 @@ import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.network.INetworkObject; import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; +import com.seibel.distanthorizons.core.util.TimerUtil; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import org.jetbrains.annotations.NotNull; -import java.io.Closeable; import java.io.IOException; import java.util.Objects; +import java.util.Timer; +import java.util.TimerTask; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; -public class FullDataPayload implements INetworkObject, Closeable +public class FullDataPayload implements INetworkObject { private static final AtomicInteger lastBufferId = new AtomicInteger(); + // Reference counting is unreliable here for some reason so this is a "fix" + private static final Timer bufferCleanupTimer = TimerUtil.CreateTimer("FullDataBufferCleanupTimer"); + public int dtoBufferId; public ByteBuf dtoBuffer; @@ -39,6 +44,15 @@ public class FullDataPayload implements INetworkObject, Closeable this.dtoBuffer = ByteBufAllocator.DEFAULT.buffer(); dataSourceDto.encode(this.dtoBuffer); + + bufferCleanupTimer.schedule(new TimerTask() + { + @Override + public void run() + { + FullDataPayload.this.dtoBuffer.release(); + } + }, 5000L); } catch (IOException e) { @@ -64,24 +78,11 @@ public class FullDataPayload implements INetworkObject, Closeable } } - - public FullDataPayload retain() - { - this.dtoBuffer.retain(); - return this; - } - - @Override public void close() - { - this.dtoBuffer.release(); - } - @Override public void encode(ByteBuf out) { out.writeInt(this.dtoBufferId); - this.dtoBuffer.release(); } @Override From f4b0c08822fe5a63f6dde7bc87339cc19aa78052 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 23 Aug 2024 17:11:32 +0500 Subject: [PATCH 188/205] Fix network compression pool not shutting down --- .../distanthorizons/core/util/threading/ThreadPoolUtil.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPoolUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPoolUtil.java index 63b200dfc..d3ee146a4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPoolUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPoolUtil.java @@ -159,6 +159,7 @@ public class ThreadPoolUtil fileHandlerThreadPool.shutdownExecutorService(); updatePropagatorThreadPool.shutdownExecutorService(); worldGenThreadPool.shutdownExecutorService(); + networkCompressionThreadPool.shutdownExecutorService(); bufferUploaderThreadPool.shutdown(); cleanupThreadPool.shutdown(); From 39db421aa1d3e7a03707646873138fd0153d5d99 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 23 Aug 2024 17:28:15 +0500 Subject: [PATCH 189/205] Remove mentions of generation task priorities --- .../distanthorizons/core/config/Config.java | 19 ------------------- .../WorldRemoteGenerationQueue.java | 5 ----- .../MultiplayerConfigChangeListener.java | 1 - .../assets/distanthorizons/lang/en_us.json | 4 ---- 4 files changed, 29 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 4e786f94c..d3deec96c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -1021,25 +1021,6 @@ public class Config + "") .build(); - public static ConfigEntry genTaskPriorityDistanceRatio = new ConfigEntry.Builder() - .setServersideShortName("genTaskPriorityDistanceRatio") - .setMinDefaultMax(1d, 3d, 10d) - // todo: this comment should probably be rewritten, but it's outside my knowledge scope - // - yeshi - .comment("" - + "Controls the max ratio between distances of nearest unloaded sections of each priority.\n" - + "For example, value of 2 means that the nearest lower priority section will be allowed to stay\n" - + "unloaded only if it's at most 2x closer than one of a higher priority." - + "") - .build(); - public static ConfigEntry genTaskPriorityRequestRateLimit = new ConfigEntry.Builder() - .setServersideShortName("genTaskPriorityRequestRateLimit") - .setMinDefaultMax(1, 50, 200) - .comment("" - + "Limits the amount of LOD sections the client can request states for per second." - + "") - .build(); - public static ConfigUIComment realTimeUpdatesSectionNote = new ConfigUIComment(); public static ConfigEntry enableRealTimeUpdates = new ConfigEntry.Builder() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 0f2b9aa35..239a5eee1 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -45,11 +45,6 @@ public class WorldRemoteGenerationQueue extends AbstractFullDataRequestQueue imp return LodUtil.BLOCK_DETAIL_LEVEL; } - @Override - protected double getPriorityDistanceRatio() - { - return Config.Client.Advanced.Multiplayer.ServerNetworking.genTaskPriorityDistanceRatio.get(); - } @Override public CompletableFuture submitGenTask(long sectionPos, byte requiredDataDetail, IWorldGenTaskTracker tracker) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java index 07e4d0835..693f97353 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java @@ -14,7 +14,6 @@ public class MultiplayerConfigChangeListener implements Closeable Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius, Config.Client.Advanced.WorldGenerator.enableDistantGeneration, Config.Client.Advanced.Multiplayer.ServerNetworking.generationRequestRCLimit, - Config.Client.Advanced.Multiplayer.ServerNetworking.genTaskPriorityRequestRateLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates, Config.Client.Advanced.Multiplayer.ServerNetworking.enableLoginDataSync, Config.Client.Advanced.Multiplayer.ServerNetworking.loginDataSyncRCLimit, diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index 26e2565b1..a05a5d2ec 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -402,10 +402,6 @@ "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestRCLimit.@tooltip": "Limits the amount of generation requests sent by client and processed by server.", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestBeginDelay": "Generation Request Begin Delay", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestBeginDelay.@tooltip": "Adds a delay in seconds before sending LOD requests, when generation is enabled. \nIncrease this value if initial generation starts too far away.", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.genTaskPriorityDistanceRatio": "Gen Task Priority Distance Max Ratio", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.genTaskPriorityDistanceRatio.@tooltip": "Controls the max ratio between distances of nearest unloaded sections of each priority. \nFor example, a value of 2 means that the nearest lower priority section will be allowed to stay \nunloaded only if it's at most 2x closer than one of a higher priority.", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.genTaskPriorityRequestRateLimit": "Gen Task Priority Check Rate Limit", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.genTaskPriorityRequestRateLimit.@tooltip": "Limits the amount of LOD sections that the client can request states for, per second.", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.realTimeUpdatesSectionNote": " \u25cf Real Time Updates", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableRealTimeUpdates": "Enable Real Time Updates", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableRealTimeUpdates.@tooltip": "Enables real time updates from server.", From 4f19f05f3b5ead93d44549e561e6be0f4977a0b5 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 26 Aug 2024 00:39:28 +0500 Subject: [PATCH 190/205] Change naming of rate limit related items --- .../distanthorizons/core/config/Config.java | 24 +++++++++-------- .../WorldRemoteGenerationQueue.java | 2 +- .../core/level/DhServerLevel.java | 14 +++++----- .../client/AbstractFullDataRequestQueue.java | 8 +++--- .../client/FullDataRefreshQueue.java | 6 ++--- .../config/AbstractMultiplayerConfig.java | 18 ++++++------- .../multiplayer/config/MultiplayerConfig.java | 18 ++++++------- .../MultiplayerConfigChangeListener.java | 6 ++--- .../server/ConstrainedMultiplayerConfig.java | 12 ++++----- .../multiplayer/server/ServerPlayerState.java | 12 ++++----- .../assets/distanthorizons/lang/en_us.json | 27 +++++++------------ 11 files changed, 71 insertions(+), 76 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index d3deec96c..710bcc2c6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -165,7 +165,7 @@ public class Config .setMinDefaultMax(32, 128, 4096) .comment("" + "The radius of the mod's render distance. (measured in chunks)\n" + - "On server changes the distance players will receive real time updates for, if enabled." + + "On server changes the distance players will receive real-time updates for, if enabled." + "\n" + "Note for servers:\n" + "This setting does not prevent players from generating farther out.\n" + @@ -1013,11 +1013,12 @@ public class Config public static ConfigUIComment generationSectionNote = new ConfigUIComment(); - public static ConfigEntry generationRequestRCLimit = new ConfigEntry.Builder() - .setServersideShortName("generationRequestRCLimit") + public static ConfigEntry generationRequestRateLimit = new ConfigEntry.Builder() + .setServersideShortName("generationRequestRateLimit") .setMinDefaultMax(1, 20, 100) .comment("" - + "Limits the amount of generation requests the server will handle." + + "How many LOD generation requests per second should a client send? \n" + + "Also limits the amount of player's requests allowed to stay in the server's queue." + "") .build(); @@ -1032,20 +1033,21 @@ public class Config .build(); - public static ConfigUIComment loginDataSyncSectionNote = new ConfigUIComment(); - public static ConfigEntry enableLoginDataSync = new ConfigEntry.Builder() - .setServersideShortName("enableLoginDataSync") + public static ConfigUIComment syncOnLoginSectionNote = new ConfigUIComment(); + public static ConfigEntry synchronizeOnLogin = new ConfigEntry.Builder() + .setServersideShortName("synchronizeOnLogin") .set(false) .comment("" - + "If true, clients will receive updated LODs on join if any changes occured since last join." + + "If true, clients will receive updated LODs on join if any changes occurred since last join." + "") .build(); - public static ConfigEntry loginDataSyncRCLimit = new ConfigEntry.Builder() - .setServersideShortName("loginDataSyncRCLimit") + public static ConfigEntry syncOnLoginRateLimit = new ConfigEntry.Builder() + .setServersideShortName("syncOnLoginRateLimit") .setMinDefaultMax(1, 50, 100) .comment("" - + "Limits the amount of sent/processed LOD *update* requests concurrently, per player." + + "How many LOD sync requests per second should a client send? \n" + + "Also limits the amount of player's requests allowed to stay in the server's queue." + "") .build(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 239a5eee1..eb7eba17e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -22,7 +22,7 @@ public class WorldRemoteGenerationQueue extends AbstractFullDataRequestQueue imp @Override - protected int getRequestConcurrencyLimit() { return this.networkState.config.fullDataRequestConcurrencyLimit; } + protected int getRequestRateLimit() { return this.networkState.config.generationRequestRateLimit; } @Override protected String getQueueName() { return "World Remote Generation Queue"; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index e14272c28..84f577c08 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -117,7 +117,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel return; } - if (!rateLimiterSet.fullDataRequestConcurrencyLimiter.tryAcquire(msg)) + if (!rateLimiterSet.generationRequestRateLimiter.tryAcquire(msg)) { return; } @@ -149,13 +149,13 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel { // Sync only - if (!serverPlayerState.config.isLoginDataSyncEnabled()) + if (!serverPlayerState.config.getSynchronizeOnLogin()) { msg.sendResponse(new RequestRejectedException("Operation is disabled from config.")); return; } - if (!rateLimiterSet.loginDataSyncRCLimiter.tryAcquire(msg)) + if (!rateLimiterSet.syncOnLoginRateLimiter.tryAcquire(msg)) { return; } @@ -163,7 +163,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel Long serverTimestamp = this.serverside.fullDataFileHandler.getTimestampForPos(msg.sectionPos); if (serverTimestamp == null || serverTimestamp <= msg.clientTimestamp) { - rateLimiterSet.loginDataSyncRCLimiter.release(); + rateLimiterSet.syncOnLoginRateLimiter.release(); msg.sendResponse(new FullDataSourceResponseMessage(null)); return; } @@ -176,7 +176,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel } this.serverside.fullDataFileHandler.getAsync(msg.sectionPos).thenAcceptAsync(fullDataSource -> { - rateLimiterSet.loginDataSyncRCLimiter.release(); + rateLimiterSet.syncOnLoginRateLimiter.release(); FullDataPayload payload = new FullDataPayload(fullDataSource); payload.acceptInChunkMessages(FULL_DATA_CHUNK_SIZE, msg.getSession()::sendMessage); @@ -200,7 +200,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel requestGroup.requestAddSemaphore.acquireUninterruptibly(Short.MAX_VALUE); requestGroup.requestRemoveSemaphore.release(); - serverPlayerState.getRateLimiterSet(this).fullDataRequestConcurrencyLimiter.release(); + serverPlayerState.getRateLimiterSet(this).generationRequestRateLimiter.release(); FullDataSourceRequestMessage requestMessage = requestGroup.requestMessages.remove(msg.futureId); if (requestGroup.requestMessages.isEmpty()) @@ -305,7 +305,7 @@ public class DhServerLevel extends AbstractDhLevel implements IDhServerLevel continue; } - serverPlayerState.getRateLimiterSet(this).fullDataRequestConcurrencyLimiter.release(); + serverPlayerState.getRateLimiterSet(this).generationRequestRateLimiter.release(); payload.acceptInChunkMessages(FULL_DATA_CHUNK_SIZE, msg.getSession()::sendMessage); msg.sendResponse(new FullDataSourceResponseMessage(payload)); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index 7455b9b15..aea3b836d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -54,7 +54,7 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, private final AtomicInteger failedRequests = new AtomicInteger(); private final ConfigEntry showDebugWireframeConfig; - private final SupplierBasedRateLimiter rateLimiter = new SupplierBasedRateLimiter<>(this::getRequestConcurrencyLimit); + private final SupplierBasedRateLimiter rateLimiter = new SupplierBasedRateLimiter<>(this::getRequestRateLimit); private final ScheduledExecutorService taskFinishScheduler = Executors.newScheduledThreadPool(1); @@ -62,7 +62,7 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, @SuppressWarnings("BooleanMethodIsAlwaysInverted") protected boolean showInDebug() { return true; } - protected abstract int getRequestConcurrencyLimit(); + protected abstract int getRequestRateLimit(); protected abstract String getQueueName(); @@ -104,7 +104,7 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, } while (this.getWaitingTaskCount() > this.getInProgressTaskCount() - && this.getInProgressTaskCount() < this.getRequestConcurrencyLimit() + && this.getInProgressTaskCount() < this.getRequestRateLimit() && this.pendingTasksSemaphore.tryAcquire()) { if (!this.rateLimiter.tryAcquire()) @@ -244,7 +244,7 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, } messageList.add(this.getQueueName() + " [" + this.level.getClientLevelWrapper().getDimensionName() + "]"); - messageList.add("Requests: " + this.finishedRequests + " / " + (this.getWaitingTaskCount() + this.finishedRequests.get()) + " (failed: " + this.failedRequests + ", rate limit: " + this.getRequestConcurrencyLimit() + ")"); + messageList.add("Requests: " + this.finishedRequests + " / " + (this.getWaitingTaskCount() + this.finishedRequests.get()) + " (failed: " + this.failedRequests + ", rate limit: " + this.getRequestRateLimit() + ")"); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/FullDataRefreshQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/FullDataRefreshQueue.java index 58bc21d9a..17a51ad0c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/FullDataRefreshQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/FullDataRefreshQueue.java @@ -12,10 +12,10 @@ public class FullDataRefreshQueue extends AbstractFullDataRequestQueue } @Override - protected boolean showInDebug() { return this.networkState.config.loginDataSyncEnabled; } + protected boolean showInDebug() { return this.networkState.config.synchronizeOnLogin; } @Override - protected int getRequestConcurrencyLimit() { return this.networkState.config.loginDataSyncRCLimit; } + protected int getRequestRateLimit() { return this.networkState.config.syncOnLoginRateLimit; } @Override protected String getQueueName() { return "Data Refresh Queue"; } @@ -23,7 +23,7 @@ public class FullDataRefreshQueue extends AbstractFullDataRequestQueue @Override public boolean tick(DhBlockPos2D targetPos) { - if (!this.networkState.config.loginDataSyncEnabled) + if (!this.networkState.config.synchronizeOnLogin) { return false; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java index 444a53464..6bda9948e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java @@ -8,20 +8,20 @@ public abstract class AbstractMultiplayerConfig implements INetworkObject { public abstract int getRenderDistanceRadius(); public abstract boolean isDistantGenerationEnabled(); - public abstract int getFullDataRequestConcurrencyLimit(); + public abstract int getGenerationRequestRateLimit(); public abstract boolean isRealTimeUpdatesEnabled(); - public abstract boolean isLoginDataSyncEnabled(); - public abstract int getLoginDataSyncRCLimit(); + public abstract boolean getSynchronizeOnLogin(); + public abstract int getSyncOnLoginRateLimit(); @Override public void encode(ByteBuf out) { out.writeInt(this.getRenderDistanceRadius()); out.writeBoolean(this.isDistantGenerationEnabled()); - out.writeInt(this.getFullDataRequestConcurrencyLimit()); + out.writeInt(this.getGenerationRequestRateLimit()); out.writeBoolean(this.isRealTimeUpdatesEnabled()); - out.writeBoolean(this.isLoginDataSyncEnabled()); - out.writeInt(this.getLoginDataSyncRCLimit()); + out.writeBoolean(this.getSynchronizeOnLogin()); + out.writeInt(this.getSyncOnLoginRateLimit()); } @@ -31,10 +31,10 @@ public abstract class AbstractMultiplayerConfig implements INetworkObject return MoreObjects.toStringHelper(this) .add("renderDistanceRadius", this.getRenderDistanceRadius()) .add("distantGenerationEnabled", this.isDistantGenerationEnabled()) - .add("fullDataRequestConcurrencyLimit", this.getFullDataRequestConcurrencyLimit()) + .add("generationRequestRateLimit", this.getGenerationRequestRateLimit()) .add("realTimeUpdatesEnabled", this.isRealTimeUpdatesEnabled()) - .add("loginDataSyncEnabled", this.isLoginDataSyncEnabled()) - .add("loginDataSyncRCLimit", this.getLoginDataSyncRCLimit()) + .add("synchronizeOnLogin", this.getSynchronizeOnLogin()) + .add("syncOnLoginRateLimit", this.getSyncOnLoginRateLimit()) .toString(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java index 9fef9e5d4..03f479cc1 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java @@ -13,17 +13,17 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig public boolean distantGenerationEnabled = Config.Client.Advanced.WorldGenerator.enableDistantGeneration.get(); @Override public boolean isDistantGenerationEnabled() { return this.distantGenerationEnabled; } - public int fullDataRequestConcurrencyLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.generationRequestRCLimit.get(); - @Override public int getFullDataRequestConcurrencyLimit() { return this.fullDataRequestConcurrencyLimit; } + public int generationRequestRateLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.generationRequestRateLimit.get(); + @Override public int getGenerationRequestRateLimit() { return this.generationRequestRateLimit; } public boolean realTimeUpdatesEnabled = Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates.get(); @Override public boolean isRealTimeUpdatesEnabled() { return this.realTimeUpdatesEnabled; } - public boolean loginDataSyncEnabled = Config.Client.Advanced.Multiplayer.ServerNetworking.enableLoginDataSync.get(); - @Override public boolean isLoginDataSyncEnabled() { return this.loginDataSyncEnabled; } + public boolean synchronizeOnLogin = Config.Client.Advanced.Multiplayer.ServerNetworking.synchronizeOnLogin.get(); + @Override public boolean getSynchronizeOnLogin() { return this.synchronizeOnLogin; } - public int loginDataSyncRCLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.loginDataSyncRCLimit.get(); - @Override public int getLoginDataSyncRCLimit() { return this.loginDataSyncRCLimit; } + public int syncOnLoginRateLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.syncOnLoginRateLimit.get(); + @Override public int getSyncOnLoginRateLimit() { return this.syncOnLoginRateLimit; } @Override @@ -31,10 +31,10 @@ public class MultiplayerConfig extends AbstractMultiplayerConfig { this.renderDistanceRadius = in.readInt(); this.distantGenerationEnabled = in.readBoolean(); - this.fullDataRequestConcurrencyLimit = in.readInt(); + this.generationRequestRateLimit = in.readInt(); this.realTimeUpdatesEnabled = in.readBoolean(); - this.loginDataSyncEnabled = in.readBoolean(); - this.loginDataSyncRCLimit = in.readInt(); + this.synchronizeOnLogin = in.readBoolean(); + this.syncOnLoginRateLimit = in.readInt(); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java index 693f97353..e035cfbf9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java @@ -13,10 +13,10 @@ public class MultiplayerConfigChangeListener implements Closeable private static final ConfigEntry[] CONFIG_ENTRIES = new ConfigEntry[] { Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius, Config.Client.Advanced.WorldGenerator.enableDistantGeneration, - Config.Client.Advanced.Multiplayer.ServerNetworking.generationRequestRCLimit, + Config.Client.Advanced.Multiplayer.ServerNetworking.generationRequestRateLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates, - Config.Client.Advanced.Multiplayer.ServerNetworking.enableLoginDataSync, - Config.Client.Advanced.Multiplayer.ServerNetworking.loginDataSyncRCLimit, + Config.Client.Advanced.Multiplayer.ServerNetworking.synchronizeOnLogin, + Config.Client.Advanced.Multiplayer.ServerNetworking.syncOnLoginRateLimit, }; private final ArrayList changeListeners; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ConstrainedMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ConstrainedMultiplayerConfig.java index 544cc3963..6ab030b82 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ConstrainedMultiplayerConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ConstrainedMultiplayerConfig.java @@ -25,9 +25,9 @@ public class ConstrainedMultiplayerConfig extends AbstractMultiplayerConfig } @Override - public int getFullDataRequestConcurrencyLimit() + public int getGenerationRequestRateLimit() { - return Math.min(this.clientConfig.fullDataRequestConcurrencyLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.generationRequestRCLimit.get()); + return Math.min(this.clientConfig.generationRequestRateLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.generationRequestRateLimit.get()); } @Override @@ -37,15 +37,15 @@ public class ConstrainedMultiplayerConfig extends AbstractMultiplayerConfig } @Override - public boolean isLoginDataSyncEnabled() + public boolean getSynchronizeOnLogin() { - return this.clientConfig.loginDataSyncEnabled && Config.Client.Advanced.Multiplayer.ServerNetworking.enableLoginDataSync.get(); + return this.clientConfig.synchronizeOnLogin && Config.Client.Advanced.Multiplayer.ServerNetworking.synchronizeOnLogin.get(); } @Override - public int getLoginDataSyncRCLimit() + public int getSyncOnLoginRateLimit() { - return Math.min(this.clientConfig.loginDataSyncRCLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.loginDataSyncRCLimit.get()); + return Math.min(this.clientConfig.syncOnLoginRateLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.syncOnLoginRateLimit.get()); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index 2d798bf86..106498ac7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -88,17 +88,17 @@ public class ServerPlayerState public class RateLimiterSet { - public final SupplierBasedRateAndConcurrencyLimiter fullDataRequestConcurrencyLimiter = new SupplierBasedRateAndConcurrencyLimiter<>( - () -> ServerNetworking.generationRequestRCLimit.get(), + public final SupplierBasedRateAndConcurrencyLimiter generationRequestRateLimiter = new SupplierBasedRateAndConcurrencyLimiter<>( + () -> ServerNetworking.generationRequestRateLimit.get(), msg -> { - msg.sendResponse(new RateLimitedException("Full data request rate/concurrency limit: " + ServerPlayerState.this.config.getFullDataRequestConcurrencyLimit())); + msg.sendResponse(new RateLimitedException("Full data request rate/concurrency limit: " + ServerPlayerState.this.config.getGenerationRequestRateLimit())); } ); - public final SupplierBasedRateAndConcurrencyLimiter loginDataSyncRCLimiter = new SupplierBasedRateAndConcurrencyLimiter<>( - () -> ServerNetworking.loginDataSyncRCLimit.get(), + public final SupplierBasedRateAndConcurrencyLimiter syncOnLoginRateLimiter = new SupplierBasedRateAndConcurrencyLimiter<>( + () -> ServerNetworking.syncOnLoginRateLimit.get(), msg -> { - msg.sendResponse(new RateLimitedException("Data sync rate/concurrency limit: " + ServerPlayerState.this.config.getLoginDataSyncRCLimit())); + msg.sendResponse(new RateLimitedException("Data sync rate/concurrency limit: " + ServerPlayerState.this.config.getSyncOnLoginRateLimit())); } ); diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index a05a5d2ec..e8eb64b03 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -395,24 +395,17 @@ "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generalSectionNote": " \u25cf General", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableServerNetworking": "Enable Server Networking", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableServerNetworking.@tooltip": "§6Attention:§r this feature is not fully implemented. \n\nIf true Distant Horizons will attempt to communicate with the connected \nserver in order to load LODs outside your vanilla render distance. \n\nNote: This requires DH to be installed on the server in order to function.", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.maxThreadPoolTasks": "Max Thread Pool Tasks", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.maxThreadPoolTasks.@tooltip": "Pauses the generation if amount of thread pool tasks is too high. \nDecrease this value if LODs don't show up for too long.\nIncrease this value if thread pool tasks often get drained completely.\nSetting this value to 0 will allow thread pool tasks to build up indefinitely.", "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationSectionNote": " \u25cf Generation", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestRCLimit": "Gen Task Rate/Concurrency Limit", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestRCLimit.@tooltip": "Limits the amount of generation requests sent by client and processed by server.", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestBeginDelay": "Generation Request Begin Delay", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestBeginDelay.@tooltip": "Adds a delay in seconds before sending LOD requests, when generation is enabled. \nIncrease this value if initial generation starts too far away.", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.realTimeUpdatesSectionNote": " \u25cf Real Time Updates", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableRealTimeUpdates": "Enable Real Time Updates", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableRealTimeUpdates.@tooltip": "Enables real time updates from server.", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.loginDataSyncSectionNote": " \u25cf Login Data Sync", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableLoginDataSync": "Synchronize LODs on Login", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableLoginDataSync.@tooltip": "Enables updating of saved LODs after login.", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.loginDataSyncRCLimit": "Login Sync Rate/Concurrency Limit", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.loginDataSyncRCLimit.@tooltip": "Limits the amount of sent/processed LOD *update* requests concurrently, per player.", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.immersivePortalsSectionNote": " \u25cf Immersive Portals", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generateMultipleDimensions": "Generate Multiple Dimensions", - "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generateMultipleDimensions.@tooltip": "Controls whether the players should be allowed to generate in multiple dimensions at once. \n\nFor server owners: \nAll dimensions share the same thread pool. This means that empty dimensions (i.e. without players in them) \nmay slow down the generation of non-empty ones.", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.generationRequestRateLimit": "Rate Limit for Generation Requests", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.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.", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.realTimeUpdatesSectionNote": " \u25cf Real-time Updates", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableRealTimeUpdates": "Enable Real-time Updates", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.enableRealTimeUpdates.@tooltip": "If true, the client will receive real-time LOD updates for chunks outside the client's render distance.", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.syncOnLoginSectionNote": " \u25cf Synchronization on Login", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.synchronizeOnLogin": "Synchronize LODs on Login", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.synchronizeOnLogin.@tooltip": "If true, clients will receive updated LODs on join if any changes occurred since last join.", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.syncOnLoginRateLimit": "Rate Limit for Sync on Login", + "distanthorizons.config.client.advanced.multiplayer.serverNetworking.syncOnLoginRateLimit.@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.", "distanthorizons.config.client.advanced.multiplayer.serverFolderNameMode": "Server Folder Mode", "distanthorizons.config.client.advanced.multiplayer.serverFolderNameMode.@tooltip": From 01c879951c86391d0fbea339f15de378c790e6fc Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 26 Aug 2024 16:53:59 +0500 Subject: [PATCH 191/205] Fix sync on login --- .../RemoteFullDataSourceProvider.java | 23 +++--- .../core/level/DhClientLevel.java | 22 ++++-- .../client/AbstractFullDataRequestQueue.java | 70 +++++-------------- ...ueue.java => SyncOnLoginRequestQueue.java} | 7 +- 4 files changed, 48 insertions(+), 74 deletions(-) rename core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/{FullDataRefreshQueue.java => SyncOnLoginRequestQueue.java} (73%) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataSourceProvider.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataSourceProvider.java index c9db0136a..9d607ed47 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataSourceProvider.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataSourceProvider.java @@ -21,24 +21,26 @@ package com.seibel.distanthorizons.core.file.fullDatafile; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; -import com.seibel.distanthorizons.core.level.IDhClientLevel; import com.seibel.distanthorizons.core.level.IDhLevel; -import com.seibel.distanthorizons.core.multiplayer.client.FullDataRefreshQueue; +import com.seibel.distanthorizons.core.multiplayer.client.SyncOnLoginRequestQueue; import com.seibel.distanthorizons.core.pos.DhSectionPos; import org.jetbrains.annotations.Nullable; import java.io.File; import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; public class RemoteFullDataSourceProvider extends GeneratedFullDataSourceProvider { @Nullable - private final FullDataRefreshQueue dataRefreshQueue; + private final SyncOnLoginRequestQueue syncOnLoginRequestQueue; + private final Set finishedTaskPositions = ConcurrentHashMap.newKeySet(); - public RemoteFullDataSourceProvider(IDhLevel level, AbstractSaveStructure saveStructure, @Nullable File saveDirOverride, @Nullable FullDataRefreshQueue dataRefreshQueue) + public RemoteFullDataSourceProvider(IDhLevel level, AbstractSaveStructure saveStructure, @Nullable File saveDirOverride, @Nullable SyncOnLoginRequestQueue syncOnLoginRequestQueue) { super(level, saveStructure, saveDirOverride); - this.dataRefreshQueue = dataRefreshQueue; + this.syncOnLoginRequestQueue = syncOnLoginRequestQueue; } @Override @@ -46,7 +48,7 @@ public class RemoteFullDataSourceProvider extends GeneratedFullDataSourceProvide public FullDataSourceV2 get(long pos) { FullDataSourceV2 fullDataSource = super.get(pos); - if (fullDataSource == null || this.dataRefreshQueue == null) + if (fullDataSource == null || this.syncOnLoginRequestQueue == null) { return fullDataSource; } @@ -61,7 +63,10 @@ public class RemoteFullDataSourceProvider extends GeneratedFullDataSourceProvide ); for (Map.Entry entry : timestamps.entrySet()) { - this.dataRefreshQueue.submitRequest(entry.getKey(), entry.getValue(), this.delayedFullDataSourceSaveCache::queueDataSourceForUpdateAndSave); + if (this.finishedTaskPositions.add(entry.getKey())) + { + this.syncOnLoginRequestQueue.submitRequest(entry.getKey(), entry.getValue(), this.delayedFullDataSourceSaveCache::queueDataSourceForUpdateAndSave); + } } return fullDataSource; @@ -70,9 +75,9 @@ public class RemoteFullDataSourceProvider extends GeneratedFullDataSourceProvide @Override public void close() { - if (this.dataRefreshQueue != null) + if (this.syncOnLoginRequestQueue != null) { - this.dataRefreshQueue.close(); + this.syncOnLoginRequestQueue.close(); } super.close(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index b6dfc266b..8eb62a033 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -30,7 +30,7 @@ import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.generation.WorldRemoteGenerationQueue; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; -import com.seibel.distanthorizons.core.multiplayer.client.FullDataRefreshQueue; +import com.seibel.distanthorizons.core.multiplayer.client.SyncOnLoginRequestQueue; import com.seibel.distanthorizons.core.network.event.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage; import com.seibel.distanthorizons.core.pos.DhBlockPos; @@ -82,7 +82,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel public final AppliedConfigState worldGeneratorEnabledConfig; @Nullable - private final FullDataRefreshQueue dataRefreshQueue; + private final SyncOnLoginRequestQueue syncOnLoginRequestQueue; @@ -105,16 +105,16 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel if (networkState != null) { this.eventSource = new ScopedNetworkEventSource(networkState.getSession()); - this.dataRefreshQueue = new FullDataRefreshQueue(this, networkState); + this.syncOnLoginRequestQueue = new SyncOnLoginRequestQueue(this, networkState); this.registerNetworkHandlers(); } else { this.eventSource = null; - this.dataRefreshQueue = null; + this.syncOnLoginRequestQueue = null; } - this.dataFileHandler = new RemoteFullDataSourceProvider(this, saveStructure, fullDataSaveDirOverride, this.dataRefreshQueue); + this.dataFileHandler = new RemoteFullDataSourceProvider(this, saveStructure, fullDataSaveDirOverride, this.syncOnLoginRequestQueue); this.worldGeneratorEnabledConfig = new AppliedConfigState<>(Config.Client.Advanced.WorldGenerator.enableDistantGeneration); this.worldGenModule = new WorldGenModule(this); @@ -166,9 +166,9 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel { this.clientside.clientTick(); - if (this.dataRefreshQueue != null) + if (this.syncOnLoginRequestQueue != null) { - this.dataRefreshQueue.tick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); + this.syncOnLoginRequestQueue.tick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); } } catch (Exception e) @@ -286,6 +286,14 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel // world gen this.worldGenModule.addDebugMenuStringsToList(messageList); + if (this.syncOnLoginRequestQueue != null) + { + assert this.networkState != null; + if (this.networkState.config.synchronizeOnLogin) + { + this.syncOnLoginRequestQueue.addDebugMenuStringsToList(messageList); + } + } } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index aea3b836d..be986fc1b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -19,6 +19,7 @@ import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; 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.TimerUtil; import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateLimiter; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import org.apache.logging.log4j.LogManager; @@ -28,6 +29,8 @@ import javax.annotation.Nullable; import java.awt.*; import java.util.List; import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; import java.util.concurrent.*; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; @@ -39,6 +42,8 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); + private static final Timer TASK_FINISH_TIMER = TimerUtil.CreateTimer("RequestTaskFinishTimer"); + protected static final long SHUTDOWN_TIMEOUT_SECONDS = 5; public final ClientNetworkState networkState; @@ -56,18 +61,11 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, private final SupplierBasedRateLimiter rateLimiter = new SupplierBasedRateLimiter<>(this::getRequestRateLimit); - private final ScheduledExecutorService taskFinishScheduler = Executors.newScheduledThreadPool(1); - - - @SuppressWarnings("BooleanMethodIsAlwaysInverted") - protected boolean showInDebug() { return true; } protected abstract int getRequestRateLimit(); protected abstract String getQueueName(); - protected double getPriorityDistanceRatio() { return 1; } - public AbstractFullDataRequestQueue(ClientNetworkState networkState, IDhClientLevel level, boolean changedOnly, ConfigEntry showDebugWireframeConfig) { @@ -91,11 +89,6 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, return entry.future; } - protected int posDistanceSquared(DhBlockPos2D targetPos, long pos) - { - return (int) DhSectionPos.getCenterBlockPos(pos).distSquared(targetPos); - } - public synchronized boolean tick(DhBlockPos2D targetPos) { if (this.closingFuture != null || !this.networkState.isReady()) @@ -113,7 +106,7 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, break; } - this.sendNewRequest(targetPos); + this.sendNewRequest(); } return true; @@ -139,28 +132,11 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, } } - private void sendNewRequest(DhBlockPos2D targetPos) + private void sendNewRequest() { Map.Entry mapEntry = this.waitingTasks.entrySet().stream() .filter(task -> task.getValue().request == null) - .reduce(null, (a, b) -> { - if (a == null) - { - return b; - } - - if (b.getValue().priority < a.getValue().priority) - { - Map.Entry temp = b; - b = a; - a = temp; - } - - double distanceRatio = Math.sqrt(this.posDistanceSquared(targetPos, b.getKey())) / Math.sqrt(this.posDistanceSquared(targetPos, a.getKey())); - double maxDistanceRatioScaled = Math.pow(this.getPriorityDistanceRatio(), b.getValue().priority - a.getValue().priority); - - return distanceRatio < maxDistanceRatioScaled ? b : a; - }); + .findAny().orElse(null); if (mapEntry == null) { @@ -199,7 +175,7 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, } else { - LodUtil.assertTrue(this.changedOnly, "Received empty data source response for not changed-only request"); + LodUtil.assertTrue(this.changedOnly, "Received empty data source response for not changes-only request"); } } catch (InvalidLevelException | RequestRejectedException ignored) @@ -229,20 +205,20 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, // Hack to work around a race condition // If you finish the request too quickly, the section will never render - this.taskFinishScheduler.schedule(() -> { + TASK_FINISH_TIMER.schedule(new TimerTask() + { + @Override + public void run() + { entry.future.complete(true); - }, 10, TimeUnit.SECONDS); + } + }, 10000); return null; }); } public void addDebugMenuStringsToList(List messageList) { - if (!this.showInDebug()) - { - return; - } - messageList.add(this.getQueueName() + " [" + this.level.getClientLevelWrapper().getDimensionName() + "]"); messageList.add("Requests: " + this.finishedRequests + " / " + (this.getWaitingTaskCount() + this.finishedRequests.get()) + " (failed: " + this.failedRequests + ", rate limit: " + this.getRequestRateLimit() + ")"); } @@ -287,11 +263,6 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, @Override public void debugRender(DebugRenderer r) { - if (!this.showInDebug()) - { - return; - } - if (MC_CLIENT.getWrappedClientLevel() != this.level.getClientLevelWrapper()) { return; @@ -300,11 +271,7 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, for (Map.Entry mapEntry : this.waitingTasks.entrySet()) { r.renderBox(new DebugRenderer.Box(mapEntry.getKey(), -32f, 64f, 0.05f, - mapEntry.getValue().request != null ? Color.red - : mapEntry.getValue().priority == 3 ? Color.orange - : mapEntry.getValue().priority == 2 ? Color.cyan - : mapEntry.getValue().priority == 1 ? Color.blue - : Color.gray + mapEntry.getValue().request != null ? Color.red : Color.gray )); } } @@ -316,9 +283,6 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, @Nullable public final Long updateTimestamp; - // Higher value = higher priority. - // Priority of 0 is reserved for unassigned value - public int priority = 0; @CheckForNull public CompletableFuture request; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/FullDataRefreshQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/SyncOnLoginRequestQueue.java similarity index 73% rename from core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/FullDataRefreshQueue.java rename to core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/SyncOnLoginRequestQueue.java index 17a51ad0c..edc36437e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/FullDataRefreshQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/SyncOnLoginRequestQueue.java @@ -4,16 +4,13 @@ import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.level.IDhClientLevel; import com.seibel.distanthorizons.core.pos.DhBlockPos2D; -public class FullDataRefreshQueue extends AbstractFullDataRequestQueue +public class SyncOnLoginRequestQueue extends AbstractFullDataRequestQueue { - public FullDataRefreshQueue(IDhClientLevel level, ClientNetworkState networkState) + public SyncOnLoginRequestQueue(IDhClientLevel level, ClientNetworkState networkState) { super(networkState, level, true, Config.Client.Advanced.Debugging.DebugWireframe.showWorldGenQueue); } - @Override - protected boolean showInDebug() { return this.networkState.config.synchronizeOnLogin; } - @Override protected int getRequestRateLimit() { return this.networkState.config.syncOnLoginRateLimit; } From ce057525d39a5c97fbc3584b908c956f00ddf872 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 27 Aug 2024 16:02:15 +0500 Subject: [PATCH 192/205] Show incompatible protocol version in F3 --- .../client/ClientNetworkState.java | 29 +++++++++++++++++++ .../client/SyncOnLoginRequestQueue.java | 2 +- .../internal/IncompatibleMessageEvent.java | 15 ++++++++++ 3 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/event/internal/IncompatibleMessageEvent.java diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index 571611e67..2e2c35e09 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -8,15 +8,19 @@ import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChang import com.seibel.distanthorizons.core.network.INetworkObject; import com.seibel.distanthorizons.core.network.event.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.event.internal.CloseEvent; +import com.seibel.distanthorizons.core.network.event.internal.IncompatibleMessageEvent; +import com.seibel.distanthorizons.core.network.messages.base.CurrentLevelKeyMessage; import com.seibel.distanthorizons.core.network.messages.base.RemotePlayerConfigMessage; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataChunkMessage; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPayload; import com.seibel.distanthorizons.core.network.session.Session; import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; +import com.seibel.distanthorizons.coreapi.ModInfo; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.CompositeByteBuf; import org.apache.logging.log4j.LogManager; +import org.jetbrains.annotations.Nullable; import java.io.Closeable; import java.util.List; @@ -38,6 +42,9 @@ public class ClientNetworkState implements Closeable private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::sendConfigMessage); public boolean isReady() { return this.configReceived; } + @Nullable + private Integer closestProtocolVersion; + /** * Returns the client used by this instance.

* If you need to subscribe to any packet events, create an instance of {@link ScopedNetworkEventSource} using the returned instance. @@ -55,6 +62,22 @@ public class ClientNetworkState implements Closeable */ public ClientNetworkState() { + this.session.registerHandler(IncompatibleMessageEvent.class, event -> + { + if (this.closestProtocolVersion == null || Math.abs(event.protocolVersion - ModInfo.PROTOCOL_VERSION) < this.closestProtocolVersion) + { + this.closestProtocolVersion = event.protocolVersion; + } + }); + + this.session.registerHandler(CurrentLevelKeyMessage.class, msg -> + { + if (this.serverSupportStatus == EServerSupportStatus.NONE) + { + this.serverSupportStatus = EServerSupportStatus.LEVELS_ONLY; + } + }); + this.session.registerHandler(RemotePlayerConfigMessage.class, msg -> { this.serverSupportStatus = EServerSupportStatus.FULL; @@ -121,6 +144,12 @@ public class ClientNetworkState implements Closeable return; } + if (this.serverSupportStatus == EServerSupportStatus.NONE && this.closestProtocolVersion != null) + { + messageList.add("Incompatible protocol version: " + this.closestProtocolVersion + ", required: " + ModInfo.PROTOCOL_VERSION); + return; + } + messageList.add(this.serverSupportStatus.message); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/SyncOnLoginRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/SyncOnLoginRequestQueue.java index edc36437e..5d56ee1c4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/SyncOnLoginRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/SyncOnLoginRequestQueue.java @@ -15,7 +15,7 @@ public class SyncOnLoginRequestQueue extends AbstractFullDataRequestQueue protected int getRequestRateLimit() { return this.networkState.config.syncOnLoginRateLimit; } @Override - protected String getQueueName() { return "Data Refresh Queue"; } + protected String getQueueName() { return "Sync On Login Queue"; } @Override public boolean tick(DhBlockPos2D targetPos) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/event/internal/IncompatibleMessageEvent.java b/core/src/main/java/com/seibel/distanthorizons/core/network/event/internal/IncompatibleMessageEvent.java new file mode 100644 index 000000000..d732ab4ed --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/event/internal/IncompatibleMessageEvent.java @@ -0,0 +1,15 @@ +package com.seibel.distanthorizons.core.network.event.internal; + +/** + * This event is received instead of a message if its protocol version is incompatible with version the mod uses. + */ +public class IncompatibleMessageEvent extends InternalEvent +{ + public final int protocolVersion; + + public IncompatibleMessageEvent(int protocolVersion) + { + this.protocolVersion = protocolVersion; + } + +} \ No newline at end of file From 375cd44cbd61d83b95445bc7b82ff8659c05301c Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 27 Aug 2024 16:35:22 +0500 Subject: [PATCH 193/205] Do not start generator until server responds with config --- .../distanthorizons/core/level/DhClientLevel.java | 2 +- .../multiplayer/client/ClientNetworkState.java | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 8eb62a033..4b83379dc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -185,7 +185,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel boolean isClientUsable = false, isAllowedDimension = false; if (networkState != null) { - isClientUsable = !networkState.getSession().isClosed(); + isClientUsable = networkState.isReady(); isAllowedDimension = MC_CLIENT.getWrappedClientLevel() == this.levelWrapper; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index 2e2c35e09..9729e573b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -34,22 +34,22 @@ public class ClientNetworkState implements Closeable () -> Config.Client.Advanced.Logging.logNetworkEvent.get()); private final Session session = new Session(null); - private EServerSupportStatus serverSupportStatus = EServerSupportStatus.NONE; - + /** + * Returns the client used by this instance.

+ * If you need to subscribe to any packet events, create an instance of {@link ScopedNetworkEventSource} using the returned instance. + */ + public Session getSession() { return this.session; } public MultiplayerConfig config = new MultiplayerConfig(); private volatile boolean configReceived = false; private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::sendConfigMessage); public boolean isReady() { return this.configReceived; } + private EServerSupportStatus serverSupportStatus = EServerSupportStatus.NONE; + /** Protocol version closest to supported by this mod version */ @Nullable private Integer closestProtocolVersion; - /** - * Returns the client used by this instance.

- * If you need to subscribe to any packet events, create an instance of {@link ScopedNetworkEventSource} using the returned instance. - */ - public Session getSession() { return this.session; } private final ConcurrentMap fullDataBuffers = CacheBuilder.newBuilder() .expireAfterAccess(10, TimeUnit.SECONDS) From 62ddb466745d6f33e216ff8454e270b91c90fb9c Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Thu, 29 Aug 2024 17:09:08 +0500 Subject: [PATCH 194/205] Restore LOD fetching by distance --- .../client/AbstractFullDataRequestQueue.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index be986fc1b..8e949502c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -89,6 +89,11 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, return entry.future; } + protected int posDistanceSquared(DhBlockPos2D targetPos, long pos) + { + return (int) DhSectionPos.getCenterBlockPos(pos).distSquared(targetPos); + } + public synchronized boolean tick(DhBlockPos2D targetPos) { if (this.closingFuture != null || !this.networkState.isReady()) @@ -106,7 +111,7 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, break; } - this.sendNewRequest(); + this.sendNewRequest(targetPos); } return true; @@ -132,11 +137,12 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, } } - private void sendNewRequest() + private void sendNewRequest(DhBlockPos2D targetPos) { Map.Entry mapEntry = this.waitingTasks.entrySet().stream() .filter(task -> task.getValue().request == null) - .findAny().orElse(null); + .min((x, y) -> this.posDistanceSquared(targetPos, x.getKey()) - this.posDistanceSquared(targetPos, y.getKey())) + .orElse(null); if (mapEntry == null) { From 40f902e2f59e7012d59cefab3faae364606a498b Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 31 Aug 2024 20:46:58 +0500 Subject: [PATCH 195/205] Delay loading first level when on server --- .../core/api/internal/ClientApi.java | 31 +++++++++++++++++-- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index 2abac5a12..4beaeb2fe 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -26,6 +26,7 @@ import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhAp import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.render.DhApiRenderProxy; +import com.seibel.distanthorizons.core.util.TimerUtil; import com.seibel.distanthorizons.core.util.objects.Pair; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import com.seibel.distanthorizons.core.level.IDhClientLevel; @@ -58,9 +59,7 @@ import org.jetbrains.annotations.NotNull; import org.lwjgl.glfw.GLFW; import java.io.File; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Queue; +import java.util.*; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; @@ -93,6 +92,8 @@ public class ClientApi private final ClientPluginChannelApi pluginChannelApi = new ClientPluginChannelApi(this::clientLevelLoadEvent, this::clientLevelUnloadEvent); + private Timer firstLevelLoadTimer; + private static final long FIRST_LEVEL_LOAD_DELAY = 1000; /** Holds any levels that were loaded before the {@link ClientApi#onClientOnlyConnected} was fired. */ public final HashSet waitingClientLevels = new HashSet<>(); @@ -170,6 +171,12 @@ public class ClientApi /** Synchronized to prevent a rare issue where multiple disconnect events are triggered on top of each other. */ public synchronized void onClientOnlyDisconnected() { + if (this.firstLevelLoadTimer != null) + { + this.firstLevelLoadTimer.cancel(); + this.firstLevelLoadTimer = null; + } + AbstractDhWorld world = SharedApi.getAbstractDhWorld(); if (world != null) { @@ -228,6 +235,24 @@ public class ClientApi public void clientLevelLoadEvent(IClientLevelWrapper level) { + if (MC.clientConnectedToDedicatedServer()) + { + if (this.firstLevelLoadTimer == null) + { + this.firstLevelLoadTimer = TimerUtil.CreateTimer("FirstLevelLoadTimer"); + this.firstLevelLoadTimer.schedule(new TimerTask() + { + @Override + public void run() + { + ClientApi.this.clientLevelLoadEvent(level); + } + }, FIRST_LEVEL_LOAD_DELAY); + return; + } + this.firstLevelLoadTimer.cancel(); + } + try { LOGGER.info("Loading client level [" + level + "]-["+level.getDimensionName()+"]."); From 048b36f80d3506a4b88e75445fb6b1c04b10657d Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 31 Aug 2024 20:50:50 +0500 Subject: [PATCH 196/205] Add comment --- .../com/seibel/distanthorizons/core/api/internal/ClientApi.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index 4beaeb2fe..5a4646387 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -92,6 +92,7 @@ public class ClientApi private final ClientPluginChannelApi pluginChannelApi = new ClientPluginChannelApi(this::clientLevelLoadEvent, this::clientLevelUnloadEvent); + // Delay loading the first level to give server some time to respond with level to actually load private Timer firstLevelLoadTimer; private static final long FIRST_LEVEL_LOAD_DELAY = 1000; From 6fe0477ca7b9b66908a1f66854e7f124506b648b Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 2 Sep 2024 15:31:39 +0500 Subject: [PATCH 197/205] Limit number of retries on request errors Add a delay if rate limit is hit --- .../client/AbstractFullDataRequestQueue.java | 53 +++++++++++++------ .../SupplierBasedRateLimiter.java | 14 ++--- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index 8e949502c..a1f24c16b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -44,6 +44,8 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, private static final Timer TASK_FINISH_TIMER = TimerUtil.CreateTimer("RequestTaskFinishTimer"); + private static final int MAX_RETRY_ATTEMPTS = 3; + protected static final long SHUTDOWN_TIMEOUT_SECONDS = 5; public final ClientNetworkState networkState; @@ -85,6 +87,16 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, LodUtil.assertTrue(DhSectionPos.getDetailLevel(sectionPos) == DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL, "Only highest-detail sections are allowed."); RequestQueueEntry entry = new RequestQueueEntry(chunkDataConsumer, clientTimestamp); + entry.future.whenComplete((result, throwable) -> + { + this.waitingTasks.remove(sectionPos); + + this.finishedRequests.incrementAndGet(); + if (!result || throwable != null) + { + this.failedRequests.incrementAndGet(); + } + }); this.waitingTasks.put(sectionPos, entry); return entry.future; } @@ -161,12 +173,9 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, request.handleAsync((response, throwable) -> { this.pendingTasksSemaphore.release(); - this.finishedRequests.incrementAndGet(); try { - this.waitingTasks.remove(sectionPos); - if (throwable != null) { throw throwable; @@ -187,26 +196,37 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, catch (InvalidLevelException | RequestRejectedException ignored) { // We're too late / some cases might trigger a bunch of expected rejections + return entry.future.complete(false); } - catch (SessionClosedException | RateLimitedException e) + catch (SessionClosedException | CancellationException ignored) { - if (e instanceof RateLimitedException) - { - LOGGER.warn("Rate limited by server, re-queueing task [" + sectionPos + "]: " + e.getMessage()); - } + // Triggered when level is unloaded + return entry.future.cancel(false); + } + catch (RateLimitedException e) + { + LOGGER.warn("Rate limited by server, re-queueing task [" + sectionPos + "]: " + e.getMessage()); + // Skip 1 second + this.rateLimiter.acquireOrDrain(Integer.MAX_VALUE); entry.request = null; - this.finishedRequests.decrementAndGet(); - } - catch (CancellationException ignored) - { - this.finishedRequests.decrementAndGet(); + return null; } catch (Throwable e) { - LOGGER.error("Error while fetching full data source", e); - this.failedRequests.incrementAndGet(); - return entry.future.complete(false); + entry.retryAttempts--; + LOGGER.error("Error while fetching full data source, attempts left: {} / {}", entry.retryAttempts, MAX_RETRY_ATTEMPTS, e); + + // Retry logic + if (entry.retryAttempts > 0) + { + entry.request = null; + return null; + } + else + { + return entry.future.complete(false); + } } // Hack to work around a race condition @@ -291,6 +311,7 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, @CheckForNull public CompletableFuture request; + public int retryAttempts = MAX_RETRY_ATTEMPTS; public RequestQueueEntry( Consumer chunkDataConsumer, diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedRateLimiter.java b/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedRateLimiter.java index f0c98fdde..42f9c6b21 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedRateLimiter.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/ratelimiting/SupplierBasedRateLimiter.java @@ -40,20 +40,12 @@ public class SupplierBasedRateLimiter { this.rateLimiter.setRate(this.maxRateSupplier.get()); - if (this.rateLimiter.tryAcquire(permits)) - { - return permits; - } - int acquired = 0; - while ((permits /= 2) > 0) + while (permits > 0 && this.rateLimiter.tryAcquire()) { - if (this.rateLimiter.tryAcquire(permits)) - { - acquired += permits; - } + acquired++; + permits--; } - return acquired; } From a2949b81248303c4949758a4a1003d0bda7c5381 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 3 Sep 2024 16:52:10 +0500 Subject: [PATCH 198/205] Change some string & remove unused config item --- .../distanthorizons/core/config/Config.java | 16 +++------------- .../multiplayer/server/ServerPlayerState.java | 4 ++-- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 710bcc2c6..afe065589 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -169,8 +169,9 @@ public class Config "\n" + "Note for servers:\n" + "This setting does not prevent players from generating farther out.\n" + - "If you want to limit performance impact, change rate/concurrency (RC) limits\n" + - "and thread count/runtime ratio settings instead.") + "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.") .setPerformance(EConfigEntryPerformance.HIGH) .build(); @@ -979,17 +980,6 @@ public class Config .build(); - public static ConfigEntry rateLimitHitTolerance = new ConfigEntry.Builder() - .setServersideShortName("rateLimitHitTolerance") - .setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) - .setMinDefaultMax(1, 10, 100) - .comment("" - + "The amount of rate/concurrency limit hits a client can make in one second before being disconnected by the server.\n" - + "This setting only applies to the server and has no effect on clients." - + "") - .build(); - - public static ConfigEntry sendLevelKeys = new ConfigEntry.Builder() .setServersideShortName("sendLevelKeys") diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index 106498ac7..0d805e676 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -91,14 +91,14 @@ public class ServerPlayerState public final SupplierBasedRateAndConcurrencyLimiter generationRequestRateLimiter = new SupplierBasedRateAndConcurrencyLimiter<>( () -> ServerNetworking.generationRequestRateLimit.get(), msg -> { - msg.sendResponse(new RateLimitedException("Full data request rate/concurrency limit: " + ServerPlayerState.this.config.getGenerationRequestRateLimit())); + msg.sendResponse(new RateLimitedException("Full data request rate limit: " + ServerPlayerState.this.config.getGenerationRequestRateLimit())); } ); public final SupplierBasedRateAndConcurrencyLimiter syncOnLoginRateLimiter = new SupplierBasedRateAndConcurrencyLimiter<>( () -> ServerNetworking.syncOnLoginRateLimit.get(), msg -> { - msg.sendResponse(new RateLimitedException("Data sync rate/concurrency limit: " + ServerPlayerState.this.config.getSyncOnLoginRateLimit())); + msg.sendResponse(new RateLimitedException("Sync on login rate limit: " + ServerPlayerState.this.config.getSyncOnLoginRateLimit())); } ); From 0361e5c69bd7177f70b0fd7bd4afea1fcdc012c1 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Wed, 4 Sep 2024 22:53:02 +0500 Subject: [PATCH 199/205] Fix compilation --- .../core/generation/WorldRemoteGenerationQueue.java | 2 +- .../com/seibel/distanthorizons/core/level/DhClientLevel.java | 2 +- .../core/multiplayer/client/AbstractFullDataRequestQueue.java | 2 +- .../core/multiplayer/client/SyncOnLoginRequestQueue.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index eb7eba17e..3f54d031a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -7,7 +7,7 @@ import com.seibel.distanthorizons.core.level.IDhClientLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.multiplayer.client.AbstractFullDataRequestQueue; import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; -import com.seibel.distanthorizons.core.pos.DhBlockPos2D; +import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D; import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; import com.seibel.distanthorizons.core.util.LodUtil; import org.apache.logging.log4j.Logger; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 533b5cf23..d096ceb0d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -36,7 +36,7 @@ import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartial import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos; import com.seibel.distanthorizons.core.render.RenderBufferHandler; import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer; -import com.seibel.distanthorizons.core.pos.DhBlockPos2D; +import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index a1f24c16b..e03ab17d2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -13,7 +13,7 @@ import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedExcepti import com.seibel.distanthorizons.core.network.session.SessionClosedException; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceResponseMessage; -import com.seibel.distanthorizons.core.pos.DhBlockPos2D; +import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/SyncOnLoginRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/SyncOnLoginRequestQueue.java index 5d56ee1c4..2cb8a6093 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/SyncOnLoginRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/SyncOnLoginRequestQueue.java @@ -2,7 +2,7 @@ package com.seibel.distanthorizons.core.multiplayer.client; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.level.IDhClientLevel; -import com.seibel.distanthorizons.core.pos.DhBlockPos2D; +import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D; public class SyncOnLoginRequestQueue extends AbstractFullDataRequestQueue { From 6293cc8c27381c9900c48a6ab35b02215a9c0d50 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Thu, 5 Sep 2024 14:22:00 +0500 Subject: [PATCH 200/205] Prefill levelKeyPrefix in new worlds --- .../com/seibel/distanthorizons/core/config/Config.java | 7 ++++++- .../minecraft/IMinecraftSharedWrapper.java | 2 ++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 4c24da923..45621b4eb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -43,6 +43,7 @@ import javax.swing.*; import java.awt.*; import java.util.*; import java.util.List; +import java.util.concurrent.ThreadLocalRandom; /** @@ -1019,7 +1020,11 @@ public class Config public static ConfigEntry levelKeyPrefix = new ConfigEntry.Builder() .setServersideShortName("levelKeyPrefix") .setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) - .set("") + .set( + SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class).isWorldInitialized() + ? "" + : "server" + ThreadLocalRandom.current().nextInt(1, 1000) + ) .comment("" + "Prefix of the level keys sent to the clients.\n" + "Should be set to a unique value for each backend server behind a proxy,\n" diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftSharedWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftSharedWrapper.java index 95bc31714..1b1b84dd9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftSharedWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftSharedWrapper.java @@ -30,4 +30,6 @@ public interface IMinecraftSharedWrapper extends IBindable File getInstallationDirectory(); + boolean isWorldInitialized(); + } \ No newline at end of file From 875b5fffccc69c65541131b20204481de1e73b12 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Thu, 5 Sep 2024 23:45:04 +0500 Subject: [PATCH 201/205] Fix client crashing --- .../java/com/seibel/distanthorizons/core/config/Config.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 45621b4eb..286de8fde 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -20,6 +20,7 @@ package com.seibel.distanthorizons.core.config; +import com.google.common.base.Suppliers; import com.seibel.distanthorizons.api.DhApi; import com.seibel.distanthorizons.api.enums.config.*; import com.seibel.distanthorizons.api.enums.config.quickOptions.*; @@ -1021,7 +1022,7 @@ public class Config .setServersideShortName("levelKeyPrefix") .setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) .set( - SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class).isWorldInitialized() + Suppliers.compose(wrapper -> !wrapper.isDedicatedServer() || wrapper.isWorldInitialized(), () -> SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class)).get() ? "" : "server" + ThreadLocalRandom.current().nextInt(1, 1000) ) From 877588ebed10d8788efdfd45f31c4c0533722999 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 7 Sep 2024 21:47:02 +0500 Subject: [PATCH 202/205] Fix rare NPE when receiving LODs --- .../client/AbstractFullDataRequestQueue.java | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java index e03ab17d2..3651a9708 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataRequestQueue.java @@ -20,13 +20,16 @@ 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.TimerUtil; +import com.seibel.distanthorizons.core.util.objects.DataCorruptedException; import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateLimiter; +import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import org.apache.logging.log4j.LogManager; import javax.annotation.CheckForNull; import javax.annotation.Nullable; import java.awt.*; +import java.io.IOException; import java.util.List; import java.util.Map; import java.util.Timer; @@ -170,7 +173,7 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, FullDataSourceResponseMessage.class ); entry.request = request; - request.handleAsync((response, throwable) -> + request.handle((response, throwable) -> { this.pendingTasksSemaphore.release(); @@ -184,9 +187,26 @@ public abstract class AbstractFullDataRequestQueue implements IDebugRenderable, if (response.payload != null) { FullDataSourceV2DTO dataSourceDto = this.networkState.decodeDataSourceAndReleaseBuffer(response.payload); - FullDataSourceV2 fullDataSource = dataSourceDto.createPooledDataSource(this.level.getLevelWrapper()); - entry.chunkDataConsumer.accept(fullDataSource); - FullDataSourceV2.DATA_SOURCE_POOL.returnPooledDataSource(fullDataSource); + + ThreadPoolExecutor executor = ThreadPoolUtil.getNetworkCompressionExecutor(); + if (executor == null) + { + LOGGER.warn("Unable to handle FullDataPayload - getNetworkCompressionExecutor() is null"); + return null; + } + CompletableFuture.runAsync(() -> + { + try + { + FullDataSourceV2 fullDataSource = dataSourceDto.createPooledDataSource(this.level.getLevelWrapper()); + entry.chunkDataConsumer.accept(fullDataSource); + FullDataSourceV2.DATA_SOURCE_POOL.returnPooledDataSource(fullDataSource); + } + catch (IOException | DataCorruptedException | InterruptedException e) + { + throw new RuntimeException(e); + } + }, executor); } else { From 576d0f5666e5218bd66e6750e3f579be371d3296 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 7 Sep 2024 21:47:43 +0500 Subject: [PATCH 203/205] Fix crash on F3 when commit hash was failed to retrieve --- .../com/seibel/distanthorizons/core/logging/f3/F3Screen.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java b/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java index 6d6080a50..ba97a4c6a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java @@ -85,7 +85,8 @@ public class F3Screen messageList.add(ModInfo.READABLE_NAME+": "+ModInfo.VERSION); if (ModInfo.IS_DEV_BUILD) { - messageList.add("Build: " + ModJarInfo.Git_Commit.substring(0, 8) + " (" + ModJarInfo.Git_Branch + ")"); + String shortCommitHash = ModJarInfo.Git_Commit.length() >= 8 ? ModJarInfo.Git_Commit.substring(0, 8) : ModJarInfo.Git_Commit; + messageList.add("Build: " + shortCommitHash + " (" + ModJarInfo.Git_Branch + ")"); } messageList.add(""); // thread pools From 8ca20527489d439cb657d6fd75ef66327c7e5a3d Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 8 Sep 2024 19:05:42 +0500 Subject: [PATCH 204/205] Refactor session config --- .../distanthorizons/coreapi/ModInfo.java | 2 +- .../WorldRemoteGenerationQueue.java | 2 +- .../core/level/DhClientLevel.java | 4 +- .../client/ClientNetworkState.java | 17 +- .../client/SyncOnLoginRequestQueue.java | 4 +- .../config/AbstractMultiplayerConfig.java | 41 ----- .../multiplayer/config/MultiplayerConfig.java | 40 ----- .../MultiplayerConfigChangeListener.java | 43 ------ .../multiplayer/config/SessionConfig.java | 145 ++++++++++++++++++ .../server/ConstrainedMultiplayerConfig.java | 57 ------- .../multiplayer/server/ServerPlayerState.java | 17 +- .../core/network/INetworkObject.java | 31 ++-- .../network/messages/MessageRegistry.java | 4 +- ...Message.java => SessionConfigMessage.java} | 17 +- 14 files changed, 199 insertions(+), 225 deletions(-) delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/SessionConfig.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ConstrainedMultiplayerConfig.java rename core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/{RemotePlayerConfigMessage.java => SessionConfigMessage.java} (66%) diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java index d1c63e61a..21e1e2637 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java @@ -32,7 +32,7 @@ public final class ModInfo // region Protocol versions // Incremented every time any packets are added, changed or removed, with a few exceptions. - public static final int PROTOCOL_VERSION = 3; + public static final int PROTOCOL_VERSION = 4; public static final String WRAPPER_PACKET_PATH = "message"; // endregion diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java index 3f54d031a..132273c0d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldRemoteGenerationQueue.java @@ -22,7 +22,7 @@ public class WorldRemoteGenerationQueue extends AbstractFullDataRequestQueue imp @Override - protected int getRequestRateLimit() { return this.networkState.config.generationRequestRateLimit; } + protected int getRequestRateLimit() { return this.networkState.config.getGenerationRequestRateLimit(); } @Override protected String getQueueName() { return "World Remote Generation Queue"; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index d096ceb0d..9b8e82310 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -190,7 +190,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel } boolean shouldDoWorldGen = isClientUsable - && networkState.config.distantGenerationEnabled + && networkState.config.isDistantGenerationEnabled() && isAllowedDimension && this.clientside.isRendering(); @@ -289,7 +289,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel if (this.syncOnLoginRequestQueue != null) { assert this.networkState != null; - if (this.networkState.config.synchronizeOnLogin) + if (this.networkState.config.getSynchronizeOnLogin()) { this.syncOnLoginRequestQueue.addDebugMenuStringsToList(messageList); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index 9729e573b..7d2bfadde 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -3,14 +3,13 @@ package com.seibel.distanthorizons.core.multiplayer.client; import com.google.common.cache.CacheBuilder; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; -import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; +import com.seibel.distanthorizons.core.multiplayer.config.SessionConfig; import com.seibel.distanthorizons.core.network.INetworkObject; import com.seibel.distanthorizons.core.network.event.ScopedNetworkEventSource; import com.seibel.distanthorizons.core.network.event.internal.CloseEvent; import com.seibel.distanthorizons.core.network.event.internal.IncompatibleMessageEvent; import com.seibel.distanthorizons.core.network.messages.base.CurrentLevelKeyMessage; -import com.seibel.distanthorizons.core.network.messages.base.RemotePlayerConfigMessage; +import com.seibel.distanthorizons.core.network.messages.base.SessionConfigMessage; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataChunkMessage; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPartialUpdateMessage; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataPayload; @@ -40,9 +39,9 @@ public class ClientNetworkState implements Closeable */ public Session getSession() { return this.session; } - public MultiplayerConfig config = new MultiplayerConfig(); + public SessionConfig config = new SessionConfig(); private volatile boolean configReceived = false; - private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::sendConfigMessage); + private final SessionConfig.ChangeListener configChangeListener = new SessionConfig.ChangeListener(this::sendConfigMessage); public boolean isReady() { return this.configReceived; } private EServerSupportStatus serverSupportStatus = EServerSupportStatus.NONE; @@ -78,12 +77,12 @@ public class ClientNetworkState implements Closeable } }); - this.session.registerHandler(RemotePlayerConfigMessage.class, msg -> + this.session.registerHandler(SessionConfigMessage.class, msg -> { this.serverSupportStatus = EServerSupportStatus.FULL; - LOGGER.info("Connection config has been changed: " + msg.payload); - this.config = (MultiplayerConfig) msg.payload; + LOGGER.info("Connection config has been changed: {}", msg.config); + this.config = msg.config; this.configReceived = true; }); @@ -133,7 +132,7 @@ public class ClientNetworkState implements Closeable public void sendConfigMessage() { this.configReceived = false; - this.getSession().sendMessage(new RemotePlayerConfigMessage(new MultiplayerConfig())); + this.getSession().sendMessage(new SessionConfigMessage(new SessionConfig())); } public void addDebugMenuStringsToList(List messageList) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/SyncOnLoginRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/SyncOnLoginRequestQueue.java index 2cb8a6093..3c35df7c1 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/SyncOnLoginRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/SyncOnLoginRequestQueue.java @@ -12,7 +12,7 @@ public class SyncOnLoginRequestQueue extends AbstractFullDataRequestQueue } @Override - protected int getRequestRateLimit() { return this.networkState.config.syncOnLoginRateLimit; } + protected int getRequestRateLimit() { return this.networkState.config.getSyncOnLoginRateLimit(); } @Override protected String getQueueName() { return "Sync On Login Queue"; } @@ -20,7 +20,7 @@ public class SyncOnLoginRequestQueue extends AbstractFullDataRequestQueue @Override public boolean tick(DhBlockPos2D targetPos) { - if (!this.networkState.config.synchronizeOnLogin) + if (!this.networkState.config.getSynchronizeOnLogin()) { return false; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java deleted file mode 100644 index 6bda9948e..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/AbstractMultiplayerConfig.java +++ /dev/null @@ -1,41 +0,0 @@ -package com.seibel.distanthorizons.core.multiplayer.config; - -import com.google.common.base.MoreObjects; -import com.seibel.distanthorizons.core.network.INetworkObject; -import io.netty.buffer.ByteBuf; - -public abstract class AbstractMultiplayerConfig implements INetworkObject -{ - public abstract int getRenderDistanceRadius(); - public abstract boolean isDistantGenerationEnabled(); - public abstract int getGenerationRequestRateLimit(); - public abstract boolean isRealTimeUpdatesEnabled(); - public abstract boolean getSynchronizeOnLogin(); - public abstract int getSyncOnLoginRateLimit(); - - @Override - public void encode(ByteBuf out) - { - out.writeInt(this.getRenderDistanceRadius()); - out.writeBoolean(this.isDistantGenerationEnabled()); - out.writeInt(this.getGenerationRequestRateLimit()); - out.writeBoolean(this.isRealTimeUpdatesEnabled()); - out.writeBoolean(this.getSynchronizeOnLogin()); - out.writeInt(this.getSyncOnLoginRateLimit()); - } - - - @Override - public String toString() - { - return MoreObjects.toStringHelper(this) - .add("renderDistanceRadius", this.getRenderDistanceRadius()) - .add("distantGenerationEnabled", this.isDistantGenerationEnabled()) - .add("generationRequestRateLimit", this.getGenerationRequestRateLimit()) - .add("realTimeUpdatesEnabled", this.isRealTimeUpdatesEnabled()) - .add("synchronizeOnLogin", this.getSynchronizeOnLogin()) - .add("syncOnLoginRateLimit", this.getSyncOnLoginRateLimit()) - .toString(); - } - -} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java deleted file mode 100644 index 03f479cc1..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfig.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.seibel.distanthorizons.core.multiplayer.config; - -import com.seibel.distanthorizons.core.config.Config; -import io.netty.buffer.ByteBuf; - -public class MultiplayerConfig extends AbstractMultiplayerConfig -{ - // IMPORTANT: Once you added/removed config fields, modify MultiplayerConfigChangeListener accordingly. - - public int renderDistanceRadius = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get(); - @Override public int getRenderDistanceRadius() { return this.renderDistanceRadius; } - - public boolean distantGenerationEnabled = Config.Client.Advanced.WorldGenerator.enableDistantGeneration.get(); - @Override public boolean isDistantGenerationEnabled() { return this.distantGenerationEnabled; } - - public int generationRequestRateLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.generationRequestRateLimit.get(); - @Override public int getGenerationRequestRateLimit() { return this.generationRequestRateLimit; } - - public boolean realTimeUpdatesEnabled = Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates.get(); - @Override public boolean isRealTimeUpdatesEnabled() { return this.realTimeUpdatesEnabled; } - - public boolean synchronizeOnLogin = Config.Client.Advanced.Multiplayer.ServerNetworking.synchronizeOnLogin.get(); - @Override public boolean getSynchronizeOnLogin() { return this.synchronizeOnLogin; } - - public int syncOnLoginRateLimit = Config.Client.Advanced.Multiplayer.ServerNetworking.syncOnLoginRateLimit.get(); - @Override public int getSyncOnLoginRateLimit() { return this.syncOnLoginRateLimit; } - - - @Override - public void decode(ByteBuf in) - { - this.renderDistanceRadius = in.readInt(); - this.distantGenerationEnabled = in.readBoolean(); - this.generationRequestRateLimit = in.readInt(); - this.realTimeUpdatesEnabled = in.readBoolean(); - this.synchronizeOnLogin = in.readBoolean(); - this.syncOnLoginRateLimit = in.readInt(); - } - -} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java deleted file mode 100644 index e035cfbf9..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/MultiplayerConfigChangeListener.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.seibel.distanthorizons.core.multiplayer.config; - -import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.config.types.ConfigEntry; -import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener; - -import java.io.Closeable; -import java.util.ArrayList; - -@SuppressWarnings({"rawtypes", "unchecked"}) -public class MultiplayerConfigChangeListener implements Closeable -{ - private static final ConfigEntry[] CONFIG_ENTRIES = new ConfigEntry[] { - Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius, - Config.Client.Advanced.WorldGenerator.enableDistantGeneration, - Config.Client.Advanced.Multiplayer.ServerNetworking.generationRequestRateLimit, - Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates, - Config.Client.Advanced.Multiplayer.ServerNetworking.synchronizeOnLogin, - Config.Client.Advanced.Multiplayer.ServerNetworking.syncOnLoginRateLimit, - }; - - private final ArrayList changeListeners; - - public MultiplayerConfigChangeListener(Runnable runnable) - { - this.changeListeners = new ArrayList<>(CONFIG_ENTRIES.length); - for (ConfigEntry entry : CONFIG_ENTRIES) - { - this.changeListeners.add(new ConfigChangeListener(entry, ignored -> runnable.run())); - } - } - - @Override - public void close() - { - for (ConfigChangeListener changeListener : this.changeListeners) - { - changeListener.close(); - } - this.changeListeners.clear(); - } - -} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/SessionConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/SessionConfig.java new file mode 100644 index 000000000..9f3f9cf89 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/SessionConfig.java @@ -0,0 +1,145 @@ +package com.seibel.distanthorizons.core.multiplayer.config; + +import com.google.common.base.MoreObjects; +import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener; +import com.seibel.distanthorizons.core.config.types.ConfigEntry; +import com.seibel.distanthorizons.core.network.INetworkObject; +import io.netty.buffer.ByteBuf; + +import java.io.Closeable; +import java.util.*; +import java.util.concurrent.ConcurrentSkipListMap; +import java.util.function.BiFunction; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static com.seibel.distanthorizons.core.config.Config.Client.Advanced.*; + +public class SessionConfig implements INetworkObject +{ + private static final Map CONFIG_ENTRIES = new HashMap<>(); + private static void registerConfigEntry(ConfigEntry configEntry, BiFunction valueConstrainer) + { + CONFIG_ENTRIES.put(Objects.requireNonNull(configEntry.getServersideShortName()), new Entry(configEntry, valueConstrainer)); + } + + private final SortedMap values = new ConcurrentSkipListMap<>(); + public SessionConfig constrainingConfig; + + + static + { + // Note: config values are ordered by serversideShortName when transmitted + + registerConfigEntry(Graphics.Quality.lodChunkRenderDistanceRadius, Math::min); + + registerConfigEntry(WorldGenerator.enableDistantGeneration, (x, y) -> x && y); + registerConfigEntry(Multiplayer.ServerNetworking.generationRequestRateLimit, Math::min); + + registerConfigEntry(Multiplayer.ServerNetworking.enableRealTimeUpdates, (x, y) -> x && y); + + registerConfigEntry(Multiplayer.ServerNetworking.synchronizeOnLogin, (x, y) -> x && y); + registerConfigEntry(Multiplayer.ServerNetworking.syncOnLoginRateLimit, Math::min); + } + + public int getRenderDistanceRadius() { return this.getValue(Graphics.Quality.lodChunkRenderDistanceRadius); } + public boolean isDistantGenerationEnabled() { return this.getValue(WorldGenerator.enableDistantGeneration); } + public int getGenerationRequestRateLimit() { return this.getValue(Multiplayer.ServerNetworking.generationRequestRateLimit); } + public boolean isRealTimeUpdatesEnabled() { return this.getValue(Multiplayer.ServerNetworking.enableRealTimeUpdates); } + public boolean getSynchronizeOnLogin() { return this.getValue(Multiplayer.ServerNetworking.synchronizeOnLogin); } + public int getSyncOnLoginRateLimit() { return this.getValue(Multiplayer.ServerNetworking.syncOnLoginRateLimit); } + + + @SuppressWarnings("unchecked") + private T getValue(String name) + { + Entry entry = CONFIG_ENTRIES.get(name); + + T value = (T) this.values.get(name); + if (value == null) + { + value = (T) entry.supplier.get(); + } + + return (this.constrainingConfig != null + ? (T) entry.valueConstrainer.apply(value, this.constrainingConfig.getValue(name)) + : value); + } + private T getValue(ConfigEntry configEntry) + { + return this.getValue(configEntry.getServersideShortName()); + } + + private Map getValues() + { + return CONFIG_ENTRIES.keySet().stream().collect(Collectors.toMap(Function.identity(), this::getValue)); + } + + + @Override + public void encode(ByteBuf out) + { + this.writeFixedLengthCollection(out, this.getValues().values()); + } + + @Override + public void decode(ByteBuf in) + { + for (String key : CONFIG_ENTRIES.keySet()) + { + Object currentValue = this.getValue(key); + Object newValue = Codec.getCodec(currentValue.getClass()).decode.apply(currentValue, in); + this.values.put(key, newValue); + } + } + + + @Override + public String toString() + { + return MoreObjects.toStringHelper(this) + .add("values", this.getValues()) + .toString(); + } + + + private static class Entry + { + public final ConfigEntry supplier; + public final BiFunction valueConstrainer; + + @SuppressWarnings("unchecked") + private Entry(ConfigEntry supplier, BiFunction valueConstrainer) + { + this.supplier = (ConfigEntry) supplier; + this.valueConstrainer = (BiFunction) valueConstrainer; + } + + } + + public static class ChangeListener implements Closeable + { + private final ArrayList> changeListeners; + + public ChangeListener(Runnable runnable) + { + this.changeListeners = new ArrayList<>(CONFIG_ENTRIES.size()); + for (Entry entry : CONFIG_ENTRIES.values()) + { + this.changeListeners.add(new ConfigChangeListener<>(entry.supplier, ignored -> runnable.run())); + } + } + + @Override + public void close() + { + for (ConfigChangeListener changeListener : this.changeListeners) + { + changeListener.close(); + } + this.changeListeners.clear(); + } + + } + +} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ConstrainedMultiplayerConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ConstrainedMultiplayerConfig.java deleted file mode 100644 index 6ab030b82..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ConstrainedMultiplayerConfig.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.seibel.distanthorizons.core.multiplayer.server; - -import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.multiplayer.config.AbstractMultiplayerConfig; -import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; -import io.netty.buffer.ByteBuf; - -/** - * Used for constraining the client config to the server config. - */ -public class ConstrainedMultiplayerConfig extends AbstractMultiplayerConfig -{ - public MultiplayerConfig clientConfig = new MultiplayerConfig(); - - @Override - public int getRenderDistanceRadius() - { - return Math.min(this.clientConfig.renderDistanceRadius, Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get()); - } - - @Override - public boolean isDistantGenerationEnabled() - { - return this.clientConfig.distantGenerationEnabled && Config.Client.Advanced.WorldGenerator.enableDistantGeneration.get(); - } - - @Override - public int getGenerationRequestRateLimit() - { - return Math.min(this.clientConfig.generationRequestRateLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.generationRequestRateLimit.get()); - } - - @Override - public boolean isRealTimeUpdatesEnabled() - { - return this.clientConfig.realTimeUpdatesEnabled && Config.Client.Advanced.Multiplayer.ServerNetworking.enableRealTimeUpdates.get(); - } - - @Override - public boolean getSynchronizeOnLogin() - { - return this.clientConfig.synchronizeOnLogin && Config.Client.Advanced.Multiplayer.ServerNetworking.synchronizeOnLogin.get(); - } - - @Override - public int getSyncOnLoginRateLimit() - { - return Math.min(this.clientConfig.syncOnLoginRateLimit, Config.Client.Advanced.Multiplayer.ServerNetworking.syncOnLoginRateLimit.get()); - } - - @Override - public void decode(ByteBuf in) - { - throw new UnsupportedOperationException("Decoding is not supported for server-only class."); - } - -} \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java index 0d805e676..346a6df41 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/server/ServerPlayerState.java @@ -2,10 +2,9 @@ package com.seibel.distanthorizons.core.multiplayer.server; import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener; import com.seibel.distanthorizons.core.level.DhServerLevel; -import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; -import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfigChangeListener; +import com.seibel.distanthorizons.core.multiplayer.config.SessionConfig; import com.seibel.distanthorizons.core.network.messages.base.CurrentLevelKeyMessage; -import com.seibel.distanthorizons.core.network.messages.base.RemotePlayerConfigMessage; +import com.seibel.distanthorizons.core.network.messages.base.SessionConfigMessage; import com.seibel.distanthorizons.core.network.event.internal.CloseEvent; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage; @@ -24,8 +23,8 @@ public class ServerPlayerState public IServerPlayerWrapper serverPlayer() { return this.session.serverPlayer; } @NotNull - public ConstrainedMultiplayerConfig config = new ConstrainedMultiplayerConfig(); - private final MultiplayerConfigChangeListener configChangeListener = new MultiplayerConfigChangeListener(this::onConfigChanged); + public SessionConfig config = new SessionConfig(); + private final SessionConfig.ChangeListener configChangeListener = new SessionConfig.ChangeListener(this::onConfigChanged); private String lastLevelKey = ""; private final ConfigChangeListener levelKeyPrefixChangeListener = new ConfigChangeListener<>(ServerNetworking.levelKeyPrefix, this::sendLevelKey); @@ -46,11 +45,11 @@ public class ServerPlayerState { this.session = new Session(serverPlayer); - this.session.registerHandler(RemotePlayerConfigMessage.class, remotePlayerConfigMessage -> + this.session.registerHandler(SessionConfigMessage.class, sessionConfigMessage -> { - this.config.clientConfig = (MultiplayerConfig) remotePlayerConfigMessage.payload; + this.config.constrainingConfig = sessionConfigMessage.config; this.sendLevelKey(null); - this.session.sendMessage(new RemotePlayerConfigMessage(this.config)); + this.session.sendMessage(new SessionConfigMessage(this.config)); }); this.session.registerHandler(CloseEvent.class, event -> { @@ -74,7 +73,7 @@ public class ServerPlayerState private void onConfigChanged() { - this.session.sendMessage(new RemotePlayerConfigMessage(this.config)); + this.session.sendMessage(new SessionConfigMessage(this.config)); } public void close() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/INetworkObject.java b/core/src/main/java/com/seibel/distanthorizons/core/network/INetworkObject.java index dccbb43b6..10ab26c96 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/INetworkObject.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/INetworkObject.java @@ -66,13 +66,23 @@ public interface INetworkObject } default void writeString(String inputString, ByteBuf outputByteBuf) + { + INetworkObject.writeStringStatic(inputString, outputByteBuf); + } + + default String readString(ByteBuf inputByteBuf) + { + return INetworkObject.readStringStatic(inputByteBuf); + } + + static void writeStringStatic(String inputString, ByteBuf outputByteBuf) { byte[] bytes = inputString.getBytes(StandardCharsets.UTF_8); outputByteBuf.writeShort(bytes.length); outputByteBuf.writeBytes(bytes); } - default String readString(ByteBuf inputByteBuf) + static String readStringStatic(ByteBuf inputByteBuf) { int length = inputByteBuf.readUnsignedShort(); return inputByteBuf.readSlice(length).toString(StandardCharsets.UTF_8); @@ -81,14 +91,13 @@ public interface INetworkObject default void writeCollection(ByteBuf outputByteBuf, Collection collection) { outputByteBuf.writeInt(collection.size()); - - Codec codec = null; + this.writeFixedLengthCollection(outputByteBuf, collection); + } + default void writeFixedLengthCollection(ByteBuf outputByteBuf, Collection collection) + { for (T item : collection) { - if (codec == null) - { - codec = Codec.getCodec(item.getClass()); - } + Codec codec = Codec.getCodec(item.getClass()); codec.encode.accept(item, outputByteBuf); } } @@ -134,6 +143,8 @@ public interface INetworkObject {{ // Primitives must be added manually here this.put(Integer.class, new Codec((obj, out) -> out.writeInt((int)obj), (obj, in) -> in.readInt())); + this.put(Boolean.class, new Codec((obj, out) -> out.writeBoolean((boolean) obj), (obj, in) -> in.readBoolean())); + this.put(String.class, new Codec((obj, out) -> INetworkObject.writeStringStatic((String) obj, out), (obj, in) -> INetworkObject.readStringStatic(in))); this.put(INetworkObject.class, new Codec(INetworkObject::encode, INetworkObject::decodeToInstance)); this.put(Map.Entry.class, new Codec( @@ -162,7 +173,8 @@ public interface INetworkObject this.decode = (BiFunction) decode; } - public static Codec getCodec(Class clazz) { + public static Codec getCodec(Class clazz) + { return codecMap.computeIfAbsent(clazz, ignored -> { for (Map.Entry, Codec> entry : codecMap.entrySet()) { @@ -172,8 +184,9 @@ public interface INetworkObject } } - throw new AssertionError("Class has no compatible codec: "+clazz.getSimpleName()); + throw new AssertionError("Class has no compatible codec: " + clazz.getSimpleName()); }); } + } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/MessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/MessageRegistry.java index e0b8a7a85..61940f192 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/MessageRegistry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/MessageRegistry.java @@ -23,7 +23,7 @@ import com.google.common.collect.BiMap; import com.google.common.collect.HashBiMap; import com.seibel.distanthorizons.core.network.messages.base.CodecCrashMessage; import com.seibel.distanthorizons.core.network.messages.base.CurrentLevelKeyMessage; -import com.seibel.distanthorizons.core.network.messages.base.RemotePlayerConfigMessage; +import com.seibel.distanthorizons.core.network.messages.base.SessionConfigMessage; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataChunkMessage; import com.seibel.distanthorizons.core.network.messages.requests.CancelMessage; import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage; @@ -59,7 +59,7 @@ public class MessageRegistry this.registerMessage(CurrentLevelKeyMessage.class, CurrentLevelKeyMessage::new); // Config (for full DH support) - this.registerMessage(RemotePlayerConfigMessage.class, RemotePlayerConfigMessage::new); + this.registerMessage(SessionConfigMessage.class, SessionConfigMessage::new); // Requests this.registerMessage(CancelMessage.class, CancelMessage::new); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/RemotePlayerConfigMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/SessionConfigMessage.java similarity index 66% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/RemotePlayerConfigMessage.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/SessionConfigMessage.java index 605c63998..85af7b6dd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/RemotePlayerConfigMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/SessionConfigMessage.java @@ -20,31 +20,30 @@ package com.seibel.distanthorizons.core.network.messages.base; import com.google.common.base.MoreObjects; -import com.seibel.distanthorizons.core.multiplayer.config.AbstractMultiplayerConfig; -import com.seibel.distanthorizons.core.multiplayer.config.MultiplayerConfig; +import com.seibel.distanthorizons.core.multiplayer.config.SessionConfig; import com.seibel.distanthorizons.core.network.INetworkObject; import com.seibel.distanthorizons.core.network.messages.NetworkMessage; import io.netty.buffer.ByteBuf; -public class RemotePlayerConfigMessage extends NetworkMessage +public class SessionConfigMessage extends NetworkMessage { - public AbstractMultiplayerConfig payload; + public SessionConfig config; - public RemotePlayerConfigMessage() { } - public RemotePlayerConfigMessage(AbstractMultiplayerConfig payload) { this.payload = payload; } + public SessionConfigMessage() { } + public SessionConfigMessage(SessionConfig config) { this.config = config; } @Override - public void encode(ByteBuf out) { this.payload.encode(out); } + public void encode(ByteBuf out) { this.config.encode(out); } @Override - public void decode(ByteBuf in) { this.payload = INetworkObject.decodeToInstance(new MultiplayerConfig(), in); } + public void decode(ByteBuf in) { this.config = INetworkObject.decodeToInstance(new SessionConfig(), in); } @Override public MoreObjects.ToStringHelper toStringHelper() { return super.toStringHelper() - .add("payload", this.payload); + .add("config", this.config); } } \ No newline at end of file From 7cd1a37914f83aca45398862abd8f828d0f6e09a Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 8 Sep 2024 21:21:38 +0500 Subject: [PATCH 205/205] Restore ordering of session config entries --- .../com/seibel/distanthorizons/coreapi/ModInfo.java | 2 +- .../core/multiplayer/config/SessionConfig.java | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java index 21e1e2637..d1c63e61a 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java @@ -32,7 +32,7 @@ public final class ModInfo // region Protocol versions // Incremented every time any packets are added, changed or removed, with a few exceptions. - public static final int PROTOCOL_VERSION = 4; + public static final int PROTOCOL_VERSION = 3; public static final String WRAPPER_PACKET_PATH = "message"; // endregion diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/SessionConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/SessionConfig.java index 9f3f9cf89..2a602a18d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/SessionConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/SessionConfig.java @@ -8,7 +8,6 @@ import io.netty.buffer.ByteBuf; import java.io.Closeable; import java.util.*; -import java.util.concurrent.ConcurrentSkipListMap; import java.util.function.BiFunction; import java.util.function.Function; import java.util.stream.Collectors; @@ -17,13 +16,13 @@ import static com.seibel.distanthorizons.core.config.Config.Client.Advanced.*; public class SessionConfig implements INetworkObject { - private static final Map CONFIG_ENTRIES = new HashMap<>(); + private static final LinkedHashMap CONFIG_ENTRIES = new LinkedHashMap<>(); private static void registerConfigEntry(ConfigEntry configEntry, BiFunction valueConstrainer) { CONFIG_ENTRIES.put(Objects.requireNonNull(configEntry.getServersideShortName()), new Entry(configEntry, valueConstrainer)); } - private final SortedMap values = new ConcurrentSkipListMap<>(); + private final LinkedHashMap values = new LinkedHashMap<>(); public SessionConfig constrainingConfig; @@ -72,7 +71,12 @@ public class SessionConfig implements INetworkObject private Map getValues() { - return CONFIG_ENTRIES.keySet().stream().collect(Collectors.toMap(Function.identity(), this::getValue)); + return CONFIG_ENTRIES.keySet().stream().collect(Collectors.toMap( + Function.identity(), + this::getValue, + (x, y) -> x, + LinkedHashMap::new + )); }