Source code for mcstatus.responses.query
from __future__ import annotations
from dataclasses import asdict, dataclass
from typing import Any, TYPE_CHECKING
from mcstatus.motd import Motd
if TYPE_CHECKING:
from typing_extensions import Self
from mcstatus.responses._raw import RawQueryResponse
__all__ = [
"QueryPlayers",
"QueryResponse",
"QuerySoftware",
]
[docs]
@dataclass(frozen=True)
class QueryResponse:
"""The response object for :meth:`JavaServer.query() <mcstatus.server.JavaServer.query>`."""
raw: RawQueryResponse
"""Raw response from the server.
This is :class:`~typing.TypedDict` actually, please see sources to find what is here.
"""
motd: Motd
"""The MOTD of the server. Also known as description.
.. seealso:: :doc:`/api/motd_parsing`.
"""
map_name: str
"""The name of the map. Default is ``world``."""
players: QueryPlayers
"""The players information."""
software: QuerySoftware
"""The software information."""
ip: str
"""The IP address the server is listening/was contacted on."""
port: int
"""The port the server is listening/was contacted on."""
game_type: str = "SMP"
"""The game type of the server. Hardcoded to ``SMP`` (survival multiplayer)."""
game_id: str = "MINECRAFT"
"""The game ID of the server. Hardcoded to ``MINECRAFT``."""
@classmethod
def build(cls, raw: RawQueryResponse, players_list: list[str]) -> Self:
return cls(
raw=raw,
motd=Motd.parse(raw["hostname"], bedrock=False),
map_name=raw["map"],
players=QueryPlayers.build(raw, players_list),
software=QuerySoftware.build(raw["version"], raw["plugins"]),
ip=raw["hostip"],
port=int(raw["hostport"]),
game_type=raw["gametype"],
game_id=raw["game_id"],
)
[docs]
def as_dict(self) -> dict[str, Any]:
"""Return the dataclass as JSON-serializable :class:`dict`.
Do note that this method doesn't return :class:`string <str>` but
:class:`dict`, so you can do some processing on returned value.
Difference from
:attr:`~mcstatus.responses.JavaStatusResponse.raw` is in that,
:attr:`~mcstatus.responses.JavaStatusResponse.raw` returns raw response
in the same format as we got it. This method returns the response
in a more user-friendly JSON serializable format (for example,
:attr:`~mcstatus.responses.BaseStatusResponse.motd` is returned as a
:func:`Minecraft string <mcstatus.motd.Motd.to_minecraft>` and not
:class:`dict`).
"""
as_dict = asdict(self)
as_dict["motd"] = self.motd.simplify().to_minecraft()
as_dict["players"] = asdict(self.players)
as_dict["software"] = asdict(self.software)
return as_dict
[docs]
@dataclass(frozen=True)
class QueryPlayers:
"""Class for storing information about players on the server."""
online: int
"""The number of online players."""
max: int
"""The maximum allowed number of players (server slots)."""
list: list[str]
"""The list of online players."""
@classmethod
def build(cls, raw: RawQueryResponse, players_list: list[str]) -> Self:
return cls(
online=int(raw["numplayers"]),
max=int(raw["maxplayers"]),
list=players_list,
)
[docs]
@dataclass(frozen=True)
class QuerySoftware:
"""Class for storing information about software on the server."""
version: str
"""The version of the software."""
brand: str
"""The brand of the software. Like `Paper <https://papermc.io>`_ or `Spigot <https://www.spigotmc.org>`_."""
plugins: list[str]
"""The list of plugins. Can be an empty list if hidden."""
@classmethod
def build(cls, version: str, plugins: str) -> Self:
brand, parsed_plugins = cls._parse_plugins(plugins)
return cls(
version=version,
brand=brand,
plugins=parsed_plugins,
)
@staticmethod
def _parse_plugins(plugins: str) -> tuple[str, list[str]]:
"""Parse plugins string to list.
Returns:
:class:`tuple` with two elements. First is brand of server (:attr:`.brand`)
and second is a list of :attr:`plugins`.
"""
brand = "vanilla"
parsed_plugins = []
if plugins:
parts = plugins.split(":", 1)
brand = parts[0].strip()
if len(parts) == 2:
parsed_plugins = [s.strip() for s in parts[1].split(";")]
return brand, parsed_plugins