package order import ( "encoding/json" "github.com/google/uuid" ) // MessageTypeUserGamesCommand is the authenticated gateway message type // used to send a batch of in-game commands to the engine through // `POST /api/v1/user/games/{game_id}/commands`. The signed payload is // a FlatBuffers `order.UserGamesCommand`. const MessageTypeUserGamesCommand = "user.games.command" // MessageTypeUserGamesOrder is the authenticated gateway message type // used to validate / store a batch of in-game orders through // `POST /api/v1/user/games/{game_id}/orders`. The signed payload is a // FlatBuffers `order.UserGamesOrder`. const MessageTypeUserGamesOrder = "user.games.order" // MessageTypeUserGamesOrderGet is the authenticated gateway message // type used to read back the player's stored order for a given turn // through `GET /api/v1/user/games/{game_id}/orders?turn=N`. The // signed payload is a FlatBuffers `order.UserGamesOrderGet`. const MessageTypeUserGamesOrderGet = "user.games.order.get" // UserGamesCommand is the typed payload of MessageTypeUserGamesCommand. // `GameID` selects the running engine container; `Commands` is the // player command batch executed atomically by the engine. The `Actor` // field present in the engine's JSON shape is rebuilt by backend from // the runtime player mapping — clients never carry it. type UserGamesCommand struct { // GameID identifies the running game for this batch. GameID uuid.UUID `json:"game_id"` // Commands is the player command batch. Commands []DecodableCommand `json:"cmd"` } // UserGamesOrder is the typed payload of MessageTypeUserGamesOrder. // Mirrors `UserGamesCommand` plus an `UpdatedAt` field that lets the // engine reject stale order submissions. type UserGamesOrder struct { // GameID identifies the running game for this batch. GameID uuid.UUID `json:"game_id"` // UpdatedAt is the client-side timestamp used for stale-order // detection on the engine side. UpdatedAt int64 `json:"updatedAt"` // Commands is the player order batch. Commands []DecodableCommand `json:"cmd"` } func (o UserGamesOrder) MarshalBinary() (data []byte, err error) { return json.Marshal(&o) } func (o *UserGamesOrder) UnmarshalBinary(data []byte) error { return json.Unmarshal(data, o) } // UserGamesOrderGet is the typed payload of // MessageTypeUserGamesOrderGet. `Turn` is mandatory and must be // non-negative; the caller pulls it from the lobby record at game // boot. Backend rebinds the player from the runtime player mapping // before forwarding to the engine. type UserGamesOrderGet struct { // GameID identifies the running game whose order is being // read back. GameID uuid.UUID `json:"game_id"` // Turn selects the turn the stored order belongs to. Negative // values are invalid. Turn int `json:"turn"` } func AsCommand[E DecodableCommand](c DecodableCommand) (result E, ok bool) { if v, ok := c.(E); ok { return v, true } return } type CommandType string const ( CommandTypeRaceQuit CommandType = "raceQuit" CommandTypeRaceVote CommandType = "raceVote" CommandTypeRaceRelation CommandType = "raceRelation" CommandTypeShipClassCreate CommandType = "shipClassCreate" CommandTypeShipClassMerge CommandType = "shipClassMerge" CommandTypeShipClassRemove CommandType = "shipClassRemove" CommandTypeShipGroupBreak CommandType = "shipGroupBreak" CommandTypeShipGroupLoad CommandType = "shipGroupLoad" CommandTypeShipGroupUnload CommandType = "shipGroupUnload" CommandTypeShipGroupSend CommandType = "shipGroupSend" CommandTypeShipGroupUpgrade CommandType = "shipGroupUpgrade" CommandTypeShipGroupMerge CommandType = "shipGroupMerge" CommandTypeShipGroupDismantle CommandType = "shipGroupDismantle" CommandTypeShipGroupTransfer CommandType = "shipGroupTransfer" CommandTypeShipGroupJoinFleet CommandType = "shipGroupJoinFleet" CommandTypeFleetMerge CommandType = "fleetMerge" CommandTypeFleetSend CommandType = "fleetSend" CommandTypeScienceCreate CommandType = "scienceCreate" CommandTypeScienceRemove CommandType = "scienceRemove" CommandTypePlanetRename CommandType = "planetRename" CommandTypePlanetProduce CommandType = "planetProduce" CommandTypePlanetRouteSet CommandType = "planetRouteSet" CommandTypePlanetRouteRemove CommandType = "planetRouteRemove" ) func (ct CommandType) String() string { return string(ct) } type DecodableCommand interface { CommandID() string CommandType() CommandType } type CommandMeta struct { CmdType CommandType `json:"@type" binding:"notblank"` CmdID string `json:"cmdId" binding:"required,uuid_rfc4122"` CmdApplied *bool `json:"cmdApplied,omitempty"` CmdErrCode *int `json:"cmdErrorCode,omitempty"` } func (cm CommandMeta) CommandType() CommandType { return cm.CmdType } func (cm CommandMeta) CommandID() string { return cm.CmdID } func (cm *CommandMeta) Result(errCode int) { cm.CmdErrCode = &errCode cm.CmdApplied = new(bool(errCode == 0)) } type CommandRaceQuit struct { CommandMeta } type CommandRaceVote struct { CommandMeta Acceptor string `json:"acceptor" binding:"notblank,entity"` } type CommandRaceRelation struct { CommandMeta Acceptor string `json:"acceptor" binding:"notblank,entity"` Relation string `json:"relation" binding:"oneof=WAR PEACE"` } type CommandShipClassCreate struct { CommandMeta Name string `json:"name" binding:"notblank,entity"` Drive float64 `json:"drive" binding:"eq=0|gte=1"` Armament int `json:"armament" binding:"ammoWeapons=Weapons"` Weapons float64 `json:"weapons" binding:"ammoWeapons=Armament"` Shields float64 `json:"shields" binding:"eq=0|gte=1"` Cargo float64 `json:"cargo" binding:"eq=0|gte=1"` } type CommandShipClassMerge struct { CommandMeta Name string `json:"name" binding:"notblank,entity,nefield=Target"` Target string `json:"target" binding:"notblank,entity,nefield=Name"` } type CommandShipClassRemove struct { CommandMeta Name string `json:"name" binding:"required,notblank,entity"` } type CommandShipGroupLoad struct { CommandMeta ID string `json:"id" binding:"required,uuid_rfc4122"` Cargo string `json:"cargo" binding:"oneof=COL MAT CAP"` Quantity float64 `json:"quantity" binding:"gte=0"` } type CommandShipGroupUnload struct { CommandMeta ID string `json:"id" binding:"required,uuid_rfc4122"` Quantity float64 `json:"quantity" binding:"gte=0"` } type CommandShipGroupSend struct { CommandMeta ID string `json:"id" binding:"required,uuid_rfc4122"` Destination int `json:"planetNumber" binding:"gte=0"` } type CommandShipGroupUpgrade struct { CommandMeta ID string `json:"id" binding:"required,uuid_rfc4122"` Tech string `json:"tech" binding:"oneof=ALL DRIVE WEAPONS SHIELDS CARGO"` Level float64 `json:"level" binding:"eq=0|gt=1"` } type CommandShipGroupMerge struct { CommandMeta } type CommandShipGroupBreak struct { CommandMeta ID string `json:"id" binding:"uuid_rfc4122,nefield=NewID"` NewID string `json:"newId" binding:"uuid_rfc4122,nefield=ID"` Quantity int `json:"quantity" binding:"gte=0"` } type CommandShipGroupDismantle struct { CommandMeta ID string `json:"id" binding:"required,uuid_rfc4122"` } type CommandShipGroupTransfer struct { CommandMeta ID string `json:"id" binding:"required,uuid_rfc4122"` Acceptor string `json:"acceptor" binding:"required,notblank,entity"` } type CommandShipGroupJoinFleet struct { CommandMeta ID string `json:"id" binding:"required,uuid_rfc4122"` Name string `json:"name" binding:"required,notblank,entity"` } type CommandFleetMerge struct { CommandMeta Name string `json:"name" binding:"required,notblank,entity,nefield=Target"` Target string `json:"target" binding:"required,notblank,entity,nefield=Name"` } type CommandFleetSend struct { CommandMeta Name string `json:"name" binding:"required,notblank,entity"` Destination int `json:"planetNumber" binding:"gte=0"` } type CommandScienceCreate struct { CommandMeta Name string `json:"name" binding:"required,notblank,entity"` Drive float64 `json:"drive" binding:"gte=0,lte=1"` Weapons float64 `json:"weapons" binding:"gte=0,lte=1"` Shields float64 `json:"shields" binding:"gte=0,lte=1"` Cargo float64 `json:"cargo" binding:"gte=0,lte=1"` } type CommandScienceRemove struct { CommandMeta Name string `json:"name" binding:"required,notblank,entity"` } type CommandPlanetRename struct { CommandMeta Number int `json:"planetNumber" binding:"gte=0"` Name string `json:"name" binding:"required,notblank,entity"` } type CommandPlanetProduce struct { CommandMeta Number int `json:"planetNumber" binding:"gte=0"` Production string `json:"production" binding:"oneof=MAT CAP DRIVE WEAPONS SHIELDS CARGO SCIENCE SHIP"` Subject string `json:"subject" binding:"subject=Production"` } type CommandPlanetRouteSet struct { CommandMeta Origin int `json:"fromPlanetNumber" binding:"gte=0,nefield=Destination"` Destination int `json:"toPlanetNumber" binding:"gte=0,nefield=Origin"` LoadType string `json:"loadType" binding:"oneof=MAT CAP COL EMP"` } type CommandPlanetRouteRemove struct { CommandMeta Origin int `json:"fromPlanetNumber" binding:"gte=0"` LoadType string `json:"loadType" binding:"oneof=MAT CAP COL EMP"` }