Playtime from PlaytimeHistory's database (SAW_PT_DSN)

A public Playtime tab and player-drawer playtime, and staff-only figures
(who's on, staff activity, disconnect reasons, countries, names, sessions)
for @css/generic on the staff panel and on the public site when signed in.
Hidden unless SAW_PT_DSN is set and its tables are there.
This commit is contained in:
Astra 2026-10-04 21:05:57 +01:00
parent daa7ec2dbc
commit 6d0fc6e463
18 changed files with 2238 additions and 12 deletions

View file

@ -13,6 +13,10 @@ A SourceBans-style web panel for [CS2-SimpleAdmin](https://github.com/daffyyyy/C
and the game server going down, on phones and computers, with quiet hours (see below).
- **Verification** (optional): who our GroupVerify plugin verifies, with overrides to give and
remove, and its settings. Only appears when GroupVerify is installed (see below).
- **Playtime** (optional): who plays the most, when the server is busy, and each player's time,
stats and maps, from our PlaytimeHistory plugin's database. Staff also see who's on with their
location, staff activity, disconnect reasons and players' name and session history. Only appears
when `SAW_PT_DSN` is set (see below).
Live data comes from the **WebPanelBridge** plugin (`plugins/WebPanelBridge` in this repo) over
RCON. Without it the panel still works, but shows the server as offline and can't kick or warn.
@ -48,6 +52,8 @@ RCON. Without it the panel still works, but shows the server as offline and can'
| Raw console commands (Server settings > Console; sent without waiting, output only logged) | `@css/rcon` |
| See the Staff & ranks page | `@zio/webpanel` |
| Give and remove verification overrides | `@zio/verify` (not on themselves, unless Root) |
| Staff playtime figures (Playtime page, player drawers, and on the public site when signed in) | `@css/generic` |
| PlaytimeHistory's own status on the Playtime page | `@css/root` |
| Ranks, staff, SimpleAdmin settings, reload admins, GroupVerify's group and on/off | `@css/root` |
`@css/root` grants everything. Nobody can act on a player whose immunity is higher than theirs,
@ -138,6 +144,43 @@ GroupVerify (0.3.0+) checks each player with the Steam Web API as they join, so
members" is everyone Steam has said is in the group when they joined, not the group's full member
list. The panel never asks Steam itself.
### Playtime (PlaytimeHistory, optional)
[PlaytimeHistory](https://git.zio.sh/cs2/PlaytimeHistory) (`plugins/PlaytimeHistory` in the cs2
repo) records sessions, team and alive/dead time, maps, stats, ping and location in **its own
database**, not SimpleAdmin's. To show it, give the panel a second connection, ideally as a user
that can only read:
```sql
CREATE USER 'pt_web'@'<panel host>' IDENTIFIED BY '<password>';
GRANT SELECT ON playtime_history.* TO 'pt_web'@'<panel host>';
```
```
SAW_PT_DSN=pt_web:<password>@tcp(<db-host>:3306)/playtime_history
```
- **Nothing shows** without `SAW_PT_DSN`, or while that database is down or doesn't have
PlaytimeHistory's tables. The panel checks once a minute and logs when that changes; it starts
and runs normally either way.
- **Public site**: a Playtime tab (once something's recorded) with the top players for the last 7
days, 30 days or all time (time on T or CT; spectating doesn't count), the last 7 days against
the 7 before, the busiest hours, players per day, a day-by-hour heatmap and maps. Player pages
get time played with the T/CT/spectating split, the last 30 days, K/D, headshots, damage per
round, MVPs and favourite maps.
- **Staff** with `@css/generic` (PlaytimeHistory's own default for looking up others) get, on the
staff panel's Playtime page and on the public site while signed in: who's on now with how long,
team, ping and location; each staff member's time in the last 7 or 30 days; disconnect reasons;
countries; and in player drawers, the names they've used, recent sessions, location and ping.
Root also sees whether the plugin is loaded and what `css_pt_status` says. Who's on comes from
the live status, so admins hidden with `css_hide` stay hidden.
- **Days and hours of the day** are in the viewer's own time zone (the browser sends it). An hour
goes to the day it starts on, which is exact for zones a whole number of hours from UTC.
- **Locations** come from DB-IP City Lite, credited (CC BY 4.0) wherever they're shown. No IP
addresses are stored.
- `SAW_PT_SERVER_ID` limits it to one `pt_servers.id` when several servers share the database.
All-time totals (pt_players) always cover every server.
### Name history (optional)
SimpleAdmin keeps one row per SteamID + IP in `sa_players_ips` and overwrites its name on each
@ -243,6 +286,16 @@ podman run -d --rm --name sa-test -e MYSQL_ROOT_PASSWORD=test -e MYSQL_DATABASE=
SAW_TEST_DSN='root:test@tcp(127.0.0.1:33061)/sa?parseTime=true&loc=UTC' go test ./internal/web -run VerifyDB
```
`internal/playtime/playtime_db_test.go` runs the playtime queries against a real MySQL or MariaDB
when `SAW_TEST_PT_DSN` is set. It drops and recreates PlaytimeHistory's tables from
`internal/playtime/testdata/schema.sql` (a copy of the plugin's migration; keep it in step), so
give it a scratch database:
```
podman run -d --rm --name pt-test -e MARIADB_ROOT_PASSWORD=test -e MARIADB_DATABASE=playtime_history -p 127.0.0.1:33071:3306 mariadb:11
SAW_TEST_PT_DSN='root:test@tcp(127.0.0.1:33071)/playtime_history' go test ./internal/playtime -run DB
```
`internal/web/notify_db_test.go` does the same for notifications (`-run NotifyDB`): the endpoints,
and the watcher sending to a fake push service. It needs only SimpleAdmin's migrations, and empties
its ban, mute, admin and group tables and the panel's push tables.