diff --git a/build.gradle b/build.gradle index 3277d27..d3f7274 100644 --- a/build.gradle +++ b/build.gradle @@ -11,13 +11,15 @@ repositories { dependencies { testCompile group: 'junit', name: 'junit', version: '4.12' + compile group: 'org.yaml', name: 'snakeyaml', version: '1.27' + compile files('libs/napi-configurate-yaml-1.0.jar') compile group: 'io.netty', name: 'netty-all', version: '4.1.54.Final' compile group: 'net.kyori', name: 'adventure-nbt', version: '4.1.1' } jar { manifest { - attributes("Main-Class": "ru.nanit.limbo.NanoLimbo") + attributes('Main-Class': 'ru.nanit.limbo.NanoLimbo') } from { diff --git a/libs/napi-configurate-yaml-1.0.jar b/libs/napi-configurate-yaml-1.0.jar new file mode 100644 index 0000000..cc69a12 Binary files /dev/null and b/libs/napi-configurate-yaml-1.0.jar differ diff --git a/src/main/java/ru/nanit/limbo/LimboConfig.java b/src/main/java/ru/nanit/limbo/LimboConfig.java deleted file mode 100644 index 1dc7189..0000000 --- a/src/main/java/ru/nanit/limbo/LimboConfig.java +++ /dev/null @@ -1,217 +0,0 @@ -package ru.nanit.limbo; - -import ru.nanit.limbo.protocol.packets.play.PacketBossBar; -import ru.nanit.limbo.util.Colors; -import ru.nanit.limbo.util.Logger; - -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; -import java.util.Properties; - -public final class LimboConfig { - - private static String host; - private static int port; - private static int maxPlayers; - private static String dimensionType; - private static SpawnPosition spawnPosition; - private static IpForwardingType ipForwardingType; - private static long readTimeout; - private static PingData pingData; - private static int debugLevel = 3; - private static JoinMessages joinMessages; - - public static void load(Path file) throws IOException { - if (!Files.exists(file)){ - Files.copy(NanoLimbo.getResource("/settings.properties"), file); - } - - Properties props = new Properties(); - props.load(Files.newInputStream(file)); - - host = props.getProperty("host"); - port = Integer.parseInt(props.getProperty("port")); - - maxPlayers = Integer.parseInt(props.getProperty("max-players")); - - dimensionType = props.getProperty("dimension"); - - String[] posData = props.getProperty("spawn-position").split(","); - - if (posData.length != 3){ - throw new IOException("Invalid spawn position. Check it in the settings.properties file"); - } - - spawnPosition = new SpawnPosition(Double.parseDouble(posData[0]), - Double.parseDouble(posData[1]), - Double.parseDouble(posData[2])); - - ipForwardingType = IpForwardingType.valueOf(props.getProperty("ip-forwarding").toUpperCase()); - readTimeout = Long.parseLong(props.getProperty("read-timeout")); - - pingData = new PingData(); - pingData.setVersion(props.getProperty("ping-version")); - pingData.setDescription(Colors.of(props.getProperty("ping-description"))); - - debugLevel = Integer.parseInt(props.getProperty("debug-level")); - - joinMessages = new JoinMessages(); - - if(props.containsKey("join-message")){ - joinMessages.setChatMessage(Colors.of(props.getProperty("join-message"))); - } - - if(props.containsKey("join-bossbar-text")){ - joinMessages.setBossBarText(Colors.of(props.getProperty("join-bossbar-text"))); - joinMessages.setBossBarHealth(Float.parseFloat(props.getProperty("join-bossbar-health"))); - joinMessages.setBossBarColor(PacketBossBar.Color.valueOf( - props.getProperty("join-bossbar-color").toUpperCase())); - joinMessages.setBossBarDivision(PacketBossBar.Division.valueOf( - props.getProperty("join-bossbar-division").toUpperCase())); - } - } - - public static String getHost() { - return host; - } - - public static int getPort() { - return port; - } - - public static int getMaxPlayers() { - return maxPlayers; - } - - public static String getDimensionType() { - return dimensionType; - } - - public static SpawnPosition getSpawnPosition() { - return spawnPosition; - } - - public static IpForwardingType getIpForwardingType() { - return ipForwardingType; - } - - public static long getReadTimeout() { - return readTimeout; - } - - public static PingData getPingData() { - return pingData; - } - - public static int getDebugLevel() { - return debugLevel; - } - - public static JoinMessages getJoinMessages() { - return joinMessages; - } - - public enum IpForwardingType { - NONE, - LEGACY, - MODERN - } - - public static class SpawnPosition { - - private final double x; - private final double y; - private final double z; - - public SpawnPosition(double x, double y, double z) { - this.x = x; - this.y = y; - this.z = z; - } - - public double getX() { - return x; - } - - public double getY() { - return y; - } - - public double getZ() { - return z; - } - } - - public static class PingData { - - private String version; - private String description; - - public String getVersion() { - return version; - } - - public void setVersion(String version) { - this.version = version; - } - - public String getDescription() { - return description; - } - - public void setDescription(String description) { - this.description = description; - } - } - - public static class JoinMessages { - - private String chatMessage; - private String bossBarText; - private float bossBarHealth; - private PacketBossBar.Color bossBarColor; - private PacketBossBar.Division bossBarDivision; - - public String getChatMessage() { - return chatMessage; - } - - public void setChatMessage(String chatMessage) { - this.chatMessage = chatMessage; - } - - public String getBossBarText() { - return bossBarText; - } - - public void setBossBarText(String bossBarText) { - this.bossBarText = bossBarText; - } - - public float getBossBarHealth() { - return bossBarHealth; - } - - public void setBossBarHealth(float bossBarHealth) { - this.bossBarHealth = bossBarHealth; - } - - public PacketBossBar.Color getBossBarColor() { - return bossBarColor; - } - - public void setBossBarColor(PacketBossBar.Color bossBarColor) { - this.bossBarColor = bossBarColor; - } - - public PacketBossBar.Division getBossBarDivision() { - return bossBarDivision; - } - - public void setBossBarDivision(PacketBossBar.Division bossBarDivision) { - this.bossBarDivision = bossBarDivision; - } - } - -} diff --git a/src/main/java/ru/nanit/limbo/NanoLimbo.java b/src/main/java/ru/nanit/limbo/NanoLimbo.java index 1f5d957..a68c977 100644 --- a/src/main/java/ru/nanit/limbo/NanoLimbo.java +++ b/src/main/java/ru/nanit/limbo/NanoLimbo.java @@ -3,8 +3,6 @@ package ru.nanit.limbo; import ru.nanit.limbo.server.LimboServer; import ru.nanit.limbo.util.Logger; -import java.io.InputStream; - public final class NanoLimbo { public static void main(String[] args){ @@ -15,7 +13,4 @@ public final class NanoLimbo { } } - public static InputStream getResource(String path){ - return NanoLimbo.class.getResourceAsStream(path); - } } diff --git a/src/main/java/ru/nanit/limbo/configuration/LimboConfig.java b/src/main/java/ru/nanit/limbo/configuration/LimboConfig.java new file mode 100644 index 0000000..2208773 --- /dev/null +++ b/src/main/java/ru/nanit/limbo/configuration/LimboConfig.java @@ -0,0 +1,109 @@ +package ru.nanit.limbo.configuration; + +import napi.configurate.Configuration; +import napi.configurate.source.ConfigSources; +import napi.configurate.yaml.YamlConfiguration; +import ru.nanit.limbo.server.data.*; +import ru.nanit.limbo.util.Colors; + +import java.net.SocketAddress; +import java.nio.file.Path; + +public final class LimboConfig { + + private final Path root; + + private SocketAddress address; + private int maxPlayers; + private PingData pingData; + + private String dimensionType; + private Position spawnPosition; + + private boolean useJoinMessage; + private boolean useBossBar; + private String joinMessage; + private BossBar bossBar; + + private InfoForwarding infoForwarding; + private long readTimeout; + private int debugLevel = 3; + + public LimboConfig(Path root){ + this.root = root; + } + + public void load() throws Exception { + Configuration conf = YamlConfiguration.builder() + .source(ConfigSources.resource("/limbo.yml", this).copyTo(root)) + .build(); + + conf.reload(); + + address = conf.getNode("bind").getValue(SocketAddress.class); + maxPlayers = conf.getNode("maxPlayers").getInt(); + pingData = conf.getNode("ping").getValue(PingData.class); + dimensionType = conf.getNode("dimension").getString(); + spawnPosition = conf.getNode("spawnPosition").getValue(Position.class); + useJoinMessage = conf.getNode("joinMessage", "enable").getBoolean(); + useBossBar = conf.getNode("bossBar", "enable").getBoolean(); + + if (useJoinMessage) + joinMessage = Colors.of(conf.getNode("joinMessage", "text").getString()); + + if (useBossBar) + bossBar = conf.getNode("bossBar").getValue(BossBar.class); + + infoForwarding = conf.getNode("infoForwarding").getValue(InfoForwarding.class); + readTimeout = conf.getNode("readTimeout").getLong(); + debugLevel = conf.getNode("debugLevel").getInt(); + } + + public SocketAddress getAddress() { + return address; + } + + public int getMaxPlayers() { + return maxPlayers; + } + + public PingData getPingData() { + return pingData; + } + + public String getDimensionType() { + return dimensionType; + } + + public Position getSpawnPosition() { + return spawnPosition; + } + + public InfoForwarding getInfoForwarding() { + return infoForwarding; + } + + public long getReadTimeout() { + return readTimeout; + } + + public int getDebugLevel() { + return debugLevel; + } + + public boolean isUseJoinMessage() { + return useJoinMessage; + } + + public boolean isUseBossBar() { + return useBossBar; + } + + public String getJoinMessage() { + return joinMessage; + } + + public BossBar getBossBar() { + return bossBar; + } +} diff --git a/src/main/java/ru/nanit/limbo/configuration/SocketAddressSerializer.java b/src/main/java/ru/nanit/limbo/configuration/SocketAddressSerializer.java new file mode 100644 index 0000000..f67bd9f --- /dev/null +++ b/src/main/java/ru/nanit/limbo/configuration/SocketAddressSerializer.java @@ -0,0 +1,31 @@ +package ru.nanit.limbo.configuration; + +import napi.configurate.data.ConfigNode; +import napi.configurate.serializing.NodeSerializer; +import napi.configurate.serializing.NodeSerializingException; + +import java.net.InetSocketAddress; +import java.net.SocketAddress; + +public class SocketAddressSerializer implements NodeSerializer { + + @Override + public SocketAddress deserialize(ConfigNode node) { + String ip = node.getNode("ip").getString(); + int port = node.getNode("port").getInt(); + SocketAddress address; + + if (ip == null || ip.isEmpty()){ + address = new InetSocketAddress(port); + } else { + address = new InetSocketAddress(ip, port); + } + + return address; + } + + @Override + public void serialize(SocketAddress socketAddress, ConfigNode configNode) throws NodeSerializingException { + + } +} diff --git a/src/main/java/ru/nanit/limbo/connection/ClientChannelInitializer.java b/src/main/java/ru/nanit/limbo/connection/ClientChannelInitializer.java index 688eaf5..34c7712 100644 --- a/src/main/java/ru/nanit/limbo/connection/ClientChannelInitializer.java +++ b/src/main/java/ru/nanit/limbo/connection/ClientChannelInitializer.java @@ -4,7 +4,7 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.handler.timeout.ReadTimeoutHandler; -import ru.nanit.limbo.LimboConfig; +import ru.nanit.limbo.configuration.LimboConfig; import ru.nanit.limbo.protocol.pipeline.VarIntFrameDecoder; import ru.nanit.limbo.protocol.pipeline.PacketDecoder; import ru.nanit.limbo.protocol.pipeline.PacketEncoder; @@ -25,7 +25,8 @@ public class ClientChannelInitializer extends ChannelInitializer { protected void initChannel(Channel channel) { ChannelPipeline pipeline = channel.pipeline(); - pipeline.addLast("timeout", new ReadTimeoutHandler(LimboConfig.getReadTimeout(), TimeUnit.MILLISECONDS)); + pipeline.addLast("timeout", new ReadTimeoutHandler(server.getConfig().getReadTimeout(), + TimeUnit.MILLISECONDS)); pipeline.addLast("frame_decoder", new VarIntFrameDecoder()); pipeline.addLast("frame_encoder", new VarIntLengthEncoder()); pipeline.addLast("decoder", new PacketDecoder()); diff --git a/src/main/java/ru/nanit/limbo/connection/ClientConnection.java b/src/main/java/ru/nanit/limbo/connection/ClientConnection.java index 8e0f7c8..6c7507f 100644 --- a/src/main/java/ru/nanit/limbo/connection/ClientConnection.java +++ b/src/main/java/ru/nanit/limbo/connection/ClientConnection.java @@ -4,7 +4,6 @@ import io.netty.channel.Channel; import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; -import ru.nanit.limbo.LimboConfig; import ru.nanit.limbo.protocol.packets.login.*; import ru.nanit.limbo.protocol.packets.play.*; import ru.nanit.limbo.protocol.pipeline.PacketDecoder; @@ -18,7 +17,6 @@ import ru.nanit.limbo.protocol.registry.Version; import ru.nanit.limbo.server.LimboServer; import ru.nanit.limbo.util.Logger; import ru.nanit.limbo.util.UuidUtil; -import ru.nanit.limbo.world.DimensionRegistry; import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; @@ -50,7 +48,7 @@ public class ClientConnection extends ChannelInboundHandlerAdapter { @Override public void channelInactive(ChannelHandlerContext ctx) throws Exception { if (state.equals(State.PLAY)){ - server.removeConnection(this); + server.getConnections().removeConnection(this); Logger.info("Player %s disconnected", this.username); } super.channelInactive(ctx); @@ -73,10 +71,11 @@ public class ClientConnection extends ChannelInboundHandlerAdapter { PacketHandshake handshake = (PacketHandshake) packet; updateState(State.getById(handshake.getNextState())); clientVersion = handshake.getVersion(); + Logger.debug("Pinged from " + handshake.getHost() + ":" + handshake.getPort()); } if (packet instanceof PacketStatusRequest){ - sendPacket(new PacketStatusResponse(server.getConnectionsCount())); + sendPacket(new PacketStatusResponse(server)); } if (packet instanceof PacketStatusPing){ @@ -84,7 +83,7 @@ public class ClientConnection extends ChannelInboundHandlerAdapter { } if (packet instanceof PacketLoginStart){ - if (server.getConnectionsCount() >= LimboConfig.getMaxPlayers()){ + if (server.getConnections().getCount() >= server.getConfig().getMaxPlayers()){ disconnect("Too many players connected"); return; } @@ -105,7 +104,7 @@ public class ClientConnection extends ChannelInboundHandlerAdapter { sendPacket(loginSuccess); updateState(State.PLAY); - server.addConnection(this); + server.getConnections().addConnection(this); Logger.info("Player %s connected (%s)", this.username, channel.remoteAddress()); sendJoinPackets(); @@ -120,7 +119,7 @@ public class ClientConnection extends ChannelInboundHandlerAdapter { joinGame.setFlat(false); joinGame.setGameMode(2); joinGame.setHardcore(false); - joinGame.setMaxPlayers(LimboConfig.getMaxPlayers()); + joinGame.setMaxPlayers(server.getConfig().getMaxPlayers()); joinGame.setPreviousGameMode(-1); joinGame.setReducedDebugInfo(false); joinGame.setDebug(false); @@ -128,16 +127,16 @@ public class ClientConnection extends ChannelInboundHandlerAdapter { joinGame.setWorldName("minecraft:world"); joinGame.setWorldNames("minecraft:world"); joinGame.setHashedSeed(0); - joinGame.setDimensionCodec(DimensionRegistry.getCodec()); - joinGame.setDimension(DimensionRegistry.getDefaultDimension()); + joinGame.setDimensionCodec(server.getDimensionRegistry().getCodec()); + joinGame.setDimension(server.getDimensionRegistry().getDefaultDimension()); PacketPlayerPositionAndLook positionAndLook = new PacketPlayerPositionAndLook(); - positionAndLook.setX(LimboConfig.getSpawnPosition().getX()); - positionAndLook.setY(LimboConfig.getSpawnPosition().getY()); - positionAndLook.setZ(LimboConfig.getSpawnPosition().getZ()); - positionAndLook.setYaw(90.0F); - positionAndLook.setPitch(0.0F); + positionAndLook.setX(server.getConfig().getSpawnPosition().getX()); + positionAndLook.setY(server.getConfig().getSpawnPosition().getY()); + positionAndLook.setZ(server.getConfig().getSpawnPosition().getZ()); + positionAndLook.setYaw(server.getConfig().getSpawnPosition().getYaw()); + positionAndLook.setPitch(server.getConfig().getSpawnPosition().getPitch()); positionAndLook.setTeleportId(ThreadLocalRandom.current().nextInt()); PacketPlayerInfo info = new PacketPlayerInfo(); diff --git a/src/main/java/ru/nanit/limbo/protocol/packets/play/PacketBossBar.java b/src/main/java/ru/nanit/limbo/protocol/packets/play/PacketBossBar.java index 2fa8249..14577d7 100644 --- a/src/main/java/ru/nanit/limbo/protocol/packets/play/PacketBossBar.java +++ b/src/main/java/ru/nanit/limbo/protocol/packets/play/PacketBossBar.java @@ -2,36 +2,22 @@ package ru.nanit.limbo.protocol.packets.play; import ru.nanit.limbo.protocol.ByteMessage; import ru.nanit.limbo.protocol.PacketOut; +import ru.nanit.limbo.server.data.BossBar; import java.util.UUID; public class PacketBossBar implements PacketOut { private UUID uuid; - private String title; - private float health; - private Color color; - private Division division; + private BossBar bossBar; private int flags; public void setUuid(UUID uuid) { this.uuid = uuid; } - public void setTitle(String title) { - this.title = title; - } - - public void setHealth(float health) { - this.health = health; - } - - public void setColor(Color color) { - this.color = color; - } - - public void setDivision(Division division) { - this.division = division; + public void setBossBar(BossBar bossBar) { + this.bossBar = bossBar; } public void setFlags(int flags) { @@ -42,43 +28,11 @@ public class PacketBossBar implements PacketOut { public void encode(ByteMessage msg) { msg.writeUuid(uuid); msg.writeVarInt(0); // Create bossbar - msg.writeString(title); - msg.writeFloat(health); - msg.writeVarInt(color.index); - msg.writeVarInt(division.index); + msg.writeString(bossBar.getText()); + msg.writeFloat(bossBar.getHealth()); + msg.writeVarInt(bossBar.getColor().getIndex()); + msg.writeVarInt(bossBar.getDivision().getIndex()); msg.writeByte(flags); } - public enum Color { - - PINK(0), - BLUE(1), - RED(2), - GREEN(3), - YELLOW(4), - PURPLE(5), - WHITE(6); - - private final int index; - - Color(int index) { - this.index = index; - } - } - - public enum Division { - - SOLID(0), - DASHES_6(1), - DASHES_10(2), - DASHES_12(3), - DASHES_20(4); - - private final int index; - - Division(int index) { - this.index = index; - } - } - } diff --git a/src/main/java/ru/nanit/limbo/protocol/packets/status/PacketStatusResponse.java b/src/main/java/ru/nanit/limbo/protocol/packets/status/PacketStatusResponse.java index 83f037d..131f406 100644 --- a/src/main/java/ru/nanit/limbo/protocol/packets/status/PacketStatusResponse.java +++ b/src/main/java/ru/nanit/limbo/protocol/packets/status/PacketStatusResponse.java @@ -1,27 +1,28 @@ package ru.nanit.limbo.protocol.packets.status; -import ru.nanit.limbo.LimboConfig; import ru.nanit.limbo.protocol.*; import ru.nanit.limbo.protocol.registry.Version; +import ru.nanit.limbo.server.LimboServer; public class PacketStatusResponse implements PacketOut { private static final String TEMPLATE = "{ \"version\": { \"name\": \"%s\", \"protocol\": %d }, \"players\": { \"max\": %d, \"online\": %d, \"sample\": [] }, \"description\": %s }"; - private int online; + private LimboServer server; public PacketStatusResponse(){ } - public PacketStatusResponse(int online){ - this.online = online; + public PacketStatusResponse(LimboServer server){ + this.server = server; } @Override public void encode(ByteMessage msg) { - String ver = LimboConfig.getPingData().getVersion(); - String desc = LimboConfig.getPingData().getDescription(); + String ver = server.getConfig().getPingData().getVersion(); + String desc = server.getConfig().getPingData().getDescription(); String json = getResponseJson(ver, Version.getCurrentSupported().getProtocolNumber(), - LimboConfig.getMaxPlayers(), online, desc); + server.getConfig().getMaxPlayers(), server.getConnections().getCount(), desc); + msg.writeString(json); } diff --git a/src/main/java/ru/nanit/limbo/server/Connections.java b/src/main/java/ru/nanit/limbo/server/Connections.java new file mode 100644 index 0000000..c4fa340 --- /dev/null +++ b/src/main/java/ru/nanit/limbo/server/Connections.java @@ -0,0 +1,34 @@ +package ru.nanit.limbo.server; + +import ru.nanit.limbo.connection.ClientConnection; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +public final class Connections { + + private final Map connections; + + public Connections(){ + connections = new ConcurrentHashMap<>(); + } + + public Collection getAllConnections(){ + return Collections.unmodifiableCollection(connections.values()); + } + + public int getCount(){ + return connections.size(); + } + + public void addConnection(ClientConnection connection){ + connections.put(connection.getUuid(), connection); + } + + public void removeConnection(ClientConnection connection){ + connections.remove(connection.getUuid()); + } +} diff --git a/src/main/java/ru/nanit/limbo/server/LimboServer.java b/src/main/java/ru/nanit/limbo/server/LimboServer.java index 88f456f..c100a1d 100644 --- a/src/main/java/ru/nanit/limbo/server/LimboServer.java +++ b/src/main/java/ru/nanit/limbo/server/LimboServer.java @@ -3,40 +3,43 @@ package ru.nanit.limbo.server; import io.netty.bootstrap.ServerBootstrap; import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; -import ru.nanit.limbo.LimboConfig; +import napi.configurate.serializing.NodeSerializers; +import ru.nanit.limbo.configuration.LimboConfig; +import ru.nanit.limbo.configuration.SocketAddressSerializer; import ru.nanit.limbo.connection.ClientChannelInitializer; import ru.nanit.limbo.connection.ClientConnection; import ru.nanit.limbo.protocol.packets.play.PacketBossBar; import ru.nanit.limbo.protocol.packets.play.PacketChatMessage; +import ru.nanit.limbo.server.data.*; import ru.nanit.limbo.util.Logger; import ru.nanit.limbo.world.DimensionRegistry; +import java.net.SocketAddress; import java.nio.file.Paths; -import java.util.Map; import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; public final class LimboServer { - private final Map connections = new ConcurrentHashMap<>(); - private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); + private LimboConfig config; + private Connections connections; + private DimensionRegistry dimensionRegistry; private PacketChatMessage joinMessage; private PacketBossBar joinBossBar; - public int getConnectionsCount(){ - return connections.size(); + public LimboConfig getConfig(){ + return config; } - public void addConnection(ClientConnection connection){ - connections.put(connection.getUuid(), connection); + public Connections getConnections(){ + return connections; } - public void removeConnection(ClientConnection connection){ - connections.remove(connection.getUuid()); + public DimensionRegistry getDimensionRegistry() { + return dimensionRegistry; } public PacketChatMessage getJoinMessage() { @@ -50,47 +53,54 @@ public final class LimboServer { public void start() throws Exception { Logger.info("Starting server..."); - LimboConfig.load(Paths.get("./settings.properties")); - DimensionRegistry.init(LimboConfig.getDimensionType()); + NodeSerializers.register(SocketAddress.class, new SocketAddressSerializer()); + NodeSerializers.register(InfoForwarding.class, new InfoForwarding.Serializer()); + NodeSerializers.register(PingData.class, new PingData.Serializer()); + NodeSerializers.register(BossBar.class, new BossBar.Serializer()); + NodeSerializers.register(Position.class, new Position.Serializer()); - initializeInGameData(); + config = new LimboConfig(Paths.get("./")); + config.load(); + Logger.setLevel(config.getDebugLevel()); + + dimensionRegistry = new DimensionRegistry(); + dimensionRegistry.load(config.getDimensionType()); + + connections = new Connections(); + + initInGameData(); + + ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(); executor.scheduleAtFixedRate(this::broadcastKeepAlive, 0L, 5L, TimeUnit.SECONDS); - ServerBootstrap bootstrap = new ServerBootstrap() + new ServerBootstrap() .group(new NioEventLoopGroup(), new NioEventLoopGroup()) .channel(NioServerSocketChannel.class) - .childHandler(new ClientChannelInitializer(this)); + .childHandler(new ClientChannelInitializer(this)) + .localAddress(config.getAddress()) + .bind(); - if (LimboConfig.getHost().isEmpty()){ - bootstrap.bind(LimboConfig.getPort()); - } else { - bootstrap.bind(LimboConfig.getHost(), LimboConfig.getPort()); - } - - Logger.info("Server started on %s:%d", LimboConfig.getHost(), LimboConfig.getPort()); + Logger.info("Server started on %s", config.getAddress()); } - private void initializeInGameData(){ - if (LimboConfig.getJoinMessages().getChatMessage() != null){ + private void initInGameData(){ + if (config.isUseJoinMessage()){ joinMessage = new PacketChatMessage(); - joinMessage.setJsonData(LimboConfig.getJoinMessages().getChatMessage()); + joinMessage.setJsonData(config.getJoinMessage()); joinMessage.setPosition(PacketChatMessage.Position.CHAT); joinMessage.setSender(UUID.randomUUID()); } - if (LimboConfig.getJoinMessages().getBossBarText() != null){ + if (config.isUseBossBar()){ joinBossBar = new PacketBossBar(); - joinBossBar.setTitle(LimboConfig.getJoinMessages().getBossBarText()); - joinBossBar.setHealth(LimboConfig.getJoinMessages().getBossBarHealth()); - joinBossBar.setColor(LimboConfig.getJoinMessages().getBossBarColor()); - joinBossBar.setDivision(LimboConfig.getJoinMessages().getBossBarDivision()); + joinBossBar.setBossBar(config.getBossBar()); joinBossBar.setUuid(UUID.randomUUID()); } } private void broadcastKeepAlive(){ - connections.values().forEach(ClientConnection::sendKeepAlive); + connections.getAllConnections().forEach(ClientConnection::sendKeepAlive); } } diff --git a/src/main/java/ru/nanit/limbo/server/data/BossBar.java b/src/main/java/ru/nanit/limbo/server/data/BossBar.java new file mode 100644 index 0000000..f14acef --- /dev/null +++ b/src/main/java/ru/nanit/limbo/server/data/BossBar.java @@ -0,0 +1,119 @@ +package ru.nanit.limbo.server.data; + +import napi.configurate.data.ConfigNode; +import napi.configurate.serializing.NodeSerializer; +import napi.configurate.serializing.NodeSerializingException; +import ru.nanit.limbo.util.Colors; + +public class BossBar { + + private String text; + private float health; + private Color color; + private Division division; + + public String getText() { + return text; + } + + public float getHealth() { + return health; + } + + public Color getColor() { + return color; + } + + public Division getDivision() { + return division; + } + + public void setText(String text) { + this.text = text; + } + + public void setHealth(float health) { + this.health = health; + } + + public void setColor(Color color) { + this.color = color; + } + + public void setDivision(Division division) { + this.division = division; + } + + public enum Color { + + PINK(0), + BLUE(1), + RED(2), + GREEN(3), + YELLOW(4), + PURPLE(5), + WHITE(6); + + private final int index; + + Color(int index) { + this.index = index; + } + + public int getIndex() { + return index; + } + } + + public enum Division { + + SOLID(0), + DASHES_6(1), + DASHES_10(2), + DASHES_12(3), + DASHES_20(4); + + private final int index; + + Division(int index) { + this.index = index; + } + + public int getIndex() { + return index; + } + } + + public static class Serializer implements NodeSerializer{ + + @Override + public BossBar deserialize(ConfigNode node) throws NodeSerializingException { + BossBar bossBar = new BossBar(); + + bossBar.setText(Colors.of(node.getNode("text").getString())); + bossBar.setHealth(node.getNode("health").getFloat()); + + if (bossBar.getHealth() < 0 || bossBar.getHealth() > 1) + throw new NodeSerializingException("BossBar health value must be between 0.0 and 1.0"); + + try { + bossBar.setColor(Color.valueOf(node.getNode("color").getString().toUpperCase())); + } catch (IllegalArgumentException e){ + throw new NodeSerializingException("Invalid bossbar color"); + } + + try { + bossBar.setDivision(Division.valueOf(node.getNode("division").getString().toUpperCase())); + } catch (IllegalArgumentException e){ + throw new NodeSerializingException("Invalid bossbar division"); + } + + return bossBar; + } + + @Override + public void serialize(BossBar bossBar, ConfigNode configNode) { + + } + } +} diff --git a/src/main/java/ru/nanit/limbo/server/data/InfoForwarding.java b/src/main/java/ru/nanit/limbo/server/data/InfoForwarding.java new file mode 100644 index 0000000..fcae3ee --- /dev/null +++ b/src/main/java/ru/nanit/limbo/server/data/InfoForwarding.java @@ -0,0 +1,53 @@ +package ru.nanit.limbo.server.data; + +import napi.configurate.data.ConfigNode; +import napi.configurate.serializing.NodeSerializer; +import napi.configurate.serializing.NodeSerializingException; + +import java.util.Optional; + +public class InfoForwarding { + + private Type type; + private String secret; + + public Type getType() { + return type; + } + + public Optional getSecret() { + return Optional.ofNullable(secret); + } + + public enum Type { + NONE, + LEGACY, + MODERN + } + + public static class Serializer implements NodeSerializer { + + @Override + public InfoForwarding deserialize(ConfigNode node) throws NodeSerializingException { + InfoForwarding forwarding = new InfoForwarding(); + + try { + forwarding.type = Type.valueOf(node.getNode("type").getString().toUpperCase()); + } catch (IllegalArgumentException e){ + throw new NodeSerializingException("Undefined info forwarding type"); + } + + if (forwarding.type == Type.MODERN){ + forwarding.secret = node.getNode("secret").getString(); + } + + return forwarding; + } + + @Override + public void serialize(InfoForwarding infoForwarding, ConfigNode configNode) throws NodeSerializingException { + + } + } + +} diff --git a/src/main/java/ru/nanit/limbo/server/data/PingData.java b/src/main/java/ru/nanit/limbo/server/data/PingData.java new file mode 100644 index 0000000..ccea251 --- /dev/null +++ b/src/main/java/ru/nanit/limbo/server/data/PingData.java @@ -0,0 +1,43 @@ +package ru.nanit.limbo.server.data; + +import napi.configurate.data.ConfigNode; +import napi.configurate.serializing.NodeSerializer; +import ru.nanit.limbo.util.Colors; + +public class PingData { + + private String version; + private String description; + + public String getVersion() { + return version; + } + + public void setVersion(String version) { + this.version = version; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public static class Serializer implements NodeSerializer { + + @Override + public PingData deserialize(ConfigNode node) { + PingData pingData = new PingData(); + pingData.setDescription(Colors.of(node.getNode("description").getString())); + pingData.setVersion(Colors.of(node.getNode("version").getString())); + return pingData; + } + + @Override + public void serialize(PingData pingData, ConfigNode configNode) { + + } + } +} \ No newline at end of file diff --git a/src/main/java/ru/nanit/limbo/server/data/Position.java b/src/main/java/ru/nanit/limbo/server/data/Position.java new file mode 100644 index 0000000..80adab9 --- /dev/null +++ b/src/main/java/ru/nanit/limbo/server/data/Position.java @@ -0,0 +1,72 @@ +package ru.nanit.limbo.server.data; + +import napi.configurate.data.ConfigNode; +import napi.configurate.serializing.NodeSerializer; + +public class Position { + + private double x; + private double y; + private double z; + private float yaw; + private float pitch; + + public double getX() { + return x; + } + + public double getY() { + return y; + } + + public double getZ() { + return z; + } + + public float getYaw() { + return yaw; + } + + public float getPitch() { + return pitch; + } + + public void setX(double x) { + this.x = x; + } + + public void setY(double y) { + this.y = y; + } + + public void setZ(double z) { + this.z = z; + } + + public void setYaw(float yaw) { + this.yaw = yaw; + } + + public void setPitch(float pitch) { + this.pitch = pitch; + } + + public static class Serializer implements NodeSerializer { + + @Override + public Position deserialize(ConfigNode node) { + Position position = new Position(); + position.setX(node.getNode("x").getDouble()); + position.setY(node.getNode("y").getDouble()); + position.setZ(node.getNode("z").getDouble()); + position.setYaw(node.getNode("yaw").getFloat()); + position.setPitch(node.getNode("pitch").getFloat()); + return position; + } + + @Override + public void serialize(Position position, ConfigNode configNode) { + + } + } +} diff --git a/src/main/java/ru/nanit/limbo/util/Logger.java b/src/main/java/ru/nanit/limbo/util/Logger.java index 440848c..ab7b6b5 100644 --- a/src/main/java/ru/nanit/limbo/util/Logger.java +++ b/src/main/java/ru/nanit/limbo/util/Logger.java @@ -1,22 +1,25 @@ package ru.nanit.limbo.util; -import ru.nanit.limbo.LimboConfig; - import java.time.LocalTime; import java.time.format.DateTimeFormatter; public final class Logger { private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("hh:mm:ss"); + private static int debugLevel = 3; private Logger(){} + public static void setLevel(int level){ + debugLevel = level; + } + public static void info(Object msg, Object... args){ print(Level.INFO, msg, null, args); } - public static void info(Object msg, Throwable t, Object... args){ - print(Level.INFO, msg, t, args); + public static void debug(Object msg, Object... args){ + print(Level.INFO, msg, null, args); } public static void warning(Object msg, Object... args){ @@ -36,7 +39,7 @@ public final class Logger { } public static void print(Level level, Object msg, Throwable t, Object... args){ - if (LimboConfig.getDebugLevel() >= level.getIndex()){ + if (debugLevel >= level.getIndex()){ System.out.println(String.format("%s: %s", getPrefix(level), String.format(msg.toString(), args))); if (t != null) t.printStackTrace(); } @@ -52,7 +55,8 @@ public final class Logger { public enum Level { - INFO ("INFO", 1), + INFO ("INFO", 0), + DEBUG ("DEBUG", 1), WARNING("WARNING", 2), ERROR("ERROR", 3); diff --git a/src/main/java/ru/nanit/limbo/world/DimensionRegistry.java b/src/main/java/ru/nanit/limbo/world/DimensionRegistry.java index 2993013..77a6f89 100644 --- a/src/main/java/ru/nanit/limbo/world/DimensionRegistry.java +++ b/src/main/java/ru/nanit/limbo/world/DimensionRegistry.java @@ -6,11 +6,55 @@ import ru.nanit.limbo.util.Logger; public final class DimensionRegistry { - private static CompoundBinaryTag codec; - private static CompoundBinaryTag defaultDimension; + private CompoundBinaryTag defaultDimension; - public static void init(String defaultDimensionName){ - CompoundBinaryTag overworld = CompoundBinaryTag.builder() + private CompoundBinaryTag codec; + private CompoundBinaryTag overWorld; + private CompoundBinaryTag theEnd; + private CompoundBinaryTag nether; + + public CompoundBinaryTag getCodec(){ + return codec; + } + + public CompoundBinaryTag getDefaultDimension() { + return defaultDimension; + } + + public CompoundBinaryTag getOverWorld() { + return overWorld; + } + + public CompoundBinaryTag getTheEnd() { + return theEnd; + } + + public CompoundBinaryTag getNether() { + return nether; + } + + public void load(String def){ + initDimensions(); + + switch (def.toLowerCase()){ + case "overworld": + defaultDimension = overWorld; + break; + case "nether": + defaultDimension = nether; + break; + case "the_end": + defaultDimension = theEnd; + break; + default: + defaultDimension = theEnd; + Logger.warning("Undefined dimension type: '%s'. Using THE_END as default", def); + break; + } + } + + private void initDimensions(){ + overWorld = CompoundBinaryTag.builder() .putString("name", "minecraft:overworld") .putByte("piglin_safe", (byte) 0) .putByte("natural", (byte) 0) @@ -28,7 +72,7 @@ public final class DimensionRegistry { .putByte("has_ceiling", (byte) 0) .build(); - CompoundBinaryTag nether = CompoundBinaryTag.builder() + nether = CompoundBinaryTag.builder() .putString("name", "minecraft:the_nether") .putByte("piglin_safe", (byte) 0) .putByte("natural", (byte) 0) @@ -46,7 +90,7 @@ public final class DimensionRegistry { .putByte("has_ceiling", (byte) 0) .build(); - CompoundBinaryTag theEnd = CompoundBinaryTag.builder() + theEnd = CompoundBinaryTag.builder() .putString("name", "minecraft:the_end") .putByte("piglin_safe", (byte) 0) .putByte("natural", (byte) 0) @@ -64,10 +108,10 @@ public final class DimensionRegistry { .putByte("has_ceiling", (byte) 0) .build(); - CompoundBinaryTag overworldData = CompoundBinaryTag.builder() + CompoundBinaryTag overWorldData = CompoundBinaryTag.builder() .putString("name", "minecraft:overworld") .putInt("id", 2) - .put("element", overworld) + .put("element", overWorld) .build(); CompoundBinaryTag netherData = CompoundBinaryTag.builder() @@ -111,7 +155,7 @@ public final class DimensionRegistry { .put("minecraft:dimension_type", CompoundBinaryTag.builder() .putString("type", "minecraft:dimension_type") .put("value", ListBinaryTag.builder() - .add(overworldData) + .add(overWorldData) .add(netherData) .add(endData) .build()) @@ -123,29 +167,5 @@ public final class DimensionRegistry { .build()) .build()) .build(); - - switch (defaultDimensionName.toLowerCase()){ - case "overworld": - defaultDimension = overworld; - break; - case "nether": - defaultDimension = nether; - break; - case "the_end": - defaultDimension = theEnd; - break; - default: - defaultDimension = theEnd; - Logger.error("Undefined dimension type: '%s'. Using THE_END as default", defaultDimensionName); - break; - } - } - - public static CompoundBinaryTag getCodec(){ - return codec; - } - - public static CompoundBinaryTag getDefaultDimension() { - return defaultDimension; } } diff --git a/src/main/resources/limbo.yml b/src/main/resources/limbo.yml new file mode 100644 index 0000000..c9332b0 --- /dev/null +++ b/src/main/resources/limbo.yml @@ -0,0 +1,60 @@ +# +# NanoLimbo configuration +# + +# Server's host address and port. Set ip empty to use public address +bind: + ip: 'localhost' + port: 65535 + +# Max amount of players can join to server +maxPlayers: 100 + +# Server's data in servers list +ping: + description: '{"text": "&9NanoLimbo"}' + version: 'NanoLimbo' + +# Available dimensions: OVERWORLD, NETHER, THE_END +dimension: THE_END + +# Spawn position in the world +spawnPosition: + x: 0.0 + y: 0.0 + z: 0.0 + yaw: 0.0 + pitch: 0.0 + +# Message sends when player join to server +joinMessage: + enable: true + text: '{"text": "&eWelcome to the Limbo!"}' + +# Bossbar sends when player join to server +bossBar: + enable: true + text: '{"text": "Welcome to the Limbo!"}' + health: 1.0 + # Available colors: PINK, BLUE, RED, GREEN, YELLOW, PURPLE, WHITE + color: PINK + # Available divisions: SOLID, DASHES_6, DASHES_10, DASHES_12, DASHES_20 + division: SOLID + +# Player info forwarding support. Available types: NONE, LEGACY, MODERN +# Don't use secret if you not use MODERN type +infoForwarding: + type: LEGACY + secret: '' + +# Read timeout for connections in milliseconds +readTimeout: 30000 + +# Define debug level. On release, i recommend to use 0 level, since +# there are many useless for release information about ping, received packets, etc. +# Levels: +# 0 - Display only useful info +# 1 - Display info and some debug +# 2 - Display info and warnings +# 3 - Display info, warnings, errors +debugLevel: 3 \ No newline at end of file diff --git a/src/main/resources/settings.properties b/src/main/resources/settings.properties deleted file mode 100644 index cc1d1fc..0000000 --- a/src/main/resources/settings.properties +++ /dev/null @@ -1,71 +0,0 @@ -# ======= General Data ======= # - -# Server's host address. Set it empty to use public address -host=localhost - -# Server's port -port=65535 - -# Max amount of players can join to server -max-players=100 - -# Available dimensions: OVERWORLD, NETHER, THE_END -dimension=THE_END - -# Spawn position in the world in format x,y,z -spawn-position=0.0,65.0,0.0 - -# Version string when client version is not compatible with server one -ping-version=NanoLimbo - -# Server's description component -ping-description={"text": "&9NanoLimbo"} - -# Player info forwarding support. Available types: NONE, LEGACY, MODERN -# MODERN - Velocity native forwarding type. -# LEGACY - BungeeCord forwarding type (Velocity supports it too) -ip-forwarding=LEGACY - -# If you use MODERN type of forwarding, enter your secret code here -ip-forwarding-secret= - -# Read timeout for connections in milliseconds -read-timeout=30000 - -# Define debug level. On release, i recommend to use 1 level, since -# there are many useless for release warnings about undefined packets and other. -# Levels: -# 0 - Display nothing -# 1 - Display only useful info -# 2 - Display info and warnings -# 3 - Display info, warnings, errors -debug-level=3 - -# ======= In-game Data ======= # - -# Message when player join to server. Comment this parameter to disable -join-message={"text": "&eWelcome to the Limbo!"} - -# Bossbar text. Comment this parameter to disable bossbar -join-bossbar-text={"text": "Welcome to the Limbo!"} - -# Bossbar percentage between 0.0 and 1.0 inclusive -join-bossbar-health=1.0 - -# Available bossbar colors: -# - PINK -# - BLUE -# - RED -# - GREEN -# - YELLOW -# - PURPLE -# - WHITE -join-bossbar-color=PINK - -# Available bossbar divisions: -# - SOLID -# - DASHES_6 -# - DASHES_10 -# - DASHES_12 -# - DASHES_20 -join-bossbar-division=SOLID \ No newline at end of file