feat: game order api methods

This commit is contained in:
Ilia Denisov
2026-05-09 10:36:44 +02:00
parent f2a7f2b515
commit 2a1e80053a
17 changed files with 166 additions and 75 deletions
+3 -3
View File
@@ -57,7 +57,7 @@ type GameState struct {
}
type GameData struct {
Turn uint `json:"turn"`
Report report.Report `json:"report"`
Order *order.Order `json:"order,omitempty"`
Turn uint `json:"turn"`
Report report.Report `json:"report"`
Order *order.UserGamesOrder `json:"order,omitempty"`
}
+3 -9
View File
@@ -40,23 +40,17 @@ type UserGamesOrder struct {
// UpdatedAt is the client-side timestamp used for stale-order
// detection on the engine side.
UpdatedAt int `json:"updatedAt"`
UpdatedAt int64 `json:"updatedAt"`
// Commands is the player order batch.
Commands []DecodableCommand `json:"cmd"`
}
type Order struct {
// TODO: check with already stored order, if any, and generate an error, if newer order exists
UpdatedAt int `json:"updatedAt"`
Commands []DecodableCommand `json:"cmd"`
}
func (o Order) MarshalBinary() (data []byte, err error) {
func (o UserGamesOrder) MarshalBinary() (data []byte, err error) {
return json.Marshal(&o)
}
func (o *Order) UnmarshalBinary(data []byte) error {
func (o *UserGamesOrder) UnmarshalBinary(data []byte) error {
return json.Unmarshal(data, o)
}
+15 -10
View File
@@ -17,6 +17,8 @@ import (
"galaxy/model/order"
"galaxy/model/report"
"galaxy/util"
"github.com/google/uuid"
)
const (
@@ -62,7 +64,8 @@ type fsStorage struct {
}
type storedOrder struct {
UpdatedAt int `json:"updatedAt"`
GameID uuid.UUID `json:"game_id"`
UpdatedAt int64 `json:"updatedAt"`
Commands []json.RawMessage `json:"cmd"`
}
@@ -155,7 +158,7 @@ func (s *fsStorage) SaveReportAsync(id client.GameID, turn uint, rep report.Repo
}()
}
func (s *fsStorage) LoadOrderAsync(id client.GameID, turn uint, callback func(order.Order, error)) {
func (s *fsStorage) LoadOrderAsync(id client.GameID, turn uint, callback func(order.UserGamesOrder, error)) {
go func() {
o, err := s.loadOrderSync(id, turn)
if callback != nil {
@@ -164,7 +167,7 @@ func (s *fsStorage) LoadOrderAsync(id client.GameID, turn uint, callback func(or
}()
}
func (s *fsStorage) SaveOrderAsync(id client.GameID, turn uint, o order.Order, callback func(error)) {
func (s *fsStorage) SaveOrderAsync(id client.GameID, turn uint, o order.UserGamesOrder, callback func(error)) {
go func() {
err := s.saveOrderSync(id, turn, o)
if callback != nil {
@@ -320,18 +323,18 @@ func (s *fsStorage) saveReportSync(id client.GameID, turn uint, rep report.Repor
}))
}
func (s *fsStorage) loadOrderSync(id client.GameID, turn uint) (order.Order, error) {
func (s *fsStorage) loadOrderSync(id client.GameID, turn uint) (order.UserGamesOrder, error) {
gameData, err := s.loadGameDataSync(id, turn)
if err != nil {
return order.Order{}, classifyStorageError(err)
return order.UserGamesOrder{}, classifyStorageError(err)
}
if gameData.Order == nil {
return order.Order{}, classifyStorageError(fmt.Errorf("load order for game %q turn %d: %w", id, turn, os.ErrNotExist))
return order.UserGamesOrder{}, classifyStorageError(fmt.Errorf("load order for game %q turn %d: %w", id, turn, os.ErrNotExist))
}
return *gameData.Order, nil
}
func (s *fsStorage) saveOrderSync(id client.GameID, turn uint, o order.Order) error {
func (s *fsStorage) saveOrderSync(id client.GameID, turn uint, o order.UserGamesOrder) error {
absPath, err := s.resolvePath(gameTurnFilePath(id, turn))
if err != nil {
return classifyStorageError(err)
@@ -474,8 +477,9 @@ func (d storedGameData) toGameData() (client.GameData, error) {
return gameData, nil
}
func makeStoredOrder(o order.Order) (storedOrder, error) {
func makeStoredOrder(o order.UserGamesOrder) (storedOrder, error) {
result := storedOrder{
GameID: o.GameID,
UpdatedAt: o.UpdatedAt,
Commands: make([]json.RawMessage, len(o.Commands)),
}
@@ -489,12 +493,13 @@ func makeStoredOrder(o order.Order) (storedOrder, error) {
return result, nil
}
func (o *storedOrder) toOrder() (*order.Order, error) {
func (o *storedOrder) toOrder() (*order.UserGamesOrder, error) {
if o == nil {
return nil, nil
}
result := &order.Order{
result := &order.UserGamesOrder{
GameID: o.GameID,
UpdatedAt: o.UpdatedAt,
Commands: make([]order.DecodableCommand, len(o.Commands)),
}
+8 -5
View File
@@ -14,6 +14,8 @@ import (
"galaxy/model/client"
"galaxy/model/order"
"galaxy/model/report"
"github.com/google/uuid"
)
const testTimeout = time.Second
@@ -137,9 +139,9 @@ func TestReportAndOrderRoundTripAsync(t *testing.T) {
t.Fatalf("loaded report mismatch\nwant: %#v\ngot: %#v", updatedReport, gotReport.value)
}
loadOrderDone := make(chan callbackResult[order.Order], 1)
s.LoadOrderAsync(id, turn, func(got order.Order, err error) {
loadOrderDone <- callbackResult[order.Order]{value: got, err: err}
loadOrderDone := make(chan callbackResult[order.UserGamesOrder], 1)
s.LoadOrderAsync(id, turn, func(got order.UserGamesOrder, err error) {
loadOrderDone <- callbackResult[order.UserGamesOrder]{value: got, err: err}
})
gotOrder := waitResult(t, loadOrderDone)
if gotOrder.err != nil {
@@ -529,8 +531,9 @@ func sampleReport(turn uint, race string) report.Report {
}
}
func sampleOrder() order.Order {
return order.Order{
func sampleOrder() order.UserGamesOrder {
return order.UserGamesOrder{
GameID: uuid.New(),
UpdatedAt: 1700,
Commands: []order.DecodableCommand{
&order.CommandPlanetRename{
+2 -2
View File
@@ -53,9 +53,9 @@ type UIStorage interface {
// LoadOrderAsync loads a [order.Order] for a given [model.GameID] and turn number from filesystem asynchronously.
// Passed callback func will will accept non-nil error in case of I/O or decoding errors occuried,
// otherwise callback func accepts loaded [order.Order].
LoadOrderAsync(client.GameID, uint, func(order.Order, error))
LoadOrderAsync(client.GameID, uint, func(order.UserGamesOrder, error))
// SaveOrderAsync stores given [order.Order] for a given [model.GameID] and turn number at the filesystem asynchronously.
// I/O or encoding error may occur, it that case callback func will be called with non-nil error.
SaveOrderAsync(client.GameID, uint, order.Order, func(error))
SaveOrderAsync(client.GameID, uint, order.UserGamesOrder, func(error))
}
+5 -5
View File
@@ -916,13 +916,13 @@ func PayloadToUserGamesOrder(data []byte) (result *model.UserGamesOrder, err err
if gameID == nil {
return nil, errors.New("decode user games order payload: game_id is missing")
}
updatedAt, convErr := int64ToInt(flat.UpdatedAt(), "updated_at")
if convErr != nil {
return nil, fmt.Errorf("decode user games order payload: %w", convErr)
}
// updatedAt, convErr := int64ToInt(flat.UpdatedAt(), "updated_at")
// if convErr != nil {
// return nil, fmt.Errorf("decode user games order payload: %w", convErr)
// }
out := &model.UserGamesOrder{
GameID: uuidFromHiLo(gameID.Hi(), gameID.Lo()),
UpdatedAt: updatedAt,
UpdatedAt: flat.UpdatedAt(),
}
count := flat.CommandsLength()
if count > 0 {