feat: gamemaster

This commit is contained in:
Ilia Denisov
2026-05-03 07:59:03 +02:00
committed by GitHub
parent a7cee15115
commit 3e2622757e
229 changed files with 41521 additions and 1098 deletions
+125
View File
@@ -0,0 +1,125 @@
package ports
import (
"context"
"encoding/json"
"errors"
)
//go:generate go run go.uber.org/mock/mockgen -destination=../adapters/mocks/mock_engineclient.go -package=mocks galaxy/gamemaster/internal/ports EngineClient
// EngineClient is the narrow surface Game Master uses against a running
// engine container. The production adapter (Stage 12) speaks REST/JSON
// against the engine routes documented in `galaxy/game/openapi.yaml`:
//
// - admin paths under `/api/v1/admin/*` (init, status, turn,
// race/banish);
// - player paths under `/api/v1/{command, order, report}`.
//
// The admin-path responses are typed (Init, Status, Turn) because GM
// reads structured fields out of them (`current_turn`, `finished`,
// per-player stats). The player-path payloads are forwarded verbatim:
// the gateway transcodes FlatBuffers to JSON, GM passes the JSON
// through, and the engine response is returned to the gateway
// unchanged.
type EngineClient interface {
// Init calls POST /api/v1/admin/init. The returned StateResponse
// carries the initial player roster used to install
// `player_mappings`.
Init(ctx context.Context, baseURL string, request InitRequest) (StateResponse, error)
// Status calls GET /api/v1/admin/status. Used by inspect surfaces
// and by recovery flows.
Status(ctx context.Context, baseURL string) (StateResponse, error)
// Turn calls PUT /api/v1/admin/turn. The returned StateResponse
// carries the new turn number, the per-player stats projected into
// `player_turn_stats`, and the `finished` flag.
Turn(ctx context.Context, baseURL string) (StateResponse, error)
// BanishRace calls POST /api/v1/admin/race/banish with body
// `{race_name}`. The engine returns 204 on success.
BanishRace(ctx context.Context, baseURL, raceName string) error
// ExecuteCommands calls PUT /api/v1/command. The request payload
// is forwarded verbatim; the engine response body is returned
// verbatim.
ExecuteCommands(ctx context.Context, baseURL string, payload json.RawMessage) (json.RawMessage, error)
// PutOrders calls PUT /api/v1/order with the same forwarding
// semantics as ExecuteCommands.
PutOrders(ctx context.Context, baseURL string, payload json.RawMessage) (json.RawMessage, error)
// GetReport calls GET /api/v1/report?player=<raceName>&turn=<turn>.
// The engine response body is returned verbatim.
GetReport(ctx context.Context, baseURL, raceName string, turn int) (json.RawMessage, error)
}
// InitRequest carries the race roster sent to the engine `/admin/init`
// route. The shape mirrors `galaxy/game/openapi.yaml`'s `InitRequest`.
type InitRequest struct {
// Races stores the per-player race entries in the order returned
// by Lobby's roster.
Races []InitRace
}
// InitRace stores one entry of an InitRequest.
type InitRace struct {
// RaceName stores the in-game race name reserved for the player.
RaceName string
}
// StateResponse is the typed projection of the engine's `StateResponse`
// payload (`galaxy/game/openapi.yaml`). GM reads only the fields it
// needs; the adapter is allowed to discard the rest.
type StateResponse struct {
// Turn stores the engine's current turn number.
Turn int
// Players stores the per-player state entries returned by the
// engine. Each entry is mapped into `player_turn_stats[]` by
// resolving `RaceName` through `playermappingstore.ListByGame` to
// the platform `user_id`.
Players []PlayerState
// Finished reports whether the engine considers the game finished.
// Becomes true on a turn-generation response when the engine's
// finish condition is satisfied.
Finished bool
}
// PlayerState stores one entry of StateResponse.Players. The set of
// fields is the minimum GM needs from the engine surface; the adapter
// may decode additional fields and discard them.
type PlayerState struct {
// RaceName stores the in-game race name.
RaceName string
// EnginePlayerUUID stores the engine-side player handle. Populated
// from `/admin/init` and `/admin/status`.
EnginePlayerUUID string
// Planets stores the planet count reported for this player on the
// most recent turn.
Planets int
// Population stores the population count reported for this player
// on the most recent turn.
Population int
}
// ErrEngineUnreachable reports that the engine returned a transport
// error or 5xx status code. Surfaced to callers as `engine_unreachable`.
var ErrEngineUnreachable = errors.New("engine unreachable")
// ErrEngineProtocolViolation reports that the engine responded with a
// payload that did not match the expected schema (missing required
// fields, malformed JSON, unexpected types). Surfaced as
// `engine_protocol_violation`.
var ErrEngineProtocolViolation = errors.New("engine protocol violation")
// ErrEngineValidation reports that the engine returned 4xx with a
// per-command result. Surfaced as `engine_validation_error`; the
// engine's body is returned verbatim to the caller through the player
// command/order forwarding paths.
var ErrEngineValidation = errors.New("engine validation error")