package net.minestom.server.network.packet.client.login; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.format.NamedTextColor; import net.minestom.server.Configuration; import net.minestom.server.MinecraftServer; import net.minestom.server.crypto.PlayerPublicKey; import net.minestom.server.entity.Player; import net.minestom.server.extras.MojangAuth; import net.minestom.server.extras.bungee.BungeeCordProxy; import net.minestom.server.extras.velocity.VelocityProxy; import net.minestom.server.network.ConnectionState; import net.minestom.server.network.packet.client.ClientPreplayPacket; import net.minestom.server.network.packet.server.login.EncryptionRequestPacket; import net.minestom.server.network.packet.server.login.LoginDisconnectPacket; import net.minestom.server.network.packet.server.login.LoginPluginRequestPacket; import net.minestom.server.network.player.PlayerConnection; import net.minestom.server.network.player.PlayerSocketConnection; import net.minestom.server.utils.binary.BinaryReader; import net.minestom.server.utils.binary.BinaryWriter; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.UUID; import java.util.concurrent.ThreadLocalRandom; public record LoginStartPacket(@NotNull String username, @Nullable PlayerPublicKey publicKey) implements ClientPreplayPacket { private static final Component ALREADY_CONNECTED = Component.text("You are already on this server", NamedTextColor.RED); public LoginStartPacket(BinaryReader reader) { this(reader.readSizedString(16), reader.readBoolean() ? new PlayerPublicKey(reader) : null); } @Override public void process(@NotNull PlayerConnection connection) { connection.setPlayerPublicKey(publicKey); final Configuration config = MinecraftServer.getConfiguration(); if (config.requireValidPlayerPublicKey()) { if (publicKey != null) { if (!publicKey.isValid()) { connection.sendPacket(new LoginDisconnectPacket(config.invalidPlayerPublicKeyMessage())); connection.disconnect(); } } else { connection.sendPacket(new LoginDisconnectPacket(config.missingPlayerPublicKeyMessage())); connection.disconnect(); } } final boolean isSocketConnection = connection instanceof PlayerSocketConnection; // Proxy support (only for socket clients) and cache the login username if (isSocketConnection) { PlayerSocketConnection socketConnection = (PlayerSocketConnection) connection; socketConnection.UNSAFE_setLoginUsername(username); // Velocity support if (VelocityProxy.isEnabled()) { final int messageId = ThreadLocalRandom.current().nextInt(); final String channel = VelocityProxy.PLAYER_INFO_CHANNEL; // Important in order to retrieve the channel in the response packet socketConnection.addPluginRequestEntry(messageId, channel); connection.sendPacket(new LoginPluginRequestPacket(messageId, channel, null)); return; } } if (MojangAuth.isEnabled() && isSocketConnection) { // Mojang auth if (CONNECTION_MANAGER.getPlayer(username) != null) { connection.sendPacket(new LoginDisconnectPacket(ALREADY_CONNECTED)); connection.disconnect(); return; } final PlayerSocketConnection socketConnection = (PlayerSocketConnection) connection; socketConnection.setConnectionState(ConnectionState.LOGIN); final byte[] publicKey = MojangAuth.getKeyPair().getPublic().getEncoded(); byte[] nonce = new byte[4]; ThreadLocalRandom.current().nextBytes(nonce); socketConnection.setNonce(nonce); socketConnection.sendPacket(new EncryptionRequestPacket("", publicKey, nonce)); } else { final boolean bungee = BungeeCordProxy.isEnabled(); // Offline final UUID playerUuid = bungee && isSocketConnection ? ((PlayerSocketConnection) connection).getBungeeUuid() : CONNECTION_MANAGER.getPlayerConnectionUuid(connection, username); Player player = CONNECTION_MANAGER.startPlayState(connection, playerUuid, username, true); if (bungee && isSocketConnection) { player.setSkin(((PlayerSocketConnection) connection).getBungeeSkin()); } } } @Override public void write(@NotNull BinaryWriter writer) { if (username.length() > 16) throw new IllegalArgumentException("Username is not allowed to be longer than 16 characters"); writer.writeSizedString(username); writer.writeNullable(publicKey); } }