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

huangapple go评论82阅读模式
英文:

Cannot read packet from Minecraft correctly in Golang

问题

我是一名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。无法在Golang中正确读取Minecraft的数据包。

我已经努力工作了几天,并尝试将其重写为wiki.vg的解决方案,但似乎它不起作用无法在Golang中正确读取Minecraft的数据包。

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;
英文:

I am a beginner in Golang. And I had a problem with reading packets from Minecraft clients lately.

My program read packets from the connection in this way.

	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
}

It worked correctly when the connection was just established, but later it cannot read the packet ID correctly.无法在Golang中正确读取Minecraft的数据包。

I have worked for days, and I tried to rewrite it to copy wiki.vg's solution, but it seemed that it doesn't work无法在Golang中正确读取Minecraft的数据包。

PS: My copy and the original one

	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;

答案1

得分: 1

代码在维基百科上是错误的。

这一行 ((value & 0x80) != 0x80) 应该改为 ((currentByte & 0x80) != 0x80)

编码的工作原理如下:数字(或其他内容)被分成7位的块。然后在每个字节中,*最高有效位(MSB)*指示是否还有更多的字节跟随,其余的位编码数字。

这一行 value |= (currentByte & 0x7F) << (length * 7); 实际上将最高有效位(MSB)置零(0x7F 是用于获取字节中除了最高有效位之外的所有位的掩码)。((value & 0x80) != 0x80) 是在测试最高有效位是否为1,但实际上它不可能为1,因为它刚刚被置零(0x80 是将除了最高有效位之外的所有位都置零的掩码)。所以它测试的是错误的值。

这是正确的示例(来源):

  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
英文:

The code in wiki is wrong.

The line ((value &amp; 0x80) != 0x80) should be ((currentByte &amp; 0x80) != 0x80)

The encoding works as follows: the number (or something else) is divided into 7 bit chunks. then in each byte the most significant bit (MSB) indicates where there is more bytes to follow and the rest encode the number.

The line value |= (currentByte &amp; 0x7F) &lt;&lt; (length * 7); basically nulls the MSB (0x7F is the mask for taking last seven bits, i.e., all but MSB from the byte). The ((value &amp; 0x80) != 0x80) is testing if MSB is one, which cannot be one, because it was just nulled (0x80 is the mask that nulls every bit except the MSB). So it is testing the wrong value.

This is the correct sample (source)

  def _ReadVarintHelper(self):
    &quot;&quot;&quot;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.
    &quot;&quot;&quot;
    result = 0
    shift = 0
    while 1:
      if shift &gt;= 64:
        raise message.DecodeError(&#39;Too many bytes when decoding varint.&#39;)
      try:
        b = ord(self._buffer[self._pos])
      except IndexError:
        raise message.DecodeError(&#39;Truncated varint.&#39;)
      self._pos += 1
      result |= ((b &amp; 0x7f) &lt;&lt; shift)
      shift += 7
      if not (b &amp; 0x80):
        return result

huangapple
  • 本文由 发表于 2021年12月11日 11:33:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/70312423.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定