Files
scrabble-game/backend/internal/game/eventwire.go
T
Ilia Denisov 8881214213 R6(a): de-stage code, docs, READMEs; split stage6_test
Mechanical, behaviour-preserving removal of Stage N / TODO-N / phase (RN)
references from comments, doc-comments, service READMEs, the current-state docs
(ARCHITECTURE, FUNCTIONAL+_ru, TESTING, UI_DESIGN), config-file comments, and the
.fbs/.proto schema comments. PLAN.md / PRERELEASE.md / CLAUDE.md keep the stage
history.

- Rename the only stage-named identifiers: registerStage8 -> registerSocialOps,
  registerStage11 -> registerLinkOps (gateway transcode).
- Split stage6_test.go: TestEmailLoginFlow -> email_test.go,
  TestGuestAutoMatchLeavesNoStats (+ provisionGuest) -> account_test.go.
- Regenerated proto bindings (push.pb.go, telegram_grpc.pb.go) from the de-staged
  .proto comments; FB Go/TS bindings unchanged (flatc strips schema comments).

go build/vet/gofmt clean across modules; integration typecheck and pnpm check green.
2026-06-10 16:56:03 +02:00

80 lines
2.6 KiB
Go

package game
import (
"scrabble/backend/internal/engine"
"scrabble/backend/internal/notify"
)
// The mappers below project the game domain into the wire-agnostic notify.* input
// structs the enriched live events carry. They keep the wire schema out of the
// game package: notify owns the FlatBuffers encoding, this file only resolves the
// values (seat display names, last-activity sort key) into its input shapes.
// gameSummary projects a game.Game into the notify.GameSummary embedded in enriched
// events. names is the seat-indexed display-name slice from seatNames; LastActivityUnix
// mirrors the gateway view (the current turn's start while active, the finish time once
// finished).
func gameSummary(g Game, names []string) notify.GameSummary {
seats := make([]notify.SeatStanding, 0, len(g.Seats))
for _, s := range g.Seats {
name := ""
if s.Seat >= 0 && s.Seat < len(names) {
name = names[s.Seat]
}
seats = append(seats, notify.SeatStanding{
Seat: s.Seat,
AccountID: s.AccountID.String(),
DisplayName: name,
Score: s.Score,
HintsUsed: s.HintsUsed,
IsWinner: s.IsWinner,
})
}
last := g.TurnStartedAt
if g.FinishedAt != nil {
last = *g.FinishedAt
}
return notify.GameSummary{
ID: g.ID.String(),
Variant: g.Variant.String(),
DictVersion: g.DictVersion,
Status: g.Status,
Players: g.Players,
ToMove: g.ToMove,
TurnTimeoutSecs: int(g.TurnTimeout.Seconds()),
MoveCount: g.MoveCount,
EndReason: g.EndReason,
Seats: seats,
LastActivityUnix: last.Unix(),
}
}
// playerState projects a StateView into the notify.PlayerState carried by the
// match_found / game_started events. The rack is re-encoded to wire alphabet indices;
// the variant alphabet display table is embedded when includeAlphabet is set (an
// initial view whose recipient may not have cached the variant yet).
func playerState(v StateView, names []string, includeAlphabet bool) (notify.PlayerState, error) {
rack, err := engine.EncodeRack(v.Game.Variant, v.Rack)
if err != nil {
return notify.PlayerState{}, err
}
ps := notify.PlayerState{
Game: gameSummary(v.Game, names),
Seat: v.Seat,
Rack: rack,
BagLen: v.BagLen,
HintsRemaining: v.HintsRemaining,
}
if includeAlphabet {
tab, err := engine.AlphabetTable(v.Game.Variant)
if err != nil {
return notify.PlayerState{}, err
}
ps.Alphabet = make([]notify.AlphabetLetter, len(tab))
for i, e := range tab {
ps.Alphabet[i] = notify.AlphabetLetter{Index: int(e.Index), Letter: e.Letter, Value: e.Value}
}
}
return ps, nil
}