feat: load player's report
This commit is contained in:
@@ -3,12 +3,21 @@ package controller
|
|||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
e "github.com/iliadenisov/galaxy/internal/error"
|
e "github.com/iliadenisov/galaxy/internal/error"
|
||||||
"github.com/iliadenisov/galaxy/internal/model/game"
|
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (c Controller) RaceID(actor string) (uuid.UUID, error) {
|
||||||
|
ri, err := c.Cache.validRace(actor)
|
||||||
|
if err != nil {
|
||||||
|
return uuid.Nil, err
|
||||||
|
}
|
||||||
|
return c.Cache.g.Race[ri].ID, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (c Controller) QuitGame(actor string) error {
|
func (c Controller) QuitGame(actor string) error {
|
||||||
ri, err := c.Cache.validActor(actor)
|
ri, err := c.Cache.validRace(actor)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/iliadenisov/galaxy/internal/model/game"
|
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||||
"github.com/iliadenisov/galaxy/internal/model/report"
|
"github.com/iliadenisov/galaxy/internal/model/report"
|
||||||
"github.com/iliadenisov/galaxy/internal/repo"
|
"github.com/iliadenisov/galaxy/internal/repo"
|
||||||
@@ -36,6 +37,9 @@ type Repo interface {
|
|||||||
|
|
||||||
// SaveReport stores latest report for a race
|
// SaveReport stores latest report for a race
|
||||||
SaveReport(uint, *report.Report) error
|
SaveReport(uint, *report.Report) error
|
||||||
|
|
||||||
|
// LoadReport loads report for specific turn and player id
|
||||||
|
LoadReport(uint, uuid.UUID) (*report.Report, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Controller struct {
|
type Controller struct {
|
||||||
@@ -47,9 +51,9 @@ type Param struct {
|
|||||||
StoragePath string
|
StoragePath string
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config func(*Param)
|
type Configurer func(*Param)
|
||||||
|
|
||||||
func NewController(config Config) (*Controller, error) {
|
func NewController(config Configurer) (*Controller, error) {
|
||||||
c := &Param{
|
c := &Param{
|
||||||
StoragePath: ".",
|
StoragePath: ".",
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,9 +31,9 @@ func (c *Cache) AddRace(n string) (int, uuid.UUID) {
|
|||||||
return len(c.g.Race) - 1, id
|
return len(c.g.Race) - 1, id
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) Race(i int) game.Race {
|
func (c *Cache) Race(i int) *game.Race {
|
||||||
c.validateRaceIndex(i)
|
c.validateRaceIndex(i)
|
||||||
return c.g.Race[i]
|
return &c.g.Race[i]
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Cache) RaceShipGroups(ri int) iter.Seq[*game.ShipGroup] {
|
func (c *Cache) RaceShipGroups(ri int) iter.Seq[*game.ShipGroup] {
|
||||||
|
|||||||
@@ -2,13 +2,10 @@ package controller_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/iliadenisov/galaxy/internal/controller"
|
"github.com/iliadenisov/galaxy/internal/controller"
|
||||||
"github.com/iliadenisov/galaxy/internal/model/game"
|
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -103,14 +100,6 @@ var (
|
|||||||
InSpace = game.InSpace{Origin: 2, X: floatRef(1.23), Y: floatRef(1.23)}
|
InSpace = game.InSpace{Origin: 2, X: floatRef(1.23), Y: floatRef(1.23)}
|
||||||
)
|
)
|
||||||
|
|
||||||
// [ ] Delete this fake test
|
|
||||||
func TestSlicesDelete(t *testing.T) {
|
|
||||||
sl := []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
|
|
||||||
assert.Len(t, sl, 10)
|
|
||||||
sl = slices.DeleteFunc(sl, func(v int) bool { return v%2 == 0 })
|
|
||||||
assert.Len(t, sl, 5)
|
|
||||||
}
|
|
||||||
|
|
||||||
func assertNoError(err error) {
|
func assertNoError(err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("init assertion failed: %v", err))
|
panic(fmt.Sprintf("init assertion failed: %v", err))
|
||||||
|
|||||||
@@ -69,3 +69,19 @@ func TestQuitGame(t *testing.T) {
|
|||||||
assert.NoError(t, g.QuitGame(Race_0.Name))
|
assert.NoError(t, g.QuitGame(Race_0.Name))
|
||||||
assert.Equal(t, 3, int(c.Race(Race_0_idx).TTL))
|
assert.Equal(t, 3, int(c.Race(Race_0_idx).TTL))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRaceID(t *testing.T) {
|
||||||
|
c, g := newCache()
|
||||||
|
|
||||||
|
c.Race(Race_0_idx).TTL = 9
|
||||||
|
|
||||||
|
_, err := g.RaceID(UnknownRace)
|
||||||
|
assert.ErrorContains(t, err, e.GenericErrorText(e.ErrInputUnknownRace))
|
||||||
|
|
||||||
|
_, err = g.RaceID(Race_Extinct.Name)
|
||||||
|
assert.ErrorContains(t, err, e.GenericErrorText(e.ErrRaceExinct))
|
||||||
|
|
||||||
|
id, err := g.RaceID(Race_0.Name)
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, Race_0_ID, id)
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ const (
|
|||||||
ErrStorageFailure int = 1000 + iota
|
ErrStorageFailure int = 1000 + iota
|
||||||
ErrGameNotInitialized
|
ErrGameNotInitialized
|
||||||
ErrGameStateInvalid
|
ErrGameStateInvalid
|
||||||
|
ErrReportNotFound
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -8,6 +8,10 @@ func NewGameNotInitializedError(arg ...any) error {
|
|||||||
return newGenericError(ErrGameNotInitialized, arg...)
|
return newGenericError(ErrGameNotInitialized, arg...)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewReportNotFoundError(arg ...any) error {
|
||||||
|
return newGenericError(ErrReportNotFound, arg...)
|
||||||
|
}
|
||||||
|
|
||||||
func NewGameStateError(arg ...any) error {
|
func NewGameStateError(arg ...any) error {
|
||||||
return newGenericError(ErrGameStateInvalid, arg...)
|
return newGenericError(ErrGameStateInvalid, arg...)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/iliadenisov/galaxy/internal/controller"
|
"github.com/iliadenisov/galaxy/internal/controller"
|
||||||
"github.com/iliadenisov/galaxy/internal/model/game"
|
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||||
|
"github.com/iliadenisov/galaxy/internal/model/report"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GenerateGame(configure func(*controller.Param), races []string) (gameID uuid.UUID, err error) {
|
func GenerateGame(configure func(*controller.Param), races []string) (gameID uuid.UUID, err error) {
|
||||||
@@ -25,13 +26,19 @@ func LoadState(configure func(*controller.Param)) (g *game.Game, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: command for loading report by players (MUST be limited by router)
|
func LoadReport(configure func(*controller.Param), t uint, actor string) (g *report.Report, err error) {
|
||||||
func LoadReport(configure func(*controller.Param)) (g *game.Game, err error) {
|
|
||||||
err = control(configure, func(c *controller.Controller) error {
|
err = control(configure, func(c *controller.Controller) error {
|
||||||
return c.ExecuteState(func(r controller.Repo) error {
|
game, err := c.Repo.LoadStateSafe()
|
||||||
g, err = c.Repo.LoadState()
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.Cache = controller.NewCache(game)
|
||||||
|
id, err := c.RaceID(actor)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
g, err = c.Repo.LoadReport(t, id)
|
||||||
return err
|
return err
|
||||||
})
|
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ const (
|
|||||||
|
|
||||||
type Production struct {
|
type Production struct {
|
||||||
Type ProductionType `json:"type"`
|
Type ProductionType `json:"type"`
|
||||||
SubjectID *uuid.UUID `json:"subjectId,omitempty"` // TODO: get rid of Nils?
|
SubjectID *uuid.UUID `json:"subjectId,omitempty"`
|
||||||
Progress *Float `json:"progress,omitempty"`
|
Progress *Float `json:"progress,omitempty"`
|
||||||
ProdUsed *Float `json:"prodUsed,omitempty"`
|
ProdUsed *Float `json:"prodUsed,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
+26
-3
@@ -1,7 +1,6 @@
|
|||||||
package repo
|
package repo
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO: only state will be saved once (current, turn); meta and bombings are saved at turn generation and saved twice
|
|
||||||
/state.json
|
/state.json
|
||||||
/0001/state.json
|
/0001/state.json
|
||||||
/0001/meta.json
|
/0001/meta.json
|
||||||
@@ -13,6 +12,7 @@ package repo
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/google/uuid"
|
||||||
"github.com/iliadenisov/galaxy/internal/model/game"
|
"github.com/iliadenisov/galaxy/internal/model/game"
|
||||||
"github.com/iliadenisov/galaxy/internal/model/report"
|
"github.com/iliadenisov/galaxy/internal/model/report"
|
||||||
)
|
)
|
||||||
@@ -27,13 +27,33 @@ func (r *repo) SaveReport(t uint, rep *report.Report) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func saveReport(s Storage, t uint, v *report.Report) error {
|
func saveReport(s Storage, t uint, v *report.Report) error {
|
||||||
path := fmt.Sprintf("%s/report/%s.json", turnDir(t), v.RaceID.String())
|
path := repDir(t, v.RaceID)
|
||||||
if err := s.Write(path, v); err != nil {
|
if err := s.Write(path, v); err != nil {
|
||||||
return NewStorageError(err)
|
return NewStorageError(err)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *repo) LoadReport(t uint, id uuid.UUID) (*report.Report, error) {
|
||||||
|
return loadReport(r.s, t, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadReport(s Storage, t uint, id uuid.UUID) (*report.Report, error) {
|
||||||
|
path := repDir(t, id)
|
||||||
|
result := new(report.Report)
|
||||||
|
exist, err := s.Exists(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, NewStorageError(err)
|
||||||
|
}
|
||||||
|
if !exist {
|
||||||
|
return nil, NewReportNotFoundError()
|
||||||
|
}
|
||||||
|
if err := s.ReadSafe(path, result); err != nil {
|
||||||
|
return nil, NewStorageError(err)
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (r *repo) SaveNewTurn(t uint, g *game.Game) error {
|
func (r *repo) SaveNewTurn(t uint, g *game.Game) error {
|
||||||
return saveNewTurn(r.s, t, g)
|
return saveNewTurn(r.s, t, g)
|
||||||
}
|
}
|
||||||
@@ -104,7 +124,6 @@ func loadMeta(s Storage) (*game.GameMeta, error) {
|
|||||||
if !exist {
|
if !exist {
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
// TODO: create separate Read func for meta ops
|
|
||||||
if err := s.ReadSafe(path, result); err != nil {
|
if err := s.ReadSafe(path, result); err != nil {
|
||||||
return nil, NewStorageError(err)
|
return nil, NewStorageError(err)
|
||||||
}
|
}
|
||||||
@@ -164,6 +183,10 @@ func (r *repo) SaveBombings(t uint, b []*game.Bombing) error {
|
|||||||
return saveMeta(r.s, t, meta)
|
return saveMeta(r.s, t, meta)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func repDir(t uint, id uuid.UUID) string {
|
||||||
|
return fmt.Sprintf("%s/report/%s.json", turnDir(t), id.String())
|
||||||
|
}
|
||||||
|
|
||||||
func turnDir(t uint) string {
|
func turnDir(t uint) string {
|
||||||
return fmt.Sprintf("%04d", t)
|
return fmt.Sprintf("%04d", t)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ func NewGameNotInitializedError() error {
|
|||||||
return e.NewGameNotInitializedError()
|
return e.NewGameNotInitializedError()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewReportNotFoundError() error {
|
||||||
|
return e.NewReportNotFoundError()
|
||||||
|
}
|
||||||
|
|
||||||
func NewStateError(msg string) error {
|
func NewStateError(msg string) error {
|
||||||
return e.NewGameStateError(msg)
|
return e.NewGameStateError(msg)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,19 +11,19 @@ import (
|
|||||||
"github.com/iliadenisov/galaxy/internal/model/rest"
|
"github.com/iliadenisov/galaxy/internal/model/rest"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CommandExecutor func(controller.Config, rest.Command) error
|
type CommandExecutor func(controller.Configurer, rest.Command) error
|
||||||
|
|
||||||
var (
|
var (
|
||||||
ErrCommandNotProcessed = errors.New("command was not processed by executor")
|
ErrCommandNotProcessed = errors.New("command was not processed by executor")
|
||||||
)
|
)
|
||||||
|
|
||||||
func CommandHandler(c *gin.Context, config controller.Config, executor CommandExecutor) {
|
func CommandHandler(c *gin.Context, configurer controller.Configurer, executor CommandExecutor) {
|
||||||
var cmd rest.Command
|
var cmd rest.Command
|
||||||
if err := c.ShouldBindJSON(&cmd); err != nil {
|
if err := c.ShouldBindJSON(&cmd); err != nil {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err := executor(config, cmd)
|
err := executor(configurer, cmd)
|
||||||
switch {
|
switch {
|
||||||
case err == nil:
|
case err == nil:
|
||||||
c.Status(http.StatusOK)
|
c.Status(http.StatusOK)
|
||||||
@@ -35,7 +35,7 @@ func CommandHandler(c *gin.Context, config controller.Config, executor CommandEx
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExecuteCommand(config controller.Config, cmd rest.Command) error {
|
func ExecuteCommand(config controller.Configurer, cmd rest.Command) error {
|
||||||
switch {
|
switch {
|
||||||
case cmd.DeclareWar != nil:
|
case cmd.DeclareWar != nil:
|
||||||
return game.DeclareWar(config, cmd.Race, cmd.DeclareWar.Opponent)
|
return game.DeclareWar(config, cmd.Race, cmd.DeclareWar.Opponent)
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/iliadenisov/galaxy/internal/model/rest"
|
"github.com/iliadenisov/galaxy/internal/model/rest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func InitHandler(c *gin.Context, config controller.Config) {
|
func InitHandler(c *gin.Context, config controller.Configurer) {
|
||||||
var init rest.Init
|
var init rest.Init
|
||||||
if err := c.ShouldBindJSON(&init); err != nil {
|
if err := c.ShouldBindJSON(&init); err != nil {
|
||||||
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/iliadenisov/galaxy/internal/model/rest"
|
"github.com/iliadenisov/galaxy/internal/model/rest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func StatusHandler(c *gin.Context, config controller.Config) {
|
func StatusHandler(c *gin.Context, config controller.Configurer) {
|
||||||
g, err := game.LoadState(config)
|
g, err := game.LoadState(config)
|
||||||
|
|
||||||
if transformError(c, err) {
|
if transformError(c, err) {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ const (
|
|||||||
|
|
||||||
func initConfig() func(*controller.Param) {
|
func initConfig() func(*controller.Param) {
|
||||||
return func(p *controller.Param) {
|
return func(p *controller.Param) {
|
||||||
// TODO: initialize base controller settings
|
|
||||||
p.StoragePath = os.Getenv("STORAGE_PATH")
|
p.StoragePath = os.Getenv("STORAGE_PATH")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -40,7 +39,7 @@ func NewRouterExecutor(executor handler.CommandExecutor) Router {
|
|||||||
return Router{r: setupRouter(initConfig(), executor)}
|
return Router{r: setupRouter(initConfig(), executor)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupRouter(config controller.Config, executor handler.CommandExecutor) *gin.Engine {
|
func setupRouter(config controller.Configurer, executor handler.CommandExecutor) *gin.Engine {
|
||||||
gin.SetMode(gin.ReleaseMode)
|
gin.SetMode(gin.ReleaseMode)
|
||||||
r := gin.New()
|
r := gin.New()
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,6 @@ func SetupRouter() *gin.Engine {
|
|||||||
return SetupRouterConfig(nil)
|
return SetupRouterConfig(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SetupRouterConfig(config controller.Config) *gin.Engine {
|
func SetupRouterConfig(config controller.Configurer) *gin.Engine {
|
||||||
return setupRouter(config, func(controller.Config, rest.Command) error { return nil })
|
return setupRouter(config, func(controller.Configurer, rest.Command) error { return nil })
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user