package gameserver import ( "fmt" "google.golang.org/protobuf/proto" "gac-hkrpg-go/internal/common" goproto "gac-hkrpg-go/protocol/proto" ) // This file ports the raw special-case arms of CommandHandler::on_message and // the dummy! macro list from the Rust packet.rs / handlers/mod.rs. func init() { // ---- dummy (empty ScRsp) list ---- for _, name := range []string{ "GetLevelRewardTakenList", "QueryProductInfo", "GetQuestRecord", "GetCurAssist", "GetDailyActiveInfo", "GetFightActivityData", "GetExpeditionData", "GetLoginActivity", "GetRaidInfo", "GetTrialActivityData", "GetNpcStatus", "GetSecretKeyInfo", "GetVideoVersionKey", "FinishTalkMission", "GetRechargeGiftInfo", "GetPreAvatarGrowthInfo", "GetPreAvatarActivityList", "GetFriendAssistList", "GetAssistList", "B51RacingGetData", } { dummy(name) } // Bind requests by the current command names; do not retain numeric aliases // from earlier builds, which may now identify responses or notifications. rawNamed("GetChallengeGroupStatisticsCsReq", challengeGroupStatsReply) rawNamed("GetChallengeTierceDataCsReq", challengeTierceReply) rawNamed("StartChallengeCsReq", handleStartChallenge) rawNamed("StartChallengeTierceCsReq", handleStartChallengeTierce) rawNamed("SetChallengeTierceLineupCsReq", handleSetChallengeTierceLineup) rawNamed("LeaveChallengeCsReq", handleLeaveChallenge) rawNamed("LeaveChallengeTierceCsReq", handleLeaveChallengeTierce) rawNamed("TakeChallengeCumulativeRewardCsReq", handleTakeChallengeReward) // The request/response names remain obfuscated in the upstream schema. rawNamed("HIGLJHLNODG", handleExRankUnlock) rawNamed("GetSwitchHandDataCsReq", func(s *Session, payload []byte) error { return s.Send(&goproto.GetSwitchHandDataScRsp{}) }) handle("GetChallengeTierceController", func(s *Session, req *goproto.GetChallengeTierceControllerCsReq, rsp *goproto.GetChallengeTierceControllerScRsp) error { rsp.Retcode = 0 return nil }) handle("StartChallengePeak", handleStartChallengePeak) rawNamed("LeaveChallengePeakCsReq", func(s *Session, payload []byte) error { return leaveChallengeScene(s, &goproto.LeaveChallengePeakScRsp{}) }) } // handleExRankUnlock handles CmdID 400 (Affinitive Eidolon / Ex-Rank unlock request from client). func handleExRankUnlock(s *Session, payload []byte) error { Log(fmt.Sprintf("[EX-RANK] Received cmd 400 (len: %d) payload: %x", len(payload), payload)) var req goproto.HIGLJHLNODG if err := proto.Unmarshal(payload, &req); err != nil { return err } parsedVal := req.OJFIGABCKLG var avatarID uint32 if parsedVal >= 100000 { avatarID = parsedVal / 100 // e.g. 120401 -> 1204, 121301 -> 1213, 130801 -> 1308 } else if parsedVal >= 1000 && parsedVal < 10000 { avatarID = parsedVal } Log(fmt.Sprintf("[EX-RANK] Target avatarID: %d (from val: %d)", avatarID, parsedVal)) player := s.JSON() if player != nil { if avatarID == 0 { // Fallback: unlock for all avatars that have ExRank defined (1204, 1213, 1308) for aid := range common.AvatarExRankMap { if av, ok := player.Avatars[aid]; ok { av.ExRankUnlocked = true Log(fmt.Sprintf("[EX-RANK] Fallback set ExRankUnlocked=true for avatar %d", aid)) syncNotify := &goproto.PlayerSyncScNotify{} buildSync(player, syncNotify, []uint32{aid}, nil, nil) _ = s.Send(syncNotify) } } } else { if av, ok := player.Avatars[avatarID]; ok { av.ExRankUnlocked = true Log(fmt.Sprintf("[EX-RANK] Set ExRankUnlocked=true for avatar %d", avatarID)) } syncNotify := &goproto.PlayerSyncScNotify{} buildSync(player, syncNotify, []uint32{avatarID}, nil, nil) _ = s.Send(syncNotify) } player.SavePersistent() } // The matching response is declared by the current upstream schema. return s.Send(&goproto.LPOLGDLAADM{}) } // challengeGroupStatsReply uses the current typed statistics response. func challengeGroupStatsReply(s *Session, payload []byte) error { var req goproto.GetChallengeGroupStatisticsCsReq if err := proto.Unmarshal(payload, &req); err != nil { return err } // Match upstream: echo the group without fabricating historical scores. return s.Send(&goproto.GetChallengeGroupStatisticsScRsp{GroupId: req.GroupId}) } // challengeTierceReply sends only the response paired with the current request. func challengeTierceReply(s *Session, payload []byte) error { return s.Send(buildGetChallengeTierceDataScRspReal()) } func readUvarint(b []byte) (uint64, int) { var x uint64 var s uint for i := 0; i < len(b) && i < 10; i++ { y := b[i] if y < 0x80 { if i == 9 && y > 1 { return 0, 0 } return x | uint64(y)<= 0x80 { b = append(b, byte(v)|0x80) v >>= 7 } return append(b, byte(v)) } var _ = goproto.CmdIDByName