Add player position tracking relay + rendering (Phase 7b)

Relays the mod's throttled player_positions roster over a new
/ws/players/:serverId gateway (Redis pub/sub + snapshot key so a
tab connecting between mod flushes isn't empty), gated per-server
by a new playerPositionsVisible admin toggle independent of the
mod's own tracking config. Frontend renders the roster as map
markers with a show/hide toggle and online count. Covered by unit
tests (players.test.ts, ws-gateway.test.ts, admin.test.ts) and a
new e2e spec that plays the real mod WS protocol from inside a
browser context.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015tKdPZt78zbPUZMXWzKEKt
This commit is contained in:
2026-08-09 20:07:11 +02:00
parent cc3860ce08
commit 826233e10c
16 changed files with 444 additions and 16 deletions
+4 -1
View File
@@ -37,7 +37,7 @@ html(lang="en")
input.flex-1.bg-neutral-900.text-sm.px-2.py-1.rounded.border.border-neutral-700(
type="text" x-model="server.name" data-testid="admin-server-name")
span.text-xs.text-neutral-500.font-mono.truncate(style="max-width: 14rem;" x-text="server.token" data-testid="admin-server-token")
div.grid.grid-cols-3.gap-2
div.grid.grid-cols-4.gap-2
select.bg-neutral-900.text-xs.px-2.py-1.rounded.border.border-neutral-700(
x-model="server.authMode" data-testid="admin-server-authmode")
option(value="online") online
@@ -50,6 +50,9 @@ html(lang="en")
label.flex.items-center.gap-1.text-xs.text-neutral-300
input(type="checkbox" x-model="server.anonymousChatAllowed" data-testid="admin-server-anonchat")
| anonymous chat
label.flex.items-center.gap-1.text-xs.text-neutral-300
input(type="checkbox" x-model="server.playerPositionsVisible" data-testid="admin-server-playerpositions")
| player positions
div.flex.gap-2
button.px-2.py-1.bg-emerald-700.rounded.text-xs(x-on:click="saveServer(server)" data-testid="admin-server-save") Save
button.px-2.py-1.bg-red-800.rounded.text-xs(x-on:click="removeServer(server)" data-testid="admin-server-delete") Delete
+8
View File
@@ -51,6 +51,14 @@ html(lang="en")
button.px-2.py-1.bg-neutral-700.rounded.text-xs(x-on:click="cancelMarkerForm" data-testid="marker-cancel") Cancel
p.text-xs.text-amber-400(x-show="markerStatus" x-text="markerStatus")
aside.w-80.flex.flex-col.border-l.border-neutral-700.bg-neutral-800(x-show="server")
div.border-b.border-neutral-700.p-2.flex.items-center.justify-between
h2.text-sm.font-semibold Players
div.flex.items-center.gap-2
span.text-xs.text-neutral-400(x-text="onlinePlayers.length + ' online'" data-testid="players-online-count")
label.flex.items-center.gap-1.text-xs.text-neutral-300
input(type="checkbox" x-model="showPlayers" x-on:change="onShowPlayersChange" data-testid="players-toggle")
| show
div.border-b.border-neutral-700.p-2.space-y-2
div.flex.items-center.justify-between
h2.text-sm.font-semibold Region export