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