The real server side
(not tested)
This commit is contained in:
@@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
+21
@@ -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);
|
||||
}
|
||||
}
|
||||
+2
-4
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
+16
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+24
@@ -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());
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-10
@@ -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)
|
||||
|
||||
+9
@@ -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();
|
||||
}
|
||||
Reference in New Issue
Block a user