Files
galaxy-game/internal/controller/controller.go
T
2026-02-10 08:22:44 +02:00

178 lines
4.8 KiB
Go

package controller
import (
"errors"
"github.com/google/uuid"
"github.com/iliadenisov/galaxy/internal/model/game"
"github.com/iliadenisov/galaxy/internal/model/report"
"github.com/iliadenisov/galaxy/internal/repo"
)
type Configurer func(*Param)
type Repo interface {
// Lock must be called before any repository operations
Lock() error
// Release must be called after first and only repository operation
Release() error
// SaveTurn stores just generated new turn
SaveNewTurn(uint, *game.Game) error
// SaveState stores current game state updated between turns
SaveLastState(*game.Game) error
// LoadState retrieves game current state with required lock acquisition
LoadState() (*game.Game, error)
// LoadStateSafe retrieves game current state without preliminary locking
LoadStateSafe() (*game.Game, error)
// SaveBattle stores a new battle protocol and battle meta data for turn t
SaveBattle(uint, *report.BattleReport, *game.BattleMeta) error
// SaveBombing stores all prodused bombings for turn t
SaveBombings(uint, []*game.Bombing) error
// SaveReport stores latest report for a race
SaveReport(uint, *report.Report) error
// LoadReport loads report for specific turn and player id
LoadReport(uint, uuid.UUID) (*report.Report, error)
}
type Ctrl interface {
RaceID(actor string) (uuid.UUID, error)
RaceQuit(actor string) error
RaceVote(actor, acceptor string) error
RaceRelation(actor, acceptor string, rel string) error
ShipClassCreate(actor, typeName string, drive float64, ammo int, weapons, shileds, cargo float64) error
ShipClassMerge(actor, name, targetName string) error
ShipClassRemove(actor, typeName string) error
ShipGroupLoad(actor string, groupIndex uint, cargoType string, ships uint, quantity float64) error
ShipGroupUnload(actor string, groupIndex uint, ships uint, quantity float64) error
ShipGroupSend(actor string, groupIndex, planetNumber, quantity uint) error
ShipGroupUpgrade(actor string, groupIndex uint, techInput string, limitShips uint, limitLevel float64) error
ShipGroupBreak(actor string, groupIndex, quantity uint) error
ShipGroupMerge(actor string) error
ShipGroupDismantle(actor string, groupIndex, quantity uint) error
ShipGroupTransfer(actor, acceptor string, groupIndex, quantity uint) error
ShipGroupJoinFleet(actor, fleetName string, group, count uint) error
FleetMerge(actor, fleetSourceName, fleetTargetName string) error
FleetSend(actor, fleetName string, planetNumber uint) error
ScienceCreate(actor, typeName string, drive, weapons, shields, cargo float64) error
ScienceRemove(actor, typeName string) error
PlanetRename(actor string, planetNumber int, typeName string) error
PlanetProduce(actor string, planetNumber int, prodType, subject string) error
PlanetRouteSet(actor, loadType string, origin, destination uint) error
PlanetRouteRemove(actor, loadType string, origin uint) error
}
func GenerateGame(configure func(*Param), races []string) (ID uuid.UUID, err error) {
ec, err := NewRepoController(configure)
if err != nil {
return uuid.Nil, err
}
if err = ec.Repo.Lock(); err != nil {
return
}
defer func() {
err = errors.Join(err, ec.Repo.Release())
}()
ID, err = NewGame(ec.Repo, races)
return
}
func ExecuteCommand(configure func(*Param), consumer func(c Ctrl) error) (err error) {
ec, err := NewRepoController(configure)
if err != nil {
return err
}
return ec.ExecuteCommand(func(c *Controller) error { return consumer(c) })
}
func GameState(configure func(*Param)) (s game.State, err error) {
ec, err := NewRepoController(configure)
g, err := ec.Repo.LoadStateSafe()
if err != nil {
return game.State{}, err
}
result := &game.State{
ID: g.ID,
Turn: g.Turn,
Stage: g.Stage,
Players: make([]game.PlayerState, len(g.Race)),
}
for i := range g.Race {
r := &g.Race[i]
result.Players[i].ID = r.ID
result.Players[i].Name = r.Name
result.Players[i].Extinct = r.Extinct
}
return *result, nil
}
type RepoController struct {
Repo Repo
}
func (ec *RepoController) ExecuteCommand(consumer func(c *Controller) error) (err error) {
if err := ec.Repo.Lock(); err != nil {
return err
}
defer func() {
err = errors.Join(err, ec.Repo.Release())
}()
g, err := ec.Repo.LoadState()
if err != nil {
return err
}
err = consumer(NewGameController(g))
if err == nil {
g.Stage += 1
ec.Repo.SaveLastState(g)
}
return
}
func NewRepoController(config Configurer) (*RepoController, error) {
c := &Param{
StoragePath: ".",
}
if config != nil {
config(c)
}
r, err := repo.NewFileRepo(c.StoragePath)
if err != nil {
return nil, err
}
return &RepoController{
Repo: r,
}, nil
}
func NewGameController(g *game.Game) *Controller {
return &Controller{
Cache: NewCache(g),
}
}
type Controller struct {
Repo Repo
Cache *Cache
}
type Param struct {
StoragePath string
}