Retention & Lifetime Cohorts
The RetentionTracker persists player visit history across sessions using DataStoreService. It automatically flags whether a joining user is a first-time player or a returning visitor, and records D1 through D30 retention milestones.
Dispatched Events
Section titled “Dispatched Events”1. PlayerFirstJoin
Section titled “1. PlayerFirstJoin”Emitted when a player visits the game for the very first time:
{ "name": "PlayerFirstJoin", "category": "retention", "timestamp": 1727790000, "userId": 12345678, "sessionId": "b8f67389-1065-4f76-871d-15ec43194511", "data": { "accountAgeDays": 140, "isNewPlayer": true }}2. PlayerReturnVisit
Section titled “2. PlayerReturnVisit”Emitted on return visits when an existing player re-enters the game:
{ "name": "PlayerReturnVisit", "category": "retention", "timestamp": 1727876400, "userId": 12345678, "sessionId": "e2a149b0-91fc-4bc2-9650-20cfd48507c9", "data": { "totalVisits": 2, "daysSinceLastVisit": 1.0, "daysSinceFirstJoin": 1, "lifetimePlaytimeHours": 0.5 }}3. PlayerRetentionMilestone
Section titled “3. PlayerRetentionMilestone”Emitted when a returning player crosses a milestone day threshold for the first time:
{ "name": "PlayerRetentionMilestone", "category": "retention", "timestamp": 1727876400, "userId": 12345678, "sessionId": "e2a149b0-91fc-4bc2-9650-20cfd48507c9", "data": { "milestone": "D1", "daysSinceFirstJoin": 1, "totalVisits": 2 }}Cohort Milestones
Section titled “Cohort Milestones”Milestones are calculated based on elapsed days since the player’s initial recorded join time:
| Milestone | Elapsed Days | Description |
|---|---|---|
| D1 | $\ge 1$ day (24 hours) | Returned the next day (Day 1 Retention) |
| D3 | $\ge 3$ days (72 hours) | Continued playing into the first week |
| D7 | $\ge 7$ days (168 hours) | Retained past week 1 (Day 7 Retention) |
| D14 | $\ge 14$ days (336 hours) | Retained into week 2 |
| D30 | $\ge 30$ days (720 hours) | Long-term active player (Day 30 Retention) |
Each milestone is claimed once per player and stored in their record (milestonesClaimed).
DataStore Record Schema
Section titled “DataStore Record Schema”Retention records are stored under AnalyticsModule_Retention_v1 using key tostring(player.UserId):
type RetentionRecord = { firstJoinTime: number, -- os.time() of first join lastJoinTime: number, -- os.time() of most recent visit totalVisits: number, -- Total visit count totalPlaytimeSeconds: number, -- Cumulative active session time milestonesClaimed: { [string]: boolean }, -- Map of claimed milestones: { ["D1"] = true }}- Offline Studio Fallback: If
DataStoreServiceis unavailable or disabled in Studio, the tracker runs in-memory so test sessions don’t error. - Server Shutdown: When the server terminates,
game:BindToCloseflushes all connected players’ playtime back into DataStore.
API Query Methods
Section titled “API Query Methods”| Method | Return Type | Description |
|---|---|---|
Analytics:IsNewPlayer(playerOrUserId) |
boolean |
Returns true if this is the player’s first visit. |
Analytics:GetTotalVisits(playerOrUserId) |
number |
Lifetime visit count. |
Analytics:GetLifetimePlaytimeSeconds(playerOrUserId) |
number |
Total playtime in seconds across all visits. |
Example: Welcoming New vs. Returning Players
Section titled “Example: Welcoming New vs. Returning Players”local Players = game:GetService("Players")
Players.PlayerAdded:Connect(function(player) -- Allow DataStore record to load task.wait(1)
if Analytics:IsNewPlayer(player) then print(player.Name .. " is a new player! Showing onboarding...") TutorialService:BeginFirstTimeOnboarding(player) else local visits = Analytics:GetTotalVisits(player) local hours = Analytics:GetLifetimePlaytimeSeconds(player) / 3600 print(string.format("Welcome back %s (Visit #%d, %.1f hrs played)", player.Name, visits, hours))
RewardService:GrantReturnBonus(player, visits) endend)