Files
galaxy-game/pkg/transcoder/order.go
T
2026-05-09 10:36:44 +02:00

1006 lines
37 KiB
Go

package transcoder
import (
"errors"
"fmt"
model "galaxy/model/order"
commonfbs "galaxy/schema/fbs/common"
fbs "galaxy/schema/fbs/order"
flatbuffers "github.com/google/flatbuffers/go"
)
type encodedCommand struct {
cmdID string
cmdApplied *bool
cmdErrCode *int
payloadType fbs.CommandPayload
payloadOffset flatbuffers.UOffsetT
}
func encodeOrderCommand(builder *flatbuffers.Builder, command model.DecodableCommand, index int) (encodedCommand, error) {
if command == nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: command is nil", index)
}
switch cmd := command.(type) {
case *model.CommandRaceQuit:
if cmd == nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: command is nil %T", index, command)
}
fbs.CommandRaceQuitStart(builder)
payload := fbs.CommandRaceQuitEnd(builder)
return encodedCommandFromMeta(cmd.CommandMeta, fbs.CommandPayloadCommandRaceQuit, payload), nil
case *model.CommandRaceVote:
if cmd == nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: command is nil %T", index, command)
}
acceptor := builder.CreateString(cmd.Acceptor)
fbs.CommandRaceVoteStart(builder)
fbs.CommandRaceVoteAddAcceptor(builder, acceptor)
payload := fbs.CommandRaceVoteEnd(builder)
return encodedCommandFromMeta(cmd.CommandMeta, fbs.CommandPayloadCommandRaceVote, payload), nil
case *model.CommandRaceRelation:
if cmd == nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: command is nil %T", index, command)
}
relation, err := relationToFBS(cmd.Relation)
if err != nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: %w", index, err)
}
acceptor := builder.CreateString(cmd.Acceptor)
fbs.CommandRaceRelationStart(builder)
fbs.CommandRaceRelationAddAcceptor(builder, acceptor)
fbs.CommandRaceRelationAddRelation(builder, relation)
payload := fbs.CommandRaceRelationEnd(builder)
return encodedCommandFromMeta(cmd.CommandMeta, fbs.CommandPayloadCommandRaceRelation, payload), nil
case *model.CommandShipClassCreate:
if cmd == nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: command is nil %T", index, command)
}
name := builder.CreateString(cmd.Name)
fbs.CommandShipClassCreateStart(builder)
fbs.CommandShipClassCreateAddName(builder, name)
fbs.CommandShipClassCreateAddDrive(builder, cmd.Drive)
fbs.CommandShipClassCreateAddArmament(builder, int64(cmd.Armament))
fbs.CommandShipClassCreateAddWeapons(builder, cmd.Weapons)
fbs.CommandShipClassCreateAddShields(builder, cmd.Shields)
fbs.CommandShipClassCreateAddCargo(builder, cmd.Cargo)
payload := fbs.CommandShipClassCreateEnd(builder)
return encodedCommandFromMeta(cmd.CommandMeta, fbs.CommandPayloadCommandShipClassCreate, payload), nil
case *model.CommandShipClassMerge:
if cmd == nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: command is nil %T", index, command)
}
name := builder.CreateString(cmd.Name)
target := builder.CreateString(cmd.Target)
fbs.CommandShipClassMergeStart(builder)
fbs.CommandShipClassMergeAddName(builder, name)
fbs.CommandShipClassMergeAddTarget(builder, target)
payload := fbs.CommandShipClassMergeEnd(builder)
return encodedCommandFromMeta(cmd.CommandMeta, fbs.CommandPayloadCommandShipClassMerge, payload), nil
case *model.CommandShipClassRemove:
if cmd == nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: command is nil %T", index, command)
}
name := builder.CreateString(cmd.Name)
fbs.CommandShipClassRemoveStart(builder)
fbs.CommandShipClassRemoveAddName(builder, name)
payload := fbs.CommandShipClassRemoveEnd(builder)
return encodedCommandFromMeta(cmd.CommandMeta, fbs.CommandPayloadCommandShipClassRemove, payload), nil
case *model.CommandShipGroupBreak:
if cmd == nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: command is nil %T", index, command)
}
id := builder.CreateString(cmd.ID)
newID := builder.CreateString(cmd.NewID)
fbs.CommandShipGroupBreakStart(builder)
fbs.CommandShipGroupBreakAddId(builder, id)
fbs.CommandShipGroupBreakAddNewId(builder, newID)
fbs.CommandShipGroupBreakAddQuantity(builder, int64(cmd.Quantity))
payload := fbs.CommandShipGroupBreakEnd(builder)
return encodedCommandFromMeta(cmd.CommandMeta, fbs.CommandPayloadCommandShipGroupBreak, payload), nil
case *model.CommandShipGroupLoad:
if cmd == nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: command is nil %T", index, command)
}
cargo, err := shipGroupCargoToFBS(cmd.Cargo)
if err != nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: %w", index, err)
}
id := builder.CreateString(cmd.ID)
fbs.CommandShipGroupLoadStart(builder)
fbs.CommandShipGroupLoadAddId(builder, id)
fbs.CommandShipGroupLoadAddCargo(builder, cargo)
fbs.CommandShipGroupLoadAddQuantity(builder, cmd.Quantity)
payload := fbs.CommandShipGroupLoadEnd(builder)
return encodedCommandFromMeta(cmd.CommandMeta, fbs.CommandPayloadCommandShipGroupLoad, payload), nil
case *model.CommandShipGroupUnload:
if cmd == nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: command is nil %T", index, command)
}
id := builder.CreateString(cmd.ID)
fbs.CommandShipGroupUnloadStart(builder)
fbs.CommandShipGroupUnloadAddId(builder, id)
fbs.CommandShipGroupUnloadAddQuantity(builder, cmd.Quantity)
payload := fbs.CommandShipGroupUnloadEnd(builder)
return encodedCommandFromMeta(cmd.CommandMeta, fbs.CommandPayloadCommandShipGroupUnload, payload), nil
case *model.CommandShipGroupSend:
if cmd == nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: command is nil %T", index, command)
}
id := builder.CreateString(cmd.ID)
fbs.CommandShipGroupSendStart(builder)
fbs.CommandShipGroupSendAddId(builder, id)
fbs.CommandShipGroupSendAddDestination(builder, int64(cmd.Destination))
payload := fbs.CommandShipGroupSendEnd(builder)
return encodedCommandFromMeta(cmd.CommandMeta, fbs.CommandPayloadCommandShipGroupSend, payload), nil
case *model.CommandShipGroupUpgrade:
if cmd == nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: command is nil %T", index, command)
}
tech, err := shipGroupUpgradeTechToFBS(cmd.Tech)
if err != nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: %w", index, err)
}
id := builder.CreateString(cmd.ID)
fbs.CommandShipGroupUpgradeStart(builder)
fbs.CommandShipGroupUpgradeAddId(builder, id)
fbs.CommandShipGroupUpgradeAddTech(builder, tech)
fbs.CommandShipGroupUpgradeAddLevel(builder, cmd.Level)
payload := fbs.CommandShipGroupUpgradeEnd(builder)
return encodedCommandFromMeta(cmd.CommandMeta, fbs.CommandPayloadCommandShipGroupUpgrade, payload), nil
case *model.CommandShipGroupMerge:
if cmd == nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: command is nil %T", index, command)
}
fbs.CommandShipGroupMergeStart(builder)
payload := fbs.CommandShipGroupMergeEnd(builder)
return encodedCommandFromMeta(cmd.CommandMeta, fbs.CommandPayloadCommandShipGroupMerge, payload), nil
case *model.CommandShipGroupDismantle:
if cmd == nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: command is nil %T", index, command)
}
id := builder.CreateString(cmd.ID)
fbs.CommandShipGroupDismantleStart(builder)
fbs.CommandShipGroupDismantleAddId(builder, id)
payload := fbs.CommandShipGroupDismantleEnd(builder)
return encodedCommandFromMeta(cmd.CommandMeta, fbs.CommandPayloadCommandShipGroupDismantle, payload), nil
case *model.CommandShipGroupTransfer:
if cmd == nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: command is nil %T", index, command)
}
id := builder.CreateString(cmd.ID)
acceptor := builder.CreateString(cmd.Acceptor)
fbs.CommandShipGroupTransferStart(builder)
fbs.CommandShipGroupTransferAddId(builder, id)
fbs.CommandShipGroupTransferAddAcceptor(builder, acceptor)
payload := fbs.CommandShipGroupTransferEnd(builder)
return encodedCommandFromMeta(cmd.CommandMeta, fbs.CommandPayloadCommandShipGroupTransfer, payload), nil
case *model.CommandShipGroupJoinFleet:
if cmd == nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: command is nil %T", index, command)
}
id := builder.CreateString(cmd.ID)
name := builder.CreateString(cmd.Name)
fbs.CommandShipGroupJoinFleetStart(builder)
fbs.CommandShipGroupJoinFleetAddId(builder, id)
fbs.CommandShipGroupJoinFleetAddName(builder, name)
payload := fbs.CommandShipGroupJoinFleetEnd(builder)
return encodedCommandFromMeta(cmd.CommandMeta, fbs.CommandPayloadCommandShipGroupJoinFleet, payload), nil
case *model.CommandFleetMerge:
if cmd == nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: command is nil %T", index, command)
}
name := builder.CreateString(cmd.Name)
target := builder.CreateString(cmd.Target)
fbs.CommandFleetMergeStart(builder)
fbs.CommandFleetMergeAddName(builder, name)
fbs.CommandFleetMergeAddTarget(builder, target)
payload := fbs.CommandFleetMergeEnd(builder)
return encodedCommandFromMeta(cmd.CommandMeta, fbs.CommandPayloadCommandFleetMerge, payload), nil
case *model.CommandFleetSend:
if cmd == nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: command is nil %T", index, command)
}
name := builder.CreateString(cmd.Name)
fbs.CommandFleetSendStart(builder)
fbs.CommandFleetSendAddName(builder, name)
fbs.CommandFleetSendAddDestination(builder, int64(cmd.Destination))
payload := fbs.CommandFleetSendEnd(builder)
return encodedCommandFromMeta(cmd.CommandMeta, fbs.CommandPayloadCommandFleetSend, payload), nil
case *model.CommandScienceCreate:
if cmd == nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: command is nil %T", index, command)
}
name := builder.CreateString(cmd.Name)
fbs.CommandScienceCreateStart(builder)
fbs.CommandScienceCreateAddName(builder, name)
fbs.CommandScienceCreateAddDrive(builder, cmd.Drive)
fbs.CommandScienceCreateAddWeapons(builder, cmd.Weapons)
fbs.CommandScienceCreateAddShields(builder, cmd.Shields)
fbs.CommandScienceCreateAddCargo(builder, cmd.Cargo)
payload := fbs.CommandScienceCreateEnd(builder)
return encodedCommandFromMeta(cmd.CommandMeta, fbs.CommandPayloadCommandScienceCreate, payload), nil
case *model.CommandScienceRemove:
if cmd == nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: command is nil %T", index, command)
}
name := builder.CreateString(cmd.Name)
fbs.CommandScienceRemoveStart(builder)
fbs.CommandScienceRemoveAddName(builder, name)
payload := fbs.CommandScienceRemoveEnd(builder)
return encodedCommandFromMeta(cmd.CommandMeta, fbs.CommandPayloadCommandScienceRemove, payload), nil
case *model.CommandPlanetRename:
if cmd == nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: command is nil %T", index, command)
}
name := builder.CreateString(cmd.Name)
fbs.CommandPlanetRenameStart(builder)
fbs.CommandPlanetRenameAddNumber(builder, int64(cmd.Number))
fbs.CommandPlanetRenameAddName(builder, name)
payload := fbs.CommandPlanetRenameEnd(builder)
return encodedCommandFromMeta(cmd.CommandMeta, fbs.CommandPayloadCommandPlanetRename, payload), nil
case *model.CommandPlanetProduce:
if cmd == nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: command is nil %T", index, command)
}
production, err := planetProductionToFBS(cmd.Production)
if err != nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: %w", index, err)
}
subject := builder.CreateString(cmd.Subject)
fbs.CommandPlanetProduceStart(builder)
fbs.CommandPlanetProduceAddNumber(builder, int64(cmd.Number))
fbs.CommandPlanetProduceAddProduction(builder, production)
fbs.CommandPlanetProduceAddSubject(builder, subject)
payload := fbs.CommandPlanetProduceEnd(builder)
return encodedCommandFromMeta(cmd.CommandMeta, fbs.CommandPayloadCommandPlanetProduce, payload), nil
case *model.CommandPlanetRouteSet:
if cmd == nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: command is nil %T", index, command)
}
loadType, err := planetRouteLoadTypeToFBS(cmd.LoadType)
if err != nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: %w", index, err)
}
fbs.CommandPlanetRouteSetStart(builder)
fbs.CommandPlanetRouteSetAddOrigin(builder, int64(cmd.Origin))
fbs.CommandPlanetRouteSetAddDestination(builder, int64(cmd.Destination))
fbs.CommandPlanetRouteSetAddLoadType(builder, loadType)
payload := fbs.CommandPlanetRouteSetEnd(builder)
return encodedCommandFromMeta(cmd.CommandMeta, fbs.CommandPayloadCommandPlanetRouteSet, payload), nil
case *model.CommandPlanetRouteRemove:
if cmd == nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: command is nil %T", index, command)
}
loadType, err := planetRouteLoadTypeToFBS(cmd.LoadType)
if err != nil {
return encodedCommand{}, fmt.Errorf("encode order command %d: %w", index, err)
}
fbs.CommandPlanetRouteRemoveStart(builder)
fbs.CommandPlanetRouteRemoveAddOrigin(builder, int64(cmd.Origin))
fbs.CommandPlanetRouteRemoveAddLoadType(builder, loadType)
payload := fbs.CommandPlanetRouteRemoveEnd(builder)
return encodedCommandFromMeta(cmd.CommandMeta, fbs.CommandPayloadCommandPlanetRouteRemove, payload), nil
default:
return encodedCommand{}, fmt.Errorf("encode order command %d: unsupported command type %T", index, command)
}
}
func encodedCommandFromMeta(meta model.CommandMeta, payloadType fbs.CommandPayload, payloadOffset flatbuffers.UOffsetT) encodedCommand {
return encodedCommand{
cmdID: meta.CmdID,
cmdApplied: cloneBoolPointer(meta.CmdApplied),
cmdErrCode: cloneIntPointer(meta.CmdErrCode),
payloadType: payloadType,
payloadOffset: payloadOffset,
}
}
func decodeOrderCommand(flatCommand *fbs.CommandItem, index int) (model.DecodableCommand, error) {
commandMeta := model.CommandMeta{
CmdID: string(flatCommand.CmdId()),
CmdApplied: cloneBoolPointer(flatCommand.CmdApplied()),
}
if cmdErrCode := flatCommand.CmdErrorCode(); cmdErrCode != nil {
decodedCmdErrCode, err := int64ToInt(*cmdErrCode, "cmd_error_code")
if err != nil {
return nil, fmt.Errorf("decode order command %d: %w", index, err)
}
commandMeta.CmdErrCode = &decodedCmdErrCode
}
payloadType := flatCommand.PayloadType()
if payloadType == fbs.CommandPayloadNONE {
return nil, fmt.Errorf("decode order command %d: payload type is NONE", index)
}
payload := new(flatbuffers.Table)
if !flatCommand.Payload(payload) {
return nil, fmt.Errorf("decode order command %d: payload is missing", index)
}
switch payloadType {
case fbs.CommandPayloadCommandRaceQuit:
commandMeta.CmdType = model.CommandTypeRaceQuit
return &model.CommandRaceQuit{CommandMeta: commandMeta}, nil
case fbs.CommandPayloadCommandRaceVote:
commandMeta.CmdType = model.CommandTypeRaceVote
commandPayload := new(fbs.CommandRaceVote)
commandPayload.Init(payload.Bytes, payload.Pos)
return &model.CommandRaceVote{
CommandMeta: commandMeta,
Acceptor: string(commandPayload.Acceptor()),
}, nil
case fbs.CommandPayloadCommandRaceRelation:
commandPayload := new(fbs.CommandRaceRelation)
commandPayload.Init(payload.Bytes, payload.Pos)
relation, err := relationFromFBS(commandPayload.Relation())
if err != nil {
return nil, fmt.Errorf("decode order command %d: %w", index, err)
}
commandMeta.CmdType = model.CommandTypeRaceRelation
return &model.CommandRaceRelation{
CommandMeta: commandMeta,
Acceptor: string(commandPayload.Acceptor()),
Relation: relation,
}, nil
case fbs.CommandPayloadCommandShipClassCreate:
commandMeta.CmdType = model.CommandTypeShipClassCreate
commandPayload := new(fbs.CommandShipClassCreate)
commandPayload.Init(payload.Bytes, payload.Pos)
armament, err := int64ToInt(commandPayload.Armament(), "armament")
if err != nil {
return nil, fmt.Errorf("decode order command %d: %w", index, err)
}
return &model.CommandShipClassCreate{
CommandMeta: commandMeta,
Name: string(commandPayload.Name()),
Drive: commandPayload.Drive(),
Armament: armament,
Weapons: commandPayload.Weapons(),
Shields: commandPayload.Shields(),
Cargo: commandPayload.Cargo(),
}, nil
case fbs.CommandPayloadCommandShipClassMerge:
commandMeta.CmdType = model.CommandTypeShipClassMerge
commandPayload := new(fbs.CommandShipClassMerge)
commandPayload.Init(payload.Bytes, payload.Pos)
return &model.CommandShipClassMerge{
CommandMeta: commandMeta,
Name: string(commandPayload.Name()),
Target: string(commandPayload.Target()),
}, nil
case fbs.CommandPayloadCommandShipClassRemove:
commandMeta.CmdType = model.CommandTypeShipClassRemove
commandPayload := new(fbs.CommandShipClassRemove)
commandPayload.Init(payload.Bytes, payload.Pos)
return &model.CommandShipClassRemove{
CommandMeta: commandMeta,
Name: string(commandPayload.Name()),
}, nil
case fbs.CommandPayloadCommandShipGroupBreak:
commandMeta.CmdType = model.CommandTypeShipGroupBreak
commandPayload := new(fbs.CommandShipGroupBreak)
commandPayload.Init(payload.Bytes, payload.Pos)
quantity, err := int64ToInt(commandPayload.Quantity(), "quantity")
if err != nil {
return nil, fmt.Errorf("decode order command %d: %w", index, err)
}
return &model.CommandShipGroupBreak{
CommandMeta: commandMeta,
ID: string(commandPayload.Id()),
NewID: string(commandPayload.NewId()),
Quantity: quantity,
}, nil
case fbs.CommandPayloadCommandShipGroupLoad:
commandPayload := new(fbs.CommandShipGroupLoad)
commandPayload.Init(payload.Bytes, payload.Pos)
cargo, err := shipGroupCargoFromFBS(commandPayload.Cargo())
if err != nil {
return nil, fmt.Errorf("decode order command %d: %w", index, err)
}
commandMeta.CmdType = model.CommandTypeShipGroupLoad
return &model.CommandShipGroupLoad{
CommandMeta: commandMeta,
ID: string(commandPayload.Id()),
Cargo: cargo,
Quantity: commandPayload.Quantity(),
}, nil
case fbs.CommandPayloadCommandShipGroupUnload:
commandMeta.CmdType = model.CommandTypeShipGroupUnload
commandPayload := new(fbs.CommandShipGroupUnload)
commandPayload.Init(payload.Bytes, payload.Pos)
return &model.CommandShipGroupUnload{
CommandMeta: commandMeta,
ID: string(commandPayload.Id()),
Quantity: commandPayload.Quantity(),
}, nil
case fbs.CommandPayloadCommandShipGroupSend:
commandMeta.CmdType = model.CommandTypeShipGroupSend
commandPayload := new(fbs.CommandShipGroupSend)
commandPayload.Init(payload.Bytes, payload.Pos)
destination, err := int64ToInt(commandPayload.Destination(), "destination")
if err != nil {
return nil, fmt.Errorf("decode order command %d: %w", index, err)
}
return &model.CommandShipGroupSend{
CommandMeta: commandMeta,
ID: string(commandPayload.Id()),
Destination: destination,
}, nil
case fbs.CommandPayloadCommandShipGroupUpgrade:
commandPayload := new(fbs.CommandShipGroupUpgrade)
commandPayload.Init(payload.Bytes, payload.Pos)
tech, err := shipGroupUpgradeTechFromFBS(commandPayload.Tech())
if err != nil {
return nil, fmt.Errorf("decode order command %d: %w", index, err)
}
commandMeta.CmdType = model.CommandTypeShipGroupUpgrade
return &model.CommandShipGroupUpgrade{
CommandMeta: commandMeta,
ID: string(commandPayload.Id()),
Tech: tech,
Level: commandPayload.Level(),
}, nil
case fbs.CommandPayloadCommandShipGroupMerge:
commandMeta.CmdType = model.CommandTypeShipGroupMerge
return &model.CommandShipGroupMerge{CommandMeta: commandMeta}, nil
case fbs.CommandPayloadCommandShipGroupDismantle:
commandMeta.CmdType = model.CommandTypeShipGroupDismantle
commandPayload := new(fbs.CommandShipGroupDismantle)
commandPayload.Init(payload.Bytes, payload.Pos)
return &model.CommandShipGroupDismantle{
CommandMeta: commandMeta,
ID: string(commandPayload.Id()),
}, nil
case fbs.CommandPayloadCommandShipGroupTransfer:
commandMeta.CmdType = model.CommandTypeShipGroupTransfer
commandPayload := new(fbs.CommandShipGroupTransfer)
commandPayload.Init(payload.Bytes, payload.Pos)
return &model.CommandShipGroupTransfer{
CommandMeta: commandMeta,
ID: string(commandPayload.Id()),
Acceptor: string(commandPayload.Acceptor()),
}, nil
case fbs.CommandPayloadCommandShipGroupJoinFleet:
commandMeta.CmdType = model.CommandTypeShipGroupJoinFleet
commandPayload := new(fbs.CommandShipGroupJoinFleet)
commandPayload.Init(payload.Bytes, payload.Pos)
return &model.CommandShipGroupJoinFleet{
CommandMeta: commandMeta,
ID: string(commandPayload.Id()),
Name: string(commandPayload.Name()),
}, nil
case fbs.CommandPayloadCommandFleetMerge:
commandMeta.CmdType = model.CommandTypeFleetMerge
commandPayload := new(fbs.CommandFleetMerge)
commandPayload.Init(payload.Bytes, payload.Pos)
return &model.CommandFleetMerge{
CommandMeta: commandMeta,
Name: string(commandPayload.Name()),
Target: string(commandPayload.Target()),
}, nil
case fbs.CommandPayloadCommandFleetSend:
commandMeta.CmdType = model.CommandTypeFleetSend
commandPayload := new(fbs.CommandFleetSend)
commandPayload.Init(payload.Bytes, payload.Pos)
destination, err := int64ToInt(commandPayload.Destination(), "destination")
if err != nil {
return nil, fmt.Errorf("decode order command %d: %w", index, err)
}
return &model.CommandFleetSend{
CommandMeta: commandMeta,
Name: string(commandPayload.Name()),
Destination: destination,
}, nil
case fbs.CommandPayloadCommandScienceCreate:
commandMeta.CmdType = model.CommandTypeScienceCreate
commandPayload := new(fbs.CommandScienceCreate)
commandPayload.Init(payload.Bytes, payload.Pos)
return &model.CommandScienceCreate{
CommandMeta: commandMeta,
Name: string(commandPayload.Name()),
Drive: commandPayload.Drive(),
Weapons: commandPayload.Weapons(),
Shields: commandPayload.Shields(),
Cargo: commandPayload.Cargo(),
}, nil
case fbs.CommandPayloadCommandScienceRemove:
commandMeta.CmdType = model.CommandTypeScienceRemove
commandPayload := new(fbs.CommandScienceRemove)
commandPayload.Init(payload.Bytes, payload.Pos)
return &model.CommandScienceRemove{
CommandMeta: commandMeta,
Name: string(commandPayload.Name()),
}, nil
case fbs.CommandPayloadCommandPlanetRename:
commandMeta.CmdType = model.CommandTypePlanetRename
commandPayload := new(fbs.CommandPlanetRename)
commandPayload.Init(payload.Bytes, payload.Pos)
number, err := int64ToInt(commandPayload.Number(), "number")
if err != nil {
return nil, fmt.Errorf("decode order command %d: %w", index, err)
}
return &model.CommandPlanetRename{
CommandMeta: commandMeta,
Number: number,
Name: string(commandPayload.Name()),
}, nil
case fbs.CommandPayloadCommandPlanetProduce:
commandPayload := new(fbs.CommandPlanetProduce)
commandPayload.Init(payload.Bytes, payload.Pos)
production, err := planetProductionFromFBS(commandPayload.Production())
if err != nil {
return nil, fmt.Errorf("decode order command %d: %w", index, err)
}
number, err := int64ToInt(commandPayload.Number(), "number")
if err != nil {
return nil, fmt.Errorf("decode order command %d: %w", index, err)
}
commandMeta.CmdType = model.CommandTypePlanetProduce
return &model.CommandPlanetProduce{
CommandMeta: commandMeta,
Number: number,
Production: production,
Subject: string(commandPayload.Subject()),
}, nil
case fbs.CommandPayloadCommandPlanetRouteSet:
commandPayload := new(fbs.CommandPlanetRouteSet)
commandPayload.Init(payload.Bytes, payload.Pos)
loadType, err := planetRouteLoadTypeFromFBS(commandPayload.LoadType())
if err != nil {
return nil, fmt.Errorf("decode order command %d: %w", index, err)
}
origin, err := int64ToInt(commandPayload.Origin(), "origin")
if err != nil {
return nil, fmt.Errorf("decode order command %d: %w", index, err)
}
destination, err := int64ToInt(commandPayload.Destination(), "destination")
if err != nil {
return nil, fmt.Errorf("decode order command %d: %w", index, err)
}
commandMeta.CmdType = model.CommandTypePlanetRouteSet
return &model.CommandPlanetRouteSet{
CommandMeta: commandMeta,
Origin: origin,
Destination: destination,
LoadType: loadType,
}, nil
case fbs.CommandPayloadCommandPlanetRouteRemove:
commandPayload := new(fbs.CommandPlanetRouteRemove)
commandPayload.Init(payload.Bytes, payload.Pos)
loadType, err := planetRouteLoadTypeFromFBS(commandPayload.LoadType())
if err != nil {
return nil, fmt.Errorf("decode order command %d: %w", index, err)
}
origin, err := int64ToInt(commandPayload.Origin(), "origin")
if err != nil {
return nil, fmt.Errorf("decode order command %d: %w", index, err)
}
commandMeta.CmdType = model.CommandTypePlanetRouteRemove
return &model.CommandPlanetRouteRemove{
CommandMeta: commandMeta,
Origin: origin,
LoadType: loadType,
}, nil
default:
return nil, fmt.Errorf("decode order command %d: unknown command payload type %d", index, payloadType)
}
}
// int64ToInt narrows v to a Go int. Returns an error when v overflows
// the platform `int` range (only possible on 32-bit builds; on 64-bit
// the check is a no-op). fieldName is used in the error for caller
// context.
func int64ToInt(value int64, field string) (int, error) {
maxInt := int64(int(^uint(0) >> 1))
minInt := -maxInt - 1
if value < minInt || value > maxInt {
return 0, fmt.Errorf("%s value %d overflows int", field, value)
}
return int(value), nil
}
func relationToFBS(value string) (fbs.Relation, error) {
switch value {
case "WAR":
return fbs.RelationWAR, nil
case "PEACE":
return fbs.RelationPEACE, nil
default:
return fbs.RelationUNKNOWN, fmt.Errorf("unsupported relation value %q", value)
}
}
func relationFromFBS(value fbs.Relation) (string, error) {
switch value {
case fbs.RelationWAR:
return "WAR", nil
case fbs.RelationPEACE:
return "PEACE", nil
case fbs.RelationUNKNOWN:
return "", errors.New("relation value UNKNOWN is not allowed")
default:
return "", fmt.Errorf("unsupported relation enum value %d", value)
}
}
func shipGroupCargoToFBS(value string) (fbs.ShipGroupCargo, error) {
switch value {
case "COL":
return fbs.ShipGroupCargoCOL, nil
case "MAT":
return fbs.ShipGroupCargoMAT, nil
case "CAP":
return fbs.ShipGroupCargoCAP, nil
default:
return fbs.ShipGroupCargoUNKNOWN, fmt.Errorf("unsupported ship group cargo value %q", value)
}
}
func shipGroupCargoFromFBS(value fbs.ShipGroupCargo) (string, error) {
switch value {
case fbs.ShipGroupCargoCOL:
return "COL", nil
case fbs.ShipGroupCargoMAT:
return "MAT", nil
case fbs.ShipGroupCargoCAP:
return "CAP", nil
case fbs.ShipGroupCargoUNKNOWN:
return "", errors.New("ship group cargo value UNKNOWN is not allowed")
default:
return "", fmt.Errorf("unsupported ship group cargo enum value %d", value)
}
}
func shipGroupUpgradeTechToFBS(value string) (fbs.ShipGroupUpgradeTech, error) {
switch value {
case "ALL":
return fbs.ShipGroupUpgradeTechALL, nil
case "DRIVE":
return fbs.ShipGroupUpgradeTechDRIVE, nil
case "WEAPONS":
return fbs.ShipGroupUpgradeTechWEAPONS, nil
case "SHIELDS":
return fbs.ShipGroupUpgradeTechSHIELDS, nil
case "CARGO":
return fbs.ShipGroupUpgradeTechCARGO, nil
default:
return fbs.ShipGroupUpgradeTechUNKNOWN, fmt.Errorf("unsupported ship group upgrade tech value %q", value)
}
}
func shipGroupUpgradeTechFromFBS(value fbs.ShipGroupUpgradeTech) (string, error) {
switch value {
case fbs.ShipGroupUpgradeTechALL:
return "ALL", nil
case fbs.ShipGroupUpgradeTechDRIVE:
return "DRIVE", nil
case fbs.ShipGroupUpgradeTechWEAPONS:
return "WEAPONS", nil
case fbs.ShipGroupUpgradeTechSHIELDS:
return "SHIELDS", nil
case fbs.ShipGroupUpgradeTechCARGO:
return "CARGO", nil
case fbs.ShipGroupUpgradeTechUNKNOWN:
return "", errors.New("ship group upgrade tech value UNKNOWN is not allowed")
default:
return "", fmt.Errorf("unsupported ship group upgrade tech enum value %d", value)
}
}
func planetProductionToFBS(value string) (fbs.PlanetProduction, error) {
switch value {
case "MAT":
return fbs.PlanetProductionMAT, nil
case "CAP":
return fbs.PlanetProductionCAP, nil
case "DRIVE":
return fbs.PlanetProductionDRIVE, nil
case "WEAPONS":
return fbs.PlanetProductionWEAPONS, nil
case "SHIELDS":
return fbs.PlanetProductionSHIELDS, nil
case "CARGO":
return fbs.PlanetProductionCARGO, nil
case "SCIENCE":
return fbs.PlanetProductionSCIENCE, nil
case "SHIP":
return fbs.PlanetProductionSHIP, nil
default:
return fbs.PlanetProductionUNKNOWN, fmt.Errorf("unsupported planet production value %q", value)
}
}
func planetProductionFromFBS(value fbs.PlanetProduction) (string, error) {
switch value {
case fbs.PlanetProductionMAT:
return "MAT", nil
case fbs.PlanetProductionCAP:
return "CAP", nil
case fbs.PlanetProductionDRIVE:
return "DRIVE", nil
case fbs.PlanetProductionWEAPONS:
return "WEAPONS", nil
case fbs.PlanetProductionSHIELDS:
return "SHIELDS", nil
case fbs.PlanetProductionCARGO:
return "CARGO", nil
case fbs.PlanetProductionSCIENCE:
return "SCIENCE", nil
case fbs.PlanetProductionSHIP:
return "SHIP", nil
case fbs.PlanetProductionUNKNOWN:
return "", errors.New("planet production value UNKNOWN is not allowed")
default:
return "", fmt.Errorf("unsupported planet production enum value %d", value)
}
}
func planetRouteLoadTypeToFBS(value string) (fbs.PlanetRouteLoadType, error) {
switch value {
case "MAT":
return fbs.PlanetRouteLoadTypeMAT, nil
case "CAP":
return fbs.PlanetRouteLoadTypeCAP, nil
case "COL":
return fbs.PlanetRouteLoadTypeCOL, nil
case "EMP":
return fbs.PlanetRouteLoadTypeEMP, nil
default:
return fbs.PlanetRouteLoadTypeUNKNOWN, fmt.Errorf("unsupported planet route load type value %q", value)
}
}
func planetRouteLoadTypeFromFBS(value fbs.PlanetRouteLoadType) (string, error) {
switch value {
case fbs.PlanetRouteLoadTypeMAT:
return "MAT", nil
case fbs.PlanetRouteLoadTypeCAP:
return "CAP", nil
case fbs.PlanetRouteLoadTypeCOL:
return "COL", nil
case fbs.PlanetRouteLoadTypeEMP:
return "EMP", nil
case fbs.PlanetRouteLoadTypeUNKNOWN:
return "", errors.New("planet route load type value UNKNOWN is not allowed")
default:
return "", fmt.Errorf("unsupported planet route load type enum value %d", value)
}
}
func cloneBoolPointer(value *bool) *bool {
if value == nil {
return nil
}
cloned := *value
return &cloned
}
func cloneIntPointer(value *int) *int {
if value == nil {
return nil
}
cloned := *value
return &cloned
}
// UserGamesCommandToPayload converts model.UserGamesCommand to
// FlatBuffers bytes suitable for the authenticated gateway transport.
// `GameID` is required.
func UserGamesCommandToPayload(req *model.UserGamesCommand) ([]byte, error) {
if req == nil {
return nil, errors.New("encode user games command payload: request is nil")
}
builder := flatbuffers.NewBuilder(1024)
commandsVec, err := encodeCommandItemVector(builder, req.Commands, "user games command")
if err != nil {
return nil, err
}
fbs.UserGamesCommandStart(builder)
hi, lo := uuidToHiLo(req.GameID)
fbs.UserGamesCommandAddGameId(builder, commonfbs.CreateUUID(builder, hi, lo))
if commandsVec != 0 {
fbs.UserGamesCommandAddCommands(builder, commandsVec)
}
offset := fbs.UserGamesCommandEnd(builder)
fbs.FinishUserGamesCommandBuffer(builder, offset)
return builder.FinishedBytes(), nil
}
// PayloadToUserGamesCommand converts FlatBuffers payload bytes into
// model.UserGamesCommand.
func PayloadToUserGamesCommand(data []byte) (result *model.UserGamesCommand, err error) {
if len(data) == 0 {
return nil, errors.New("decode user games command payload: data is empty")
}
defer func() {
if recovered := recover(); recovered != nil {
result = nil
err = fmt.Errorf("decode user games command payload: panic recovered: %v", recovered)
}
}()
flat := fbs.GetRootAsUserGamesCommand(data, 0)
gameID := flat.GameId(nil)
if gameID == nil {
return nil, errors.New("decode user games command payload: game_id is missing")
}
out := &model.UserGamesCommand{
GameID: uuidFromHiLo(gameID.Hi(), gameID.Lo()),
}
count := flat.CommandsLength()
if count > 0 {
out.Commands = make([]model.DecodableCommand, count)
flatCommand := new(fbs.CommandItem)
for i := 0; i < count; i++ {
if !flat.Commands(flatCommand, i) {
return nil, fmt.Errorf("decode user games command %d: command item is missing", i)
}
cmd, decodeErr := decodeOrderCommand(flatCommand, i)
if decodeErr != nil {
return nil, decodeErr
}
out.Commands[i] = cmd
}
}
return out, nil
}
// UserGamesOrderToPayload converts model.UserGamesOrder to FlatBuffers
// bytes suitable for the authenticated gateway transport.
func UserGamesOrderToPayload(req *model.UserGamesOrder) ([]byte, error) {
if req == nil {
return nil, errors.New("encode user games order payload: request is nil")
}
builder := flatbuffers.NewBuilder(1024)
commandsVec, err := encodeCommandItemVector(builder, req.Commands, "user games order")
if err != nil {
return nil, err
}
fbs.UserGamesOrderStart(builder)
hi, lo := uuidToHiLo(req.GameID)
fbs.UserGamesOrderAddGameId(builder, commonfbs.CreateUUID(builder, hi, lo))
fbs.UserGamesOrderAddUpdatedAt(builder, int64(req.UpdatedAt))
if commandsVec != 0 {
fbs.UserGamesOrderAddCommands(builder, commandsVec)
}
offset := fbs.UserGamesOrderEnd(builder)
fbs.FinishUserGamesOrderBuffer(builder, offset)
return builder.FinishedBytes(), nil
}
// PayloadToUserGamesOrder converts FlatBuffers payload bytes into
// model.UserGamesOrder.
func PayloadToUserGamesOrder(data []byte) (result *model.UserGamesOrder, err error) {
if len(data) == 0 {
return nil, errors.New("decode user games order payload: data is empty")
}
defer func() {
if recovered := recover(); recovered != nil {
result = nil
err = fmt.Errorf("decode user games order payload: panic recovered: %v", recovered)
}
}()
flat := fbs.GetRootAsUserGamesOrder(data, 0)
gameID := flat.GameId(nil)
if gameID == nil {
return nil, errors.New("decode user games order payload: game_id is missing")
}
// updatedAt, convErr := int64ToInt(flat.UpdatedAt(), "updated_at")
// if convErr != nil {
// return nil, fmt.Errorf("decode user games order payload: %w", convErr)
// }
out := &model.UserGamesOrder{
GameID: uuidFromHiLo(gameID.Hi(), gameID.Lo()),
UpdatedAt: flat.UpdatedAt(),
}
count := flat.CommandsLength()
if count > 0 {
out.Commands = make([]model.DecodableCommand, count)
flatCommand := new(fbs.CommandItem)
for i := 0; i < count; i++ {
if !flat.Commands(flatCommand, i) {
return nil, fmt.Errorf("decode user games order %d: command item is missing", i)
}
cmd, decodeErr := decodeOrderCommand(flatCommand, i)
if decodeErr != nil {
return nil, decodeErr
}
out.Commands[i] = cmd
}
}
return out, nil
}
// EmptyUserGamesCommandResponsePayload returns a FlatBuffers-encoded
// empty `UserGamesCommandResponse` buffer. Used by gateway to ack a
// successful `MessageTypeUserGamesCommand` even though the engine
// returns 204 No Content — the typed envelope keeps the message-type
// contract symmetric with other authenticated routes.
func EmptyUserGamesCommandResponsePayload() []byte {
builder := flatbuffers.NewBuilder(16)
fbs.UserGamesCommandResponseStart(builder)
offset := fbs.UserGamesCommandResponseEnd(builder)
fbs.FinishUserGamesCommandResponseBuffer(builder, offset)
return builder.FinishedBytes()
}
// EmptyUserGamesOrderResponsePayload mirrors
// EmptyUserGamesCommandResponsePayload for `MessageTypeUserGamesOrder`.
func EmptyUserGamesOrderResponsePayload() []byte {
builder := flatbuffers.NewBuilder(16)
fbs.UserGamesOrderResponseStart(builder)
offset := fbs.UserGamesOrderResponseEnd(builder)
fbs.FinishUserGamesOrderResponseBuffer(builder, offset)
return builder.FinishedBytes()
}
// encodeCommandItemVector serialises a slice of DecodableCommand into a
// FlatBuffers vector of CommandItem. Used by UserGamesCommandToPayload
// and UserGamesOrderToPayload to keep the per-command encoding logic in
// one place.
func encodeCommandItemVector(builder *flatbuffers.Builder, commands []model.DecodableCommand, opLabel string) (flatbuffers.UOffsetT, error) {
offsets := make([]flatbuffers.UOffsetT, len(commands))
for i := range commands {
encoded, err := encodeOrderCommand(builder, commands[i], i)
if err != nil {
return 0, fmt.Errorf("encode %s: %w", opLabel, err)
}
cmdID := builder.CreateString(encoded.cmdID)
fbs.CommandItemStart(builder)
fbs.CommandItemAddCmdId(builder, cmdID)
if encoded.cmdApplied != nil {
fbs.CommandItemAddCmdApplied(builder, *encoded.cmdApplied)
}
if encoded.cmdErrCode != nil {
fbs.CommandItemAddCmdErrorCode(builder, int64(*encoded.cmdErrCode))
}
fbs.CommandItemAddPayloadType(builder, encoded.payloadType)
fbs.CommandItemAddPayload(builder, encoded.payloadOffset)
offsets[i] = fbs.CommandItemEnd(builder)
}
if len(offsets) == 0 {
return 0, nil
}
// `UserGamesCommandStartCommandsVector` and the corresponding
// `UserGamesOrderStartCommandsVector` are identical helpers (both
// expand to `builder.StartVector(4, numElems, 4)`); we use the
// command flavour for both message types so the helper has a
// single dependency point.
fbs.UserGamesCommandStartCommandsVector(builder, len(offsets))
for i := len(offsets) - 1; i >= 0; i-- {
builder.PrependUOffsetT(offsets[i])
}
return builder.EndVector(len(offsets)), nil
}