]> git.xonotic.org Git - xonotic/xonotic.git/commitdiff
slist: add game server query support
authorTimePath <andrew.hardaker1995@gmail.com>
Sun, 13 May 2018 10:11:01 +0000 (20:11 +1000)
committerTimePath <andrew.hardaker1995@gmail.com>
Sun, 13 May 2018 10:11:01 +0000 (20:11 +1000)
misc/infrastructure/python/slist/game.py [new file with mode: 0644]
misc/infrastructure/python/slist/main.py [new file with mode: 0644]
misc/infrastructure/python/slist/master.py

diff --git a/misc/infrastructure/python/slist/game.py b/misc/infrastructure/python/slist/game.py
new file mode 100644 (file)
index 0000000..1c838f8
--- /dev/null
@@ -0,0 +1,62 @@
+import uuid
+
+import attr
+
+from .utils import *
+
+HEADER = b"\xFF\xFF\xFF\xFF"
+
+
+@attr.s(auto_attribs=True, frozen=True, slots=True)
+class CLGetInfo(Writable):
+    def encode(self) -> bytes:
+        return HEADER + f"getinfo {uuid.uuid4()}".encode(UTF_8)
+
+
+@attr.s(auto_attribs=True, frozen=True, slots=True)
+class SVGetInfoResponse(Readable):
+    gamename: str
+    modname: str
+    gameversion: int
+    sv_maxclients: int
+    clients: int
+    bots: int
+    mapname: str
+    hostname: str
+    protocol: int
+    qcstatus: Optional[str]
+    challenge: Optional[str]
+    d0_blind_id: Optional[str] = None
+
+    @classmethod
+    @generator
+    def decode(cls) -> Generator[Optional["SVGetInfoResponse"], bytes, None]:
+        ret: Optional[SVGetInfoResponse] = None
+        while True:
+            buf: bytes
+            buf = yield ret
+            parts = buf.decode(UTF_8).split("\\")[1:]
+            pairs = zip(*[iter(parts)] * 2)
+            args = dict(pairs)
+            for k in ("gameversion", "sv_maxclients", "clients", "bots", "protocol"):
+                args[k] = int(args[k])
+            ret = SVGetInfoResponse(**args)
+
+
+SVMessage = Union[SVGetInfoResponse]
+
+
+@generator
+def sv_parse() -> Generator[Optional[SVMessage], bytes, None]:
+    getinfo_response = b"infoResponse\n"
+    ret: Optional[SVMessage] = None
+    while True:
+        buf: bytes
+        buf = yield ret
+        ret = None
+        if buf.startswith(HEADER):
+            buf = buf[len(HEADER):]
+            if buf.startswith(getinfo_response):
+                buf = buf[len(getinfo_response):]
+                ret = SVGetInfoResponse.decode().send(buf)
+                continue
diff --git a/misc/infrastructure/python/slist/main.py b/misc/infrastructure/python/slist/main.py
new file mode 100644 (file)
index 0000000..824eea6
--- /dev/null
@@ -0,0 +1,42 @@
+#!/usr/bin/env python3
+import logging
+from typing import *
+
+from . import game
+from . import master
+
+logger = logging.getLogger(__name__)
+
+if __name__ == "__main__":
+    import socket
+
+    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+
+    connection = Tuple[str, int]
+    connections: Dict[connection, Generator[Optional[Union[master.SVMessage, game.SVMessage]], bytes, None]] = {}
+    count_inforesponse = 0
+
+    q = master.CLGetServersExt(game="Xonotic", protocol=3)
+    conn = (socket.gethostbyname("dpmaster.deathmask.net"), 27950)
+    sock.sendto(q.encode(), conn)
+    connections[conn] = master.sv_parse()
+    while True:
+        logger.debug("wait")
+        data, addr = sock.recvfrom(1400)
+        logger.debug(f"recv({addr}): {data}")
+        msg = connections[addr].send(data)
+        if msg:
+            logger.info(f"recv({addr}): {msg}")
+            if isinstance(msg, master.SVGetServersExtResponse):
+                logger.info(f"servers: {len(msg.servers)}")
+                for srv in msg.servers:
+                    try:
+                        q_info = game.CLGetInfo()
+                        conn = (str(srv.addr), srv.port)
+                        sock.sendto(q_info.encode(), conn)
+                        connections[conn] = game.sv_parse()
+                    except socket.gaierror:
+                        pass
+            if isinstance(msg, game.SVGetInfoResponse):
+                count_inforesponse += 1
+                logger.info(f"status-{count_inforesponse}: {msg}")
index 12bfd220857b648b1c008f397a727fe2d602941b..62462881bc5669ac856769529a38772403e24427 100644 (file)
@@ -1,13 +1,10 @@
 import ipaddress
-import logging
 from struct import Struct
 
 import attr
 
 from .utils import *
 
-logger = logging.getLogger(__name__)
-
 HEADER = b"\xFF\xFF\xFF\xFF"
 
 
@@ -103,26 +100,3 @@ def sv_parse() -> Generator[Optional[SVMessage], bytes, None]:
                 if ret:
                     getservers_ext_gen = None
                 continue
-
-
-if __name__ == "__main__":
-    import socket
-
-    connection = Tuple[str, int]
-    connections: Dict[connection, Generator[Optional[SVMessage], bytes, None]] = {}
-
-    conn = (socket.gethostbyname("dpmaster.deathmask.net"), 27950)
-    connections[conn] = sv_parse()
-
-    sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-    q = CLGetServersExt(game="Xonotic", protocol=3)
-    sock.sendto(q.encode(), conn)
-    while True:
-        logger.debug("wait")
-        data, addr = sock.recvfrom(1400)
-        logger.debug(f"recv({addr}): {data}")
-        msg = connections[addr].send(data)
-        if msg:
-            logger.info(f"recv({addr}): {msg}")
-            if isinstance(msg, SVGetServersExtResponse):
-                logger.info(f"servers: {len(msg.servers)}")