diff --git a/internal/controller/cache.go b/internal/controller/cache.go index 2a1a066..00cb3d8 100644 --- a/internal/controller/cache.go +++ b/internal/controller/cache.go @@ -28,6 +28,10 @@ func NewCache(g *game.Game) *Cache { return c } +func (c Cache) Stage() uint { + return c.g.Stage +} + func (c *Cache) ShipGroupShipClass(groupIndex int) *game.ShipType { if len(c.cacheShipClassByShipGroupIndex) == 0 { c.cacheShipsAndGroups() diff --git a/internal/controller/controller.go b/internal/controller/controller.go index 3cd533c..b7f9a8f 100644 --- a/internal/controller/controller.go +++ b/internal/controller/controller.go @@ -1,6 +1,7 @@ package controller import ( + "errors" "fmt" "github.com/iliadenisov/galaxy/internal/model/game" @@ -70,15 +71,18 @@ func NewRepoController(r Repo) *Controller { } } -func (c *Controller) ExecuteState(consumer func(Repo)) error { +func (c *Controller) ExecuteState(consumer func(Repo) error) (err error) { if err := c.Repo.Lock(); err != nil { return fmt.Errorf("execute: lock failed: %s", err) } - consumer(c.Repo) - return c.Repo.Release() + defer func() { + err = errors.Join(err, c.Repo.Release()) + }() + err = consumer(c.Repo) + return } -func (c *Controller) ExecuteGame(consumer func(Repo, *game.Game)) error { +func (c *Controller) ExecuteCommand(consumer func(Repo, *game.Game) error) (err error) { if err := c.Repo.Lock(); err != nil { return fmt.Errorf("execute: lock failed: %s", err) } @@ -86,7 +90,14 @@ func (c *Controller) ExecuteGame(consumer func(Repo, *game.Game)) error { if err != nil { return err } + defer func() { + err = errors.Join(err, c.Repo.Release()) + }() c.Cache = NewCache(g) - consumer(c.Repo, g) - return c.Repo.Release() + err = consumer(c.Repo, g) + if err == nil { + g.Stage += 1 + c.Repo.SaveLastState(g) + } + return } diff --git a/internal/controller/generate_turn.go b/internal/controller/generate_turn.go index 0aad426..54761f4 100644 --- a/internal/controller/generate_turn.go +++ b/internal/controller/generate_turn.go @@ -12,6 +12,7 @@ import ( func MakeTurn(c *Controller, r Repo) error { // Next turn c.Cache.g.Turn += 1 + c.Cache.g.Stage = 0 // 00. Вышедшие расы удаляются из списка участвующих рас перед началом просчета очередного хода c.Cache.TurnWipeExtinctRaces() diff --git a/internal/game/cmd_group.go b/internal/game/cmd_group.go index 4b0ecbd..e9aadf7 100644 --- a/internal/game/cmd_group.go +++ b/internal/game/cmd_group.go @@ -6,17 +6,14 @@ import ( ) func JoinEqualGroups(configure func(*controller.Param), race string) (err error) { - control(configure, func(c *controller.Controller) { - c.ExecuteGame(func(r controller.Repo, g *game.Game) { - err = joinEqualGroups(c, r, g, race) + err = control(configure, func(c *controller.Controller) error { + return c.ExecuteCommand(func(r controller.Repo, g *game.Game) error { + return joinEqualGroups(c, race) }) }) return } -func joinEqualGroups(c *controller.Controller, r controller.Repo, g *game.Game, race string) error { - if err := c.JoinEqualGroups(race); err != nil { - return err - } - return r.SaveLastState(g) +func joinEqualGroups(c *controller.Controller, race string) error { + return c.JoinEqualGroups(race) } diff --git a/internal/game/cmd_planet.go b/internal/game/cmd_planet.go index 0009619..659e6a2 100644 --- a/internal/game/cmd_planet.go +++ b/internal/game/cmd_planet.go @@ -6,17 +6,14 @@ import ( ) func RenamePlanet(configure func(*controller.Param), race string, number int, name string) (err error) { - control(configure, func(c *controller.Controller) { - c.ExecuteGame(func(r controller.Repo, g *game.Game) { - err = renamePlanet(c, r, g, race, number, name) + err = control(configure, func(c *controller.Controller) error { + return c.ExecuteCommand(func(r controller.Repo, g *game.Game) error { + return renamePlanet(c, race, number, name) }) }) return } -func renamePlanet(c *controller.Controller, r controller.Repo, g *game.Game, race string, number int, name string) error { - if err := c.RenamePlanet(race, number, name); err != nil { - return err - } - return r.SaveLastState(g) +func renamePlanet(c *controller.Controller, race string, number int, name string) error { + return c.RenamePlanet(race, number, name) } diff --git a/internal/game/cmd_production.go b/internal/game/cmd_production.go index f64a18d..49871d2 100644 --- a/internal/game/cmd_production.go +++ b/internal/game/cmd_production.go @@ -6,17 +6,14 @@ import ( ) func PlanetProduction(configure func(*controller.Param), race string, planetNumber int, prodType, subject string) (err error) { - control(configure, func(c *controller.Controller) { - c.ExecuteGame(func(r controller.Repo, g *game.Game) { - err = planetProduction(c, r, g, race, planetNumber, prodType, subject) + err = control(configure, func(c *controller.Controller) error { + return c.ExecuteCommand(func(r controller.Repo, g *game.Game) error { + return planetProduction(c, race, planetNumber, prodType, subject) }) }) return } -func planetProduction(c *controller.Controller, r controller.Repo, g *game.Game, race string, planetNumber int, prodType, subject string) error { - if err := c.PlanetProduction(race, planetNumber, prodType, subject); err != nil { - return err - } - return r.SaveLastState(g) +func planetProduction(c *controller.Controller, race string, planetNumber int, prodType, subject string) error { + return c.PlanetProduction(race, planetNumber, prodType, subject) } diff --git a/internal/game/cmd_science.go b/internal/game/cmd_science.go index 3009766..5c3fbd5 100644 --- a/internal/game/cmd_science.go +++ b/internal/game/cmd_science.go @@ -6,33 +6,27 @@ import ( ) func CreateScience(configure func(*controller.Param), race, typeName string, drive, weapons, shields, cargo float64) (err error) { - control(configure, func(c *controller.Controller) { - c.ExecuteGame(func(r controller.Repo, g *game.Game) { - err = createScience(c, r, g, race, typeName, drive, weapons, shields, cargo) + err = control(configure, func(c *controller.Controller) error { + return c.ExecuteCommand(func(r controller.Repo, g *game.Game) error { + return createScience(c, race, typeName, drive, weapons, shields, cargo) }) }) return } -func createScience(c *controller.Controller, r controller.Repo, g *game.Game, race, typeName string, drive, weapons, shields, cargo float64) error { - if err := c.CreateScience(race, typeName, drive, weapons, shields, cargo); err != nil { - return err - } - return r.SaveLastState(g) +func createScience(c *controller.Controller, race, typeName string, drive, weapons, shields, cargo float64) error { + return c.CreateScience(race, typeName, drive, weapons, shields, cargo) } func DeleteScience(configure func(*controller.Param), race, typeName string) (err error) { - control(configure, func(c *controller.Controller) { - c.ExecuteGame(func(r controller.Repo, g *game.Game) { - err = deleteScience(c, r, g, race, typeName) + err = control(configure, func(c *controller.Controller) error { + return c.ExecuteCommand(func(r controller.Repo, g *game.Game) error { + return deleteScience(c, race, typeName) }) }) return } -func deleteScience(c *controller.Controller, r controller.Repo, g *game.Game, race, typeName string) error { - if err := c.DeleteScience(race, typeName); err != nil { - return err - } - return r.SaveLastState(g) +func deleteScience(c *controller.Controller, race, typeName string) error { + return c.DeleteScience(race, typeName) } diff --git a/internal/game/cmd_science_test.go b/internal/game/cmd_science_test.go index eba707c..bd74414 100644 --- a/internal/game/cmd_science_test.go +++ b/internal/game/cmd_science_test.go @@ -53,7 +53,7 @@ func TestCreateScienceValidation(t *testing.T) { {typeName, 0, 0, 0, -1, e.GenericErrorText(e.ErrInputCargoValue)}, {typeName, 0, 1, 1, -1, e.GenericErrorText(e.ErrInputCargoValue)}, } - c(t, func(p func(*controller.Param), g func() *controller.Controller) { + c(t, func(p func(*controller.Param), ctrl func() *controller.Controller) { for i, tc := range table { if tc.err == "" { n := tc.name + strconv.Itoa(i) diff --git a/internal/game/cmd_ship_type.go b/internal/game/cmd_ship_type.go index 3037578..aef6791 100644 --- a/internal/game/cmd_ship_type.go +++ b/internal/game/cmd_ship_type.go @@ -6,49 +6,49 @@ import ( ) func CreateShipType(configure func(*controller.Param), race, typeName string, drive float64, ammo int, weapons, shields, cargo float64) (err error) { - control(configure, func(c *controller.Controller) { - c.ExecuteGame(func(r controller.Repo, g *game.Game) { - err = createShipType(c, r, g, race, typeName, drive, ammo, weapons, shields, cargo) + err = control(configure, func(c *controller.Controller) error { + return c.ExecuteCommand(func(r controller.Repo, g *game.Game) error { + return createShipType(c, race, typeName, drive, ammo, weapons, shields, cargo) }) }) return } -func createShipType(c *controller.Controller, r controller.Repo, g *game.Game, race, typeName string, drive float64, ammo int, weapons, shields, cargo float64) error { +func createShipType(c *controller.Controller, race, typeName string, drive float64, ammo int, weapons, shields, cargo float64) error { if err := c.CreateShipType(race, typeName, drive, ammo, weapons, shields, cargo); err != nil { return err } - return r.SaveLastState(g) + return nil // r.SaveLastState(g) } func MergeShipType(configure func(*controller.Param), race, source, target string) (err error) { - control(configure, func(c *controller.Controller) { - c.ExecuteGame(func(r controller.Repo, g *game.Game) { - err = mergeShipType(c, r, g, race, source, target) + err = control(configure, func(c *controller.Controller) error { + return c.ExecuteCommand(func(r controller.Repo, g *game.Game) error { + return mergeShipType(c, race, source, target) }) }) return } -func mergeShipType(c *controller.Controller, r controller.Repo, g *game.Game, race, source, target string) error { +func mergeShipType(c *controller.Controller, race, source, target string) error { if err := c.MergeShipType(race, source, target); err != nil { return err } - return r.SaveLastState(g) + return nil // r.SaveLastState(g) } func DeleteShipType(configure func(*controller.Param), race, typeName string) (err error) { - control(configure, func(c *controller.Controller) { - c.ExecuteGame(func(r controller.Repo, g *game.Game) { - err = deleteShipType(c, r, g, race, typeName) + err = control(configure, func(c *controller.Controller) error { + return c.ExecuteCommand(func(r controller.Repo, g *game.Game) error { + return deleteShipType(c, race, typeName) }) }) return } -func deleteShipType(c *controller.Controller, r controller.Repo, g *game.Game, race, typeName string) error { +func deleteShipType(c *controller.Controller, race, typeName string) error { if err := c.DeleteShipType(race, typeName); err != nil { return err } - return r.SaveLastState(g) + return nil // r.SaveLastState(g) } diff --git a/internal/game/cmd_turn.go b/internal/game/cmd_turn.go index ce2a40e..420518d 100644 --- a/internal/game/cmd_turn.go +++ b/internal/game/cmd_turn.go @@ -2,12 +2,18 @@ package game import ( "github.com/iliadenisov/galaxy/internal/controller" - "github.com/iliadenisov/galaxy/internal/model/game" ) -func MakeTurn(configure func(*controller.Param), race string, number int, name string) (err error) { - control(configure, func(c *controller.Controller) { - c.ExecuteGame(func(r controller.Repo, g *game.Game) { controller.MakeTurn(c, r) }) +func GenerateTurn(configure func(*controller.Param)) (err error) { + err = control(configure, func(c *controller.Controller) error { + return c.ExecuteState(func(r controller.Repo) error { + g, err := r.LoadState() + if err != nil { + return err + } + c.Cache = controller.NewCache(g) + return controller.MakeTurn(c, r) + }) }) return } diff --git a/internal/game/cmd_war_peace.go b/internal/game/cmd_war_peace.go index 5a3d3ae..8da79a3 100644 --- a/internal/game/cmd_war_peace.go +++ b/internal/game/cmd_war_peace.go @@ -6,22 +6,26 @@ import ( ) func DeclareWar(configure func(*controller.Param), from, to string) (err error) { - control(configure, func(c *controller.Controller) { - c.ExecuteGame(func(r controller.Repo, g *game.Game) { err = updateRelation(c, r, g, from, to, game.RelationWar) }) + err = control(configure, func(c *controller.Controller) error { + return c.ExecuteCommand(func(r controller.Repo, g *game.Game) error { + return updateRelation(c, from, to, game.RelationWar) + }) }) return } func DeclarePeace(configure func(*controller.Param), from, to string) (err error) { - control(configure, func(c *controller.Controller) { - c.ExecuteGame(func(r controller.Repo, g *game.Game) { err = updateRelation(c, r, g, from, to, game.RelationPeace) }) + err = control(configure, func(c *controller.Controller) error { + return c.ExecuteCommand(func(r controller.Repo, g *game.Game) error { + return updateRelation(c, from, to, game.RelationPeace) + }) }) return } -func updateRelation(c *controller.Controller, r controller.Repo, g *game.Game, hostRace, opponentRace string, rel game.Relation) error { +func updateRelation(c *controller.Controller, hostRace, opponentRace string, rel game.Relation) error { if err := c.UpdateRelation(hostRace, opponentRace, rel); err != nil { return err } - return r.SaveLastState(g) + return nil // r.SaveLastState(g) } diff --git a/internal/game/cmd_war_peace_test.go b/internal/game/cmd_war_peace_test.go index 6032282..bc58cff 100644 --- a/internal/game/cmd_war_peace_test.go +++ b/internal/game/cmd_war_peace_test.go @@ -37,6 +37,7 @@ func TestDeclarePeaceAndWarAll(t *testing.T) { } assert.NoError(t, game.DeclarePeace(f, raceNum(hostRace), raceNum(hostRace))) + assert.Equal(t, 1, int(ctrl().Cache.Stage())) for i := range testRaceCount { if i == hostRace { @@ -46,6 +47,7 @@ func TestDeclarePeaceAndWarAll(t *testing.T) { } assert.NoError(t, game.DeclareWar(f, raceNum(hostRace), raceNum(hostRace))) + assert.Equal(t, 2, int(ctrl().Cache.Stage())) for i := range testRaceCount { if i == hostRace { diff --git a/internal/game/controller.go b/internal/game/controller.go index 9abbcda..84e1ac2 100644 --- a/internal/game/controller.go +++ b/internal/game/controller.go @@ -7,31 +7,39 @@ import ( ) func GenerateGame(configure func(*controller.Param), races []string) (gameID uuid.UUID, err error) { - control(configure, func(c *controller.Controller) { - c.ExecuteState(func(r controller.Repo) { gameID, err = controller.NewGame(r, races) }) + err = control(configure, func(c *controller.Controller) error { + return c.ExecuteState(func(r controller.Repo) error { + gameID, err = controller.NewGame(r, races) + return err + }) }) return } // LoadState used for lock-safe loading game state and may be called concurrently. func LoadState(configure func(*controller.Param)) (g *game.Game, err error) { - control(configure, func(c *controller.Controller) { g, err = c.Repo.LoadStateSafe() }) + err = control(configure, func(c *controller.Controller) error { + g, err = c.Repo.LoadStateSafe() + return err + }) return } // TODO: command for loading report by players (MUST be limited by router) func LoadReport(configure func(*controller.Param)) (g *game.Game, err error) { - control(configure, func(c *controller.Controller) { - c.ExecuteState(func(r controller.Repo) { g, err = c.Repo.LoadState() }) + err = control(configure, func(c *controller.Controller) error { + return c.ExecuteState(func(r controller.Repo) error { + g, err = c.Repo.LoadState() + return err + }) }) return } -func control(configure func(*controller.Param), consumer func(*controller.Controller)) error { +func control(configure func(*controller.Param), consumer func(*controller.Controller) error) (err error) { c, err := controller.NewController(configure) if err != nil { return err } - consumer(c) - return nil + return consumer(c) } diff --git a/internal/model/game/game.go b/internal/model/game/game.go index ff4bae4..68a5857 100644 --- a/internal/model/game/game.go +++ b/internal/model/game/game.go @@ -48,10 +48,10 @@ func NewTechSet() TechSet { } } -// TODO: turn's incremental Version type Game struct { ID uuid.UUID `json:"id"` Turn uint `json:"turn"` + Stage uint `json:"stage"` Map Map `json:"map"` Race []Race `json:"races"` Votes Float `json:"votes"`