Lonami / mc-ping.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
import struct |
import socket |
import base64 |
import json |
import sys |
class Server : |
def __init__ ( self , data ): |
self . description = data . get ( ‘description’ ) |
if isinstance ( self . description , dict ): |
self . description = self . description [ ‘text’ ] |
self . icon = base64 . b64decode ( data . get ( ‘favicon’ , » )[ 22 :]) |
self . players = Players ( data [ ‘players’ ]) |
self . version = data [ ‘version’ ][ ‘name’ ] |
self . protocol = data [ ‘version’ ][ ‘protocol’ ] |
def __str__ ( self ): |
return ‘Server(description=, icon=, version=, ‘ \ |
‘protocol=, players=<>)’ . format ( |
self . description , bool ( self . icon ), self . version , |
self . protocol , self . players |
) |
class Players ( list ): |
def __init__ ( self , data ): |
super (). __init__ ( Player ( x ) for x in data . get ( ‘sample’ , [])) |
self . max = data [ ‘max’ ] |
self . online = data [ ‘online’ ] |
def __str__ ( self ): |
return ‘[<>, online=<>, max=<>]’ . format ( |
‘, ‘ . join ( str ( x ) for x in self ), self . online , self . max |
) |
class Player : |
def __init__ ( self , data ): |
self . id = data [ ‘id’ ] |
self . name = data [ ‘name’ ] |
def __str__ ( self ): |
return self . name |
# For the rest of requests see wiki.vg/Protocol |
def ping ( ip , port = 25565 ): |
def read_var_int (): |
i = 0 |
j = 0 |
while True : |
k = sock . recv ( 1 ) |
if not k : |
return 0 |
k = k [ 0 ] |
i |= ( k & 0x7f ) |
j += 1 |
if j > 5 : |
raise ValueError ( ‘var_int too big’ ) |
if not ( k & 0x80 ): |
return i |
sock = socket . socket () |
sock . connect (( ip , port )) |
try : |
host = ip . encode ( ‘utf-8’ ) |
data = b» # wiki.vg/Server_List_Ping |
data += b’ \x00 ‘ # packet ID |
data += b’ \x04 ‘ # protocol variant |
data += struct . pack ( ‘>b’ , len ( host )) + host |
data += struct . pack ( ‘>H’ , port ) |
data += b’ \x01 ‘ # next state |
data = struct . pack ( ‘>b’ , len ( data )) + data |
sock . sendall ( data + b’ \x01 \x00 ‘ ) # handshake + status ping |
length = read_var_int () # full packet length |
if length < 10 : |
if length < 0 : |
raise ValueError ( ‘negative length read’ ) |
else : |
raise ValueError ( ‘invalid response %s’ % sock . read ( length )) |
sock . recv ( 1 ) # packet type, 0 for pings |
length = read_var_int () # string length |
data = b» |
while len ( data ) != length : |
chunk = sock . recv ( length — len ( data )) |
if not chunk : |
raise ValueError ( ‘connection abborted’ ) |
data += chunk |
return Server ( json . loads ( data )) |
finally : |
sock . close () |
if __name__ == ‘__main__’ : |
for sv in sys . argv [ 1 :]: |
print ( ping ( sv )) |
Ping Tutorial¶
Welcome to the tutorial for PINGClient! This section aims to give you a basic understanding on PINGClient usage.
Basic Ping usage¶
mctools gives you the ability to ping and retrieve basic statistics from a server via the Server List Ping Interface. mctools uses a method similar to how the official Minecraft client pings servers. You can retrieve a lot of useful information, such as ping latency, server version, message of the day, players connected, ect.
Plus, Server List Ping is built into the protocol, meaning that it is always enabled, regardless of server configuration. This gives you a reliable way to get basic server statistics, even if RCON or Query is disabled.
As of now, PINGClient only supports Minecraft servers that are version 1.7 or greater.
Creating the instance¶
First, you must create a PINGClient instance:
from mctools import PINGClient ping = PINGClient('mc.server.net')
This will create a PINGClient instance with the default configuration. All of the options can be set as you see fit, but the default configuration is recommended for most users.
Unlike other clients, PINGClient allows you to optionally specify the protocol number to use when communicating. You can do this like so:
from mctools import PINGClient ping = PINGClient('mc.server.net', proto_num=PROTOCOL_NUMBER)
This allows PINGClient to emulate certain versions of the Minecraft client. By default, we use protocol number 0, which means we are inquiring on which protocol version we should use. You can find a list of protocol numbers and their versions here.
For more information on general client configuration and instantiation, see the client tutorial.
Pinging the server¶
The server will automatically close the connection after you ping the server or fetch statistics.
If you are running mctools version 1.2.0 or above, the PINGClient will be automatically stopped for you after each operation. The PINGClient can then be started again and used as expected.
However, older versions of mctools do not support client restarting, and you will have to create a new PINGClient instance.
Once you have created your PINGClient, you can ping the server:
This will return the latency for the ping in milliseconds. You can use this to determine if a server is receiving connections.
Retrieving Statistics¶
You can also receive statistics from the server:
This will fetch ping stats, and put it into a dictionary. The contents of the dictionary should be in the following format:
'description': 'text': 'Now we got business!'>, 'players': 'max': 20, 'online': 1, 'sample': [ 'id': 'fbf11fd0-5b74-490c-adc4-91febe9de2ae', 'name': 'MinecraftPlayer'>], 'message': ''>, 'time': 0.09879999993245292, 'version': 'name': '1.15.2', 'protocol': 578>, 'favicon': 'data:image/png;base64,'>
The description field is the message of the day.
The players field gives some information about connected players. It tells us the maximum amount of players allowed on the server at once(max), as well as how many players are currently connected(online). It also supplies a sample list of players who are online(sample). Some very large scale servers might not offer a sample list of connected players, and simply leave it blank.
The message field contains the message embedded in the player sample list. If you have formatting enabled, PINGClient will automatically separate the message and the valid players. We touch on this more later in the document.
The time field is the latency in milliseconds.
The version field gives some information about the server version. The name field usually contains the server version, and this can differ if the server is using a different implementation(Such as PaperMC, Spigot, or Bukkit). The protocol is the protocol number the server is using.
The favicon field is a PNG image encoded in Base64. This field is optional, and may not be present.
Note on Packet Format¶
For most cases, the information received will match the example above, and each field will contain the expected values that they should contain.
However, some servers take it upon themselves to embed messages into the player sample list, or give the description in ChatObject notation. If you have formatting enabled, then these cases are automatically handled for you.
You can read more about the ping formatters and how they handle data in the Formatting tutorial.
Stopping the instance¶
As of mctools version 1.2.0, the PINGClient is automatically stopped for you after each operation.
Still, it is recommended to stop the client anyway when it is done being used:
This will stop the underlying TCP connection to the Minecraft server, if their still is a connection. Again, most of the time it is not necessary to stop the client as it is done for you. You should still do so, as it can ensure that the client is stopped in case it did not automatically stop itself. It also helps readability, and allows you to explicitly state when you are done communicating over a network.
Conclusion¶
That concludes the tutorial for PINGClient!
MCStatus#
Mcstatus provides an API and command line script to fetch publicly available data from Minecraft servers. Specifically, mcstatus retrieves data by using these protocols: Server List Ping and Query. Because of mcstatus, you do not need to fully understand those protocols and can instead skip straight to retrieving minecraft server data quickly in your own programs.
Installation#
python3 -m pip install mcstatus
Usage#
Python API#
Java Edition#
from mcstatus import JavaServer # You can pass the same address you'd enter into the address field in minecraft into the 'lookup' function # If you know the host and port, you may skip this and use JavaServer("example.org", 1234) server = JavaServer.lookup("example.org:1234") # 'status' is supported by all Minecraft servers that are version 1.7 or higher. # Don't expect the player list to always be complete, because many servers run # plugins that hide this information or limit the number of players returned or even # alter this list to contain fake players for purposes of having a custom message here. status = server.status() print(f"The server has status.players.online> player(s) online and replied in status.latency> ms") # 'ping' is supported by all Minecraft servers that are version 1.7 or higher. # It is included in a 'status' call, but is also exposed separate if you do not require the additional info. latency = server.ping() print(f"The server replied in latency> ms") # 'query' has to be enabled in a server's server.properties file! # It may give more information than a ping, such as a full player list or mod information. query = server.query() print(f"The server has the following players online: ', '.join(query.players.names)>")
Bedrock Edition#
from mcstatus import BedrockServer # You can pass the same address you'd enter into the address field in minecraft into the 'lookup' function # If you know the host and port, you may skip this and use BedrockServer("example.org", 19132) server = BedrockServer.lookup("example.org:19132") # 'status' is the only feature that is supported by Bedrock at this time. # In this case status includes players.online, latency, motd, map, gamemode, and players.max. (ex: status.gamemode) status = server.status() print(f"The server has status.players.online> players online and replied in status.latency> ms")
Command Line Interface#
This only works with Java servers; Bedrock is not yet supported. Use mcstatus -h to see helpful information on how to use this script.