From 83ab7e46b3185170fba7e3a5dbd5b7018d8a6948 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 8 Jul 2023 12:25:20 +0500 Subject: [PATCH] Base is pretty much finished --- .../core/network/NetworkClient.java | 24 +++++++++++++++---- .../core/network/NetworkServer.java | 16 +++++++------ .../network/messages/LodConfigMessage.java | 5 ++++ .../protocol/DhNetworkChannelInitializer.java | 16 ++++++++----- .../network/protocol/ExceptionHandler.java | 16 +++++++++++++ .../core/network/protocol/MessageDecoder.java | 2 ++ .../core/network/protocol/MessageEncoder.java | 2 ++ .../core/network/protocol/MessageHandler.java | 2 ++ .../protocol/OutboundExceptionRouter.java | 14 +++++++++++ .../core/world/DhClientWorld.java | 2 +- .../core/world/DhServerWorld.java | 9 ++++--- 11 files changed, 85 insertions(+), 23 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/protocol/ExceptionHandler.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/protocol/OutboundExceptionRouter.java 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 f22102a2a..2253f7ea9 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 @@ -5,6 +5,7 @@ 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.DhNetworkChannelInitializer; +import com.seibel.distanthorizons.core.network.protocol.MessageHandler; import io.netty.bootstrap.Bootstrap; import io.netty.channel.Channel; import io.netty.channel.ChannelFuture; @@ -29,6 +30,7 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable { } private static final int FAILURE_RECONNECT_DELAY_SEC = 5; + private static final int FAILURE_RECONNECT_ATTEMPTS = 5; // TODO move to config of some sort private final InetSocketAddress address; @@ -42,6 +44,7 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable { private State state; private Channel channel; + private int reconnectAttempts = FAILURE_RECONNECT_ATTEMPTS; public NetworkClient(String host, int port) { this.address = new InetSocketAddress(host, port); @@ -62,6 +65,8 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable { registerHandler(CloseMessage.class, (msg, ctx) -> { LOGGER.info("Disconnected from server: {}", ctx.channel().remoteAddress()); + if (state == State.CLOSE_WAIT) + close(); }); } @@ -71,21 +76,31 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable { ChannelFuture connectFuture = clientBootstrap.connect(address); connectFuture.addListener((ChannelFuture channelFuture) -> { - if (!channelFuture.isSuccess()) return; + if (!channelFuture.isSuccess()) { + LOGGER.warn("Connection failed: {0}", channelFuture.cause()); + return; + } channel.writeAndFlush(new HelloMessage()); }); channel = connectFuture.channel(); channel.closeFuture().addListener((ChannelFuture channelFuture) -> { switch (state) { - case CLOSE_WAIT: - close(); - break; case OPEN: + reconnectAttempts--; + LOGGER.info("Reconnection attempts left: {} of {}", reconnectAttempts, FAILURE_RECONNECT_ATTEMPTS); + if (reconnectAttempts == 0) { + state = State.CLOSE_WAIT; + return; + } + state = State.RECONNECT; workerGroup.schedule(this::connect, FAILURE_RECONNECT_DELAY_SEC, TimeUnit.SECONDS); break; case RECONNECT_FORCE: + LOGGER.info("Reconnecting forcefully."); + reconnectAttempts = FAILURE_RECONNECT_ATTEMPTS; + state = State.RECONNECT; workerGroup.schedule(this::connect, 0, TimeUnit.SECONDS); break; @@ -104,6 +119,7 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable { if (closeReason != null) LOGGER.error(closeReason); + if (state == State.CLOSED) return; state = State.CLOSED; workerGroup.shutdownGracefully().syncUninterruptibly(); channel.close().syncUninterruptibly(); 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 2c5813ba2..3d3c70a4d 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 @@ -6,10 +6,7 @@ import com.seibel.distanthorizons.core.network.messages.CloseReasonMessage; import com.seibel.distanthorizons.core.network.messages.HelloMessage; import com.seibel.distanthorizons.core.network.protocol.DhNetworkChannelInitializer; 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.*; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.logging.LogLevel; @@ -25,6 +22,7 @@ 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; public NetworkServer(int port) { this.port = port; @@ -67,7 +65,7 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable { public void disconnectClient(ChannelHandlerContext ctx, String reason) { ctx.channel().config().setAutoRead(false); ctx.writeAndFlush(new CloseReasonMessage(reason)) - .addListener(future -> ctx.close()); + .addListener(ChannelFutureListener.CLOSE); } @Override @@ -75,8 +73,12 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable { if (closeReason != null) LOGGER.error(closeReason); - bossGroup.shutdownGracefully().syncUninterruptibly(); + if (isClosed) return; + isClosed = true; + + LOGGER.info("Shutting down the server."); workerGroup.shutdownGracefully().syncUninterruptibly(); - channel.close().syncUninterruptibly(); + bossGroup.shutdownGracefully().syncUninterruptibly(); + LOGGER.info("Server is closed."); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/LodConfigMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/LodConfigMessage.java index bff6c2ca2..43eb092dc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/LodConfigMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/LodConfigMessage.java @@ -8,6 +8,11 @@ import io.netty.buffer.ByteBuf; public class LodConfigMessage implements INetworkMessage { public DhRemotePlayer.Config config; + public LodConfigMessage() { } + public LodConfigMessage(DhRemotePlayer.Config config) { + this.config = config; + } + @Override public void encode(ByteBuf out) { config.encode(out); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/DhNetworkChannelInitializer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/DhNetworkChannelInitializer.java index fa102b35d..c0283fef5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/DhNetworkChannelInitializer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/DhNetworkChannelInitializer.java @@ -1,10 +1,11 @@ package com.seibel.distanthorizons.core.network.protocol; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import io.netty.channel.*; import io.netty.channel.socket.SocketChannel; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; import io.netty.handler.codec.LengthFieldPrepender; +import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.NotNull; public class DhNetworkChannelInitializer extends ChannelInitializer { @@ -18,14 +19,17 @@ public class DhNetworkChannelInitializer extends ChannelInitializer { @Override 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 e4aa4a285..0ca377314 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,8 @@ public class MessageHandler extends SimpleChannelInboundHandler @Override protected void channelRead0(ChannelHandlerContext ctx, INetworkMessage msg) { + LOGGER.trace("Received message: {}", msg.getClass().getSimpleName()); + List> handlerList = handlers.get(msg.getClass()); if (handlerList == null) { LOGGER.warn("Unhandled message type: {}", msg.getClass().getSimpleName()); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/OutboundExceptionRouter.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/OutboundExceptionRouter.java new file mode 100644 index 000000000..e222aeb10 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/OutboundExceptionRouter.java @@ -0,0 +1,14 @@ +package com.seibel.distanthorizons.core.network.protocol; + +import io.netty.channel.ChannelFutureListener; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPromise; + +public class OutboundExceptionRouter extends ChannelOutboundHandlerAdapter { + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + promise.addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); + super.write(ctx, msg, promise); + } +} 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 2a65f80a3..66ef535a2 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 @@ -56,7 +56,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld // TODO Proper config handling networkClient.registerAckHandler(PlayerUUIDMessage.class, ctx -> { - ctx.writeAndFlush(new LodConfigMessage()); + ctx.writeAndFlush(new LodConfigMessage(new DhRemotePlayer.Config())); }); networkClient.registerHandler(LodConfigMessage.class, (msg, ctx) -> { 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 f55980ab9..e76a53c1b 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 @@ -72,11 +72,12 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld }); networkServer.registerHandler(LodConfigMessage.class, (msg, ctx) -> { - // TODO Take notice of received config + // TODO Take notice of received config and possibly echo back a constrained version ctx.writeAndFlush(new AckMessage(LodConfigMessage.class)); }); networkServer.registerHandler(RequestChunksMessage.class, (msg, ctx) -> { + LOGGER.info("RequestChunksMessage"); // hasReceivedChunkRequest should be false somewhere ??? // to avoid sending updates until client says at least something about its state }); @@ -89,10 +90,8 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld public void removePlayer(IServerPlayerWrapper serverPlayer) { DhRemotePlayer dhPlayer = playersByUUID.remove(serverPlayer.getUUID()); ChannelHandlerContext ctx = playersByConnection.inverse().remove(dhPlayer); - if (ctx != null) { - ctx.writeAndFlush(new CloseReasonMessage("You are being disconnected.")) - .addListener(future -> ctx.close()); - } + if (ctx != null) + networkServer.disconnectClient(ctx, "You are being disconnected."); } @Override