我正在尝试使用 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;
}
}
}
}
好吧,错误是因为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));
}