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 ?
Arqade is a question and answer site for passionate videogamers on all platforms. It only takes a minute to sign up.Sign up to join this community
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())+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 if len(sys.argv) > 1 else 'localhost' port = int(sys.argv) 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) while b&128:a,b=(a<<7)+b&127,s.recv(1) 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)))
(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)
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: