Add /le stats resetProgress commands to reset player/area stats & progression (recalc-based)
Goal
Add admin/edit commands to reset a player's and/or an area's stats and progression (not just leaderboard stats). Today the only way to clear anything is the GUI AREA_STATS_CLEAR button, which is area-only and stats-only.
Commands — /lasersenigma stats resetProgress <variant>
| Variant | Targets | Permission | Confirm |
|---|---|---|---|
allPlayersForArea <areaId> [confirm] |
one area, all players | ADMIN | yes |
allAreasForPlayer <playerName> [confirm] |
one player, all areas | EDIT if playerName = self, else ADMIN |
yes |
playerForArea [<playerName>] [<areaId>] |
one player × one area | EDIT if self/omitted, else ADMIN | no (low impact) |
allPlayerForAllArea [confirm] |
all players, all areas | ADMIN | yes |
playerNameresolved viaOfflinePlayerOrSelectorParser(offline-capable); omitted ⇒ self.areaIdomitted ⇒ resolved from sender location. Console + self-variant without a name ⇒errors.command.unavailable_in_console.confirm: literal last argument, never tab-completed (empty suggestion provider). If absent on a confirm-required variant, send a caution message inviting the user to re-type the command withconfirm. Mirrors theRaceLifecycleCommandRegistrarreset precedent.
Data scope
Erased per variant. Kept in all cases: playersinventories, active race membership.
| Table | allPlayersForArea | allAreasForPlayer | playerForArea | allPlayerForAllArea |
|---|---|---|---|---|
area_player_stats |
per area | per player | player×area | all |
player_global_stats |
recalc/player | reset to 0 (keep name) | recalc/player | reset to 0 (keep name) |
area_global_stats / server_global_stats |
recalc | recalc | recalc | reset |
playerskeys |
per area | per player | player×area | all |
bonus_obtained |
per area | per player | player×area | all |
checkpoints (×2) |
Checkpoints are stored per world, not per area → only erased when all of a player's areas are targeted (variants 2 & 4).
Aggregate strategy: recalculate, never decrement
Decrementing player_global_stats is unsafe: the existing onAreaStatsCleared only fixes nb_unique_areas_won (6 of 7 cumulative fields stay inflated), iterates winners only (misses non-winners' entries/time), and any pre-existing drift makes a decrement permanently wrong. Instead:
- New service helper
recalculatePlayerGlobalStats(uuid)recomputes a player row from the source of truth (area_player_statsSUM/COUNT +bonus_obtainedCOUNT), preservinglast_known_name. Shared by the 4 variants and the GUI button. - Safe order: delete targeted
area_player_stats+bonus_obtained→ recalc affected player rows → recalcarea_global_stats(or delete) → recalcserver_global_stats. player_global_stats: never DELETE the row on full-wipe variants — reset counters to 0 and keeplast_known_name(we are resetting progression, not anonymizing).
Two server-aggregate bugs to fix (required for the recalc to be correct)
server_global_stats.nb_unique_areas_wondefinition mismatch. The incremental path (onPlayerWon) counts (player, area) winning pairs; the recalc (computeServerTotals) usesCOUNT(DISTINCT playeruuid)= distinct winners. The same field yields different values depending on the code path. Fix the recalc formula toSUM(CASE WHEN nb_victories > 0 THEN 1 ELSE 0 END)to match the incremental semantics (and the player-level field of the same name).server_global_stats.total_time_spentforced to 0 on recalc. The "cannot be summed in SQL" comment is stale — the column is aBIGINTof milliseconds and is summable. AddCOALESCE(SUM(total_time_spent), 0)tocomputeServerTotalsinstead of resetting to0L.
Layering (must respect the existing architecture)
- Command (
StatsCommandRegistrar): parse args, resolveareaId/playerName, read theconfirmflag, delegate. No business logic, no SQL. - Controller (new
StatsResetController, static likeAreaController): dynamic permission check (Permission.EDIT/ADMIN.checkPermissionAndSendMsg) before the confirm gate; success messages. - Service (
GlobalStatsUpdateServiceextended): delete + recalc + events. - Repository: new delete/aggregate queries (
deleteByAreaAndPlayer,deleteByPlayer,deleteAll,findPlayerUUIDsByAreaId,computePlayerTotals; bonusdeleteByPlayer/deleteByArea/deleteByAreaAndPlayer/countByPlayer).
GUI alignment
AreaStats.clear() (the GUI AREA_STATS_CLEAR button) is repointed to the new full resetAllPlayersForArea service logic, so the button and the allPlayersForArea command share a single code path (now also clearing keys/bonus).
Side deliverables
- New translation codes (caution/confirm prompts, success messages) — 25 language files.
- CHANGELOG entry.
- Wiki update (commands / stats page).