package transcode import ( flatbuffers "github.com/google/flatbuffers/go" "scrabble/gateway/internal/backendclient" fb "scrabble/pkg/fbs/scrabblefb" ) // Stage 8 encoders: friends, blocks, invitations, statistics and GCG. They follow // encode.go's bottom-up rule (build every string/child vector before the table). // buildAccountRef builds an AccountRef table and returns its offset. func buildAccountRef(b *flatbuffers.Builder, r backendclient.AccountRefResp) flatbuffers.UOffsetT { id := b.CreateString(r.AccountID) name := b.CreateString(r.DisplayName) fb.AccountRefStart(b) fb.AccountRefAddAccountId(b, id) fb.AccountRefAddDisplayName(b, name) return fb.AccountRefEnd(b) } // buildAccountRefVector builds a [AccountRef] vector using the table-specific // StartXVector function and returns the vector offset. func buildAccountRefVector(b *flatbuffers.Builder, refs []backendclient.AccountRefResp, start func(*flatbuffers.Builder, int) flatbuffers.UOffsetT) flatbuffers.UOffsetT { offs := make([]flatbuffers.UOffsetT, len(refs)) for i, r := range refs { offs[i] = buildAccountRef(b, r) } start(b, len(offs)) for i := len(offs) - 1; i >= 0; i-- { b.PrependUOffsetT(offs[i]) } return b.EndVector(len(offs)) } // encodeFriendList builds a FriendList payload. func encodeFriendList(r backendclient.FriendListResp) []byte { b := flatbuffers.NewBuilder(256) v := buildAccountRefVector(b, r.Friends, fb.FriendListStartFriendsVector) fb.FriendListStart(b) fb.FriendListAddFriends(b, v) b.Finish(fb.FriendListEnd(b)) return b.FinishedBytes() } // encodeIncomingList builds an IncomingRequestList payload. func encodeIncomingList(r backendclient.IncomingListResp) []byte { b := flatbuffers.NewBuilder(256) v := buildAccountRefVector(b, r.Requests, fb.IncomingRequestListStartRequestsVector) fb.IncomingRequestListStart(b) fb.IncomingRequestListAddRequests(b, v) b.Finish(fb.IncomingRequestListEnd(b)) return b.FinishedBytes() } // encodeBlockList builds a BlockList payload. func encodeBlockList(r backendclient.BlockListResp) []byte { b := flatbuffers.NewBuilder(256) v := buildAccountRefVector(b, r.Blocked, fb.BlockListStartBlockedVector) fb.BlockListStart(b) fb.BlockListAddBlocked(b, v) b.Finish(fb.BlockListEnd(b)) return b.FinishedBytes() } // encodeFriendCode builds a FriendCode payload. func encodeFriendCode(r backendclient.FriendCodeResp) []byte { b := flatbuffers.NewBuilder(64) code := b.CreateString(r.Code) fb.FriendCodeStart(b) fb.FriendCodeAddCode(b, code) fb.FriendCodeAddExpiresAtUnix(b, r.ExpiresAtUnix) b.Finish(fb.FriendCodeEnd(b)) return b.FinishedBytes() } // encodeRedeemResult builds a RedeemResult payload. func encodeRedeemResult(r backendclient.RedeemResultResp) []byte { b := flatbuffers.NewBuilder(128) friend := buildAccountRef(b, r.Friend) fb.RedeemResultStart(b) fb.RedeemResultAddFriend(b, friend) b.Finish(fb.RedeemResultEnd(b)) return b.FinishedBytes() } // encodeStats builds a StatsView payload. func encodeStats(r backendclient.StatsResp) []byte { b := flatbuffers.NewBuilder(64) fb.StatsViewStart(b) fb.StatsViewAddWins(b, int32(r.Wins)) fb.StatsViewAddLosses(b, int32(r.Losses)) fb.StatsViewAddDraws(b, int32(r.Draws)) fb.StatsViewAddMaxGamePoints(b, int32(r.MaxGamePoints)) fb.StatsViewAddMaxWordPoints(b, int32(r.MaxWordPoints)) b.Finish(fb.StatsViewEnd(b)) return b.FinishedBytes() } // buildInvitation builds an Invitation table and returns its offset. func buildInvitation(b *flatbuffers.Builder, inv backendclient.InvitationResp) flatbuffers.UOffsetT { 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)) inviter := buildAccountRef(b, inv.Inviter) 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) } // encodeInvitation builds an Invitation payload. func encodeInvitation(inv backendclient.InvitationResp) []byte { b := flatbuffers.NewBuilder(512) b.Finish(buildInvitation(b, inv)) return b.FinishedBytes() } // encodeInvitationList builds an InvitationList payload. func encodeInvitationList(r backendclient.InvitationListResp) []byte { b := flatbuffers.NewBuilder(1024) offs := make([]flatbuffers.UOffsetT, len(r.Invitations)) for i, inv := range r.Invitations { offs[i] = buildInvitation(b, inv) } fb.InvitationListStartInvitationsVector(b, len(offs)) for i := len(offs) - 1; i >= 0; i-- { b.PrependUOffsetT(offs[i]) } v := b.EndVector(len(offs)) fb.InvitationListStart(b) fb.InvitationListAddInvitations(b, v) b.Finish(fb.InvitationListEnd(b)) return b.FinishedBytes() } // encodeGcg builds a GcgExport payload. func encodeGcg(r backendclient.GcgResp) []byte { b := flatbuffers.NewBuilder(1024) gid := b.CreateString(r.GameID) fn := b.CreateString(r.Filename) content := b.CreateString(r.Content) fb.GcgExportStart(b) fb.GcgExportAddGameId(b, gid) fb.GcgExportAddFilename(b, fn) fb.GcgExportAddContent(b, content) b.Finish(fb.GcgExportEnd(b)) return b.FinishedBytes() }