package minecraft import ( "bytes" "encoding/binary" "encoding/json" "fmt" "io" "net" "strings" "time" ) type StatusResponse struct { Version struct { Name string `json:"name"` Protocol int `json:"protocol"` } `json:"version"` Players struct { Max int `json:"max"` Online int `json:"online"` Sample []struct { Name string `json:"name"` ID string `json:"id"` } `json:"sample"` } `json:"players"` } func ProtocolForVersion(version string) int { v := strings.TrimSpace(strings.TrimPrefix(version, "v")) switch v { case "1.21.1": return 767 case "1.21": return 767 case "1.20.4": return 765 case "1.20.1": return 763 case "1.19.4": return 762 default: return 754 } } func QueryStatus(host string, port int, protocol int) (StatusResponse, error) { addr := fmt.Sprintf("%s:%d", host, port) conn, err := net.DialTimeout("tcp", addr, 3*time.Second) if err != nil { return StatusResponse{}, err } defer conn.Close() _ = conn.SetDeadline(time.Now().Add(5 * time.Second)) if err := writeHandshake(conn, host, port, protocol); err != nil { return StatusResponse{}, err } if err := writeStatusRequest(conn); err != nil { return StatusResponse{}, err } payload, err := readPacket(conn) if err != nil { return StatusResponse{}, err } r := bytes.NewReader(payload) packetID, err := readVarInt(r) if err != nil { return StatusResponse{}, err } if packetID != 0x00 { return StatusResponse{}, fmt.Errorf("unexpected packet id: %d", packetID) } respStr, err := readString(r) if err != nil { return StatusResponse{}, err } var status StatusResponse if err := json.Unmarshal([]byte(respStr), &status); err != nil { return StatusResponse{}, err } return status, nil } func writeHandshake(w io.Writer, host string, port int, protocol int) error { var payload bytes.Buffer if err := writeVarInt(&payload, 0x00); err != nil { return err } if err := writeVarInt(&payload, protocol); err != nil { return err } if err := writeString(&payload, host); err != nil { return err } if err := binary.Write(&payload, binary.BigEndian, uint16(port)); err != nil { return err } if err := writeVarInt(&payload, 0x01); err != nil { return err } return writePacket(w, payload.Bytes()) } func writeStatusRequest(w io.Writer) error { var payload bytes.Buffer if err := writeVarInt(&payload, 0x00); err != nil { return err } return writePacket(w, payload.Bytes()) } func writePacket(w io.Writer, payload []byte) error { var buf bytes.Buffer if err := writeVarInt(&buf, len(payload)); err != nil { return err } if _, err := buf.Write(payload); err != nil { return err } _, err := w.Write(buf.Bytes()) return err } func readPacket(r io.Reader) ([]byte, error) { length, err := readVarInt(r) if err != nil { return nil, err } if length <= 0 || length > 1<<20 { return nil, fmt.Errorf("invalid packet length: %d", length) } buf := make([]byte, length) if _, err := io.ReadFull(r, buf); err != nil { return nil, err } return buf, nil } func writeVarInt(w io.Writer, value int) error { for { temp := byte(value & 0x7F) value >>= 7 if value != 0 { temp |= 0x80 } if _, err := w.Write([]byte{temp}); err != nil { return err } if value == 0 { break } } return nil } func readVarInt(r io.Reader) (int, error) { numRead := 0 result := 0 for { if numRead > 5 { return 0, fmt.Errorf("varint too long") } b := make([]byte, 1) if _, err := r.Read(b); err != nil { return 0, err } value := int(b[0] & 0x7F) result |= value << (7 * numRead) numRead++ if b[0]&0x80 == 0 { break } } return result, nil } func writeString(w io.Writer, s string) error { if err := writeVarInt(w, len(s)); err != nil { return err } _, err := w.Write([]byte(s)) return err } func readString(r io.Reader) (string, error) { length, err := readVarInt(r) if err != nil { return "", err } if length < 0 || length > 1<<20 { return "", fmt.Errorf("invalid string length: %d", length) } buf := make([]byte, length) if _, err := io.ReadFull(r, buf); err != nil { return "", err } return string(buf), nil }