ce7a66b3e6
Replaces the Phase 10 map stub with live planet rendering driven by `user.games.report`, and wires the header turn counter to the same data. Phase 11's frontend sits on a per-game `GameStateStore` that lives in `lib/game-state.svelte.ts`: the in-game shell layout instantiates one per game, exposes it through Svelte context, and disposes it on remount. The store discovers the game's current turn through `lobby.my.games.list`, fetches the matching report, and exposes a TS-friendly snapshot to the header turn counter, the map view, and the inspector / order / calculator tabs that later phases will plug onto the same instance. The pipeline forced one cross-stage decision: the user surface needs the current turn number to know which report to fetch, but `GameSummary` did not expose it. Phase 11 extends the lobby catalogue (FB schema, transcoder, Go model, backend gameSummaryWire, gateway decoders, openapi, TS bindings, api/lobby.ts) with `current_turn:int32`. The data was already tracked in backend's `RuntimeSnapshot.CurrentTurn`; surfacing it is a wire change only. Two alternatives were rejected: a brand-new `user.games.state` message (full wire-flow for one field) and hard-coding `turn=0` (works for the dev sandbox, which never advances past zero, but renders the initial state for any real game). The change crosses Phase 8's already-shipped catalogue per the project's "decisions baked back into the live plan" rule — existing tests and fixtures are updated in the same patch. The state binding lives in `map/state-binding.ts::reportToWorld`: one Point primitive per planet across all four kinds (local / other / uninhabited / unidentified) with distinct fill colours, fill alphas, and point radii so the user can tell them apart at a glance. The planet engine number is reused as the primitive id so a hit-test result resolves directly to a planet without an extra lookup table. Zero-planet reports yield a well-formed empty world; malformed dimensions fall back to 1×1 so a bad report cannot crash the renderer. The map view's mount effect creates the renderer once and skips re-mount on no-op refreshes (same turn, same wrap mode); a turn change or wrap-mode flip disposes and recreates it. The renderer's external API does not yet expose `setWorld`; Phase 24 / 34 will extract it once high-frequency updates land. The store installs a `visibilitychange` listener that calls `refresh()` when the tab regains focus. Wrap-mode preference uses `Cache` namespace `game-prefs`, key `<gameId>/wrap-mode`, default `torus`. Phase 11 reads through `store.wrapMode`; Phase 29 wires the toggle UI on top of `setWrapMode`. Tests: Vitest unit coverage for `reportToWorld` (every kind, ids, styling, empty / zero-dimension edges, priority order) and for the store lifecycle (init success, missing-membership error, forbidden-result error, `setTurn`, wrap-mode persistence across instances, `failBootstrap`). Playwright e2e mocks the gateway for `lobby.my.games.list` and `user.games.report` and asserts the live data path: turn counter shows the reported turn, `active-view-map` flips to `data-status="ready"`, and `data-planet-count` matches the fixture count. The zero-planet regression and the missing-membership error path are covered. Phase 11 status stays `pending` in `ui/PLAN.md` until the local-ci run lands green; flipping to `done` follows in the next commit per the per-stage CI gate in `CLAUDE.md`. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
892 lines
34 KiB
Go
892 lines
34 KiB
Go
package transcoder
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
lobbymodel "galaxy/model/lobby"
|
|
lobbyfbs "galaxy/schema/fbs/lobby"
|
|
|
|
flatbuffers "github.com/google/flatbuffers/go"
|
|
)
|
|
|
|
// MyGamesListRequestToPayload converts a typed lobbymodel.MyGamesListRequest
|
|
// to FlatBuffers bytes suitable for the authenticated gateway transport.
|
|
func MyGamesListRequestToPayload(request *lobbymodel.MyGamesListRequest) ([]byte, error) {
|
|
if request == nil {
|
|
return nil, errors.New("encode my games list request payload: request is nil")
|
|
}
|
|
|
|
builder := flatbuffers.NewBuilder(32)
|
|
lobbyfbs.MyGamesListRequestStart(builder)
|
|
offset := lobbyfbs.MyGamesListRequestEnd(builder)
|
|
lobbyfbs.FinishMyGamesListRequestBuffer(builder, offset)
|
|
|
|
return builder.FinishedBytes(), nil
|
|
}
|
|
|
|
// PayloadToMyGamesListRequest converts FlatBuffers payload bytes into
|
|
// lobbymodel.MyGamesListRequest.
|
|
func PayloadToMyGamesListRequest(data []byte) (result *lobbymodel.MyGamesListRequest, err error) {
|
|
if len(data) == 0 {
|
|
return nil, errors.New("decode my games list request payload: data is empty")
|
|
}
|
|
|
|
defer recoverLobbyDecodePanic("decode my games list request payload", &result, &err)
|
|
|
|
_ = lobbyfbs.GetRootAsMyGamesListRequest(data, 0)
|
|
return &lobbymodel.MyGamesListRequest{}, nil
|
|
}
|
|
|
|
// MyGamesListResponseToPayload converts lobbymodel.MyGamesListResponse to
|
|
// FlatBuffers bytes suitable for the authenticated gateway transport.
|
|
func MyGamesListResponseToPayload(response *lobbymodel.MyGamesListResponse) ([]byte, error) {
|
|
if response == nil {
|
|
return nil, errors.New("encode my games list response payload: response is nil")
|
|
}
|
|
|
|
builder := flatbuffers.NewBuilder(512)
|
|
|
|
itemOffsets := make([]flatbuffers.UOffsetT, len(response.Items))
|
|
for index := range response.Items {
|
|
itemOffsets[index] = encodeGameSummary(builder, response.Items[index])
|
|
}
|
|
|
|
itemsVector := finishOffsetVector(builder, itemOffsets)
|
|
|
|
lobbyfbs.MyGamesListResponseStart(builder)
|
|
if itemsVector != 0 {
|
|
lobbyfbs.MyGamesListResponseAddItems(builder, itemsVector)
|
|
}
|
|
offset := lobbyfbs.MyGamesListResponseEnd(builder)
|
|
lobbyfbs.FinishMyGamesListResponseBuffer(builder, offset)
|
|
|
|
return builder.FinishedBytes(), nil
|
|
}
|
|
|
|
// PayloadToMyGamesListResponse converts FlatBuffers payload bytes into
|
|
// lobbymodel.MyGamesListResponse.
|
|
func PayloadToMyGamesListResponse(data []byte) (result *lobbymodel.MyGamesListResponse, err error) {
|
|
if len(data) == 0 {
|
|
return nil, errors.New("decode my games list response payload: data is empty")
|
|
}
|
|
|
|
defer recoverLobbyDecodePanic("decode my games list response payload", &result, &err)
|
|
|
|
response := lobbyfbs.GetRootAsMyGamesListResponse(data, 0)
|
|
out := &lobbymodel.MyGamesListResponse{
|
|
Items: make([]lobbymodel.GameSummary, 0, response.ItemsLength()),
|
|
}
|
|
|
|
summary := new(lobbyfbs.GameSummary)
|
|
for index := 0; index < response.ItemsLength(); index++ {
|
|
if !response.Items(summary, index) {
|
|
return nil, fmt.Errorf("decode my games list response payload: items[%d] is missing", index)
|
|
}
|
|
out.Items = append(out.Items, decodeGameSummary(summary))
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// PublicGamesListRequestToPayload converts lobbymodel.PublicGamesListRequest to
|
|
// FlatBuffers bytes.
|
|
func PublicGamesListRequestToPayload(request *lobbymodel.PublicGamesListRequest) ([]byte, error) {
|
|
if request == nil {
|
|
return nil, errors.New("encode public games list request payload: request is nil")
|
|
}
|
|
|
|
builder := flatbuffers.NewBuilder(32)
|
|
lobbyfbs.PublicGamesListRequestStart(builder)
|
|
lobbyfbs.PublicGamesListRequestAddPage(builder, int32(request.Page))
|
|
lobbyfbs.PublicGamesListRequestAddPageSize(builder, int32(request.PageSize))
|
|
offset := lobbyfbs.PublicGamesListRequestEnd(builder)
|
|
lobbyfbs.FinishPublicGamesListRequestBuffer(builder, offset)
|
|
|
|
return builder.FinishedBytes(), nil
|
|
}
|
|
|
|
// PayloadToPublicGamesListRequest converts FlatBuffers payload bytes into
|
|
// lobbymodel.PublicGamesListRequest.
|
|
func PayloadToPublicGamesListRequest(data []byte) (result *lobbymodel.PublicGamesListRequest, err error) {
|
|
if len(data) == 0 {
|
|
return nil, errors.New("decode public games list request payload: data is empty")
|
|
}
|
|
|
|
defer recoverLobbyDecodePanic("decode public games list request payload", &result, &err)
|
|
|
|
request := lobbyfbs.GetRootAsPublicGamesListRequest(data, 0)
|
|
return &lobbymodel.PublicGamesListRequest{
|
|
Page: int(request.Page()),
|
|
PageSize: int(request.PageSize()),
|
|
}, nil
|
|
}
|
|
|
|
// PublicGamesListResponseToPayload converts lobbymodel.PublicGamesListResponse
|
|
// to FlatBuffers bytes.
|
|
func PublicGamesListResponseToPayload(response *lobbymodel.PublicGamesListResponse) ([]byte, error) {
|
|
if response == nil {
|
|
return nil, errors.New("encode public games list response payload: response is nil")
|
|
}
|
|
|
|
builder := flatbuffers.NewBuilder(512)
|
|
|
|
itemOffsets := make([]flatbuffers.UOffsetT, len(response.Items))
|
|
for index := range response.Items {
|
|
itemOffsets[index] = encodeGameSummary(builder, response.Items[index])
|
|
}
|
|
itemsVector := finishOffsetVector(builder, itemOffsets)
|
|
|
|
lobbyfbs.PublicGamesListResponseStart(builder)
|
|
if itemsVector != 0 {
|
|
lobbyfbs.PublicGamesListResponseAddItems(builder, itemsVector)
|
|
}
|
|
lobbyfbs.PublicGamesListResponseAddPage(builder, int32(response.Page))
|
|
lobbyfbs.PublicGamesListResponseAddPageSize(builder, int32(response.PageSize))
|
|
lobbyfbs.PublicGamesListResponseAddTotal(builder, int32(response.Total))
|
|
offset := lobbyfbs.PublicGamesListResponseEnd(builder)
|
|
lobbyfbs.FinishPublicGamesListResponseBuffer(builder, offset)
|
|
|
|
return builder.FinishedBytes(), nil
|
|
}
|
|
|
|
// PayloadToPublicGamesListResponse converts FlatBuffers payload bytes into
|
|
// lobbymodel.PublicGamesListResponse.
|
|
func PayloadToPublicGamesListResponse(data []byte) (result *lobbymodel.PublicGamesListResponse, err error) {
|
|
if len(data) == 0 {
|
|
return nil, errors.New("decode public games list response payload: data is empty")
|
|
}
|
|
|
|
defer recoverLobbyDecodePanic("decode public games list response payload", &result, &err)
|
|
|
|
response := lobbyfbs.GetRootAsPublicGamesListResponse(data, 0)
|
|
out := &lobbymodel.PublicGamesListResponse{
|
|
Items: make([]lobbymodel.GameSummary, 0, response.ItemsLength()),
|
|
Page: int(response.Page()),
|
|
PageSize: int(response.PageSize()),
|
|
Total: int(response.Total()),
|
|
}
|
|
|
|
summary := new(lobbyfbs.GameSummary)
|
|
for index := 0; index < response.ItemsLength(); index++ {
|
|
if !response.Items(summary, index) {
|
|
return nil, fmt.Errorf("decode public games list response payload: items[%d] is missing", index)
|
|
}
|
|
out.Items = append(out.Items, decodeGameSummary(summary))
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// MyApplicationsListRequestToPayload converts lobbymodel.MyApplicationsListRequest
|
|
// to FlatBuffers bytes.
|
|
func MyApplicationsListRequestToPayload(request *lobbymodel.MyApplicationsListRequest) ([]byte, error) {
|
|
if request == nil {
|
|
return nil, errors.New("encode my applications list request payload: request is nil")
|
|
}
|
|
|
|
builder := flatbuffers.NewBuilder(32)
|
|
lobbyfbs.MyApplicationsListRequestStart(builder)
|
|
offset := lobbyfbs.MyApplicationsListRequestEnd(builder)
|
|
lobbyfbs.FinishMyApplicationsListRequestBuffer(builder, offset)
|
|
|
|
return builder.FinishedBytes(), nil
|
|
}
|
|
|
|
// PayloadToMyApplicationsListRequest converts FlatBuffers payload bytes into
|
|
// lobbymodel.MyApplicationsListRequest.
|
|
func PayloadToMyApplicationsListRequest(data []byte) (result *lobbymodel.MyApplicationsListRequest, err error) {
|
|
if len(data) == 0 {
|
|
return nil, errors.New("decode my applications list request payload: data is empty")
|
|
}
|
|
|
|
defer recoverLobbyDecodePanic("decode my applications list request payload", &result, &err)
|
|
|
|
_ = lobbyfbs.GetRootAsMyApplicationsListRequest(data, 0)
|
|
return &lobbymodel.MyApplicationsListRequest{}, nil
|
|
}
|
|
|
|
// MyApplicationsListResponseToPayload converts lobbymodel.MyApplicationsListResponse
|
|
// to FlatBuffers bytes.
|
|
func MyApplicationsListResponseToPayload(response *lobbymodel.MyApplicationsListResponse) ([]byte, error) {
|
|
if response == nil {
|
|
return nil, errors.New("encode my applications list response payload: response is nil")
|
|
}
|
|
|
|
builder := flatbuffers.NewBuilder(512)
|
|
|
|
itemOffsets := make([]flatbuffers.UOffsetT, len(response.Items))
|
|
for index := range response.Items {
|
|
itemOffsets[index] = encodeApplicationSummary(builder, response.Items[index])
|
|
}
|
|
itemsVector := finishOffsetVector(builder, itemOffsets)
|
|
|
|
lobbyfbs.MyApplicationsListResponseStart(builder)
|
|
if itemsVector != 0 {
|
|
lobbyfbs.MyApplicationsListResponseAddItems(builder, itemsVector)
|
|
}
|
|
offset := lobbyfbs.MyApplicationsListResponseEnd(builder)
|
|
lobbyfbs.FinishMyApplicationsListResponseBuffer(builder, offset)
|
|
|
|
return builder.FinishedBytes(), nil
|
|
}
|
|
|
|
// PayloadToMyApplicationsListResponse converts FlatBuffers payload bytes into
|
|
// lobbymodel.MyApplicationsListResponse.
|
|
func PayloadToMyApplicationsListResponse(data []byte) (result *lobbymodel.MyApplicationsListResponse, err error) {
|
|
if len(data) == 0 {
|
|
return nil, errors.New("decode my applications list response payload: data is empty")
|
|
}
|
|
|
|
defer recoverLobbyDecodePanic("decode my applications list response payload", &result, &err)
|
|
|
|
response := lobbyfbs.GetRootAsMyApplicationsListResponse(data, 0)
|
|
out := &lobbymodel.MyApplicationsListResponse{
|
|
Items: make([]lobbymodel.ApplicationSummary, 0, response.ItemsLength()),
|
|
}
|
|
|
|
app := new(lobbyfbs.ApplicationSummary)
|
|
for index := 0; index < response.ItemsLength(); index++ {
|
|
if !response.Items(app, index) {
|
|
return nil, fmt.Errorf("decode my applications list response payload: items[%d] is missing", index)
|
|
}
|
|
out.Items = append(out.Items, decodeApplicationSummary(app))
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// MyInvitesListRequestToPayload converts lobbymodel.MyInvitesListRequest
|
|
// to FlatBuffers bytes.
|
|
func MyInvitesListRequestToPayload(request *lobbymodel.MyInvitesListRequest) ([]byte, error) {
|
|
if request == nil {
|
|
return nil, errors.New("encode my invites list request payload: request is nil")
|
|
}
|
|
|
|
builder := flatbuffers.NewBuilder(32)
|
|
lobbyfbs.MyInvitesListRequestStart(builder)
|
|
offset := lobbyfbs.MyInvitesListRequestEnd(builder)
|
|
lobbyfbs.FinishMyInvitesListRequestBuffer(builder, offset)
|
|
|
|
return builder.FinishedBytes(), nil
|
|
}
|
|
|
|
// PayloadToMyInvitesListRequest converts FlatBuffers payload bytes into
|
|
// lobbymodel.MyInvitesListRequest.
|
|
func PayloadToMyInvitesListRequest(data []byte) (result *lobbymodel.MyInvitesListRequest, err error) {
|
|
if len(data) == 0 {
|
|
return nil, errors.New("decode my invites list request payload: data is empty")
|
|
}
|
|
|
|
defer recoverLobbyDecodePanic("decode my invites list request payload", &result, &err)
|
|
|
|
_ = lobbyfbs.GetRootAsMyInvitesListRequest(data, 0)
|
|
return &lobbymodel.MyInvitesListRequest{}, nil
|
|
}
|
|
|
|
// MyInvitesListResponseToPayload converts lobbymodel.MyInvitesListResponse
|
|
// to FlatBuffers bytes.
|
|
func MyInvitesListResponseToPayload(response *lobbymodel.MyInvitesListResponse) ([]byte, error) {
|
|
if response == nil {
|
|
return nil, errors.New("encode my invites list response payload: response is nil")
|
|
}
|
|
|
|
builder := flatbuffers.NewBuilder(512)
|
|
|
|
itemOffsets := make([]flatbuffers.UOffsetT, len(response.Items))
|
|
for index := range response.Items {
|
|
itemOffsets[index] = encodeInviteSummary(builder, response.Items[index])
|
|
}
|
|
itemsVector := finishOffsetVector(builder, itemOffsets)
|
|
|
|
lobbyfbs.MyInvitesListResponseStart(builder)
|
|
if itemsVector != 0 {
|
|
lobbyfbs.MyInvitesListResponseAddItems(builder, itemsVector)
|
|
}
|
|
offset := lobbyfbs.MyInvitesListResponseEnd(builder)
|
|
lobbyfbs.FinishMyInvitesListResponseBuffer(builder, offset)
|
|
|
|
return builder.FinishedBytes(), nil
|
|
}
|
|
|
|
// PayloadToMyInvitesListResponse converts FlatBuffers payload bytes into
|
|
// lobbymodel.MyInvitesListResponse.
|
|
func PayloadToMyInvitesListResponse(data []byte) (result *lobbymodel.MyInvitesListResponse, err error) {
|
|
if len(data) == 0 {
|
|
return nil, errors.New("decode my invites list response payload: data is empty")
|
|
}
|
|
|
|
defer recoverLobbyDecodePanic("decode my invites list response payload", &result, &err)
|
|
|
|
response := lobbyfbs.GetRootAsMyInvitesListResponse(data, 0)
|
|
out := &lobbymodel.MyInvitesListResponse{
|
|
Items: make([]lobbymodel.InviteSummary, 0, response.ItemsLength()),
|
|
}
|
|
|
|
invite := new(lobbyfbs.InviteSummary)
|
|
for index := 0; index < response.ItemsLength(); index++ {
|
|
if !response.Items(invite, index) {
|
|
return nil, fmt.Errorf("decode my invites list response payload: items[%d] is missing", index)
|
|
}
|
|
out.Items = append(out.Items, decodeInviteSummary(invite))
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
// OpenEnrollmentRequestToPayload converts lobbymodel.OpenEnrollmentRequest to
|
|
// FlatBuffers bytes suitable for the authenticated gateway transport.
|
|
func OpenEnrollmentRequestToPayload(request *lobbymodel.OpenEnrollmentRequest) ([]byte, error) {
|
|
if request == nil {
|
|
return nil, errors.New("encode open enrollment request payload: request is nil")
|
|
}
|
|
|
|
builder := flatbuffers.NewBuilder(64)
|
|
gameID := builder.CreateString(request.GameID)
|
|
|
|
lobbyfbs.OpenEnrollmentRequestStart(builder)
|
|
lobbyfbs.OpenEnrollmentRequestAddGameId(builder, gameID)
|
|
offset := lobbyfbs.OpenEnrollmentRequestEnd(builder)
|
|
lobbyfbs.FinishOpenEnrollmentRequestBuffer(builder, offset)
|
|
|
|
return builder.FinishedBytes(), nil
|
|
}
|
|
|
|
// PayloadToOpenEnrollmentRequest converts FlatBuffers payload bytes into
|
|
// lobbymodel.OpenEnrollmentRequest.
|
|
func PayloadToOpenEnrollmentRequest(data []byte) (result *lobbymodel.OpenEnrollmentRequest, err error) {
|
|
if len(data) == 0 {
|
|
return nil, errors.New("decode open enrollment request payload: data is empty")
|
|
}
|
|
|
|
defer recoverLobbyDecodePanic("decode open enrollment request payload", &result, &err)
|
|
|
|
request := lobbyfbs.GetRootAsOpenEnrollmentRequest(data, 0)
|
|
return &lobbymodel.OpenEnrollmentRequest{
|
|
GameID: string(request.GameId()),
|
|
}, nil
|
|
}
|
|
|
|
// OpenEnrollmentResponseToPayload converts lobbymodel.OpenEnrollmentResponse to
|
|
// FlatBuffers bytes suitable for the authenticated gateway transport.
|
|
func OpenEnrollmentResponseToPayload(response *lobbymodel.OpenEnrollmentResponse) ([]byte, error) {
|
|
if response == nil {
|
|
return nil, errors.New("encode open enrollment response payload: response is nil")
|
|
}
|
|
|
|
builder := flatbuffers.NewBuilder(64)
|
|
gameID := builder.CreateString(response.GameID)
|
|
status := builder.CreateString(response.Status)
|
|
|
|
lobbyfbs.OpenEnrollmentResponseStart(builder)
|
|
lobbyfbs.OpenEnrollmentResponseAddGameId(builder, gameID)
|
|
lobbyfbs.OpenEnrollmentResponseAddStatus(builder, status)
|
|
offset := lobbyfbs.OpenEnrollmentResponseEnd(builder)
|
|
lobbyfbs.FinishOpenEnrollmentResponseBuffer(builder, offset)
|
|
|
|
return builder.FinishedBytes(), nil
|
|
}
|
|
|
|
// PayloadToOpenEnrollmentResponse converts FlatBuffers payload bytes into
|
|
// lobbymodel.OpenEnrollmentResponse.
|
|
func PayloadToOpenEnrollmentResponse(data []byte) (result *lobbymodel.OpenEnrollmentResponse, err error) {
|
|
if len(data) == 0 {
|
|
return nil, errors.New("decode open enrollment response payload: data is empty")
|
|
}
|
|
|
|
defer recoverLobbyDecodePanic("decode open enrollment response payload", &result, &err)
|
|
|
|
response := lobbyfbs.GetRootAsOpenEnrollmentResponse(data, 0)
|
|
return &lobbymodel.OpenEnrollmentResponse{
|
|
GameID: string(response.GameId()),
|
|
Status: string(response.Status()),
|
|
}, nil
|
|
}
|
|
|
|
// GameCreateRequestToPayload converts lobbymodel.GameCreateRequest to
|
|
// FlatBuffers bytes.
|
|
func GameCreateRequestToPayload(request *lobbymodel.GameCreateRequest) ([]byte, error) {
|
|
if request == nil {
|
|
return nil, errors.New("encode game create request payload: request is nil")
|
|
}
|
|
|
|
builder := flatbuffers.NewBuilder(256)
|
|
gameName := builder.CreateString(request.GameName)
|
|
description := builder.CreateString(request.Description)
|
|
turnSchedule := builder.CreateString(request.TurnSchedule)
|
|
targetEngineVersion := builder.CreateString(request.TargetEngineVersion)
|
|
|
|
lobbyfbs.GameCreateRequestStart(builder)
|
|
lobbyfbs.GameCreateRequestAddGameName(builder, gameName)
|
|
lobbyfbs.GameCreateRequestAddDescription(builder, description)
|
|
lobbyfbs.GameCreateRequestAddMinPlayers(builder, int32(request.MinPlayers))
|
|
lobbyfbs.GameCreateRequestAddMaxPlayers(builder, int32(request.MaxPlayers))
|
|
lobbyfbs.GameCreateRequestAddStartGapHours(builder, int32(request.StartGapHours))
|
|
lobbyfbs.GameCreateRequestAddStartGapPlayers(builder, int32(request.StartGapPlayers))
|
|
lobbyfbs.GameCreateRequestAddEnrollmentEndsAtMs(builder, request.EnrollmentEndsAt.UTC().UnixMilli())
|
|
lobbyfbs.GameCreateRequestAddTurnSchedule(builder, turnSchedule)
|
|
lobbyfbs.GameCreateRequestAddTargetEngineVersion(builder, targetEngineVersion)
|
|
offset := lobbyfbs.GameCreateRequestEnd(builder)
|
|
lobbyfbs.FinishGameCreateRequestBuffer(builder, offset)
|
|
|
|
return builder.FinishedBytes(), nil
|
|
}
|
|
|
|
// PayloadToGameCreateRequest converts FlatBuffers payload bytes into
|
|
// lobbymodel.GameCreateRequest.
|
|
func PayloadToGameCreateRequest(data []byte) (result *lobbymodel.GameCreateRequest, err error) {
|
|
if len(data) == 0 {
|
|
return nil, errors.New("decode game create request payload: data is empty")
|
|
}
|
|
|
|
defer recoverLobbyDecodePanic("decode game create request payload", &result, &err)
|
|
|
|
request := lobbyfbs.GetRootAsGameCreateRequest(data, 0)
|
|
return &lobbymodel.GameCreateRequest{
|
|
GameName: string(request.GameName()),
|
|
Description: string(request.Description()),
|
|
MinPlayers: int(request.MinPlayers()),
|
|
MaxPlayers: int(request.MaxPlayers()),
|
|
StartGapHours: int(request.StartGapHours()),
|
|
StartGapPlayers: int(request.StartGapPlayers()),
|
|
EnrollmentEndsAt: time.UnixMilli(request.EnrollmentEndsAtMs()).UTC(),
|
|
TurnSchedule: string(request.TurnSchedule()),
|
|
TargetEngineVersion: string(request.TargetEngineVersion()),
|
|
}, nil
|
|
}
|
|
|
|
// GameCreateResponseToPayload converts lobbymodel.GameCreateResponse to
|
|
// FlatBuffers bytes.
|
|
func GameCreateResponseToPayload(response *lobbymodel.GameCreateResponse) ([]byte, error) {
|
|
if response == nil {
|
|
return nil, errors.New("encode game create response payload: response is nil")
|
|
}
|
|
|
|
builder := flatbuffers.NewBuilder(256)
|
|
gameOffset := encodeGameSummary(builder, response.Game)
|
|
|
|
lobbyfbs.GameCreateResponseStart(builder)
|
|
lobbyfbs.GameCreateResponseAddGame(builder, gameOffset)
|
|
offset := lobbyfbs.GameCreateResponseEnd(builder)
|
|
lobbyfbs.FinishGameCreateResponseBuffer(builder, offset)
|
|
|
|
return builder.FinishedBytes(), nil
|
|
}
|
|
|
|
// PayloadToGameCreateResponse converts FlatBuffers payload bytes into
|
|
// lobbymodel.GameCreateResponse.
|
|
func PayloadToGameCreateResponse(data []byte) (result *lobbymodel.GameCreateResponse, err error) {
|
|
if len(data) == 0 {
|
|
return nil, errors.New("decode game create response payload: data is empty")
|
|
}
|
|
|
|
defer recoverLobbyDecodePanic("decode game create response payload", &result, &err)
|
|
|
|
response := lobbyfbs.GetRootAsGameCreateResponse(data, 0)
|
|
game := response.Game(nil)
|
|
if game == nil {
|
|
return nil, errors.New("decode game create response payload: game is missing")
|
|
}
|
|
return &lobbymodel.GameCreateResponse{
|
|
Game: decodeGameSummary(game),
|
|
}, nil
|
|
}
|
|
|
|
// ApplicationSubmitRequestToPayload converts lobbymodel.ApplicationSubmitRequest
|
|
// to FlatBuffers bytes.
|
|
func ApplicationSubmitRequestToPayload(request *lobbymodel.ApplicationSubmitRequest) ([]byte, error) {
|
|
if request == nil {
|
|
return nil, errors.New("encode application submit request payload: request is nil")
|
|
}
|
|
|
|
builder := flatbuffers.NewBuilder(128)
|
|
gameID := builder.CreateString(request.GameID)
|
|
raceName := builder.CreateString(request.RaceName)
|
|
|
|
lobbyfbs.ApplicationSubmitRequestStart(builder)
|
|
lobbyfbs.ApplicationSubmitRequestAddGameId(builder, gameID)
|
|
lobbyfbs.ApplicationSubmitRequestAddRaceName(builder, raceName)
|
|
offset := lobbyfbs.ApplicationSubmitRequestEnd(builder)
|
|
lobbyfbs.FinishApplicationSubmitRequestBuffer(builder, offset)
|
|
|
|
return builder.FinishedBytes(), nil
|
|
}
|
|
|
|
// PayloadToApplicationSubmitRequest converts FlatBuffers payload bytes into
|
|
// lobbymodel.ApplicationSubmitRequest.
|
|
func PayloadToApplicationSubmitRequest(data []byte) (result *lobbymodel.ApplicationSubmitRequest, err error) {
|
|
if len(data) == 0 {
|
|
return nil, errors.New("decode application submit request payload: data is empty")
|
|
}
|
|
|
|
defer recoverLobbyDecodePanic("decode application submit request payload", &result, &err)
|
|
|
|
request := lobbyfbs.GetRootAsApplicationSubmitRequest(data, 0)
|
|
return &lobbymodel.ApplicationSubmitRequest{
|
|
GameID: string(request.GameId()),
|
|
RaceName: string(request.RaceName()),
|
|
}, nil
|
|
}
|
|
|
|
// ApplicationSubmitResponseToPayload converts lobbymodel.ApplicationSubmitResponse
|
|
// to FlatBuffers bytes.
|
|
func ApplicationSubmitResponseToPayload(response *lobbymodel.ApplicationSubmitResponse) ([]byte, error) {
|
|
if response == nil {
|
|
return nil, errors.New("encode application submit response payload: response is nil")
|
|
}
|
|
|
|
builder := flatbuffers.NewBuilder(256)
|
|
appOffset := encodeApplicationSummary(builder, response.Application)
|
|
|
|
lobbyfbs.ApplicationSubmitResponseStart(builder)
|
|
lobbyfbs.ApplicationSubmitResponseAddApplication(builder, appOffset)
|
|
offset := lobbyfbs.ApplicationSubmitResponseEnd(builder)
|
|
lobbyfbs.FinishApplicationSubmitResponseBuffer(builder, offset)
|
|
|
|
return builder.FinishedBytes(), nil
|
|
}
|
|
|
|
// PayloadToApplicationSubmitResponse converts FlatBuffers payload bytes into
|
|
// lobbymodel.ApplicationSubmitResponse.
|
|
func PayloadToApplicationSubmitResponse(data []byte) (result *lobbymodel.ApplicationSubmitResponse, err error) {
|
|
if len(data) == 0 {
|
|
return nil, errors.New("decode application submit response payload: data is empty")
|
|
}
|
|
|
|
defer recoverLobbyDecodePanic("decode application submit response payload", &result, &err)
|
|
|
|
response := lobbyfbs.GetRootAsApplicationSubmitResponse(data, 0)
|
|
app := response.Application(nil)
|
|
if app == nil {
|
|
return nil, errors.New("decode application submit response payload: application is missing")
|
|
}
|
|
return &lobbymodel.ApplicationSubmitResponse{
|
|
Application: decodeApplicationSummary(app),
|
|
}, nil
|
|
}
|
|
|
|
// InviteRedeemRequestToPayload converts lobbymodel.InviteRedeemRequest to
|
|
// FlatBuffers bytes.
|
|
func InviteRedeemRequestToPayload(request *lobbymodel.InviteRedeemRequest) ([]byte, error) {
|
|
if request == nil {
|
|
return nil, errors.New("encode invite redeem request payload: request is nil")
|
|
}
|
|
return encodeInviteAction(request.GameID, request.InviteID, true)
|
|
}
|
|
|
|
// PayloadToInviteRedeemRequest converts FlatBuffers payload bytes into
|
|
// lobbymodel.InviteRedeemRequest.
|
|
func PayloadToInviteRedeemRequest(data []byte) (result *lobbymodel.InviteRedeemRequest, err error) {
|
|
if len(data) == 0 {
|
|
return nil, errors.New("decode invite redeem request payload: data is empty")
|
|
}
|
|
|
|
defer recoverLobbyDecodePanic("decode invite redeem request payload", &result, &err)
|
|
|
|
request := lobbyfbs.GetRootAsInviteRedeemRequest(data, 0)
|
|
return &lobbymodel.InviteRedeemRequest{
|
|
GameID: string(request.GameId()),
|
|
InviteID: string(request.InviteId()),
|
|
}, nil
|
|
}
|
|
|
|
// InviteRedeemResponseToPayload converts lobbymodel.InviteRedeemResponse to
|
|
// FlatBuffers bytes.
|
|
func InviteRedeemResponseToPayload(response *lobbymodel.InviteRedeemResponse) ([]byte, error) {
|
|
if response == nil {
|
|
return nil, errors.New("encode invite redeem response payload: response is nil")
|
|
}
|
|
|
|
builder := flatbuffers.NewBuilder(256)
|
|
inviteOffset := encodeInviteSummary(builder, response.Invite)
|
|
|
|
lobbyfbs.InviteRedeemResponseStart(builder)
|
|
lobbyfbs.InviteRedeemResponseAddInvite(builder, inviteOffset)
|
|
offset := lobbyfbs.InviteRedeemResponseEnd(builder)
|
|
lobbyfbs.FinishInviteRedeemResponseBuffer(builder, offset)
|
|
|
|
return builder.FinishedBytes(), nil
|
|
}
|
|
|
|
// PayloadToInviteRedeemResponse converts FlatBuffers payload bytes into
|
|
// lobbymodel.InviteRedeemResponse.
|
|
func PayloadToInviteRedeemResponse(data []byte) (result *lobbymodel.InviteRedeemResponse, err error) {
|
|
if len(data) == 0 {
|
|
return nil, errors.New("decode invite redeem response payload: data is empty")
|
|
}
|
|
|
|
defer recoverLobbyDecodePanic("decode invite redeem response payload", &result, &err)
|
|
|
|
response := lobbyfbs.GetRootAsInviteRedeemResponse(data, 0)
|
|
invite := response.Invite(nil)
|
|
if invite == nil {
|
|
return nil, errors.New("decode invite redeem response payload: invite is missing")
|
|
}
|
|
return &lobbymodel.InviteRedeemResponse{
|
|
Invite: decodeInviteSummary(invite),
|
|
}, nil
|
|
}
|
|
|
|
// InviteDeclineRequestToPayload converts lobbymodel.InviteDeclineRequest to
|
|
// FlatBuffers bytes.
|
|
func InviteDeclineRequestToPayload(request *lobbymodel.InviteDeclineRequest) ([]byte, error) {
|
|
if request == nil {
|
|
return nil, errors.New("encode invite decline request payload: request is nil")
|
|
}
|
|
return encodeInviteAction(request.GameID, request.InviteID, false)
|
|
}
|
|
|
|
// PayloadToInviteDeclineRequest converts FlatBuffers payload bytes into
|
|
// lobbymodel.InviteDeclineRequest.
|
|
func PayloadToInviteDeclineRequest(data []byte) (result *lobbymodel.InviteDeclineRequest, err error) {
|
|
if len(data) == 0 {
|
|
return nil, errors.New("decode invite decline request payload: data is empty")
|
|
}
|
|
|
|
defer recoverLobbyDecodePanic("decode invite decline request payload", &result, &err)
|
|
|
|
request := lobbyfbs.GetRootAsInviteDeclineRequest(data, 0)
|
|
return &lobbymodel.InviteDeclineRequest{
|
|
GameID: string(request.GameId()),
|
|
InviteID: string(request.InviteId()),
|
|
}, nil
|
|
}
|
|
|
|
// InviteDeclineResponseToPayload converts lobbymodel.InviteDeclineResponse to
|
|
// FlatBuffers bytes.
|
|
func InviteDeclineResponseToPayload(response *lobbymodel.InviteDeclineResponse) ([]byte, error) {
|
|
if response == nil {
|
|
return nil, errors.New("encode invite decline response payload: response is nil")
|
|
}
|
|
|
|
builder := flatbuffers.NewBuilder(256)
|
|
inviteOffset := encodeInviteSummary(builder, response.Invite)
|
|
|
|
lobbyfbs.InviteDeclineResponseStart(builder)
|
|
lobbyfbs.InviteDeclineResponseAddInvite(builder, inviteOffset)
|
|
offset := lobbyfbs.InviteDeclineResponseEnd(builder)
|
|
lobbyfbs.FinishInviteDeclineResponseBuffer(builder, offset)
|
|
|
|
return builder.FinishedBytes(), nil
|
|
}
|
|
|
|
// PayloadToInviteDeclineResponse converts FlatBuffers payload bytes into
|
|
// lobbymodel.InviteDeclineResponse.
|
|
func PayloadToInviteDeclineResponse(data []byte) (result *lobbymodel.InviteDeclineResponse, err error) {
|
|
if len(data) == 0 {
|
|
return nil, errors.New("decode invite decline response payload: data is empty")
|
|
}
|
|
|
|
defer recoverLobbyDecodePanic("decode invite decline response payload", &result, &err)
|
|
|
|
response := lobbyfbs.GetRootAsInviteDeclineResponse(data, 0)
|
|
invite := response.Invite(nil)
|
|
if invite == nil {
|
|
return nil, errors.New("decode invite decline response payload: invite is missing")
|
|
}
|
|
return &lobbymodel.InviteDeclineResponse{
|
|
Invite: decodeInviteSummary(invite),
|
|
}, nil
|
|
}
|
|
|
|
// LobbyErrorResponseToPayload converts lobbymodel.ErrorResponse to FlatBuffers
|
|
// bytes suitable for the authenticated gateway transport.
|
|
func LobbyErrorResponseToPayload(response *lobbymodel.ErrorResponse) ([]byte, error) {
|
|
if response == nil {
|
|
return nil, errors.New("encode lobby error response payload: response is nil")
|
|
}
|
|
|
|
builder := flatbuffers.NewBuilder(128)
|
|
code := builder.CreateString(response.Error.Code)
|
|
message := builder.CreateString(response.Error.Message)
|
|
|
|
lobbyfbs.ErrorBodyStart(builder)
|
|
lobbyfbs.ErrorBodyAddCode(builder, code)
|
|
lobbyfbs.ErrorBodyAddMessage(builder, message)
|
|
errorOffset := lobbyfbs.ErrorBodyEnd(builder)
|
|
|
|
lobbyfbs.ErrorResponseStart(builder)
|
|
lobbyfbs.ErrorResponseAddError(builder, errorOffset)
|
|
offset := lobbyfbs.ErrorResponseEnd(builder)
|
|
lobbyfbs.FinishErrorResponseBuffer(builder, offset)
|
|
|
|
return builder.FinishedBytes(), nil
|
|
}
|
|
|
|
// PayloadToLobbyErrorResponse converts FlatBuffers payload bytes into
|
|
// lobbymodel.ErrorResponse.
|
|
func PayloadToLobbyErrorResponse(data []byte) (result *lobbymodel.ErrorResponse, err error) {
|
|
if len(data) == 0 {
|
|
return nil, errors.New("decode lobby error response payload: data is empty")
|
|
}
|
|
|
|
defer recoverLobbyDecodePanic("decode lobby error response payload", &result, &err)
|
|
|
|
response := lobbyfbs.GetRootAsErrorResponse(data, 0)
|
|
body := response.Error(nil)
|
|
if body == nil {
|
|
return nil, errors.New("decode lobby error response payload: error is missing")
|
|
}
|
|
|
|
return &lobbymodel.ErrorResponse{
|
|
Error: lobbymodel.ErrorBody{
|
|
Code: string(body.Code()),
|
|
Message: string(body.Message()),
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
func encodeInviteAction(gameID, inviteID string, redeem bool) ([]byte, error) {
|
|
builder := flatbuffers.NewBuilder(128)
|
|
gameOffset := builder.CreateString(gameID)
|
|
inviteOffset := builder.CreateString(inviteID)
|
|
|
|
if redeem {
|
|
lobbyfbs.InviteRedeemRequestStart(builder)
|
|
lobbyfbs.InviteRedeemRequestAddGameId(builder, gameOffset)
|
|
lobbyfbs.InviteRedeemRequestAddInviteId(builder, inviteOffset)
|
|
offset := lobbyfbs.InviteRedeemRequestEnd(builder)
|
|
lobbyfbs.FinishInviteRedeemRequestBuffer(builder, offset)
|
|
} else {
|
|
lobbyfbs.InviteDeclineRequestStart(builder)
|
|
lobbyfbs.InviteDeclineRequestAddGameId(builder, gameOffset)
|
|
lobbyfbs.InviteDeclineRequestAddInviteId(builder, inviteOffset)
|
|
offset := lobbyfbs.InviteDeclineRequestEnd(builder)
|
|
lobbyfbs.FinishInviteDeclineRequestBuffer(builder, offset)
|
|
}
|
|
return builder.FinishedBytes(), nil
|
|
}
|
|
|
|
func finishOffsetVector(builder *flatbuffers.Builder, offsets []flatbuffers.UOffsetT) flatbuffers.UOffsetT {
|
|
if len(offsets) == 0 {
|
|
return 0
|
|
}
|
|
builder.StartVector(4, len(offsets), 4)
|
|
for index := len(offsets) - 1; index >= 0; index-- {
|
|
builder.PrependUOffsetT(offsets[index])
|
|
}
|
|
return builder.EndVector(len(offsets))
|
|
}
|
|
|
|
func encodeGameSummary(builder *flatbuffers.Builder, summary lobbymodel.GameSummary) flatbuffers.UOffsetT {
|
|
gameID := builder.CreateString(summary.GameID)
|
|
gameName := builder.CreateString(summary.GameName)
|
|
gameType := builder.CreateString(summary.GameType)
|
|
status := builder.CreateString(summary.Status)
|
|
ownerUserID := builder.CreateString(summary.OwnerUserID)
|
|
|
|
lobbyfbs.GameSummaryStart(builder)
|
|
lobbyfbs.GameSummaryAddGameId(builder, gameID)
|
|
lobbyfbs.GameSummaryAddGameName(builder, gameName)
|
|
lobbyfbs.GameSummaryAddGameType(builder, gameType)
|
|
lobbyfbs.GameSummaryAddStatus(builder, status)
|
|
lobbyfbs.GameSummaryAddOwnerUserId(builder, ownerUserID)
|
|
lobbyfbs.GameSummaryAddMinPlayers(builder, int32(summary.MinPlayers))
|
|
lobbyfbs.GameSummaryAddMaxPlayers(builder, int32(summary.MaxPlayers))
|
|
lobbyfbs.GameSummaryAddEnrollmentEndsAtMs(builder, summary.EnrollmentEndsAt.UTC().UnixMilli())
|
|
lobbyfbs.GameSummaryAddCreatedAtMs(builder, summary.CreatedAt.UTC().UnixMilli())
|
|
lobbyfbs.GameSummaryAddUpdatedAtMs(builder, summary.UpdatedAt.UTC().UnixMilli())
|
|
lobbyfbs.GameSummaryAddCurrentTurn(builder, summary.CurrentTurn)
|
|
return lobbyfbs.GameSummaryEnd(builder)
|
|
}
|
|
|
|
func decodeGameSummary(summary *lobbyfbs.GameSummary) lobbymodel.GameSummary {
|
|
return lobbymodel.GameSummary{
|
|
GameID: string(summary.GameId()),
|
|
GameName: string(summary.GameName()),
|
|
GameType: string(summary.GameType()),
|
|
Status: string(summary.Status()),
|
|
OwnerUserID: string(summary.OwnerUserId()),
|
|
MinPlayers: int(summary.MinPlayers()),
|
|
MaxPlayers: int(summary.MaxPlayers()),
|
|
EnrollmentEndsAt: time.UnixMilli(summary.EnrollmentEndsAtMs()).UTC(),
|
|
CreatedAt: time.UnixMilli(summary.CreatedAtMs()).UTC(),
|
|
UpdatedAt: time.UnixMilli(summary.UpdatedAtMs()).UTC(),
|
|
CurrentTurn: summary.CurrentTurn(),
|
|
}
|
|
}
|
|
|
|
func encodeApplicationSummary(builder *flatbuffers.Builder, app lobbymodel.ApplicationSummary) flatbuffers.UOffsetT {
|
|
applicationID := builder.CreateString(app.ApplicationID)
|
|
gameID := builder.CreateString(app.GameID)
|
|
applicantUserID := builder.CreateString(app.ApplicantUserID)
|
|
raceName := builder.CreateString(app.RaceName)
|
|
status := builder.CreateString(app.Status)
|
|
|
|
lobbyfbs.ApplicationSummaryStart(builder)
|
|
lobbyfbs.ApplicationSummaryAddApplicationId(builder, applicationID)
|
|
lobbyfbs.ApplicationSummaryAddGameId(builder, gameID)
|
|
lobbyfbs.ApplicationSummaryAddApplicantUserId(builder, applicantUserID)
|
|
lobbyfbs.ApplicationSummaryAddRaceName(builder, raceName)
|
|
lobbyfbs.ApplicationSummaryAddStatus(builder, status)
|
|
lobbyfbs.ApplicationSummaryAddCreatedAtMs(builder, app.CreatedAt.UTC().UnixMilli())
|
|
lobbyfbs.ApplicationSummaryAddDecidedAtMs(builder, unixMilliFromOptional(app.DecidedAt))
|
|
return lobbyfbs.ApplicationSummaryEnd(builder)
|
|
}
|
|
|
|
func decodeApplicationSummary(app *lobbyfbs.ApplicationSummary) lobbymodel.ApplicationSummary {
|
|
return lobbymodel.ApplicationSummary{
|
|
ApplicationID: string(app.ApplicationId()),
|
|
GameID: string(app.GameId()),
|
|
ApplicantUserID: string(app.ApplicantUserId()),
|
|
RaceName: string(app.RaceName()),
|
|
Status: string(app.Status()),
|
|
CreatedAt: time.UnixMilli(app.CreatedAtMs()).UTC(),
|
|
DecidedAt: optionalUnixMilli(app.DecidedAtMs()),
|
|
}
|
|
}
|
|
|
|
func encodeInviteSummary(builder *flatbuffers.Builder, invite lobbymodel.InviteSummary) flatbuffers.UOffsetT {
|
|
inviteID := builder.CreateString(invite.InviteID)
|
|
gameID := builder.CreateString(invite.GameID)
|
|
inviterUserID := builder.CreateString(invite.InviterUserID)
|
|
invitedUserID := builder.CreateString(invite.InvitedUserID)
|
|
code := builder.CreateString(invite.Code)
|
|
raceName := builder.CreateString(invite.RaceName)
|
|
status := builder.CreateString(invite.Status)
|
|
|
|
lobbyfbs.InviteSummaryStart(builder)
|
|
lobbyfbs.InviteSummaryAddInviteId(builder, inviteID)
|
|
lobbyfbs.InviteSummaryAddGameId(builder, gameID)
|
|
lobbyfbs.InviteSummaryAddInviterUserId(builder, inviterUserID)
|
|
lobbyfbs.InviteSummaryAddInvitedUserId(builder, invitedUserID)
|
|
lobbyfbs.InviteSummaryAddCode(builder, code)
|
|
lobbyfbs.InviteSummaryAddRaceName(builder, raceName)
|
|
lobbyfbs.InviteSummaryAddStatus(builder, status)
|
|
lobbyfbs.InviteSummaryAddCreatedAtMs(builder, invite.CreatedAt.UTC().UnixMilli())
|
|
lobbyfbs.InviteSummaryAddExpiresAtMs(builder, invite.ExpiresAt.UTC().UnixMilli())
|
|
lobbyfbs.InviteSummaryAddDecidedAtMs(builder, unixMilliFromOptional(invite.DecidedAt))
|
|
return lobbyfbs.InviteSummaryEnd(builder)
|
|
}
|
|
|
|
func decodeInviteSummary(invite *lobbyfbs.InviteSummary) lobbymodel.InviteSummary {
|
|
return lobbymodel.InviteSummary{
|
|
InviteID: string(invite.InviteId()),
|
|
GameID: string(invite.GameId()),
|
|
InviterUserID: string(invite.InviterUserId()),
|
|
InvitedUserID: string(invite.InvitedUserId()),
|
|
Code: string(invite.Code()),
|
|
RaceName: string(invite.RaceName()),
|
|
Status: string(invite.Status()),
|
|
CreatedAt: time.UnixMilli(invite.CreatedAtMs()).UTC(),
|
|
ExpiresAt: time.UnixMilli(invite.ExpiresAtMs()).UTC(),
|
|
DecidedAt: optionalUnixMilli(invite.DecidedAtMs()),
|
|
}
|
|
}
|
|
|
|
// unixMilliFromOptional returns 0 when the pointer is nil, else the
|
|
// millisecond UTC timestamp. The transcoder uses 0 as the "not set"
|
|
// sentinel because FlatBuffers tables do not carry per-field presence
|
|
// flags, and 0 is unreachable for any realistic decided/redeemed time.
|
|
// optionalUnixMilli (defined in user.go) is the decode counterpart.
|
|
func unixMilliFromOptional(t *time.Time) int64 {
|
|
if t == nil {
|
|
return 0
|
|
}
|
|
return t.UTC().UnixMilli()
|
|
}
|
|
|
|
func recoverLobbyDecodePanic[T any](message string, result **T, err *error) {
|
|
if recovered := recover(); recovered != nil {
|
|
*result = nil
|
|
*err = fmt.Errorf("%s: panic recovered: %v", message, recovered)
|
|
}
|
|
}
|