fs storage
This commit is contained in:
@@ -0,0 +1,343 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
|
||||
"galaxy/game/internal/controller"
|
||||
|
||||
"github.com/go-playground/validator/v10"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"galaxy/model/order"
|
||||
"galaxy/model/rest"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gin-gonic/gin/binding"
|
||||
)
|
||||
|
||||
func CommandHandler(c *gin.Context, executor CommandExecutor) {
|
||||
var cmd rest.Command
|
||||
if errorResponse(c, c.ShouldBindJSON(&cmd)) {
|
||||
return
|
||||
}
|
||||
|
||||
commands := make([]Command, len(cmd.Commands))
|
||||
for i := range cmd.Commands {
|
||||
command, err := parseCommand(cmd.Actor, cmd.Commands[i])
|
||||
if errorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
commands[i] = command
|
||||
}
|
||||
if len(commands) == 0 {
|
||||
errorResponse(c, errors.New("no commands given"))
|
||||
return
|
||||
}
|
||||
|
||||
if errorResponse(c, executor.Execute(commands...)) {
|
||||
return
|
||||
}
|
||||
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
|
||||
func parseCommand(actor string, c json.RawMessage) (Command, error) {
|
||||
meta := new(order.CommandMeta)
|
||||
if err := json.Unmarshal(c, meta); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch t := meta.CmdType; t {
|
||||
case order.CommandTypeRaceQuit:
|
||||
return commandRaceQuit(actor)
|
||||
case order.CommandTypeRaceVote:
|
||||
return commandRaceVote(actor, c)
|
||||
case order.CommandTypeRaceRelation:
|
||||
return commandRaceRelation(actor, c)
|
||||
case order.CommandTypeShipClassCreate:
|
||||
return commandShipClassCreate(actor, c)
|
||||
case order.CommandTypeShipClassMerge:
|
||||
return commandShipClassMerge(actor, c)
|
||||
case order.CommandTypeShipClassRemove:
|
||||
return commandShipClassRemove(actor, c)
|
||||
case order.CommandTypeShipGroupBreak:
|
||||
return commandShipGroupBreak(actor, c)
|
||||
case order.CommandTypeShipGroupLoad:
|
||||
return commandShipGroupLoad(actor, c)
|
||||
case order.CommandTypeShipGroupUnload:
|
||||
return commandShipGroupUnload(actor, c)
|
||||
case order.CommandTypeShipGroupSend:
|
||||
return commandShipGroupSend(actor, c)
|
||||
case order.CommandTypeShipGroupUpgrade:
|
||||
return commandShipGroupUpgrade(actor, c)
|
||||
case order.CommandTypeShipGroupMerge:
|
||||
return commandShipGroupMerge(actor, c)
|
||||
case order.CommandTypeShipGroupDismantle:
|
||||
return commandShipGroupDismantle(actor, c)
|
||||
case order.CommandTypeShipGroupTransfer:
|
||||
return commandShipGroupTransfer(actor, c)
|
||||
case order.CommandTypeShipGroupJoinFleet:
|
||||
return commandShipGroupJoinFleet(actor, c)
|
||||
case order.CommandTypeFleetMerge:
|
||||
return commandFleetMerge(actor, c)
|
||||
case order.CommandTypeFleetSend:
|
||||
return commandFleetSend(actor, c)
|
||||
case order.CommandTypeScienceCreate:
|
||||
return commandScienceCreate(actor, c)
|
||||
case order.CommandTypeScienceRemove:
|
||||
return commandScienceRemove(actor, c)
|
||||
case order.CommandTypePlanetRename:
|
||||
return commandPlanetRename(actor, c)
|
||||
case order.CommandTypePlanetProduce:
|
||||
return commandPlanetProduce(actor, c)
|
||||
case order.CommandTypePlanetRouteSet:
|
||||
return commandPlanetRouteSet(actor, c)
|
||||
case order.CommandTypePlanetRouteRemove:
|
||||
return commandPlanetRouteRemove(actor, c)
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown comman type: %s", t)
|
||||
}
|
||||
}
|
||||
|
||||
func commandRaceQuit(actor string) (Command, error) {
|
||||
return func(c controller.Ctrl) error { return c.RaceQuit(actor) }, nil
|
||||
}
|
||||
|
||||
func commandRaceVote(actor string, c json.RawMessage) (Command, error) {
|
||||
if v, err := unmarshallCommand(c, new(order.CommandRaceVote)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return func(c controller.Ctrl) error {
|
||||
return c.RaceVote(actor, v.Acceptor)
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func commandRaceRelation(actor string, c json.RawMessage) (Command, error) {
|
||||
if v, err := unmarshallCommand(c, new(order.CommandRaceRelation)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return func(c controller.Ctrl) error {
|
||||
return c.RaceRelation(actor, v.Acceptor, v.Relation)
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func commandShipClassCreate(actor string, c json.RawMessage) (Command, error) {
|
||||
if v, err := unmarshallCommand(c, new(order.CommandShipClassCreate)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return func(c controller.Ctrl) error {
|
||||
return c.ShipClassCreate(actor, v.Name, v.Drive, int(v.Armament), v.Weapons, v.Shields, v.Cargo)
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func commandShipClassMerge(actor string, c json.RawMessage) (Command, error) {
|
||||
if v, err := unmarshallCommand(c, new(order.CommandShipClassMerge)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return func(c controller.Ctrl) error {
|
||||
return c.ShipClassMerge(actor, v.Name, v.Target)
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func commandShipClassRemove(actor string, c json.RawMessage) (Command, error) {
|
||||
if v, err := unmarshallCommand(c, new(order.CommandShipClassRemove)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return func(c controller.Ctrl) error {
|
||||
return c.ShipClassRemove(actor, v.Name)
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func commandShipGroupBreak(actor string, c json.RawMessage) (Command, error) {
|
||||
if v, err := unmarshallCommand(c, new(order.CommandShipGroupBreak)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return func(c controller.Ctrl) error {
|
||||
return c.ShipGroupBreak(actor, uuid.MustParse(v.ID), uuid.MustParse(v.NewID), uint(v.Quantity))
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func commandShipGroupLoad(actor string, c json.RawMessage) (Command, error) {
|
||||
if v, err := unmarshallCommand(c, new(order.CommandShipGroupLoad)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return func(c controller.Ctrl) error {
|
||||
return c.ShipGroupLoad(actor, uuid.MustParse(v.ID), v.Cargo, v.Quantity)
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func commandShipGroupUnload(actor string, c json.RawMessage) (Command, error) {
|
||||
if v, err := unmarshallCommand(c, new(order.CommandShipGroupUnload)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return func(c controller.Ctrl) error {
|
||||
return c.ShipGroupUnload(actor, uuid.MustParse(v.ID), v.Quantity)
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func commandShipGroupSend(actor string, c json.RawMessage) (Command, error) {
|
||||
if v, err := unmarshallCommand(c, new(order.CommandShipGroupSend)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return func(c controller.Ctrl) error {
|
||||
return c.ShipGroupSend(actor, uuid.MustParse(v.ID), uint(v.Destination))
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func commandShipGroupUpgrade(actor string, c json.RawMessage) (Command, error) {
|
||||
if v, err := unmarshallCommand(c, new(order.CommandShipGroupUpgrade)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return func(c controller.Ctrl) error {
|
||||
return c.ShipGroupUpgrade(actor, uuid.MustParse(v.ID), v.Tech, v.Level)
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func commandShipGroupMerge(actor string, c json.RawMessage) (Command, error) {
|
||||
return func(c controller.Ctrl) error {
|
||||
return c.ShipGroupMerge(actor)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func commandShipGroupDismantle(actor string, c json.RawMessage) (Command, error) {
|
||||
if v, err := unmarshallCommand(c, new(order.CommandShipGroupDismantle)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return func(c controller.Ctrl) error {
|
||||
return c.ShipGroupDismantle(actor, uuid.MustParse(v.ID))
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func commandShipGroupTransfer(actor string, c json.RawMessage) (Command, error) {
|
||||
if v, err := unmarshallCommand(c, new(order.CommandShipGroupTransfer)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return func(c controller.Ctrl) error {
|
||||
return c.ShipGroupTransfer(actor, v.Acceptor, uuid.MustParse(v.ID))
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func commandShipGroupJoinFleet(actor string, c json.RawMessage) (Command, error) {
|
||||
if v, err := unmarshallCommand(c, new(order.CommandShipGroupJoinFleet)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return func(c controller.Ctrl) error {
|
||||
return c.ShipGroupJoinFleet(actor, v.Name, uuid.MustParse(v.ID))
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func commandFleetMerge(actor string, c json.RawMessage) (Command, error) {
|
||||
if v, err := unmarshallCommand(c, new(order.CommandFleetMerge)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return func(c controller.Ctrl) error {
|
||||
return c.FleetMerge(actor, v.Name, v.Target)
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func commandFleetSend(actor string, c json.RawMessage) (Command, error) {
|
||||
if v, err := unmarshallCommand(c, new(order.CommandFleetSend)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return func(c controller.Ctrl) error {
|
||||
return c.FleetSend(actor, v.Name, uint(v.Destination))
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func commandScienceCreate(actor string, c json.RawMessage) (Command, error) {
|
||||
if v, err := unmarshallCommand(c, new(order.CommandScienceCreate)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return func(c controller.Ctrl) error {
|
||||
return c.ScienceCreate(actor, v.Name, v.Drive, v.Weapons, v.Shields, v.Cargo)
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func commandScienceRemove(actor string, c json.RawMessage) (Command, error) {
|
||||
if v, err := unmarshallCommand(c, new(order.CommandScienceRemove)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return func(c controller.Ctrl) error {
|
||||
return c.ScienceRemove(actor, v.Name)
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func commandPlanetRename(actor string, c json.RawMessage) (Command, error) {
|
||||
if v, err := unmarshallCommand(c, new(order.CommandPlanetRename)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return func(c controller.Ctrl) error {
|
||||
return c.PlanetRename(actor, v.Number, v.Name)
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func commandPlanetProduce(actor string, c json.RawMessage) (Command, error) {
|
||||
if v, err := unmarshallCommand(c, new(order.CommandPlanetProduce)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return func(c controller.Ctrl) error {
|
||||
return c.PlanetProduce(actor, v.Number, v.Production, v.Subject)
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func commandPlanetRouteSet(actor string, c json.RawMessage) (Command, error) {
|
||||
if v, err := unmarshallCommand(c, new(order.CommandPlanetRouteSet)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return func(c controller.Ctrl) error {
|
||||
return c.PlanetRouteSet(actor, v.LoadType, uint(v.Origin), uint(v.Destination))
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func commandPlanetRouteRemove(actor string, c json.RawMessage) (Command, error) {
|
||||
if v, err := unmarshallCommand(c, new(order.CommandPlanetRouteRemove)); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return func(c controller.Ctrl) error {
|
||||
return c.PlanetRouteRemove(actor, v.LoadType, uint(v.Origin))
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Helpers
|
||||
|
||||
func unmarshallCommand[T order.DecodableCommand](c json.RawMessage, v T) (T, error) {
|
||||
if err := json.Unmarshal(c, v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
if err := validateCommand(v); err != nil {
|
||||
return v, err
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
func validateCommand(v order.DecodableCommand) error {
|
||||
if ve, ok := binding.Validator.Engine().(*validator.Validate); ok {
|
||||
if err := ve.Struct(v); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"galaxy/model/order"
|
||||
"galaxy/model/rest"
|
||||
|
||||
e "galaxy/error"
|
||||
|
||||
"galaxy/game/internal/controller"
|
||||
"galaxy/game/internal/model/game"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/go-playground/validator/v10"
|
||||
)
|
||||
|
||||
type CommandExecutor interface {
|
||||
GenerateGame([]string) (rest.StateResponse, error)
|
||||
GenerateTurn() (rest.StateResponse, error)
|
||||
GameState() (rest.StateResponse, error)
|
||||
Execute(cmd ...Command) error
|
||||
ValidateOrder(actor string, cmd ...order.DecodableCommand) error
|
||||
}
|
||||
|
||||
type Command func(controller.Ctrl) error
|
||||
|
||||
type executor struct {
|
||||
cfg controller.Configurer
|
||||
}
|
||||
|
||||
func initConfig() controller.Configurer {
|
||||
return func(p *controller.Param) {
|
||||
p.StoragePath = os.Getenv("STORAGE_PATH")
|
||||
}
|
||||
}
|
||||
|
||||
func NewDefaultExecutor() CommandExecutor {
|
||||
return NewDefaultConfigExecutor(initConfig())
|
||||
}
|
||||
|
||||
func NewDefaultConfigExecutor(configurer controller.Configurer) CommandExecutor {
|
||||
return &executor{cfg: configurer}
|
||||
}
|
||||
|
||||
func (e *executor) Execute(cmd ...Command) error {
|
||||
return controller.ExecuteCommand(e.cfg, func(c controller.Ctrl) error {
|
||||
for i := range cmd {
|
||||
if err := cmd[i](c); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
func (e *executor) ValidateOrder(actor string, cmd ...order.DecodableCommand) error {
|
||||
return controller.ValidateOrder(e.cfg, actor, cmd...)
|
||||
}
|
||||
|
||||
func (e *executor) GenerateGame(races []string) (rest.StateResponse, error) {
|
||||
s, err := controller.GenerateGame(e.cfg, races)
|
||||
if err != nil {
|
||||
return rest.StateResponse{}, err
|
||||
}
|
||||
return stateResponse(s), nil
|
||||
}
|
||||
|
||||
func (e *executor) GenerateTurn() (rest.StateResponse, error) {
|
||||
err := controller.GenerateTurn(e.cfg)
|
||||
if err != nil {
|
||||
return rest.StateResponse{}, err
|
||||
}
|
||||
return e.GameState()
|
||||
}
|
||||
|
||||
func (e *executor) GameState() (rest.StateResponse, error) {
|
||||
s, err := controller.GameState(e.cfg)
|
||||
if err != nil {
|
||||
return rest.StateResponse{}, err
|
||||
}
|
||||
return stateResponse(s), nil
|
||||
}
|
||||
|
||||
func stateResponse(s game.State) rest.StateResponse {
|
||||
result := &rest.StateResponse{
|
||||
ID: s.ID,
|
||||
Turn: s.Turn,
|
||||
Stage: s.Stage,
|
||||
Players: make([]rest.PlayerState, len(s.Players)),
|
||||
}
|
||||
for i := range s.Players {
|
||||
result.Players[i].ID = s.Players[i].ID
|
||||
result.Players[i].Name = s.Players[i].Name
|
||||
result.Players[i].Extinct = s.Players[i].Extinct
|
||||
}
|
||||
return *result
|
||||
}
|
||||
|
||||
func errorResponse(c *gin.Context, err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if v, ok := err.(validator.ValidationErrors); ok {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": v.Error()})
|
||||
return true
|
||||
}
|
||||
|
||||
if ge, ok := errors.AsType[*e.GenericError](err); ok {
|
||||
switch ge.Code {
|
||||
case e.ErrGameNotInitialized:
|
||||
c.Status(http.StatusNotImplemented)
|
||||
default:
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"generic_error": ge.Error(), "code": ge.Code})
|
||||
}
|
||||
} else {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"galaxy/model/rest"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func InitHandler(c *gin.Context, executor CommandExecutor) {
|
||||
var init rest.Init
|
||||
if errorResponse(c, c.ShouldBindJSON(&init)) {
|
||||
return
|
||||
}
|
||||
|
||||
races := make([]string, len(init.Races))
|
||||
for i := range init.Races {
|
||||
races[i] = init.Races[i].Name
|
||||
}
|
||||
|
||||
s, err := executor.GenerateGame(races)
|
||||
if errorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusCreated, s)
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"galaxy/model/order"
|
||||
"galaxy/model/rest"
|
||||
|
||||
"galaxy/game/internal/repo"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func OrderHandler(c *gin.Context, executor CommandExecutor) {
|
||||
var cmd rest.Command
|
||||
if errorResponse(c, c.ShouldBindJSON(&cmd)) {
|
||||
return
|
||||
}
|
||||
|
||||
commands := make([]order.DecodableCommand, len(cmd.Commands))
|
||||
for i := range cmd.Commands {
|
||||
command, err := repo.ParseOrder(cmd.Commands[i], validateCommand)
|
||||
if errorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
commands[i] = command
|
||||
}
|
||||
if len(commands) == 0 {
|
||||
errorResponse(c, errors.New("no commands given"))
|
||||
return
|
||||
}
|
||||
|
||||
if errorResponse(c, executor.ValidateOrder(cmd.Actor, commands...)) {
|
||||
return
|
||||
}
|
||||
|
||||
c.Status(http.StatusNoContent)
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func StatusHandler(c *gin.Context, executor CommandExecutor) {
|
||||
state, err := executor.GameState()
|
||||
|
||||
if errorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, state)
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func TurnHandler(c *gin.Context, executor CommandExecutor) {
|
||||
state, err := executor.GenerateTurn()
|
||||
|
||||
if errorResponse(c, err) {
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, state)
|
||||
}
|
||||
Reference in New Issue
Block a user