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]>
43 lines
1.8 KiB
Python
Executable File
43 lines
1.8 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""All-time kill counter (bots and humans).
|
|
./kills show the scoreboard (and save the tally)
|
|
Counts the " killed " lines in hlds/cstrike/logs. Per-log counts are kept in kills-tally.json,
|
|
so the totals survive scripts/backup.sh deleting logs older than 30 days (it runs this first)."""
|
|
import collections, json, os, re
|
|
|
|
D = os.path.dirname(os.path.abspath(__file__))
|
|
LOGS = os.path.join(D, "hlds", "cstrike", "logs")
|
|
STATE = os.path.join(D, "kills-tally.json")
|
|
KILL = re.compile(r'^L (\d\d/\d\d/\d{4}) - [\d:]+: "(.+?)<\d+><[^>]*><[^>]*>" killed '
|
|
r'".+?<\d+><[^>]*><[^>]*>" with "([^"]+)"')
|
|
|
|
try:
|
|
st = json.load(open(STATE))
|
|
except (OSError, ValueError):
|
|
st = {"files": {}}
|
|
for f in sorted(os.listdir(LOGS)) if os.path.isdir(LOGS) else []:
|
|
if not f.endswith(".log"):
|
|
continue
|
|
n, who, wpn, days = 0, collections.Counter(), collections.Counter(), set()
|
|
for line in open(os.path.join(LOGS, f), encoding="utf-8", errors="replace"):
|
|
m = KILL.match(line)
|
|
if m:
|
|
n += 1; days.add(m[1]); who[m[2]] += 1; wpn[m[3]] += 1
|
|
if n or f not in st["files"]:
|
|
st["files"][f] = {"kills": n, "who": who, "wpn": wpn, "days": sorted(days)}
|
|
tmp = STATE + ".tmp"
|
|
json.dump(st, open(tmp, "w"))
|
|
os.replace(tmp, STATE)
|
|
|
|
total, who, wpn, days = 0, collections.Counter(), collections.Counter(), set()
|
|
for v in st["files"].values():
|
|
total += v["kills"]; who.update(v["who"]); wpn.update(v["wpn"]); days.update(v["days"])
|
|
print(f"\n ALL-TIME KILLS: {total:,} ({len(days)} days of carnage, {len(st['files'])} maps played)\n")
|
|
print(" top fraggers:")
|
|
for i, (n, k) in enumerate(who.most_common(5), 1):
|
|
print(f" {i}. {n:<18} {k:,}")
|
|
print("\n weapons of choice:")
|
|
for n, k in wpn.most_common(5):
|
|
print(f" {n:<18} {k:,}")
|
|
print()
|