Skip to content

Commit 5b6cb4e

Browse files
Add the progression site design
1 parent b8deb9b commit 5b6cb4e

1 file changed

Lines changed: 136 additions & 0 deletions

File tree

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# CounterStrikeProgression site design
2+
3+
## Intent
4+
5+
The CS2 server's players want to see their progression outside the game. The site at
6+
`cs2-progression.ktsu.dev` shows what is happening on the server right now, and lets anyone look up
7+
the server-wide ranks and any player's full record. It's public and read-only. Players open it on a
8+
second screen or a phone while they play, or afterwards to see how they're doing.
9+
10+
## Goals
11+
12+
- **Live match:** the current map and game mode, the player count, and a scoreboard of everyone on the
13+
server with their name, rank, kills, deaths, and XP earned this map. It updates as it happens.
14+
- **XP feed:** skill shots and rank-ups scroll in as players earn them, for example
15+
"Katsu · Noscope · AWP +40". When the page opens, the feed already shows the last 50 entries.
16+
- **Server ranks:** every human who has played, ordered by rank XP, with their rank, total XP, and when
17+
they were last on. Players on the server right now show "online". Each name links to their player page.
18+
- **Player page:** one player's rank, a progress bar to the next rank, total XP, first seen and last
19+
seen, a table with one row per weapon (level, kills, headshot percentage, accuracy), and each weapon's
20+
challenge tiers, with finished tiers highlighted.
21+
- **Every page is live.** XP and stats climb on screen while the player is playing, without a reload.
22+
Lifetime totals, weapon stats, and challenge tiers update within a few seconds, not on the plugin's
23+
database save.
24+
25+
## Non-goals
26+
27+
- No login and no accounts. Every page is public.
28+
- No admin actions. The CSS-BANS panel at `cs2-admin.ktsu.dev` covers administration.
29+
- No match history beyond the current map. The scoreboard resets on map change.
30+
- No graphs or history over time in the first version.
31+
32+
## Anti-goals
33+
34+
- The site must never slow down or break the game server. If the site is down, slow, or
35+
misconfigured, the plugin carries on exactly as it does today.
36+
- The site must never be able to change progression data. It reads the database with a read-only
37+
user.
38+
- Nothing shown may reveal anything a player couldn't already see in game (no SteamIDs beyond what a
39+
Steam profile link needs, no IPs).
40+
41+
## Look
42+
43+
The game HUD style, chosen from mockups: dark backgrounds (#0d0f12, #15181d), a monospace face, orange
44+
accents (#f2a33a) for headers and rank badges, and gold (#f2c94c) for highlights such as skill shots,
45+
rank-ups, and finished challenge tiers. It matches the in-game floaties, whose highlight color is the
46+
same gold. Online players are marked in green. It must work on a phone. The mockups are in
47+
`.superpowers/brainstorm/` in this repo and are a guide, not a pixel spec.
48+
49+
## How it's built
50+
51+
These were decided in conversation.
52+
53+
- **An ASP.NET Core app in this repo**, built the ktsu way under `ktsu.Sdk`, alongside the plugin. Follow
54+
GitLfsCache's pattern (`C:/dev/ktsu-dev/GitLfsCache`): an ASP.NET project under plain
55+
`Microsoft.NET.Sdk` plus `ktsu.Sdk` with a `FrameworkReference`, `net10.0` only, and a container image
56+
through SDK container support (`EnableSdkContainerSupport`, the chiseled `aspnet:10.0` base, linux-x64
57+
and linux-arm64). The image is published to GHCR by CI, the way GitLfsCache's is.
58+
- **Ranks and player pages read the MariaDB** the plugin already writes: `csp_players`, `csp_weapons`,
59+
and `csp_challenges` in the `cs2` database that SimpleAdmin also uses.
60+
- **Live data comes from the plugin.** The database only holds lifetime totals, so the plugin sends
61+
events to the site as they happen, and the site pushes them on to open browsers.
62+
- **It runs as its own pod in the homelab**, deployed by Terraform in `matt-edmondson/homelab`.
63+
64+
## Constraints
65+
66+
- The plugin builds against CounterStrikeSharp v1.0.376 on .NET 10 with `ktsu.Sdk`. Warnings are errors
67+
and code style is strict (see `CLAUDE.md`).
68+
- All game state is read on the game thread. Anything that leaves the plugin must be captured there and
69+
sent from the thread pool.
70+
- The Core library holds the rank ladder, the weapon catalog, the skill shot names, and the challenge
71+
definitions. The site uses them from Core rather than copying them, so names and thresholds can't
72+
drift. Core is `internal`, so it needs another `InternalsVisibleTo` for the site.
73+
- The site project must not be packed to NuGet, like the plugin. See `Directory.Build.targets`.
74+
- The release zip for the server stays plugin-only. The site ships as an image.
75+
76+
## Plugin side
77+
78+
- A new optional `Site` section in `PluginConfig`, with the site's URL and a shared token. With no URL
79+
set, nothing is sent, which is the default.
80+
- The plugin sends two kinds of message:
81+
- **A live snapshot** of the server: map, game mode, the player list with each player's slot, name,
82+
SteamID, rank, kills, deaths, XP this map, and whether they're a bot. It's sent every couple of
83+
seconds and straight after a map change.
84+
- **Lifetime progress for online humans,** so player pages and the ranks page are live within
85+
seconds rather than waiting for the plugin's once-a-minute database save (the user's choice). This
86+
covers rank XP, rank, and each weapon's and challenge's rows. Sending only what changed since the
87+
last message keeps it small. The site overlays these on what it reads from the database, and the
88+
newer value wins.
89+
- **Feed events:** a skill shot (player, skill shot name, weapon, XP) and a rank-up (player, new rank).
90+
Weapon level-ups and finished challenge tiers are good candidates for the feed too, in the
91+
highlight color.
92+
- Sending is fire and forget: a bounded queue drained on the thread pool, with a short timeout. When the
93+
queue is full or the site doesn't answer, messages are dropped and a failure is logged at most once a
94+
minute. A save never waits on the site.
95+
- Kills, deaths, and XP this map need counting per player per map, since nothing tracks them today
96+
outside the database totals.
97+
- The rank ladder, weapon level curve, and challenge tiers can be changed in the plugin's
98+
`Progression` settings. The plugin sends the values it's using, and the site draws progress bars and
99+
"next tier" targets from them. Until it has heard from the plugin, the site uses Core's defaults. This
100+
was the design author's call.
101+
102+
## Site side
103+
104+
- An endpoint for the plugin's messages, which rejects any request without the shared token. It's
105+
reachable inside the cluster only, never through the public route.
106+
- The latest snapshot is kept in memory. The feed's history is kept so a page opened, or a site
107+
restart, still shows the last 50 entries.
108+
- Browsers get live updates over a server push channel. Snapshots and feed entries go to every open
109+
page. The ranks page and player pages refresh what changed, so an online player's XP climbs.
110+
- When no snapshot has arrived for a while, the live page shows the server as offline instead of a
111+
frozen scoreboard.
112+
- Player pages are addressed by SteamID64, so a link survives a name change.
113+
114+
## Homelab side
115+
116+
- Terraform in the homelab repo next to `cs2.tf`: a Deployment, a ClusterIP Service, a public Traefik
117+
route at `cs2-progression.<domain>` behind crowdsec only (no oauth), the `.local` bypass route, and an
118+
Azure DNS record, like the other public sites.
119+
- A read-only MariaDB user for the site with `SELECT` on the `csp_*` tables, created and kept in step by
120+
Terraform, with its password from `terraform.tfvars`.
121+
- The plugin config's `Site` section is rendered by Terraform into the existing
122+
`CounterStrikeProgression.json` secret, pointing at the site's in-cluster Service.
123+
- A homepage tile, like the CS2 server and admin panel tiles.
124+
- The homelab `CLAUDE.md` gets an entry for the new file.
125+
126+
## Left to the builder
127+
128+
- The push channel (server-sent events or SignalR), and the wire format between the plugin and the site.
129+
- Where the feed history lives (a site-owned table in the MariaDB, or the site's own storage), as long as
130+
it survives a site restart.
131+
- Whether the pages are server-rendered with live patches or a small client-side app. Keep the
132+
front-end free of a build step if that's practical.
133+
- Caching of ranks and player pages, sized for a 16-player server and a few hundred players in total.
134+
- Page and endpoint names.
135+
- Tests: MSTest for the parts that can be tested without a game server, such as snapshot and feed
136+
handling, ordering, and the token check.

0 commit comments

Comments
 (0)