Skip to content

com.stratkit.user-activity-tracker

Tracks per-game-round user activity for use by triggers (e.g. rate-us popup).

What it tracks

Each field is scoped to the current game round (GameId), except AccountAgeDay which is account-wide.

Field Description
AccountAgeDay Days since user registration
SessionCount Number of sessions started in this round
SessionLength Cumulative play time in this round across all sessions (seconds)
DailyLoginStreak Consecutive in-game-days the player logged into this round
PointsTrend Decreasing / Stable / Increasing derived from per-day ranking points history
DailyHistory Ring buffer of per-day ranking points snapshots

Persisted to PlayerPrefs keyed by userId (one entry per account, not per round). Joining a different game round resets the round-scoped counters and overwrites the same key. Data is local-only, not cross-platform.

How it works

Three systems run in the persistent world:

  • UserActivityPersistenceSystem — loads the log on user-ready; writes back when UserActivityLogDirty tag is present.
  • UserActivityUpdateSystem — runs every SessionUpdateInterval. One pass handles day rollover (streak, ranking snapshot, trend recompute), session expiry, session-length extension, and dirty-tag emission.
  • UserInactivityStateUpdateSystem — runs every frame; stamps LastInteractionTimestamp on any input edge and additionally stamps LastUserActionTimestamp when a ServerCommandRequest exists.

Ranking points come from CurrentDayPlayerRankingPointsComponent (set per player by RankingPointsPlayerResolverSystem in com.stratkit.newspaper-state-loader).

Inactivity tracking (busy / idle gate)

In addition to the day-level log, the package exposes a UserInactivityStateComponent singleton with two timestamps:

  • LastInteractionTimestamp — last UI click / tap / key (legacy hup.lastClick).
  • LastUserActionTimestamp — last in-game command issued (legacy lastUserActionTime).

UserInactivityStateUpdateSystem writes both timestamps automatically — no host wiring needed. PositionActionComponent (from com.stratkit.user-input-actions) bumps LastInteractionTimestamp; ServerCommandRequest (from com.stratkit.game-state-loader) additionally bumps LastUserActionTimestamp.

Consumers query idle state via:

InactivityKind kind = UserActivityTrackerUtils.GetInactivityKind(state, config, inGame, now);

which returns the matching InactivityKind (or None when active) once either threshold has elapsed (NoInteractionTimeout = 30s and, in-game only, NoActionTimeout = 15s). Mirrors legacy ReviewTrigger.onUserInactive / onUserInactiveNoAction.

Enable / disable

UserActivityTrackerUtils.Disable() stops all ticking and IO; Enable() resumes. The state is persisted to PlayerPrefs and re-applied at module setup, so a disable survives app restarts (e.g. once rate-us no longer needs the data after the player has rated).

Consumers

Read UserActivityLogComponent from the persistent world. The PointsTrend is the most commonly gated-on field — it's set to Unknown when there's no in-game data yet (uber / lobby), Stable when only one day of data exists.

Setup

Create a UserActivityTrackerModule asset and add it to the persistent world module list. Config values are edited directly on the module asset (UserActivityTrackerConfig is an embedded [Serializable] struct — no separate config asset).