diff --git a/src/main/java/com/loohp/limbo/file/ServerProperties.java b/src/main/java/com/loohp/limbo/file/ServerProperties.java index 203f4f1..a2c2418 100644 --- a/src/main/java/com/loohp/limbo/file/ServerProperties.java +++ b/src/main/java/com/loohp/limbo/file/ServerProperties.java @@ -1,26 +1,21 @@ package com.loohp.limbo.file; -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStreamReader; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.nio.charset.StandardCharsets; -import java.util.Map.Entry; -import java.util.Optional; -import java.util.Properties; - -import javax.imageio.ImageIO; - +import com.google.common.collect.Lists; import com.loohp.limbo.Limbo; import com.loohp.limbo.location.Location; import com.loohp.limbo.utils.GameMode; import com.loohp.limbo.utils.NamespacedKey; import com.loohp.limbo.world.World; +import javax.imageio.ImageIO; +import java.awt.image.BufferedImage; +import java.io.*; +import java.nio.charset.StandardCharsets; +import java.util.List; +import java.util.Map.Entry; +import java.util.Optional; +import java.util.Properties; + public class ServerProperties { public static final String COMMENT = "For explaination of what each of the options does, please visit:\nhttps://github.com/LOOHP/Limbo/blob/master/src/main/resources/server.properties"; @@ -40,6 +35,9 @@ public class ServerProperties { private String versionString; private int protocol; private boolean bungeecord; + private boolean velocityModern; + private boolean bungeeGuard; + private List forwardingSecrets; private int viewDistance; private double ticksPerSecond; private boolean handshakeVerbose; @@ -91,6 +89,27 @@ public class ServerProperties { motdJson = prop.getProperty("motd"); versionString = prop.getProperty("version"); bungeecord = Boolean.parseBoolean(prop.getProperty("bungeecord")); + velocityModern = Boolean.parseBoolean(prop.getProperty("velocity-modern")); + bungeeGuard = Boolean.parseBoolean(prop.getProperty("bungee-guard")); + if (velocityModern || bungeeGuard) { + String forwardingSecretsStr = prop.getProperty("forwarding-secrets"); + if (forwardingSecretsStr == null || forwardingSecretsStr.equals("")) { + Limbo.getInstance().getConsole().sendMessage("Velocity Modern Forwarding or BungeeGuard is enabled but no forwarding-secret was found!"); + Limbo.getInstance().getConsole().sendMessage("Server will exit!"); + System.exit(1); + return; + } + this.forwardingSecrets = Lists.newArrayList(forwardingSecretsStr.split(";")); + if (bungeecord) { + Limbo.getInstance().getConsole().sendMessage("BungeeCord is enabled but so is Velocity Modern Forwarding or BungeeGuard, We will automatically disable BungeeCord forwarding because of this"); + bungeecord = false; + } + if (velocityModern && bungeeGuard) { + Limbo.getInstance().getConsole().sendMessage("Both Velocity Modern Forwarding and BungeeGuard are enabled! Because of this we always prefer Modern Forwarding, disabling BungeeGuard"); + bungeeGuard = false; + } + } + viewDistance = Integer.parseInt(prop.getProperty("view-distance")); ticksPerSecond = Double.parseDouble(prop.getProperty("ticks-per-second")); handshakeVerbose = Boolean.parseBoolean(prop.getProperty("handshake-verbose")); @@ -123,6 +142,18 @@ public class ServerProperties { return bungeecord; } + public boolean isVelocityModern() { + return velocityModern; + } + + public boolean isBungeeGuard() { + return bungeeGuard; + } + + public List getForwardingSecrets() { + return forwardingSecrets; + } + public Optional getFavicon() { return favicon; } diff --git a/src/main/java/com/loohp/limbo/server/ClientConnection.java b/src/main/java/com/loohp/limbo/server/ClientConnection.java index a80c373..d667cf2 100644 --- a/src/main/java/com/loohp/limbo/server/ClientConnection.java +++ b/src/main/java/com/loohp/limbo/server/ClientConnection.java @@ -1,81 +1,41 @@ package com.loohp.limbo.server; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonParser; +import com.loohp.limbo.Limbo; +import com.loohp.limbo.events.player.*; +import com.loohp.limbo.events.status.StatusPingEvent; +import com.loohp.limbo.file.ServerProperties; +import com.loohp.limbo.location.Location; +import com.loohp.limbo.player.Player; +import com.loohp.limbo.player.PlayerInteractManager; +import com.loohp.limbo.server.packets.*; +import com.loohp.limbo.server.packets.PacketPlayOutPlayerAbilities.PlayerAbilityFlags; +import com.loohp.limbo.server.packets.PacketPlayOutPlayerInfo.PlayerInfoAction; +import com.loohp.limbo.server.packets.PacketPlayOutPlayerInfo.PlayerInfoData; +import com.loohp.limbo.server.packets.PacketPlayOutPlayerInfo.PlayerInfoData.PlayerInfoDataAddPlayer.PlayerSkinProperty; +import com.loohp.limbo.server.packets.PacketPlayOutTabComplete.TabCompleteMatches; +import com.loohp.limbo.utils.*; +import com.loohp.limbo.utils.MojangAPIUtils.SkinResponse; +import com.loohp.limbo.world.BlockPosition; +import com.loohp.limbo.world.World; +import net.md_5.bungee.api.ChatColor; +import net.md_5.bungee.api.chat.BaseComponent; +import net.md_5.bungee.api.chat.TextComponent; +import net.md_5.bungee.chat.ComponentSerializer; + import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; import java.net.InetAddress; import java.net.Socket; import java.nio.charset.StandardCharsets; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; -import java.util.Optional; -import java.util.Set; -import java.util.UUID; +import java.util.*; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicLong; import java.util.stream.Collectors; -import com.loohp.limbo.Limbo; -import com.loohp.limbo.events.player.PlayerJoinEvent; -import com.loohp.limbo.events.player.PlayerLoginEvent; -import com.loohp.limbo.events.player.PlayerMoveEvent; -import com.loohp.limbo.events.player.PlayerQuitEvent; -import com.loohp.limbo.events.player.PlayerSelectedSlotChangeEvent; -import com.loohp.limbo.events.status.StatusPingEvent; -import com.loohp.limbo.file.ServerProperties; -import com.loohp.limbo.location.Location; -import com.loohp.limbo.player.Player; -import com.loohp.limbo.player.PlayerInteractManager; -import com.loohp.limbo.server.packets.Packet; -import com.loohp.limbo.server.packets.PacketHandshakingIn; -import com.loohp.limbo.server.packets.PacketLoginInLoginStart; -import com.loohp.limbo.server.packets.PacketLoginOutDisconnect; -import com.loohp.limbo.server.packets.PacketLoginOutLoginSuccess; -import com.loohp.limbo.server.packets.PacketOut; -import com.loohp.limbo.server.packets.PacketPlayInChat; -import com.loohp.limbo.server.packets.PacketPlayInHeldItemChange; -import com.loohp.limbo.server.packets.PacketPlayInKeepAlive; -import com.loohp.limbo.server.packets.PacketPlayInPosition; -import com.loohp.limbo.server.packets.PacketPlayInPositionAndLook; -import com.loohp.limbo.server.packets.PacketPlayInRotation; -import com.loohp.limbo.server.packets.PacketPlayInTabComplete; -import com.loohp.limbo.server.packets.PacketPlayOutDeclareCommands; -import com.loohp.limbo.server.packets.PacketPlayOutDisconnect; -import com.loohp.limbo.server.packets.PacketPlayOutEntityMetadata; -import com.loohp.limbo.server.packets.PacketPlayOutGameState; -import com.loohp.limbo.server.packets.PacketPlayOutHeldItemChange; -import com.loohp.limbo.server.packets.PacketPlayOutLogin; -import com.loohp.limbo.server.packets.PacketPlayOutPlayerAbilities; -import com.loohp.limbo.server.packets.PacketPlayOutPlayerAbilities.PlayerAbilityFlags; -import com.loohp.limbo.server.packets.PacketPlayOutPlayerInfo; -import com.loohp.limbo.server.packets.PacketPlayOutPlayerInfo.PlayerInfoAction; -import com.loohp.limbo.server.packets.PacketPlayOutPlayerInfo.PlayerInfoData; -import com.loohp.limbo.server.packets.PacketPlayOutPlayerInfo.PlayerInfoData.PlayerInfoDataAddPlayer.PlayerSkinProperty; -import com.loohp.limbo.server.packets.PacketPlayOutPositionAndLook; -import com.loohp.limbo.server.packets.PacketPlayOutSpawnPosition; -import com.loohp.limbo.server.packets.PacketPlayOutTabComplete; -import com.loohp.limbo.server.packets.PacketPlayOutTabComplete.TabCompleteMatches; -import com.loohp.limbo.server.packets.PacketPlayOutUpdateViewPosition; -import com.loohp.limbo.server.packets.PacketStatusInPing; -import com.loohp.limbo.server.packets.PacketStatusInRequest; -import com.loohp.limbo.server.packets.PacketStatusOutPong; -import com.loohp.limbo.server.packets.PacketStatusOutResponse; -import com.loohp.limbo.utils.CustomStringUtils; -import com.loohp.limbo.utils.DataTypeIO; -import com.loohp.limbo.utils.DeclareCommands; -import com.loohp.limbo.utils.GameMode; -import com.loohp.limbo.utils.MojangAPIUtils; -import com.loohp.limbo.utils.MojangAPIUtils.SkinResponse; -import com.loohp.limbo.utils.NamespacedKey; -import com.loohp.limbo.world.BlockPosition; -import com.loohp.limbo.world.World; - -import net.md_5.bungee.api.ChatColor; -import net.md_5.bungee.api.chat.BaseComponent; -import net.md_5.bungee.api.chat.TextComponent; -import net.md_5.bungee.chat.ComponentSerializer; - public class ClientConnection extends Thread { public static enum ClientState { @@ -87,6 +47,9 @@ public class ClientConnection extends Thread { DISCONNECTED; } + private final Random random = new Random(); + private final JsonParser jsonParser = new JsonParser(); + private final Socket client_socket; private boolean running; private ClientState state; @@ -201,10 +164,12 @@ public class ClientConnection extends Thread { PacketHandshakingIn handshake = new PacketHandshakingIn(input); - boolean isBungeecord = Limbo.getInstance().getServerProperties().isBungeecord(); + boolean isBungeecord = Limbo.getInstance().getServerProperties().isBungeecord(); + boolean isBungeeGuard = Limbo.getInstance().getServerProperties().isBungeeGuard(); + boolean isVelocityModern = Limbo.getInstance().getServerProperties().isVelocityModern(); String bungeeForwarding = handshake.getServerAddress(); UUID bungeeUUID = null; - SkinResponse bungeeSkin = null; + SkinResponse forwardedSkin = null; try { switch (handshake.getHandshakeType()) { @@ -236,7 +201,7 @@ public class ClientConnection extends Thread { case LOGIN: state = ClientState.LOGIN; - if (isBungeecord) { + if (isBungeecord || isBungeeGuard) { try { String[] data = bungeeForwarding.split("\\x00"); //String host = data[0]; @@ -244,20 +209,34 @@ public class ClientConnection extends Thread { bungeeUUID = UUID.fromString(data[2].replaceFirst("([0-9a-fA-F]{8})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]{4})([0-9a-fA-F]+)", "$1-$2-$3-$4-$5")); inetAddress = InetAddress.getByName(ip); - + + boolean bungeeGuardFound = false; if (data.length > 3) { - String skinJson = data[3]; - - String skin = skinJson.split("\"value\":\"")[1].split("\"")[0]; - String signature = skinJson.split("\"signature\":\"")[1].split("\"")[0]; - bungeeSkin = new SkinResponse(skin, signature); + JsonArray skinJson = this.jsonParser.parse(data[3]).getAsJsonArray(); + + for (JsonElement property : skinJson) { + if (property.getAsJsonObject().get("name").getAsString().equals("textures")) { + String skin = property.getAsJsonObject().get("value").getAsString(); + String signature = property.getAsJsonObject().get("signature").getAsString(); + forwardedSkin = new SkinResponse(skin, signature); + } else if (isBungeeGuard && property.getAsJsonObject().get("name").getAsString().equals("bungeeguard-token")) { + String token = property.getAsJsonObject().get("value").getAsString(); + bungeeGuardFound = Limbo.getInstance().getServerProperties().getForwardingSecrets().contains(token); + } + } } + + if (isBungeeGuard && !bungeeGuardFound) { + disconnectDuringLogin(TextComponent.fromLegacyText("Invalid information forwarding")); + break; + } } catch (Exception e) { Limbo.getInstance().getConsole().sendMessage("If you wish to use bungeecord's IP forwarding, please enable that in your bungeecord config.yml as well!"); disconnectDuringLogin(new BaseComponent[] {new TextComponent(ChatColor.RED + "Please connect from the proxy!")}); } } - + + int messageId = this.random.nextInt(); while (client_socket.isConnected()) { int size = DataTypeIO.readVarInt(input); int packetId = DataTypeIO.readVarInt(input); @@ -268,7 +247,14 @@ public class ClientConnection extends Thread { } else if (packetType.equals(PacketLoginInLoginStart.class)) { PacketLoginInLoginStart start = new PacketLoginInLoginStart(input); String username = start.getUsername(); - UUID uuid = isBungeecord ? bungeeUUID : UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(StandardCharsets.UTF_8)); + + if (Limbo.getInstance().getServerProperties().isVelocityModern()) { + PacketLoginOutPluginMessaging loginPluginRequest = new PacketLoginOutPluginMessaging(messageId, ForwardingUtils.VELOCITY_FORWARDING_CHANNEL); + sendPacket(loginPluginRequest); + continue; + } + + UUID uuid = isBungeecord || isBungeeGuard ? bungeeUUID : UUID.nameUUIDFromBytes(("OfflinePlayer:" + username).getBytes(StandardCharsets.UTF_8)); PacketLoginOutLoginSuccess success = new PacketLoginOutLoginSuccess(uuid, username); sendPacket(success); @@ -279,6 +265,34 @@ public class ClientConnection extends Thread { player.setSkinLayers((byte) (0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40)); Limbo.getInstance().addPlayer(player); + break; + } else if (packetType.equals(PacketLoginInPluginMessaging.class)) { + PacketLoginInPluginMessaging response = new PacketLoginInPluginMessaging(input, size, packetId); + if (response.getMessageId() != messageId) { + disconnectDuringLogin(TextComponent.fromLegacyText("Internal error, messageId did not match")); + break; + } + if (response.getData() == null) { + disconnectDuringLogin(TextComponent.fromLegacyText("Unknown login plugin response packet!")); + break; + } + if (!ForwardingUtils.validateVelocityModernResponse(response.getData())) { + disconnectDuringLogin(TextComponent.fromLegacyText("Invalid playerinfo forwarding!")); + break; + } + ForwardingUtils.VelocityModernForwardingData data = ForwardingUtils.getVelocityDataFrom(response.getData()); + inetAddress = InetAddress.getByName(data.getIpAddress()); + forwardedSkin = data.getSkinResponse(); + + PacketLoginOutLoginSuccess success = new PacketLoginOutLoginSuccess(data.getUuid(), data.getUsername()); + sendPacket(success); + + state = ClientState.PLAY; + + player = new Player(this, data.getUsername(), data.getUuid(), Limbo.getInstance().getNextEntityId(), Limbo.getInstance().getServerProperties().getWorldSpawn(), new PlayerInteractManager()); + player.setSkinLayers((byte) (0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 | 0x40)); + Limbo.getInstance().addPlayer(player); + break; } else { input.skipBytes(size - DataTypeIO.getVarIntLength(packetId)); @@ -293,6 +307,7 @@ public class ClientConnection extends Thread { break; } } catch (Exception e) { + e.printStackTrace(); client_socket.close(); state = ClientState.DISCONNECTED; } @@ -312,7 +327,7 @@ public class ClientConnection extends Thread { sendPacket(join); Limbo.getInstance().getUnsafe().setPlayerGameModeSilently(player, properties.getDefaultGamemode()); - SkinResponse skinresponce = isBungeecord && bungeeSkin != null ? bungeeSkin : MojangAPIUtils.getSkinFromMojangServer(player.getName()); + SkinResponse skinresponce = (isVelocityModern || isBungeeGuard || isBungeecord) && forwardedSkin != null ? forwardedSkin : MojangAPIUtils.getSkinFromMojangServer(player.getName()); PlayerSkinProperty skin = skinresponce != null ? new PlayerSkinProperty(skinresponce.getSkin(), skinresponce.getSignature()) : null; PacketPlayOutPlayerInfo info = new PacketPlayOutPlayerInfo(PlayerInfoAction.ADD_PLAYER, player.getUniqueId(), new PlayerInfoData.PlayerInfoDataAddPlayer(player.getName(), Optional.ofNullable(skin), properties.getDefaultGamemode(), 0, false, Optional.empty())); sendPacket(info); @@ -327,7 +342,7 @@ public class ClientConnection extends Thread { PacketPlayOutPlayerAbilities abilities = new PacketPlayOutPlayerAbilities(0.05F, 0.1F, flags.toArray(new PlayerAbilityFlags[flags.size()])); sendPacket(abilities); - String str = inetAddress.getHostName() + ":" + client_socket.getPort() + "|" + player.getName(); + String str = inetAddress.getHostName() + ":" + client_socket.getPort() + "|" + player.getName() + "(" + player.getUniqueId() + ")"; Limbo.getInstance().getConsole().sendMessage("[/" + str + "] <-> Player had connected to the Limbo server!"); PacketPlayOutDeclareCommands declare = DeclareCommands.getDeclareCommandsPacket(player); diff --git a/src/main/java/com/loohp/limbo/server/packets/PacketLoginInPluginMessaging.java b/src/main/java/com/loohp/limbo/server/packets/PacketLoginInPluginMessaging.java index 5e6e166..d0a1582 100644 --- a/src/main/java/com/loohp/limbo/server/packets/PacketLoginInPluginMessaging.java +++ b/src/main/java/com/loohp/limbo/server/packets/PacketLoginInPluginMessaging.java @@ -1,39 +1,39 @@ package com.loohp.limbo.server.packets; +import com.loohp.limbo.utils.DataTypeIO; + import java.io.DataInputStream; import java.io.IOException; -import java.nio.charset.StandardCharsets; - -import com.loohp.limbo.utils.DataTypeIO; -import com.loohp.limbo.utils.NamespacedKey; public class PacketLoginInPluginMessaging extends PacketIn { private int messageId; - private NamespacedKey channel; - private byte[] data; + private boolean successful; + private byte[] data = null; - public PacketLoginInPluginMessaging(int messageId, NamespacedKey channel, byte[] data) { + public PacketLoginInPluginMessaging(int messageId, boolean successful, byte[] data) { this.messageId = messageId; - this.channel = channel; this.data = data; } public PacketLoginInPluginMessaging(DataInputStream in, int packetLength, int packetId) throws IOException { messageId = DataTypeIO.readVarInt(in); - String rawChannel = DataTypeIO.readString(in, StandardCharsets.UTF_8); - channel = new NamespacedKey(rawChannel); - int dataLength = packetLength - DataTypeIO.getVarIntLength(packetId) - DataTypeIO.getVarIntLength(messageId) - DataTypeIO.getStringLength(rawChannel, StandardCharsets.UTF_8); - data = new byte[dataLength]; - in.read(data); + successful = in.readBoolean(); + if (successful) { + int dataLength = packetLength - DataTypeIO.getVarIntLength(packetId) - DataTypeIO.getVarIntLength(messageId) - 1; + if (dataLength != 0) { + data = new byte[dataLength]; + in.readFully(data); + } + } } public int getMessageId() { return messageId; } - public NamespacedKey getChannel() { - return channel; + public boolean isSuccessful() { + return successful; } public byte[] getData() { diff --git a/src/main/java/com/loohp/limbo/server/packets/PacketLoginOutPluginMessaging.java b/src/main/java/com/loohp/limbo/server/packets/PacketLoginOutPluginMessaging.java index a5a890a..bcd0ec3 100644 --- a/src/main/java/com/loohp/limbo/server/packets/PacketLoginOutPluginMessaging.java +++ b/src/main/java/com/loohp/limbo/server/packets/PacketLoginOutPluginMessaging.java @@ -1,19 +1,23 @@ package com.loohp.limbo.server.packets; +import com.loohp.limbo.utils.DataTypeIO; +import com.loohp.limbo.utils.NamespacedKey; + import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; -import com.loohp.limbo.utils.DataTypeIO; -import com.loohp.limbo.utils.NamespacedKey; - public class PacketLoginOutPluginMessaging extends PacketOut { private int messageId; private NamespacedKey channel; private byte[] data; + public PacketLoginOutPluginMessaging(int messageId, NamespacedKey channel) { + this(messageId, channel, null); + } + public PacketLoginOutPluginMessaging(int messageId, NamespacedKey channel, byte[] data) { this.messageId = messageId; this.channel = channel; @@ -40,7 +44,9 @@ public class PacketLoginOutPluginMessaging extends PacketOut { output.writeByte(Packet.getLoginOut().get(getClass())); DataTypeIO.writeVarInt(output, messageId); DataTypeIO.writeString(output, channel.toString(), StandardCharsets.UTF_8); - output.write(data); + if (data != null) { + output.write(data); + } return buffer.toByteArray(); } diff --git a/src/main/java/com/loohp/limbo/utils/DataTypeIO.java b/src/main/java/com/loohp/limbo/utils/DataTypeIO.java index dbf376f..d9f7331 100644 --- a/src/main/java/com/loohp/limbo/utils/DataTypeIO.java +++ b/src/main/java/com/loohp/limbo/utils/DataTypeIO.java @@ -23,6 +23,10 @@ public class DataTypeIO { out.writeLong(uuid.getMostSignificantBits()); out.writeLong(uuid.getLeastSignificantBits()); } + + public static UUID readUUID(DataInputStream in) throws IOException { + return new UUID(in.readLong(), in.readLong()); + } public static void writeCompoundTag(DataOutputStream out, CompoundTag tag) throws IOException { ByteArrayOutputStream buffer = new ByteArrayOutputStream(); diff --git a/src/main/java/com/loohp/limbo/utils/ForwardingUtils.java b/src/main/java/com/loohp/limbo/utils/ForwardingUtils.java new file mode 100644 index 0000000..192a6c7 --- /dev/null +++ b/src/main/java/com/loohp/limbo/utils/ForwardingUtils.java @@ -0,0 +1,127 @@ +package com.loohp.limbo.utils; + +import com.loohp.limbo.Limbo; + +import javax.crypto.Mac; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; +import java.io.ByteArrayInputStream; +import java.io.DataInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.UUID; + +public class ForwardingUtils { + + public static final NamespacedKey VELOCITY_FORWARDING_CHANNEL = new NamespacedKey("velocity", "player_info"); + + public static boolean validateVelocityModernResponse(byte[] data) throws IOException { + ByteArrayInputStream byteIn = new ByteArrayInputStream(data); + DataInputStream input = new DataInputStream(byteIn); + + byte[] signature = new byte[32]; + input.readFully(signature); + + boolean foundValid = false; + try { + Mac mac = Mac.getInstance("HmacSHA256"); + for (String secret : Limbo.getInstance().getServerProperties().getForwardingSecrets()) { + SecretKey key = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"); + mac.init(key); + mac.update(data, 32, data.length - 32); + byte[] sig = mac.doFinal(); + if (Arrays.equals(signature, sig)) { + foundValid = true; + break; + } + } + } catch (InvalidKeyException e) { + throw new RuntimeException("Unable to authenticate data", e); + } catch (NoSuchAlgorithmException e) { + // Should never happen + throw new AssertionError(e); + } + + return foundValid; + } + + public static VelocityModernForwardingData getVelocityDataFrom(byte[] data) throws IOException { + ByteArrayInputStream byteIn = new ByteArrayInputStream(data); + DataInputStream input = new DataInputStream(byteIn); + + input.skipBytes(32); + + int velocityVersion = DataTypeIO.readVarInt(input); + if (velocityVersion != 1) { + System.out.println("Unsupported Velocity version! Stopping Execution"); + throw new AssertionError("Unknown Velocity Packet"); + } + String address = DataTypeIO.readString(input, StandardCharsets.UTF_8); + UUID uuid = DataTypeIO.readUUID(input); + String username = DataTypeIO.readString(input, StandardCharsets.UTF_8); + + //cycle properties + MojangAPIUtils.SkinResponse response = null; + int count = DataTypeIO.readVarInt(input); + for (int i = 0; i < count; ++i) { + String propertyName = DataTypeIO.readString(input, StandardCharsets.UTF_8); + String propertyValue = DataTypeIO.readString(input, StandardCharsets.UTF_8); + String propertySignature = ""; + boolean signatureIncluded = input.readBoolean(); + if (signatureIncluded) { + propertySignature = DataTypeIO.readString(input, StandardCharsets.UTF_8); + } + if (propertyName.equals("textures")) { + response = new MojangAPIUtils.SkinResponse(propertyValue, propertySignature); + break; // we don't use others properties for now + } + } + + return new VelocityModernForwardingData(velocityVersion, address, uuid, username, response); + } + + public static class VelocityModernForwardingData { + + private final int version; + private final String ipAddress; + private final UUID uuid; + private final String username; + private final MojangAPIUtils.SkinResponse skinResponse; + + public VelocityModernForwardingData(int version, String ipAddress, UUID uuid, String username, MojangAPIUtils.SkinResponse skinResponse) { + this.version = version; + this.ipAddress = ipAddress; + this.uuid = uuid; + this.username = username; + this.skinResponse = skinResponse; + } + + public int getVersion() + { + return version; + } + + public String getIpAddress() + { + return ipAddress; + } + + public UUID getUuid() + { + return uuid; + } + + public String getUsername() + { + return username; + } + + public MojangAPIUtils.SkinResponse getSkinResponse() + { + return skinResponse; + } + } +} diff --git a/src/main/resources/mapping.json b/src/main/resources/mapping.json index 0e1e2a9..76341ff 100644 --- a/src/main/resources/mapping.json +++ b/src/main/resources/mapping.json @@ -9,7 +9,7 @@ "LoginOut": { "PacketLoginOutLoginSuccess": "0x02", "PacketLoginOutDisconnect": "0x00", - "PacketLoginOutPluginMessaging": "0x04", + "PacketLoginOutPluginMessaging": "0x04" }, "PlayIn": { "0x0F": "PacketPlayInKeepAlive", diff --git a/src/main/resources/server.properties b/src/main/resources/server.properties index 6211af4..cc712b1 100644 --- a/src/main/resources/server.properties +++ b/src/main/resources/server.properties @@ -7,9 +7,21 @@ server-port=30000 #Server ip, localhost for local access only server-ip=0.0.0.0 -#Whether this is server is behind a bungeecord proxy +#Whether this server is behind a bungeecord proxy +#Mutually exclusive with velocity-modern and bungee-guard bungeecord=false +#Whether this server is behind a velocity proxy with modern player forwarding +#Mutually exclusive with bungeecord and bungee-guard +velocity-modern=false + +#Whether this server is behind a bungeecord proxy with BungeeGuard installed (velocity can do this too for <1.13) +#Mutually exclusive with bungeecord and velocity-modern +bungee-guard=false + +#For Velocity Modern Forwarding or BungeeGuard a list (separated by `;`) of valid secrets +forwarding-secrets= + #World Name and the Schematic file containing map level-name=world;spawn.schem