The real server side

(not tested)
This commit is contained in:
s809
2023-06-30 22:05:02 +05:00
parent e288302bfa
commit 748b484377
16 changed files with 207 additions and 31 deletions
@@ -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);
}
}
}
@@ -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.");
});
}
@@ -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<ChannelHandlerContext> handler) {
messageHandler.registerDisconnectHandler(handler);
public void close(String reason) throws Exception {
closeReason = reason;
close();
}
}
@@ -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());
});
}
@@ -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.");
}
}
@@ -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);
}
}
@@ -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();
}
}
@@ -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) {
}
}
@@ -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);
}
}
@@ -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());
}
}
@@ -11,7 +11,7 @@ public class MessageDecoder extends ByteToMessageDecoder {
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) {
Message message = MessageRegistry.INSTANCE.createMessage(in.readShort());
message.decode(ctx, in);
message.decode(in);
out.add(message);
}
}
@@ -9,6 +9,6 @@ public class MessageEncoder extends MessageToByteEncoder<Message> {
@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);
}
}
@@ -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<Message> {
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private Map<Class<? extends Message>, List<BiConsumer<Message, ChannelHandlerContext>>> handlers = new HashMap<>();
private List<Consumer<ChannelHandlerContext>> disconnectHandlers = new LinkedList<>();
public <T extends Message> void registerHandler(Class<T> clazz, BiConsumer<T, ChannelHandlerContext> handler) {
handlers.computeIfAbsent(clazz, k -> new LinkedList<>())
.add((BiConsumer<Message, ChannelHandlerContext>) handler);
}
public void registerDisconnectHandler(Consumer<ChannelHandlerContext> handler) {
disconnectHandlers.add(handler);
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, Message msg) {
List<BiConsumer<Message, ChannelHandlerContext>> handlerList = handlers.get(msg.getClass());
@@ -47,7 +40,6 @@ public class MessageHandler extends SimpleChannelInboundHandler<Message> {
@Override
public void channelInactive(@NotNull ChannelHandlerContext ctx) {
for (Consumer<ChannelHandlerContext> handler : disconnectHandlers)
handler.accept(ctx);
channelRead0(ctx, new CloseMessage());
}
}
@@ -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;
}
}
@@ -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<IServerLevelWrapper, DhServerLevel> levels;
public final LocalSaveStructure saveStructure;
private final NetworkServer networkServer;
private final HashMap<UUID, DhPlayer> players;
private final HashMap<ChannelHandlerContext, DhPlayer> 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)
@@ -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();
}