Minecraft 协议超过 WriterIndex

问题描述 投票:0回答:1

我正在尝试使用 C# 连接到 Minecraft Java 服务器。服务器处于离线模式。

首先,我发送 Handshake 数据包并且它有效,但是当我发送 LoginStart 时,我收到此错误:

{"translate":"disconnect.genericReason","with":["Internal Exception: io.netty.handler.codec.DecoderException: java.lang.IndexOutOfBoundsException: readerIndex(15) + length(8) exceeds writerIndex(22): PooledUnsafeDirectByteBuf(ridx: 15, widx: 22, cap: 22)"]}

我正在跟踪发送到服务器(托管在我的计算机上)的所有数据包,并且长度与我的数据包中写入的长度相同。 (数据包格式)

代码

向服务器发送

Packet
的功能

public void Send(Packet packet) {
    if (this.state.Equals(packet.state) && Side.Server.Equals(packet.side)) stream.Write(packet.GetBytes());
    else if (Side.Server.Equals(packet.side)) Debug.LogError($"Packet {packet.state}:0x{packet.id:x2} is sent when in an invalid state ({packet.state} when {this.state})");
    else Debug.LogError($"Packet {packet.state}:0x{packet.id:x2} is sent to server when should be recieved");
}

这是发送

LoginStart
...Packet

public class ServerLoginStartPacket : ServerPacket {
    public ServerLoginStartPacket(string? username, UUID? uuid = null) {
        id = 0x00;
        state = State.Login;

        this.WriteString(username ?? "Test");
        this.WriteUUID(uuid ?? UUID.Random());
    }
}

...继承自默认值

Packet

public class Packet {
    public int id;
    public Side side;
    public State state;
    MemoryStream buffer = new();

    readonly object bufferLock = new();

    public Span<byte> GetBytes() {
        lock (bufferLock) {
            byte[] data = buffer.ToArray();

            byte[] temp = buffer.ToArray();
            this.buffer = new();

            this.WriteVarInt(this.id);
            buffer.Write(temp);

            temp = buffer.ToArray();
            this.buffer = new();

            this.WriteVarInt((int)temp.Length);
            buffer.Write(temp);

            temp = buffer.ToArray();
            this.buffer = new();

            this.buffer.Write(data);

            return temp.AsSpan();
        }
    }

    public void WriteBool(bool value) => this.WriteByte((byte)(value ? 1 : 0));

    public void WriteByte(byte b) {
        lock (bufferLock) this.buffer.WriteByte(b);
    }

    public void WriteSByte(sbyte b) => this.WriteByte((byte)b);

    public void WriteBytes(Span<byte> bytes) {
        lock (bufferLock) this.buffer.Write(bytes);
    }

    public void WriteULong(ulong value) {
        if (BitConverter.IsLittleEndian) {
            this.WriteByte((byte)(value >> 56 & 0xFF));
            this.WriteByte((byte)(value >> 48 & 0xFF));
            this.WriteByte((byte)(value >> 40 & 0xFF));
            this.WriteByte((byte)(value >> 32 & 0xFF));
            this.WriteByte((byte)(value >> 24 & 0xFF));
            this.WriteByte((byte)(value >> 16 & 0xFF));
            this.WriteByte((byte)(value >> 8 & 0xFF));
            this.WriteByte((byte)(value & 0xFF));
        } else {
            this.WriteByte((byte)(value & 0xFF));
            this.WriteByte((byte)(value >> 8 & 0xFF));
            this.WriteByte((byte)(value >> 16 & 0xFF));
            this.WriteByte((byte)(value >> 24 & 0xFF));
            this.WriteByte((byte)(value >> 32 & 0xFF));
            this.WriteByte((byte)(value >> 40 & 0xFF));
            this.WriteByte((byte)(value >> 48 & 0xFF));
            this.WriteByte((byte)(value >> 56 & 0xFF));
        }
    }

    public void WriteLong(long value) => this.WriteULong((ulong)value);

    public void WriteUShort(ushort value) {
        if (BitConverter.IsLittleEndian) {
            this.WriteByte((byte)(value >> 8 & 0xFF));
            this.WriteByte((byte)(value & 0xFF));
        } else {
            this.WriteByte((byte)(value & 0xFF));
            this.WriteByte((byte)(value >> 8 & 0xFF));
        }
    }

    public void WriteShort(short value) => this.WriteUShort((ushort)value);

    public void WriteString(string value, Encoding encoding = null) {
        encoding ??= Encoding.UTF8;

        Span<byte> bytes = stackalloc byte[encoding.GetByteCount(value)];
        int length = encoding.GetBytes(value, bytes);

        this.WriteVarInt(length);
        this.WriteBytes(bytes);
    }

    public void WriteUUID(UUID value) {
        this.WriteLong(value.MostSignificantBits);
        this.WriteLong(value.LeastSignificantBits);
    }

    public void WriteVarInt(int value) {
        while (true) {
            if ((value & ~0x7F) == 0) {
                this.WriteByte((byte)value);
                break;
            } else {
                this.WriteByte((byte)(value & 0x7F | 0x80));
                value >>= 7;
            }
        }
    }
}
c# tcp protocols minecraft
1个回答
0
投票

好吧,错误是因为UUID而发生的。

一开始就缺少一个字节,我遇到了这个问题,因为该字节没有写在文档中......

如果以后有人需要的话,还有最终的功能。

public void WriteUUID(Guid value) {
    byte[] guidBytes = value.ToByteArray();

    byte[] uuidBytes = {
        guidBytes[6],
        guidBytes[7],
        guidBytes[4],
        guidBytes[5],
        guidBytes[0],
        guidBytes[1],
        guidBytes[2],
        guidBytes[3],
        guidBytes[15],
        guidBytes[14],
        guidBytes[13],
        guidBytes[12],
        guidBytes[11],
        guidBytes[10],
        guidBytes[9],
        guidBytes[8]
    };

    this.WriteByte(0x01); // I don't know why it doesn't work without adding 0x01
    this.WriteLong(BitConverter.ToInt64(uuidBytes, 0));
    this.WriteLong(BitConverter.ToInt64(uuidBytes, 8));
}
© www.soinside.com 2019 - 2024. All rights reserved.