package gameserver import ( "fmt" "gac-hkrpg-go/internal/common" goproto "gac-hkrpg-go/protocol/proto" ) // Request/response layouts follow services/profile.zig and protocol.pb.zig at // goproto.CommandSourceRevision. CSReq Go spellings below are legacy type names; // handle resolves only the current CsReq command name, never numeric aliases. func selectedProfileID(value *uint32, fallback uint32) uint32 { if value != nil { return *value } return fallback } func saveProfile(player *common.FreesrData, next common.ProfileSettings) error { previous := player.Profile player.Profile = next if err := player.SavePersistentChecked(); err != nil { player.Profile = previous return err } return nil } func profilePlayer(s *Session) (*common.FreesrData, error) { if p := s.JSON(); p != nil { return p, nil } return nil, fmt.Errorf("profile command requires player data") } func fillPhoneData(p *common.FreesrData, resource *profileResources, rsp *goproto.GetPhoneDataScRsp) { rsp.CurPhoneTheme = selectedProfileID(p.Profile.PhoneTheme, resource.CurrentPhoneTheme) rsp.CurPhoneCase = selectedProfileID(p.Profile.PhoneCase, resource.CurrentPhoneCase) rsp.CurChatBubble = selectedProfileID(p.Profile.ChatBubble, resource.CurrentChatBubble) rsp.OwnedPhoneThemes = resource.PhoneThemes rsp.OwnedPhoneCases = resource.PhoneCases rsp.OwnedChatBubbles = resource.ChatBubbles } func fillPlayerBoard(p *common.FreesrData, resource *profileResources, rsp *goproto.GetPlayerBoardDataScRsp) { rsp.CurrentHeadIconId = selectedProfileID(p.Profile.HeadIcon, resource.Icons[0]) rsp.CurrentPersonalCardId = selectedProfileID(p.Profile.PersonalCard, resource.CurrentPersonalCard) rsp.HeadFrameInfo = resource.HeadFrame if p.Profile.Signature != nil { rsp.Signature = *p.Profile.Signature } rsp.JJDDEKIAOFC = resource.PersonalCards for _, id := range resource.Icons { rsp.KNINLDPFLMA = append(rsp.KNINLDPFLMA, &goproto.HeadIconData{Id: id}) } rsp.NBIEJKLENOB = &goproto.DisplayAvatarVec{IsDisplay: true} if p.Profile.IsDisplay != nil { rsp.NBIEJKLENOB.IsDisplay = *p.Profile.IsDisplay } for _, slot := range p.SortedLineupKeys() { id := p.Lineups[slot] if p.Avatars[id] != nil { rsp.KJAABDABLJJ = append(rsp.KJAABDABLJJ, id) rsp.NBIEJKLENOB.DisplayAvatarList = append(rsp.NBIEJKLENOB.DisplayAvatarList, &goproto.DisplayAvatarData{AvatarId: id, Pos: slot}) } } if p.Profile.DisplayAvatars != nil { rsp.NBIEJKLENOB.DisplayAvatarList = nil for _, av := range *p.Profile.DisplayAvatars { rsp.NBIEJKLENOB.DisplayAvatarList = append(rsp.NBIEJKLENOB.DisplayAvatarList, &goproto.DisplayAvatarData{AvatarId: av.AvatarID, Pos: av.Pos}) } } if p.Profile.AssistAvatars != nil { rsp.KJAABDABLJJ = append([]uint32{}, (*p.Profile.AssistAvatars)...) } } func init() { handle("SelectPhoneTheme", func(s *Session, req *goproto.SelectPhoneThemeCsReq, rsp *goproto.SelectPhoneThemeScRsp) error { p, err := profilePlayer(s) if err != nil { return err } resource, err := loadProfileResources() if err != nil { return err } if req.ThemeId != resource.CurrentPhoneTheme && !containsU32(resource.PhoneThemes, req.ThemeId) { rsp.Retcode = uint32(goproto.Retcode_RET_ITEM_NOT_EXIST) return nil } next := p.Profile next.PhoneTheme = &req.ThemeId if err := saveProfile(p, next); err != nil { return err } rsp.CurPhoneTheme = req.ThemeId return nil }) handle("SelectPhoneCase", func(s *Session, req *goproto.SelectPhoneCaseCSReq, rsp *goproto.SelectPhoneCaseScRsp) error { p, err := profilePlayer(s) if err != nil { return err } resource, err := loadProfileResources() if err != nil { return err } if !containsU32(resource.PhoneCases, req.PhoneCaseId) { rsp.Retcode = uint32(goproto.Retcode_RET_ITEM_NOT_EXIST) return nil } next := p.Profile next.PhoneCase = &req.PhoneCaseId if err := saveProfile(p, next); err != nil { return err } rsp.CurPhoneCase = req.PhoneCaseId return nil }) handle("SelectChatBubble", func(s *Session, req *goproto.SelectChatBubbleCsReq, rsp *goproto.SelectChatBubbleScRsp) error { p, err := profilePlayer(s) if err != nil { return err } resource, err := loadProfileResources() if err != nil { return err } if req.BubbleId != resource.CurrentChatBubble && !containsU32(resource.ChatBubbles, req.BubbleId) { rsp.Retcode = uint32(goproto.Retcode_RET_ITEM_NOT_EXIST) return nil } next := p.Profile next.ChatBubble = &req.BubbleId if err := saveProfile(p, next); err != nil { return err } rsp.CurChatBubble = req.BubbleId return nil }) handle("SetHeadIcon", func(s *Session, req *goproto.SetHeadIconCsReq, rsp *goproto.SetHeadIconScRsp) error { p, err := profilePlayer(s) if err != nil { return err } resource, err := loadProfileResources() if err != nil { return err } if !containsU32(resource.Icons, req.Id) { rsp.Retcode = uint32(goproto.Retcode_RET_ITEM_NOT_EXIST) return nil } next := p.Profile next.HeadIcon = &req.Id if err := saveProfile(p, next); err != nil { return err } rsp.CurrentHeadIconId = req.Id return nil }) handle("SetPersonalCard", func(s *Session, req *goproto.SetPersonalCardCSReq, rsp *goproto.SetPersonalCardScRsp) error { p, err := profilePlayer(s) if err != nil { return err } resource, err := loadProfileResources() if err != nil { return err } if !containsU32(resource.PersonalCards, req.Id) { rsp.Retcode = uint32(goproto.Retcode_RET_ITEM_NOT_EXIST) return nil } next := p.Profile next.PersonalCard = &req.Id if err := saveProfile(p, next); err != nil { return err } rsp.CurrentPersonalCardId = req.Id return nil }) handle("SetSignature", func(s *Session, req *goproto.SetSignatureCsReq, rsp *goproto.SetSignatureScRsp) error { p, err := profilePlayer(s) if err != nil { return err } next := p.Profile next.Signature = &req.Signature if err := saveProfile(p, next); err != nil { return err } rsp.Signature = req.Signature return nil }) handle("SetIsDisplayAvatarInfo", func(s *Session, req *goproto.SetIsDisplayAvatarInfoCsReq, rsp *goproto.SetIsDisplayAvatarInfoScRsp) error { p, err := profilePlayer(s) if err != nil { return err } next := p.Profile next.IsDisplay = &req.IsDisplay if err := saveProfile(p, next); err != nil { return err } rsp.IsDisplay = req.IsDisplay return nil }) handle("SetDisplayAvatar", func(s *Session, req *goproto.SetDisplayAvatarCsReq, rsp *goproto.SetDisplayAvatarScRsp) error { p, err := profilePlayer(s) if err != nil { return err } list := make([]common.ProfileAvatar, 0, len(req.DisplayAvatarList)) positions, avatars := map[uint32]bool{}, map[uint32]bool{} for _, av := range req.DisplayAvatarList { if av == nil || p.Avatars[av.AvatarId] == nil || positions[av.Pos] || avatars[av.AvatarId] { rsp.Retcode = uint32(goproto.Retcode_RET_AVATAR_NOT_EXIST) return nil } positions[av.Pos], avatars[av.AvatarId] = true, true list = append(list, common.ProfileAvatar{AvatarID: av.AvatarId, Pos: av.Pos}) } next := p.Profile next.DisplayAvatars = &list if err := saveProfile(p, next); err != nil { return err } rsp.DisplayAvatarList = req.DisplayAvatarList return nil }) handle("SetAssistAvatar", func(s *Session, req *goproto.SetAssistAvatarCsReq, rsp *goproto.SetAssistAvatarScRsp) error { p, err := profilePlayer(s) if err != nil { return err } list := append([]uint32{}, req.AvatarIdList...) if req.AvatarId != 0 && !containsU32(list, req.AvatarId) { list = append(list, req.AvatarId) } seen := map[uint32]bool{} for _, id := range list { if p.Avatars[id] == nil || seen[id] { rsp.Retcode = uint32(goproto.Retcode_RET_AVATAR_NOT_EXIST) return nil } seen[id] = true } next := p.Profile next.AssistAvatars = &list if err := saveProfile(p, next); err != nil { return err } rsp.AvatarId, rsp.AvatarIdList = req.AvatarId, req.AvatarIdList return nil }) }