main
1package minecraft
2
3import (
4 "bufio"
5 "bytes"
6 "encoding/binary"
7
8 "encoding/json"
9 "fmt"
10 "net"
11 //"zappem.net/pub/debug/xxd"
12)
13
14type status struct {
15 Version struct {
16 Name string `json:"name"`
17 Protocol int `json:"protocol"`
18 } `json:"version"`
19 Players struct {
20 Max int `json:"max"`
21 Online int `json:"online"`
22 } `json:"players"`
23}
24
25// MinecraftPacket has support for writing minecraft varint packed data
26// as well as normal bytes
27type MinecraftPacket struct {
28 bytes.Buffer
29}
30
31// WriteVarInt writes an integer in Minecraft VarInt to the buffer.
32func (b *MinecraftPacket) WriteVarInt(value uint64) error {
33 var buf []byte = make([]byte, binary.MaxVarintLen64)
34 n := binary.PutUvarint(buf[:], value)
35 _, err := b.Write(buf[:n])
36 return err
37}
38
39func (b *MinecraftPacket) Finalize() []byte {
40 length := b.Len()
41 var buf []byte = make([]byte, binary.MaxVarintLen64)
42 n := binary.PutUvarint(buf[:], uint64(length))
43 return append(buf[:n], b.Bytes()...)
44}
45
46func Connections(host string, port uint16) (int, error) {
47
48 address := fmt.Sprintf("%s:%d", host, port)
49 conn, err := net.Dial("tcp", address)
50 if err != nil {
51 return 0, fmt.Errorf("failed to connect to server: %w", err)
52 }
53 defer conn.Close()
54
55 // Protocol Version - also try 0 if it fails
56 protocolVersion := uint64(769)
57 // Server port (BigEndian 2 bytes)
58 portBytes := make([]byte, 2)
59 binary.BigEndian.PutUint16(portBytes, uint16(port))
60
61 h := MinecraftPacket{bytes.Buffer{}}
62 h.WriteVarInt(0) // Packet ID
63 h.WriteVarInt(protocolVersion) // Protocol Version
64 h.WriteVarInt(uint64(len(host))) // Server Addr Len
65 h.Write([]byte(host)) // Server Addr
66 h.Write(portBytes) // Server Port
67 h.WriteVarInt(1) // Next State = 1 - Status
68 handshake := h.Finalize() // Prepend Packet Len
69
70 _, err = conn.Write(handshake)
71 if err != nil {
72 return 0, fmt.Errorf("failed to connect send handshake: %w", err)
73 }
74
75 sr := MinecraftPacket{bytes.Buffer{}}
76 sr.WriteVarInt(0) // Status Packet ID
77 statusRequest := sr.Finalize() // Prepend Packet Len
78
79 _, err = conn.Write(statusRequest)
80 if err != nil {
81 return 0, fmt.Errorf("failed to connect send status request: %w", err)
82 }
83
84 byteReader := bufio.NewReader(conn)
85 _, err = binary.ReadUvarint(byteReader) // respLen
86 if err != nil {
87 return 0, fmt.Errorf("failed to read response len: %w", err)
88 }
89 _, err = binary.ReadUvarint(byteReader) // packetID
90 if err != nil {
91 return 0, fmt.Errorf("failed to read paceket id: %w", err)
92 }
93 _, err = binary.ReadUvarint(byteReader) // statusLen
94 if err != nil {
95 return 0, fmt.Errorf("failed to read status len: %w", err)
96 }
97
98 decoder := json.NewDecoder(byteReader)
99 var s status
100 err = decoder.Decode(&s)
101 if err != nil {
102 return 0, fmt.Errorf("failed decoding server status response: %w", err)
103 }
104
105 return s.Players.Online, nil
106}