├── .github └── FUNDING.yml ├── .gitignore ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src └── main │ ├── java │ └── ua │ │ └── nanit │ │ └── limbo │ │ ├── server │ │ ├── Command.java │ │ ├── commands │ │ │ ├── CmdStop.java │ │ │ ├── CmdConn.java │ │ │ ├── CmdHelp.java │ │ │ └── CmdMem.java │ │ ├── CommandManager.java │ │ ├── Connections.java │ │ ├── data │ │ │ ├── PingData.java │ │ │ ├── Title.java │ │ │ ├── InfoForwarding.java │ │ │ └── BossBar.java │ │ ├── Logger.java │ │ └── LimboServer.java │ │ ├── protocol │ │ ├── packets │ │ │ ├── play │ │ │ │ ├── PacketSpawnPosition.java │ │ │ │ ├── PacketTitleSetTitle.java │ │ │ │ ├── PacketTitleSetSubTitle.java │ │ │ │ ├── PacketPlayerListHeader.java │ │ │ │ ├── PacketPluginMessage.java │ │ │ │ ├── PacketTitleTimes.java │ │ │ │ ├── PacketPlayerAbilities.java │ │ │ │ ├── PacketChatMessage.java │ │ │ │ ├── PacketBossBar.java │ │ │ │ ├── PacketKeepAlive.java │ │ │ │ ├── PacketPlayerPositionAndLook.java │ │ │ │ ├── PacketDeclareCommands.java │ │ │ │ ├── PacketSystemMessage.java │ │ │ │ ├── PacketTitleLegacy.java │ │ │ │ ├── PacketPlayerInfo.java │ │ │ │ └── PacketJoinGame.java │ │ │ ├── login │ │ │ │ ├── PacketDisconnect.java │ │ │ │ ├── PacketLoginStart.java │ │ │ │ ├── PacketLoginPluginRequest.java │ │ │ │ ├── PacketLoginSuccess.java │ │ │ │ └── PacketLoginPluginResponse.java │ │ │ ├── status │ │ │ │ ├── PacketStatusRequest.java │ │ │ │ ├── PacketStatusPing.java │ │ │ │ └── PacketStatusResponse.java │ │ │ └── PacketHandshake.java │ │ ├── PacketIn.java │ │ ├── PacketOut.java │ │ ├── Packet.java │ │ ├── PacketSnapshot.java │ │ └── registry │ │ │ ├── Version.java │ │ │ └── State.java │ │ ├── LimboConstants.java │ │ ├── util │ │ ├── Colors.java │ │ └── UuidUtil.java │ │ ├── NanoLimbo.java │ │ ├── connection │ │ ├── GameProfile.java │ │ ├── pipeline │ │ │ ├── VarIntLengthEncoder.java │ │ │ ├── VarIntByteDecoder.java │ │ │ ├── VarIntFrameDecoder.java │ │ │ ├── PacketDecoder.java │ │ │ └── PacketEncoder.java │ │ ├── ClientChannelInitializer.java │ │ ├── PacketHandler.java │ │ ├── PacketSnapshots.java │ │ └── ClientConnection.java │ │ ├── world │ │ ├── Dimension.java │ │ └── DimensionRegistry.java │ │ └── configuration │ │ ├── SocketAddressSerializer.java │ │ └── LimboConfig.java │ └── resources │ ├── dimension │ └── codec_old.snbt │ ├── settings.yml │ ├── LICENSE-netty │ └── LICENSE-configurate-yaml ├── gradlew.bat ├── README.md └── gradlew /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: nanit -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .gradle 3 | build -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'NanoLimbo' 2 | 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/firestarter/limbo/main/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/server/Command.java: -------------------------------------------------------------------------------- 1 | package ua.nanit.limbo.server; 2 | 3 | public interface Command { 4 | 5 | void execute(); 6 | 7 | String description(); 8 | 9 | } 10 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/server/commands/CmdStop.java: -------------------------------------------------------------------------------- 1 | package ua.nanit.limbo.server.commands; 2 | 3 | import ua.nanit.limbo.server.Command; 4 | 5 | public class CmdStop implements Command { 6 | 7 | @Override 8 | public void execute() { 9 | System.exit(0); 10 | } 11 | 12 | @Override 13 | public String description() { 14 | return "Stop the server"; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/server/commands/CmdConn.java: -------------------------------------------------------------------------------- 1 | package ua.nanit.limbo.server.commands; 2 | 3 | import ua.nanit.limbo.server.Command; 4 | import ua.nanit.limbo.server.LimboServer; 5 | import ua.nanit.limbo.server.Logger; 6 | 7 | public class CmdConn implements Command { 8 | 9 | private final LimboServer server; 10 | 11 | public CmdConn(LimboServer server) { 12 | this.server = server; 13 | } 14 | 15 | @Override 16 | public void execute() { 17 | Logger.info("Connections: %d", server.getConnections().getCount()); 18 | } 19 | 20 | @Override 21 | public String description() { 22 | return "Display connections count"; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/play/PacketSpawnPosition.java: -------------------------------------------------------------------------------- 1 | package ua.nanit.limbo.protocol.packets.play; 2 | 3 | import ua.nanit.limbo.protocol.ByteMessage; 4 | import ua.nanit.limbo.protocol.PacketOut; 5 | import ua.nanit.limbo.protocol.registry.Version; 6 | 7 | public class PacketSpawnPosition implements PacketOut { 8 | 9 | private long x; 10 | private long y; 11 | private long z; 12 | 13 | public PacketSpawnPosition() { } 14 | 15 | public PacketSpawnPosition(long x, long y, long z) { 16 | this.x = x; 17 | this.y = y; 18 | this.z = z; 19 | } 20 | 21 | @Override 22 | public void encode(ByteMessage msg, Version version) { 23 | msg.writeLong(encodePosition(x, y ,z)); 24 | msg.writeFloat(0); 25 | } 26 | 27 | private static long encodePosition(long x, long y, long z) { 28 | return ((x & 0x3FFFFFF) << 38) | ((z & 0x3FFFFFF) << 12) | (y & 0xFFF); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/server/commands/CmdHelp.java: -------------------------------------------------------------------------------- 1 | package ua.nanit.limbo.server.commands; 2 | 3 | import ua.nanit.limbo.server.Command; 4 | import ua.nanit.limbo.server.LimboServer; 5 | import ua.nanit.limbo.server.Logger; 6 | 7 | import java.util.Map; 8 | 9 | public class CmdHelp implements Command { 10 | 11 | private final LimboServer server; 12 | 13 | public CmdHelp(LimboServer server) { 14 | this.server = server; 15 | } 16 | 17 | @Override 18 | public void execute() { 19 | Map commands = server.getCommandManager().getCommands(); 20 | 21 | Logger.info("Available commands:"); 22 | 23 | for (Map.Entry entry : commands.entrySet()) { 24 | Logger.info("%s - %s", entry.getKey(), entry.getValue().description()); 25 | } 26 | } 27 | 28 | @Override 29 | public String description() { 30 | return "Show this message"; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/server/commands/CmdMem.java: -------------------------------------------------------------------------------- 1 | package ua.nanit.limbo.server.commands; 2 | 3 | import ua.nanit.limbo.server.Command; 4 | import ua.nanit.limbo.server.Logger; 5 | 6 | public class CmdMem implements Command { 7 | 8 | @Override 9 | public void execute() { 10 | Runtime runtime = Runtime.getRuntime(); 11 | long mb = 1024 * 1024; 12 | long used = (runtime.totalMemory() - runtime.freeMemory()) / mb; 13 | long total = runtime.totalMemory() / mb; 14 | long free = runtime.freeMemory() / mb; 15 | long max = runtime.maxMemory() / mb; 16 | 17 | Logger.info("Memory usage:"); 18 | Logger.info("Used: %d MB", used); 19 | Logger.info("Total: %d MB", total); 20 | Logger.info("Free: %d MB", free); 21 | Logger.info("Max: %d MB", max); 22 | } 23 | 24 | @Override 25 | public String description() { 26 | return "Display memory usage"; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/LimboConstants.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo; 19 | 20 | public final class LimboConstants { 21 | 22 | public static final String VELOCITY_INFO_CHANNEL = "velocity:player_info"; 23 | public static final String BRAND_CHANNEL = "minecraft:brand"; 24 | 25 | private LimboConstants() {} 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/PacketIn.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.protocol; 19 | 20 | import ua.nanit.limbo.protocol.registry.Version; 21 | 22 | public interface PacketIn extends Packet { 23 | 24 | @Override 25 | default void encode(ByteMessage msg, Version version) { 26 | // Can be ignored for incoming packets 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/PacketOut.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.protocol; 19 | 20 | import ua.nanit.limbo.protocol.registry.Version; 21 | 22 | public interface PacketOut extends Packet { 23 | 24 | @Override 25 | default void decode(ByteMessage msg, Version version) { 26 | // Can be ignored for outgoing packets 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/util/Colors.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.util; 19 | 20 | public final class Colors { 21 | 22 | private static final char CHAR_FROM = '&'; 23 | private static final char CHAR_TO = '\u00a7'; 24 | 25 | private Colors() { 26 | } 27 | 28 | public static String of(String text) { 29 | if (text == null) return null; 30 | return text.replace(CHAR_FROM, CHAR_TO); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/NanoLimbo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo; 19 | 20 | import ua.nanit.limbo.server.LimboServer; 21 | import ua.nanit.limbo.server.Logger; 22 | 23 | public final class NanoLimbo { 24 | 25 | public static void main(String[] args) { 26 | try { 27 | new LimboServer().start(); 28 | } catch (Exception e) { 29 | Logger.error("Cannot start server: ", e); 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/Packet.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.protocol; 19 | 20 | import ua.nanit.limbo.connection.ClientConnection; 21 | import ua.nanit.limbo.protocol.registry.Version; 22 | import ua.nanit.limbo.server.LimboServer; 23 | 24 | public interface Packet { 25 | 26 | void encode(ByteMessage msg, Version version); 27 | 28 | void decode(ByteMessage msg, Version version); 29 | 30 | default void handle(ClientConnection conn, LimboServer server) { 31 | // Ignored by default 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/connection/GameProfile.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.connection; 19 | 20 | import java.util.UUID; 21 | 22 | public class GameProfile { 23 | 24 | private UUID uuid; 25 | private String username; 26 | 27 | public UUID getUuid() { 28 | return uuid; 29 | } 30 | 31 | public void setUuid(UUID uuid) { 32 | this.uuid = uuid; 33 | } 34 | 35 | public String getUsername() { 36 | return username; 37 | } 38 | 39 | public void setUsername(String username) { 40 | this.username = username; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/play/PacketTitleSetTitle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.packets.play; 19 | 20 | import ua.nanit.limbo.protocol.ByteMessage; 21 | import ua.nanit.limbo.protocol.PacketOut; 22 | import ua.nanit.limbo.protocol.registry.Version; 23 | 24 | public class PacketTitleSetTitle implements PacketOut { 25 | 26 | private String title; 27 | 28 | public void setTitle(String title) { 29 | this.title = title; 30 | } 31 | 32 | @Override 33 | public void encode(ByteMessage msg, Version version) { 34 | msg.writeString(title); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/play/PacketTitleSetSubTitle.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.packets.play; 19 | 20 | import ua.nanit.limbo.protocol.ByteMessage; 21 | import ua.nanit.limbo.protocol.PacketOut; 22 | import ua.nanit.limbo.protocol.registry.Version; 23 | 24 | public class PacketTitleSetSubTitle implements PacketOut { 25 | 26 | private String subtitle; 27 | 28 | public void setSubtitle(String subtitle) { 29 | this.subtitle = subtitle; 30 | } 31 | 32 | @Override 33 | public void encode(ByteMessage msg, Version version) { 34 | msg.writeString(subtitle); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/world/Dimension.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.world; 19 | 20 | import net.kyori.adventure.nbt.CompoundBinaryTag; 21 | 22 | public class Dimension { 23 | 24 | private final int id; 25 | private final String name; 26 | private final CompoundBinaryTag data; 27 | 28 | public Dimension(int id, String name, CompoundBinaryTag data) { 29 | this.id = id; 30 | this.name = name; 31 | this.data = data; 32 | } 33 | 34 | public int getId() { 35 | return id; 36 | } 37 | 38 | public String getName() { 39 | return name; 40 | } 41 | 42 | public CompoundBinaryTag getData() { 43 | return data; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/util/UuidUtil.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.util; 19 | 20 | import java.nio.charset.StandardCharsets; 21 | import java.util.UUID; 22 | 23 | public final class UuidUtil { 24 | 25 | private UuidUtil() {} 26 | 27 | public static UUID getOfflineModeUuid(String username) { 28 | return UUID.nameUUIDFromBytes(("OfflinePlayer:" + username) 29 | .getBytes(StandardCharsets.UTF_8)); 30 | } 31 | 32 | public static UUID fromString(String str) { 33 | if(str.contains("-")) return UUID.fromString(str); 34 | return UUID.fromString(str.replaceFirst("(\\p{XDigit}{8})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}{4})(\\p{XDigit}+)", "$1-$2-$3-$4-$5")); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/login/PacketDisconnect.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.packets.login; 19 | 20 | import ua.nanit.limbo.protocol.ByteMessage; 21 | import ua.nanit.limbo.protocol.PacketOut; 22 | import ua.nanit.limbo.protocol.registry.Version; 23 | 24 | public class PacketDisconnect implements PacketOut { 25 | 26 | private String reason; 27 | 28 | public void setReason(String reason) { 29 | this.reason = reason; 30 | } 31 | 32 | @Override 33 | public void encode(ByteMessage msg, Version version) { 34 | msg.writeString(String.format("{\"text\": \"%s\"}", reason)); 35 | } 36 | 37 | @Override 38 | public String toString() { 39 | return getClass().getSimpleName(); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/play/PacketPlayerListHeader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.packets.play; 19 | 20 | import ua.nanit.limbo.protocol.ByteMessage; 21 | import ua.nanit.limbo.protocol.PacketOut; 22 | import ua.nanit.limbo.protocol.registry.Version; 23 | 24 | public class PacketPlayerListHeader implements PacketOut { 25 | 26 | private String header; 27 | private String footer; 28 | 29 | public void setHeader(String header) { 30 | this.header = header; 31 | } 32 | 33 | public void setFooter(String footer) { 34 | this.footer = footer; 35 | } 36 | 37 | @Override 38 | public void encode(ByteMessage msg, Version version) { 39 | msg.writeString(header); 40 | msg.writeString(footer); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/play/PacketPluginMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.packets.play; 19 | 20 | import ua.nanit.limbo.protocol.ByteMessage; 21 | import ua.nanit.limbo.protocol.PacketOut; 22 | import ua.nanit.limbo.protocol.registry.Version; 23 | 24 | public class PacketPluginMessage implements PacketOut { 25 | 26 | private String channel; 27 | private String message; 28 | 29 | public void setChannel(String channel) { 30 | this.channel = channel; 31 | } 32 | 33 | public void setMessage(String message) { 34 | this.message = message; 35 | } 36 | 37 | @Override 38 | public void encode(ByteMessage msg, Version version) { 39 | msg.writeString(channel); 40 | msg.writeString(message); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/status/PacketStatusRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.packets.status; 19 | 20 | import ua.nanit.limbo.connection.ClientConnection; 21 | import ua.nanit.limbo.protocol.ByteMessage; 22 | import ua.nanit.limbo.protocol.PacketIn; 23 | import ua.nanit.limbo.protocol.registry.Version; 24 | import ua.nanit.limbo.server.LimboServer; 25 | 26 | public class PacketStatusRequest implements PacketIn { 27 | 28 | @Override 29 | public void decode(ByteMessage msg, Version version) { 30 | 31 | } 32 | 33 | @Override 34 | public void handle(ClientConnection conn, LimboServer server) { 35 | server.getPacketHandler().handle(conn, this); 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return getClass().getSimpleName(); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/play/PacketTitleTimes.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.packets.play; 19 | 20 | import ua.nanit.limbo.protocol.ByteMessage; 21 | import ua.nanit.limbo.protocol.PacketOut; 22 | import ua.nanit.limbo.protocol.registry.Version; 23 | 24 | public class PacketTitleTimes implements PacketOut { 25 | 26 | private int fadeIn; 27 | private int stay; 28 | private int fadeOut; 29 | 30 | public void setFadeIn(int fadeIn) { 31 | this.fadeIn = fadeIn; 32 | } 33 | 34 | public void setStay(int stay) { 35 | this.stay = stay; 36 | } 37 | 38 | public void setFadeOut(int fadeOut) { 39 | this.fadeOut = fadeOut; 40 | } 41 | 42 | @Override 43 | public void encode(ByteMessage msg, Version version) { 44 | msg.writeInt(fadeIn); 45 | msg.writeInt(stay); 46 | msg.writeInt(fadeOut); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/login/PacketLoginStart.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.packets.login; 19 | 20 | import ua.nanit.limbo.connection.ClientConnection; 21 | import ua.nanit.limbo.protocol.ByteMessage; 22 | import ua.nanit.limbo.protocol.PacketIn; 23 | import ua.nanit.limbo.protocol.registry.Version; 24 | import ua.nanit.limbo.server.LimboServer; 25 | 26 | public class PacketLoginStart implements PacketIn { 27 | 28 | private String username; 29 | 30 | public String getUsername() { 31 | return username; 32 | } 33 | 34 | @Override 35 | public void decode(ByteMessage msg, Version version) { 36 | this.username = msg.readString(); 37 | } 38 | 39 | @Override 40 | public void handle(ClientConnection conn, LimboServer server) { 41 | server.getPacketHandler().handle(conn, this); 42 | } 43 | 44 | @Override 45 | public String toString() { 46 | return getClass().getSimpleName(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/connection/pipeline/VarIntLengthEncoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.connection.pipeline; 19 | 20 | import io.netty.buffer.ByteBuf; 21 | import io.netty.channel.ChannelHandler; 22 | import io.netty.channel.ChannelHandlerContext; 23 | import io.netty.handler.codec.MessageToByteEncoder; 24 | import ua.nanit.limbo.protocol.ByteMessage; 25 | 26 | @ChannelHandler.Sharable 27 | public class VarIntLengthEncoder extends MessageToByteEncoder { 28 | 29 | @Override 30 | protected void encode(ChannelHandlerContext ctx, ByteBuf buf, ByteBuf out) { 31 | ByteMessage msg = new ByteMessage(out); 32 | msg.writeVarInt(buf.readableBytes()); 33 | msg.writeBytes(buf); 34 | } 35 | 36 | @Override 37 | protected ByteBuf allocateBuffer(ChannelHandlerContext ctx, ByteBuf msg, boolean preferDirect) { 38 | int anticipatedRequiredCapacity = 5 + msg.readableBytes(); 39 | return ctx.alloc().heapBuffer(anticipatedRequiredCapacity); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/play/PacketPlayerAbilities.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.packets.play; 19 | 20 | import ua.nanit.limbo.protocol.ByteMessage; 21 | import ua.nanit.limbo.protocol.PacketOut; 22 | import ua.nanit.limbo.protocol.registry.Version; 23 | 24 | public class PacketPlayerAbilities implements PacketOut { 25 | 26 | private int flags = 0x02; 27 | private float flyingSpeed = 0.0F; 28 | private float fieldOfView = 0.1F; 29 | 30 | public void setFlags(int flags) { 31 | this.flags = flags; 32 | } 33 | 34 | public void setFlyingSpeed(float flyingSpeed) { 35 | this.flyingSpeed = flyingSpeed; 36 | } 37 | 38 | public void setFieldOfView(float fieldOfView) { 39 | this.fieldOfView = fieldOfView; 40 | } 41 | 42 | @Override 43 | public void encode(ByteMessage msg, Version version) { 44 | msg.writeByte(flags); 45 | msg.writeFloat(flyingSpeed); 46 | msg.writeFloat(fieldOfView); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/status/PacketStatusPing.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.packets.status; 19 | 20 | import ua.nanit.limbo.connection.ClientConnection; 21 | import ua.nanit.limbo.protocol.ByteMessage; 22 | import ua.nanit.limbo.protocol.Packet; 23 | import ua.nanit.limbo.protocol.registry.Version; 24 | import ua.nanit.limbo.server.LimboServer; 25 | 26 | public class PacketStatusPing implements Packet { 27 | 28 | private long randomId; 29 | 30 | @Override 31 | public void encode(ByteMessage msg, Version version) { 32 | msg.writeLong(randomId); 33 | } 34 | 35 | @Override 36 | public void decode(ByteMessage msg, Version version) { 37 | this.randomId = msg.readLong(); 38 | } 39 | 40 | @Override 41 | public void handle(ClientConnection conn, LimboServer server) { 42 | server.getPacketHandler().handle(conn, this); 43 | } 44 | 45 | @Override 46 | public String toString() { 47 | return getClass().getSimpleName(); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/play/PacketChatMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.packets.play; 19 | 20 | import org.jetbrains.annotations.NotNull; 21 | import ua.nanit.limbo.connection.ClientConnection; 22 | import ua.nanit.limbo.protocol.ByteMessage; 23 | import ua.nanit.limbo.protocol.PacketIn; 24 | import ua.nanit.limbo.protocol.registry.Version; 25 | import ua.nanit.limbo.server.LimboServer; 26 | 27 | public class PacketChatMessage implements PacketIn { 28 | 29 | private String message; 30 | 31 | @NotNull 32 | public String getMessage() { 33 | return message; 34 | } 35 | 36 | @Override 37 | public void decode(ByteMessage msg, Version version) { 38 | String message = msg.readString(); 39 | 40 | if (message.length() > 256) { 41 | message = message.substring(0, 256); 42 | } 43 | 44 | this.message = message; 45 | } 46 | 47 | @Override 48 | public void handle(ClientConnection conn, LimboServer server) { 49 | server.getPacketHandler().handle(conn, this); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/login/PacketLoginPluginRequest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.packets.login; 19 | 20 | import io.netty.buffer.ByteBuf; 21 | import ua.nanit.limbo.protocol.ByteMessage; 22 | import ua.nanit.limbo.protocol.PacketOut; 23 | import ua.nanit.limbo.protocol.registry.Version; 24 | 25 | public class PacketLoginPluginRequest implements PacketOut { 26 | 27 | private int messageId; 28 | private String channel; 29 | private ByteBuf data; 30 | 31 | public void setMessageId(int messageId) { 32 | this.messageId = messageId; 33 | } 34 | 35 | public void setChannel(String channel) { 36 | this.channel = channel; 37 | } 38 | 39 | public void setData(ByteBuf data) { 40 | this.data = data; 41 | } 42 | 43 | @Override 44 | public void encode(ByteMessage msg, Version version) { 45 | msg.writeVarInt(messageId); 46 | msg.writeString(channel); 47 | msg.writeBytes(data); 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return getClass().getSimpleName(); 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/configuration/SocketAddressSerializer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.configuration; 19 | 20 | import org.checkerframework.checker.nullness.qual.Nullable; 21 | import org.spongepowered.configurate.ConfigurationNode; 22 | import org.spongepowered.configurate.serialize.TypeSerializer; 23 | 24 | import java.lang.reflect.Type; 25 | import java.net.InetSocketAddress; 26 | import java.net.SocketAddress; 27 | 28 | public class SocketAddressSerializer implements TypeSerializer { 29 | 30 | @Override 31 | public SocketAddress deserialize(Type type, ConfigurationNode node) { 32 | String ip = node.node("ip").getString(); 33 | int port = node.node("port").getInt(); 34 | SocketAddress address; 35 | 36 | if (ip == null || ip.isEmpty()) { 37 | address = new InetSocketAddress(port); 38 | } else { 39 | address = new InetSocketAddress(ip, port); 40 | } 41 | 42 | return address; 43 | } 44 | 45 | @Override 46 | public void serialize(Type type, @Nullable SocketAddress obj, ConfigurationNode node) { 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/connection/pipeline/VarIntByteDecoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.connection.pipeline; 19 | 20 | import io.netty.util.ByteProcessor; 21 | 22 | public class VarIntByteDecoder implements ByteProcessor { 23 | 24 | private int readVarInt; 25 | private int bytesRead; 26 | private DecodeResult result = DecodeResult.TOO_SHORT; 27 | 28 | @Override 29 | public boolean process(byte k) { 30 | readVarInt |= (k & 0x7F) << bytesRead++ * 7; 31 | if (bytesRead > 3) { 32 | result = DecodeResult.TOO_BIG; 33 | return false; 34 | } 35 | if ((k & 0x80) != 128) { 36 | result = DecodeResult.SUCCESS; 37 | return false; 38 | } 39 | return true; 40 | } 41 | 42 | public int getReadVarInt() { 43 | return readVarInt; 44 | } 45 | 46 | public int getBytesRead() { 47 | return bytesRead; 48 | } 49 | 50 | public DecodeResult getResult() { 51 | return result; 52 | } 53 | 54 | public enum DecodeResult { 55 | SUCCESS, 56 | TOO_SHORT, 57 | TOO_BIG 58 | } 59 | } -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/server/CommandManager.java: -------------------------------------------------------------------------------- 1 | package ua.nanit.limbo.server; 2 | 3 | import ua.nanit.limbo.server.commands.CmdConn; 4 | import ua.nanit.limbo.server.commands.CmdHelp; 5 | import ua.nanit.limbo.server.commands.CmdMem; 6 | import ua.nanit.limbo.server.commands.CmdStop; 7 | 8 | import java.util.*; 9 | 10 | public final class CommandManager extends Thread { 11 | 12 | private final Map commands = new HashMap<>(); 13 | 14 | public Map getCommands() { 15 | return Collections.unmodifiableMap(commands); 16 | } 17 | 18 | public Command getCommand(String name) { 19 | return commands.get(name.toLowerCase()); 20 | } 21 | 22 | public void register(String name, Command cmd) { 23 | commands.put(name.toLowerCase(), cmd); 24 | } 25 | 26 | @Override 27 | public void run() { 28 | Scanner scanner = new Scanner(System.in); 29 | String command; 30 | 31 | while (true) { 32 | try { 33 | command = scanner.nextLine().trim(); 34 | } catch (NoSuchElementException e) { 35 | break; 36 | } 37 | 38 | Command handler = getCommand(command); 39 | 40 | if (handler != null) { 41 | try { 42 | handler.execute(); 43 | } catch (Throwable t) { 44 | Logger.error("Cannot execute command:", t); 45 | } 46 | continue; 47 | } 48 | 49 | Logger.info("Unknown command. Type \"help\" to get commands list"); 50 | } 51 | } 52 | 53 | public void registerAll(LimboServer server) { 54 | register("help", new CmdHelp(server)); 55 | register("conn", new CmdConn(server)); 56 | register("mem", new CmdMem()); 57 | register("stop", new CmdStop()); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/play/PacketBossBar.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.packets.play; 19 | 20 | import ua.nanit.limbo.protocol.ByteMessage; 21 | import ua.nanit.limbo.protocol.PacketOut; 22 | import ua.nanit.limbo.protocol.registry.Version; 23 | import ua.nanit.limbo.server.data.BossBar; 24 | 25 | import java.util.UUID; 26 | 27 | /** 28 | * Packet for 1.9+ 29 | */ 30 | public class PacketBossBar implements PacketOut { 31 | 32 | private UUID uuid; 33 | private BossBar bossBar; 34 | private int flags; 35 | 36 | public void setUuid(UUID uuid) { 37 | this.uuid = uuid; 38 | } 39 | 40 | public void setBossBar(BossBar bossBar) { 41 | this.bossBar = bossBar; 42 | } 43 | 44 | public void setFlags(int flags) { 45 | this.flags = flags; 46 | } 47 | 48 | @Override 49 | public void encode(ByteMessage msg, Version version) { 50 | msg.writeUuid(uuid); 51 | msg.writeVarInt(0); // Create bossbar 52 | msg.writeString(bossBar.getText()); 53 | msg.writeFloat(bossBar.getHealth()); 54 | msg.writeVarInt(bossBar.getColor().getIndex()); 55 | msg.writeVarInt(bossBar.getDivision().getIndex()); 56 | msg.writeByte(flags); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/server/Connections.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.server; 19 | 20 | import ua.nanit.limbo.connection.ClientConnection; 21 | 22 | import java.util.Collection; 23 | import java.util.Collections; 24 | import java.util.Map; 25 | import java.util.UUID; 26 | import java.util.concurrent.ConcurrentHashMap; 27 | 28 | public final class Connections { 29 | 30 | private final Map connections; 31 | 32 | public Connections() { 33 | connections = new ConcurrentHashMap<>(); 34 | } 35 | 36 | public Collection getAllConnections() { 37 | return Collections.unmodifiableCollection(connections.values()); 38 | } 39 | 40 | public int getCount() { 41 | return connections.size(); 42 | } 43 | 44 | public void addConnection(ClientConnection connection) { 45 | connections.put(connection.getUuid(), connection); 46 | Logger.info("Player %s connected (%s) [%s]", connection.getUsername(), 47 | connection.getAddress(), connection.getClientVersion()); 48 | } 49 | 50 | public void removeConnection(ClientConnection connection) { 51 | connections.remove(connection.getUuid()); 52 | Logger.info("Player %s disconnected", connection.getUsername()); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/login/PacketLoginSuccess.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.packets.login; 19 | 20 | import ua.nanit.limbo.protocol.ByteMessage; 21 | import ua.nanit.limbo.protocol.PacketOut; 22 | import ua.nanit.limbo.protocol.registry.Version; 23 | 24 | import java.util.UUID; 25 | 26 | public class PacketLoginSuccess implements PacketOut { 27 | 28 | private UUID uuid; 29 | private String username; 30 | 31 | public void setUuid(UUID uuid) { 32 | this.uuid = uuid; 33 | } 34 | 35 | public void setUsername(String username) { 36 | this.username = username; 37 | } 38 | 39 | @Override 40 | public void encode(ByteMessage msg, Version version) { 41 | if (version.moreOrEqual(Version.V1_16)) { 42 | msg.writeUuid(uuid); 43 | } else if (version.moreOrEqual(Version.V1_7_6)) { 44 | msg.writeString(uuid.toString()); 45 | } else { 46 | msg.writeString(uuid.toString().replace("-", "")); 47 | } 48 | msg.writeString(username); 49 | if (version.moreOrEqual(Version.V1_19)) { 50 | msg.writeVarInt(0); 51 | } 52 | } 53 | 54 | @Override 55 | public String toString() { 56 | return getClass().getSimpleName(); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/play/PacketKeepAlive.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.packets.play; 19 | 20 | import ua.nanit.limbo.protocol.ByteMessage; 21 | import ua.nanit.limbo.protocol.Packet; 22 | import ua.nanit.limbo.protocol.registry.Version; 23 | 24 | public class PacketKeepAlive implements Packet { 25 | 26 | private long id; 27 | 28 | public long getId() { 29 | return id; 30 | } 31 | 32 | public void setId(long id) { 33 | this.id = id; 34 | } 35 | 36 | @Override 37 | public void encode(ByteMessage msg, Version version) { 38 | if (version.moreOrEqual(Version.V1_12_2)) { 39 | msg.writeLong(id); 40 | } else if (version.moreOrEqual(Version.V1_8)) { 41 | msg.writeVarInt((int) id); 42 | } else { 43 | msg.writeInt((int) id); 44 | } 45 | } 46 | 47 | @Override 48 | public void decode(ByteMessage msg, Version version) { 49 | if (version.moreOrEqual(Version.V1_12_2)) { 50 | this.id = msg.readLong(); 51 | } else if (version.moreOrEqual(Version.V1_8)) { 52 | this.id = msg.readVarInt(); 53 | } else { 54 | this.id = msg.readInt(); 55 | } 56 | } 57 | 58 | @Override 59 | public String toString() { 60 | return getClass().getSimpleName(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/login/PacketLoginPluginResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.packets.login; 19 | 20 | import ua.nanit.limbo.connection.ClientConnection; 21 | import ua.nanit.limbo.protocol.ByteMessage; 22 | import ua.nanit.limbo.protocol.PacketIn; 23 | import ua.nanit.limbo.protocol.registry.Version; 24 | import ua.nanit.limbo.server.LimboServer; 25 | 26 | public class PacketLoginPluginResponse implements PacketIn { 27 | 28 | private int messageId; 29 | private boolean successful; 30 | private ByteMessage data; 31 | 32 | public int getMessageId() { 33 | return messageId; 34 | } 35 | 36 | public boolean isSuccessful() { 37 | return successful; 38 | } 39 | 40 | public ByteMessage getData() { 41 | return data; 42 | } 43 | 44 | @Override 45 | public void decode(ByteMessage msg, Version version) { 46 | messageId = msg.readVarInt(); 47 | successful = msg.readBoolean(); 48 | 49 | if (msg.readableBytes() > 0) { 50 | int i = msg.readableBytes(); 51 | data = new ByteMessage(msg.readBytes(i)); 52 | } 53 | } 54 | 55 | @Override 56 | public void handle(ClientConnection conn, LimboServer server) { 57 | server.getPacketHandler().handle(conn, this); 58 | } 59 | 60 | @Override 61 | public String toString() { 62 | return getClass().getSimpleName(); 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/server/data/PingData.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.server.data; 19 | 20 | import org.checkerframework.checker.nullness.qual.Nullable; 21 | import org.spongepowered.configurate.ConfigurationNode; 22 | import org.spongepowered.configurate.serialize.TypeSerializer; 23 | import ua.nanit.limbo.util.Colors; 24 | 25 | import java.lang.reflect.Type; 26 | 27 | public class PingData { 28 | 29 | private String version; 30 | private String description; 31 | 32 | public String getVersion() { 33 | return version; 34 | } 35 | 36 | public void setVersion(String version) { 37 | this.version = version; 38 | } 39 | 40 | public String getDescription() { 41 | return description; 42 | } 43 | 44 | public void setDescription(String description) { 45 | this.description = description; 46 | } 47 | 48 | public static class Serializer implements TypeSerializer { 49 | 50 | @Override 51 | public PingData deserialize(Type type, ConfigurationNode node) { 52 | PingData pingData = new PingData(); 53 | pingData.setDescription(Colors.of(node.node("description").getString(""))); 54 | pingData.setVersion(Colors.of(node.node("version").getString(""))); 55 | return pingData; 56 | } 57 | 58 | @Override 59 | public void serialize(Type type, @Nullable PingData obj, ConfigurationNode node) { 60 | 61 | } 62 | } 63 | } -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/play/PacketPlayerPositionAndLook.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.packets.play; 19 | 20 | import ua.nanit.limbo.protocol.ByteMessage; 21 | import ua.nanit.limbo.protocol.PacketOut; 22 | import ua.nanit.limbo.protocol.registry.Version; 23 | 24 | public class PacketPlayerPositionAndLook implements PacketOut { 25 | 26 | private double x; 27 | private double y; 28 | private double z; 29 | private float yaw; 30 | private float pitch; 31 | private int teleportId; 32 | 33 | public PacketPlayerPositionAndLook() {} 34 | 35 | public PacketPlayerPositionAndLook(double x, double y, double z, float yaw, float pitch, int teleportId) { 36 | this.x = x; 37 | this.y = y; 38 | this.z = z; 39 | this.yaw = yaw; 40 | this.pitch = pitch; 41 | this.teleportId = teleportId; 42 | } 43 | 44 | @Override 45 | public void encode(ByteMessage msg, Version version) { 46 | msg.writeDouble(x); 47 | msg.writeDouble(y + (version.less(Version.V1_8) ? 1.62F : 0)); 48 | msg.writeDouble(z); 49 | msg.writeFloat(yaw); 50 | msg.writeFloat(pitch); 51 | 52 | if (version.moreOrEqual(Version.V1_8)) { 53 | msg.writeByte(0x08); 54 | } else { 55 | msg.writeBoolean(true); 56 | } 57 | 58 | if (version.moreOrEqual(Version.V1_9)) { 59 | msg.writeVarInt(teleportId); 60 | } 61 | 62 | if (version.fromTo(Version.V1_17, Version.V1_19_3)) { 63 | msg.writeBoolean(false); // Dismount vehicle 64 | } 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/PacketHandshake.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.packets; 19 | 20 | import ua.nanit.limbo.connection.ClientConnection; 21 | import ua.nanit.limbo.protocol.ByteMessage; 22 | import ua.nanit.limbo.protocol.PacketIn; 23 | import ua.nanit.limbo.protocol.registry.State; 24 | import ua.nanit.limbo.protocol.registry.Version; 25 | import ua.nanit.limbo.server.LimboServer; 26 | 27 | public class PacketHandshake implements PacketIn { 28 | 29 | private Version version; 30 | private String host; 31 | private int port; 32 | private State nextState; 33 | 34 | public Version getVersion() { 35 | return version; 36 | } 37 | 38 | public String getHost() { 39 | return host; 40 | } 41 | 42 | public int getPort() { 43 | return port; 44 | } 45 | 46 | public State getNextState() { 47 | return nextState; 48 | } 49 | 50 | @Override 51 | public void decode(ByteMessage msg, Version version) { 52 | try { 53 | this.version = Version.of(msg.readVarInt()); 54 | } catch (IllegalArgumentException e) { 55 | this.version = Version.UNDEFINED; 56 | } 57 | 58 | this.host = msg.readString(); 59 | this.port = msg.readUnsignedShort(); 60 | this.nextState = State.getById(msg.readVarInt()); 61 | } 62 | 63 | @Override 64 | public String toString() { 65 | return getClass().getSimpleName(); 66 | } 67 | 68 | @Override 69 | public void handle(ClientConnection conn, LimboServer server) { 70 | server.getPacketHandler().handle(conn, this); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/play/PacketDeclareCommands.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.packets.play; 19 | 20 | import ua.nanit.limbo.protocol.ByteMessage; 21 | import ua.nanit.limbo.protocol.PacketOut; 22 | import ua.nanit.limbo.protocol.registry.Version; 23 | 24 | import java.util.List; 25 | 26 | /** 27 | * Packet for 1.13+ 28 | */ 29 | public class PacketDeclareCommands implements PacketOut { 30 | 31 | private List commands; 32 | 33 | public void setCommands(List commands) { 34 | this.commands = commands; 35 | } 36 | 37 | @Override 38 | public void encode(ByteMessage msg, Version version) { 39 | msg.writeVarInt(commands.size() * 2 + 1); // +1 because declaring root node 40 | 41 | // Declare root node 42 | 43 | msg.writeByte(0); 44 | msg.writeVarInt(commands.size()); 45 | 46 | for (int i = 1; i <= commands.size() * 2; i++) { 47 | msg.writeVarInt(i++); 48 | } 49 | 50 | // Declare other commands 51 | 52 | int i = 1; 53 | for (String cmd : commands) { 54 | msg.writeByte(1 | 0x04); 55 | msg.writeVarInt(1); 56 | msg.writeVarInt(i + 1); 57 | msg.writeString(cmd); 58 | i++; 59 | 60 | msg.writeByte(2 | 0x04 | 0x10); 61 | msg.writeVarInt(1); 62 | msg.writeVarInt(i); 63 | msg.writeString("arg"); 64 | msg.writeString("brigadier:string"); 65 | msg.writeVarInt(0); 66 | msg.writeString("minecraft:ask_server"); 67 | i++; 68 | } 69 | 70 | msg.writeVarInt(0); 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/connection/pipeline/VarIntFrameDecoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.connection.pipeline; 19 | 20 | import io.netty.buffer.ByteBuf; 21 | import io.netty.channel.ChannelHandlerContext; 22 | import io.netty.handler.codec.ByteToMessageDecoder; 23 | import ua.nanit.limbo.server.Logger; 24 | 25 | import java.util.List; 26 | 27 | public class VarIntFrameDecoder extends ByteToMessageDecoder { 28 | 29 | @Override 30 | protected void decode(ChannelHandlerContext ctx, ByteBuf in, List out) { 31 | if (!ctx.channel().isActive()) { 32 | in.clear(); 33 | return; 34 | } 35 | 36 | VarIntByteDecoder reader = new VarIntByteDecoder(); 37 | int varIntEnd = in.forEachByte(reader); 38 | 39 | if (varIntEnd == -1) return; 40 | 41 | if (reader.getResult() == VarIntByteDecoder.DecodeResult.SUCCESS) { 42 | int readVarInt = reader.getReadVarInt(); 43 | int bytesRead = reader.getBytesRead(); 44 | if (readVarInt < 0) { 45 | Logger.error("[VarIntFrameDecoder] Bad data length"); 46 | } else if (readVarInt == 0) { 47 | in.readerIndex(varIntEnd + 1); 48 | } else { 49 | int minimumRead = bytesRead + readVarInt; 50 | if (in.isReadable(minimumRead)) { 51 | out.add(in.retainedSlice(varIntEnd + 1, readVarInt)); 52 | in.skipBytes(minimumRead); 53 | } 54 | } 55 | } else if (reader.getResult() == VarIntByteDecoder.DecodeResult.TOO_BIG) { 56 | Logger.error("[VarIntFrameDecoder] Too big data"); 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/connection/ClientChannelInitializer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.connection; 19 | 20 | import io.netty.channel.Channel; 21 | import io.netty.channel.ChannelInitializer; 22 | import io.netty.channel.ChannelPipeline; 23 | import io.netty.handler.timeout.ReadTimeoutHandler; 24 | import ua.nanit.limbo.connection.pipeline.PacketDecoder; 25 | import ua.nanit.limbo.connection.pipeline.PacketEncoder; 26 | import ua.nanit.limbo.connection.pipeline.VarIntFrameDecoder; 27 | import ua.nanit.limbo.connection.pipeline.VarIntLengthEncoder; 28 | import ua.nanit.limbo.server.LimboServer; 29 | 30 | import java.util.concurrent.TimeUnit; 31 | 32 | public class ClientChannelInitializer extends ChannelInitializer { 33 | 34 | private final LimboServer server; 35 | 36 | public ClientChannelInitializer(LimboServer server) { 37 | this.server = server; 38 | } 39 | 40 | @Override 41 | protected void initChannel(Channel channel) { 42 | ChannelPipeline pipeline = channel.pipeline(); 43 | 44 | PacketDecoder decoder = new PacketDecoder(); 45 | PacketEncoder encoder = new PacketEncoder(); 46 | ClientConnection connection = new ClientConnection(channel, server, decoder, encoder); 47 | 48 | pipeline.addLast("timeout", new ReadTimeoutHandler(server.getConfig().getReadTimeout(), 49 | TimeUnit.MILLISECONDS)); 50 | pipeline.addLast("frame_decoder", new VarIntFrameDecoder()); 51 | pipeline.addLast("frame_encoder", new VarIntLengthEncoder()); 52 | pipeline.addLast("decoder", decoder); 53 | pipeline.addLast("encoder", encoder); 54 | pipeline.addLast("handler", connection); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/play/PacketSystemMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.packets.play; 19 | 20 | import ua.nanit.limbo.protocol.ByteMessage; 21 | import ua.nanit.limbo.protocol.PacketOut; 22 | import ua.nanit.limbo.protocol.registry.Version; 23 | 24 | import java.util.UUID; 25 | 26 | public class PacketSystemMessage implements PacketOut { 27 | 28 | private String jsonData; 29 | private PositionLegacy position; 30 | private UUID sender; 31 | 32 | public void setJsonData(String jsonData) { 33 | this.jsonData = jsonData; 34 | } 35 | 36 | public void setPosition(PositionLegacy position) { 37 | this.position = position; 38 | } 39 | 40 | public void setSender(UUID sender) { 41 | this.sender = sender; 42 | } 43 | 44 | @Override 45 | public void encode(ByteMessage msg, Version version) { 46 | msg.writeString(jsonData); 47 | if (version.moreOrEqual(Version.V1_19_1)) { 48 | msg.writeBoolean(position.index == PositionLegacy.ACTION_BAR.index); 49 | } else if (version.moreOrEqual(Version.V1_19)) { 50 | msg.writeVarInt(position.index); 51 | } else if (version.moreOrEqual(Version.V1_8)) { 52 | msg.writeByte(position.index); 53 | } 54 | 55 | if (version.moreOrEqual(Version.V1_16) && version.less(Version.V1_19)) 56 | msg.writeUuid(sender); 57 | } 58 | 59 | public enum PositionLegacy { 60 | 61 | CHAT(0), 62 | SYSTEM_MESSAGE(1), 63 | ACTION_BAR(2); 64 | 65 | private final int index; 66 | 67 | PositionLegacy(int index) { 68 | this.index = index; 69 | } 70 | 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/status/PacketStatusResponse.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.packets.status; 19 | 20 | import ua.nanit.limbo.protocol.ByteMessage; 21 | import ua.nanit.limbo.protocol.PacketOut; 22 | import ua.nanit.limbo.protocol.registry.Version; 23 | import ua.nanit.limbo.server.LimboServer; 24 | 25 | public class PacketStatusResponse implements PacketOut { 26 | 27 | private static final String TEMPLATE = "{ \"version\": { \"name\": \"%s\", \"protocol\": %d }, \"players\": { \"max\": %d, \"online\": %d, \"sample\": [] }, \"description\": %s }"; 28 | 29 | private LimboServer server; 30 | 31 | public PacketStatusResponse() { } 32 | 33 | public PacketStatusResponse(LimboServer server) { 34 | this.server = server; 35 | } 36 | 37 | @Override 38 | public void encode(ByteMessage msg, Version version) { 39 | int protocol = server.getConfig().getInfoForwarding().isNone() 40 | ? version.getProtocolNumber() 41 | : Version.getMax().getProtocolNumber(); 42 | 43 | String ver = server.getConfig().getPingData().getVersion(); 44 | String desc = server.getConfig().getPingData().getDescription(); 45 | 46 | msg.writeString(getResponseJson(ver, protocol, 47 | server.getConfig().getMaxPlayers(), 48 | server.getConnections().getCount(), desc)); 49 | } 50 | 51 | @Override 52 | public String toString() { 53 | return getClass().getSimpleName(); 54 | } 55 | 56 | private String getResponseJson(String version, int protocol, int maxPlayers, int online, String description) { 57 | return String.format(TEMPLATE, version, protocol, maxPlayers, online, description); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/connection/pipeline/PacketDecoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.connection.pipeline; 19 | 20 | import io.netty.buffer.ByteBuf; 21 | import io.netty.channel.ChannelHandlerContext; 22 | import io.netty.handler.codec.MessageToMessageDecoder; 23 | import ua.nanit.limbo.protocol.ByteMessage; 24 | import ua.nanit.limbo.protocol.Packet; 25 | import ua.nanit.limbo.protocol.registry.State; 26 | import ua.nanit.limbo.protocol.registry.Version; 27 | import ua.nanit.limbo.server.Logger; 28 | 29 | import java.util.List; 30 | 31 | public class PacketDecoder extends MessageToMessageDecoder { 32 | 33 | private State.PacketRegistry mappings; 34 | private Version version; 35 | 36 | public PacketDecoder() { 37 | updateVersion(Version.getMin()); 38 | updateState(State.HANDSHAKING); 39 | } 40 | 41 | @Override 42 | protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List out) throws Exception { 43 | if (!ctx.channel().isActive() || mappings == null) return; 44 | 45 | ByteMessage msg = new ByteMessage(buf); 46 | int packetId = msg.readVarInt(); 47 | Packet packet = mappings.getPacket(packetId); 48 | 49 | if (packet != null) { 50 | Logger.debug("Received packet %s[0x%s]", packet.toString(), Integer.toHexString(packetId)); 51 | try { 52 | packet.decode(msg, version); 53 | } catch (Exception e) { 54 | Logger.warning("Cannot decode packet 0x%s: %s", Integer.toHexString(packetId), e.getMessage()); 55 | } 56 | 57 | ctx.fireChannelRead(packet); 58 | } else { 59 | Logger.debug("Undefined incoming packet: 0x" + Integer.toHexString(packetId)); 60 | } 61 | } 62 | 63 | public void updateVersion(Version version) { 64 | this.version = version; 65 | } 66 | 67 | public void updateState(State state) { 68 | this.mappings = state.serverBound.getRegistry(version); 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/resources/dimension/codec_old.snbt: -------------------------------------------------------------------------------- 1 | { 2 | "dimension": [ 3 | { 4 | name: "minecraft:overworld", 5 | id: 0, 6 | piglin_safe: 0b, 7 | natural: 1b, 8 | ambient_light: 0.0f, 9 | infiniburn: "minecraft:infiniburn_overworld", 10 | respawn_anchor_works: 0b, 11 | has_skylight: 1b, 12 | bed_works: 1b, 13 | shrunk: 0, 14 | effects: "minecraft:overworld", 15 | has_raids: 1b, 16 | min_y: 0, 17 | height: 256, 18 | logical_height: 256, 19 | coordinate_scale: 1.0d, 20 | ultrawarm: 0b, 21 | has_ceiling: 0b 22 | }, 23 | { 24 | name: "minecraft:overworld_caves", 25 | id: 1, 26 | piglin_safe: 0b, 27 | natural: 1b, 28 | shrunk: 0, 29 | ambient_light: 0.0f, 30 | infiniburn: "minecraft:infiniburn_overworld", 31 | respawn_anchor_works: 0b, 32 | has_skylight: 1b, 33 | bed_works: 1b, 34 | effects: "minecraft:overworld", 35 | has_raids: 1b, 36 | min_y: 0, 37 | height: 256, 38 | logical_height: 256, 39 | coordinate_scale: 1.0d, 40 | ultrawarm: 0b, 41 | has_ceiling: 1b 42 | }, 43 | { 44 | name: "minecraft:the_nether", 45 | id: 2, 46 | piglin_safe: 1b, 47 | natural: 0b, 48 | shrunk: 0, 49 | ambient_light: 0.1f, 50 | infiniburn: "minecraft:infiniburn_nether", 51 | respawn_anchor_works: 1b, 52 | has_skylight: 0b, 53 | bed_works: 0b, 54 | effects: "minecraft:the_nether", 55 | fixed_time: 18000L, 56 | has_raids: 0b, 57 | min_y: 0, 58 | height: 256, 59 | logical_height: 128, 60 | coordinate_scale: 8.0d, 61 | ultrawarm: 1b, 62 | has_ceiling: 1b 63 | }, 64 | { 65 | name: "minecraft:the_end", 66 | id: 3, 67 | piglin_safe: 0b, 68 | natural: 0b, 69 | ambient_light: 0.0f, 70 | infiniburn: "minecraft:infiniburn_end", 71 | respawn_anchor_works: 0b, 72 | has_skylight: 0b, 73 | shrunk: 0, 74 | bed_works: 0b, 75 | effects: "minecraft:the_end", 76 | fixed_time: 6000L, 77 | has_raids: 1b, 78 | min_y: 0, 79 | height: 256, 80 | logical_height: 256, 81 | coordinate_scale: 1.0d, 82 | ultrawarm: 0b, 83 | has_ceiling: 0b 84 | } 85 | ] 86 | } 87 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/server/data/Title.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see . 16 | */ 17 | 18 | package ua.nanit.limbo.server.data; 19 | 20 | import org.checkerframework.checker.nullness.qual.Nullable; 21 | import org.spongepowered.configurate.ConfigurationNode; 22 | import org.spongepowered.configurate.serialize.TypeSerializer; 23 | import ua.nanit.limbo.util.Colors; 24 | 25 | import java.lang.reflect.Type; 26 | 27 | public class Title { 28 | 29 | private String title; 30 | private String subtitle; 31 | private int fadeIn; 32 | private int stay; 33 | private int fadeOut; 34 | 35 | public String getTitle() { 36 | return title; 37 | } 38 | 39 | public String getSubtitle() { 40 | return subtitle; 41 | } 42 | 43 | public int getFadeIn() { 44 | return fadeIn; 45 | } 46 | 47 | public int getStay() { 48 | return stay; 49 | } 50 | 51 | public int getFadeOut() { 52 | return fadeOut; 53 | } 54 | 55 | public void setTitle(String title) { 56 | this.title = title; 57 | } 58 | 59 | public void setSubtitle(String subtitle) { 60 | this.subtitle = subtitle; 61 | } 62 | 63 | public void setFadeIn(int fadeIn) { 64 | this.fadeIn = fadeIn; 65 | } 66 | 67 | public void setStay(int stay) { 68 | this.stay = stay; 69 | } 70 | 71 | public void setFadeOut(int fadeOut) { 72 | this.fadeOut = fadeOut; 73 | } 74 | 75 | public static class Serializer implements TypeSerializer { 76 | 77 | @Override 78 | public Title deserialize(Type type, ConfigurationNode node) { 79 | Title title = new Title(); 80 | title.setTitle(Colors.of(node.node("title").getString(""))); 81 | title.setSubtitle(Colors.of(node.node("subtitle").getString(""))); 82 | title.setFadeIn(node.node("fadeIn").getInt(10)); 83 | title.setStay(node.node("stay").getInt(100)); 84 | title.setFadeOut(node.node("fadeOut").getInt(10)); 85 | return title; 86 | } 87 | 88 | @Override 89 | public void serialize(Type type, @Nullable Title obj, ConfigurationNode node) { 90 | // Not used 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/connection/pipeline/PacketEncoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 | */ 17 | 18 | package ua.nanit.limbo.connection.pipeline; 19 | 20 | import io.netty.buffer.ByteBuf; 21 | import io.netty.channel.ChannelHandlerContext; 22 | import io.netty.handler.codec.MessageToByteEncoder; 23 | import ua.nanit.limbo.protocol.ByteMessage; 24 | import ua.nanit.limbo.protocol.Packet; 25 | import ua.nanit.limbo.protocol.PacketSnapshot; 26 | import ua.nanit.limbo.protocol.registry.State; 27 | import ua.nanit.limbo.protocol.registry.Version; 28 | import ua.nanit.limbo.server.Logger; 29 | 30 | public class PacketEncoder extends MessageToByteEncoder<Packet> { 31 | 32 | private State.PacketRegistry registry; 33 | private Version version; 34 | 35 | public PacketEncoder() { 36 | updateVersion(Version.getMin()); 37 | updateState(State.HANDSHAKING); 38 | } 39 | 40 | @Override 41 | protected void encode(ChannelHandlerContext ctx, Packet packet, ByteBuf out) throws Exception { 42 | if (registry == null) return; 43 | 44 | ByteMessage msg = new ByteMessage(out); 45 | int packetId; 46 | 47 | if (packet instanceof PacketSnapshot) { 48 | packetId = registry.getPacketId(((PacketSnapshot)packet).getWrappedPacket().getClass()); 49 | } else { 50 | packetId = registry.getPacketId(packet.getClass()); 51 | } 52 | 53 | if (packetId == -1) { 54 | Logger.warning("Undefined packet class: %s[0x%s]", packet.getClass().getName(), Integer.toHexString(packetId)); 55 | return; 56 | } 57 | 58 | msg.writeVarInt(packetId); 59 | 60 | try { 61 | packet.encode(msg, version); 62 | 63 | if (Logger.getLevel() >= Logger.Level.DEBUG.getIndex()) { 64 | Logger.debug("Sending %s[0x%s] packet (%d bytes)", packet.toString(), Integer.toHexString(packetId), msg.readableBytes()); 65 | } 66 | } catch (Exception e) { 67 | Logger.error("Cannot encode packet 0x%s: %s", Integer.toHexString(packetId), e.getMessage()); 68 | } 69 | } 70 | 71 | public void updateVersion(Version version) { 72 | this.version = version; 73 | } 74 | 75 | public void updateState(State state) { 76 | this.registry = state.clientBound.getRegistry(version); 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/PacketSnapshot.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 | */ 17 | 18 | package ua.nanit.limbo.protocol; 19 | 20 | import ua.nanit.limbo.protocol.registry.Version; 21 | 22 | import java.util.HashMap; 23 | import java.util.Map; 24 | 25 | /** 26 | * PacketSnapshot encodes packet to byte array for each MC version. 27 | * Some versions have same snapshot, so there are mappings to avoid data copying 28 | */ 29 | public class PacketSnapshot implements PacketOut { 30 | 31 | private final PacketOut packet; 32 | private final Map<Version, byte[]> versionMessages = new HashMap<>(); 33 | private final Map<Version, Version> mappings = new HashMap<>(); 34 | 35 | public PacketSnapshot(PacketOut packet) { 36 | this.packet = packet; 37 | } 38 | 39 | public PacketOut getWrappedPacket() { 40 | return packet; 41 | } 42 | 43 | public void encode() { 44 | Map<Integer, Version> hashes = new HashMap<>(); 45 | 46 | for (Version version : Version.values()) { 47 | if (version.equals(Version.UNDEFINED)) continue; 48 | 49 | ByteMessage encodedMessage = ByteMessage.create(); 50 | packet.encode(encodedMessage, version); 51 | 52 | int hash = encodedMessage.hashCode(); 53 | Version hashed = hashes.get(hash); 54 | 55 | if (hashed != null) { 56 | mappings.put(version, hashed); 57 | } else { 58 | hashes.put(hash, version); 59 | mappings.put(version, version); 60 | versionMessages.put(version, encodedMessage.toByteArray()); 61 | } 62 | 63 | encodedMessage.release(); 64 | } 65 | } 66 | 67 | @Override 68 | public void encode(ByteMessage msg, Version version) { 69 | Version mapped = mappings.get(version); 70 | byte[] message = versionMessages.get(mapped); 71 | 72 | if (message != null) 73 | msg.writeBytes(message); 74 | else 75 | throw new IllegalArgumentException("No mappings for version " + version); 76 | } 77 | 78 | @Override 79 | public String toString() { 80 | return packet.getClass().getSimpleName(); 81 | } 82 | 83 | public static PacketSnapshot of(PacketOut packet) { 84 | PacketSnapshot snapshot = new PacketSnapshot(packet); 85 | snapshot.encode(); 86 | return snapshot; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/play/PacketTitleLegacy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.packets.play; 19 | 20 | import ua.nanit.limbo.protocol.ByteMessage; 21 | import ua.nanit.limbo.protocol.PacketOut; 22 | import ua.nanit.limbo.protocol.registry.Version; 23 | import ua.nanit.limbo.server.data.Title; 24 | 25 | public class PacketTitleLegacy implements PacketOut { 26 | 27 | private Action action; 28 | private final PacketTitleSetTitle title; 29 | private final PacketTitleSetSubTitle subtitle; 30 | private final PacketTitleTimes times; 31 | 32 | public PacketTitleLegacy() { 33 | this.title = new PacketTitleSetTitle(); 34 | this.subtitle = new PacketTitleSetSubTitle(); 35 | this.times = new PacketTitleTimes(); 36 | } 37 | 38 | public void setAction(Action action) { 39 | this.action = action; 40 | } 41 | 42 | public void setTitle(Title title) { 43 | this.title.setTitle(title.getTitle()); 44 | this.subtitle.setSubtitle(title.getSubtitle()); 45 | this.times.setFadeIn(title.getFadeIn()); 46 | this.times.setStay(title.getStay()); 47 | this.times.setFadeOut(title.getFadeOut()); 48 | } 49 | 50 | @Override 51 | public void encode(ByteMessage msg, Version version) { 52 | msg.writeVarInt(action.getId(version)); 53 | 54 | switch (action) { 55 | case SET_TITLE: 56 | title.encode(msg, version); 57 | break; 58 | case SET_SUBTITLE: 59 | subtitle.encode(msg, version); 60 | break; 61 | case SET_TIMES_AND_DISPLAY: 62 | times.encode(msg, version); 63 | break; 64 | default: 65 | throw new IllegalArgumentException("Invalid title action: " + action); 66 | } 67 | } 68 | 69 | public enum Action { 70 | SET_TITLE(0), 71 | SET_SUBTITLE(1), 72 | SET_TIMES_AND_DISPLAY(3, 2); 73 | 74 | private final int id; 75 | private final int legacyId; 76 | 77 | Action(int id, int legacyId) { 78 | this.id = id; 79 | this.legacyId = legacyId; 80 | } 81 | 82 | Action(int id) { 83 | this(id, id); 84 | } 85 | 86 | public int getId(Version version) { 87 | return version.less(Version.V1_11) ? legacyId : id; 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/resources/settings.yml: -------------------------------------------------------------------------------- 1 | # 2 | # NanoLimbo configuration 3 | # 4 | 5 | # Server's host address and port. Set ip empty to use public address 6 | bind: 7 | ip: 'localhost' 8 | port: 65535 9 | 10 | # Max amount of players can join to server 11 | # Set -1 to make it infinite 12 | maxPlayers: 100 13 | 14 | # Server's data in servers list 15 | ping: 16 | description: '{"text": "&9NanoLimbo"}' 17 | version: 'NanoLimbo' 18 | 19 | # Available dimensions: OVERWORLD, NETHER, THE_END 20 | dimension: THE_END 21 | 22 | # Whether to display the player in the player list 23 | # For 1.16.5 clients, player list will be sent even if disabled, to avoid crash 24 | playerList: 25 | enable: false 26 | username: 'NanoLimbo' 27 | 28 | # Whether to display header and footer in player list 29 | # For 1.8+ clients 30 | headerAndFooter: 31 | enable: false 32 | header: '{"text": "&eWelcome!"}' 33 | footer: '{"text": "&9NanoLimbo"}' 34 | 35 | # Setup player's game mode 36 | # 0 - Survival 37 | # 1 - Creative (hide HP and food bar) 38 | # 2 - Adventure 39 | # 3 - Spectator (hide all UI bars) 40 | # Spectator works on 1.8+ clients 41 | gameMode: 3 42 | 43 | # Server name which is shown under F3 44 | # For 1.13+ clients 45 | brandName: 46 | enable: true 47 | content: 'NanoLimbo' 48 | 49 | # Message sends when player join to server 50 | joinMessage: 51 | enable: true 52 | text: '{"text": "&eWelcome to the Limbo!"}' 53 | 54 | # BossBar displays when player join to server 55 | # For 1.9+ clients 56 | bossBar: 57 | enable: true 58 | text: '{"text": "Welcome to the Limbo!"}' 59 | health: 1.0 60 | # Available colors: PINK, BLUE, RED, GREEN, YELLOW, PURPLE, WHITE 61 | color: PINK 62 | # Available divisions: SOLID, DASHES_6, DASHES_10, DASHES_12, DASHES_20 63 | division: SOLID 64 | 65 | # Display title and subtitle 66 | # For 1.8+ clients 67 | title: 68 | enable: true 69 | # Set title text value empty, if you need only subtitle 70 | title: '{"text": "&9&lWelcome!"}' 71 | # Set subtitle text value empty, if you need only title 72 | subtitle: '{"text": "&6NanoLimbo"}' 73 | # Fade in time in ticks (1 sec = 20 ticks) 74 | fadeIn: 10 75 | # Stay time in ticks 76 | stay: 100 77 | # Fade out time in ticks 78 | fadeOut: 10 79 | 80 | # Player info forwarding support. 81 | # Available types: 82 | # - NONE 83 | # - LEGACY 84 | # - MODERN 85 | # - BUNGEE_GUARD 86 | # Don't use secret if you not use MODERN type 87 | infoForwarding: 88 | type: NONE 89 | secret: '<YOUR_SECRET_HERE>' 90 | tokens: 91 | - '<BUNGEE_GUARD_TOKEN>' 92 | 93 | # Read timeout for connections in milliseconds 94 | readTimeout: 30000 95 | 96 | # Define log level. For production, I'd recommend to use level 2 97 | # Log levels: 98 | # 0 - Display only errors 99 | # 1 - Display errors, warnings 100 | # 2 - Display errors, warnings, info 101 | # 3 - Display errors, warnings, info, debug 102 | debugLevel: 2 103 | 104 | # Warning! Do not touch params of this block, if you not completely sure what is this! 105 | netty: 106 | # Use Linux native transport type, if it possible 107 | useEpoll: true 108 | # EventLoopGroup threads count 109 | threads: 110 | bossGroup: 1 111 | workerGroup: 4 -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/server/Logger.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 | */ 17 | 18 | package ua.nanit.limbo.server; 19 | 20 | import java.time.LocalTime; 21 | import java.time.format.DateTimeFormatter; 22 | 23 | public final class Logger { 24 | 25 | private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("hh:mm:ss"); 26 | private static int debugLevel = Level.INFO.getIndex(); 27 | 28 | private Logger() {} 29 | 30 | public static int getLevel() { 31 | return debugLevel; 32 | } 33 | 34 | public static void info(Object msg, Object... args) { 35 | print(Level.INFO, msg, null, args); 36 | } 37 | 38 | public static void debug(Object msg, Object... args) { 39 | print(Level.DEBUG, msg, null, args); 40 | } 41 | 42 | public static void warning(Object msg, Object... args) { 43 | print(Level.WARNING, msg, null, args); 44 | } 45 | 46 | public static void warning(Object msg, Throwable t, Object... args) { 47 | print(Level.WARNING, msg, t, args); 48 | } 49 | 50 | public static void error(Object msg, Object... args) { 51 | print(Level.ERROR, msg, null, args); 52 | } 53 | 54 | public static void error(Object msg, Throwable t, Object... args) { 55 | print(Level.ERROR, msg, t, args); 56 | } 57 | 58 | public static void print(Level level, Object msg, Throwable t, Object... args) { 59 | if (debugLevel >= level.getIndex()) { 60 | System.out.printf("%s: %s%n", getPrefix(level), String.format(msg.toString(), args)); 61 | if (t != null) t.printStackTrace(); 62 | } 63 | } 64 | 65 | private static String getPrefix(Level level) { 66 | return String.format("[%s] [%s]", getTime(), level.getDisplay()); 67 | } 68 | 69 | private static String getTime() { 70 | return LocalTime.now().format(FORMATTER); 71 | } 72 | 73 | static void setLevel(int level) { 74 | debugLevel = level; 75 | } 76 | 77 | public enum Level { 78 | 79 | ERROR("ERROR", 0), 80 | WARNING("WARNING", 1), 81 | INFO("INFO", 2), 82 | DEBUG("DEBUG", 3); 83 | 84 | private final String display; 85 | private final int index; 86 | 87 | Level(String display, int index) { 88 | this.display = display; 89 | this.index = index; 90 | } 91 | 92 | public String getDisplay() { 93 | return display; 94 | } 95 | 96 | public int getIndex() { 97 | return index; 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/play/PacketPlayerInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.packets.play; 19 | 20 | import ua.nanit.limbo.protocol.ByteMessage; 21 | import ua.nanit.limbo.protocol.PacketOut; 22 | import ua.nanit.limbo.protocol.registry.Version; 23 | 24 | import java.util.EnumSet; 25 | import java.util.UUID; 26 | 27 | /** 28 | * This packet was very simplified and using only for ADD_PLAYER action 29 | */ 30 | public class PacketPlayerInfo implements PacketOut { 31 | 32 | private int gameMode = 3; 33 | private String username = ""; 34 | private UUID uuid; 35 | 36 | public void setGameMode(int gameMode) { 37 | this.gameMode = gameMode; 38 | } 39 | 40 | public void setUsername(String username) { 41 | this.username = username; 42 | } 43 | 44 | public void setUuid(UUID uuid) { 45 | this.uuid = uuid; 46 | } 47 | 48 | @Override 49 | public void encode(ByteMessage msg, Version version) { 50 | if (version.less(Version.V1_8)) { 51 | msg.writeString(username); 52 | msg.writeBoolean(true); // Is online 53 | msg.writeShort(0); 54 | } else { 55 | if (version.moreOrEqual(Version.V1_19_3)) { 56 | EnumSet<Action> actions = EnumSet.noneOf(Action.class); 57 | actions.add(Action.ADD_PLAYER); 58 | actions.add(Action.UPDATE_LISTED); 59 | actions.add(Action.UPDATE_GAMEMODE); 60 | msg.writeEnumSet(actions, Action.class); 61 | 62 | msg.writeVarInt(1); // Array length (1 element) 63 | msg.writeUuid(uuid); // UUID 64 | msg.writeString(username); //Username 65 | msg.writeVarInt(0); //Properties (0 is empty) 66 | 67 | msg.writeBoolean(true); //Update listed 68 | msg.writeVarInt(gameMode); //Gamemode 69 | return; 70 | } 71 | 72 | msg.writeVarInt(0); // Add player action 73 | msg.writeVarInt(1); 74 | msg.writeUuid(uuid); 75 | msg.writeString(username); 76 | msg.writeVarInt(0); 77 | msg.writeVarInt(gameMode); 78 | msg.writeVarInt(60); 79 | msg.writeBoolean(false); 80 | 81 | if (version.moreOrEqual(Version.V1_19)) { 82 | msg.writeBoolean(false); 83 | } 84 | } 85 | } 86 | 87 | public static enum Action { 88 | ADD_PLAYER, 89 | INITIALIZE_CHAT, 90 | UPDATE_GAMEMODE, 91 | UPDATE_LISTED, 92 | UPDATE_LATENCY, 93 | UPDATE_DISPLAY_NAME; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## NanoLimbo 2 | 3 | This is lightweight minecraft limbo server, written on Java with Netty. 4 | The main goal of the project is maximum simplicity with a minimum number of sent and processed packets. 5 | This limbo is empty, there are no ability to set schematic building since 6 | this is not necessary. You can send useful information in chat or BossBar. 7 | 8 | No plugins, no logs. The server is fully clear. It only able keep a lot of players while the main server is down. 9 | 10 | The general features: 11 | * High performance. The server not saves and not cached any useless (for limbo) data. 12 | * Doesn't spawn threads per player. Uses fixed threads pool. 13 | * Support for **BungeeCord** and **Velocity** info forwarding. 14 | * Support for [BungeeGuard](https://www.spigotmc.org/resources/79601/) handshake format. 15 | * Multiple versions support. 16 | * Fully configurable. 17 | * Lightweight. App size around **2MB.** 18 | 19 | ![](https://i.imgur.com/sT8p1Gz.png) 20 | 21 | ### Versions support 22 | 23 | Symbol `X` means all minor versions. 24 | 25 | - [x] 1.7.X 26 | - [x] 1.8.X 27 | - [x] 1.9.X 28 | - [x] 1.10.X 29 | - [x] 1.11.X 30 | - [x] 1.12.X 31 | - [x] 1.13.X 32 | - [x] 1.14.X 33 | - [x] 1.15.X 34 | - [x] 1.16.X 35 | - [x] 1.17.X 36 | - [x] 1.18.X 37 | - [x] 1.19.X 38 | 39 | The server **doesn't** support snapshots. 40 | 41 | ### Commands 42 | 43 | * `help` - Show help message 44 | * `conn` - Display amount of connections 45 | * `mem` - Display memory usage stats 46 | * `stop` - Stop the server 47 | 48 | Note, that it also will be closed correctly if you just press `Ctrl+C`. 49 | 50 | ### Installation 51 | 52 | The installation process is simple. 53 | 54 | 1. Download the latest version of program **[here](https://github.com/Nan1t/NanoLimbo/releases)** 55 | 2. Put jar file in the folder you want. 56 | 3. Create a start script as you did it for Bukkit or BungeeCord with command like this: 57 | `java -jar NanoLimbo-<version>.jar` 58 | 4. The server will create `settings.yml` file. It's a server configuration. 59 | 5. Configure it as you want and restart server. 60 | 61 | ### About player info forwarding 62 | 63 | The server supports player info forwarding from the proxy. There are several types of info forwarding: 64 | 65 | * `LEGACY` - The **BungeeCord** IP forwarding. 66 | * `MODERN` - **Velocity** native info forwarding type. 67 | * `BUNGEE_GUARD` - **BungeeGuard** forwarding type. 68 | 69 | If you use BungeeCord, or Velocity with `LEGACY` forwarding, just set this type in the config. 70 | If you use Velocity with `MODERN` info forwarding, set this type and paste secret key from Velocity 71 | config into `secret` field. 72 | If you installed BungeeGuard on your proxy, then use `BUNGEE_GUARD` forwarding type. 73 | Then add your tokens to `tokens` list. 74 | 75 | ### Contributing 76 | 77 | You can create pull request, if you found some bug, optimization ability, or you want to add some functional, 78 | which is suitable for limbo server and won't significantly load the server. 79 | 80 | All PR's should be targeted to the `dev` branch to keep the `main` stable and clear. 81 | 82 | ### Building 83 | 84 | Required software: 85 | 86 | * JDK 1.8+ 87 | * Gradle 7+ (optional) 88 | 89 | To build minimized .jar, go to project root and write in terminal: 90 | 91 | ``` 92 | ./gradlew shadowJar 93 | ``` 94 | 95 | ### Contacts 96 | 97 | If you have any question or suggestion, join to [Discord server](https://discord.gg/4VGP3Gv) 98 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/server/data/InfoForwarding.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 | */ 17 | 18 | package ua.nanit.limbo.server.data; 19 | 20 | import org.checkerframework.checker.nullness.qual.Nullable; 21 | import org.spongepowered.configurate.ConfigurationNode; 22 | import org.spongepowered.configurate.serialize.SerializationException; 23 | import org.spongepowered.configurate.serialize.TypeSerializer; 24 | 25 | import java.nio.charset.StandardCharsets; 26 | import java.util.List; 27 | 28 | public class InfoForwarding { 29 | 30 | private Type type; 31 | private byte[] secretKey; 32 | private List<String> tokens; 33 | 34 | public Type getType() { 35 | return type; 36 | } 37 | 38 | public byte[] getSecretKey() { 39 | return secretKey; 40 | } 41 | 42 | public List<String> getTokens() { 43 | return tokens; 44 | } 45 | 46 | public boolean hasToken(String token) { 47 | return tokens != null && token != null && tokens.contains(token); 48 | } 49 | 50 | public boolean isNone() { 51 | return type == Type.NONE; 52 | } 53 | 54 | public boolean isLegacy() { 55 | return type == Type.LEGACY; 56 | } 57 | 58 | public boolean isModern() { 59 | return type == Type.MODERN; 60 | } 61 | 62 | public boolean isBungeeGuard() { 63 | return type == Type.BUNGEE_GUARD; 64 | } 65 | 66 | public enum Type { 67 | NONE, 68 | LEGACY, 69 | MODERN, 70 | BUNGEE_GUARD 71 | } 72 | 73 | public static class Serializer implements TypeSerializer<InfoForwarding> { 74 | 75 | @Override 76 | public InfoForwarding deserialize(java.lang.reflect.Type type, ConfigurationNode node) throws SerializationException { 77 | InfoForwarding forwarding = new InfoForwarding(); 78 | 79 | try { 80 | forwarding.type = Type.valueOf(node.node("type").getString("").toUpperCase()); 81 | } catch (IllegalArgumentException e) { 82 | throw new SerializationException("Undefined info forwarding type"); 83 | } 84 | 85 | if (forwarding.type == Type.MODERN) { 86 | forwarding.secretKey = node.node("secret").getString("").getBytes(StandardCharsets.UTF_8); 87 | } 88 | 89 | if (forwarding.type == Type.BUNGEE_GUARD) { 90 | forwarding.tokens = node.node("tokens").getList(String.class); 91 | } 92 | 93 | return forwarding; 94 | } 95 | 96 | @Override 97 | public void serialize(java.lang.reflect.Type type, @Nullable InfoForwarding obj, ConfigurationNode node) throws SerializationException { 98 | 99 | } 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/registry/Version.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.registry; 19 | 20 | import java.util.HashMap; 21 | import java.util.Map; 22 | 23 | public enum Version { 24 | 25 | UNDEFINED(-1), 26 | V1_7_2(4), 27 | // 1.7.2-1.7.5 has same protocol numbers 28 | V1_7_6(5), 29 | // 1.7.6-1.7.10 has same protocol numbers 30 | V1_8(47), 31 | // 1.8-1.8.8 has same protocol numbers 32 | V1_9(107), 33 | V1_9_1(108), 34 | V1_9_2(109), 35 | V1_9_4(110), 36 | V1_10(210), 37 | // 1.10-1.10.2 has same protocol numbers 38 | V1_11(315), 39 | V1_11_1(316), 40 | // 1.11.2 has same protocol number 41 | V1_12(335), 42 | V1_12_1(338), 43 | V1_12_2(340), 44 | V1_13(393), 45 | V1_13_1(401), 46 | V1_13_2(404), 47 | V1_14(477), 48 | V1_14_1(480), 49 | V1_14_2(485), 50 | V1_14_3(490), 51 | V1_14_4(498), 52 | V1_15(573), 53 | V1_15_1(575), 54 | V1_15_2(578), 55 | V1_16(735), 56 | V1_16_1(736), 57 | V1_16_2(751), 58 | V1_16_3(753), 59 | V1_16_4(754), 60 | // 1.16.5 has same protocol number 61 | V1_17(755), 62 | V1_17_1(756), 63 | V1_18(757), 64 | // 1.18.1 has same protocol number 65 | V1_18_2(758), 66 | V1_19(759), 67 | V1_19_1(760), 68 | // 1.19.2 has same protocol number 69 | V1_19_3(761), 70 | V1_19_4(762); 71 | 72 | private static final Map<Integer, Version> VERSION_MAP; 73 | private static final Version MAX; 74 | 75 | static { 76 | Version[] values = values(); 77 | 78 | VERSION_MAP = new HashMap<>(); 79 | MAX = values[values.length - 1]; 80 | 81 | Version last = null; 82 | for (Version version : values) { 83 | version.prev = last; 84 | last = version; 85 | VERSION_MAP.put(version.getProtocolNumber(), version); 86 | } 87 | } 88 | 89 | private final int protocolNumber; 90 | private Version prev; 91 | 92 | Version(int protocolNumber) { 93 | this.protocolNumber = protocolNumber; 94 | } 95 | 96 | public int getProtocolNumber() { 97 | return this.protocolNumber; 98 | } 99 | 100 | public Version getPrev() { 101 | return prev; 102 | } 103 | 104 | public boolean more(Version another) { 105 | return this.protocolNumber > another.protocolNumber; 106 | } 107 | 108 | public boolean moreOrEqual(Version another) { 109 | return this.protocolNumber >= another.protocolNumber; 110 | } 111 | 112 | public boolean less(Version another) { 113 | return this.protocolNumber < another.protocolNumber; 114 | } 115 | 116 | public boolean lessOrEqual(Version another) { 117 | return this.protocolNumber <= another.protocolNumber; 118 | } 119 | 120 | public boolean fromTo(Version min, Version max) { 121 | return this.protocolNumber >= min.protocolNumber && this.protocolNumber <= max.protocolNumber; 122 | } 123 | 124 | public boolean isSupported() { 125 | return this != UNDEFINED; 126 | } 127 | 128 | public static Version getMin() { 129 | return V1_7_2; 130 | } 131 | 132 | public static Version getMax() { 133 | return MAX; 134 | } 135 | 136 | public static Version of(int protocolNumber) { 137 | return VERSION_MAP.getOrDefault(protocolNumber, UNDEFINED); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/server/data/BossBar.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 | */ 17 | 18 | package ua.nanit.limbo.server.data; 19 | 20 | import org.checkerframework.checker.nullness.qual.Nullable; 21 | import org.spongepowered.configurate.ConfigurationNode; 22 | import org.spongepowered.configurate.serialize.SerializationException; 23 | import org.spongepowered.configurate.serialize.TypeSerializer; 24 | import ua.nanit.limbo.util.Colors; 25 | 26 | import java.lang.reflect.Type; 27 | 28 | public class BossBar { 29 | 30 | private String text; 31 | private float health; 32 | private Color color; 33 | private Division division; 34 | 35 | public String getText() { 36 | return text; 37 | } 38 | 39 | public float getHealth() { 40 | return health; 41 | } 42 | 43 | public Color getColor() { 44 | return color; 45 | } 46 | 47 | public Division getDivision() { 48 | return division; 49 | } 50 | 51 | public void setText(String text) { 52 | this.text = text; 53 | } 54 | 55 | public void setHealth(float health) { 56 | this.health = health; 57 | } 58 | 59 | public void setColor(Color color) { 60 | this.color = color; 61 | } 62 | 63 | public void setDivision(Division division) { 64 | this.division = division; 65 | } 66 | 67 | public enum Color { 68 | 69 | PINK(0), 70 | BLUE(1), 71 | RED(2), 72 | GREEN(3), 73 | YELLOW(4), 74 | PURPLE(5), 75 | WHITE(6); 76 | 77 | private final int index; 78 | 79 | Color(int index) { 80 | this.index = index; 81 | } 82 | 83 | public int getIndex() { 84 | return index; 85 | } 86 | } 87 | 88 | public enum Division { 89 | 90 | SOLID(0), 91 | DASHES_6(1), 92 | DASHES_10(2), 93 | DASHES_12(3), 94 | DASHES_20(4); 95 | 96 | private final int index; 97 | 98 | Division(int index) { 99 | this.index = index; 100 | } 101 | 102 | public int getIndex() { 103 | return index; 104 | } 105 | } 106 | 107 | public static class Serializer implements TypeSerializer<BossBar> { 108 | 109 | @Override 110 | public BossBar deserialize(Type type, ConfigurationNode node) throws SerializationException { 111 | BossBar bossBar = new BossBar(); 112 | 113 | bossBar.setText(Colors.of(node.node("text").getString(""))); 114 | bossBar.setHealth(node.node("health").getFloat()); 115 | 116 | if (bossBar.getHealth() < 0 || bossBar.getHealth() > 1) 117 | throw new SerializationException("BossBar health value must be between 0.0 and 1.0"); 118 | 119 | try { 120 | bossBar.setColor(Color.valueOf(node.node("color").getString("").toUpperCase())); 121 | } catch (IllegalArgumentException e) { 122 | throw new SerializationException("Invalid bossbar color"); 123 | } 124 | 125 | try { 126 | bossBar.setDivision(Division.valueOf(node.node("division").getString("").toUpperCase())); 127 | } catch (IllegalArgumentException e) { 128 | throw new SerializationException("Invalid bossbar division"); 129 | } 130 | 131 | return bossBar; 132 | } 133 | 134 | @Override 135 | public void serialize(Type type, @Nullable BossBar obj, ConfigurationNode node) { 136 | 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/world/DimensionRegistry.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 | */ 17 | 18 | package ua.nanit.limbo.world; 19 | 20 | import net.kyori.adventure.nbt.CompoundBinaryTag; 21 | import net.kyori.adventure.nbt.ListBinaryTag; 22 | import net.kyori.adventure.nbt.TagStringIO; 23 | import ua.nanit.limbo.server.LimboServer; 24 | import ua.nanit.limbo.server.Logger; 25 | 26 | import java.io.*; 27 | import java.nio.charset.StandardCharsets; 28 | import java.util.stream.Collectors; 29 | 30 | public final class DimensionRegistry { 31 | 32 | private final LimboServer server; 33 | 34 | private Dimension defaultDimension_1_16; 35 | private Dimension defaultDimension_1_18_2; 36 | 37 | private CompoundBinaryTag codec_1_16; 38 | private CompoundBinaryTag codec_1_18_2; 39 | private CompoundBinaryTag codec_1_19; 40 | private CompoundBinaryTag codec_1_19_1; 41 | private CompoundBinaryTag codec_1_19_4; 42 | private CompoundBinaryTag oldCodec; 43 | 44 | public DimensionRegistry(LimboServer server) { 45 | this.server = server; 46 | } 47 | 48 | public CompoundBinaryTag getCodec_1_16() { 49 | return codec_1_16; 50 | } 51 | 52 | public CompoundBinaryTag getCodec_1_18_2() { 53 | return codec_1_18_2; 54 | } 55 | 56 | public CompoundBinaryTag getCodec_1_19() { 57 | return codec_1_19; 58 | } 59 | 60 | public CompoundBinaryTag getCodec_1_19_1() { 61 | return codec_1_19_1; 62 | } 63 | 64 | public CompoundBinaryTag getCodec_1_19_4() { 65 | return codec_1_19_4; 66 | } 67 | 68 | public CompoundBinaryTag getOldCodec() { 69 | return oldCodec; 70 | } 71 | 72 | public Dimension getDefaultDimension_1_16() { 73 | return defaultDimension_1_16; 74 | } 75 | 76 | public Dimension getDefaultDimension_1_18_2() { 77 | return defaultDimension_1_18_2; 78 | } 79 | 80 | public void load(String def) throws IOException { 81 | codec_1_16 = readCodecFile("/dimension/codec_1_16.snbt"); 82 | codec_1_18_2 = readCodecFile("/dimension/codec_1_18_2.snbt"); 83 | codec_1_19 = readCodecFile("/dimension/codec_1_19.snbt"); 84 | codec_1_19_1 = readCodecFile("/dimension/codec_1_19_1.snbt"); 85 | codec_1_19_4 = readCodecFile("/dimension/codec_1_19_4.snbt"); 86 | // On 1.16-1.16.1 different codec format 87 | oldCodec = readCodecFile("/dimension/codec_old.snbt"); 88 | 89 | defaultDimension_1_16 = getDefaultDimension(def, codec_1_16); 90 | defaultDimension_1_18_2 = getDefaultDimension(def, codec_1_18_2); 91 | } 92 | 93 | private Dimension getDefaultDimension(String def, CompoundBinaryTag tag) { 94 | ListBinaryTag dimensions = tag.getCompound("minecraft:dimension_type").getList("value"); 95 | 96 | CompoundBinaryTag overWorld = (CompoundBinaryTag) ((CompoundBinaryTag) dimensions.get(0)).get("element"); 97 | CompoundBinaryTag nether = (CompoundBinaryTag) ((CompoundBinaryTag) dimensions.get(2)).get("element"); 98 | CompoundBinaryTag theEnd = (CompoundBinaryTag) ((CompoundBinaryTag) dimensions.get(3)).get("element"); 99 | 100 | switch (def.toLowerCase()) { 101 | case "overworld": 102 | return new Dimension(0, "minecraft:overworld", overWorld); 103 | case "the_nether": 104 | return new Dimension(-1, "minecraft:nether", nether); 105 | case "the_end": 106 | return new Dimension(1, "minecraft:the_end", theEnd); 107 | default: 108 | Logger.warning("Undefined dimension type: '%s'. Using THE_END as default", def); 109 | return new Dimension(1, "minecraft:the_end", theEnd); 110 | } 111 | } 112 | 113 | private CompoundBinaryTag readCodecFile(String resPath) throws IOException { 114 | InputStream in = server.getClass().getResourceAsStream(resPath); 115 | 116 | if(in == null) 117 | throw new FileNotFoundException("Cannot find dimension registry file"); 118 | 119 | return TagStringIO.get().asCompound(streamToString(in)); 120 | } 121 | 122 | private String streamToString(InputStream in) throws IOException { 123 | InputStreamReader isReader = new InputStreamReader(in, StandardCharsets.UTF_8); 124 | BufferedReader bufReader = new BufferedReader(isReader); 125 | String content = bufReader.lines() 126 | .collect(Collectors.joining("\n")); 127 | 128 | isReader.close(); 129 | bufReader.close(); 130 | 131 | return content; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/server/LimboServer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 | */ 17 | 18 | package ua.nanit.limbo.server; 19 | 20 | import io.netty.bootstrap.ServerBootstrap; 21 | import io.netty.channel.ChannelOption; 22 | import io.netty.channel.EventLoopGroup; 23 | import io.netty.channel.ServerChannel; 24 | import io.netty.channel.epoll.Epoll; 25 | import io.netty.channel.epoll.EpollEventLoopGroup; 26 | import io.netty.channel.epoll.EpollServerSocketChannel; 27 | import io.netty.channel.nio.NioEventLoopGroup; 28 | import io.netty.channel.socket.nio.NioServerSocketChannel; 29 | import io.netty.util.ResourceLeakDetector; 30 | import ua.nanit.limbo.configuration.LimboConfig; 31 | import ua.nanit.limbo.connection.ClientChannelInitializer; 32 | import ua.nanit.limbo.connection.ClientConnection; 33 | import ua.nanit.limbo.connection.PacketHandler; 34 | import ua.nanit.limbo.connection.PacketSnapshots; 35 | import ua.nanit.limbo.world.DimensionRegistry; 36 | 37 | import java.nio.file.Paths; 38 | import java.util.concurrent.ScheduledFuture; 39 | import java.util.concurrent.TimeUnit; 40 | 41 | public final class LimboServer { 42 | 43 | private LimboConfig config; 44 | private PacketHandler packetHandler; 45 | private Connections connections; 46 | private DimensionRegistry dimensionRegistry; 47 | private ScheduledFuture<?> keepAliveTask; 48 | 49 | private EventLoopGroup bossGroup; 50 | private EventLoopGroup workerGroup; 51 | 52 | private CommandManager commandManager; 53 | 54 | public LimboConfig getConfig() { 55 | return config; 56 | } 57 | 58 | public PacketHandler getPacketHandler() { 59 | return packetHandler; 60 | } 61 | 62 | public Connections getConnections() { 63 | return connections; 64 | } 65 | 66 | public DimensionRegistry getDimensionRegistry() { 67 | return dimensionRegistry; 68 | } 69 | 70 | public CommandManager getCommandManager() { 71 | return commandManager; 72 | } 73 | 74 | public void start() throws Exception { 75 | Logger.info("Starting server..."); 76 | 77 | ResourceLeakDetector.setLevel(ResourceLeakDetector.Level.DISABLED); 78 | 79 | config = new LimboConfig(Paths.get("./")); 80 | config.load(); 81 | 82 | packetHandler = new PacketHandler(this); 83 | dimensionRegistry = new DimensionRegistry(this); 84 | dimensionRegistry.load(config.getDimensionType()); 85 | connections = new Connections(); 86 | 87 | PacketSnapshots.initPackets(this); 88 | 89 | startBootstrap(); 90 | 91 | keepAliveTask = workerGroup.scheduleAtFixedRate(this::broadcastKeepAlive, 0L, 5L, TimeUnit.SECONDS); 92 | 93 | Runtime.getRuntime().addShutdownHook(new Thread(this::stop, "NanoLimbo shutdown thread")); 94 | 95 | Logger.info("Server started on %s", config.getAddress()); 96 | 97 | Logger.setLevel(config.getDebugLevel()); 98 | 99 | commandManager = new CommandManager(); 100 | commandManager.registerAll(this); 101 | commandManager.start(); 102 | 103 | System.gc(); 104 | } 105 | 106 | private void startBootstrap() { 107 | Class<? extends ServerChannel> channelClass; 108 | 109 | if (config.isUseEpoll() && Epoll.isAvailable()) { 110 | bossGroup = new EpollEventLoopGroup(config.getBossGroupSize()); 111 | workerGroup = new EpollEventLoopGroup(config.getWorkerGroupSize()); 112 | channelClass = EpollServerSocketChannel.class; 113 | Logger.debug("Using Epoll transport type"); 114 | } else { 115 | bossGroup = new NioEventLoopGroup(config.getBossGroupSize()); 116 | workerGroup = new NioEventLoopGroup(config.getWorkerGroupSize()); 117 | channelClass = NioServerSocketChannel.class; 118 | Logger.debug("Using Java NIO transport type"); 119 | } 120 | 121 | new ServerBootstrap() 122 | .group(bossGroup, workerGroup) 123 | .channel(channelClass) 124 | .childHandler(new ClientChannelInitializer(this)) 125 | .childOption(ChannelOption.TCP_NODELAY, true) 126 | .localAddress(config.getAddress()) 127 | .bind(); 128 | } 129 | 130 | private void broadcastKeepAlive() { 131 | connections.getAllConnections().forEach(ClientConnection::sendKeepAlive); 132 | } 133 | 134 | private void stop() { 135 | Logger.info("Stopping server..."); 136 | 137 | if (keepAliveTask != null) { 138 | keepAliveTask.cancel(true); 139 | } 140 | 141 | if (bossGroup != null) { 142 | bossGroup.shutdownGracefully(); 143 | } 144 | 145 | if (workerGroup != null) { 146 | workerGroup.shutdownGracefully(); 147 | } 148 | 149 | Logger.info("Server stopped, Goodbye!"); 150 | } 151 | 152 | } 153 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/connection/PacketHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 | */ 17 | 18 | package ua.nanit.limbo.connection; 19 | 20 | import com.grack.nanojson.JsonWriter; 21 | import io.netty.buffer.Unpooled; 22 | import ua.nanit.limbo.LimboConstants; 23 | import ua.nanit.limbo.protocol.packets.PacketHandshake; 24 | import ua.nanit.limbo.protocol.packets.login.PacketLoginPluginRequest; 25 | import ua.nanit.limbo.protocol.packets.login.PacketLoginPluginResponse; 26 | import ua.nanit.limbo.protocol.packets.login.PacketLoginStart; 27 | import ua.nanit.limbo.protocol.packets.play.PacketChatMessage; 28 | import ua.nanit.limbo.protocol.packets.play.PacketSystemMessage; 29 | import ua.nanit.limbo.protocol.packets.status.PacketStatusPing; 30 | import ua.nanit.limbo.protocol.packets.status.PacketStatusRequest; 31 | import ua.nanit.limbo.protocol.packets.status.PacketStatusResponse; 32 | import ua.nanit.limbo.server.LimboServer; 33 | import ua.nanit.limbo.server.Logger; 34 | import ua.nanit.limbo.util.UuidUtil; 35 | 36 | import java.util.concurrent.ThreadLocalRandom; 37 | 38 | public class PacketHandler { 39 | 40 | private final LimboServer server; 41 | 42 | public PacketHandler(LimboServer server) { 43 | this.server = server; 44 | } 45 | 46 | public void handle(ClientConnection conn, PacketHandshake packet) { 47 | conn.updateVersion(packet.getVersion()); 48 | conn.updateState(packet.getNextState()); 49 | 50 | Logger.debug("Pinged from %s [%s]", conn.getAddress(), 51 | conn.getClientVersion().toString()); 52 | 53 | if (server.getConfig().getInfoForwarding().isLegacy()) { 54 | String[] split = packet.getHost().split("\00"); 55 | 56 | if (split.length == 3 || split.length == 4) { 57 | conn.setAddress(split[1]); 58 | conn.getGameProfile().setUuid(UuidUtil.fromString(split[2])); 59 | } else { 60 | conn.disconnectLogin("You've enabled player info forwarding. You need to connect with proxy"); 61 | } 62 | } else if (server.getConfig().getInfoForwarding().isBungeeGuard()) { 63 | if (!conn.checkBungeeGuardHandshake(packet.getHost())) { 64 | conn.disconnectLogin("Invalid BungeeGuard token or handshake format"); 65 | } 66 | } 67 | } 68 | 69 | public void handle(ClientConnection conn, PacketChatMessage packet) { 70 | String json = JsonWriter.string() 71 | .object() 72 | .value("text", "<" + conn.getUsername() + "> " + packet.getMessage()) 73 | .value("color", "#9ca3af") 74 | .end() 75 | .done(); 76 | 77 | PacketSystemMessage broadcastPacket = new PacketSystemMessage(); 78 | broadcastPacket.setJsonData(json); 79 | broadcastPacket.setPosition(PacketSystemMessage.PositionLegacy.SYSTEM_MESSAGE); 80 | broadcastPacket.setSender(conn.getUuid()); 81 | 82 | server.getConnections().getAllConnections().forEach(connection -> connection.sendPacket(broadcastPacket)); 83 | Logger.info("[chat] %s: %s", conn.getUsername(), packet.getMessage()); 84 | } 85 | 86 | public void handle(ClientConnection conn, PacketStatusRequest packet) { 87 | conn.sendPacket(new PacketStatusResponse(server)); 88 | } 89 | 90 | public void handle(ClientConnection conn, PacketStatusPing packet) { 91 | conn.sendPacketAndClose(packet); 92 | } 93 | 94 | public void handle(ClientConnection conn, PacketLoginStart packet) { 95 | if (server.getConfig().getMaxPlayers() > 0 && 96 | server.getConnections().getCount() >= server.getConfig().getMaxPlayers()) { 97 | conn.disconnectLogin("Too many players connected"); 98 | return; 99 | } 100 | 101 | if (!conn.getClientVersion().isSupported()) { 102 | conn.disconnectLogin("Unsupported client version"); 103 | return; 104 | } 105 | 106 | if (server.getConfig().getInfoForwarding().isModern()) { 107 | int loginId = ThreadLocalRandom.current().nextInt(0, Integer.MAX_VALUE); 108 | PacketLoginPluginRequest request = new PacketLoginPluginRequest(); 109 | 110 | request.setMessageId(loginId); 111 | request.setChannel(LimboConstants.VELOCITY_INFO_CHANNEL); 112 | request.setData(Unpooled.EMPTY_BUFFER); 113 | 114 | conn.setVelocityLoginMessageId(loginId); 115 | conn.sendPacket(request); 116 | return; 117 | } 118 | 119 | if (!server.getConfig().getInfoForwarding().isModern()) { 120 | conn.getGameProfile().setUsername(packet.getUsername()); 121 | conn.getGameProfile().setUuid(UuidUtil.getOfflineModeUuid(packet.getUsername())); 122 | } 123 | 124 | conn.fireLoginSuccess(); 125 | } 126 | 127 | public void handle(ClientConnection conn, PacketLoginPluginResponse packet) { 128 | if (server.getConfig().getInfoForwarding().isModern() 129 | && packet.getMessageId() == conn.getVelocityLoginMessageId()) { 130 | 131 | if (!packet.isSuccessful() || packet.getData() == null) { 132 | conn.disconnectLogin("You need to connect with Velocity"); 133 | return; 134 | } 135 | 136 | if (!conn.checkVelocityKeyIntegrity(packet.getData())) { 137 | conn.disconnectLogin("Can't verify forwarded player info"); 138 | return; 139 | } 140 | 141 | // Order is important 142 | conn.setAddress(packet.getData().readString()); 143 | conn.getGameProfile().setUuid(packet.getData().readUuid()); 144 | conn.getGameProfile().setUsername(packet.getData().readString()); 145 | 146 | conn.fireLoginSuccess(); 147 | } 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/configuration/LimboConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 | */ 17 | 18 | package ua.nanit.limbo.configuration; 19 | 20 | import org.spongepowered.configurate.ConfigurationNode; 21 | import org.spongepowered.configurate.ConfigurationOptions; 22 | import org.spongepowered.configurate.serialize.TypeSerializerCollection; 23 | import org.spongepowered.configurate.yaml.YamlConfigurationLoader; 24 | import ua.nanit.limbo.util.Colors; 25 | import ua.nanit.limbo.server.data.BossBar; 26 | import ua.nanit.limbo.server.data.InfoForwarding; 27 | import ua.nanit.limbo.server.data.PingData; 28 | import ua.nanit.limbo.server.data.Title; 29 | 30 | import java.io.BufferedReader; 31 | import java.io.FileNotFoundException; 32 | import java.io.IOException; 33 | import java.io.InputStream; 34 | import java.net.SocketAddress; 35 | import java.nio.file.Files; 36 | import java.nio.file.Path; 37 | import java.nio.file.Paths; 38 | 39 | public final class LimboConfig { 40 | 41 | private final Path root; 42 | 43 | private SocketAddress address; 44 | private int maxPlayers; 45 | private PingData pingData; 46 | 47 | private String dimensionType; 48 | private int gameMode; 49 | 50 | private boolean useBrandName; 51 | private boolean useJoinMessage; 52 | private boolean useBossBar; 53 | private boolean useTitle; 54 | private boolean usePlayerList; 55 | private boolean useHeaderAndFooter; 56 | 57 | private String brandName; 58 | private String joinMessage; 59 | private BossBar bossBar; 60 | private Title title; 61 | 62 | private String playerListUsername; 63 | private String playerListHeader; 64 | private String playerListFooter; 65 | 66 | private InfoForwarding infoForwarding; 67 | private long readTimeout; 68 | private int debugLevel; 69 | 70 | private boolean useEpoll; 71 | private int bossGroupSize; 72 | private int workerGroupSize; 73 | 74 | public LimboConfig(Path root) { 75 | this.root = root; 76 | } 77 | 78 | public void load() throws Exception { 79 | ConfigurationOptions options = ConfigurationOptions.defaults().serializers(getSerializers()); 80 | YamlConfigurationLoader loader = YamlConfigurationLoader.builder() 81 | .source(this::getReader) 82 | .defaultOptions(options) 83 | .build(); 84 | 85 | ConfigurationNode conf = loader.load(); 86 | 87 | address = conf.node("bind").get(SocketAddress.class); 88 | maxPlayers = conf.node("maxPlayers").getInt(); 89 | pingData = conf.node("ping").get(PingData.class); 90 | dimensionType = conf.node("dimension").getString("the_end"); 91 | if (dimensionType.equalsIgnoreCase("nether")) { 92 | dimensionType = "the_nether"; 93 | } 94 | if (dimensionType.equalsIgnoreCase("end")) { 95 | dimensionType = "the_end"; 96 | } 97 | gameMode = conf.node("gameMode").getInt(); 98 | useBrandName = conf.node("brandName", "enable").getBoolean(); 99 | useJoinMessage = conf.node("joinMessage", "enable").getBoolean(); 100 | useBossBar = conf.node("bossBar", "enable").getBoolean(); 101 | useTitle = conf.node("title", "enable").getBoolean(); 102 | usePlayerList = conf.node("playerList", "enable").getBoolean(); 103 | playerListUsername = conf.node("playerList", "username").getString(); 104 | useHeaderAndFooter = conf.node("headerAndFooter", "enable").getBoolean(); 105 | 106 | if (useBrandName) 107 | brandName = conf.node("brandName", "content").getString(); 108 | 109 | if (useJoinMessage) 110 | joinMessage = Colors.of(conf.node("joinMessage", "text").getString("")); 111 | 112 | if (useBossBar) 113 | bossBar = conf.node("bossBar").get(BossBar.class); 114 | 115 | if (useTitle) 116 | title = conf.node("title").get(Title.class); 117 | 118 | if (useHeaderAndFooter) { 119 | playerListHeader = Colors.of(conf.node("headerAndFooter", "header").getString()); 120 | playerListFooter = Colors.of(conf.node("headerAndFooter", "footer").getString()); 121 | } 122 | 123 | infoForwarding = conf.node("infoForwarding").get(InfoForwarding.class); 124 | readTimeout = conf.node("readTimeout").getLong(); 125 | debugLevel = conf.node("debugLevel").getInt(); 126 | 127 | useEpoll = conf.node("netty", "useEpoll").getBoolean(true); 128 | bossGroupSize = conf.node("netty", "threads", "bossGroup").getInt(1); 129 | workerGroupSize = conf.node("netty", "threads", "workerGroup").getInt(4); 130 | } 131 | 132 | private BufferedReader getReader() throws IOException { 133 | String name = "settings.yml"; 134 | Path filePath = Paths.get(root.toString(), name); 135 | 136 | if (!Files.exists(filePath)) { 137 | InputStream stream = getClass().getResourceAsStream( "/" + name); 138 | 139 | if (stream == null) 140 | throw new FileNotFoundException("Cannot find settings resource file"); 141 | 142 | Files.copy(stream, filePath); 143 | } 144 | 145 | return Files.newBufferedReader(filePath); 146 | } 147 | 148 | private TypeSerializerCollection getSerializers() { 149 | return TypeSerializerCollection.builder() 150 | .register(SocketAddress.class, new SocketAddressSerializer()) 151 | .register(InfoForwarding.class, new InfoForwarding.Serializer()) 152 | .register(PingData.class, new PingData.Serializer()) 153 | .register(BossBar.class, new BossBar.Serializer()) 154 | .register(Title.class, new Title.Serializer()) 155 | .build(); 156 | } 157 | 158 | public SocketAddress getAddress() { 159 | return address; 160 | } 161 | 162 | public int getMaxPlayers() { 163 | return maxPlayers; 164 | } 165 | 166 | public PingData getPingData() { 167 | return pingData; 168 | } 169 | 170 | public String getDimensionType() { 171 | return dimensionType; 172 | } 173 | 174 | public int getGameMode() { 175 | return gameMode; 176 | } 177 | 178 | public InfoForwarding getInfoForwarding() { 179 | return infoForwarding; 180 | } 181 | 182 | public long getReadTimeout() { 183 | return readTimeout; 184 | } 185 | 186 | public int getDebugLevel() { 187 | return debugLevel; 188 | } 189 | 190 | public boolean isUseBrandName() { 191 | return useBrandName; 192 | } 193 | 194 | public boolean isUseJoinMessage() { 195 | return useJoinMessage; 196 | } 197 | 198 | public boolean isUseBossBar() { 199 | return useBossBar; 200 | } 201 | 202 | public boolean isUseTitle() { 203 | return useTitle; 204 | } 205 | 206 | public boolean isUsePlayerList() { 207 | return usePlayerList; 208 | } 209 | 210 | public boolean isUseHeaderAndFooter() { 211 | return useHeaderAndFooter; 212 | } 213 | 214 | public String getBrandName() { 215 | return brandName; 216 | } 217 | 218 | public String getJoinMessage() { 219 | return joinMessage; 220 | } 221 | 222 | public BossBar getBossBar() { 223 | return bossBar; 224 | } 225 | 226 | public Title getTitle() { 227 | return title; 228 | } 229 | 230 | public String getPlayerListUsername() { 231 | return playerListUsername; 232 | } 233 | 234 | public String getPlayerListHeader() { 235 | return playerListHeader; 236 | } 237 | 238 | public String getPlayerListFooter() { 239 | return playerListFooter; 240 | } 241 | 242 | public boolean isUseEpoll() { 243 | return useEpoll; 244 | } 245 | 246 | public int getBossGroupSize() { 247 | return bossGroupSize; 248 | } 249 | 250 | public int getWorkerGroupSize() { 251 | return workerGroupSize; 252 | } 253 | } 254 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/connection/PacketSnapshots.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 | */ 17 | 18 | package ua.nanit.limbo.connection; 19 | 20 | import ua.nanit.limbo.LimboConstants; 21 | import ua.nanit.limbo.protocol.PacketSnapshot; 22 | import ua.nanit.limbo.protocol.packets.login.PacketLoginSuccess; 23 | import ua.nanit.limbo.protocol.packets.play.*; 24 | import ua.nanit.limbo.server.LimboServer; 25 | import ua.nanit.limbo.server.data.Title; 26 | import ua.nanit.limbo.util.UuidUtil; 27 | 28 | import java.util.Collections; 29 | import java.util.UUID; 30 | import java.util.concurrent.ThreadLocalRandom; 31 | 32 | public final class PacketSnapshots { 33 | 34 | public static PacketSnapshot PACKET_LOGIN_SUCCESS; 35 | public static PacketSnapshot PACKET_JOIN_GAME; 36 | public static PacketSnapshot PACKET_SPAWN_POSITION; 37 | public static PacketSnapshot PACKET_PLUGIN_MESSAGE; 38 | public static PacketSnapshot PACKET_PLAYER_ABILITIES; 39 | public static PacketSnapshot PACKET_PLAYER_INFO; 40 | public static PacketSnapshot PACKET_DECLARE_COMMANDS; 41 | public static PacketSnapshot PACKET_JOIN_MESSAGE; 42 | public static PacketSnapshot PACKET_BOSS_BAR; 43 | public static PacketSnapshot PACKET_HEADER_AND_FOOTER; 44 | 45 | public static PacketSnapshot PACKET_PLAYER_POS_AND_LOOK_LEGACY; 46 | // For 1.19 we need to spawn player outside world to avoid stuck in terrain loading 47 | public static PacketSnapshot PACKET_PLAYER_POS_AND_LOOK; 48 | 49 | public static PacketSnapshot PACKET_TITLE_TITLE; 50 | public static PacketSnapshot PACKET_TITLE_SUBTITLE; 51 | public static PacketSnapshot PACKET_TITLE_TIMES; 52 | 53 | public static PacketSnapshot PACKET_TITLE_LEGACY_TITLE; 54 | public static PacketSnapshot PACKET_TITLE_LEGACY_SUBTITLE; 55 | public static PacketSnapshot PACKET_TITLE_LEGACY_TIMES; 56 | 57 | 58 | private PacketSnapshots() { } 59 | 60 | public static void initPackets(LimboServer server) { 61 | final String username = server.getConfig().getPingData().getVersion(); 62 | final UUID uuid = UuidUtil.getOfflineModeUuid(username); 63 | 64 | PacketLoginSuccess loginSuccess = new PacketLoginSuccess(); 65 | loginSuccess.setUsername(username); 66 | loginSuccess.setUuid(uuid); 67 | 68 | PacketJoinGame joinGame = new PacketJoinGame(); 69 | String worldName = "minecraft:" + server.getConfig().getDimensionType().toLowerCase(); 70 | joinGame.setEntityId(0); 71 | joinGame.setEnableRespawnScreen(true); 72 | joinGame.setFlat(false); 73 | joinGame.setGameMode(server.getConfig().getGameMode()); 74 | joinGame.setHardcore(false); 75 | joinGame.setMaxPlayers(server.getConfig().getMaxPlayers()); 76 | joinGame.setPreviousGameMode(-1); 77 | joinGame.setReducedDebugInfo(true); 78 | joinGame.setDebug(false); 79 | joinGame.setViewDistance(0); 80 | joinGame.setWorldName(worldName); 81 | joinGame.setWorldNames(worldName); 82 | joinGame.setHashedSeed(0); 83 | joinGame.setDimensionRegistry(server.getDimensionRegistry()); 84 | 85 | PacketPlayerAbilities playerAbilities = new PacketPlayerAbilities(); 86 | playerAbilities.setFlyingSpeed(0.0F); 87 | playerAbilities.setFlags(0x02); 88 | playerAbilities.setFieldOfView(0.1F); 89 | 90 | int teleportId = ThreadLocalRandom.current().nextInt(); 91 | 92 | PacketPlayerPositionAndLook positionAndLookLegacy 93 | = new PacketPlayerPositionAndLook(0, 64, 0, 0, 0, teleportId); 94 | 95 | PacketPlayerPositionAndLook positionAndLook 96 | = new PacketPlayerPositionAndLook(0, 400, 0, 0, 0, teleportId); 97 | 98 | PacketSpawnPosition packetSpawnPosition = new PacketSpawnPosition(0, 400, 0); 99 | 100 | PacketDeclareCommands declareCommands = new PacketDeclareCommands(); 101 | declareCommands.setCommands(Collections.emptyList()); 102 | 103 | PacketPlayerInfo info = new PacketPlayerInfo(); 104 | info.setUsername(server.getConfig().getPlayerListUsername()); 105 | info.setGameMode(server.getConfig().getGameMode()); 106 | info.setUuid(uuid); 107 | 108 | PACKET_LOGIN_SUCCESS = PacketSnapshot.of(loginSuccess); 109 | PACKET_JOIN_GAME = PacketSnapshot.of(joinGame); 110 | PACKET_PLAYER_POS_AND_LOOK_LEGACY = PacketSnapshot.of(positionAndLookLegacy); 111 | PACKET_PLAYER_POS_AND_LOOK = PacketSnapshot.of(positionAndLook); 112 | PACKET_SPAWN_POSITION = PacketSnapshot.of(packetSpawnPosition); 113 | PACKET_PLAYER_ABILITIES = PacketSnapshot.of(playerAbilities); 114 | PACKET_PLAYER_INFO = PacketSnapshot.of(info); 115 | 116 | PACKET_DECLARE_COMMANDS = PacketSnapshot.of(declareCommands); 117 | 118 | if (server.getConfig().isUseHeaderAndFooter()) { 119 | PacketPlayerListHeader header = new PacketPlayerListHeader(); 120 | header.setHeader(server.getConfig().getPlayerListHeader()); 121 | header.setFooter(server.getConfig().getPlayerListFooter()); 122 | PACKET_HEADER_AND_FOOTER = PacketSnapshot.of(header); 123 | } 124 | 125 | if (server.getConfig().isUseBrandName()){ 126 | PacketPluginMessage pluginMessage = new PacketPluginMessage(); 127 | pluginMessage.setChannel(LimboConstants.BRAND_CHANNEL); 128 | pluginMessage.setMessage(server.getConfig().getBrandName()); 129 | PACKET_PLUGIN_MESSAGE = PacketSnapshot.of(pluginMessage); 130 | } 131 | 132 | if (server.getConfig().isUseJoinMessage()) { 133 | PacketSystemMessage joinMessage = new PacketSystemMessage(); 134 | joinMessage.setJsonData(server.getConfig().getJoinMessage()); 135 | joinMessage.setPosition(PacketSystemMessage.PositionLegacy.SYSTEM_MESSAGE); 136 | joinMessage.setSender(UUID.randomUUID()); 137 | PACKET_JOIN_MESSAGE = PacketSnapshot.of(joinMessage); 138 | } 139 | 140 | if (server.getConfig().isUseBossBar()) { 141 | PacketBossBar bossBar = new PacketBossBar(); 142 | bossBar.setBossBar(server.getConfig().getBossBar()); 143 | bossBar.setUuid(UUID.randomUUID()); 144 | PACKET_BOSS_BAR = PacketSnapshot.of(bossBar); 145 | } 146 | 147 | if (server.getConfig().isUseTitle()) { 148 | Title title = server.getConfig().getTitle(); 149 | 150 | PacketTitleSetTitle packetTitle = new PacketTitleSetTitle(); 151 | PacketTitleSetSubTitle packetSubtitle = new PacketTitleSetSubTitle(); 152 | PacketTitleTimes packetTimes = new PacketTitleTimes(); 153 | 154 | PacketTitleLegacy legacyTitle = new PacketTitleLegacy(); 155 | PacketTitleLegacy legacySubtitle = new PacketTitleLegacy(); 156 | PacketTitleLegacy legacyTimes = new PacketTitleLegacy(); 157 | 158 | packetTitle.setTitle(title.getTitle()); 159 | packetSubtitle.setSubtitle(title.getSubtitle()); 160 | packetTimes.setFadeIn(title.getFadeIn()); 161 | packetTimes.setStay(title.getStay()); 162 | packetTimes.setFadeOut(title.getFadeOut()); 163 | 164 | legacyTitle.setTitle(title); 165 | legacyTitle.setAction(PacketTitleLegacy.Action.SET_TITLE); 166 | 167 | legacySubtitle.setTitle(title); 168 | legacySubtitle.setAction(PacketTitleLegacy.Action.SET_SUBTITLE); 169 | 170 | legacyTimes.setTitle(title); 171 | legacyTimes.setAction(PacketTitleLegacy.Action.SET_TIMES_AND_DISPLAY); 172 | 173 | PACKET_TITLE_TITLE = PacketSnapshot.of(packetTitle); 174 | PACKET_TITLE_SUBTITLE = PacketSnapshot.of(packetSubtitle); 175 | PACKET_TITLE_TIMES = PacketSnapshot.of(packetTimes); 176 | 177 | PACKET_TITLE_LEGACY_TITLE = PacketSnapshot.of(legacyTitle); 178 | PACKET_TITLE_LEGACY_SUBTITLE = PacketSnapshot.of(legacySubtitle); 179 | PACKET_TITLE_LEGACY_TIMES = PacketSnapshot.of(legacyTimes); 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/packets/play/PacketJoinGame.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.packets.play; 19 | 20 | import ua.nanit.limbo.protocol.ByteMessage; 21 | import ua.nanit.limbo.protocol.PacketOut; 22 | import ua.nanit.limbo.protocol.registry.Version; 23 | import ua.nanit.limbo.world.DimensionRegistry; 24 | 25 | public class PacketJoinGame implements PacketOut { 26 | 27 | private int entityId; 28 | private boolean isHardcore = false; 29 | private int gameMode = 2; 30 | private int previousGameMode = -1; 31 | private String[] worldNames; 32 | private DimensionRegistry dimensionRegistry; 33 | private String worldName; 34 | private long hashedSeed; 35 | private int maxPlayers; 36 | private int viewDistance = 2; 37 | private boolean reducedDebugInfo; 38 | private boolean enableRespawnScreen; 39 | private boolean isDebug; 40 | private boolean isFlat; 41 | 42 | public void setEntityId(int entityId) { 43 | this.entityId = entityId; 44 | } 45 | 46 | public void setHardcore(boolean hardcore) { 47 | isHardcore = hardcore; 48 | } 49 | 50 | public void setGameMode(int gameMode) { 51 | this.gameMode = gameMode; 52 | } 53 | 54 | public void setPreviousGameMode(int previousGameMode) { 55 | this.previousGameMode = previousGameMode; 56 | } 57 | 58 | public void setWorldNames(String... worldNames) { 59 | this.worldNames = worldNames; 60 | } 61 | 62 | public void setDimensionRegistry(DimensionRegistry dimensionRegistry) { 63 | this.dimensionRegistry = dimensionRegistry; 64 | } 65 | 66 | public void setWorldName(String worldName) { 67 | this.worldName = worldName; 68 | } 69 | 70 | public void setHashedSeed(long hashedSeed) { 71 | this.hashedSeed = hashedSeed; 72 | } 73 | 74 | public void setMaxPlayers(int maxPlayers) { 75 | this.maxPlayers = maxPlayers; 76 | } 77 | 78 | public void setViewDistance(int viewDistance) { 79 | this.viewDistance = viewDistance; 80 | } 81 | 82 | public void setReducedDebugInfo(boolean reducedDebugInfo) { 83 | this.reducedDebugInfo = reducedDebugInfo; 84 | } 85 | 86 | public void setEnableRespawnScreen(boolean enableRespawnScreen) { 87 | this.enableRespawnScreen = enableRespawnScreen; 88 | } 89 | 90 | public void setDebug(boolean debug) { 91 | isDebug = debug; 92 | } 93 | 94 | public void setFlat(boolean flat) { 95 | isFlat = flat; 96 | } 97 | 98 | @Override 99 | public void encode(ByteMessage msg, Version version) { 100 | msg.writeInt(entityId); 101 | 102 | if (version.fromTo(Version.V1_7_2, Version.V1_7_6)) { 103 | msg.writeByte(gameMode == 3 ? 1 : gameMode); 104 | msg.writeByte(dimensionRegistry.getDefaultDimension_1_16().getId()); 105 | msg.writeByte(0); // Difficulty 106 | msg.writeByte(maxPlayers); 107 | msg.writeString("flat"); // Level type 108 | } 109 | 110 | if (version.fromTo(Version.V1_8, Version.V1_9)) { 111 | msg.writeByte(gameMode); 112 | msg.writeByte(dimensionRegistry.getDefaultDimension_1_16().getId()); 113 | msg.writeByte(0); // Difficulty 114 | msg.writeByte(maxPlayers); 115 | msg.writeString("flat"); // Level type 116 | msg.writeBoolean(reducedDebugInfo); 117 | } 118 | 119 | if (version.fromTo(Version.V1_9_1, Version.V1_13_2)) { 120 | msg.writeByte(gameMode); 121 | msg.writeInt(dimensionRegistry.getDefaultDimension_1_16().getId()); 122 | msg.writeByte(0); // Difficulty 123 | msg.writeByte(maxPlayers); 124 | msg.writeString("flat"); // Level type 125 | msg.writeBoolean(reducedDebugInfo); 126 | } 127 | 128 | if (version.fromTo(Version.V1_14, Version.V1_14_4)) { 129 | msg.writeByte(gameMode); 130 | msg.writeInt(dimensionRegistry.getDefaultDimension_1_16().getId()); 131 | msg.writeByte(maxPlayers); 132 | msg.writeString("flat"); // Level type 133 | msg.writeVarInt(viewDistance); 134 | msg.writeBoolean(reducedDebugInfo); 135 | } 136 | 137 | if (version.fromTo(Version.V1_15, Version.V1_15_2)) { 138 | msg.writeByte(gameMode); 139 | msg.writeInt(dimensionRegistry.getDefaultDimension_1_16().getId()); 140 | msg.writeLong(hashedSeed); 141 | msg.writeByte(maxPlayers); 142 | msg.writeString("flat"); // Level type 143 | msg.writeVarInt(viewDistance); 144 | msg.writeBoolean(reducedDebugInfo); 145 | msg.writeBoolean(enableRespawnScreen); 146 | } 147 | 148 | if (version.fromTo(Version.V1_16, Version.V1_16_1)) { 149 | msg.writeByte(gameMode); 150 | msg.writeByte(previousGameMode); 151 | msg.writeStringsArray(worldNames); 152 | msg.writeCompoundTag(dimensionRegistry.getOldCodec()); 153 | msg.writeString(dimensionRegistry.getDefaultDimension_1_16().getName()); 154 | msg.writeString(worldName); 155 | msg.writeLong(hashedSeed); 156 | msg.writeByte(maxPlayers); 157 | msg.writeVarInt(viewDistance); 158 | msg.writeBoolean(reducedDebugInfo); 159 | msg.writeBoolean(enableRespawnScreen); 160 | msg.writeBoolean(isDebug); 161 | msg.writeBoolean(isFlat); 162 | } 163 | 164 | if (version.fromTo(Version.V1_16_2, Version.V1_17_1)) { 165 | msg.writeBoolean(isHardcore); 166 | msg.writeByte(gameMode); 167 | msg.writeByte(previousGameMode); 168 | msg.writeStringsArray(worldNames); 169 | msg.writeCompoundTag(dimensionRegistry.getCodec_1_16()); 170 | msg.writeCompoundTag(dimensionRegistry.getDefaultDimension_1_16().getData()); 171 | msg.writeString(worldName); 172 | msg.writeLong(hashedSeed); 173 | msg.writeVarInt(maxPlayers); 174 | msg.writeVarInt(viewDistance); 175 | msg.writeBoolean(reducedDebugInfo); 176 | msg.writeBoolean(enableRespawnScreen); 177 | msg.writeBoolean(isDebug); 178 | msg.writeBoolean(isFlat); 179 | } 180 | 181 | if (version.fromTo(Version.V1_18, Version.V1_18_2)) { 182 | msg.writeBoolean(isHardcore); 183 | msg.writeByte(gameMode); 184 | msg.writeByte(previousGameMode); 185 | msg.writeStringsArray(worldNames); 186 | if (version.moreOrEqual(Version.V1_18_2)) { 187 | msg.writeCompoundTag(dimensionRegistry.getCodec_1_18_2()); 188 | msg.writeCompoundTag(dimensionRegistry.getDefaultDimension_1_18_2().getData()); 189 | } else { 190 | msg.writeCompoundTag(dimensionRegistry.getCodec_1_16()); 191 | msg.writeCompoundTag(dimensionRegistry.getDefaultDimension_1_16().getData()); 192 | } 193 | msg.writeString(worldName); 194 | msg.writeLong(hashedSeed); 195 | msg.writeVarInt(maxPlayers); 196 | msg.writeVarInt(viewDistance); 197 | msg.writeVarInt(viewDistance); // Simulation Distance 198 | msg.writeBoolean(reducedDebugInfo); 199 | msg.writeBoolean(enableRespawnScreen); 200 | msg.writeBoolean(isDebug); 201 | msg.writeBoolean(isFlat); 202 | } 203 | 204 | if (version.moreOrEqual(Version.V1_19)) { 205 | msg.writeBoolean(isHardcore); 206 | msg.writeByte(gameMode); 207 | msg.writeByte(previousGameMode); 208 | msg.writeStringsArray(worldNames); 209 | if (version.moreOrEqual(Version.V1_19_1)) { 210 | if (version.moreOrEqual(Version.V1_19_4)) { 211 | msg.writeCompoundTag(dimensionRegistry.getCodec_1_19_4()); 212 | } 213 | else { 214 | msg.writeCompoundTag(dimensionRegistry.getCodec_1_19_1()); 215 | } 216 | } 217 | else { 218 | msg.writeCompoundTag(dimensionRegistry.getCodec_1_19()); 219 | } 220 | msg.writeString(worldName); // World type 221 | msg.writeString(worldName); 222 | msg.writeLong(hashedSeed); 223 | msg.writeVarInt(maxPlayers); 224 | msg.writeVarInt(viewDistance); 225 | msg.writeVarInt(viewDistance); // Simulation Distance 226 | msg.writeBoolean(reducedDebugInfo); 227 | msg.writeBoolean(enableRespawnScreen); 228 | msg.writeBoolean(isDebug); 229 | msg.writeBoolean(isFlat); 230 | msg.writeBoolean(false); 231 | } 232 | } 233 | 234 | } 235 | -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/connection/ClientConnection.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 | */ 17 | 18 | package ua.nanit.limbo.connection; 19 | 20 | import com.grack.nanojson.JsonArray; 21 | import com.grack.nanojson.JsonObject; 22 | import com.grack.nanojson.JsonParser; 23 | import com.grack.nanojson.JsonParserException; 24 | import io.netty.channel.Channel; 25 | import io.netty.channel.ChannelFutureListener; 26 | import io.netty.channel.ChannelHandlerContext; 27 | import io.netty.channel.ChannelInboundHandlerAdapter; 28 | import org.jetbrains.annotations.NotNull; 29 | import ua.nanit.limbo.connection.pipeline.PacketDecoder; 30 | import ua.nanit.limbo.connection.pipeline.PacketEncoder; 31 | import ua.nanit.limbo.protocol.ByteMessage; 32 | import ua.nanit.limbo.protocol.Packet; 33 | import ua.nanit.limbo.protocol.packets.login.PacketDisconnect; 34 | import ua.nanit.limbo.protocol.packets.play.PacketKeepAlive; 35 | import ua.nanit.limbo.protocol.registry.State; 36 | import ua.nanit.limbo.protocol.registry.Version; 37 | import ua.nanit.limbo.server.LimboServer; 38 | import ua.nanit.limbo.server.Logger; 39 | import ua.nanit.limbo.util.UuidUtil; 40 | 41 | import javax.crypto.Mac; 42 | import javax.crypto.spec.SecretKeySpec; 43 | import java.net.InetSocketAddress; 44 | import java.net.SocketAddress; 45 | import java.security.InvalidKeyException; 46 | import java.security.MessageDigest; 47 | import java.util.UUID; 48 | import java.util.concurrent.ThreadLocalRandom; 49 | import java.util.concurrent.TimeUnit; 50 | 51 | public class ClientConnection extends ChannelInboundHandlerAdapter { 52 | 53 | private final LimboServer server; 54 | private final Channel channel; 55 | private final GameProfile gameProfile; 56 | 57 | private final PacketDecoder decoder; 58 | private final PacketEncoder encoder; 59 | 60 | private State state; 61 | private Version clientVersion; 62 | private SocketAddress address; 63 | 64 | private int velocityLoginMessageId = -1; 65 | 66 | public ClientConnection(Channel channel, LimboServer server, PacketDecoder decoder, PacketEncoder encoder) { 67 | this.server = server; 68 | this.channel = channel; 69 | this.decoder = decoder; 70 | this.encoder = encoder; 71 | this.address = channel.remoteAddress(); 72 | this.gameProfile = new GameProfile(); 73 | } 74 | 75 | public UUID getUuid() { 76 | return gameProfile.getUuid(); 77 | } 78 | 79 | public String getUsername() { 80 | return gameProfile.getUsername(); 81 | } 82 | 83 | public SocketAddress getAddress() { 84 | return address; 85 | } 86 | 87 | public Version getClientVersion() { 88 | return clientVersion; 89 | } 90 | 91 | public GameProfile getGameProfile() { 92 | return gameProfile; 93 | } 94 | 95 | @Override 96 | public void channelInactive(@NotNull ChannelHandlerContext ctx) throws Exception { 97 | if (state.equals(State.PLAY)) { 98 | server.getConnections().removeConnection(this); 99 | } 100 | super.channelInactive(ctx); 101 | } 102 | 103 | @Override 104 | public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { 105 | if (channel.isActive()) { 106 | Logger.error("Unhandled exception: ", cause); 107 | } 108 | } 109 | 110 | @Override 111 | public void channelRead(@NotNull ChannelHandlerContext ctx, @NotNull Object msg) { 112 | handlePacket(msg); 113 | } 114 | 115 | public void handlePacket(Object packet) { 116 | if (packet instanceof Packet) { 117 | ((Packet)packet).handle(this, server); 118 | } 119 | } 120 | 121 | public void fireLoginSuccess() { 122 | if (server.getConfig().getInfoForwarding().isModern() && velocityLoginMessageId == -1) { 123 | disconnectLogin("You need to connect with Velocity"); 124 | return; 125 | } 126 | 127 | sendPacket(PacketSnapshots.PACKET_LOGIN_SUCCESS); 128 | updateState(State.PLAY); 129 | server.getConnections().addConnection(this); 130 | 131 | Runnable sendPlayPackets = () -> { 132 | writePacket(PacketSnapshots.PACKET_JOIN_GAME); 133 | writePacket(PacketSnapshots.PACKET_PLAYER_ABILITIES); 134 | 135 | if (clientVersion.less(Version.V1_9)) { 136 | writePacket(PacketSnapshots.PACKET_PLAYER_POS_AND_LOOK_LEGACY); 137 | } else { 138 | writePacket(PacketSnapshots.PACKET_PLAYER_POS_AND_LOOK); 139 | } 140 | 141 | if (clientVersion.moreOrEqual(Version.V1_19_3)) 142 | writePacket(PacketSnapshots.PACKET_SPAWN_POSITION); 143 | 144 | if (server.getConfig().isUsePlayerList() || clientVersion.equals(Version.V1_16_4)) 145 | writePacket(PacketSnapshots.PACKET_PLAYER_INFO); 146 | 147 | if (clientVersion.moreOrEqual(Version.V1_13)) { 148 | writePacket(PacketSnapshots.PACKET_DECLARE_COMMANDS); 149 | 150 | if (PacketSnapshots.PACKET_PLUGIN_MESSAGE != null) 151 | writePacket(PacketSnapshots.PACKET_PLUGIN_MESSAGE); 152 | } 153 | 154 | if (PacketSnapshots.PACKET_BOSS_BAR != null && clientVersion.moreOrEqual(Version.V1_9)) 155 | writePacket(PacketSnapshots.PACKET_BOSS_BAR); 156 | 157 | if (PacketSnapshots.PACKET_JOIN_MESSAGE != null) 158 | writePacket(PacketSnapshots.PACKET_JOIN_MESSAGE); 159 | 160 | if (PacketSnapshots.PACKET_TITLE_TITLE != null && clientVersion.moreOrEqual(Version.V1_8)) 161 | writeTitle(); 162 | 163 | if (PacketSnapshots.PACKET_HEADER_AND_FOOTER != null && clientVersion.moreOrEqual(Version.V1_8)) 164 | writePacket(PacketSnapshots.PACKET_HEADER_AND_FOOTER); 165 | 166 | sendKeepAlive(); 167 | }; 168 | 169 | if (clientVersion.lessOrEqual(Version.V1_7_6)) { 170 | this.channel.eventLoop().schedule(sendPlayPackets, 100, TimeUnit.MILLISECONDS); 171 | } else { 172 | sendPlayPackets.run(); 173 | } 174 | } 175 | 176 | public void disconnectLogin(String reason) { 177 | if (isConnected() && state == State.LOGIN) { 178 | PacketDisconnect disconnect = new PacketDisconnect(); 179 | disconnect.setReason(reason); 180 | sendPacketAndClose(disconnect); 181 | } 182 | } 183 | 184 | public void writeTitle() { 185 | if (clientVersion.moreOrEqual(Version.V1_17)) { 186 | writePacket(PacketSnapshots.PACKET_TITLE_TITLE); 187 | writePacket(PacketSnapshots.PACKET_TITLE_SUBTITLE); 188 | writePacket(PacketSnapshots.PACKET_TITLE_TIMES); 189 | } else { 190 | writePacket(PacketSnapshots.PACKET_TITLE_LEGACY_TITLE); 191 | writePacket(PacketSnapshots.PACKET_TITLE_LEGACY_SUBTITLE); 192 | writePacket(PacketSnapshots.PACKET_TITLE_LEGACY_TIMES); 193 | } 194 | } 195 | 196 | public void sendKeepAlive() { 197 | if (state.equals(State.PLAY)) { 198 | PacketKeepAlive keepAlive = new PacketKeepAlive(); 199 | keepAlive.setId(ThreadLocalRandom.current().nextLong()); 200 | sendPacket(keepAlive); 201 | } 202 | } 203 | 204 | public void sendPacket(Object packet) { 205 | if (isConnected()) 206 | channel.writeAndFlush(packet, channel.voidPromise()); 207 | } 208 | 209 | public void sendPacketAndClose(Object packet) { 210 | if (isConnected()) 211 | channel.writeAndFlush(packet).addListener(ChannelFutureListener.CLOSE); 212 | } 213 | 214 | public void writePacket(Object packet) { 215 | if (isConnected()) 216 | channel.write(packet, channel.voidPromise()); 217 | } 218 | 219 | public boolean isConnected() { 220 | return channel.isActive(); 221 | } 222 | 223 | public void updateState(State state) { 224 | this.state = state; 225 | decoder.updateState(state); 226 | encoder.updateState(state); 227 | } 228 | 229 | public void updateVersion(Version version) { 230 | clientVersion = version; 231 | decoder.updateVersion(version); 232 | encoder.updateVersion(version); 233 | } 234 | 235 | public void setAddress(String host) { 236 | this.address = new InetSocketAddress(host, ((InetSocketAddress)this.address).getPort()); 237 | } 238 | 239 | boolean checkBungeeGuardHandshake(String handshake) { 240 | String[] split = handshake.split("\00"); 241 | 242 | if (split.length != 4) 243 | return false; 244 | 245 | String socketAddressHostname = split[1]; 246 | UUID uuid = UuidUtil.fromString(split[2]); 247 | JsonArray arr; 248 | 249 | try { 250 | arr = JsonParser.array().from(split[3]); 251 | } catch (JsonParserException e) { 252 | return false; 253 | } 254 | 255 | String token = null; 256 | 257 | for (Object obj : arr) { 258 | if (obj instanceof JsonObject) { 259 | JsonObject prop = (JsonObject) obj; 260 | if (prop.getString("name").equals("bungeeguard-token")) { 261 | token = prop.getString("value"); 262 | break; 263 | } 264 | } 265 | } 266 | 267 | if (!server.getConfig().getInfoForwarding().hasToken(token)) 268 | return false; 269 | 270 | setAddress(socketAddressHostname); 271 | gameProfile.setUuid(uuid); 272 | 273 | Logger.debug("Successfully verified BungeeGuard token"); 274 | 275 | return true; 276 | } 277 | 278 | int getVelocityLoginMessageId() { 279 | return velocityLoginMessageId; 280 | } 281 | 282 | void setVelocityLoginMessageId(int velocityLoginMessageId) { 283 | this.velocityLoginMessageId = velocityLoginMessageId; 284 | } 285 | 286 | boolean checkVelocityKeyIntegrity(ByteMessage buf) { 287 | byte[] signature = new byte[32]; 288 | buf.readBytes(signature); 289 | byte[] data = new byte[buf.readableBytes()]; 290 | buf.getBytes(buf.readerIndex(), data); 291 | try { 292 | Mac mac = Mac.getInstance("HmacSHA256"); 293 | mac.init(new SecretKeySpec(server.getConfig().getInfoForwarding().getSecretKey(), "HmacSHA256")); 294 | byte[] mySignature = mac.doFinal(data); 295 | if (!MessageDigest.isEqual(signature, mySignature)) 296 | return false; 297 | } catch (InvalidKeyException |java.security.NoSuchAlgorithmException e) { 298 | throw new AssertionError(e); 299 | } 300 | int version = buf.readVarInt(); 301 | if (version != 1) 302 | throw new IllegalStateException("Unsupported forwarding version " + version + ", wanted " + '\001'); 303 | return true; 304 | } 305 | } 306 | -------------------------------------------------------------------------------- /src/main/resources/LICENSE-netty: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | https://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | https://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /src/main/resources/LICENSE-configurate-yaml: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. -------------------------------------------------------------------------------- /src/main/java/ua/nanit/limbo/protocol/registry/State.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2020 Nan1t 3 | * 4 | * This program is free software: you can redistribute it and/or modify 5 | * it under the terms of the GNU General Public License as published by 6 | * the Free Software Foundation, either version 3 of the License, or 7 | * (at your option) any later version. 8 | * 9 | * This program is distributed in the hope that it will be useful, 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | * GNU General Public License for more details. 13 | * 14 | * You should have received a copy of the GNU General Public License 15 | * along with this program. If not, see <https://www.gnu.org/licenses/>. 16 | */ 17 | 18 | package ua.nanit.limbo.protocol.registry; 19 | 20 | import ua.nanit.limbo.protocol.Packet; 21 | import ua.nanit.limbo.protocol.packets.PacketHandshake; 22 | import ua.nanit.limbo.protocol.packets.login.*; 23 | import ua.nanit.limbo.protocol.packets.play.*; 24 | import ua.nanit.limbo.protocol.packets.status.PacketStatusPing; 25 | import ua.nanit.limbo.protocol.packets.status.PacketStatusRequest; 26 | import ua.nanit.limbo.protocol.packets.status.PacketStatusResponse; 27 | 28 | import java.util.*; 29 | import java.util.function.Supplier; 30 | 31 | import static ua.nanit.limbo.protocol.registry.Version.*; 32 | 33 | public enum State { 34 | 35 | HANDSHAKING(0) { 36 | { 37 | serverBound.register(PacketHandshake::new, 38 | map(0x00, Version.getMin(), Version.getMax()) 39 | ); 40 | } 41 | }, 42 | STATUS(1) { 43 | { 44 | serverBound.register(PacketStatusRequest::new, 45 | map(0x00, Version.getMin(), Version.getMax()) 46 | ); 47 | serverBound.register(PacketStatusPing::new, 48 | map(0x01, Version.getMin(), Version.getMax()) 49 | ); 50 | clientBound.register(PacketStatusResponse::new, 51 | map(0x00, Version.getMin(), Version.getMax()) 52 | ); 53 | clientBound.register(PacketStatusPing::new, 54 | map(0x01, Version.getMin(), Version.getMax()) 55 | ); 56 | } 57 | }, 58 | LOGIN(2) { 59 | { 60 | serverBound.register(PacketLoginStart::new, 61 | map(0x00, Version.getMin(), Version.getMax()) 62 | ); 63 | serverBound.register(PacketLoginPluginResponse::new, 64 | map(0x02, Version.getMin(), Version.getMax()) 65 | ); 66 | clientBound.register(PacketDisconnect::new, 67 | map(0x00, Version.getMin(), Version.getMax()) 68 | ); 69 | clientBound.register(PacketLoginSuccess::new, 70 | map(0x02, Version.getMin(), Version.getMax()) 71 | ); 72 | clientBound.register(PacketLoginPluginRequest::new, 73 | map(0x04, Version.getMin(), Version.getMax()) 74 | ); 75 | } 76 | }, 77 | PLAY(3) { 78 | { 79 | serverBound.register(PacketKeepAlive::new, 80 | map(0x00, V1_7_2, V1_8), 81 | map(0x0B, V1_9, V1_11_1), 82 | map(0x0C, V1_12, V1_12), 83 | map(0x0B, V1_12_1, V1_12_2), 84 | map(0x0E, V1_13, V1_13_2), 85 | map(0x0F, V1_14, V1_15_2), 86 | map(0x10, V1_16, V1_16_4), 87 | map(0x0F, V1_17, V1_18_2), 88 | map(0x11, V1_19, V1_19), 89 | map(0x12, V1_19_1, V1_19_1), 90 | map(0x11, V1_19_3, V1_19_3), 91 | map(0x12, V1_19_4, V1_19_4) 92 | ); 93 | serverBound.register(PacketChatMessage::new, 94 | map(0x05, V1_19, V1_19_3) 95 | ); 96 | 97 | clientBound.register(PacketDeclareCommands::new, 98 | map(0x11, V1_13, V1_14_4), 99 | map(0x12, V1_15, V1_15_2), 100 | map(0x11, V1_16, V1_16_1), 101 | map(0x10, V1_16_2, V1_16_4), 102 | map(0x12, V1_17, V1_18_2), 103 | map(0x0F, V1_19, V1_19_1), 104 | map(0x0E, V1_19_3, V1_19_3), 105 | map(0x10, V1_19_4, V1_19_4) 106 | ); 107 | clientBound.register(PacketJoinGame::new, 108 | map(0x01, V1_7_2, V1_8), 109 | map(0x23, V1_9, V1_12_2), 110 | map(0x25, V1_13, V1_14_4), 111 | map(0x26, V1_15, V1_15_2), 112 | map(0x25, V1_16, V1_16_1), 113 | map(0x24, V1_16_2, V1_16_4), 114 | map(0x26, V1_17, V1_18_2), 115 | map(0x23, V1_19, V1_19), 116 | map(0x25, V1_19_1, V1_19_1), 117 | map(0x24, V1_19_3, V1_19_3), 118 | map(0x28, V1_19_4, V1_19_4) 119 | ); 120 | clientBound.register(PacketPluginMessage::new, 121 | map(0x19, V1_13, V1_13_2), 122 | map(0x18, V1_14, V1_14_4), 123 | map(0x19, V1_15, V1_15_2), 124 | map(0x18, V1_16, V1_16_1), 125 | map(0x17, V1_16_2, V1_16_4), 126 | map(0x18, V1_17, V1_18_2), 127 | map(0x15, V1_19, V1_19), 128 | map(0x16, V1_19_1, V1_19_1), 129 | map(0x15, V1_19_3, V1_19_3), 130 | map(0x17, V1_19_4, V1_19_4) 131 | ); 132 | clientBound.register(PacketPlayerAbilities::new, 133 | map(0x39, V1_7_2, V1_8), 134 | map(0x2B, V1_9, V1_12), 135 | map(0x2C, V1_12_1, V1_12_2), 136 | map(0x2E, V1_13, V1_13_2), 137 | map(0x31, V1_14, V1_14_4), 138 | map(0x32, V1_15, V1_15_2), 139 | map(0x31, V1_16, V1_16_1), 140 | map(0x30, V1_16_2, V1_16_4), 141 | map(0x32, V1_17, V1_18_2), 142 | map(0x2F, V1_19, V1_19), 143 | map(0x31, V1_19_1, V1_19_1), 144 | map(0x30, V1_19_3, V1_19_3), 145 | map(0x34, V1_19_4, V1_19_4) 146 | ); 147 | clientBound.register(PacketPlayerPositionAndLook::new, 148 | map(0x08, V1_7_2, V1_8), 149 | map(0x2E, V1_9, V1_12), 150 | map(0x2F, V1_12_1, V1_12_2), 151 | map(0x32, V1_13, V1_13_2), 152 | map(0x35, V1_14, V1_14_4), 153 | map(0x36, V1_15, V1_15_2), 154 | map(0x35, V1_16, V1_16_1), 155 | map(0x34, V1_16_2, V1_16_4), 156 | map(0x38, V1_17, V1_18_2), 157 | map(0x36, V1_19, V1_19), 158 | map(0x39, V1_19_1, V1_19_1), 159 | map(0x38, V1_19_3, V1_19_3), 160 | map(0x3C, V1_19_4, V1_19_4) 161 | ); 162 | clientBound.register(PacketKeepAlive::new, 163 | map(0x00, V1_7_2, V1_8), 164 | map(0x1F, V1_9, V1_12_2), 165 | map(0x21, V1_13, V1_13_2), 166 | map(0x20, V1_14, V1_14_4), 167 | map(0x21, V1_15, V1_15_2), 168 | map(0x20, V1_16, V1_16_1), 169 | map(0x1F, V1_16_2, V1_16_4), 170 | map(0x21, V1_17, V1_18_2), 171 | map(0x1E, V1_19, V1_19), 172 | map(0x20, V1_19_1, V1_19_1), 173 | map(0x1F, V1_19_3, V1_19_3), 174 | map(0x23, V1_19_4, V1_19_4) 175 | ); 176 | clientBound.register(PacketSystemMessage::new, 177 | map(0x02, V1_7_2, V1_8), 178 | map(0x0F, V1_9, V1_12_2), 179 | map(0x0E, V1_13, V1_14_4), 180 | map(0x0F, V1_15, V1_15_2), 181 | map(0x0E, V1_16, V1_16_4), 182 | map(0x0F, V1_17, V1_18_2), 183 | map(0x5F, V1_19, V1_19), 184 | map(0x62, V1_19_1, V1_19_1), 185 | map(0x60, V1_19_3, V1_19_3), 186 | map(0x64, V1_19_4, V1_19_4) 187 | ); 188 | clientBound.register(PacketBossBar::new, 189 | map(0x0C, V1_9, V1_14_4), 190 | map(0x0D, V1_15, V1_15_2), 191 | map(0x0C, V1_16, V1_16_4), 192 | map(0x0D, V1_17, V1_18_2), 193 | map(0x0A, V1_19, V1_19_3), 194 | map(0x0B, V1_19_4, V1_19_4) 195 | ); 196 | clientBound.register(PacketPlayerInfo::new, 197 | map(0x38, V1_7_2, V1_8), 198 | map(0x2D, V1_9, V1_12), 199 | map(0x2E, V1_12_1, V1_12_2), 200 | map(0x30, V1_13, V1_13_2), 201 | map(0x33, V1_14, V1_14_4), 202 | map(0x34, V1_15, V1_15_2), 203 | map(0x33, V1_16, V1_16_1), 204 | map(0x32, V1_16_2, V1_16_4), 205 | map(0x36, V1_17, V1_18_2), 206 | map(0x34, V1_19, V1_19), 207 | map(0x37, V1_19_1, V1_19_1), 208 | map(0x36, V1_19_3, V1_19_3), 209 | map(0x3A, V1_19_4, V1_19_4) 210 | ); 211 | clientBound.register(PacketTitleLegacy::new, 212 | map(0x45, V1_8, V1_11_1), 213 | map(0x47, V1_12, V1_12), 214 | map(0x48, V1_12_1, V1_12_2), 215 | map(0x4B, V1_13, V1_13_2), 216 | map(0x4F, V1_14, V1_14_4), 217 | map(0x50, V1_15, V1_15_2), 218 | map(0x4F, V1_16, V1_16_4) 219 | ); 220 | clientBound.register(PacketTitleSetTitle::new, 221 | map(0x59, V1_17, V1_17_1), 222 | map(0x5A, V1_18, V1_19), 223 | map(0x5D, V1_19_1, V1_19_1), 224 | map(0x5B, V1_19_3, V1_19_3), 225 | map(0x5F, V1_19_4, V1_19_4) 226 | ); 227 | clientBound.register(PacketTitleSetSubTitle::new, 228 | map(0x57, V1_17, V1_17_1), 229 | map(0x58, V1_18, V1_19), 230 | map(0x5B, V1_19_1, V1_19_1), 231 | map(0x59, V1_19_3, V1_19_3), 232 | map(0x5D, V1_19_4, V1_19_4) 233 | ); 234 | clientBound.register(PacketTitleTimes::new, 235 | map(0x5A, V1_17, V1_17_1), 236 | map(0x5B, V1_18, V1_19), 237 | map(0x5E, V1_19_1, V1_19_1), 238 | map(0x5C, V1_19_3, V1_19_3), 239 | map(0x60, V1_19_4, V1_19_4) 240 | ); 241 | clientBound.register(PacketPlayerListHeader::new, 242 | map(0x47, V1_8, V1_8), 243 | map(0x48, V1_9, V1_9_2), 244 | map(0x47, V1_9_4, V1_11_1), 245 | map(0x49, V1_12, V1_12), 246 | map(0x4A, V1_12_1, V1_12_2), 247 | map(0x4E, V1_13, V1_13_2), 248 | map(0x53, V1_14, V1_14_4), 249 | map(0x54, V1_15, V1_15_2), 250 | map(0x53, V1_16, V1_16_4), 251 | map(0x5E, V1_17, V1_17_1), 252 | map(0x5F, V1_18, V1_18_2), 253 | map(0x60, V1_19, V1_19), 254 | map(0x63, V1_19_1, V1_19_1), 255 | map(0x61, V1_19_3, V1_19_3), 256 | map(0x65, V1_19_4, V1_19_4) 257 | ); 258 | clientBound.register(PacketSpawnPosition::new, 259 | map(0x4C, V1_19_3, V1_19_3), 260 | map(0x50, V1_19_4, V1_19_4) 261 | ); 262 | } 263 | }; 264 | 265 | private static final Map<Integer, State> STATE_BY_ID = new HashMap<>(); 266 | 267 | static { 268 | for (State registry : values()) { 269 | STATE_BY_ID.put(registry.stateId, registry); 270 | } 271 | } 272 | 273 | private final int stateId; 274 | public final ProtocolMappings serverBound = new ProtocolMappings(); 275 | public final ProtocolMappings clientBound = new ProtocolMappings(); 276 | 277 | State(int stateId) { 278 | this.stateId = stateId; 279 | } 280 | 281 | public static State getById(int stateId) { 282 | return STATE_BY_ID.get(stateId); 283 | } 284 | 285 | public static class ProtocolMappings { 286 | 287 | private final Map<Version, PacketRegistry> registry = new HashMap<>(); 288 | 289 | public PacketRegistry getRegistry(Version version) { 290 | return registry.getOrDefault(version, registry.get(getMin())); 291 | } 292 | 293 | public void register(Supplier<?> packet, Mapping... mappings) { 294 | for (Mapping mapping : mappings) { 295 | for (Version ver : getRange(mapping)) { 296 | PacketRegistry reg = registry.computeIfAbsent(ver, PacketRegistry::new); 297 | reg.register(mapping.packetId, packet); 298 | } 299 | } 300 | } 301 | 302 | private Collection<Version> getRange(Mapping mapping) { 303 | Version from = mapping.from; 304 | Version curr = mapping.to; 305 | 306 | if (curr == from) 307 | return Collections.singletonList(from); 308 | 309 | List<Version> versions = new LinkedList<>(); 310 | 311 | while (curr != from) { 312 | versions.add(curr); 313 | curr = curr.getPrev(); 314 | } 315 | 316 | versions.add(from); 317 | 318 | return versions; 319 | } 320 | 321 | } 322 | 323 | public static class PacketRegistry { 324 | 325 | private final Version version; 326 | private final Map<Integer, Supplier<?>> packetsById = new HashMap<>(); 327 | private final Map<Class<?>, Integer> packetIdByClass = new HashMap<>(); 328 | 329 | public PacketRegistry(Version version) { 330 | this.version = version; 331 | } 332 | 333 | public Version getVersion() { 334 | return version; 335 | } 336 | 337 | public Packet getPacket(int packetId) { 338 | Supplier<?> supplier = packetsById.get(packetId); 339 | return supplier == null ? null : (Packet) supplier.get(); 340 | } 341 | 342 | public int getPacketId(Class<?> packetClass) { 343 | return packetIdByClass.getOrDefault(packetClass, -1); 344 | } 345 | 346 | public void register(int packetId, Supplier<?> supplier) { 347 | packetsById.put(packetId, supplier); 348 | packetIdByClass.put(supplier.get().getClass(), packetId); 349 | } 350 | 351 | } 352 | 353 | private static class Mapping { 354 | 355 | private final int packetId; 356 | private final Version from; 357 | private final Version to; 358 | 359 | public Mapping(int packetId, Version from, Version to) { 360 | this.from = from; 361 | this.to = to; 362 | this.packetId = packetId; 363 | } 364 | } 365 | 366 | /** 367 | * Map packet id to version range 368 | * @param packetId Packet id 369 | * @param from Minimal version (include) 370 | * @param to Last version (include) 371 | * @return Created mapping 372 | */ 373 | private static Mapping map(int packetId, Version from, Version to) { 374 | return new Mapping(packetId, from, to); 375 | } 376 | 377 | } 378 | --------------------------------------------------------------------------------