8881214213
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.
198 lines
6.9 KiB
Go
198 lines
6.9 KiB
Go
package notify
|
|
|
|
import (
|
|
flatbuffers "github.com/google/flatbuffers/go"
|
|
|
|
"scrabble/backend/internal/engine"
|
|
fb "scrabble/pkg/fbs/scrabblefb"
|
|
)
|
|
|
|
// The builders below encode the nested wire tables embedded in enriched event
|
|
// payloads. They mirror the gateway's transcode encoders, but read the domain's
|
|
// already-resolved values (notify.* input structs and the decoded engine.MoveRecord)
|
|
// rather than the gateway's REST DTOs. Each returns the offset of the table it built;
|
|
// callers must build every nested table before opening the parent event table.
|
|
|
|
// buildGameView builds a GameView table from a GameSummary and returns its offset.
|
|
func buildGameView(b *flatbuffers.Builder, g GameSummary) flatbuffers.UOffsetT {
|
|
seatOffs := make([]flatbuffers.UOffsetT, len(g.Seats))
|
|
for i, s := range g.Seats {
|
|
aid := b.CreateString(s.AccountID)
|
|
dname := b.CreateString(s.DisplayName)
|
|
fb.SeatViewStart(b)
|
|
fb.SeatViewAddSeat(b, int32(s.Seat))
|
|
fb.SeatViewAddAccountId(b, aid)
|
|
fb.SeatViewAddScore(b, int32(s.Score))
|
|
fb.SeatViewAddHintsUsed(b, int32(s.HintsUsed))
|
|
fb.SeatViewAddIsWinner(b, s.IsWinner)
|
|
fb.SeatViewAddDisplayName(b, dname)
|
|
seatOffs[i] = fb.SeatViewEnd(b)
|
|
}
|
|
fb.GameViewStartSeatsVector(b, len(seatOffs))
|
|
for i := len(seatOffs) - 1; i >= 0; i-- {
|
|
b.PrependUOffsetT(seatOffs[i])
|
|
}
|
|
seats := b.EndVector(len(seatOffs))
|
|
|
|
id := b.CreateString(g.ID)
|
|
variant := b.CreateString(g.Variant)
|
|
dictVer := b.CreateString(g.DictVersion)
|
|
status := b.CreateString(g.Status)
|
|
endReason := b.CreateString(g.EndReason)
|
|
|
|
fb.GameViewStart(b)
|
|
fb.GameViewAddId(b, id)
|
|
fb.GameViewAddVariant(b, variant)
|
|
fb.GameViewAddDictVersion(b, dictVer)
|
|
fb.GameViewAddStatus(b, status)
|
|
fb.GameViewAddPlayers(b, int32(g.Players))
|
|
fb.GameViewAddToMove(b, int32(g.ToMove))
|
|
fb.GameViewAddTurnTimeoutSecs(b, int32(g.TurnTimeoutSecs))
|
|
fb.GameViewAddMoveCount(b, int32(g.MoveCount))
|
|
fb.GameViewAddEndReason(b, endReason)
|
|
fb.GameViewAddSeats(b, seats)
|
|
fb.GameViewAddLastActivityUnix(b, g.LastActivityUnix)
|
|
return fb.GameViewEnd(b)
|
|
}
|
|
|
|
// buildMoveRecord builds a MoveRecord table from a decoded engine move and returns
|
|
// its offset. The values match the move-result DTO (Count is the engine count: the
|
|
// number of tiles swapped on an exchange, zero otherwise).
|
|
func buildMoveRecord(b *flatbuffers.Builder, m engine.MoveRecord) flatbuffers.UOffsetT {
|
|
tileOffs := make([]flatbuffers.UOffsetT, len(m.Tiles))
|
|
for i, t := range m.Tiles {
|
|
letter := b.CreateString(t.Letter)
|
|
fb.TileRecordStart(b)
|
|
fb.TileRecordAddRow(b, int32(t.Row))
|
|
fb.TileRecordAddCol(b, int32(t.Col))
|
|
fb.TileRecordAddLetter(b, letter)
|
|
fb.TileRecordAddBlank(b, t.Blank)
|
|
tileOffs[i] = fb.TileRecordEnd(b)
|
|
}
|
|
fb.MoveRecordStartTilesVector(b, len(tileOffs))
|
|
for i := len(tileOffs) - 1; i >= 0; i-- {
|
|
b.PrependUOffsetT(tileOffs[i])
|
|
}
|
|
tiles := b.EndVector(len(tileOffs))
|
|
|
|
wordOffs := make([]flatbuffers.UOffsetT, len(m.Words))
|
|
for i, w := range m.Words {
|
|
wordOffs[i] = b.CreateString(w)
|
|
}
|
|
fb.MoveRecordStartWordsVector(b, len(wordOffs))
|
|
for i := len(wordOffs) - 1; i >= 0; i-- {
|
|
b.PrependUOffsetT(wordOffs[i])
|
|
}
|
|
words := b.EndVector(len(wordOffs))
|
|
|
|
action := b.CreateString(m.Action.String())
|
|
dir := b.CreateString(m.Dir.String())
|
|
fb.MoveRecordStart(b)
|
|
fb.MoveRecordAddPlayer(b, int32(m.Player))
|
|
fb.MoveRecordAddAction(b, action)
|
|
fb.MoveRecordAddDir(b, dir)
|
|
fb.MoveRecordAddMainRow(b, int32(m.MainRow))
|
|
fb.MoveRecordAddMainCol(b, int32(m.MainCol))
|
|
fb.MoveRecordAddTiles(b, tiles)
|
|
fb.MoveRecordAddWords(b, words)
|
|
fb.MoveRecordAddCount(b, int32(m.Count))
|
|
fb.MoveRecordAddScore(b, int32(m.Score))
|
|
fb.MoveRecordAddTotal(b, int32(m.Total))
|
|
return fb.MoveRecordEnd(b)
|
|
}
|
|
|
|
// buildAlphabet builds the AlphabetEntry vector embedded in a StateView and returns
|
|
// its offset.
|
|
func buildAlphabet(b *flatbuffers.Builder, entries []AlphabetLetter) flatbuffers.UOffsetT {
|
|
offs := make([]flatbuffers.UOffsetT, len(entries))
|
|
for i, e := range entries {
|
|
letter := b.CreateString(e.Letter)
|
|
fb.AlphabetEntryStart(b)
|
|
fb.AlphabetEntryAddIndex(b, byte(e.Index))
|
|
fb.AlphabetEntryAddLetter(b, letter)
|
|
fb.AlphabetEntryAddValue(b, int32(e.Value))
|
|
offs[i] = fb.AlphabetEntryEnd(b)
|
|
}
|
|
fb.StateViewStartAlphabetVector(b, len(offs))
|
|
for i := len(offs) - 1; i >= 0; i-- {
|
|
b.PrependUOffsetT(offs[i])
|
|
}
|
|
return b.EndVector(len(offs))
|
|
}
|
|
|
|
// buildStateView builds a StateView table from a PlayerState and returns its offset.
|
|
func buildStateView(b *flatbuffers.Builder, s PlayerState) flatbuffers.UOffsetT {
|
|
game := buildGameView(b, s.Game)
|
|
rackBytes := make([]byte, len(s.Rack))
|
|
for i, v := range s.Rack {
|
|
rackBytes[i] = byte(v)
|
|
}
|
|
rack := b.CreateByteVector(rackBytes)
|
|
hasAlphabet := len(s.Alphabet) > 0
|
|
var alphabet flatbuffers.UOffsetT
|
|
if hasAlphabet {
|
|
alphabet = buildAlphabet(b, s.Alphabet)
|
|
}
|
|
fb.StateViewStart(b)
|
|
fb.StateViewAddGame(b, game)
|
|
fb.StateViewAddSeat(b, int32(s.Seat))
|
|
fb.StateViewAddRack(b, rack)
|
|
fb.StateViewAddBagLen(b, int32(s.BagLen))
|
|
fb.StateViewAddHintsRemaining(b, int32(s.HintsRemaining))
|
|
if hasAlphabet {
|
|
fb.StateViewAddAlphabet(b, alphabet)
|
|
}
|
|
return fb.StateViewEnd(b)
|
|
}
|
|
|
|
// buildAccountRef builds an AccountRef table and returns its offset.
|
|
func buildAccountRef(b *flatbuffers.Builder, a AccountRef) flatbuffers.UOffsetT {
|
|
aid := b.CreateString(a.AccountID)
|
|
name := b.CreateString(a.DisplayName)
|
|
fb.AccountRefStart(b)
|
|
fb.AccountRefAddAccountId(b, aid)
|
|
fb.AccountRefAddDisplayName(b, name)
|
|
return fb.AccountRefEnd(b)
|
|
}
|
|
|
|
// buildInvitation builds an Invitation table from an InvitationSummary and returns its offset.
|
|
func buildInvitation(b *flatbuffers.Builder, inv InvitationSummary) flatbuffers.UOffsetT {
|
|
inviter := buildAccountRef(b, inv.Inviter)
|
|
inviteeOffs := make([]flatbuffers.UOffsetT, len(inv.Invitees))
|
|
for i, iv := range inv.Invitees {
|
|
aid := b.CreateString(iv.AccountID)
|
|
name := b.CreateString(iv.DisplayName)
|
|
resp := b.CreateString(iv.Response)
|
|
fb.InvitationInviteeStart(b)
|
|
fb.InvitationInviteeAddAccountId(b, aid)
|
|
fb.InvitationInviteeAddDisplayName(b, name)
|
|
fb.InvitationInviteeAddSeat(b, int32(iv.Seat))
|
|
fb.InvitationInviteeAddResponse(b, resp)
|
|
inviteeOffs[i] = fb.InvitationInviteeEnd(b)
|
|
}
|
|
fb.InvitationStartInviteesVector(b, len(inviteeOffs))
|
|
for i := len(inviteeOffs) - 1; i >= 0; i-- {
|
|
b.PrependUOffsetT(inviteeOffs[i])
|
|
}
|
|
invitees := b.EndVector(len(inviteeOffs))
|
|
|
|
id := b.CreateString(inv.ID)
|
|
variant := b.CreateString(inv.Variant)
|
|
dropout := b.CreateString(inv.DropoutTiles)
|
|
status := b.CreateString(inv.Status)
|
|
gameID := b.CreateString(inv.GameID)
|
|
fb.InvitationStart(b)
|
|
fb.InvitationAddId(b, id)
|
|
fb.InvitationAddInviter(b, inviter)
|
|
fb.InvitationAddInvitees(b, invitees)
|
|
fb.InvitationAddVariant(b, variant)
|
|
fb.InvitationAddTurnTimeoutSecs(b, int32(inv.TurnTimeoutSecs))
|
|
fb.InvitationAddHintsAllowed(b, inv.HintsAllowed)
|
|
fb.InvitationAddHintsPerPlayer(b, int32(inv.HintsPerPlayer))
|
|
fb.InvitationAddDropoutTiles(b, dropout)
|
|
fb.InvitationAddStatus(b, status)
|
|
fb.InvitationAddGameId(b, gameID)
|
|
fb.InvitationAddExpiresAtUnix(b, inv.ExpiresAtUnix)
|
|
return fb.InvitationEnd(b)
|
|
}
|