package transcoder import ( "errors" "fmt" "sort" model "galaxy/model/report" commonfbs "galaxy/schema/fbs/common" fbs "galaxy/schema/fbs/report" flatbuffers "github.com/google/flatbuffers/go" "github.com/google/uuid" ) // ReportToPayload converts model.Report from the internal representation to // FlatBuffers bytes that can be sent over network transports. // // The function returns an error when the input is nil. func ReportToPayload(report *model.Report) ([]byte, error) { if report == nil { return nil, errors.New("encode report payload: report is nil") } builder := flatbuffers.NewBuilder(4096) race := builder.CreateString(report.Race) voteFor := builder.CreateString(report.VoteFor) playerOffsets := make([]flatbuffers.UOffsetT, len(report.Player)) for i := range report.Player { playerOffsets[i] = encodeReportPlayer(builder, &report.Player[i]) } localScienceOffsets := make([]flatbuffers.UOffsetT, len(report.LocalScience)) for i := range report.LocalScience { localScienceOffsets[i] = encodeReportScience(builder, &report.LocalScience[i]) } otherScienceOffsets := make([]flatbuffers.UOffsetT, len(report.OtherScience)) for i := range report.OtherScience { otherScienceOffsets[i] = encodeReportOtherScience(builder, &report.OtherScience[i]) } localShipClassOffsets := make([]flatbuffers.UOffsetT, len(report.LocalShipClass)) for i := range report.LocalShipClass { localShipClassOffsets[i] = encodeReportShipClass(builder, &report.LocalShipClass[i]) } otherShipClassOffsets := make([]flatbuffers.UOffsetT, len(report.OtherShipClass)) for i := range report.OtherShipClass { otherShipClassOffsets[i] = encodeReportOthersShipClass(builder, &report.OtherShipClass[i]) } bombingOffsets := make([]flatbuffers.UOffsetT, len(report.Bombing)) for i := range report.Bombing { if report.Bombing[i] == nil { return nil, fmt.Errorf("encode report bombing %d: bombing is nil", i) } bombingOffsets[i] = encodeReportBombing(builder, report.Bombing[i]) } incomingGroupOffsets := make([]flatbuffers.UOffsetT, len(report.IncomingGroup)) for i := range report.IncomingGroup { incomingGroupOffsets[i] = encodeReportIncomingGroup(builder, &report.IncomingGroup[i]) } localPlanetOffsets := make([]flatbuffers.UOffsetT, len(report.LocalPlanet)) for i := range report.LocalPlanet { localPlanetOffsets[i] = encodeReportLocalPlanet(builder, &report.LocalPlanet[i]) } shipProductionOffsets := make([]flatbuffers.UOffsetT, len(report.ShipProduction)) for i := range report.ShipProduction { shipProductionOffsets[i] = encodeReportShipProduction(builder, &report.ShipProduction[i]) } routeOffsets := make([]flatbuffers.UOffsetT, len(report.Route)) for i := range report.Route { routeOffsets[i] = encodeReportRoute(builder, &report.Route[i]) } otherPlanetOffsets := make([]flatbuffers.UOffsetT, len(report.OtherPlanet)) for i := range report.OtherPlanet { otherPlanetOffsets[i] = encodeReportOtherPlanet(builder, &report.OtherPlanet[i]) } uninhabitedPlanetOffsets := make([]flatbuffers.UOffsetT, len(report.UninhabitedPlanet)) for i := range report.UninhabitedPlanet { uninhabitedPlanetOffsets[i] = encodeReportUninhabitedPlanet(builder, &report.UninhabitedPlanet[i]) } unidentifiedPlanetOffsets := make([]flatbuffers.UOffsetT, len(report.UnidentifiedPlanet)) for i := range report.UnidentifiedPlanet { unidentifiedPlanetOffsets[i] = encodeReportUnidentifiedPlanet(builder, &report.UnidentifiedPlanet[i]) } localFleetOffsets := make([]flatbuffers.UOffsetT, len(report.LocalFleet)) for i := range report.LocalFleet { localFleetOffsets[i] = encodeReportLocalFleet(builder, &report.LocalFleet[i]) } localGroupOffsets := make([]flatbuffers.UOffsetT, len(report.LocalGroup)) for i := range report.LocalGroup { localGroupOffsets[i] = encodeReportLocalGroup(builder, &report.LocalGroup[i]) } otherGroupOffsets := make([]flatbuffers.UOffsetT, len(report.OtherGroup)) for i := range report.OtherGroup { otherGroupOffsets[i] = encodeReportOtherGroup(builder, &report.OtherGroup[i]) } unidentifiedGroupOffsets := make([]flatbuffers.UOffsetT, len(report.UnidentifiedGroup)) for i := range report.UnidentifiedGroup { unidentifiedGroupOffsets[i] = encodeReportUnidentifiedGroup(builder, &report.UnidentifiedGroup[i]) } playerVector := encodeReportOffsetVector(builder, len(playerOffsets), fbs.ReportStartPlayerVector, playerOffsets) localScienceVector := encodeReportOffsetVector(builder, len(localScienceOffsets), fbs.ReportStartLocalScienceVector, localScienceOffsets) otherScienceVector := encodeReportOffsetVector(builder, len(otherScienceOffsets), fbs.ReportStartOtherScienceVector, otherScienceOffsets) localShipClassVector := encodeReportOffsetVector(builder, len(localShipClassOffsets), fbs.ReportStartLocalShipClassVector, localShipClassOffsets) otherShipClassVector := encodeReportOffsetVector(builder, len(otherShipClassOffsets), fbs.ReportStartOtherShipClassVector, otherShipClassOffsets) battleVector := encodeReportUUIDVector(builder, report.Battle) bombingVector := encodeReportOffsetVector(builder, len(bombingOffsets), fbs.ReportStartBombingVector, bombingOffsets) incomingGroupVector := encodeReportOffsetVector(builder, len(incomingGroupOffsets), fbs.ReportStartIncomingGroupVector, incomingGroupOffsets) localPlanetVector := encodeReportOffsetVector(builder, len(localPlanetOffsets), fbs.ReportStartLocalPlanetVector, localPlanetOffsets) shipProductionVector := encodeReportOffsetVector(builder, len(shipProductionOffsets), fbs.ReportStartShipProductionVector, shipProductionOffsets) routeVector := encodeReportOffsetVector(builder, len(routeOffsets), fbs.ReportStartRouteVector, routeOffsets) otherPlanetVector := encodeReportOffsetVector(builder, len(otherPlanetOffsets), fbs.ReportStartOtherPlanetVector, otherPlanetOffsets) uninhabitedPlanetVector := encodeReportOffsetVector(builder, len(uninhabitedPlanetOffsets), fbs.ReportStartUninhabitedPlanetVector, uninhabitedPlanetOffsets) unidentifiedPlanetVector := encodeReportOffsetVector(builder, len(unidentifiedPlanetOffsets), fbs.ReportStartUnidentifiedPlanetVector, unidentifiedPlanetOffsets) localFleetVector := encodeReportOffsetVector(builder, len(localFleetOffsets), fbs.ReportStartLocalFleetVector, localFleetOffsets) localGroupVector := encodeReportOffsetVector(builder, len(localGroupOffsets), fbs.ReportStartLocalGroupVector, localGroupOffsets) otherGroupVector := encodeReportOffsetVector(builder, len(otherGroupOffsets), fbs.ReportStartOtherGroupVector, otherGroupOffsets) unidentifiedGroupVector := encodeReportOffsetVector(builder, len(unidentifiedGroupOffsets), fbs.ReportStartUnidentifiedGroupVector, unidentifiedGroupOffsets) fbs.ReportStart(builder) fbs.ReportAddVersion(builder, uint64(report.Version)) fbs.ReportAddTurn(builder, uint64(report.Turn)) fbs.ReportAddWidth(builder, report.Width) fbs.ReportAddHeight(builder, report.Height) fbs.ReportAddPlanetCount(builder, report.PlanetCount) fbs.ReportAddRace(builder, race) fbs.ReportAddVotes(builder, reportFloatToFBS(report.Votes)) fbs.ReportAddVoteFor(builder, voteFor) if len(playerOffsets) > 0 { fbs.ReportAddPlayer(builder, playerVector) } if len(localScienceOffsets) > 0 { fbs.ReportAddLocalScience(builder, localScienceVector) } if len(otherScienceOffsets) > 0 { fbs.ReportAddOtherScience(builder, otherScienceVector) } if len(localShipClassOffsets) > 0 { fbs.ReportAddLocalShipClass(builder, localShipClassVector) } if len(otherShipClassOffsets) > 0 { fbs.ReportAddOtherShipClass(builder, otherShipClassVector) } if len(report.Battle) > 0 { fbs.ReportAddBattle(builder, battleVector) } if len(bombingOffsets) > 0 { fbs.ReportAddBombing(builder, bombingVector) } if len(incomingGroupOffsets) > 0 { fbs.ReportAddIncomingGroup(builder, incomingGroupVector) } if len(localPlanetOffsets) > 0 { fbs.ReportAddLocalPlanet(builder, localPlanetVector) } if len(shipProductionOffsets) > 0 { fbs.ReportAddShipProduction(builder, shipProductionVector) } if len(routeOffsets) > 0 { fbs.ReportAddRoute(builder, routeVector) } if len(otherPlanetOffsets) > 0 { fbs.ReportAddOtherPlanet(builder, otherPlanetVector) } if len(uninhabitedPlanetOffsets) > 0 { fbs.ReportAddUninhabitedPlanet(builder, uninhabitedPlanetVector) } if len(unidentifiedPlanetOffsets) > 0 { fbs.ReportAddUnidentifiedPlanet(builder, unidentifiedPlanetVector) } if len(localFleetOffsets) > 0 { fbs.ReportAddLocalFleet(builder, localFleetVector) } if len(localGroupOffsets) > 0 { fbs.ReportAddLocalGroup(builder, localGroupVector) } if len(otherGroupOffsets) > 0 { fbs.ReportAddOtherGroup(builder, otherGroupVector) } if len(unidentifiedGroupOffsets) > 0 { fbs.ReportAddUnidentifiedGroup(builder, unidentifiedGroupVector) } reportOffset := fbs.ReportEnd(builder) fbs.FinishReportBuffer(builder, reportOffset) return builder.FinishedBytes(), nil } // PayloadToReport converts FlatBuffers payload bytes into model.Report. // // The function validates payload structure and integer conversions. // Malformed payloads are returned as errors. func PayloadToReport(data []byte) (result *model.Report, err error) { if len(data) == 0 { return nil, errors.New("decode report payload: data is empty") } defer func() { if recovered := recover(); recovered != nil { result = nil err = fmt.Errorf("decode report payload: panic recovered: %v", recovered) } }() flatReport := fbs.GetRootAsReport(data, 0) version, err := uint64ToUint(flatReport.Version(), "version") if err != nil { return nil, fmt.Errorf("decode report payload: %w", err) } turn, err := uint64ToUint(flatReport.Turn(), "turn") if err != nil { return nil, fmt.Errorf("decode report payload: %w", err) } result = &model.Report{ Version: version, Turn: turn, Width: flatReport.Width(), Height: flatReport.Height(), PlanetCount: flatReport.PlanetCount(), Race: string(flatReport.Race()), Votes: reportFloatFromFBS(flatReport.Votes()), VoteFor: string(flatReport.VoteFor()), } if err := decodeReportPlayerVector(flatReport, result); err != nil { return nil, err } if err := decodeReportLocalScienceVector(flatReport, result); err != nil { return nil, err } if err := decodeReportOtherScienceVector(flatReport, result); err != nil { return nil, err } if err := decodeReportLocalShipClassVector(flatReport, result); err != nil { return nil, err } if err := decodeReportOtherShipClassVector(flatReport, result); err != nil { return nil, err } if err := decodeReportBattleVector(flatReport, result); err != nil { return nil, err } if err := decodeReportBombingVector(flatReport, result); err != nil { return nil, err } if err := decodeReportIncomingGroupVector(flatReport, result); err != nil { return nil, err } if err := decodeReportLocalPlanetVector(flatReport, result); err != nil { return nil, err } if err := decodeReportShipProductionVector(flatReport, result); err != nil { return nil, err } if err := decodeReportRouteVector(flatReport, result); err != nil { return nil, err } if err := decodeReportOtherPlanetVector(flatReport, result); err != nil { return nil, err } if err := decodeReportUninhabitedPlanetVector(flatReport, result); err != nil { return nil, err } if err := decodeReportUnidentifiedPlanetVector(flatReport, result); err != nil { return nil, err } if err := decodeReportLocalFleetVector(flatReport, result); err != nil { return nil, err } if err := decodeReportLocalGroupVector(flatReport, result); err != nil { return nil, err } if err := decodeReportOtherGroupVector(flatReport, result); err != nil { return nil, err } if err := decodeReportUnidentifiedGroupVector(flatReport, result); err != nil { return nil, err } return result, nil } func encodeReportPlayer(builder *flatbuffers.Builder, player *model.Player) flatbuffers.UOffsetT { name := builder.CreateString(player.Name) relation := builder.CreateString(player.Relation) fbs.PlayerStart(builder) fbs.PlayerAddName(builder, name) fbs.PlayerAddDrive(builder, reportFloatToFBS(player.Drive)) fbs.PlayerAddWeapons(builder, reportFloatToFBS(player.Weapons)) fbs.PlayerAddShields(builder, reportFloatToFBS(player.Shields)) fbs.PlayerAddCargo(builder, reportFloatToFBS(player.Cargo)) fbs.PlayerAddPopulation(builder, reportFloatToFBS(player.Population)) fbs.PlayerAddIndustry(builder, reportFloatToFBS(player.Industry)) fbs.PlayerAddPlanets(builder, player.Planets) fbs.PlayerAddRelation(builder, relation) fbs.PlayerAddVotes(builder, reportFloatToFBS(player.Votes)) fbs.PlayerAddExtinct(builder, player.Extinct) return fbs.PlayerEnd(builder) } func encodeReportScience(builder *flatbuffers.Builder, science *model.Science) flatbuffers.UOffsetT { name := builder.CreateString(science.Name) fbs.ScienceStart(builder) fbs.ScienceAddName(builder, name) fbs.ScienceAddDrive(builder, reportFloatToFBS(science.Drive)) fbs.ScienceAddWeapons(builder, reportFloatToFBS(science.Weapons)) fbs.ScienceAddShields(builder, reportFloatToFBS(science.Shields)) fbs.ScienceAddCargo(builder, reportFloatToFBS(science.Cargo)) return fbs.ScienceEnd(builder) } func encodeReportOtherScience(builder *flatbuffers.Builder, science *model.OtherScience) flatbuffers.UOffsetT { race := builder.CreateString(science.Race) name := builder.CreateString(science.Name) fbs.OtherScienceStart(builder) fbs.OtherScienceAddRace(builder, race) fbs.OtherScienceAddName(builder, name) fbs.OtherScienceAddDrive(builder, reportFloatToFBS(science.Drive)) fbs.OtherScienceAddWeapons(builder, reportFloatToFBS(science.Weapons)) fbs.OtherScienceAddShields(builder, reportFloatToFBS(science.Shields)) fbs.OtherScienceAddCargo(builder, reportFloatToFBS(science.Cargo)) return fbs.OtherScienceEnd(builder) } func encodeReportShipClass(builder *flatbuffers.Builder, shipClass *model.ShipClass) flatbuffers.UOffsetT { name := builder.CreateString(shipClass.Name) fbs.ShipClassStart(builder) fbs.ShipClassAddName(builder, name) fbs.ShipClassAddDrive(builder, reportFloatToFBS(shipClass.Drive)) fbs.ShipClassAddArmament(builder, uint64(shipClass.Armament)) fbs.ShipClassAddWeapons(builder, reportFloatToFBS(shipClass.Weapons)) fbs.ShipClassAddShields(builder, reportFloatToFBS(shipClass.Shields)) fbs.ShipClassAddCargo(builder, reportFloatToFBS(shipClass.Cargo)) fbs.ShipClassAddMass(builder, reportFloatToFBS(shipClass.Mass)) return fbs.ShipClassEnd(builder) } func encodeReportOthersShipClass(builder *flatbuffers.Builder, shipClass *model.OthersShipClass) flatbuffers.UOffsetT { race := builder.CreateString(shipClass.Race) name := builder.CreateString(shipClass.Name) fbs.OthersShipClassStart(builder) fbs.OthersShipClassAddRace(builder, race) fbs.OthersShipClassAddName(builder, name) fbs.OthersShipClassAddDrive(builder, reportFloatToFBS(shipClass.Drive)) fbs.OthersShipClassAddArmament(builder, uint64(shipClass.Armament)) fbs.OthersShipClassAddWeapons(builder, reportFloatToFBS(shipClass.Weapons)) fbs.OthersShipClassAddShields(builder, reportFloatToFBS(shipClass.Shields)) fbs.OthersShipClassAddCargo(builder, reportFloatToFBS(shipClass.Cargo)) fbs.OthersShipClassAddMass(builder, reportFloatToFBS(shipClass.Mass)) return fbs.OthersShipClassEnd(builder) } func encodeReportBombing(builder *flatbuffers.Builder, bombing *model.Bombing) flatbuffers.UOffsetT { planet := builder.CreateString(bombing.Planet) owner := builder.CreateString(bombing.Owner) attacker := builder.CreateString(bombing.Attacker) production := builder.CreateString(bombing.Production) fbs.BombingStart(builder) fbs.BombingAddNumber(builder, uint64(bombing.Number)) fbs.BombingAddPlanet(builder, planet) fbs.BombingAddOwner(builder, owner) fbs.BombingAddAttacker(builder, attacker) fbs.BombingAddProduction(builder, production) fbs.BombingAddIndustry(builder, reportFloatToFBS(bombing.Industry)) fbs.BombingAddPopulation(builder, reportFloatToFBS(bombing.Population)) fbs.BombingAddColonists(builder, reportFloatToFBS(bombing.Colonists)) fbs.BombingAddCapital(builder, reportFloatToFBS(bombing.Capital)) fbs.BombingAddMaterial(builder, reportFloatToFBS(bombing.Material)) fbs.BombingAddAttackPower(builder, reportFloatToFBS(bombing.AttackPower)) fbs.BombingAddWiped(builder, bombing.Wiped) return fbs.BombingEnd(builder) } func encodeReportIncomingGroup(builder *flatbuffers.Builder, group *model.IncomingGroup) flatbuffers.UOffsetT { fbs.IncomingGroupStart(builder) fbs.IncomingGroupAddOrigin(builder, uint64(group.Origin)) fbs.IncomingGroupAddDestination(builder, uint64(group.Destination)) fbs.IncomingGroupAddDistance(builder, reportFloatToFBS(group.Distance)) fbs.IncomingGroupAddSpeed(builder, reportFloatToFBS(group.Speed)) fbs.IncomingGroupAddMass(builder, reportFloatToFBS(group.Mass)) return fbs.IncomingGroupEnd(builder) } func encodeReportLocalPlanet(builder *flatbuffers.Builder, planet *model.LocalPlanet) flatbuffers.UOffsetT { name := builder.CreateString(planet.Name) production := builder.CreateString(planet.Production) fbs.LocalPlanetStart(builder) fbs.LocalPlanetAddX(builder, reportFloatToFBS(planet.X)) fbs.LocalPlanetAddY(builder, reportFloatToFBS(planet.Y)) fbs.LocalPlanetAddNumber(builder, uint64(planet.Number)) fbs.LocalPlanetAddSize(builder, reportFloatToFBS(planet.Size)) fbs.LocalPlanetAddName(builder, name) fbs.LocalPlanetAddResources(builder, reportFloatToFBS(planet.Resources)) fbs.LocalPlanetAddCapital(builder, reportFloatToFBS(planet.Capital)) fbs.LocalPlanetAddMaterial(builder, reportFloatToFBS(planet.Material)) fbs.LocalPlanetAddIndustry(builder, reportFloatToFBS(planet.Industry)) fbs.LocalPlanetAddPopulation(builder, reportFloatToFBS(planet.Population)) fbs.LocalPlanetAddColonists(builder, reportFloatToFBS(planet.Colonists)) fbs.LocalPlanetAddProduction(builder, production) fbs.LocalPlanetAddFreeIndustry(builder, reportFloatToFBS(planet.FreeIndustry)) return fbs.LocalPlanetEnd(builder) } func encodeReportShipProduction(builder *flatbuffers.Builder, production *model.ShipProduction) flatbuffers.UOffsetT { class := builder.CreateString(production.Class) fbs.ShipProductionStart(builder) fbs.ShipProductionAddPlanet(builder, uint64(production.Planet)) fbs.ShipProductionAddClass(builder, class) fbs.ShipProductionAddCost(builder, reportFloatToFBS(production.Cost)) fbs.ShipProductionAddProdUsed(builder, reportFloatToFBS(production.ProdUsed)) fbs.ShipProductionAddPercent(builder, reportFloatToFBS(production.Percent)) fbs.ShipProductionAddFree(builder, reportFloatToFBS(production.Free)) return fbs.ShipProductionEnd(builder) } func encodeReportRoute(builder *flatbuffers.Builder, route *model.Route) flatbuffers.UOffsetT { routeEntries := encodeReportRouteEntryVector(builder, route.Route) fbs.RouteStart(builder) fbs.RouteAddPlanet(builder, uint64(route.Planet)) if routeEntries != 0 { fbs.RouteAddRoute(builder, routeEntries) } return fbs.RouteEnd(builder) } func encodeReportOtherPlanet(builder *flatbuffers.Builder, planet *model.OtherPlanet) flatbuffers.UOffsetT { owner := builder.CreateString(planet.Owner) name := builder.CreateString(planet.Name) production := builder.CreateString(planet.Production) fbs.OtherPlanetStart(builder) fbs.OtherPlanetAddOwner(builder, owner) fbs.OtherPlanetAddX(builder, reportFloatToFBS(planet.X)) fbs.OtherPlanetAddY(builder, reportFloatToFBS(planet.Y)) fbs.OtherPlanetAddNumber(builder, uint64(planet.Number)) fbs.OtherPlanetAddSize(builder, reportFloatToFBS(planet.Size)) fbs.OtherPlanetAddName(builder, name) fbs.OtherPlanetAddResources(builder, reportFloatToFBS(planet.Resources)) fbs.OtherPlanetAddCapital(builder, reportFloatToFBS(planet.Capital)) fbs.OtherPlanetAddMaterial(builder, reportFloatToFBS(planet.Material)) fbs.OtherPlanetAddIndustry(builder, reportFloatToFBS(planet.Industry)) fbs.OtherPlanetAddPopulation(builder, reportFloatToFBS(planet.Population)) fbs.OtherPlanetAddColonists(builder, reportFloatToFBS(planet.Colonists)) fbs.OtherPlanetAddProduction(builder, production) fbs.OtherPlanetAddFreeIndustry(builder, reportFloatToFBS(planet.FreeIndustry)) return fbs.OtherPlanetEnd(builder) } func encodeReportUninhabitedPlanet(builder *flatbuffers.Builder, planet *model.UninhabitedPlanet) flatbuffers.UOffsetT { name := builder.CreateString(planet.Name) fbs.UninhabitedPlanetStart(builder) fbs.UninhabitedPlanetAddX(builder, reportFloatToFBS(planet.X)) fbs.UninhabitedPlanetAddY(builder, reportFloatToFBS(planet.Y)) fbs.UninhabitedPlanetAddNumber(builder, uint64(planet.Number)) fbs.UninhabitedPlanetAddSize(builder, reportFloatToFBS(planet.Size)) fbs.UninhabitedPlanetAddName(builder, name) fbs.UninhabitedPlanetAddResources(builder, reportFloatToFBS(planet.Resources)) fbs.UninhabitedPlanetAddCapital(builder, reportFloatToFBS(planet.Capital)) fbs.UninhabitedPlanetAddMaterial(builder, reportFloatToFBS(planet.Material)) return fbs.UninhabitedPlanetEnd(builder) } func encodeReportUnidentifiedPlanet(builder *flatbuffers.Builder, planet *model.UnidentifiedPlanet) flatbuffers.UOffsetT { fbs.UnidentifiedPlanetStart(builder) fbs.UnidentifiedPlanetAddX(builder, reportFloatToFBS(planet.X)) fbs.UnidentifiedPlanetAddY(builder, reportFloatToFBS(planet.Y)) fbs.UnidentifiedPlanetAddNumber(builder, uint64(planet.Number)) return fbs.UnidentifiedPlanetEnd(builder) } func encodeReportLocalFleet(builder *flatbuffers.Builder, fleet *model.LocalFleet) flatbuffers.UOffsetT { name := builder.CreateString(fleet.Name) state := builder.CreateString(fleet.State) fbs.LocalFleetStart(builder) fbs.LocalFleetAddName(builder, name) fbs.LocalFleetAddGroups(builder, uint64(fleet.Groups)) fbs.LocalFleetAddDestination(builder, uint64(fleet.Destination)) if fleet.Origin != nil { fbs.LocalFleetAddOrigin(builder, uint64(*fleet.Origin)) } if fleet.Range != nil { fbs.LocalFleetAddRange(builder, reportFloatToFBS(*fleet.Range)) } fbs.LocalFleetAddSpeed(builder, reportFloatToFBS(fleet.Speed)) fbs.LocalFleetAddState(builder, state) return fbs.LocalFleetEnd(builder) } func encodeReportLocalGroup(builder *flatbuffers.Builder, group *model.LocalGroup) flatbuffers.UOffsetT { class := builder.CreateString(group.Class) cargo := builder.CreateString(group.Cargo) state := builder.CreateString(group.State) tech := encodeReportTechEntryVector(builder, group.Tech) var fleet flatbuffers.UOffsetT if group.Fleet != nil { fleet = builder.CreateString(*group.Fleet) } idHi, idLo := uuidToHiLo(group.ID) fbs.LocalGroupStart(builder) fbs.LocalGroupAddNumber(builder, uint64(group.Number)) fbs.LocalGroupAddClass(builder, class) if tech != 0 { fbs.LocalGroupAddTech(builder, tech) } fbs.LocalGroupAddCargo(builder, cargo) fbs.LocalGroupAddLoad(builder, reportFloatToFBS(group.Load)) fbs.LocalGroupAddDestination(builder, uint64(group.Destination)) if group.Origin != nil { fbs.LocalGroupAddOrigin(builder, uint64(*group.Origin)) } if group.Range != nil { fbs.LocalGroupAddRange(builder, reportFloatToFBS(*group.Range)) } fbs.LocalGroupAddSpeed(builder, reportFloatToFBS(group.Speed)) fbs.LocalGroupAddMass(builder, reportFloatToFBS(group.Mass)) fbs.LocalGroupAddId(builder, commonfbs.CreateUUID(builder, idHi, idLo)) fbs.LocalGroupAddState(builder, state) if group.Fleet != nil { fbs.LocalGroupAddFleet(builder, fleet) } return fbs.LocalGroupEnd(builder) } func encodeReportOtherGroup(builder *flatbuffers.Builder, group *model.OtherGroup) flatbuffers.UOffsetT { class := builder.CreateString(group.Class) cargo := builder.CreateString(group.Cargo) tech := encodeReportTechEntryVector(builder, group.Tech) fbs.OtherGroupStart(builder) fbs.OtherGroupAddNumber(builder, uint64(group.Number)) fbs.OtherGroupAddClass(builder, class) if tech != 0 { fbs.OtherGroupAddTech(builder, tech) } fbs.OtherGroupAddCargo(builder, cargo) fbs.OtherGroupAddLoad(builder, reportFloatToFBS(group.Load)) fbs.OtherGroupAddDestination(builder, uint64(group.Destination)) if group.Origin != nil { fbs.OtherGroupAddOrigin(builder, uint64(*group.Origin)) } if group.Range != nil { fbs.OtherGroupAddRange(builder, reportFloatToFBS(*group.Range)) } fbs.OtherGroupAddSpeed(builder, reportFloatToFBS(group.Speed)) fbs.OtherGroupAddMass(builder, reportFloatToFBS(group.Mass)) return fbs.OtherGroupEnd(builder) } func encodeReportUnidentifiedGroup(builder *flatbuffers.Builder, group *model.UnidentifiedGroup) flatbuffers.UOffsetT { fbs.UnidentifiedGroupStart(builder) fbs.UnidentifiedGroupAddX(builder, reportFloatToFBS(group.X)) fbs.UnidentifiedGroupAddY(builder, reportFloatToFBS(group.Y)) return fbs.UnidentifiedGroupEnd(builder) } func decodeReportPlayerVector(flatReport *fbs.Report, result *model.Report) error { length := flatReport.PlayerLength() if length == 0 { return nil } result.Player = make([]model.Player, length) item := new(fbs.Player) for i := 0; i < length; i++ { if !flatReport.Player(item, i) { return fmt.Errorf("decode report player %d: player is missing", i) } result.Player[i] = model.Player{ Name: string(item.Name()), Drive: reportFloatFromFBS(item.Drive()), Weapons: reportFloatFromFBS(item.Weapons()), Shields: reportFloatFromFBS(item.Shields()), Cargo: reportFloatFromFBS(item.Cargo()), Population: reportFloatFromFBS(item.Population()), Industry: reportFloatFromFBS(item.Industry()), Planets: item.Planets(), Relation: string(item.Relation()), Votes: reportFloatFromFBS(item.Votes()), Extinct: item.Extinct(), } } return nil } func decodeReportLocalScienceVector(flatReport *fbs.Report, result *model.Report) error { length := flatReport.LocalScienceLength() if length == 0 { return nil } result.LocalScience = make([]model.Science, length) item := new(fbs.Science) for i := 0; i < length; i++ { if !flatReport.LocalScience(item, i) { return fmt.Errorf("decode report local science %d: science is missing", i) } result.LocalScience[i] = model.Science{ Name: string(item.Name()), Drive: reportFloatFromFBS(item.Drive()), Weapons: reportFloatFromFBS(item.Weapons()), Shields: reportFloatFromFBS(item.Shields()), Cargo: reportFloatFromFBS(item.Cargo()), } } return nil } func decodeReportOtherScienceVector(flatReport *fbs.Report, result *model.Report) error { length := flatReport.OtherScienceLength() if length == 0 { return nil } result.OtherScience = make([]model.OtherScience, length) item := new(fbs.OtherScience) for i := 0; i < length; i++ { if !flatReport.OtherScience(item, i) { return fmt.Errorf("decode report other science %d: science is missing", i) } result.OtherScience[i] = model.OtherScience{ Race: string(item.Race()), Science: model.Science{ Name: string(item.Name()), Drive: reportFloatFromFBS(item.Drive()), Weapons: reportFloatFromFBS(item.Weapons()), Shields: reportFloatFromFBS(item.Shields()), Cargo: reportFloatFromFBS(item.Cargo()), }, } } return nil } func decodeReportLocalShipClassVector(flatReport *fbs.Report, result *model.Report) error { length := flatReport.LocalShipClassLength() if length == 0 { return nil } result.LocalShipClass = make([]model.ShipClass, length) item := new(fbs.ShipClass) for i := 0; i < length; i++ { if !flatReport.LocalShipClass(item, i) { return fmt.Errorf("decode report local ship class %d: ship class is missing", i) } armament, err := uint64ToUint(item.Armament(), "armament") if err != nil { return fmt.Errorf("decode report local ship class %d: %w", i, err) } result.LocalShipClass[i] = model.ShipClass{ Name: string(item.Name()), Drive: reportFloatFromFBS(item.Drive()), Armament: armament, Weapons: reportFloatFromFBS(item.Weapons()), Shields: reportFloatFromFBS(item.Shields()), Cargo: reportFloatFromFBS(item.Cargo()), Mass: reportFloatFromFBS(item.Mass()), } } return nil } func decodeReportOtherShipClassVector(flatReport *fbs.Report, result *model.Report) error { length := flatReport.OtherShipClassLength() if length == 0 { return nil } result.OtherShipClass = make([]model.OthersShipClass, length) item := new(fbs.OthersShipClass) for i := 0; i < length; i++ { if !flatReport.OtherShipClass(item, i) { return fmt.Errorf("decode report other ship class %d: ship class is missing", i) } armament, err := uint64ToUint(item.Armament(), "armament") if err != nil { return fmt.Errorf("decode report other ship class %d: %w", i, err) } result.OtherShipClass[i] = model.OthersShipClass{ Race: string(item.Race()), ShipClass: model.ShipClass{ Name: string(item.Name()), Drive: reportFloatFromFBS(item.Drive()), Armament: armament, Weapons: reportFloatFromFBS(item.Weapons()), Shields: reportFloatFromFBS(item.Shields()), Cargo: reportFloatFromFBS(item.Cargo()), Mass: reportFloatFromFBS(item.Mass()), }, } } return nil } func decodeReportBattleVector(flatReport *fbs.Report, result *model.Report) error { length := flatReport.BattleLength() if length == 0 { return nil } result.Battle = make([]uuid.UUID, length) item := new(commonfbs.UUID) for i := 0; i < length; i++ { if !flatReport.Battle(item, i) { return fmt.Errorf("decode report battle %d: battle id is missing", i) } result.Battle[i] = uuidFromHiLo(item.Hi(), item.Lo()) } return nil } func decodeReportBombingVector(flatReport *fbs.Report, result *model.Report) error { length := flatReport.BombingLength() if length == 0 { return nil } result.Bombing = make([]*model.Bombing, length) item := new(fbs.Bombing) for i := 0; i < length; i++ { if !flatReport.Bombing(item, i) { return fmt.Errorf("decode report bombing %d: bombing is missing", i) } number, err := uint64ToUint(item.Number(), "number") if err != nil { return fmt.Errorf("decode report bombing %d: %w", i, err) } result.Bombing[i] = &model.Bombing{ Number: number, Planet: string(item.Planet()), Owner: string(item.Owner()), Attacker: string(item.Attacker()), Production: string(item.Production()), Industry: reportFloatFromFBS(item.Industry()), Population: reportFloatFromFBS(item.Population()), Colonists: reportFloatFromFBS(item.Colonists()), Capital: reportFloatFromFBS(item.Capital()), Material: reportFloatFromFBS(item.Material()), AttackPower: reportFloatFromFBS(item.AttackPower()), Wiped: item.Wiped(), } } return nil } func decodeReportIncomingGroupVector(flatReport *fbs.Report, result *model.Report) error { length := flatReport.IncomingGroupLength() if length == 0 { return nil } result.IncomingGroup = make([]model.IncomingGroup, length) item := new(fbs.IncomingGroup) for i := 0; i < length; i++ { if !flatReport.IncomingGroup(item, i) { return fmt.Errorf("decode report incoming group %d: group is missing", i) } origin, err := uint64ToUint(item.Origin(), "origin") if err != nil { return fmt.Errorf("decode report incoming group %d: %w", i, err) } destination, err := uint64ToUint(item.Destination(), "destination") if err != nil { return fmt.Errorf("decode report incoming group %d: %w", i, err) } result.IncomingGroup[i] = model.IncomingGroup{ Origin: origin, Destination: destination, Distance: reportFloatFromFBS(item.Distance()), Speed: reportFloatFromFBS(item.Speed()), Mass: reportFloatFromFBS(item.Mass()), } } return nil } func decodeReportLocalPlanetVector(flatReport *fbs.Report, result *model.Report) error { length := flatReport.LocalPlanetLength() if length == 0 { return nil } result.LocalPlanet = make([]model.LocalPlanet, length) item := new(fbs.LocalPlanet) for i := 0; i < length; i++ { if !flatReport.LocalPlanet(item, i) { return fmt.Errorf("decode report local planet %d: planet is missing", i) } number, err := uint64ToUint(item.Number(), "number") if err != nil { return fmt.Errorf("decode report local planet %d: %w", i, err) } result.LocalPlanet[i] = model.LocalPlanet{ UninhabitedPlanet: model.UninhabitedPlanet{ UnidentifiedPlanet: model.UnidentifiedPlanet{ X: reportFloatFromFBS(item.X()), Y: reportFloatFromFBS(item.Y()), Number: number, }, Size: reportFloatFromFBS(item.Size()), Name: string(item.Name()), Resources: reportFloatFromFBS(item.Resources()), Capital: reportFloatFromFBS(item.Capital()), Material: reportFloatFromFBS(item.Material()), }, Industry: reportFloatFromFBS(item.Industry()), Population: reportFloatFromFBS(item.Population()), Colonists: reportFloatFromFBS(item.Colonists()), Production: string(item.Production()), FreeIndustry: reportFloatFromFBS(item.FreeIndustry()), } } return nil } func decodeReportShipProductionVector(flatReport *fbs.Report, result *model.Report) error { length := flatReport.ShipProductionLength() if length == 0 { return nil } result.ShipProduction = make([]model.ShipProduction, length) item := new(fbs.ShipProduction) for i := 0; i < length; i++ { if !flatReport.ShipProduction(item, i) { return fmt.Errorf("decode report ship production %d: production is missing", i) } planet, err := uint64ToUint(item.Planet(), "planet") if err != nil { return fmt.Errorf("decode report ship production %d: %w", i, err) } result.ShipProduction[i] = model.ShipProduction{ Planet: planet, Class: string(item.Class()), Cost: reportFloatFromFBS(item.Cost()), ProdUsed: reportFloatFromFBS(item.ProdUsed()), Percent: reportFloatFromFBS(item.Percent()), Free: reportFloatFromFBS(item.Free()), } } return nil } func decodeReportRouteVector(flatReport *fbs.Report, result *model.Report) error { length := flatReport.RouteLength() if length == 0 { return nil } result.Route = make([]model.Route, length) item := new(fbs.Route) for i := 0; i < length; i++ { if !flatReport.Route(item, i) { return fmt.Errorf("decode report route %d: route is missing", i) } planet, err := uint64ToUint(item.Planet(), "planet") if err != nil { return fmt.Errorf("decode report route %d: %w", i, err) } routeMap, err := decodeReportRouteMap(item, i) if err != nil { return err } result.Route[i] = model.Route{ Planet: planet, Route: routeMap, } } return nil } func decodeReportOtherPlanetVector(flatReport *fbs.Report, result *model.Report) error { length := flatReport.OtherPlanetLength() if length == 0 { return nil } result.OtherPlanet = make([]model.OtherPlanet, length) item := new(fbs.OtherPlanet) for i := 0; i < length; i++ { if !flatReport.OtherPlanet(item, i) { return fmt.Errorf("decode report other planet %d: planet is missing", i) } number, err := uint64ToUint(item.Number(), "number") if err != nil { return fmt.Errorf("decode report other planet %d: %w", i, err) } result.OtherPlanet[i] = model.OtherPlanet{ Owner: string(item.Owner()), LocalPlanet: model.LocalPlanet{ UninhabitedPlanet: model.UninhabitedPlanet{ UnidentifiedPlanet: model.UnidentifiedPlanet{ X: reportFloatFromFBS(item.X()), Y: reportFloatFromFBS(item.Y()), Number: number, }, Size: reportFloatFromFBS(item.Size()), Name: string(item.Name()), Resources: reportFloatFromFBS(item.Resources()), Capital: reportFloatFromFBS(item.Capital()), Material: reportFloatFromFBS(item.Material()), }, Industry: reportFloatFromFBS(item.Industry()), Population: reportFloatFromFBS(item.Population()), Colonists: reportFloatFromFBS(item.Colonists()), Production: string(item.Production()), FreeIndustry: reportFloatFromFBS(item.FreeIndustry()), }, } } return nil } func decodeReportUninhabitedPlanetVector(flatReport *fbs.Report, result *model.Report) error { length := flatReport.UninhabitedPlanetLength() if length == 0 { return nil } result.UninhabitedPlanet = make([]model.UninhabitedPlanet, length) item := new(fbs.UninhabitedPlanet) for i := 0; i < length; i++ { if !flatReport.UninhabitedPlanet(item, i) { return fmt.Errorf("decode report uninhabited planet %d: planet is missing", i) } number, err := uint64ToUint(item.Number(), "number") if err != nil { return fmt.Errorf("decode report uninhabited planet %d: %w", i, err) } result.UninhabitedPlanet[i] = model.UninhabitedPlanet{ UnidentifiedPlanet: model.UnidentifiedPlanet{ X: reportFloatFromFBS(item.X()), Y: reportFloatFromFBS(item.Y()), Number: number, }, Size: reportFloatFromFBS(item.Size()), Name: string(item.Name()), Resources: reportFloatFromFBS(item.Resources()), Capital: reportFloatFromFBS(item.Capital()), Material: reportFloatFromFBS(item.Material()), } } return nil } func decodeReportUnidentifiedPlanetVector(flatReport *fbs.Report, result *model.Report) error { length := flatReport.UnidentifiedPlanetLength() if length == 0 { return nil } result.UnidentifiedPlanet = make([]model.UnidentifiedPlanet, length) item := new(fbs.UnidentifiedPlanet) for i := 0; i < length; i++ { if !flatReport.UnidentifiedPlanet(item, i) { return fmt.Errorf("decode report unidentified planet %d: planet is missing", i) } number, err := uint64ToUint(item.Number(), "number") if err != nil { return fmt.Errorf("decode report unidentified planet %d: %w", i, err) } result.UnidentifiedPlanet[i] = model.UnidentifiedPlanet{ X: reportFloatFromFBS(item.X()), Y: reportFloatFromFBS(item.Y()), Number: number, } } return nil } func decodeReportLocalFleetVector(flatReport *fbs.Report, result *model.Report) error { length := flatReport.LocalFleetLength() if length == 0 { return nil } result.LocalFleet = make([]model.LocalFleet, length) item := new(fbs.LocalFleet) for i := 0; i < length; i++ { if !flatReport.LocalFleet(item, i) { return fmt.Errorf("decode report local fleet %d: fleet is missing", i) } groups, err := uint64ToUint(item.Groups(), "groups") if err != nil { return fmt.Errorf("decode report local fleet %d: %w", i, err) } destination, err := uint64ToUint(item.Destination(), "destination") if err != nil { return fmt.Errorf("decode report local fleet %d: %w", i, err) } decoded := model.LocalFleet{ Name: string(item.Name()), Groups: groups, Destination: destination, Speed: reportFloatFromFBS(item.Speed()), State: string(item.State()), } if origin := item.Origin(); origin != nil { decodedOrigin, err := uint64ToUint(*origin, "origin") if err != nil { return fmt.Errorf("decode report local fleet %d: %w", i, err) } decoded.Origin = &decodedOrigin } if range_ := item.Range(); range_ != nil { decodedRange := reportFloatFromFBS(*range_) decoded.Range = &decodedRange } result.LocalFleet[i] = decoded } return nil } func decodeReportLocalGroupVector(flatReport *fbs.Report, result *model.Report) error { length := flatReport.LocalGroupLength() if length == 0 { return nil } result.LocalGroup = make([]model.LocalGroup, length) item := new(fbs.LocalGroup) for i := 0; i < length; i++ { if !flatReport.LocalGroup(item, i) { return fmt.Errorf("decode report local group %d: group is missing", i) } number, err := uint64ToUint(item.Number(), "number") if err != nil { return fmt.Errorf("decode report local group %d: %w", i, err) } destination, err := uint64ToUint(item.Destination(), "destination") if err != nil { return fmt.Errorf("decode report local group %d: %w", i, err) } tech, err := decodeReportTechMapFromLocalGroup(item, i) if err != nil { return err } id := item.Id(nil) if id == nil { return fmt.Errorf("decode report local group %d: id is missing", i) } decoded := model.LocalGroup{ OtherGroup: model.OtherGroup{ Number: number, Class: string(item.Class()), Tech: tech, Cargo: string(item.Cargo()), Load: reportFloatFromFBS(item.Load()), Destination: destination, Speed: reportFloatFromFBS(item.Speed()), Mass: reportFloatFromFBS(item.Mass()), }, ID: uuidFromHiLo(id.Hi(), id.Lo()), State: string(item.State()), } if origin := item.Origin(); origin != nil { decodedOrigin, err := uint64ToUint(*origin, "origin") if err != nil { return fmt.Errorf("decode report local group %d: %w", i, err) } decoded.Origin = &decodedOrigin } if range_ := item.Range(); range_ != nil { decodedRange := reportFloatFromFBS(*range_) decoded.Range = &decodedRange } if fleet := item.Fleet(); fleet != nil { decodedFleet := string(fleet) decoded.Fleet = &decodedFleet } result.LocalGroup[i] = decoded } return nil } func decodeReportOtherGroupVector(flatReport *fbs.Report, result *model.Report) error { length := flatReport.OtherGroupLength() if length == 0 { return nil } result.OtherGroup = make([]model.OtherGroup, length) item := new(fbs.OtherGroup) for i := 0; i < length; i++ { if !flatReport.OtherGroup(item, i) { return fmt.Errorf("decode report other group %d: group is missing", i) } number, err := uint64ToUint(item.Number(), "number") if err != nil { return fmt.Errorf("decode report other group %d: %w", i, err) } destination, err := uint64ToUint(item.Destination(), "destination") if err != nil { return fmt.Errorf("decode report other group %d: %w", i, err) } tech, err := decodeReportTechMapFromOtherGroup(item, i) if err != nil { return err } decoded := model.OtherGroup{ Number: number, Class: string(item.Class()), Tech: tech, Cargo: string(item.Cargo()), Load: reportFloatFromFBS(item.Load()), Destination: destination, Speed: reportFloatFromFBS(item.Speed()), Mass: reportFloatFromFBS(item.Mass()), } if origin := item.Origin(); origin != nil { decodedOrigin, err := uint64ToUint(*origin, "origin") if err != nil { return fmt.Errorf("decode report other group %d: %w", i, err) } decoded.Origin = &decodedOrigin } if range_ := item.Range(); range_ != nil { decodedRange := reportFloatFromFBS(*range_) decoded.Range = &decodedRange } result.OtherGroup[i] = decoded } return nil } func decodeReportUnidentifiedGroupVector(flatReport *fbs.Report, result *model.Report) error { length := flatReport.UnidentifiedGroupLength() if length == 0 { return nil } result.UnidentifiedGroup = make([]model.UnidentifiedGroup, length) item := new(fbs.UnidentifiedGroup) for i := 0; i < length; i++ { if !flatReport.UnidentifiedGroup(item, i) { return fmt.Errorf("decode report unidentified group %d: group is missing", i) } result.UnidentifiedGroup[i] = model.UnidentifiedGroup{ X: reportFloatFromFBS(item.X()), Y: reportFloatFromFBS(item.Y()), } } return nil } func decodeReportRouteMap(flatRoute *fbs.Route, routeIndex int) (map[uint]string, error) { length := flatRoute.RouteLength() if length == 0 { return nil, nil } result := make(map[uint]string, length) item := new(fbs.RouteEntry) for i := 0; i < length; i++ { if !flatRoute.Route(item, i) { return nil, fmt.Errorf("decode report route %d entry %d: route entry is missing", routeIndex, i) } key, err := uint64ToUint(item.Key(), "route key") if err != nil { return nil, fmt.Errorf("decode report route %d entry %d: %w", routeIndex, i, err) } result[key] = string(item.Value()) } return result, nil } func decodeReportTechMapFromOtherGroup(group *fbs.OtherGroup, groupIndex int) (map[string]model.Float, error) { length := group.TechLength() if length == 0 { return nil, nil } result := make(map[string]model.Float, length) item := new(fbs.TechEntry) for i := 0; i < length; i++ { if !group.Tech(item, i) { return nil, fmt.Errorf("decode report other group %d tech entry %d: tech entry is missing", groupIndex, i) } result[string(item.Key())] = reportFloatFromFBS(item.Value()) } return result, nil } func decodeReportTechMapFromLocalGroup(group *fbs.LocalGroup, groupIndex int) (map[string]model.Float, error) { length := group.TechLength() if length == 0 { return nil, nil } result := make(map[string]model.Float, length) item := new(fbs.TechEntry) for i := 0; i < length; i++ { if !group.Tech(item, i) { return nil, fmt.Errorf("decode report local group %d tech entry %d: tech entry is missing", groupIndex, i) } result[string(item.Key())] = reportFloatFromFBS(item.Value()) } return result, nil } func encodeReportOffsetVector( builder *flatbuffers.Builder, length int, startVector func(*flatbuffers.Builder, int) flatbuffers.UOffsetT, offsets []flatbuffers.UOffsetT, ) flatbuffers.UOffsetT { if length == 0 { return 0 } startVector(builder, length) for i := length - 1; i >= 0; i-- { builder.PrependUOffsetT(offsets[i]) } return builder.EndVector(length) } func encodeReportUUIDVector(builder *flatbuffers.Builder, ids []uuid.UUID) flatbuffers.UOffsetT { if len(ids) == 0 { return 0 } fbs.ReportStartBattleVector(builder, len(ids)) for i := len(ids) - 1; i >= 0; i-- { hi, lo := uuidToHiLo(ids[i]) commonfbs.CreateUUID(builder, hi, lo) } return builder.EndVector(len(ids)) } func encodeReportRouteEntryVector(builder *flatbuffers.Builder, route map[uint]string) flatbuffers.UOffsetT { if len(route) == 0 { return 0 } keys := make([]uint, 0, len(route)) for key := range route { keys = append(keys, key) } sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] }) offsets := make([]flatbuffers.UOffsetT, len(keys)) for i, key := range keys { value := builder.CreateString(route[key]) fbs.RouteEntryStart(builder) fbs.RouteEntryAddKey(builder, uint64(key)) fbs.RouteEntryAddValue(builder, value) offsets[i] = fbs.RouteEntryEnd(builder) } fbs.RouteStartRouteVector(builder, len(offsets)) for i := len(offsets) - 1; i >= 0; i-- { builder.PrependUOffsetT(offsets[i]) } return builder.EndVector(len(offsets)) } func encodeReportTechEntryVector(builder *flatbuffers.Builder, tech map[string]model.Float) flatbuffers.UOffsetT { if len(tech) == 0 { return 0 } keys := make([]string, 0, len(tech)) for key := range tech { keys = append(keys, key) } sort.Strings(keys) offsets := make([]flatbuffers.UOffsetT, len(keys)) for i, key := range keys { encodedKey := builder.CreateString(key) fbs.TechEntryStart(builder) fbs.TechEntryAddKey(builder, encodedKey) fbs.TechEntryAddValue(builder, reportFloatToFBS(tech[key])) offsets[i] = fbs.TechEntryEnd(builder) } fbs.OtherGroupStartTechVector(builder, len(offsets)) for i := len(offsets) - 1; i >= 0; i-- { builder.PrependUOffsetT(offsets[i]) } return builder.EndVector(len(offsets)) } func reportFloatToFBS(value model.Float) float32 { return float32(value.F()) } func reportFloatFromFBS(value float32) model.Float { return model.Float(float64(value)) } func uint64ToUint(value uint64, field string) (uint, error) { maxUint := uint64(^uint(0)) if value > maxUint { return 0, fmt.Errorf("%s value %d overflows uint", field, value) } return uint(value), nil } // GameReportRequestToPayload converts model.GameReportRequest to // FlatBuffers bytes suitable for the authenticated gateway transport. func GameReportRequestToPayload(req *model.GameReportRequest) ([]byte, error) { if req == nil { return nil, errors.New("encode game report request payload: request is nil") } builder := flatbuffers.NewBuilder(64) fbs.GameReportRequestStart(builder) hi, lo := uuidToHiLo(req.GameID) fbs.GameReportRequestAddGameId(builder, commonfbs.CreateUUID(builder, hi, lo)) fbs.GameReportRequestAddTurn(builder, uint32(req.Turn)) offset := fbs.GameReportRequestEnd(builder) fbs.FinishGameReportRequestBuffer(builder, offset) return builder.FinishedBytes(), nil } // PayloadToGameReportRequest converts FlatBuffers payload bytes into // model.GameReportRequest. func PayloadToGameReportRequest(data []byte) (result *model.GameReportRequest, err error) { if len(data) == 0 { return nil, errors.New("decode game report request payload: data is empty") } defer func() { if recovered := recover(); recovered != nil { result = nil err = fmt.Errorf("decode game report request payload: panic recovered: %v", recovered) } }() req := fbs.GetRootAsGameReportRequest(data, 0) gameID := req.GameId(nil) if gameID == nil { return nil, errors.New("decode game report request payload: game_id is missing") } return &model.GameReportRequest{ GameID: uuidFromHiLo(gameID.Hi(), gameID.Lo()), Turn: uint(req.Turn()), }, nil }