masa- icon

Minecraft server ping (ping.py for the other server scripts)

masa- | PRO | 08/16/17 05:18:58 PM UTC | 0 ⭐ | 1789 👁️ | Never ⏰ | []
Python |

5.08 KB

|

None

|

0 👍

/

0 👎

# -*- coding: UTF-8 -*-
 
import socket
import sys
 
 
timeout = 1.7
server_host = "localhost"
server_port = 25565
 
# Minecraft Ping protocol to use:
# 1 = Minecraft Beta 1.8 - Minecraft 1.3
# 2 = Minecraft 1.4 - 1.5
# 3 = Minecraft 1.6
protocol = 1
 
# Parse the command line arguments
# We can have '[host]', '[host] [port]', '[host:port]',
# '[host] [port] [timeout]' or '[host:port] [timeout]'
if len(sys.argv) == 2:
    if ':' in sys.argv[1]:
        tmp = str(sys.argv[1]).split(':')
        server_host = str(tmp[0])
        server_port = int(tmp[1])
    else:
        server_host = str(sys.argv[1])
 
elif len(sys.argv) >= 3:
    if ':' in sys.argv[1]:
        tmp = str(sys.argv[1]).split(':')
        server_host = str(tmp[0])
        server_port = int(tmp[1])
        timeout = float(sys.argv[2])
    else:
        server_host = str(sys.argv[1])
        server_port = int(sys.argv[2])
 
    if len(sys.argv) >= 4:
        timeout = float(sys.argv[3])
        if len(sys.argv) >= 5:
            protocol = int(sys.argv[4])
 
 
#print str('host: {} port: {} timeout: {}').format(server_host, server_port, timeout)
#sys.exit()
 
def ping(host, port, timeout = 1.7, protocol = 1):
    """Ping the server and return the values the server gives us"""
 
    if protocol == 1:
        # Send 0xFE: Server list ping (Minecraft Beta 1.8 - 1.3)
        msg = "\xFE"
        #sock.sendall("\xFE")
    elif protocol == 2:
        # Send 0xFE 0x01: Server list ping (Minecraft 1.4 - 1.5)
        msg = "\xFE\x01"
        #sock.sendall("\xFE\x01")
    elif protocol == 3:
        # Server list ping (Minecraft 1.6)
        msg = "\xFE\x01\xFA"
        msg += "\x00\x0B" # The length of the following string, in characters, as a short (always 11)
 
        # "MC|PingHost" encoded as a big-endian UCS-2 string:
        msg += "\x00\x4D\x00\x43\x00\x7C\x00\x50\x00\x69\x00\x6E\x00\x67\x00\x48\x00\x6F\x00\x73\x00\x74"
 
        # FIXME/TODO:
        # Alternatively:
        #msg += str('MC|PingHost').encode('UTF-16BE')
 
        #msg += "" # The length of the rest of the data, as a short. Compute as 7 + 2 * strlen(hostname)
        #msg += "" # Protocol version, currently 74 (decimal)
        #msg += "" # The length of following string, in characters, as a short
        #msg += "" # The hostname the client is connecting to, encoded in the same way as "MC|PingHost"
        #msg += "" # The port the client is connecting to, as an int
    else:
        print(str('ping(): Error, invalid protocol: {}').format(protocol))
        return None
 
    try:
        #sock = socket.create_connection((server_address, server_port), timeout)
        sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    except socket.error as msg:
        #print('socket(): except:', msg)
        sock = None
        return None
 
    try:
        #sock.setblocking(0)
        sock.settimeout(timeout)
        sock.connect((host, port))
    except socket.error as msg:
        #print('connect(): except:', msg)
        sock = None
        return None
 
    sock.sendall(msg)
 
    try:
        val = sock.recv(256)
    except socket.error as msg:
        sock.close()
        #print('recv(): except:', msg)
        return None
 
    sock.shutdown(socket.SHUT_RDWR)
    sock.close()
 
    # Check for a valid 0xFF disconnect
    if val[0] != "\xFF":
        return None
 
    if protocol == 1:
        # Remove the packet ident (0xFF) and the short containing the length of the string
        # Minecraft Beta 1.8 - 1.3
        val = val[3:]
    elif protocol == 2 or protocol == 3:
        # Remove the packet ident (0xFF) and the short containing the length of the string
        # and the rest of the crap
        # Minecraft 1.4 - 1.5, 1.6
        val = val[9:]
 
    # Decode UCS-2 string
    #val = val.decode('UTF-16LE')
    val = val.decode('UTF-16BE')
 
    if protocol == 1:
        # Split into a list (Minecraft Beta 1.8 - 1.3)
        val = val.split(u"\xA7")
    elif protocol == 2 or protocol == 3:
        # Split into a list (Minecraft 1.4 - 1.5, 1.6)
        val = val.split("\x00")
 
    # Debug:
    #print('Length of val:', len(val))
    #print(val)
    #return "foo"
 
    #if len(val) < 3:
    #   return None
 
    if len(val) == 3:
        # Return a dictionary of values (Minecraft Beta 1.8 - 1.3)
        # values: MOTD, num_player, max_players
        return {'motd': val[0],\
            'num_players': int(val[1]),\
            'max_players': int(val[2])}
    elif len(val) == 5:
        # Return a dictionary of values (Minecraft 1.4 - 1.5, 1.6)
        # values: protocol_version, version, MOTD, num_player, max_players
        return {'protocol_version': int(val[0]),\
            'version': val[1],\
            'motd': val[2],\
            'num_players': int(val[3]),\
            'max_players': int(val[4])}
    else:
        return None
 
 
 
 
 
def check_server(host, port, timeout = 1.7, protocol = 1):
    """Check if the server is up by ping()ing it checking for the known keys"""
 
    data = ping(host, port, timeout, protocol)
    #print('host:', host, 'port:', port, data)
 
    if type(data) is dict:
        if 'version' in data:
            msg = str('{}:{} ([{}], {}): OK').format(host, port, data['version'], data['motd'])
            print msg           
        elif 'motd' in data:
            msg = str('{}:{} ({}): OK').format(host, port, data['motd'])
            print msg
            #print '%s: up' % data['motd']
        else:
            msg = str('{}:{}: N/A').format(host, port)
            print msg
            #print '%s: down' % data['motd']
    else:
        msg = str('{}:{}: N/A').format(host, port)
        print msg
 
    return None
 
 
check_server(server_host, server_port, timeout, protocol)

Comments