Minecraft server returns error after sending LoginStart packet

58 views Asked by At

I'm trying to send a LoginStart packet, but the server responds with an error.

The entire server response: �{"translate":"disconnect.genericReason","with":["Internal Exception: io.netty.handler.codec.DecoderException: java.lang.IndexOutOfBoundsException: readerIndex(27) + length(8) exceeds writerIndex(34): PooledUnsafeDirectByteBuf(ridx: 27, widx: 34, cap: 34)"]}

MinecraftPlayer class that starting connection:

public class MinecraftPlayer {
    public Socket sock;

    public DataOutputStream output;
    public DataInputStream input;

    public MinecraftServer server;

    public UUID uuid;
    public String name;

    public MinecraftPlayer(MinecraftServer server, UUID uuid, String name) {
        this.server = server;
        this.uuid = uuid;
        this.name = name;
    }

    public void startConnection() {
        new Thread(() -> {
            try {
                this.sock = new Socket();

                sock.connect(server.host);

                this.output = new DataOutputStream(sock.getOutputStream());
                this.input = new DataInputStream(sock.getInputStream());

                // Отправляем handshake
                sendHandshake();

                // Отправляем пакет login start
                sendLoginStartPacket();

                // Читаем ответ от сервера
                if (readLoginSuccessPacket()) {
                    System.out.println("Login Successful!");
                } else {
                    System.out.println("Login Failed!");
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }).start();
    }

    private void sendHandshake() throws IOException {
//        System.out.println("send handshake");

        OutputPacketContainer packet = new OutputPacketContainer((byte) 0x00);

        packet.writeVarInt(server.protocol_version); // Protocol version
        packet.writeString(server.host.getHostString()); // Server address
        packet.writeShort((short) server.host.getPort()); // Server port as// unsigned short
        packet.writeVarInt(2); // Next state (2 for login)

        packet.sendPacket(this);
    }

    private void sendLoginStartPacket() throws IOException {
//        System.out.println("send login start");

        OutputPacketContainer packet = new OutputPacketContainer((byte) 0x00);

        packet.writeString(name);
        packet.writeUUID(uuid);

        packet.sendPacket(this);
    }

    private boolean readLoginSuccessPacket() throws IOException {
        int length = readVarInt(input);
        int packetId = readVarInt(input);

        if (packetId == 0x02) { // Packet ID for login success
            UUID uuid = new UUID(input.readLong(), input.readLong()); // UUID is received
            String username = readString(input); // Username is received as String
            System.out.println("UUID: " + uuid + ", Username: " + username);
            return true;
        } else if (packetId == 0x00) {
            Main.logInputPacket(length, packetId, input.readAllBytes());
//            System.out.println((String)((List<Object>)((Map<String,Object>)JSON.load(readString(input))).get("with")).get(0));
        }
        return false;
    }

    private int readVarInt(DataInputStream in) throws IOException {
        int value = 0;
        int bitOffset = 0;
        byte b;
        do {
            b = in.readByte();
            value |= (b & 127) << bitOffset;
            bitOffset += 7;
        } while ((b & 128) == 128);
        return value;
    }

    private String readString(DataInputStream in) throws IOException {
        int length = readVarInt(in); // Length of string
        byte[] bytes = new byte[length];
        in.readFully(bytes); // Read string bytes
        return new String(bytes, StandardCharsets.UTF_8); // Create string
    }
}

OutputPacketContainer class that creates packet:

public class OutputPacketContainer {
    private ByteArrayOutputStream buffer;
    private DataOutputStream output;
    private byte packet_id;

    public OutputPacketContainer(byte packet_id) {
        this.packet_id = packet_id;

        buffer = new ByteArrayOutputStream();
        output = new DataOutputStream(buffer);

        writeVarInt(packet_id);
    }

    public ByteArrayOutputStream getBuffer() {
        return buffer;
    }

    public DataOutputStream getOutput() {
        return output;
    }

    public byte getPacketId() {
        return packet_id;
    }

    public void writeByte(byte v) {
        try {
            output.writeByte(v);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void writeShort(short v) {
        try {
            output.writeShort(v);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void writeString(String v, Charset charset) {
        byte[] bytes = v.getBytes(charset);
        writeVarInt(bytes.length);
        writeBytes(bytes);
    }

    public void writeString(String v) {
        writeString(v,StandardCharsets.UTF_8);
    }

    public void writeVarInt(int v) {
        writeVarInt(output,v);
    }

    public void writeVarLong(long v) {
        writeVarLong(output,v);
    }

    public void writeBytes(byte[] v) {
        try {
            output.write(v);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void writeUUID(UUID v) {
        try {
            output.writeLong(v.getMostSignificantBits());
            output.writeLong(v.getLeastSignificantBits());
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void sendPacket(MinecraftPlayer player) {
        byte[] message = buffer.toByteArray();
        try {
            writeVarInt(player.output, message.length);
            player.output.write(message);
            player.output.flush();

            Main.logOutputPacket(message.length, packet_id, message);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static final int SEGMENT_BITS = 0x7F;
    private static final int CONTINUE_BIT = 0x80;

    private static void writeVarInt(DataOutputStream output, int v) {
        try {
            while (true) {
                if ((v & ~SEGMENT_BITS) == 0) {
                    output.writeByte(v);
                    return;
                }

                output.writeByte((v & SEGMENT_BITS) | CONTINUE_BIT);

                // Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone
                v >>>= 7;
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static void writeVarLong(DataOutputStream output, long v) {
        try {
            while (true) {
                if ((v & ~((long) SEGMENT_BITS)) == 0) {
                    output.writeByte((int) v);
                    return;
                }

                output.writeByte((int) ((v & SEGMENT_BITS) | CONTINUE_BIT));

                // Note: >>> means that the sign bit is shifted with the rest of the number rather than being left alone
                v >>>= 7;
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}

I think the problem is OutputPacketContainer#writeUUID, but couldn’t find a solution. Wrote code based on wiki.vg

0

There are 0 answers