Files
esefandClaude Opus 5.5 7e9cbee14d cs16-dm-server: CS 1.6 deathmatch server in Docker
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]>
2026-10-11 05:03:07 +03:00

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)