Player Activity Tracking¶
The player-activity layer records session and gameplay telemetry in Redis with TTL-based expiration. It adds no database tables: it leans on Redis for hot, ephemeral data and on the existing Player.last_game_login column for the one durable signal it needs.
Implemented in services/gameserver/src/services/player_activity_service.py (PlayerActivityService), reached through the module-level singleton accessor get_player_activity_service(). Each method lazily resolves the shared RedisService.
What is tracked¶
Two kinds of writes flow through the service:
- Session lifecycle —
track_loginopens a session;track_logoutfinalises it, computingduration_secondsand folding the session into the rolling and daily summaries. Login and logout are wired into the auth routes (services/gameserver/src/api/routes/auth.py), mapping the authenticated user to itsPlayerrow; admins and non-player users are skipped, and any tracking failure is swallowed so it can never break auth. - Gameplay events —
track_activity(player_id, event_type, details)increments the live session counters and appends an event record. The recognised event types (ActivityEventType) arelogin,logout,trade_buy,trade_sell,combat_attack,combat_defend,sector_move,dock,undock,planet_land, andwarp. Trade events add totrades_countandtrade_volume(fromdetails["total_value"]); combat events add tocombat_events;sector_moveappends to a deduplicatedsectors_visitedlist capped at the last 100 entries.
Every write also pushes an individual event onto the player's event list (_record_event), trimmed to the most recent 500 entries.
Redis keys and TTLs¶
| Key | Contents | TTL |
|---|---|---|
activity:session:{player_id} |
Current session: login/last-activity timestamps and live counters | 24 hours |
activity:events:{player_id} |
Most recent events (list, capped at 500) | 7 days |
activity:summary:{player_id} |
Rolling per-player summary: total sessions, playtime, actions, trades, trade volume, combat events, unique sectors | 30 days |
activity:daily:{player_id}:{date} |
Per-day aggregates (UTC date) | 14 days |
activity:online_players |
Set of currently-online player IDs | no TTL (managed by add on login / remove on logout) |
The session key is added to activity:online_players on login and removed on logout; get_online_player_count and get_online_player_ids read that set. The session key itself is deleted on logout.
Persistence is Redis-only¶
All activity data lives in Redis and is not durable. Counters and summaries are written with SETEX (RedisService.cache_set), so they expire on the TTLs above and are lost on a Redis flush or data loss. There is no write-back into Postgres from this service. The only database touch is the optional refresh of Player.last_game_login inside track_login, which the auth wiring does not currently pass a session to.
Consequences:
- The online-player set, session counters, summaries, and per-day aggregates reset to zero on Redis loss.
- The longest-lived record is the 30-day rolling summary; per-day aggregates persist 14 days, raw events 7 days, and the live session 24 hours.
- Read accessors (
get_player_session,get_player_summary,get_recent_events,get_daily_stats) return zeroed defaults when the corresponding key has expired or never existed, rather than erroring.
Source map¶
| Concern | Path |
|---|---|
| Activity service | services/gameserver/src/services/player_activity_service.py |
Redis cache primitives (cache_set/cache_get/cache_delete) |
services/gameserver/src/services/redis_service.py |
| Login / logout wiring | services/gameserver/src/api/routes/auth.py |
| Durable last-login column | services/gameserver/src/models/player.py (Player.last_game_login) |
Related¶
OPERATIONS/retention.md— engagement metrics and the wider retention machinery that consume activity telemetry.OPERATIONS/realtime.md— presence and the Redis-backed real-time layer.