From 748b484377d580e259fdc45812a8a82d4d722c24 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 30 Jun 2023 22:05:02 +0500 Subject: [PATCH] The real server side (not tested) --- .../core/api/internal/ServerApi.java | 21 +++++++- .../core/network/NetworkClient.java | 7 +-- .../core/network/NetworkEventSource.java | 8 +-- .../core/network/NetworkServer.java | 3 +- .../core/network/messages/CloseMessage.java | 20 ++++++++ .../network/messages/CloseReasonMessage.java | 21 ++++++++ .../core/network/messages/HelloMessage.java | 6 +-- .../network/messages/LodConfigMessage.java | 16 ++++++ .../core/network/messages/Message.java | 18 +++++-- .../network/messages/PlayerIdMessage.java | 24 +++++++++ .../core/network/protocol/MessageDecoder.java | 2 +- .../core/network/protocol/MessageEncoder.java | 2 +- .../core/network/protocol/MessageHandler.java | 12 +---- .../distanthorizons/core/world/DhPlayer.java | 19 +++++++ .../core/world/DhServerWorld.java | 50 ++++++++++++++++++- .../misc/IServerPlayerWrapper.java | 9 ++++ 16 files changed, 207 insertions(+), 31 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseMessage.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseReasonMessage.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/LodConfigMessage.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/network/messages/PlayerIdMessage.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/world/DhPlayer.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.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 b6b8b8040..35fef56f8 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,6 +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.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.world.AbstractDhWorld; @@ -159,5 +160,23 @@ public class ServerApi } } } - + + public void serverPlayerJoinEvent(IServerPlayerWrapper player) + { + IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); + if (serverWorld instanceof DhServerWorld) + { + LOGGER.debug("Waiting for player to connect: " + player.getUUID()); + ((DhServerWorld) serverWorld).addPlayer(player); + } + } + public void serverPlayerDisconnectEvent(IServerPlayerWrapper player) + { + IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); + if (serverWorld instanceof DhServerWorld) + { + LOGGER.debug("Removing player from connect wait list: " + player.getUUID()); + ((DhServerWorld) serverWorld).removePlayer(player); + } + } } 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 bd553ed78..7fc6df361 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,6 +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.HelloMessage; import com.seibel.distanthorizons.core.network.protocol.DhNetworkChannelInitializer; import io.netty.bootstrap.Bootstrap; @@ -63,11 +64,11 @@ public class NetworkClient extends NetworkEventSource implements AutoCloseable { }); registerHandler(HelloMessage.class, (msg, ctx) -> { - LOGGER.info("Connected"); + LOGGER.info("Connected."); }); - registerDisconnectHandler(ctx -> { - LOGGER.info("Disconnected"); + registerHandler(CloseMessage.class, (msg, ctx) -> { + LOGGER.info("Disconnected."); }); } 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 7729eafac..df7338896 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 @@ -17,8 +17,7 @@ public abstract class NetworkEventSource implements AutoCloseable { registerHandler(HelloMessage.class, (msg, ctx) -> { if (msg.version != ModInfo.PROTOCOL_VERSION) { try { - closeReason = "Protocol version mismatch"; - close(); + close("Protocol version mismatch"); } catch (Exception e) { throw new RuntimeException(e); } @@ -30,7 +29,8 @@ public abstract class NetworkEventSource implements AutoCloseable { messageHandler.registerHandler(clazz, handler); } - public void registerDisconnectHandler(Consumer handler) { - messageHandler.registerDisconnectHandler(handler); + public void close(String reason) throws Exception { + closeReason = reason; + 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 48fc1b7c5..1bcb84431 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,6 +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.HelloMessage; import com.seibel.distanthorizons.core.network.protocol.*; import io.netty.bootstrap.ServerBootstrap; @@ -50,7 +51,7 @@ public class NetworkServer extends NetworkEventSource implements AutoCloseable { ctx.channel().writeAndFlush(new HelloMessage()); }); - registerDisconnectHandler(ctx -> { + registerHandler(CloseMessage.class, (msg, ctx) -> { LOGGER.info("Client disconnected: {}", ctx.channel().remoteAddress()); }); } 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 new file mode 100644 index 000000000..aed5933c6 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseMessage.java @@ -0,0 +1,20 @@ +package com.seibel.distanthorizons.core.network.messages; + +import io.netty.buffer.ByteBuf; + +/** + * This is not a "real" message, and only used as indication of disconnection. + * To send a "disconnect reason" message, use {@link CloseReasonMessage}. + */ +public class CloseMessage extends Message { + @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/CloseReasonMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseReasonMessage.java new file mode 100644 index 000000000..b6f467768 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/CloseReasonMessage.java @@ -0,0 +1,21 @@ +package com.seibel.distanthorizons.core.network.messages; + +import io.netty.buffer.ByteBuf; + +public class CloseReasonMessage extends Message { + public String reason; + + public CloseReasonMessage(String reason) { + this.reason = reason; + } + + @Override + public void encode(ByteBuf out) { + encodeString(reason, out); + } + + @Override + public void decode(ByteBuf in) { + reason = decodeString(in); + } +} 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 c29490b23..d52f17488 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 @@ -8,17 +8,15 @@ import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Logger; public class HelloMessage extends Message { - private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - public int version = ModInfo.PROTOCOL_VERSION; @Override - public void encode(ChannelHandlerContext ctx, ByteBuf out) { + public void encode(ByteBuf out) { out.writeInt(version); } @Override - public void decode(ChannelHandlerContext ctx, ByteBuf in) { + public void decode(ByteBuf in) { version = in.readInt(); } } 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 new file mode 100644 index 000000000..54b114d33 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/LodConfigMessage.java @@ -0,0 +1,16 @@ +package com.seibel.distanthorizons.core.network.messages; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; + +public class LodConfigMessage extends Message { + @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/Message.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/Message.java index decb24d7e..4958f8e18 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 @@ -3,10 +3,20 @@ package com.seibel.distanthorizons.core.network.messages; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; -public abstract class Message { - public Message() { } +import java.nio.charset.StandardCharsets; - public abstract void encode(ChannelHandlerContext ctx, ByteBuf out); - public abstract void decode(ChannelHandlerContext ctx, ByteBuf in); +public abstract class Message { + public abstract void encode(ByteBuf out); + public abstract void decode(ByteBuf in); + + protected void encodeString(String str, ByteBuf out) { + out.writeShort(str.length()); + out.writeBytes(str.getBytes(StandardCharsets.UTF_8)); + } + + protected String decodeString(ByteBuf in) { + int length = in.readShort(); + return new String(in.readBytes(length).array(), StandardCharsets.UTF_8); + } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PlayerIdMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PlayerIdMessage.java new file mode 100644 index 000000000..a071b8cda --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/PlayerIdMessage.java @@ -0,0 +1,24 @@ +package com.seibel.distanthorizons.core.network.messages; + +import io.netty.buffer.ByteBuf; + +import java.util.UUID; + +public class PlayerIdMessage extends Message { + public UUID playerUUID; + + public PlayerIdMessage(UUID playerUUID) { + this.playerUUID = playerUUID; + } + + @Override + public void encode(ByteBuf out) { + out.writeLong(playerUUID.getMostSignificantBits()); + out.writeLong(playerUUID.getLeastSignificantBits()); + } + + @Override + public void decode(ByteBuf in) { + playerUUID = new UUID(in.readLong(), in.readLong()); + } +} 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 82b653c56..092c20dbf 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 ctx, ByteBuf in, List out) { Message message = MessageRegistry.INSTANCE.createMessage(in.readShort()); - message.decode(ctx, in); + message.decode(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 5b491ccc7..f1e54e4d1 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 @@ -9,6 +9,6 @@ public class MessageEncoder extends MessageToByteEncoder { @Override protected void encode(ChannelHandlerContext ctx, Message msg, ByteBuf out) throws IllegalArgumentException { out.writeShort(MessageRegistry.INSTANCE.getMessageId(msg)); - msg.encode(ctx, out); + msg.encode(out); } } 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 90e84f1b6..e3e0b9e40 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,9 +1,8 @@ package com.seibel.distanthorizons.core.network.protocol; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.network.messages.HelloMessage; +import com.seibel.distanthorizons.core.network.messages.CloseMessage; import com.seibel.distanthorizons.core.network.messages.Message; -import com.seibel.distanthorizons.coreapi.ModInfo; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.SimpleChannelInboundHandler; @@ -15,24 +14,18 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; 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 Map, List>> handlers = new HashMap<>(); - private List> disconnectHandlers = new LinkedList<>(); public void registerHandler(Class clazz, BiConsumer handler) { handlers.computeIfAbsent(clazz, k -> new LinkedList<>()) .add((BiConsumer) handler); } - public void registerDisconnectHandler(Consumer handler) { - disconnectHandlers.add(handler); - } - @Override protected void channelRead0(ChannelHandlerContext ctx, Message msg) { List> handlerList = handlers.get(msg.getClass()); @@ -47,7 +40,6 @@ public class MessageHandler extends SimpleChannelInboundHandler { @Override public void channelInactive(@NotNull ChannelHandlerContext ctx) { - for (Consumer handler : disconnectHandlers) - handler.accept(ctx); + channelRead0(ctx, new CloseMessage()); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhPlayer.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhPlayer.java new file mode 100644 index 000000000..4d641e49d --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhPlayer.java @@ -0,0 +1,19 @@ +package com.seibel.distanthorizons.core.world; + +import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; +import io.netty.channel.ChannelHandlerContext; + +public class DhPlayer { + public IServerPlayerWrapper serverPlayer; + public Config config; + public ChannelHandlerContext ctx; + + public DhPlayer(IServerPlayerWrapper serverPlayer) { + this.serverPlayer = serverPlayer; + } + + public static class Config { + // TODO Replace this example with actually useful fields + public int renderDistance; + } +} 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 dda84d78e..2fbfadfd9 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 @@ -4,20 +4,30 @@ 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.CloseMessage; +import com.seibel.distanthorizons.core.network.messages.CloseReasonMessage; +import com.seibel.distanthorizons.core.network.messages.HelloMessage; +import com.seibel.distanthorizons.core.network.messages.PlayerIdMessage; 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 io.netty.channel.ChannelHandlerContext; import java.io.File; import java.util.HashMap; +import java.util.UUID; import java.util.concurrent.CompletableFuture; public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld { private final HashMap levels; public final LocalSaveStructure saveStructure; + private final NetworkServer networkServer; - + private final HashMap players; + private final HashMap connections; + public DhServerWorld() { @@ -28,11 +38,47 @@ public class DhServerWorld extends AbstractDhWorld implements IDhServerWorld // TODO move to global config once server specific configs are implemented this.networkServer = new NetworkServer(25049); + this.players = new HashMap<>(); + this.connections = new HashMap<>(); + registerNetworkHandlers(); LOGGER.info("Started "+DhServerWorld.class.getSimpleName()+" of type "+this.environment); } + + private void registerNetworkHandlers() { + networkServer.registerHandler(PlayerIdMessage.class, (msg, ctx) -> { + DhPlayer dhPlayer = players.get(msg.playerUUID); + + if (dhPlayer == null) { + ctx.writeAndFlush(new CloseReasonMessage("Player is not logged in.")) + .addListener(future -> ctx.close()); + return; + } + + if (dhPlayer.ctx != null) { + ctx.writeAndFlush(new CloseReasonMessage("Another connection is already in use.")) + .addListener(future -> ctx.close()); + return; + } + + dhPlayer.ctx = ctx; + connections.put(ctx, dhPlayer); + }); + + networkServer.registerHandler(CloseMessage.class, (msg, ctx) -> { + DhPlayer dhPlayer = connections.remove(ctx); + if (dhPlayer != null) + dhPlayer.ctx = null; + }); + } - + public void addPlayer(IServerPlayerWrapper serverPlayer) { + players.put(serverPlayer.getUUID(), new DhPlayer(serverPlayer)); + } + + public void removePlayer(IServerPlayerWrapper serverPlayer) { + players.remove(serverPlayer.getUUID()); + } @Override public DhServerLevel getOrLoadLevel(ILevelWrapper wrapper) 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 new file mode 100644 index 000000000..7ac391ecd --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/misc/IServerPlayerWrapper.java @@ -0,0 +1,9 @@ +package com.seibel.distanthorizons.core.wrapperInterfaces.misc; + +import com.seibel.distanthorizons.api.interfaces.IDhApiUnsafeWrapper; + +import java.util.UUID; + +public interface IServerPlayerWrapper extends IDhApiUnsafeWrapper { + UUID getUUID(); +}