Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Annotate some of the functions and classes. #134

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions mcstatus/bedrock_status.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@ def __init__(self, host, port=19132, timeout=3):
self.timeout = timeout

@staticmethod
def parse_response(data: bytes, latency: int):
def parse_response(data: bytes, latency: float):
data = data[1:]
name_length = struct.unpack(">H", data[32:34])[0]
data = data[34 : 34 + name_length].decode().split(";")
data = data[34 : 34 + name_length].decode().split(";") # type: ignore

try:
map_ = data[7]
Expand Down Expand Up @@ -57,7 +57,7 @@ async def read_status_async(self):
data, _ = await stream.recv()
finally:
try:
stream.close()
stream.close() # type: ignore - stream may be unbound
except BaseException:
pass

Expand Down
29 changes: 25 additions & 4 deletions mcstatus/pinger.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import datetime
import json
import random
from typing import List, Optional

from six import string_types

from mcstatus.protocol.connection import Connection


class ServerPinger:
def __init__(self, connection, host="", port=0, version=47, ping_token=None):
def __init__(self, connection, host: str = "", port: int = 0, version: int = 47, ping_token=None):
if ping_token is None:
ping_token = random.randint(0, (1 << 63) - 1)
self.version = version
Expand Down Expand Up @@ -106,8 +108,13 @@ async def test_ping(self):


class PingResponse:
# THIS IS SO UNPYTHONIC
# it's staying just because the tests depend on this structure
class Players:
class Player:
name: str
id: str

def __init__(self, raw):
if not isinstance(raw, dict):
raise ValueError("Invalid player object (expected dict, found %s" % type(raw))
Expand All @@ -124,6 +131,10 @@ def __init__(self, raw):
raise ValueError("Invalid player object (expected 'id' to be str, was %s)" % type(raw["id"]))
self.id = raw["id"]

online: int
max: int
sample: Optional[List["PingResponse.Players.Player"]]

def __init__(self, raw):
if not isinstance(raw, dict):
raise ValueError("Invalid players object (expected dict, found %s" % type(raw))
Expand All @@ -148,6 +159,9 @@ def __init__(self, raw):
self.sample = None

class Version:
name: str
protocol: int

def __init__(self, raw):
if not isinstance(raw, dict):
raise ValueError("Invalid version object (expected dict, found %s" % type(raw))
Expand All @@ -164,6 +178,12 @@ def __init__(self, raw):
raise ValueError("Invalid version object (expected 'protocol' to be int, was %s)" % type(raw["protocol"]))
self.protocol = raw["protocol"]

players: Players
version: Version
description: str
favicon: Optional[str]
latency: float = 0

def __init__(self, raw):
self.raw = raw

Expand All @@ -177,11 +197,12 @@ def __init__(self, raw):

if "description" not in raw:
raise ValueError("Invalid status object (no 'description' value)")
self.description = raw["description"]
if isinstance(raw["description"], dict):
self.description = raw["description"]["text"]
else:
self.description = raw["description"]

if "favicon" in raw:
self.favicon = raw["favicon"]
else:
self.favicon = None

self.latency = None
16 changes: 12 additions & 4 deletions mcstatus/protocol/connection.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
from abc import abstractmethod
import socket
import struct
import asyncio
Expand All @@ -18,9 +19,9 @@ def read(self, length):

def write(self, data):
if isinstance(data, Connection):
data = bytearray(data.flush())
data = data.flush()
if isinstance(data, str):
data = bytearray(data)
data = bytearray(data, "utf-8")
self.sent.extend(data)

def receive(self, data):
Expand All @@ -33,7 +34,7 @@ def remaining(self):

def flush(self):
result = self.sent
self.sent = ""
self.sent = bytearray()
return result

def _unpack(self, format, data):
Expand Down Expand Up @@ -128,6 +129,10 @@ def write_buffer(self, buffer):


class AsyncReadConnection(Connection):
@abstractmethod
async def read(self, length: int) -> bytearray:
...

async def read_varint(self):
result = 0
for i in range(5):
Expand All @@ -139,7 +144,7 @@ async def read_varint(self):

async def read_utf(self):
length = await self.read_varint()
return self.read(length).decode("utf8")
return (await self.read(length)).decode("utf8")

async def read_ascii(self):
result = bytearray()
Expand Down Expand Up @@ -263,6 +268,9 @@ async def read(self, length):
def write(self, data):
self.writer.write(data)

def close(self):
self.writer.close()


class UDPAsyncSocketConnection(AsyncReadConnection):
stream = None
Expand Down
81 changes: 50 additions & 31 deletions mcstatus/querier.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import struct
from typing import List

from mcstatus.protocol.connection import Connection

Expand Down Expand Up @@ -38,7 +39,7 @@ def read_query(self):
self.connection.write(request)

response = self._read_packet()
return parse_response(response)
return QueryResponse.from_connection(response)


class AsyncServerQuerier(ServerQuerier):
Expand All @@ -60,17 +61,27 @@ async def read_query(self):
await self.connection.write(request)

response = await self._read_packet()
return parse_response(response)
return QueryResponse.from_connection(response)


class QueryResponse:
# THIS IS SO UNPYTHONIC
# it's staying just because the tests depend on this structure
class Players:
online: int
max: int
names: List[str]

def __init__(self, online, max, names):
self.online = int(online)
self.max = int(max)
self.names = names

class Software:
version: str
brand: str
plugins: List[str]

def __init__(self, version, plugins):
self.version = version
self.brand = "vanilla"
Expand All @@ -83,33 +94,41 @@ def __init__(self, version, plugins):
if len(parts) == 2:
self.plugins = [s.strip() for s in parts[1].split(";")]

motd: str
map: str
players: Players
software: Software

def __init__(self, raw, players):
self.raw = raw
self.motd = raw["hostname"]
self.map = raw["map"]
self.players = QueryResponse.Players(raw["numplayers"], raw["maxplayers"], players)
self.software = QueryResponse.Software(raw["version"], raw["plugins"])


def parse_response(response: Connection) -> QueryResponse:
response.read(len("splitnum") + 1 + 1 + 1)
data = {}
players = []

while True:
key = response.read_ascii()
if len(key) == 0:
response.read(1)
break
value = response.read_ascii()
data[key] = value

response.read(len("player_") + 1 + 1)

while True:
name = response.read_ascii()
if len(name) == 0:
break
players.append(name)

return QueryResponse(data, players)
try:
self.raw = raw
self.motd = raw["hostname"]
self.map = raw["map"]
self.players = QueryResponse.Players(raw["numplayers"], raw["maxplayers"], players)
self.software = QueryResponse.Software(raw["version"], raw["plugins"])
except:
raise ValueError("The provided data is not valid")

@classmethod
def from_connection(cls, response: Connection):
response.read(len("splitnum") + 1 + 1 + 1)
data = {}
players = []

while True:
key = response.read_ascii()
if len(key) == 0:
response.read(1)
break
value = response.read_ascii()
data[key] = value

response.read(len("player_") + 1 + 1)

while True:
name = response.read_ascii()
if len(name) == 0:
break
players.append(name)

return cls(data, players)
4 changes: 3 additions & 1 deletion mcstatus/scripts/mcstatus.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import socket
from typing import Any
import click
from json import dumps as json_dumps

Expand Down Expand Up @@ -77,7 +78,8 @@ def json():
Prints server status and query in json. Supported by all Minecraft
servers that are version 1.7 or higher.
"""
data = {"online": False}
data = {}
data["online"] = False
# Build data with responses and quit on exception
try:
ping_res = server.ping()
Expand Down
Loading