Search code examples
gotcpminecraft

Cannot read packet from Minecraft correctly in Golang


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.Wrong IDs

I have worked for days, and I tried to rewrite it to copy wiki.vg's solution, but it seemed that it doesn't workenter image description here

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;

Solution

  • The code in wiki is wrong.

    The line ((value & 0x80) != 0x80) should be ((currentByte & 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 & 0x7F) << (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 & 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):
        """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