pre cw with probe and a bunch script to read game behavior
This commit is contained in:
1 parent
5120219ae2
commit
da8a32f60d
310 files changed
+219693
-872
No files matched your search
@@ -0,0 +1,218 @@
|
||||
package gameserver
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"gac-hkrpg-go/internal/common"
|
||||
goproto "gac-hkrpg-go/protocol/proto"
|
||||
"google.golang.org/protobuf/proto"
|
||||
)
|
||||
|
||||
// This advances the existing captured-entry prototype, not the final
|
||||
// procedural portal/effect processor. Each starter outcome is a complete,
|
||||
// internally consistent reference capture. No adventure inventory is touched.
|
||||
func currencyWarsEntryChoice(catalog *common.CurrencyWarsCatalog, before *goproto.MPIMAEMCEFH, queue, portal uint32) (*goproto.MPIMAEMCEFH, *goproto.GridFightSyncUpdateResultScNotify, error) {
|
||||
if catalog == nil || before == nil || before.FMIGOKIDKJL == nil || before.FMIGOKIDKJL.QueuePosition != queue {
|
||||
return nil, nil, fmt.Errorf("CW pending queue differs from the active run")
|
||||
}
|
||||
offer := before.FMIGOKIDKJL.GetMFFCPLBNHCF()
|
||||
if offer == nil {
|
||||
return nil, nil, fmt.Errorf("CW active pending action is not an initial portal selection")
|
||||
}
|
||||
found := false
|
||||
for _, id := range offer.NPHOIMEEDHM {
|
||||
found = found || id == portal
|
||||
}
|
||||
if portal == 0 || !found {
|
||||
return nil, nil, fmt.Errorf("CW portal is not in the active offer")
|
||||
}
|
||||
for _, component := range before.NAGDJGMMCBE {
|
||||
if team := component.GetEJIPINIKNCK(); team != nil && len(team.GridGameRoleList) != 0 {
|
||||
return nil, nil, fmt.Errorf("CW entry prototype cannot replace an existing team")
|
||||
}
|
||||
}
|
||||
raw, err := common.ReadResourceFile("resources/CurrencyWarsEntryTransitions.json")
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
var resource struct {
|
||||
Format uint32 `json:"format_version"`
|
||||
Client string `json:"target_client_version"`
|
||||
Assembly string `json:"target_assembly_sha256"`
|
||||
Source string `json:"source_binary_sha256"`
|
||||
Choices []struct {
|
||||
Division uint32 `json:"division_id"`
|
||||
Season uint32 `json:"season_id"`
|
||||
Overclock bool `json:"is_overclock"`
|
||||
Portal uint32 `json:"portal_id"`
|
||||
BeforeQueue uint32 `json:"before_queue"`
|
||||
AfterWire []byte `json:"after_wire"`
|
||||
NotifyWire []byte `json:"notify_wire"`
|
||||
AfterSHA string `json:"after_sha256"`
|
||||
NotifySHA string `json:"notify_sha256"`
|
||||
} `json:"choices"`
|
||||
}
|
||||
if err := json.Unmarshal(raw, &resource); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if resource.Format != 1 || resource.Client != catalog.ClientVersion || resource.Assembly != catalog.Provenance.GameAssemblySHA256 {
|
||||
return nil, nil, fmt.Errorf("CW entry transition revision differs from the active catalog")
|
||||
}
|
||||
if h, err := hex.DecodeString(resource.Source); err != nil || len(h) != sha256.Size {
|
||||
return nil, nil, fmt.Errorf("CW entry transition source identity is missing")
|
||||
}
|
||||
var after *goproto.MPIMAEMCEFH
|
||||
var notify *goproto.GridFightSyncUpdateResultScNotify
|
||||
for _, choice := range resource.Choices {
|
||||
if choice.Division != before.DivisionId || choice.Season != before.LJNGPCKPJLF || choice.Overclock != before.IsOverlock || choice.Portal != portal || choice.BeforeQueue != queue {
|
||||
continue
|
||||
}
|
||||
if after != nil {
|
||||
return nil, nil, fmt.Errorf("CW entry transition key is duplicated")
|
||||
}
|
||||
if !currencyWarsWireDigest(choice.AfterWire, choice.AfterSHA) || !currencyWarsWireDigest(choice.NotifyWire, choice.NotifySHA) {
|
||||
return nil, nil, fmt.Errorf("CW entry transition wire digest differs")
|
||||
}
|
||||
after = new(goproto.MPIMAEMCEFH)
|
||||
notify = new(goproto.GridFightSyncUpdateResultScNotify)
|
||||
if err := proto.Unmarshal(choice.AfterWire, after); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err := proto.Unmarshal(choice.NotifyWire, notify); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if after.DivisionId != before.DivisionId || after.LJNGPCKPJLF != before.LJNGPCKPJLF || after.IsOverlock != before.IsOverlock || after.FMIGOKIDKJL == nil || after.FMIGOKIDKJL.QueuePosition <= queue {
|
||||
return nil, nil, fmt.Errorf("CW captured transition identity or next queue is invalid")
|
||||
}
|
||||
selected := false
|
||||
finished := false
|
||||
for _, result := range notify.GCPNIHHBGGD {
|
||||
for _, update := range result.JKPJFKCBIEM {
|
||||
if value := update.GetHPIIGHOELBH(); value != nil && value.PortalBuffId == portal {
|
||||
selected = true
|
||||
}
|
||||
finished = finished || update.GetPMFBIGLPJJA() == queue
|
||||
}
|
||||
}
|
||||
if !selected || !finished {
|
||||
return nil, nil, fmt.Errorf("CW captured transition lacks portal or queue acknowledgement")
|
||||
}
|
||||
if err := validateCurrencyWarsEntryComponents(after, catalog); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
after.UniqueId = before.UniqueId
|
||||
}
|
||||
if after == nil {
|
||||
return nil, nil, fmt.Errorf("CW initial portal outcome is not captured for this division/mode")
|
||||
}
|
||||
// The native reference's GetData projection omits selected portals even
|
||||
// though the live sync includes them. Preserve this in our resume snapshot.
|
||||
for _, component := range after.NAGDJGMMCBE {
|
||||
if stage := component.GetKLCPAAHDPJF(); stage != nil {
|
||||
stage.GridFightPortalBuffList = append(stage.GridFightPortalBuffList, &goproto.GIDEDMOPFKF{PortalBuffId: portal})
|
||||
}
|
||||
}
|
||||
cwEnsureSpotlights(after)
|
||||
return after, notify, nil
|
||||
}
|
||||
|
||||
func currencyWarsWireDigest(wire []byte, expected string) bool {
|
||||
hash := sha256.Sum256(wire)
|
||||
return len(wire) > 0 && hex.EncodeToString(hash[:]) == expected
|
||||
}
|
||||
|
||||
func init() {
|
||||
// The revision's request name is misleading. Controlled reference replay
|
||||
// establishes this request's MFFCPLBNHCF arm as portal selection and its
|
||||
// response as GridFightHandlePendingActionScRsp. No numeric alias is used.
|
||||
handleNamed("GridFightHandleBonfireHealHp", "GridFightHandlePendingActionScRsp", func(s *Session, req *goproto.GridFightHandleBonfireHealHpCsReq, rsp *goproto.GridFightHandlePendingActionScRsp) (resultErr error) {
|
||||
rsp.QueuePosition = req.QueuePosition
|
||||
rsp.Retcode = uint32(goproto.Retcode_RET_GRID_FIGHT_CONF_MISS)
|
||||
defer func() {
|
||||
if resultErr != nil {
|
||||
PacketLog(fmt.Sprintf("CW_PENDING_ACTION failed queue=%d error=%v", req.QueuePosition, resultErr))
|
||||
rsp.Retcode = uint32(goproto.Retcode_RET_GRID_FIGHT_CONF_MISS)
|
||||
resultErr = nil // Send the typed error response so the wait block is released.
|
||||
}
|
||||
}()
|
||||
selection := req.GetMFFCPLBNHCF()
|
||||
if selection == nil && req.GetNNDGEJFLGGF() == nil && req.GetLGNPHHBGKFH() == nil && req.GetIBCHMBNIPLK() == nil && req.GetDKDFDGJGIHM() == nil {
|
||||
PacketLog(fmt.Sprintf("CW_PENDING_ACTION unsupported queue=%d action=%T", req.QueuePosition, req.FGLIODAJFML))
|
||||
return nil
|
||||
}
|
||||
catalog, err := common.LoadCurrencyWarsCatalog()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
data, revision, err := readCurrencyWarsData(catalog)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if data == nil || data.DHFOFLFCLAH == nil {
|
||||
rsp.Retcode = uint32(goproto.Retcode_RET_GRID_FIGHT_NOT_IN_GAMEPLAY)
|
||||
return nil
|
||||
}
|
||||
if revision != s.currencyWarsRevision {
|
||||
rsp.Retcode = uint32(goproto.Retcode_RET_GRID_FIGHT_PARAM_NOT_MATCH)
|
||||
return nil
|
||||
}
|
||||
before := data.DHFOFLFCLAH
|
||||
if before.FMIGOKIDKJL == nil || before.FMIGOKIDKJL.QueuePosition != req.QueuePosition {
|
||||
rsp.Retcode = uint32(goproto.Retcode_RET_GRID_FIGHT_PARAM_NOT_MATCH)
|
||||
return nil
|
||||
}
|
||||
var after *goproto.MPIMAEMCEFH
|
||||
var notify *goproto.GridFightSyncUpdateResultScNotify
|
||||
if selection != nil {
|
||||
after, notify, err = currencyWarsEntryChoice(catalog, before, req.QueuePosition, selection.JCAPCNLJGFC)
|
||||
} else {
|
||||
after = proto.Clone(before).(*goproto.MPIMAEMCEFH)
|
||||
switch {
|
||||
case req.GetDKDFDGJGIHM() != nil && before.FMIGOKIDKJL.GetEEKMIAMGHFL() != nil:
|
||||
notify, err = cwSelectSpotlight(catalog, after, req.GetDKDFDGJGIHM())
|
||||
case req.GetNNDGEJFLGGF() != nil && before.FMIGOKIDKJL.GetNNDGEJFLGGF() != nil:
|
||||
stage, _ := cwStage(after)
|
||||
entries, loadErr := cwNodeTransitions(catalog)
|
||||
isNextRound := false
|
||||
if loadErr == nil && stage != nil {
|
||||
for _, entry := range entries {
|
||||
level := new(goproto.AINFNFIDGFF)
|
||||
if proto.Unmarshal(entry.LevelWire, level) == nil && level.DCAHJELNJNP == stage.DCAHJELNJNP && level.SectionId == stage.SectionId {
|
||||
isNextRound = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if isNextRound {
|
||||
notify, err = cwNextRound(catalog, after)
|
||||
} else {
|
||||
notify, err = cwInitialRound(catalog, after)
|
||||
}
|
||||
case req.GetLGNPHHBGKFH() != nil && before.FMIGOKIDKJL.GetLGNPHHBGKFH() != nil:
|
||||
notify, err = cwReturnPreparation(after)
|
||||
case req.GetIBCHMBNIPLK() != nil && before.FMIGOKIDKJL.GetIBCHMBNIPLK() != nil:
|
||||
notify, err = cwSelectInvestment(catalog, after, req.GetIBCHMBNIPLK().AugmentId)
|
||||
default:
|
||||
err = fmt.Errorf("pending request kind differs from active queue")
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
PacketLog(fmt.Sprintf("CW_PENDING_ACTION rejected queue=%d action=%T error=%v", req.QueuePosition, req.FGLIODAJFML, err))
|
||||
return nil
|
||||
}
|
||||
data.DHFOFLFCLAH = after
|
||||
if err := s.saveCurrencyWarsData(data, catalog); err != nil {
|
||||
return err
|
||||
}
|
||||
// Save before synchronizing, then finish the request after the ordered
|
||||
// notification, matching the reference's pending-action callback flow.
|
||||
if err := s.Send(notify); err != nil {
|
||||
return err
|
||||
}
|
||||
rsp.Retcode = 0
|
||||
PacketLog(fmt.Sprintf("CW_PENDING_ACTION advanced action=%T queue=%d next_queue=%d preparing=%t revision=%d", req.FGLIODAJFML, req.QueuePosition, after.GetFMIGOKIDKJL().GetQueuePosition(), after.FMIGOKIDKJL == nil, s.currencyWarsRevision))
|
||||
return nil
|
||||
})
|
||||
}
|
||||
Reference in new issue
Block a user