package gameserver import ( "testing" "gac-hkrpg-go/internal/common" pb "gac-hkrpg-go/protocol/proto" "google.golang.org/protobuf/proto" ) func TestCurrencyWarsTraitAndSpotlightTracking(t *testing.T) { catalog, err := common.LoadCurrencyWarsCatalog() if err != nil { t.Fatalf("load catalog: %v", err) } data, rev, err := readCurrencyWarsData(catalog) if err != nil || data == nil || data.DHFOFLFCLAH == nil { t.Skip("no active run in persistent snapshot") } data.DHFOFLFCLAH.FMIGOKIDKJL = nil session := &Session{Token: 12345, currencyWarsRevision: rev} if err := session.saveCurrencyWarsData(data, catalog); err != nil { t.Fatalf("save initial data: %v", err) } // 1. Verify handler registrations traitReqCmdID, ok := pb.CmdIDByName["MCDBJHOCGHI"] if !ok || typedHandlers[uint16(traitReqCmdID)] == nil { t.Fatalf("handler MCDBJHOCGHI (CmdID %d) is not registered in typedHandlers", traitReqCmdID) } spotReqCmdID, ok := pb.CmdIDByName["GridFightUpdateSpotLightTrackCsReq"] if !ok || typedHandlers[uint16(spotReqCmdID)] == nil { t.Fatalf("handler GridFightUpdateSpotLightTrackCsReq (CmdID %d) is not registered in typedHandlers", spotReqCmdID) } // 2. Test Trait Tracking (enable trait 1001) traitReq := &pb.MCDBJHOCGHI{ TraitId: 1001, BJDOINAHDLN: true, } rawTrait, err := proto.Marshal(traitReq) if err != nil { t.Fatalf("marshal traitReq: %v", err) } err = typedHandlers[uint16(traitReqCmdID)](session, rawTrait) if err != nil { t.Fatalf("typedHandler MCDBJHOCGHI returned error: %v", err) } saved, _, err := readCurrencyWarsData(catalog) if err != nil { t.Fatalf("read saved: %v", err) } player, _, _ := cwComponents(saved.DHFOFLFCLAH) foundTrait := false for _, id := range player.ONHDKLIMLNM { if id == 1001 { foundTrait = true break } } if !foundTrait { t.Fatalf("expected trait 1001 in player.ONHDKLIMLNM, got %v", player.ONHDKLIMLNM) } // 3. Test Trait Tracking Disable (disable trait 1001) traitReq.BJDOINAHDLN = false rawTrait, _ = proto.Marshal(traitReq) err = typedHandlers[uint16(traitReqCmdID)](session, rawTrait) if err != nil { t.Fatalf("disable MCDBJHOCGHI error: %v", err) } saved, _, _ = readCurrencyWarsData(catalog) player, _, _ = cwComponents(saved.DHFOFLFCLAH) for _, id := range player.ONHDKLIMLNM { if id == 1001 { t.Fatalf("trait 1001 was not removed from player.ONHDKLIMLNM: %v", player.ONHDKLIMLNM) } } // 4. Test SpotLight Tracking (enable spotlight track for role 1001) spotReq := &pb.GridFightUpdateSpotLightTrackCsReq{ Source: pb.DFNHMOGNJGJ_DFNHMOGNJGJ_HPKIIGODDFO, ACCFJKBOJIJ: []*pb.BMHEAGKIILO{ { LDLDLHBCFCF: &pb.OGEIHMPCFKA{ RoleId: 1001, NALMBOOCCIN: 1, DKEIDONNDCJ: []*pb.NAODPHAPKOA{ { Source: pb.DFNHMOGNJGJ_DFNHMOGNJGJ_HPKIIGODDFO, JHGCJKCHNCO: []uint32{101}, }, }, }, BJDOINAHDLN: true, }, }, } rawSpot, err := proto.Marshal(spotReq) if err != nil { t.Fatalf("marshal spotReq: %v", err) } err = typedHandlers[uint16(spotReqCmdID)](session, rawSpot) if err != nil { t.Fatalf("typedHandler GridFightUpdateSpotLightTrack returned error: %v", err) } saved, _, _ = readCurrencyWarsData(catalog) spotlights := cwSpotlights(saved.DHFOFLFCLAH) if spotlights == nil || len(spotlights.DKEIDONNDCJ) == 0 { t.Fatalf("expected spotlights.DKEIDONNDCJ to have tracks, got %v", spotlights) } if spotlights.DKEIDONNDCJ[0].RoleId != 1001 { t.Fatalf("expected track for role 1001, got %d", spotlights.DKEIDONNDCJ[0].RoleId) } // 5. Test SpotLight Tracking Disable spotReq.ACCFJKBOJIJ[0].BJDOINAHDLN = false rawSpot, _ = proto.Marshal(spotReq) err = typedHandlers[uint16(spotReqCmdID)](session, rawSpot) if err != nil { t.Fatalf("disable spotlight track error: %v", err) } saved, _, _ = readCurrencyWarsData(catalog) spotlights = cwSpotlights(saved.DHFOFLFCLAH) if len(spotlights.DKEIDONNDCJ) != 0 { t.Fatalf("expected 0 spotlight tracks, got %d", len(spotlights.DKEIDONNDCJ)) } } func TestCurrencyWarsBonfireReplayIdempotency(t *testing.T) { catalog, err := common.LoadCurrencyWarsCatalog() if err != nil { t.Fatalf("load catalog: %v", err) } data, _, err := readCurrencyWarsData(catalog) if err != nil || data == nil || data.DHFOFLFCLAH == nil { t.Skip("no active run in persistent snapshot") } offer := &pb.NCEPOLNLNKH{ Phase: cwBonfireSelected, EIFICNFGGHI: 10, AHJJJNBOMPG: 2, DANCNIMJOLE: 0, } pending := &pb.CHCHAJIANIM{ QueuePosition: 795, FGLIODAJFML: &pb.CHCHAJIANIM_IIDBLCFAAPC{ IIDBLCFAAPC: offer, }, } data.DHFOFLFCLAH.FMIGOKIDKJL = pending session := &Session{Token: 12345, currencyWarsRevision: 0} _ = session.saveCurrencyWarsData(data, catalog) req := &pb.GridFightHandleBonfireHealHpCsReq{ QueuePosition: 795, FGLIODAJFML: &pb.GridFightHandleBonfireHealHpCsReq_IIDBLCFAAPC{ IIDBLCFAAPC: &pb.CAMIICJDOBO{ FFBBOFAGGDI: &pb.CAMIICJDOBO_NCGOOEMOBIC{ NCGOOEMOBIC: &pb.MLAENDOFBCK{ FFBBOFAGGDI: &pb.MLAENDOFBCK_LGAJNMNOAEM{ LGAJNMNOAEM: true, }, }, }, }, }, } cmdID, ok := pb.CmdIDByName["GridFightHandleBonfireHealHpCsReq"] if !ok || typedHandlers[uint16(cmdID)] == nil { t.Fatalf("handler GridFightHandleBonfireHealHpCsReq not registered") } raw, _ := proto.Marshal(req) err = typedHandlers[uint16(cmdID)](session, raw) if err != nil { t.Fatalf("expected idempotent response without error, got: %v", err) } }