mirror of
https://github.com/prosolis/gogobee.git
synced 2026-07-16 08:52:42 +00:00
Adv 2.0 Phase L2 step 5: arena counters → player_meta
Creates the player_meta table (gogobee_legacy_migration.md §2.1) with the three columns L2 needs: arena_wins, arena_losses, invasion_score. Future phases ALTER in their own columns. Naming follows the §2.0 callout — no dnd_ prefix on new tables. Helpers in internal/plugin/player_meta.go: loadPlayerMeta, upsertPlayerMetaArena, backfillPlayerMetaArena (INSERT OR IGNORE, idempotent, logs row count per Phase R1 precedent). Plugin Init runs the backfill once on startup. Dual-write per §11: every char.ArenaWins++/ArenaLosses++ in adventure_arena.go also calls upsertPlayerMetaArena. AdvCharacter columns stay in place — reads will switch back to AdvCharacter trivially if a rollback is needed before L5 teardown. Read swap: handleArenaStats and renderDnDSheet now load player_meta and prefer its values (falling back to AdvCharacter if the row is missing, which only matters during the deploy window before backfill runs). renderArenaPersonalStats's signature changed from (char, stats) to (displayName, wins, losses, stats) so the render is DB-free. Tests: TestPlayerMetaArenaBackfill_Idempotent verifies double-run backfill is a no-op and doesn't clobber post-backfill dual-writes; TestUpsertPlayerMetaArena_RoundTrip covers insert/update/missing-user paths. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -317,7 +317,13 @@ func (p *AdventurePlugin) handleArenaStats(ctx MessageContext) error {
|
||||
}
|
||||
|
||||
stats := loadArenaPersonalStats(ctx.Sender)
|
||||
return p.SendDM(ctx.Sender, renderArenaPersonalStats(char, stats))
|
||||
wins, losses := char.ArenaWins, char.ArenaLosses
|
||||
if meta, err := loadPlayerMeta(ctx.Sender); err == nil {
|
||||
wins, losses = meta.ArenaWins, meta.ArenaLosses
|
||||
} else {
|
||||
slog.Error("player_meta: arena stats read failed, falling back to AdvCharacter", "user", ctx.Sender, "err", err)
|
||||
}
|
||||
return p.SendDM(ctx.Sender, renderArenaPersonalStats(char.DisplayName, wins, losses, stats))
|
||||
}
|
||||
|
||||
func (p *AdventurePlugin) handleArenaLeaderboard(ctx MessageContext) error {
|
||||
@@ -516,6 +522,10 @@ func (p *AdventurePlugin) resolveArenaDeath(ctx MessageContext, run *ArenaRun, c
|
||||
if err := saveAdvCharacter(char); err != nil {
|
||||
slog.Error("arena: failed to save character after death", "user", ctx.Sender, "err", err)
|
||||
}
|
||||
// Dual-write to player_meta (Adv 2.0 Phase L2 step 5).
|
||||
if err := upsertPlayerMetaArena(ctx.Sender, char.ArenaWins, char.ArenaLosses, char.InvasionScore); err != nil {
|
||||
slog.Error("player_meta: arena loss dual-write failed", "user", ctx.Sender, "err", err)
|
||||
}
|
||||
|
||||
now := time.Now().UTC()
|
||||
run.Status = "dead"
|
||||
@@ -599,6 +609,10 @@ func (p *AdventurePlugin) arenaCompleteSession(userID id.UserID, run *ArenaRun,
|
||||
if err := saveAdvCharacter(char); err != nil {
|
||||
slog.Error("arena: failed to save character after session complete", "user", userID, "err", err)
|
||||
}
|
||||
// Dual-write to player_meta (Adv 2.0 Phase L2 step 5).
|
||||
if err := upsertPlayerMetaArena(userID, char.ArenaWins, char.ArenaLosses, char.InvasionScore); err != nil {
|
||||
slog.Error("player_meta: arena win dual-write failed", "user", userID, "err", err)
|
||||
}
|
||||
|
||||
// End run if not already ended
|
||||
if run.Status != "completed" && run.Status != "cashed_out" {
|
||||
|
||||
Reference in New Issue
Block a user