From cc35e50edc53f619bdc562c4d53a33ef7100e5c0 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 26 Jun 2023 20:59:25 +0500 Subject: [PATCH 1/3] Move everything to core --- .../core/level/DhClientServerLevel.java | 2 +- .../core/level/DhServerLevel.java | 2 +- .../core/level/ServerLevelModule.java | 2 +- .../core/network/LodClient.java | 24 +++++++ .../core/network/LodServer.java | 64 +++++++++++++++++++ .../messageHandling/MessageDecoder.java | 23 +++++++ .../messageHandling/MessageHandler.java | 27 ++++++++ .../messageHandling/MessageHandlerSide.java | 6 ++ .../messageHandling/MessageRegistry.java | 35 ++++++++++ .../core/network/messages/HelloMessage.java | 26 ++++++++ .../core/network/messages/Message.java | 19 ++++++ 11 files changed, 227 insertions(+), 3 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/LodClient.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/LodServer.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageDecoder.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageHandler.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageHandlerSide.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageRegistry.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/HelloMessage.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/Message.java 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 bc037860f..1d0f7ca5c 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 @@ -39,7 +39,7 @@ public class DhClientServerLevel extends DhLevel implements IDhClientLevel, IDhS { LOGGER.warn("unable to create data folder."); } - serverside = new ServerLevelModule(this, serverLevelWrapper, saveStructure); + serverside = new ServerLevelModule(this, serverLevelWrapper, saveStructure, false); clientside = new ClientLevelModule(this); LOGGER.info("Started "+DhClientServerLevel.class.getSimpleName()+" for "+ serverLevelWrapper +" with saves at "+saveStructure); } 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 8434d4c8b..b10c10cfd 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,7 +25,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel { LOGGER.warn("unable to create data folder."); } - serverside = new ServerLevelModule(this, serverLevelWrapper, saveStructure); + serverside = new ServerLevelModule(this, serverLevelWrapper, saveStructure, true); LOGGER.info("Started DHLevel for {} with saves at {}", serverLevelWrapper, saveStructure); } 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 81e5be59c..2bfb94322 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 @@ -64,7 +64,7 @@ public class ServerLevelModule { public final AppliedConfigState worldGeneratorEnabledConfig; private final AtomicReference worldGenStateRef = new AtomicReference<>(); - public ServerLevelModule(IDhServerLevel parent, IServerLevelWrapper levelWrapper, AbstractSaveStructure saveStructure) + public ServerLevelModule(IDhServerLevel parent, IServerLevelWrapper levelWrapper, AbstractSaveStructure saveStructure, boolean startLodServer) { this.parent = parent; this.levelWrapper = levelWrapper; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/LodClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/LodClient.java new file mode 100644 index 000000000..9bab5d702 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/LodClient.java @@ -0,0 +1,24 @@ +package com.seibel.distanthorizons.core.network; + +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; +//import net.minecraft.client.Minecraft; + +public class LodClient { +// public static final LodClient INSTANCE = new LodClient(); +// +// private final Minecraft client; +// private final NetworkHandler networkHandler; +// +// private LodClient() { +// client = Minecraft.getInstance(); +// this.networkHandler = new NetworkHandler(); +// } + + public void connect() { + + } + + public void disconnect() { + + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/LodServer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/LodServer.java new file mode 100644 index 000000000..3673a0109 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/LodServer.java @@ -0,0 +1,64 @@ +package com.seibel.distanthorizons.core.network; + +import com.seibel.distanthorizons.core.network.messageHandling.MessageDecoder; +import com.seibel.distanthorizons.core.network.messageHandling.MessageHandler; +import com.seibel.distanthorizons.core.network.messageHandling.MessageHandlerSide; +import com.seibel.distanthorizons.core.network.messageHandling.MessageRegistry; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioServerSocketChannel; +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; +import io.netty.handler.logging.LogLevel; +import io.netty.handler.logging.LoggingHandler; + +public class LodServer { + // TODO move to config of some sort + static final int PORT = 25049; + + public LodServer(/* initial settings */) { + + } + + public void start() throws InterruptedException { + EventLoopGroup bossGroup = new NioEventLoopGroup(1); + EventLoopGroup workerGroup = new NioEventLoopGroup(); + try { + ServerBootstrap b = new ServerBootstrap(); + b.group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .handler(new LoggingHandler(LogLevel.INFO)) + .childHandler(getInitializer()); + + b.bind(PORT).sync().channel().closeFuture().sync(); + } finally { + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); + } + } + + public void stop() { + + } + + private ChannelInitializer getInitializer() { + return new ChannelInitializer() { + @Override + public void initChannel(SocketChannel ch) { + ChannelPipeline pipeline = ch.pipeline(); + + MessageRegistry messageRegistry = new MessageRegistry(); + + // Encoding + pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 2, 0, 2)); + pipeline.addLast(new MessageDecoder(messageRegistry)); + // TODO packet encoder + + pipeline.addLast(new MessageHandler(MessageHandlerSide.SERVER)); + } + }; + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageDecoder.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageDecoder.java new file mode 100644 index 000000000..0a35f4fc0 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageDecoder.java @@ -0,0 +1,23 @@ +package com.seibel.distanthorizons.core.network.messageHandling; + +import com.seibel.distanthorizons.core.network.messages.Message; +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 MessageRegistry messageRegistry; + + public MessageDecoder(MessageRegistry messageRegistry) { + this.messageRegistry = messageRegistry; + } + + @Override + protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { + Message message = messageRegistry.createMessage(in.readShort()); + message.decode(in); + out.add(message); + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageHandler.java new file mode 100644 index 000000000..20817f27b --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageHandler.java @@ -0,0 +1,27 @@ +package com.seibel.distanthorizons.core.network.messageHandling; + +import com.seibel.distanthorizons.core.network.messages.Message; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; + +public class MessageHandler extends SimpleChannelInboundHandler { + private final MessageHandlerSide side; + + public MessageHandler(MessageHandlerSide side) { + this.side = side; + } + + @Override + public void channelRead0(ChannelHandlerContext ctx, Message msg) { + switch (side) { + case CLIENT: + msg.handle_Client(ctx); + break; + case SERVER: + msg.handle_Server(ctx); + break; + default: + throw new IllegalStateException("Invalid handler side"); + } + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageHandlerSide.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageHandlerSide.java new file mode 100644 index 000000000..36eb6216c --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageHandlerSide.java @@ -0,0 +1,6 @@ +package com.seibel.distanthorizons.core.network.messageHandling; + +public enum MessageHandlerSide { + CLIENT, + SERVER +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageRegistry.java new file mode 100644 index 000000000..77b849501 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageRegistry.java @@ -0,0 +1,35 @@ +package com.seibel.distanthorizons.core.network.messageHandling; + +import com.seibel.distanthorizons.core.network.messages.HelloMessage; +import com.seibel.distanthorizons.core.network.messages.Message; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; +import java.util.stream.Collectors; + +public class MessageRegistry { + private final Map> idToConstructor = new HashMap>() {{ + // Note: Make sure IDs are unique + // Also note: Removing IDs will break backwards compatibility + put(1, HelloMessage::new); + }}; + + private final Map, Integer> classToId = idToConstructor.entrySet().stream() + .collect(Collectors.toMap( + e -> e.getValue().getClass(), + Map.Entry::getKey + )); + + public Message createMessage(int id) throws IllegalArgumentException { + try { + return idToConstructor.get(id).get(); + } catch (NullPointerException e) { + throw new IllegalArgumentException("Invalid message ID"); + } + } + + public int getMessageId(Message message) { + return classToId.get(message.getClass()); + } +} 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 new file mode 100644 index 000000000..e96673f95 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/HelloMessage.java @@ -0,0 +1,26 @@ +package com.seibel.distanthorizons.core.network.messages; + +import com.seibel.distanthorizons.coreapi.ModInfo; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; + +// This message is critical to maintain backwards compatibility +// as it's used to receive version BEFORE everything else. +public class HelloMessage extends Message { + public int version = ModInfo.PROTOCOL_VERSION; + + @Override + public void encode(ByteBuf out) { + out.writeInt(version); + } + + @Override + public void decode(ByteBuf in) { + version = in.readInt(); + } + + @Override + public void handle_Server(ChannelHandlerContext ctx) { + // TODO Adjust message handling to client's version + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/Message.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/Message.java new file mode 100644 index 000000000..6be22c006 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/Message.java @@ -0,0 +1,19 @@ +package com.seibel.distanthorizons.core.network.messages; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; + +public abstract class Message { + public Message() { } + + public abstract void encode(ByteBuf out); + public abstract void decode(ByteBuf in); + + public void handle_Server(ChannelHandlerContext ctx) { + throw new UnsupportedOperationException(); + } + public void handle_Client(ChannelHandlerContext ctx) { + throw new UnsupportedOperationException(); + } +} + From aee752a112d5f8fffa120845d3a1383c079f6f43 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Mon, 26 Jun 2023 21:56:44 +0500 Subject: [PATCH 2/3] Add encoder --- .../core/network/LodServer.java | 16 ++++++++------- .../messageHandling/MessageHandlerSide.java | 6 ------ .../core/network/messages/HelloMessage.java | 6 +++--- .../core/network/messages/Message.java | 4 ++-- .../MessageDecoder.java | 6 +++--- .../core/network/protocol/MessageEncoder.java | 20 +++++++++++++++++++ .../MessageHandler.java | 2 +- .../network/protocol/MessageHandlerSide.java | 6 ++++++ .../MessageRegistry.java | 2 +- 9 files changed, 45 insertions(+), 23 deletions(-) delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageHandlerSide.java rename core/src/main/java/com/seibel/distanthorizons/core/network/{messageHandling => protocol}/MessageDecoder.java (80%) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageEncoder.java rename core/src/main/java/com/seibel/distanthorizons/core/network/{messageHandling => protocol}/MessageHandler.java (92%) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandlerSide.java rename core/src/main/java/com/seibel/distanthorizons/core/network/{messageHandling => protocol}/MessageRegistry.java (94%) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/LodServer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/LodServer.java index 3673a0109..0866ecaa7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/LodServer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/LodServer.java @@ -1,9 +1,6 @@ package com.seibel.distanthorizons.core.network; -import com.seibel.distanthorizons.core.network.messageHandling.MessageDecoder; -import com.seibel.distanthorizons.core.network.messageHandling.MessageHandler; -import com.seibel.distanthorizons.core.network.messageHandling.MessageHandlerSide; -import com.seibel.distanthorizons.core.network.messageHandling.MessageRegistry; +import com.seibel.distanthorizons.core.network.protocol.*; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; @@ -12,8 +9,10 @@ import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.SocketChannel; import io.netty.channel.socket.nio.NioServerSocketChannel; import io.netty.handler.codec.LengthFieldBasedFrameDecoder; +import io.netty.handler.codec.LengthFieldPrepender; import io.netty.handler.logging.LogLevel; import io.netty.handler.logging.LoggingHandler; +import org.jetbrains.annotations.NotNull; public class LodServer { // TODO move to config of some sort @@ -47,15 +46,18 @@ public class LodServer { private ChannelInitializer getInitializer() { return new ChannelInitializer() { @Override - public void initChannel(SocketChannel ch) { + public void initChannel(@NotNull SocketChannel ch) { ChannelPipeline pipeline = ch.pipeline(); MessageRegistry messageRegistry = new MessageRegistry(); // Encoding - pipeline.addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 2, 0, 2)); + pipeline.addLast(new LengthFieldBasedFrameDecoder(Short.MAX_VALUE, 0, Short.BYTES, 0, Short.BYTES)); pipeline.addLast(new MessageDecoder(messageRegistry)); - // TODO packet encoder + + // Encoder + pipeline.addLast(new LengthFieldPrepender(Short.BYTES)); + pipeline.addLast(new MessageEncoder(messageRegistry)); pipeline.addLast(new MessageHandler(MessageHandlerSide.SERVER)); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageHandlerSide.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageHandlerSide.java deleted file mode 100644 index 36eb6216c..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageHandlerSide.java +++ /dev/null @@ -1,6 +0,0 @@ -package com.seibel.distanthorizons.core.network.messageHandling; - -public enum MessageHandlerSide { - CLIENT, - SERVER -} 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 e96673f95..d5b64398a 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 @@ -5,17 +5,17 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; // This message is critical to maintain backwards compatibility -// as it's used to receive version BEFORE everything else. +// as it's used to receive version before everything else. public class HelloMessage extends Message { public int version = ModInfo.PROTOCOL_VERSION; @Override - public void encode(ByteBuf out) { + public void encode(ChannelHandlerContext ctx, ByteBuf out) { out.writeInt(version); } @Override - public void decode(ByteBuf in) { + public void decode(ChannelHandlerContext ctx, ByteBuf in) { version = in.readInt(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/Message.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/Message.java index 6be22c006..ac665966f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/Message.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/Message.java @@ -6,8 +6,8 @@ import io.netty.channel.ChannelHandlerContext; public abstract class Message { public Message() { } - public abstract void encode(ByteBuf out); - public abstract void decode(ByteBuf in); + public abstract void encode(ChannelHandlerContext ctx, ByteBuf out); + public abstract void decode(ChannelHandlerContext ctx, ByteBuf in); public void handle_Server(ChannelHandlerContext ctx) { throw new UnsupportedOperationException(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageDecoder.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageDecoder.java similarity index 80% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageDecoder.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageDecoder.java index 0a35f4fc0..de2b4c7d1 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageDecoder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageDecoder.java @@ -1,4 +1,4 @@ -package com.seibel.distanthorizons.core.network.messageHandling; +package com.seibel.distanthorizons.core.network.protocol; import com.seibel.distanthorizons.core.network.messages.Message; import io.netty.buffer.ByteBuf; @@ -8,7 +8,7 @@ import io.netty.handler.codec.ByteToMessageDecoder; import java.util.List; public class MessageDecoder extends ByteToMessageDecoder { - private MessageRegistry messageRegistry; + private final MessageRegistry messageRegistry; public MessageDecoder(MessageRegistry messageRegistry) { this.messageRegistry = messageRegistry; @@ -17,7 +17,7 @@ public class MessageDecoder extends ByteToMessageDecoder { @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { Message message = messageRegistry.createMessage(in.readShort()); - message.decode(in); + message.decode(ctx, in); out.add(message); } } 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 new file mode 100644 index 000000000..43603e25a --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageEncoder.java @@ -0,0 +1,20 @@ +package com.seibel.distanthorizons.core.network.protocol; + +import com.seibel.distanthorizons.core.network.messages.Message; +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; + +public class MessageEncoder extends MessageToByteEncoder { + private final MessageRegistry messageRegistry; + + public MessageEncoder(MessageRegistry messageRegistry) { + this.messageRegistry = messageRegistry; + } + + @Override + protected void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) throws IllegalArgumentException { + out.writeShort(messageRegistry.getMessageId(msg)); + msg.encode(ctx, out); + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java similarity index 92% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageHandler.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java index 20817f27b..19ed385d7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandler.java @@ -1,4 +1,4 @@ -package com.seibel.distanthorizons.core.network.messageHandling; +package com.seibel.distanthorizons.core.network.protocol; import com.seibel.distanthorizons.core.network.messages.Message; import io.netty.channel.ChannelHandlerContext; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandlerSide.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandlerSide.java new file mode 100644 index 000000000..3e44e1642 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageHandlerSide.java @@ -0,0 +1,6 @@ +package com.seibel.distanthorizons.core.network.protocol; + +public enum MessageHandlerSide { + CLIENT, + SERVER +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageRegistry.java b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java similarity index 94% rename from core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageRegistry.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java index 77b849501..f6f702461 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messageHandling/MessageRegistry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/protocol/MessageRegistry.java @@ -1,4 +1,4 @@ -package com.seibel.distanthorizons.core.network.messageHandling; +package com.seibel.distanthorizons.core.network.protocol; import com.seibel.distanthorizons.core.network.messages.HelloMessage; import com.seibel.distanthorizons.core.network.messages.Message; From 2358797a820ef44a137dc271b4be1cec6bdfbcaa Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Wed, 28 Jun 2023 22:46:49 +0500 Subject: [PATCH 3/3] Minimal working client-server No cleanup logic yet --- .../transformers/ChunkToLodBuilder.java | 4 +- .../core/network/LodClient.java | 24 ------ .../core/network/LodServer.java | 66 ---------------- .../core/network/NetworkClient.java | 77 +++++++++++++++++++ .../core/network/NetworkServer.java | 45 +++++++++++ .../core/network/messages/HelloMessage.java | 12 ++- .../core/network/protocol/MessageDecoder.java | 8 +- .../core/network/protocol/MessageEncoder.java | 8 +- .../network/protocol/MessageRegistry.java | 21 ++--- .../core/util/NetworkUtil.java | 30 ++++++++ .../core/world/DhClientWorld.java | 7 +- .../core/world/DhServerWorld.java | 4 +- 12 files changed, 189 insertions(+), 117 deletions(-) delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/LodClient.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/LodServer.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/util/NetworkUtil.java 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 6ef86d61e..3841e293b 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 @@ -77,8 +77,10 @@ public class ChunkToLodBuilder implements AutoCloseable { return; } - else if (!MC.playerExists()) + 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/network/LodClient.java b/core/src/main/java/com/seibel/distanthorizons/core/network/LodClient.java deleted file mode 100644 index 9bab5d702..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/LodClient.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.seibel.distanthorizons.core.network; - -import io.netty.handler.codec.LengthFieldBasedFrameDecoder; -//import net.minecraft.client.Minecraft; - -public class LodClient { -// public static final LodClient INSTANCE = new LodClient(); -// -// private final Minecraft client; -// private final NetworkHandler networkHandler; -// -// private LodClient() { -// client = Minecraft.getInstance(); -// this.networkHandler = new NetworkHandler(); -// } - - public void connect() { - - } - - public void disconnect() { - - } -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/LodServer.java b/core/src/main/java/com/seibel/distanthorizons/core/network/LodServer.java deleted file mode 100644 index 0866ecaa7..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/LodServer.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.seibel.distanthorizons.core.network; - -import com.seibel.distanthorizons.core.network.protocol.*; -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.ChannelPipeline; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.SocketChannel; -import io.netty.channel.socket.nio.NioServerSocketChannel; -import io.netty.handler.codec.LengthFieldBasedFrameDecoder; -import io.netty.handler.codec.LengthFieldPrepender; -import io.netty.handler.logging.LogLevel; -import io.netty.handler.logging.LoggingHandler; -import org.jetbrains.annotations.NotNull; - -public class LodServer { - // TODO move to config of some sort - static final int PORT = 25049; - - public LodServer(/* initial settings */) { - - } - - public void start() throws InterruptedException { - EventLoopGroup bossGroup = new NioEventLoopGroup(1); - EventLoopGroup workerGroup = new NioEventLoopGroup(); - try { - ServerBootstrap b = new ServerBootstrap(); - b.group(bossGroup, workerGroup) - .channel(NioServerSocketChannel.class) - .handler(new LoggingHandler(LogLevel.INFO)) - .childHandler(getInitializer()); - - b.bind(PORT).sync().channel().closeFuture().sync(); - } finally { - bossGroup.shutdownGracefully(); - workerGroup.shutdownGracefully(); - } - } - - public void stop() { - - } - - private ChannelInitializer getInitializer() { - return new ChannelInitializer() { - @Override - public void initChannel(@NotNull SocketChannel ch) { - ChannelPipeline pipeline = ch.pipeline(); - - MessageRegistry messageRegistry = new MessageRegistry(); - - // Encoding - pipeline.addLast(new LengthFieldBasedFrameDecoder(Short.MAX_VALUE, 0, Short.BYTES, 0, Short.BYTES)); - pipeline.addLast(new MessageDecoder(messageRegistry)); - - // Encoder - pipeline.addLast(new LengthFieldPrepender(Short.BYTES)); - pipeline.addLast(new MessageEncoder(messageRegistry)); - - pipeline.addLast(new MessageHandler(MessageHandlerSide.SERVER)); - } - }; - } -} 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 new file mode 100644 index 000000000..6ed422cc6 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkClient.java @@ -0,0 +1,77 @@ +package com.seibel.distanthorizons.core.network; + +import com.seibel.distanthorizons.core.network.messages.HelloMessage; +import com.seibel.distanthorizons.core.util.NetworkUtil; +import com.seibel.distanthorizons.core.network.protocol.MessageHandlerSide; +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.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioSocketChannel; + +import java.io.Closeable; +import java.io.IOException; +import java.util.concurrent.TimeUnit; + +public class NetworkClient implements Closeable { + private enum State { + OPEN, + RECONNECT, + CLOSED + } + + private static final int FAILURE_RECONNECT_DELAY_SEC = 5; + + // TODO move to config of some sort + private final String host; + private final int port; + private State state = State.OPEN; + + private final Bootstrap clientBootstrap; + private final EventLoopGroup workerGroup = new NioEventLoopGroup(); + private Channel channel; + + public NetworkClient(String host, int port) { + this.host = host; + this.port = port; + + clientBootstrap = new Bootstrap() + .group(workerGroup) + .channel(NioSocketChannel.class) + .option(ChannelOption.SO_KEEPALIVE, true) + .handler(NetworkUtil.getChannelInitializer(MessageHandlerSide.CLIENT)); + connect(); + } + + private void connect() { + ChannelFuture connectFuture = clientBootstrap.connect(host, port); + connectFuture.addListener((ChannelFuture channelFuture) -> { + if (!channelFuture.isSuccess()) return; + + channel = channelFuture.channel(); + channel.writeAndFlush(new HelloMessage()); + }); + + channel = connectFuture.channel(); + channel.closeFuture().addListener((ChannelFuture channelFuture) -> { + if (state == State.CLOSED) return; + + workerGroup.schedule(this::connect, state == State.RECONNECT ? 0 : FAILURE_RECONNECT_DELAY_SEC, TimeUnit.SECONDS); + state = State.OPEN; + }); + } + + /** Kills the current connection, triggering auto-reconnection immediately. */ + public void reconnect() { + state = State.RECONNECT; + channel.disconnect(); + } + + @Override + public void close() throws IOException { + state = State.CLOSED; + workerGroup.shutdownGracefully(); + } +} 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 new file mode 100644 index 000000000..3149d7e1a --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/NetworkServer.java @@ -0,0 +1,45 @@ +package com.seibel.distanthorizons.core.network; + +import com.seibel.distanthorizons.core.network.protocol.*; +import com.seibel.distanthorizons.core.util.NetworkUtil; +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.ChannelFuture; +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 java.io.Closeable; +import java.io.IOException; + +public class NetworkServer implements Closeable { + // TODO move to config of some sort + private final int port; + + EventLoopGroup bossGroup = new NioEventLoopGroup(1); + EventLoopGroup workerGroup = new NioEventLoopGroup(); + + public NetworkServer(int port) { + this.port = port; + + ServerBootstrap b = new ServerBootstrap() + .group(bossGroup, workerGroup) + .channel(NioServerSocketChannel.class) + .handler(new LoggingHandler(LogLevel.INFO)) + .childHandler(NetworkUtil.getChannelInitializer(MessageHandlerSide.SERVER)); + + b.bind(port) + .addListener((ChannelFuture channelFuture) -> { + if (!channelFuture.isSuccess()) + throw new RuntimeException("Failed to bind: " + channelFuture); + }) + .channel().closeFuture().addListener(future -> close()); + } + + @Override + public void close() throws IOException { + bossGroup.shutdownGracefully(); + workerGroup.shutdownGracefully(); + } +} 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 d5b64398a..c790e72a9 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,12 +1,17 @@ package com.seibel.distanthorizons.core.network.messages; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.coreapi.ModInfo; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; +import org.apache.logging.log4j.Level; +import org.apache.logging.log4j.Logger; // This message is critical to maintain backwards compatibility // as it's used to receive version before everything else. public class HelloMessage extends Message { + private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + public int version = ModInfo.PROTOCOL_VERSION; @Override @@ -21,6 +26,11 @@ public class HelloMessage extends Message { @Override public void handle_Server(ChannelHandlerContext ctx) { - // TODO Adjust message handling to client's version + LOGGER.log(Level.INFO, "Client version: " + version); + } + + @Override + public void handle_Client(ChannelHandlerContext ctx) { + LOGGER.log(Level.INFO, "Server version: " + version); } } 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 de2b4c7d1..82b653c56 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 @@ -8,15 +8,9 @@ import io.netty.handler.codec.ByteToMessageDecoder; import java.util.List; public class MessageDecoder extends ByteToMessageDecoder { - private final MessageRegistry messageRegistry; - - public MessageDecoder(MessageRegistry messageRegistry) { - this.messageRegistry = messageRegistry; - } - @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { - Message message = messageRegistry.createMessage(in.readShort()); + Message message = MessageRegistry.INSTANCE.createMessage(in.readShort()); message.decode(ctx, in); out.add(message); } 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 43603e25a..5b491ccc7 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 @@ -6,15 +6,9 @@ import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; public class MessageEncoder extends MessageToByteEncoder { - private final MessageRegistry messageRegistry; - - public MessageEncoder(MessageRegistry messageRegistry) { - this.messageRegistry = messageRegistry; - } - @Override protected void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) throws IllegalArgumentException { - out.writeShort(messageRegistry.getMessageId(msg)); + out.writeShort(MessageRegistry.INSTANCE.getMessageId(msg)); msg.encode(ctx, out); } } 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 f6f702461..e02ba0b20 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 @@ -6,24 +6,27 @@ import com.seibel.distanthorizons.core.network.messages.Message; import java.util.HashMap; import java.util.Map; import java.util.function.Supplier; -import java.util.stream.Collectors; public class MessageRegistry { - private final Map> idToConstructor = new HashMap>() {{ + public static final MessageRegistry INSTANCE = new MessageRegistry() {{ // Note: Make sure IDs are unique // Also note: Removing IDs will break backwards compatibility - put(1, HelloMessage::new); + registerMessage(1, HelloMessage.class, HelloMessage::new); }}; - private final Map, Integer> classToId = idToConstructor.entrySet().stream() - .collect(Collectors.toMap( - e -> e.getValue().getClass(), - Map.Entry::getKey - )); + private final Map> idToSupplier = new HashMap<>(); + private final Map, Integer> classToId = new HashMap<>(); + + private MessageRegistry() { } + + public void registerMessage(int id, Class clazz, Supplier supplier) { + idToSupplier.put(id, (Supplier) supplier); + classToId.put(clazz, id); + } public Message createMessage(int id) throws IllegalArgumentException { try { - return idToConstructor.get(id).get(); + return idToSupplier.get(id).get(); } catch (NullPointerException e) { throw new IllegalArgumentException("Invalid message ID"); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/NetworkUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/NetworkUtil.java new file mode 100644 index 000000000..a67d68bb1 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/NetworkUtil.java @@ -0,0 +1,30 @@ +package com.seibel.distanthorizons.core.util; + +import com.seibel.distanthorizons.core.network.protocol.*; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.socket.SocketChannel; +import io.netty.handler.codec.LengthFieldBasedFrameDecoder; +import io.netty.handler.codec.LengthFieldPrepender; +import org.jetbrains.annotations.NotNull; + +public class NetworkUtil { + public static ChannelInitializer getChannelInitializer(MessageHandlerSide side) { + return new ChannelInitializer() { + @Override + public void initChannel(@NotNull SocketChannel ch) { + ChannelPipeline pipeline = ch.pipeline(); + + // Encoding + pipeline.addLast(new LengthFieldBasedFrameDecoder(Short.MAX_VALUE, 0, Short.BYTES, 0, Short.BYTES)); + pipeline.addLast(new MessageDecoder()); + + // Encoder + pipeline.addLast(new LengthFieldPrepender(Short.BYTES)); + pipeline.addLast(new MessageEncoder()); + + pipeline.addLast(new MessageHandler(side)); + } + }; + } +} 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 08b12a129..90247a8e8 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 @@ -3,6 +3,7 @@ package com.seibel.distanthorizons.core.world; 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.NetworkClient; import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.util.objects.EventLoop; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; @@ -17,6 +18,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld { private final HashMap levels; public final ClientOnlySaveStructure saveStructure; + private final NetworkClient networkClient; // TODO why does this executor have 2 threads? public ExecutorService dhTickerThread = ThreadUtil.makeSingleThreadPool("DH Client World Ticker Thread", 2); @@ -27,8 +29,11 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld public DhClientWorld() { super(EWorldEnvironment.Client_Only); - this.saveStructure = new ClientOnlySaveStructure(); + + this.saveStructure = new ClientOnlySaveStructure(); this.levels = new HashMap<>(); + this.networkClient = new NetworkClient("127.0.0.1", 25049); + LOGGER.info("Started DhWorld of type "+this.environment); } 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 aa0574629..c32dcbe3d 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 @@ -3,6 +3,7 @@ package com.seibel.distanthorizons.core.world; 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.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; @@ -15,7 +16,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld { private final HashMap levels; public final LocalSaveStructure saveStructure; - + private final NetworkServer networkServer; public DhServerWorld() @@ -24,6 +25,7 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld this.saveStructure = new LocalSaveStructure(); this.levels = new HashMap<>(); + this.networkServer = new NetworkServer(25049); LOGGER.info("Started "+DhServerWorld.class.getSimpleName()+" of type "+this.environment); }