One source of truth per metric
Combat metrics often appear in multiple event streams. Define a small ledger that records which stream owns each value.
| Metric | Typical live source | Final correction |
|---|---|---|
| Direct damage | Processed battle feedback or summary events. | Final result record when hidden or delayed values are possible. |
| Assist | Recognized assist categories from battle feedback. | Final result totals when required by the feature. |
| Vehicle health | Entity or health-component updates. | Usually not a persisted metric. |
| Session battles and wins | Difference between current synchronized season totals and a local baseline. | Resynchronize after the lobby dossier becomes current. |
| Rating | The owning mode controller. | Controller or dossier refresh; do not infer from battle outcome. |
Derived metrics need game-specific rules
Do not assume that all related counters are additive. Two counters exposed by the same event may represent alternative views of one source, mutually exclusive categories, corrected estimates, or values that are only meaningful after the final results screen.
Mission averages can divide by a counter goal
The personal-mission value-progress builder selects an average wrapper and strategy for an AVERAGE description. The wrapper resolves its linked counter by ID from the progress cache. Its displayed values then use AverageProgressGetter, which divides by counter.getGoal(), not the counter's current progress.
For synthetic cumulative value 200, total goal 1000 and linked counter goal 10, the displayed current/goal pair is 20 / 100. Changing that counter's current value does not change this denominator. The incomplete path uses the capped current projection before division; completed state projects the target, and results are rounded to one decimal. This is a display calculation, not an observed average over completed attempts or a reward receipt.
A missing counter or zero counter goal logs a configuration problem and produces fallback goal 1.0 and current 0.0. Those values do not establish a real target of one or observed zero contribution. Selected readable strategies were checked with inert progress/counter records, symbolic states and a logging sink; full quest configuration, cache construction, client rendering, Python 2 rounding ties and server progress were not exercised.
Distinct targets can alter the displayed counter
The separate VehicleTypesProgressBuilder selects CounterProgressGetter. For incomplete progress, it computes int(min(totalCount, totalGoal - (uniqueGoal - uniqueCount))). Keep that expression intact rather than replacing it with a simple capped raw total.
The common counter record separately defines total count as the sum of its values and unique count as the number of keys. Its completion criterion requires both goals. With synthetic total 12, total goal 10 and unique goal four, two keys give displayed current eight and fail that criterion; four keys give displayed current ten and pass. A supplied completed state has its own display override. These checks used positive synthetic counters and selected accessors/strategies, without full record construction or real quest configuration. They do not identify what live keys mean, prove a reward, or establish a universal progress formula.
Biathlon progress counts retained success flags
The common BattlesSeries record counts entries equal to True in its retained battle list. The BiathlonProgressGetter strategy uses that count as the current scalar unless the supplied state is COMPLETED, when it displays the goal instead. It does not calculate a consecutive-success run from the end of the list.
With supplied boolean flags [True, False, True], the scalar is 2. Reordering the same flags to [False, True, True] or [True, True, False] leaves it at two, although their trailing runs differ. Supplying completed state with goal three makes the displayed scalar three. These checks demonstrate the reader and state projection; they do not establish which gameplay condition generated a flag or whether a battle was won.
The biathlon builder selects this strategy and its header wrapper. The header publishes value, goal and state separately, and the header formatter consumes that payload. Its separate terminal slot payload is uniformly completed or failed, even when the retained flags are mixed. Keep raw flags, scalar and state separate; neither a scalar nor terminal slot payload reconstructs the full attempt history or establishes a server award.
Selected readable counter, strategy and terminal-slot checks used inert lists, slot constants and supplied state/goal values. Builder, storage, formatter and controller wiring were source-inspected. Actual quest definitions, flag production, reset policy, rendered slot presentation, rewards and client/server runtime behavior were not exercised.
Baseline ownership
A baseline is useful only if its owner and reset boundary are explicit.
| Baseline | Owner | Reset when |
|---|---|---|
| Battle-start vehicle statistic | Arena + vehicle type | A new arena begins or the feature deliberately recaptures it. |
| Session rating | Account + season | User resets, account changes, or the season identity changes. |
| Independent progression history | Account + vehicle type + local schema | User resets that vehicle or migrates the schema. |
| Entity-local combat state | Arena + entity generation | Respawn, entity replacement, or arena teardown. |
Leaving Account to become PlayerAvatar is not an account logout.
Preserve account-owned session baselines across the battle and clear them only on the real
ownership boundary.
Early empty caches are not authoritative zeros
Fast entry, reconnect, or account-to-avatar transition can expose a dossier object whose relevant record is empty even though a confirmed value was previously available.
- Read the normal synchronized account source when available.
- Maintain a small validated snapshot keyed by the stable vehicle identity.
- During battle readiness, reject structurally empty records rather than overwriting the snapshot.
- Refresh the snapshot after a confirmed lobby synchronization.
- Log the source used so diagnostics distinguish native, persisted fallback, and unavailable states.
A fallback must remain read-only evidence. It should not pretend to update official dossier values.
Live projection versus final result
Live feedback can omit information known only after the battle, including hidden or delayed damage. A live overlay should describe its value as a projection or known-so-far total. Final result processing may then reconcile the persisted history.
Use arenaUniqueID to prevent the same result from being processed through both an
immediate notification and a later full-result callback.