Files
GaCy-GO/internal/gameserver/handlers_misc.go
T

411 lines
12 KiB
Go

package gameserver
import (
"math/rand"
"sync"
"gac-hkrpg-go/internal/common"
goproto "gac-hkrpg-go/protocol/proto"
)
// CURRENT_BATTLE mirrors battle.rs CURRENT_BATTLE (token -> SceneBattleInfo).
var CURRENT_BATTLE syncMap
type syncMap struct {
m map[uint32]*goproto.SceneBattleInfo
}
func (sm *syncMap) init() {
if sm.m == nil {
sm.m = map[uint32]*goproto.SceneBattleInfo{}
}
}
func (sm *syncMap) Store(k uint32, v *goproto.SceneBattleInfo) {
muBattle.Lock()
sm.init()
sm.m[k] = v
muBattle.Unlock()
}
func (sm *syncMap) Load(k uint32) (*goproto.SceneBattleInfo, bool) {
muBattle.Lock()
defer muBattle.Unlock()
sm.init()
v, ok := sm.m[k]
return v, ok
}
func (sm *syncMap) Delete(k uint32) { muBattle.Lock(); sm.init(); delete(sm.m, k); muBattle.Unlock() }
func randUint32() uint32 { return rand.Uint32() }
func init() {
handle("GetBag", func(s *Session, req *goproto.GetBagCsReq, rsp *goproto.GetBagScRsp) error {
player := s.JSON()
if player == nil {
return nil
}
for i := range player.Lightcones {
rsp.EquipmentList = append(rsp.EquipmentList, player.Lightcones[i].ToProtoEquipment())
}
for i := range player.Relics {
rsp.RelicList = append(rsp.RelicList, player.Relics[i].ToProtoRelic())
}
rsp.MaterialList = []*goproto.Material{
{Tid: 1, Num: 9999999}, // Stellar Jade
{Tid: 2, Num: 99999999}, // Credit
{Tid: 101, Num: 999999}, // Normal Pass
{Tid: 102, Num: 999999}, // Special Pass
}
for _, aid := range BaseAvatarIDs {
rsp.MaterialList = append(rsp.MaterialList, &goproto.Material{
Tid: 130000 + aid,
Num: 10,
})
}
return nil
})
handle("GetArchiveData", func(s *Session, req *goproto.GetArchiveDataCsReq, rsp *goproto.GetArchiveDataScRsp) error {
rsp.Retcode = 0
return nil
})
// --- lightcone equipping (Rust: set_lightcone_equipper) ---
handle("DressAvatar", func(s *Session, req *goproto.DressAvatarCsReq, rsp *goproto.DressAvatarScRsp) error {
if pkt := setLightconeEquipper(s, req.AvatarId, req.EquipmentUniqueId); pkt != nil {
return s.Send(pkt)
}
return nil
})
handle("TakeOffEquipment", func(s *Session, req *goproto.TakeOffEquipmentCsReq, rsp *goproto.TakeOffEquipmentScRsp) error {
if pkt := setLightconeEquipper(s, req.AvatarId, 0); pkt != nil {
return s.Send(pkt)
}
return nil
})
handle("DressRelicAvatar", func(s *Session, req *goproto.DressRelicAvatarCsReq, rsp *goproto.DressRelicAvatarScRsp) error {
if pkt := equipRelic(s, req); pkt != nil {
return s.Send(pkt)
}
return nil
})
handle("TakeOffRelic", func(s *Session, req *goproto.TakeOffRelicCsReq, rsp *goproto.TakeOffRelicScRsp) error {
if pkt := unequipRelic(s, req); pkt != nil {
return s.Send(pkt)
}
return nil
})
handle("RankUpAvatar", func(s *Session, req *goproto.RankUpAvatarCsReq, rsp *goproto.RankUpAvatarScRsp) error {
player := s.JSON()
if player == nil {
return nil
}
avatar, ok := player.Avatars[req.AvatarId]
if !ok {
return nil
}
isExRank := false
if req.CostData != nil {
for _, item := range req.CostData.ItemList {
if pile := item.GetPileItem(); pile != nil {
if (pile.ItemId >= 130000 && pile.ItemId < 140000) || pile.ItemId == 131204 || pile.ItemId == 131213 || pile.ItemId == 131308 {
isExRank = true
break
}
}
}
}
if req.Rank > 6 {
isExRank = true
}
if isExRank || (avatar.Data.Rank >= 6 && common.AvatarExRankMap[req.AvatarId] != 0) {
avatar.ExRankUnlocked = true
if req.Rank > 0 && req.Rank <= 6 {
avatar.Data.Rank = req.Rank
}
} else {
avatar.Data.Rank = req.Rank
}
ret := &goproto.PlayerSyncScNotify{}
buildSync(player, ret, []uint32{avatar.AvatarID}, nil, nil)
return s.Send(ret)
})
handle("GetBigDataAllRecommend", func(s *Session, req *goproto.GetBigDataAllRecommendCsReq, rsp *goproto.GetBigDataAllRecommendScRsp) error {
rsp.BigDataRecommendType = req.BigDataRecommendType
res, err := common.GameRes()
if err != nil {
return nil
}
switch req.BigDataRecommendType {
case goproto.BigDataRecommendType_BIG_DATA_RECOMMEND_TYPE_RELIC_AVATAR:
inner := &goproto.BigDataRecommendRelicAvatar{}
setIDs := sortedU32Keys2(res.RelicAvatarRecommend)
for _, setID := range setIDs {
list := res.RelicAvatarRecommend[setID]
first := uint32(0)
if len(list) > 0 {
first = list[0]
}
inner.RecommendedAvatarInfoList = append(inner.RecommendedAvatarInfoList, &goproto.RecomendedAvatarInfo{
AvatarIdList: list,
RecommendAvatarId: first,
RelicSetId: setID,
})
}
rsp.RecommendType = &goproto.GetBigDataAllRecommendScRsp_RelicAvatar{RelicAvatar: inner}
case goproto.BigDataRecommendType_BIG_DATA_RECOMMEND_TYPE_AVATAR_RELIC:
// 4.6.51 proto: recomended_relic_info_list is repeated GDFKJKDLEPM
// (obfuscated, contents unused by this PS) — reply with empty entries
inner := &goproto.BigDataRecommendAvatarRelic{}
for range BaseAvatarIDs {
inner.RecomendedRelicInfoList = append(inner.RecomendedRelicInfoList, &goproto.GDFKJKDLEPM{})
}
rsp.RecommendType = &goproto.GetBigDataAllRecommendScRsp_AvatarRelic{AvatarRelic: inner}
}
return nil
})
// --- gacha ---
handle("GetGachaInfo", func(s *Session, req *goproto.GetGachaInfoCsReq, rsp *goproto.GetGachaInfoScRsp) error {
rsp.GachaInfoList = append(rsp.GachaInfoList, &goproto.GachaInfo{
EndTime: 1924992000,
BeginTime: 0,
GachaCeiling: &goproto.GachaCeiling{},
PrizeItemList: []uint32{23002, 1003, 1101, 1104, 23000, 23003},
ItemDetailList: []uint32{
1001, 1002, 1008, 1009, 1013, 1103, 1105, 1106, 1108, 1109, 1110, 1111, 1201, 1202,
1206, 1207, 1210, 1003, 1004, 1101, 1107, 1104, 1209, 1211, 21000, 21001, 21002, 21003,
21004, 21005, 21006, 21007, 21008, 21009, 21010, 21011, 21012, 21013, 21014, 21015,
21016, 21017, 21018, 21019, 21020, 23000, 23002, 23003, 23004, 23005, 23012, 23013,
},
GachaId: 1001,
})
return nil
})
handle("DoGacha", func(s *Session, req *goproto.DoGachaCsReq, rsp *goproto.DoGachaScRsp) error {
rsp.GachaId = req.GachaId
rsp.GachaNum = req.GachaNum
for v := uint32(0); v < req.GachaNum; v++ {
itemID := uint32(1310)
if v%2 != 0 {
itemID = 1314
}
rsp.GachaItemList = append(rsp.GachaItemList, &goproto.GachaItem{
GachaItem: &goproto.Item{ItemId: itemID},
TokenItem: &goproto.ItemList{
ItemList: []*goproto.Item{{ItemId: 251, Num: 100}},
},
TransferItemList: &goproto.ItemList{},
})
}
return nil
})
// --- mail ---
handle("GetMail", func(s *Session, req *goproto.GetMailCsReq, rsp *goproto.GetMailScRsp) error {
rsp.IsEnd = true
rsp.MailList = append(rsp.MailList, &goproto.ClientMail{
Title: "Welcome!",
Sender: "Server",
Content: "Welcome!",
Id: 1,
Time: 1716041089,
ExpireTime: 1718633089,
Attachment: &goproto.ItemList{
ItemList: []*goproto.Item{{ItemId: 1310, Level: 80, Num: 1}},
},
})
return nil
})
// --- chat/friends (empty-but-valid) ---
handle("GetPrivateChatHistory", func(s *Session, req *goproto.GetPrivateChatHistoryCsReq, rsp *goproto.GetPrivateChatHistoryScRsp) error {
rsp.Retcode = 0
return nil
})
handle("GetFriendListInfo", func(s *Session, req *goproto.GetFriendListInfoCsReq, rsp *goproto.GetFriendListInfoScRsp) error {
rsp.Retcode = 0
return nil
})
handle("GetFriendLoginInfo", func(s *Session, req *goproto.GetFriendLoginInfoCsReq, rsp *goproto.GetFriendLoginInfoScRsp) error {
rsp.Retcode = 0
return nil
})
}
// ---------------------------------------------------------------------------
// equip helpers (Go port of inventory.rs helper fns)
// ---------------------------------------------------------------------------
func setLightconeEquipper(s *Session, targetAvatarID, targetLCUID uint32) *goproto.PlayerSyncScNotify {
player := s.JSON()
if player == nil {
return nil
}
targetAvatar, ok := player.Avatars[targetAvatarID]
if !ok {
return nil
}
ret := &goproto.PlayerSyncScNotify{}
curAvatarLCIdx := -1
targetLCIdx := -1
for i := range player.Lightcones {
if player.Lightcones[i].EquipAvatar == targetAvatar.AvatarID {
curAvatarLCIdx = i
}
if player.Lightcones[i].UniqueID() == targetLCUID {
targetLCIdx = i
}
}
// undress
if targetLCUID == 0 {
if curAvatarLCIdx >= 0 {
player.Lightcones[curAvatarLCIdx].EquipAvatar = 0
buildSync(player, ret, []uint32{targetAvatar.AvatarID}, []int{curAvatarLCIdx}, nil)
return ret
}
return nil
}
if targetLCIdx < 0 {
return nil
}
var avatarsSync []uint32
var lcIdxSync []int
// if the current avatar holds an LC, swap owners with the target LC's owner
if curAvatarLCIdx >= 0 {
player.Lightcones[curAvatarLCIdx].EquipAvatar = player.Lightcones[targetLCIdx].EquipAvatar
}
avatarsSync = append(avatarsSync, player.Lightcones[targetLCIdx].EquipAvatar, targetAvatar.AvatarID)
player.Lightcones[targetLCIdx].EquipAvatar = targetAvatar.AvatarID
if curAvatarLCIdx >= 0 {
lcIdxSync = append(lcIdxSync, curAvatarLCIdx)
}
lcIdxSync = append(lcIdxSync, targetLCIdx)
buildSync(player, ret, avatarsSync, lcIdxSync, nil)
return ret
}
func equipRelic(s *Session, req *goproto.DressRelicAvatarCsReq) *goproto.PlayerSyncScNotify {
player := s.JSON()
if player == nil {
return nil
}
targetAvatar, ok := player.Avatars[req.AvatarId]
if !ok {
return nil
}
ret := &goproto.PlayerSyncScNotify{}
var avatarIDsToSync []uint32
var relicIdxToSync []int
for _, param := range req.SwitchList {
targetRelicIdx := -1
for i := range player.Relics {
if player.Relics[i].UniqueID() == param.RelicUniqueId {
targetRelicIdx = i
break
}
}
if targetRelicIdx < 0 {
continue
}
curAvatarRelicIdx := -1
for i := range player.Relics {
r := &player.Relics[i]
if r.EquipAvatar == targetAvatar.AvatarID && r.Slot() == param.RelicType {
curAvatarRelicIdx = i
break
}
}
if curAvatarRelicIdx >= 0 {
avatarIDsToSync = append(avatarIDsToSync, player.Relics[curAvatarRelicIdx].EquipAvatar)
player.Relics[curAvatarRelicIdx].EquipAvatar = player.Relics[targetRelicIdx].EquipAvatar
relicIdxToSync = append(relicIdxToSync, curAvatarRelicIdx)
}
avatarIDsToSync = append(avatarIDsToSync, player.Relics[targetRelicIdx].EquipAvatar)
player.Relics[targetRelicIdx].EquipAvatar = targetAvatar.AvatarID
avatarIDsToSync = append(avatarIDsToSync, player.Relics[targetRelicIdx].EquipAvatar)
relicIdxToSync = append(relicIdxToSync, targetRelicIdx)
}
buildSync(player, ret, avatarIDsToSync, nil, relicIdxToSync)
return ret
}
func unequipRelic(s *Session, req *goproto.TakeOffRelicCsReq) *goproto.PlayerSyncScNotify {
player := s.JSON()
if player == nil {
return nil
}
if _, ok := player.Avatars[req.AvatarId]; !ok {
return nil
}
ret := &goproto.PlayerSyncScNotify{}
var relicIdxToSync []int
for _, slot := range req.RelicTypeList {
for i := range player.Relics {
r := &player.Relics[i]
if r.EquipAvatar == req.AvatarId && r.Slot() == slot {
r.EquipAvatar = 0
relicIdxToSync = append(relicIdxToSync, i)
}
}
}
buildSync(player, ret, []uint32{req.AvatarId}, nil, relicIdxToSync)
return ret
}
func buildSync(
player *common.FreesrData,
ret *goproto.PlayerSyncScNotify,
avatarIDs []uint32,
lightconeIndexes []int,
relicIndexes []int,
) {
sync := &goproto.AvatarSync{}
for _, id := range avatarIDs {
if av, ok := player.Avatars[id]; ok {
sync.AvatarList = append(sync.AvatarList, av.ToAvatarProto(player.FindLightconeFor(id), player.MainCharacter.ID(), player.MarchType.ID()))
sync.AvatarPathDataInfoList = append(sync.AvatarPathDataInfoList,
av.ToAvatarPathDataProto(player.FindLightconeFor(id), player.FindRelicsFor(id), player.DressedSkins[id]))
}
}
ret.AvatarSync = sync
for _, idx := range relicIndexes {
if idx >= 0 && idx < len(player.Relics) {
ret.RelicList = append(ret.RelicList, player.Relics[idx].ToProtoRelic())
}
}
for _, idx := range lightconeIndexes {
if idx >= 0 && idx < len(player.Lightcones) {
ret.EquipmentList = append(ret.EquipmentList, player.Lightcones[idx].ToProtoEquipment())
}
}
}
var muBattle sync.Mutex