package edge import ( flatbuffers "github.com/google/flatbuffers/go" fb "scrabble/pkg/fbs/scrabblefb" ) // PlayTile is one tile to place, addressed by alphabet index (255 marks a blank's // carrier letter together with Blank=true), as the submit-play request carries it. type PlayTile struct { Row, Col int Letter byte Blank bool } // gameAction builds a GameActionRequest payload (just a game id): pass, nudge, // history, draft.get. func gameAction(gameID string) []byte { b := flatbuffers.NewBuilder(64) gid := b.CreateString(gameID) fb.GameActionRequestStart(b) fb.GameActionRequestAddGameId(b, gid) b.Finish(fb.GameActionRequestEnd(b)) return b.FinishedBytes() } // stateReq builds a StateRequest payload. includeAlphabet asks the backend to embed // the variant alphabet table (the driver sets it once per variant). func stateReq(gameID string, includeAlphabet bool) []byte { b := flatbuffers.NewBuilder(64) gid := b.CreateString(gameID) fb.StateRequestStart(b) fb.StateRequestAddGameId(b, gid) fb.StateRequestAddIncludeAlphabet(b, includeAlphabet) b.Finish(fb.StateRequestEnd(b)) return b.FinishedBytes() } // submitPlay builds a SubmitPlayRequest payload. dir is "H" or "V"; tiles are the // newly-placed tiles in main-word order. func submitPlay(gameID, dir string, tiles []PlayTile) []byte { b := flatbuffers.NewBuilder(256) gid := b.CreateString(gameID) d := b.CreateString(dir) offs := make([]flatbuffers.UOffsetT, len(tiles)) for i, t := range tiles { fb.PlayTileStart(b) fb.PlayTileAddRow(b, int32(t.Row)) fb.PlayTileAddCol(b, int32(t.Col)) fb.PlayTileAddLetter(b, t.Letter) fb.PlayTileAddBlank(b, t.Blank) offs[i] = fb.PlayTileEnd(b) } fb.SubmitPlayRequestStartTilesVector(b, len(offs)) for i := len(offs) - 1; i >= 0; i-- { b.PrependUOffsetT(offs[i]) } tilesVec := b.EndVector(len(offs)) fb.SubmitPlayRequestStart(b) fb.SubmitPlayRequestAddGameId(b, gid) fb.SubmitPlayRequestAddDir(b, d) fb.SubmitPlayRequestAddTiles(b, tilesVec) b.Finish(fb.SubmitPlayRequestEnd(b)) return b.FinishedBytes() } // exchange builds an ExchangeRequest payload swapping the listed rack tiles (alphabet // indices; 255 a blank). func exchange(gameID string, tiles []byte) []byte { b := flatbuffers.NewBuilder(64) gid := b.CreateString(gameID) vec := b.CreateByteVector(tiles) fb.ExchangeRequestStart(b) fb.ExchangeRequestAddGameId(b, gid) fb.ExchangeRequestAddTiles(b, vec) b.Finish(fb.ExchangeRequestEnd(b)) return b.FinishedBytes() } // checkWord builds a CheckWordRequest payload (alphabet indices for the word). func checkWord(gameID string, word []byte) []byte { b := flatbuffers.NewBuilder(64) gid := b.CreateString(gameID) vec := b.CreateByteVector(word) fb.CheckWordRequestStart(b) fb.CheckWordRequestAddGameId(b, gid) fb.CheckWordRequestAddWord(b, vec) b.Finish(fb.CheckWordRequestEnd(b)) return b.FinishedBytes() } // chatPost builds a ChatPostRequest payload. func chatPost(gameID, body string) []byte { b := flatbuffers.NewBuilder(128) gid := b.CreateString(gameID) bd := b.CreateString(body) fb.ChatPostRequestStart(b) fb.ChatPostRequestAddGameId(b, gid) fb.ChatPostRequestAddBody(b, bd) b.Finish(fb.ChatPostRequestEnd(b)) return b.FinishedBytes() } // draftSave builds a DraftRequest payload carrying the opaque composition JSON. func draftSave(gameID, jsonStr string) []byte { b := flatbuffers.NewBuilder(128) gid := b.CreateString(gameID) j := b.CreateString(jsonStr) fb.DraftRequestStart(b) fb.DraftRequestAddGameId(b, gid) fb.DraftRequestAddJson(b, j) b.Finish(fb.DraftRequestEnd(b)) return b.FinishedBytes() } // updateProfile builds an UpdateProfileRequest payload. It resends the marker display // name and sane defaults so the account stays findable by the seeder's Cleanup. func updateProfile(displayName, lang string) []byte { b := flatbuffers.NewBuilder(192) name := b.CreateString(displayName) pl := b.CreateString(lang) tz := b.CreateString("UTC") as := b.CreateString("00:00") ae := b.CreateString("07:00") fb.UpdateProfileRequestStart(b) fb.UpdateProfileRequestAddDisplayName(b, name) fb.UpdateProfileRequestAddPreferredLanguage(b, pl) fb.UpdateProfileRequestAddTimeZone(b, tz) fb.UpdateProfileRequestAddAwayStart(b, as) fb.UpdateProfileRequestAddAwayEnd(b, ae) fb.UpdateProfileRequestAddBlockChat(b, false) fb.UpdateProfileRequestAddBlockFriendRequests(b, false) fb.UpdateProfileRequestAddNotificationsInAppOnly(b, true) b.Finish(fb.UpdateProfileRequestEnd(b)) return b.FinishedBytes() } // createInvitation builds a CreateInvitationRequest payload. turnTimeoutSecs 0 asks // the backend for its default; dropoutTiles "remove" is the standard policy. func createInvitation(inviteeIDs []string, variant string, turnTimeoutSecs int) []byte { b := flatbuffers.NewBuilder(256) idOffs := make([]flatbuffers.UOffsetT, len(inviteeIDs)) for i, id := range inviteeIDs { idOffs[i] = b.CreateString(id) } fb.CreateInvitationRequestStartInviteeIdsVector(b, len(idOffs)) for i := len(idOffs) - 1; i >= 0; i-- { b.PrependUOffsetT(idOffs[i]) } ids := b.EndVector(len(idOffs)) variantOff := b.CreateString(variant) dropout := b.CreateString("remove") fb.CreateInvitationRequestStart(b) fb.CreateInvitationRequestAddInviteeIds(b, ids) fb.CreateInvitationRequestAddVariant(b, variantOff) fb.CreateInvitationRequestAddTurnTimeoutSecs(b, int32(turnTimeoutSecs)) fb.CreateInvitationRequestAddHintsAllowed(b, true) fb.CreateInvitationRequestAddHintsPerPlayer(b, 1) fb.CreateInvitationRequestAddDropoutTiles(b, dropout) b.Finish(fb.CreateInvitationRequestEnd(b)) return b.FinishedBytes() } // invitationAction builds an InvitationActionRequest payload (accept / decline / // cancel by id). func invitationAction(invitationID string) []byte { b := flatbuffers.NewBuilder(64) id := b.CreateString(invitationID) fb.InvitationActionRequestStart(b) fb.InvitationActionRequestAddInvitationId(b, id) b.Finish(fb.InvitationActionRequestEnd(b)) return b.FinishedBytes() } // enqueueReq builds an EnqueueRequest payload (join the per-variant auto-match pool). func enqueueReq(variant string) []byte { b := flatbuffers.NewBuilder(64) v := b.CreateString(variant) fb.EnqueueRequestStart(b) fb.EnqueueRequestAddVariant(b, v) b.Finish(fb.EnqueueRequestEnd(b)) return b.FinishedBytes() }