One-command setup (SteamCMD + pinned, checksum-verified ReHLDS / ReGameDLL / Metamod-R / AMXX / ReAPI / ReUnion / CSDM-ReAPI / YaPB / GunGame), .env settings, ./cs menu (map, DM/GunGame mode, bots, kill counter, restart, update), own plugins (extras, rtv, mode switch), all-time kill counter, watchdog + nightly backup, docs for admins and players. Co-Authored-By: Claude Opus 5.5 <[email protected]>
36 lines
1.4 KiB
Python
Executable File
36 lines
1.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Asks a CS 1.6 / GoldSrc server for its info (A2S_INFO), like the in-game server browser does.
|
|
python3 tools/hlq.py info <host> <port>
|
|
Prints: name | map | players x/y (bots n) | password yes/no. Exit code 1 when there's no answer.
|
|
Works against any address (LAN, VPN, a playit tunnel), so it also tests the way in from outside."""
|
|
import socket, sys
|
|
|
|
H = b"\xff\xff\xff\xff"
|
|
|
|
|
|
def info(host, port, timeout=4):
|
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
|
s.settimeout(timeout)
|
|
q = H + b"TSource Engine Query\x00"
|
|
s.sendto(q, (host, port))
|
|
d = s.recvfrom(65535)[0]
|
|
if d[4:5] == b"A": # newer servers answer with a challenge first: ask again with it
|
|
s.sendto(q + d[5:9], (host, port))
|
|
d = s.recvfrom(65535)[0]
|
|
if d[4:5] != b"I":
|
|
raise ValueError("unexpected reply type %r" % d[4:5])
|
|
name, mp, folder, game, rest = d[6:].split(b"\x00", 4)
|
|
players, maxp, bots, pw = rest[2], rest[3], rest[4], rest[7]
|
|
return (f"{name.decode(errors='replace')} | map {mp.decode()} | players {players}/{maxp} (bots {bots})"
|
|
f" | password {'yes' if pw else 'no'}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) != 4 or sys.argv[1] != "info":
|
|
sys.exit(__doc__)
|
|
try:
|
|
print("A2S_INFO ok:", info(sys.argv[2], int(sys.argv[3])))
|
|
except (OSError, ValueError) as e:
|
|
print("no answer:", e)
|
|
sys.exit(1)
|