Files

165 lines
4.9 KiB
Go

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
if run := s.ChallengeRun; run != nil && run.Tierce {
rsp.CurTierceChallenge = run.TierceCur
}
return nil
})
handle("StartChallengePeak", handleStartChallengePeak)
rawNamed("LeaveChallengePeakCsReq", func(s *Session, payload []byte) error {
return leaveChallengeScene(s, &goproto.LeaveChallengePeakScRsp{})
})
}
// The EX request remains obfuscated in PearlSR 005d2c1: field 5 is
// interpreted as an avatar, EX-rank or material ID only by an explicit mapping.
// Never unlock every avatar when an unknown value is received.
func handleExRankUnlock(s *Session, payload []byte) error {
var req goproto.HIGLJHLNODG
if err := proto.Unmarshal(payload, &req); err != nil {
return err
}
value := req.OJFIGABCKLG
resources, err := loadEidolonResources()
if err != nil {
return err
}
var avatarID uint32
for id, exID := range common.AvatarExRankMap {
if value == id || value == exID || value == resources.ExMaterials[id].ItemID {
avatarID = id
break
}
}
rsp := &goproto.LPOLGDLAADM{}
player := s.JSON()
if player == nil || avatarID == 0 || player.Avatars[avatarID] == nil {
rsp.Retcode = uint32(goproto.Retcode_RET_AVATAR_NOT_EXIST)
Log(fmt.Sprintf("[EX-RANK] rejected unmapped value=%d", value))
return s.Send(rsp)
}
if _, active := CURRENT_BATTLE.Load(s.Token); active {
rsp.Retcode = uint32(goproto.Retcode_RET_AVATAR_RANK_NOT_MATCH)
return s.Send(rsp)
}
avatar := player.Avatars[avatarID]
previous := avatar.ExRankUnlocked
avatar.ExRankUnlocked = true
if err := player.SavePersistentChecked(); err != nil {
avatar.ExRankUnlocked = previous
return err
}
notify := &goproto.PlayerSyncScNotify{}
buildSync(player, notify, []uint32{avatarID}, nil, nil)
notify.MaterialList, err = eidolonMaterials(player)
if err != nil {
return err
}
if err := s.Send(notify); err != nil {
return err
}
Log(fmt.Sprintf("[EX-RANK] unlocked avatar=%d value=%d", avatarID, value))
return s.Send(rsp)
}
// 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(s))
}
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)<<s, i + 1
}
x |= uint64(y&0x7f) << s
s += 7
}
return 0, 0
}
func appendUvarint(b []byte, v uint64) []byte {
for v >= 0x80 {
b = append(b, byte(v)|0x80)
v >>= 7
}
return append(b, byte(v))
}
var _ = goproto.CmdIDByName