mirror of
https://github.com/Dinnerbone/mcstatus.git
synced 2026-04-05 11:31:23 +08:00
100 lines
2.8 KiB
Python
100 lines
2.8 KiB
Python
from __future__ import annotations
|
|
|
|
import asyncio
|
|
import socket
|
|
import struct
|
|
from time import perf_counter
|
|
|
|
import asyncio_dgram
|
|
|
|
|
|
class BedrockServerStatus:
|
|
request_status_data = b"\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\xfe\xfe\xfe\xfe\xfd\xfd\xfd\xfd\x124Vx"
|
|
|
|
def __init__(self, host: str, port: int = 19132, timeout: float = 3):
|
|
self.host = host
|
|
self.port = port
|
|
self.timeout = timeout
|
|
|
|
@staticmethod
|
|
def parse_response(data: bytes, latency: float) -> "BedrockStatusResponse":
|
|
data = data[1:]
|
|
name_length = struct.unpack(">H", data[32:34])[0]
|
|
decoded_data = data[34 : 34 + name_length].decode().split(";")
|
|
|
|
try:
|
|
map_ = decoded_data[7]
|
|
except IndexError:
|
|
map_ = None
|
|
try:
|
|
gamemode = decoded_data[8]
|
|
except IndexError:
|
|
gamemode = None
|
|
|
|
return BedrockStatusResponse(
|
|
protocol=decoded_data[2],
|
|
brand=decoded_data[0],
|
|
version=decoded_data[3],
|
|
latency=latency,
|
|
players_online=decoded_data[4],
|
|
players_max=decoded_data[5],
|
|
motd=decoded_data[1],
|
|
map_=map_,
|
|
gamemode=gamemode,
|
|
)
|
|
|
|
def read_status(self) -> BedrockStatusResponse:
|
|
start = perf_counter()
|
|
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
s.settimeout(self.timeout)
|
|
|
|
s.sendto(self.request_status_data, (self.host, self.port))
|
|
data, _ = s.recvfrom(2048)
|
|
|
|
return self.parse_response(data, (perf_counter() - start))
|
|
|
|
async def read_status_async(self) -> BedrockStatusResponse:
|
|
start = perf_counter()
|
|
stream = None
|
|
|
|
try:
|
|
conn = asyncio_dgram.connect((self.host, self.port))
|
|
stream = await asyncio.wait_for(conn, timeout=self.timeout)
|
|
|
|
await asyncio.wait_for(stream.send(self.request_status_data), timeout=self.timeout)
|
|
data, _ = await asyncio.wait_for(stream.recv(), timeout=self.timeout)
|
|
finally:
|
|
if stream is not None:
|
|
stream.close()
|
|
|
|
return self.parse_response(data, (perf_counter() - start))
|
|
|
|
|
|
class BedrockStatusResponse:
|
|
class Version:
|
|
def __init__(self, protocol, brand, version):
|
|
self.protocol = protocol
|
|
self.brand = brand
|
|
self.version = version
|
|
|
|
def __init__(
|
|
self,
|
|
protocol,
|
|
brand,
|
|
version,
|
|
latency,
|
|
players_online,
|
|
players_max,
|
|
motd,
|
|
map_,
|
|
gamemode,
|
|
):
|
|
self.version = self.Version(protocol, brand, version)
|
|
self.latency = latency
|
|
self.players_online = players_online
|
|
self.players_max = players_max
|
|
self.motd = motd
|
|
self.map = map_
|
|
self.gamemode = gamemode
|