Minecraft – Query number of players on a minecraft server

minecraft-java-editionminecraft-java-edition-server

Without the minecraft client, there is a scripted (php, python, whatever) way to ask basic information (what you see in the multiplayer menu) to a minecraft server.

Does anyone knows the few magical bytes to send on the port 25565 ?

Best Answer

Before the 1.7 version, a custom TCP protocol was used, and thus some escaped hexadecimal through a netcat / telnet did worked.

Today, they use JSON objects, and a more complex protocol, as implemented on the next link. On the wiki page little python script was linked : https://gist.github.com/barneygale/1209061.

I made this small implementation (freely inspired from last link) which prints the JSON object answered by the Minecraft server (localhost:25565 by default)

#!/usr/bin/env python3
import sys,json,struct,socket

def popint(s):
  acc = 0
  b = ord(s.recv(1))
  while b & 0x80:
    acc = (acc<<7)+(b&0x7f)
    b = ord(s.recv(1))
  return (acc<<7)+(b&0x7f)

def pack_varint(d):
  return bytes([(0x40*(i!=d.bit_length()//7))+((d>>(7*(i)))%128) for i in range(1+d.bit_length()//7)])

def pack_data(d):
  return pack_varint(len(d)) + d

def get_info(host,port):
  s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
  s.connect((host, port))
  s.send(pack_data(bytes(2)+pack_data(bytes(host,'utf8'))+struct.pack('>H',port)+bytes([1]))+bytes([1,0]))
  popint(s)   # Packet length
  popint(s)   # Packet ID
  l,d = popint(s),bytes()
  while len(d) < l: d += s.recv(1024)
  s.close()
  return json.loads(d.decode('utf8'))

if __name__ == '__main__':
  host = sys.argv[1] if len(sys.argv) > 1 else 'localhost'
  port = int(sys.argv[2]) if len(sys.argv) > 2 else 25565
  print(get_info(host,port))

Downloadable here https://gist.github.com/qolund/6d10c02f331ca8ee047f

Edit : minimal version, use it with python3 script.py host port

import json,sys,socket as S
h,p=sys.argv[1:]
p=int(p)
u,K,L='utf8',bytes,len
s=S.socket(2,1);s.connect((h,p))
def z():
 a,b=0,s.recv(1)[0]
 while b&128:a,b=(a<<7)+b&127,s.recv(1)[0]
 return b&127+(a<<7)
def V(d,b):return K([(64*(i!=b//7))+((d>>(7*(i)))%128)for i in range(1+b//7)])
def D(d):return V(L(d),L(d).bit_length())+d
s.send(D(K(2)+D(K(h,u))+K([p>>8,p%256,1]))+K([1,0]))
z();z();l,d=z(),K()
while L(d)<l:d+=s.recv(1024)
s.close()
print(json.loads(str(d,u)))