Не удается правильно прочитать пакет из Minecraft в Golang

#go #tcp #minecraft

#Вперед #tcp #Minecraft

Вопрос:

Я новичок в Golang. И в последнее время у меня возникла проблема с чтением пакетов из клиентов Minecraft.

Моя программа считывает пакеты из соединения таким образом.

     player := amp;Player{
        conn:     conn,
        state:    HANDSHAKING,
        io: amp;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
}

 

Он работал правильно, когда соединение только что было установлено, но позже он не может правильно прочитать идентификатор пакета.Неправильные идентификаторы

Я работал несколько дней, и я попытался переписать его, чтобы скопировать wiki.vg это решение, но казалось, что оно не работаетвведите описание изображения здесь

PS: Моя копия и оригинал

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

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

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

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

        if ((value amp; 0x80) != 0x80) {
            break;
        }
    }
    return value;
 

Комментарии:

1. я немного смущен оригиналом. во-первых, значение равно (currentByteamp; 0x7F), что означает, что старший значащий бит обнуляется. Затем (значение amp; 0x80) пытается прочитать указанный самый значимый бит, которого больше нет. Мне кажется, это должно быть currentByte amp; 0x80, а не value amp; 0x80.

2. по крайней мере, здесь код проверяет currentByte на предмет того, установлен ли старший значащий бит; и здесь тоже, поэтому я думаю, что ваш оригинал неверен.

Ответ №1:

Код в wiki неверен.

Строка ((value amp; 0x80) != 0x80) должна быть ((currentByte amp; 0x80) != 0x80)

Кодирование работает следующим образом: число (или что-то еще) делится на 7 битных блоков. затем в каждом байте старший значащий бит (MSB) указывает, где нужно больше байтов, а остальные кодируют число.

Строка value |= (currentByte amp; 0x7F) << (length * 7); в основном обнуляет MSB (0x7F — это маска для взятия последних семи битов, т. Е. Всех, Кроме MSB, из байта). Проверяется ((value amp; 0x80) != 0x80) , является ли MSB единицей, которая не может быть единицей, потому что она была просто обнулена (0x80 — это маска, которая обнуляет каждый бит, кроме MSB). Таким образом, он проверяет неправильное значение.

Это правильный образец (источник)

   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 amp; 0x7f) << shift)
      shift  = 7
      if not (b amp; 0x80):
        return result