package gameserver import ( "encoding/json" "fmt" "gac-hkrpg-go/internal/common" "sync" goproto "gac-hkrpg-go/protocol/proto" ) type profileResources struct { PamSkins []uint32 `json:"pam_skins"` CurrentPamSkin uint32 `json:"current_pam_skin"` Source struct { Revision string `json:"revision"` } `json:"source"` PersonalCards []uint32 `json:"personal_cards"` CurrentPersonalCard uint32 `json:"current_personal_card"` HeadFrame *goproto.HeadFrameInfo `json:"head_frame"` PhoneCases []uint32 `json:"phone_cases"` PhoneThemes []uint32 `json:"phone_themes"` ChatBubbles []uint32 `json:"chat_bubbles"` CurrentPhoneCase uint32 `json:"current_phone_case"` CurrentPhoneTheme uint32 `json:"current_phone_theme"` CurrentChatBubble uint32 `json:"current_chat_bubble"` Icons []uint32 `json:"-"` } var profileOnce sync.Once var profileData profileResources var profileError error func loadProfileResources() (*profileResources, error) { profileOnce.Do(func() { raw, err := common.ReadResourceFile("resources/ProfileConfig.json", "ProfileConfig.json") if err != nil { profileError = err return } if err := json.Unmarshal(raw, &profileData); err != nil { profileError = err return } if profileData.Source.Revision == "" || profileData.HeadFrame == nil || !containsU32(profileData.PersonalCards, profileData.CurrentPersonalCard) { profileError = fmt.Errorf("profile source, head frame or selected personal card is invalid") return } if profileData.CurrentPamSkin == 0 || !containsU32(profileData.PamSkins, profileData.CurrentPamSkin) { profileError = fmt.Errorf("selected Pam skin is missing or not owned") return } pamRaw, err := common.ReadResourceFile("resources/PamSkinConfig.json") if err != nil { profileError = err return } var pamTable struct { Rows []struct { ID uint32 `json:"SkinID"` Path string `json:"ConfigEntityPath"` } `json:"pam_skin_config"` } if err := json.Unmarshal(pamRaw, &pamTable); err != nil { profileError = err return } validSkins := map[uint32]bool{} for _, row := range pamTable.Rows { if row.ID != 0 && row.Path != "" { validSkins[row.ID] = true } } for _, id := range profileData.PamSkins { if !validSkins[id] { profileError = fmt.Errorf("Pam skin %d has no Client entity config", id) return } } if !containsU32(profileData.PhoneCases, profileData.CurrentPhoneCase) { profileError = fmt.Errorf("selected phone case is not owned") return } raw, err = common.ReadResourceFile("resources/AvatarPlayerIcon.json", "AvatarPlayerIcon.json") if err != nil { profileError = err return } var icons struct { Entries []struct { ID uint32 `json:"ID"` } `json:"player_icon_config"` } if err := json.Unmarshal(raw, &icons); err != nil { profileError = err return } for _, icon := range icons.Entries { if icon.ID != 0 { profileData.Icons = append(profileData.Icons, icon.ID) } } if len(profileData.Icons) == 0 { profileError = fmt.Errorf("AvatarPlayerIcon.json has no icons") } }) if profileError != nil { return nil, fmt.Errorf("load profile resources: %w", profileError) } return &profileData, nil }