在 Golang 中无法从 Minecraft 正确读取数据包

Posted

技术标签:

【中文标题】在 Golang 中无法从 Minecraft 正确读取数据包【英文标题】:Cannot read packet from Minecraft correctly in Golang 【发布时间】:2022-01-15 14:42:45 【问题描述】:

我是 Golang 的初学者。最近我在从 Minecraft 客户端读取数据包时遇到问题。

我的程序就是这样从连接中读取数据包的。

    player := &Player
        conn:     conn,
        state:    HANDSHAKING,
        io: &ConnReadWrite
            rdr: bufio.NewReader(conn),
            wtr: bufio.NewWriter(conn),
        ,
        inaddr: InAddr
            "",
            0,
        ,
        keepalive:    0,
        compression:  false
    
func (player *Player) ReadVarInt() (i int, err error) 
    val, _ := binary.ReadUvarint(player.io)
    return int(val), nil


刚建立连接时可以正常工作,但后来无法正确读取数据包ID。

工作了好几天,想重写复制wiki.vg的解决方案,但是好像不行

PS:我的副本和原件

    val, length := 0, 0
    for 
        current, err := player.io.ReadByte()
        if err != nil 
            return 0, err
        

        val |= int((current & 0x7F) << (length * 7))
        length += 1
        if length > 5 
            return 0, errors.New(fmt.Sprintf("%s: VarInt is too big", player.name))
        

        if val&0x80 != 0x80 
            break
        
    
    return int(val), nil
    int value = 0;
    int length = 0;
    byte currentByte;

    while (true) 
        currentByte = readByte();
        value |= (currentByte & 0x7F) << (length * 7);
        
        length += 1;
        if (length > 5) 
            throw new RuntimeException("VarInt is too big");
        

        if ((value & 0x80) != 0x80) 
            break;
        
    
    return value;

【问题讨论】:

我对原作有点困惑。首先,值为 (currentByte&0x7F),表示最高有效位为空。然后 (value & 0x80) 尝试读取所述最高有效位,该位不再存在。在我看来,它应该是 currentByte & 0x80,而不是 value & 0x80。 至少here 代码检查 currentByte 是否设置了最高有效位;还有here,所以我认为你的原件是错误的。 【参考方案1】:

wiki 中的代码是错误的。

((value &amp; 0x80) != 0x80) 应该是((currentByte &amp; 0x80) != 0x80)

编码工作如下:数字(或其他)被分成 7 位块。然后在每个字节中,最高有效位 (MSB) 表示后面还有更多字节,其余的对数字进行编码。

value |= (currentByte &amp; 0x7F) &lt;&lt; (length * 7); 行基本上使 MSB 为空(0x7F 是用于获取最后七位的掩码,即字节中除 MSB 之外的所有位)。 ((value &amp; 0x80) != 0x80) 正在测试 MSB 是否为 1,不能为 1,因为它只是被清零(0x80 是除 MSB 之外的每一位都清零的掩码)。所以它正在测试错误的值。

这是正确的示例 (source)

  def _ReadVarintHelper(self):
    """Helper for the various varint-reading methods above.
    Reads an unsigned, varint-encoded integer from the stream and
    returns this integer.
    Does no bounds checking except to ensure that we read at most as many bytes
    as could possibly be present in a varint-encoded 64-bit number.
    """
    result = 0
    shift = 0
    while 1:
      if shift >= 64:
        raise message.DecodeError('Too many bytes when decoding varint.')
      try:
        b = ord(self._buffer[self._pos])
      except IndexError:
        raise message.DecodeError('Truncated varint.')
      self._pos += 1
      result |= ((b & 0x7f) << shift)
      shift += 7
      if not (b & 0x80):
        return result

【讨论】:

以上是关于在 Golang 中无法从 Minecraft 正确读取数据包的主要内容,如果未能解决你的问题,请参考以下文章

Minecraft 1.64 的 Bukkit 服务器 怎么安装Mod ? 我在客户端里安装了,

我的世界minecraft正版启动器无法启动

golang实现正/反向代理服务

如何从 NodeJS 中的用户名中获取 Minecraft 玩家的 UUID?

C# 使用 tcp/ip 从 Minecraft 服务器获取数据包

在windows 8.1下无法利用批处理文件(.bat)启动bukkit(minecraft水桶服)