#!/usr/bin/env python3 """Server console from the terminal (the RCON password never leaves this machine). ./rcon status one command ./rcon interactive prompt (Ctrl+D or 'exit' to quit) Reads the port from .env and the password from secrets/rcon-password.txt.""" import os, socket, sys D = os.path.dirname(os.path.abspath(__file__)) env = {} for line in open(os.path.join(D, ".env"), encoding="utf-8"): k, _, v = line.strip().partition("=") env[k] = v PW = open(os.path.join(D, "secrets", "rcon-password.txt")).read().strip() ADDR = ("127.0.0.1", int(env.get("CS_PORT") or 27015)) H = b"\xff\xff\xff\xff" def rcon(cmd): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.settimeout(3) try: s.sendto(H + b"challenge rcon\n", ADDR) ch = s.recvfrom(4096)[0].split()[2] except (OSError, IndexError): return "(no answer: server down or changing map?)" s.sendto(H + b"rcon " + ch + b' "' + PW.encode() + b'" ' + cmd.encode(), ADDR) out = [] while True: # long replies (status, cvarlist) come in several packets try: data = s.recvfrom(65535)[0] except OSError: break out.append(data[5:].decode(errors="replace") if data[:4] == H else data.decode(errors="replace")) s.settimeout(0.4) text = "".join(out) # never show passwords (a map change prints the whole cvar list) return "\n".join(l for l in text.splitlines() if "rcon_password" not in l and "sv_password" not in l) or "(ok, no output)" if __name__ == "__main__": if len(sys.argv) > 1: print(rcon(" ".join(sys.argv[1:]))) sys.exit() print("CS 1.6 console: status, changelevel de_nuke, yb_quota 6, esef_mode gg, say hi. Ctrl+D to quit.") while True: try: line = input("cs16> ").strip() except (EOFError, KeyboardInterrupt): print() break if line in ("exit", "quit"): break if line: print(rcon(line))