feat: turn stage increment after player's command

This commit is contained in:
Ilia Denisov
2026-02-08 16:35:58 +02:00
parent bf34843568
commit ba5d4e1ba8
14 changed files with 102 additions and 81 deletions
+4
View File
@@ -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()
+17 -6
View File
@@ -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
}
+1
View File
@@ -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()
+5 -8
View File
@@ -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)
}
+5 -8
View File
@@ -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)
}
+5 -8
View File
@@ -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)
}
+10 -16
View File
@@ -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)
}
+1 -1
View File
@@ -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)
+15 -15
View File
@@ -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)
}
+10 -4
View File
@@ -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
}
+10 -6
View File
@@ -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)
}
+2
View File
@@ -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 {
+16 -8
View File
@@ -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)
}
+1 -1
View File
@@ -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"`