Removed everything; we're starting from scratch.

This commit is contained in:
Nathan Adams
2014-09-02 17:18:28 +02:00
parent 7c36615ea8
commit 34d0154f7a
5 changed files with 103 additions and 192 deletions

103
.gitignore vendored
View File

@@ -1,2 +1,105 @@
# Created by http://www.gitignore.io
### Python ###
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod] *.py[cod]
# C extensions
*.so
# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.cache
nosetests.xml
coverage.xml
# Translations
*.mo
*.pot
# Django stuff:
*.log
# Sphinx documentation
docs/_build/
# PyBuilder
target/
### PyCharm ###
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm
## Directory-based project format
.idea/
/*.iml
# if you remove the above rule, at least ignore user-specific stuff:
# .idea/workspace.xml
# .idea/tasks.xml
# .idea/dictionaries
# and these sensitive or high-churn files:
# .idea/dataSources.ids
# .idea/dataSources.xml
# .idea/sqlDataSources.xml
# .idea/dynamic.xml
# and, if using gradle::
# .idea/gradle.xml
# .idea/libraries
## File-based project format
*.ipr
*.iws
## Additional for IntelliJ
out/
# generated by mpeltonen/sbt-idea plugin
.idea_modules/
# generated by JIRA plugin
atlassian-ide-plugin.xml
# generated by Crashlytics plugin (for Android Studio and Intellij)
com_crashlytics_export_strings.xml
### SublimeText ###
# workspace files are user-specific
*.sublime-workspace
# project files should be checked into the repository, unless a significant
# proportion of contributors will probably not be using SublimeText
# *.sublime-project
#sftp configuration file
sftp-config.json

44
cli.py
View File

@@ -1,44 +0,0 @@
#!/usr/bin/env python
import socket
import sys
from pprint import pprint
from argparse import ArgumentParser
from minecraft_query import MinecraftQuery
def main():
parser = ArgumentParser(description="Query status of Minecraft multiplayer server",
epilog="Exit status: 0 if the server can be reached, otherwise nonzero."
)
parser.add_argument("host", help="target hostname")
parser.add_argument("-q", "--quiet", action='store_true', default=False,
help='don\'t print anything, just check if the server is running')
parser.add_argument("-p", "--port", type=int, default=25565,
help='UDP port of server\'s "query" service [25565]')
parser.add_argument("-r", "--retries", type=int, default=3,
help='retry query at most this number of times [3]')
parser.add_argument("-t", "--timeout", type=int, default=10,
help='retry timeout in seconds [10]')
options = parser.parse_args()
try:
query = MinecraftQuery(options.host, options.port,
timeout=options.timeout,
retries=options.retries)
server_data = query.get_rules()
except socket.error as e:
if not options.quiet:
print "socket exception caught:", e.message
print "Server is down or unreachable."
sys.exit(1)
if not options.quiet:
print "Server response data:"
pprint(server_data)
sys.exit(0)
if __name__=="__main__":
main()

0
mcstatus/__init__.py Normal file
View File

View File

@@ -1,2 +0,0 @@
# Backwards-compatibility
from query import *

View File

@@ -1,146 +0,0 @@
import socket
import struct
class MinecraftQuery:
MAGIC_PREFIX = '\xFE\xFD'
PACKET_TYPE_CHALLENGE = 9
PACKET_TYPE_QUERY = 0
HUMAN_READABLE_NAMES = dict(
game_id = "Game Name",
gametype = "Game Type",
motd = "Message of the Day",
hostname = "Server Address",
hostport = "Server Port",
map = "Main World Name",
maxplayers = "Maximum Players",
numplayers = "Players Online",
players = "List of Players",
plugins = "List of Plugins",
raw_plugins = "Raw Plugin Info",
software = "Server Software",
version = "Game Version",
)
def __init__(self, host, port, timeout=10, id=0, retries=2):
self.addr = (host, port)
self.id = id
self.id_packed = struct.pack('>l', id)
self.challenge_packed = struct.pack('>l', 0)
self.retries = 0
self.max_retries = retries
self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.socket.settimeout(timeout)
def send_raw(self, data):
self.socket.sendto(self.MAGIC_PREFIX + data, self.addr)
def send_packet(self, type, data=''):
self.send_raw(struct.pack('>B', type) + self.id_packed + self.challenge_packed + data)
def read_packet(self):
buff = self.socket.recvfrom(1460)[0]
type = struct.unpack('>B', buff[0])[0]
id = struct.unpack('>l', buff[1:5])[0]
return type, id, buff[5:]
def handshake(self, bypass_retries=False):
self.send_packet(self.PACKET_TYPE_CHALLENGE)
try:
type, id, buff = self.read_packet()
except:
if not bypass_retries:
self.retries += 1
if self.retries < self.max_retries:
self.handshake(bypass_retries=bypass_retries)
return
else:
raise
self.challenge = int(buff[:-1])
self.challenge_packed = struct.pack('>l', self.challenge)
def get_status(self):
if not hasattr(self, 'challenge'):
self.handshake()
self.send_packet(self.PACKET_TYPE_QUERY)
try:
type, id, buff = self.read_packet()
except:
self.handshake()
return self.get_status()
data = {}
data['motd'], data['gametype'], data['map'], data['numplayers'], data['maxplayers'], buff = buff.split('\x00', 5)
data['hostport'] = struct.unpack('<h', buff[:2])[0]
buff = buff[2:]
data['hostname'] = buff[:-1]
for key in ('numplayers', 'maxplayers'):
try:
data[key] = int(data[key])
except:
pass
return data
def get_rules(self):
if not hasattr(self, 'challenge'):
self.handshake()
self.send_packet(self.PACKET_TYPE_QUERY, self.id_packed)
try:
type, id, buff = self.read_packet()
except:
self.retries += 1
if self.retries < self.max_retries:
self.handshake(bypass_retries=True)
return self.get_rules()
else:
raise
data = {}
buff = buff[11:] # splitnum + 2 ints
items, players = buff.split('\x00\x00\x01player_\x00\x00') # Shamefully stole from https://github.com/barneygale/MCQuery
if items[:8] == 'hostname':
items = 'motd' + items[8:]
items = items.split('\x00')
data = dict(zip(items[::2], items[1::2]))
players = players[:-2]
if players:
data['players'] = players.split('\x00')
else:
data['players'] = []
for key in ('numplayers', 'maxplayers', 'hostport'):
try:
data[key] = int(data[key])
except:
pass
data['raw_plugins'] = data['plugins']
data['software'], data['plugins'] = self.parse_plugins(data['raw_plugins'])
return data
def parse_plugins(self, raw):
parts = raw.split(':', 1)
server = parts[0].strip()
plugins = []
if len(parts) == 2:
plugins = parts[1].split(';')
plugins = map(lambda s: s.strip(), plugins)
return server, plugins