feat(go-services): Phase 2 — combat_stats accumulator (cross-language exact)
Ports main.py's _combat_session_delta / _combat_merge_into_lifetime (incl. the documented "offense/defense use latest, additively" quirk) and the combat_stats handler (session delta -> DB-backed lifetime merge -> delete-then-insert of combat_stats + combat_stats_sessions). Read handlers gain the live combat overlay (union live + DB), like Python. Validation: - combat.go `combat-merge` CLI folds snapshots through the accumulator; diffed against the Python functions on identical input -> byte-IDENTICAL. - combat_test.go golden test runs in the build (go test now part of the tracker Dockerfile). - Live: 40 combat lifetime rows + 40 session snapshots + rare_events flowing in dereth_go via the shadow consumer. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
parent
a5d69ba88d
commit
7350b00341
6 changed files with 347 additions and 4 deletions
|
|
@ -43,6 +43,14 @@ func (s *Server) handleCharacterStats(w http.ResponseWriter, r *http.Request) {
|
|||
// so session is always null. (main.py:1819)
|
||||
func (s *Server) handleCombatStatsOne(w http.ResponseWriter, r *http.Request) {
|
||||
cn := r.PathValue("character_name")
|
||||
if s.ingestor != nil {
|
||||
if live, ok := s.ingestor.getCombatStats(cn); ok {
|
||||
writeJSON(w, http.StatusOK, map[string]any{
|
||||
"character_name": cn, "session": live["session"], "lifetime": live["lifetime"],
|
||||
})
|
||||
return
|
||||
}
|
||||
}
|
||||
ctx, cancel := reqCtx(r)
|
||||
defer cancel()
|
||||
|
||||
|
|
@ -67,13 +75,25 @@ func (s *Server) handleCombatStatsAll(w http.ResponseWriter, r *http.Request) {
|
|||
ctx, cancel := reqCtx(r)
|
||||
defer cancel()
|
||||
|
||||
results := make([]map[string]any, 0)
|
||||
seen := map[string]bool{}
|
||||
if s.ingestor != nil { // live overlay first, like Python
|
||||
for char, live := range s.ingestor.allCombatStats() {
|
||||
seen[char] = true
|
||||
results = append(results, map[string]any{
|
||||
"character_name": char, "session": live["session"], "lifetime": live["lifetime"],
|
||||
})
|
||||
}
|
||||
}
|
||||
rows, err := queryRowsAsMaps(ctx, s.pool, `SELECT character_name, stats_data FROM combat_stats`)
|
||||
if err != nil {
|
||||
s.dbErr(w, "combat-stats/all", err)
|
||||
return
|
||||
}
|
||||
results := make([]map[string]any, 0, len(rows))
|
||||
for _, row := range rows {
|
||||
if seen[toStr(row["character_name"])] {
|
||||
continue
|
||||
}
|
||||
results = append(results, map[string]any{
|
||||
"character_name": row["character_name"],
|
||||
"session": nil,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue