Files
galaxy-game/pkg/schema/fbs/lobby.fbs
T
Ilia Denisov f57a290432 phase 8: lobby UI + cross-stack lobby command catalog + TS FlatBuffers
- Extend pkg/model/lobby and pkg/schema/fbs/lobby.fbs with public-games
  list, my-applications/invites lists, game-create, application-submit,
  invite-redeem/decline. Mirror the matching transcoder pairs and Go
  fixture round-trip tests.
- Wire the seven new lobby message types through
  gateway/internal/backendclient/{routes,lobby_commands}.go with
  per-command REST helpers, JSON-tolerant decoding of backend wire
  shapes, and httptest-based unit coverage for success / 4xx / 5xx /
  503 across each command.
- Introduce TS-side FlatBuffers via the `flatbuffers` runtime dep, a
  `make fbs-ts` target driving flatc, and the generated bindings under
  ui/frontend/src/proto/galaxy/fbs. Phase 7's `user.account.get` decode
  now uses these bindings as well, closing the JSON.parse vs
  FlatBuffers gap that would have failed against a real local stack.
- Replace the placeholder lobby with five sections (my games, pending
  invitations, my applications, public games, create new game) and the
  /lobby/create form. Submit-application uses an inline race-name
  form on the public-game card; create-game keeps name / description /
  turn_schedule / enrollment_ends_at always visible and the rest under
  an Advanced toggle with TS-side defaults.
- Update lobby/+page.svelte to throw LobbyError on non-ok result codes;
  GalaxyClient.executeCommand now returns { resultCode, payloadBytes }.
- Vitest binding round-trips, lobby.ts wrapper unit tests, lobby-page
  + lobby-create component tests, Playwright lobby-flow.spec covering
  create / submit / accept across all four projects. Phase 7 e2e was
  migrated to the FlatBuffers fixtures and to click+fill against the
  Safari-autofill readonly inputs.
- Mark Phase 8 done in ui/PLAN.md, mirror the wire-format note into
  Phase 7, append the new lobby commands to gateway/README.md and
  docs/ARCHITECTURE.md, add ui/docs/lobby.md.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-07 18:05:08 +02:00

184 lines
4.7 KiB
Plaintext

// lobby contains FlatBuffers payloads used by the authenticated gateway
// boundary for Game Lobby. The wire shapes here mirror the trusted
// internal lobby REST surface; gateway derives the calling `user_id`
// from the verified session and forwards it as `X-User-Id` to lobby.
namespace lobby;
// GameSummary stores one game record returned by the lobby list
// endpoints. owner_user_id is empty for public games (no human owner).
// The shape matches `lobby/openapi.yaml` `MyGameSummary`.
table GameSummary {
game_id:string;
game_name:string;
game_type:string;
status:string;
owner_user_id:string;
min_players:int32;
max_players:int32;
enrollment_ends_at_ms:int64;
created_at_ms:int64;
updated_at_ms:int64;
}
// MyGamesListRequest stores the authenticated read request for the
// caller's games. Empty body — gateway derives identity from the
// authenticated session.
table MyGamesListRequest {
}
// MyGamesListResponse stores the list of games the caller participates
// in.
table MyGamesListResponse {
items:[GameSummary];
}
// PublicGamesListRequest stores the paginated read request for joinable
// public games. Page numbers start at 1; backend caps page_size.
table PublicGamesListRequest {
page:int32;
page_size:int32;
}
// PublicGamesListResponse stores one page of public games together with
// the pagination metadata.
table PublicGamesListResponse {
items:[GameSummary];
page:int32;
page_size:int32;
total:int32;
}
// ApplicationSummary stores one application record. decided_at_ms is 0
// while the application is pending.
table ApplicationSummary {
application_id:string;
game_id:string;
applicant_user_id:string;
race_name:string;
status:string;
created_at_ms:int64;
decided_at_ms:int64;
}
// MyApplicationsListRequest stores the authenticated read request for
// the caller's applications. Empty body.
table MyApplicationsListRequest {
}
// MyApplicationsListResponse stores the caller's applications in the
// order the backend returns them.
table MyApplicationsListResponse {
items:[ApplicationSummary];
}
// InviteSummary stores one invite record. invited_user_id is empty for
// code-based invites; code is empty for user-bound invites;
// decided_at_ms is 0 while the invite is still pending.
table InviteSummary {
invite_id:string;
game_id:string;
inviter_user_id:string;
invited_user_id:string;
code:string;
race_name:string;
status:string;
created_at_ms:int64;
expires_at_ms:int64;
decided_at_ms:int64;
}
// MyInvitesListRequest stores the authenticated read request for the
// caller's invites. Empty body.
table MyInvitesListRequest {
}
// MyInvitesListResponse stores the caller's invite list.
table MyInvitesListResponse {
items:[InviteSummary];
}
// OpenEnrollmentRequest stores the owner-only command that transitions
// a game from `draft` to `enrollment_open`.
table OpenEnrollmentRequest {
game_id:string;
}
// OpenEnrollmentResponse stores the resulting game status.
table OpenEnrollmentResponse {
game_id:string;
status:string;
}
// GameCreateRequest stores the create-game command. Visibility is
// always `private` for the user surface; the gateway rejects any other
// value before forwarding to backend.
table GameCreateRequest {
game_name:string;
description:string;
min_players:int32;
max_players:int32;
start_gap_hours:int32;
start_gap_players:int32;
enrollment_ends_at_ms:int64;
turn_schedule:string;
target_engine_version:string;
}
// GameCreateResponse wraps the freshly created game projected onto the
// shared GameSummary shape.
table GameCreateResponse {
game:GameSummary;
}
// ApplicationSubmitRequest stores the submit-application command for a
// public game in `enrollment_open`.
table ApplicationSubmitRequest {
game_id:string;
race_name:string;
}
// ApplicationSubmitResponse wraps the freshly created application
// record.
table ApplicationSubmitResponse {
application:ApplicationSummary;
}
// InviteRedeemRequest accepts a pending invite and creates the
// corresponding membership.
table InviteRedeemRequest {
game_id:string;
invite_id:string;
}
// InviteRedeemResponse wraps the updated invite record (status
// transitioned to `accepted`).
table InviteRedeemResponse {
invite:InviteSummary;
}
// InviteDeclineRequest terminally declines a pending invite.
table InviteDeclineRequest {
game_id:string;
invite_id:string;
}
// InviteDeclineResponse wraps the updated invite record (status
// transitioned to `declined`).
table InviteDeclineResponse {
invite:InviteSummary;
}
// ErrorBody stores the canonical lobby error envelope code/message
// pair.
table ErrorBody {
code:string;
message:string;
}
// ErrorResponse wraps ErrorBody for the FlatBuffers payload boundary.
table ErrorResponse {
error:ErrorBody;
}
root_type MyGamesListResponse;