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 }