Files
sampy/sampy/client/query.py
2021-03-25 00:00:39 +01:00

119 lines
3.8 KiB
Python

import socket
import struct
from .base import BaseClient
from ..shared import glob
import logging
logger = logging.getLogger(__name__)
class QueryClient(BaseClient):
def __init__(self, server: "__ServerInstance__", ip: str, port: int):
super().__init__(server, ip, port)
logger.debug("Client resolved to QueryClient")
self.handlers = {
b"i": self.query_i,
b"r": self.query_r,
b"c": self.query_c,
b"d": self.query_d,
b"p": self.query_p,
b"x": self.query_x
}
async def on_packet(self, packet: bytes):
logger.debug("on_packet(%s)" % packet)
if len(packet) <= 10: # Invalid
return
if packet[10:11] in self.handlers:
packet = await self.handlers[packet[10:11]](packet)
if len(packet): # Send packet back if not 0
await self.send(packet)
async def query_i(self, packet: bytes) -> bytes:
len_hostname = len(self.server.config.hostname)
len_mode = len(self.server.config.mode)
len_language = len(self.server.config.language)
return packet + struct.pack(b"<?HHI%dsI%dsI%ds" % (len_hostname, len_mode, len_language),
len(self.server.config.password) != 0,
len(await self.server.get_online_players()),
self.server.config.max_players,
len_hostname,
self.server.config.hostname.encode(),
len_mode,
self.server.config.mode.encode(),
len_language,
self.server.config.language.encode()
)
async def query_r(self, packet: bytes) -> bytes:
data = []
rules = await self.server.get_rules()
data.append(len(rules))
for k, v in rules.items():
data += [
len(k), k,
len(v), v
]
return packet + struct.pack(b"<H" + (b"B%dsB%ds" * data[0]) % tuple(len(y) for x in rules.items() for y in x), *data)
async def query_c(self, packet: bytes) -> bytes:
data = []
scores = await self.server.get_players_scores()
data.append(len(scores))
for k, v in scores.items():
data += [
len(k), k,
v
]
return packet + struct.pack(b"<H" + (b"B%dsI" * data[0]) % tuple(len(x) for x in scores.keys()), *data)
async def query_d(self, packet: bytes) -> bytes:
data = []
players = await self.server.get_online_players()
data.append(len(players))
for p in players:
data += [
p["id"],
len(p["nick"]), p["nick"],
p["score"],
p["ping"]
]
return packet + struct.pack(b"<H" + (b"BB%dsII" * data[0]) % tuple(len(p["nick"]) for p in players), *data)
async def query_p(self, packet: bytes) -> bytes:
return packet
async def query_x(self, packet: bytes) -> bytes:
len_pswd, = struct.unpack_from(b"<H", packet, 11)
pswd, len_cmd = struct.unpack_from(b"<%dsH" % len_pswd, packet, 13)
cmd = struct.unpack_from(b"<%ds" % len_cmd, packet, 15 + len_pswd)
if len(self.server.config.rcon_password) == 0:
msg = b"Remote Console is not enabled on this server."
elif self.server.config.rcon_password.encode() != pswd:
msg = b"Invalid RCON password."
logger.warning("BAD RCON ATTEMPT BY: %s:%d" % (self.ip, self.port))
else:
# TODO: Add rcon client to command stdouts
return b"" # No response as all is ok
return packet[:11] + struct.pack(b"<H%ds" % len(msg), len(msg), msg)