2

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 ?

3
4

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)))
0
3

(Not an answer as it expands on Nope's, but too long for a comment, and I need code formatting)

Nope's code breaks for me (MC 1.10) as popint doesn't seem to decode multi-byte integers correctly; bytes are received in little endian order (lowest byte first). If a server has a large icon, the length doesn't get decoded correctly, which, in some cases, works anyway (as the code always reads 1024 bytes even when l is smaller), in others, you get an error from the JSON decoder about an unterminated string.

Replacing the popint function with this fixes the issue:

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

Yes, there is now a quasi-official Python class to do this; it’s written by this guy.

It can be called directly from the command line (or can be used as a Python library).

To check if your server is up, get player count, etc., is as simple as:

mcstatus minecraft.example.com query

…and the exit code will be set, so you can even integrate this into e.g. automated heartbeat systems for checking uptime.

(You can use ping instead of query to test latency, at the cost of it taking a bit longer to run.)

It, unlike the currently-accepted answer:

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.