216 lines
6.8 KiB
Go
216 lines
6.8 KiB
Go
package gameserver
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"fmt"
|
|
"gac-hkrpg-go/internal/common"
|
|
pb "gac-hkrpg-go/protocol/proto"
|
|
"google.golang.org/protobuf/proto"
|
|
"math/big"
|
|
"sort"
|
|
)
|
|
|
|
func cwSpotlights(run *pb.MPIMAEMCEFH) *pb.GNOBLNOCEHA {
|
|
for _, c := range run.NAGDJGMMCBE {
|
|
if v := c.GetLEGMAJLHBLA(); v != nil {
|
|
return v
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func cwEnsureSpotlights(run *pb.MPIMAEMCEFH) bool {
|
|
if cwSpotlights(run) != nil {
|
|
return false
|
|
}
|
|
run.NAGDJGMMCBE = append(run.NAGDJGMMCBE, &pb.GridFightGameInfo{LEOIAOFBBFA: &pb.GridFightGameInfo_LEGMAJLHBLA{LEGMAJLHBLA: &pb.GNOBLNOCEHA{}}})
|
|
return true
|
|
}
|
|
|
|
func cwSpotlightChoices(c *common.CurrencyWarsCatalog, run *pb.MPIMAEMCEFH) (*pb.BDLBHJGNCFM, error) {
|
|
rows, policy, e := c.SpotlightData()
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
_, team, _ := cwComponents(run)
|
|
if team == nil {
|
|
return nil, fmt.Errorf("CW Spotlight team missing")
|
|
}
|
|
owned := map[uint32]bool{}
|
|
for _, r := range team.GridGameRoleList {
|
|
owned[r.Id] = true
|
|
}
|
|
used := map[uint32]bool{}
|
|
if v := cwSpotlights(run); v != nil {
|
|
for _, r := range v.FNNEPMAPOOF {
|
|
used[r.RoleId] = true
|
|
}
|
|
}
|
|
var ids, safeIDs []uint32
|
|
for id, row := range rows {
|
|
if run.IsOverlock && !row.IsOCEffective {
|
|
continue
|
|
}
|
|
compatible, hasUsedRole := false, false
|
|
for _, role := range row.SuitableRoles {
|
|
if owned[role] && !used[role] {
|
|
compatible = true
|
|
}
|
|
if owned[role] && used[role] {
|
|
hasUsedRole = true
|
|
}
|
|
}
|
|
if compatible {
|
|
ids = append(ids, id)
|
|
if !hasUsedRole {
|
|
safeIDs = append(safeIDs, id)
|
|
}
|
|
}
|
|
}
|
|
// The Client can offer every compatible owned role, including one with an
|
|
// existing binding. Prefer options without such roles when available so a
|
|
// valid-looking selection cannot be rejected as a duplicate. This is a
|
|
// server offer policy, not a claim about the original selection algorithm.
|
|
if len(safeIDs) > 0 {
|
|
ids = safeIDs
|
|
}
|
|
sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
|
|
if len(ids) == 0 {
|
|
return nil, fmt.Errorf("CW no compatible Spotlight choices for owned roles")
|
|
}
|
|
// Explicit resource policy, not an invented claim about native weights.
|
|
count := int(policy.OfferCount)
|
|
if count > len(ids) {
|
|
count = len(ids)
|
|
}
|
|
out := &pb.BDLBHJGNCFM{BKOGIFHEKHH: true, ACKOKJBBPKH: true, DOAHJPPPMGM: policy.FreeRefreshCount}
|
|
for i := 0; i < count; i++ {
|
|
n, e := rand.Int(rand.Reader, big.NewInt(int64(len(ids)-i)))
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
j := i + int(n.Int64())
|
|
ids[i], ids[j] = ids[j], ids[i]
|
|
out.HPKFKBKOADP = append(out.HPKFKBKOADP, &pb.FGEOPPMIHJM{IHHNLLAJAGB: ids[i]})
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func cwSelectSpotlight(c *common.CurrencyWarsCatalog, run *pb.MPIMAEMCEFH, selection *pb.NIJPHAAONFB) (*pb.GridFightSyncUpdateResultScNotify, error) {
|
|
if selection == nil || run.FMIGOKIDKJL == nil || run.FMIGOKIDKJL.GetEEKMIAMGHFL() == nil {
|
|
return nil, fmt.Errorf("CW Spotlight is not pending")
|
|
}
|
|
pending := run.FMIGOKIDKJL
|
|
offer := pending.GetEEKMIAMGHFL()
|
|
if !offer.BKOGIFHEKHH || offer.GFGJOKJGGCO != 0 {
|
|
return nil, fmt.Errorf("CW Spotlight replacement is not a grant")
|
|
}
|
|
offered := false
|
|
for _, v := range offer.HPKFKBKOADP {
|
|
offered = offered || v.IHHNLLAJAGB == selection.IHHNLLAJAGB
|
|
}
|
|
if !offered {
|
|
return nil, fmt.Errorf("CW Spotlight choice not offered")
|
|
}
|
|
rows, _, e := c.SpotlightData()
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
row, ok := rows[selection.IHHNLLAJAGB]
|
|
if !ok {
|
|
return nil, fmt.Errorf("CW Spotlight configuration missing")
|
|
}
|
|
compatible := false
|
|
for _, id := range row.SuitableRoles {
|
|
compatible = compatible || id == selection.RoleId
|
|
}
|
|
if !compatible {
|
|
return nil, fmt.Errorf("CW Spotlight role is incompatible")
|
|
}
|
|
player, team, _ := cwComponents(run)
|
|
if player == nil || team == nil {
|
|
return nil, fmt.Errorf("CW Spotlight player or team missing")
|
|
}
|
|
var owned *pb.CDEAABODKCP
|
|
for _, r := range team.GridGameRoleList {
|
|
if r.Id == selection.RoleId && (owned == nil || r.RoleStar > owned.RoleStar || r.RoleStar == owned.RoleStar && r.UniqueId < owned.UniqueId) {
|
|
owned = r
|
|
}
|
|
}
|
|
if owned == nil {
|
|
return nil, fmt.Errorf("CW Spotlight role not owned")
|
|
}
|
|
cwEnsureSpotlights(run)
|
|
state := cwSpotlights(run)
|
|
for _, v := range state.FNNEPMAPOOF {
|
|
if v.RoleId == selection.RoleId {
|
|
return nil, fmt.Errorf("CW role already has Spotlight")
|
|
}
|
|
}
|
|
uid, e := cwNextEntityID(run)
|
|
if e != nil {
|
|
return nil, e
|
|
}
|
|
// AddOrUpdate shuffles proto fields into the binding record; MDNAGANGNKK
|
|
// becomes RoleUniqueID, RoleId remains the configuration ID. Native
|
|
// GetActiveSpotLightBindingByRoleId + binding constructor corroborate it.
|
|
item := &pb.FMBACDOFCAL{UniqueId: uid, IHHNLLAJAGB: row.ID, RoleId: selection.RoleId, MDNAGANGNKK: owned.UniqueId}
|
|
state.FNNEPMAPOOF = append(state.FNNEPMAPOOF, item)
|
|
stage, node := cwStage(run)
|
|
if stage == nil || node == nil || !node.HPNNLMBHOPF {
|
|
return nil, fmt.Errorf("CW current node has no unclaimed Spotlight")
|
|
}
|
|
node.HPNNLMBHOPF = false
|
|
var routeNode *pb.BCCIJHPLJOJ
|
|
for _, v := range stage.OOMMIIKPHBC {
|
|
if v.DCAHJELNJNP == stage.DCAHJELNJNP && v.SectionId == stage.SectionId {
|
|
routeNode = v
|
|
break
|
|
}
|
|
}
|
|
if routeNode == nil {
|
|
return nil, fmt.Errorf("CW current route node missing")
|
|
}
|
|
run.FMIGOKIDKJL = nil
|
|
player.GAJGBBFELEL = &pb.GMBMNFLMKPI{}
|
|
return cwNotify(&pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_OHHBBOGFDON{OHHBBOGFDON: proto.Clone(item).(*pb.FMBACDOFCAL)}},
|
|
&pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_KBHOBGHKIBA{KBHOBGHKIBA: &pb.BAAGMMIDHDB{DBIIPEEDBHI: proto.Clone(routeNode).(*pb.BCCIJHPLJOJ)}}}, cwFinish(pending.QueuePosition), cwUnlock()), nil
|
|
}
|
|
|
|
func init() {
|
|
cwHandle("GridFightSpotLightEnter", func(s *Session, _ *pb.GridFightSpotLightEnterCsReq, rsp *pb.GridFightSpotLightEnterScRsp) error {
|
|
rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_CONF_MISS)
|
|
c, data, e := cwPreparingData(s)
|
|
if e != nil {
|
|
PacketLog("CW_SPOTLIGHT_ENTER rejected: " + e.Error())
|
|
return nil
|
|
}
|
|
run := data.DHFOFLFCLAH
|
|
_, node := cwStage(run)
|
|
if node == nil || !node.HPNNLMBHOPF {
|
|
rsp.Retcode = uint32(pb.Retcode_RET_GRID_FIGHT_PARAM_NOT_MATCH)
|
|
return nil
|
|
}
|
|
if s.currencyWarsRevision >= uint64(^uint32(0)) {
|
|
return nil
|
|
}
|
|
offer, e := cwSpotlightChoices(c, run)
|
|
if e != nil {
|
|
PacketLog("CW_SPOTLIGHT_ENTER rejected: " + e.Error())
|
|
return nil
|
|
}
|
|
cwEnsureSpotlights(run)
|
|
pending := &pb.CHCHAJIANIM{QueuePosition: uint32(s.currencyWarsRevision + 1), FGLIODAJFML: &pb.CHCHAJIANIM_EEKMIAMGHFL{EEKMIAMGHFL: offer}}
|
|
run.FMIGOKIDKJL = pending
|
|
if e = s.saveCurrencyWarsData(data, c); e != nil {
|
|
return nil
|
|
}
|
|
if e = cwSend(s, cwNotify(&pb.KFNCAIHIHCM{MNLCHEJKDHO: &pb.KFNCAIHIHCM_FMIGOKIDKJL{FMIGOKIDKJL: pending}})); e != nil {
|
|
return e
|
|
}
|
|
rsp.Retcode = 0
|
|
PacketLog(fmt.Sprintf("CW_SPOTLIGHT_ENTER offered=%d queue=%d revision=%d", len(offer.HPKFKBKOADP), pending.QueuePosition, s.currencyWarsRevision))
|
|
return nil
|
|
})
|
|
}
|