package catalogs import ( "encoding/json" "os" "path/filepath" "sync" ) var _ = json.RawMessage{} var _ = sync.Mutex{} var _ = os.ReadFile var _ = filepath.Join // 手机「通讯录 / 拨号」里一个可拨号码。服务端只持有号码,名字头像台词都在客户端本地表。 type PhoneContact4229938 struct { Id uint32 `json:"id"` PhoneNumber string `json:"phoneNumber"` Name string `json:"name"` AutoAdd bool `json:"autoAdd"` IsQuickCall bool `json:"isQuickCall"` RelatedNpcId uint32 `json:"relatedNpcId"` StartDialogId uint32 `json:"startDialogId"` } // // Reads Configs/PhoneContacts.json — a projection of the client's own PhoneContactConfig (66 rows) // and PhoneContactUnlockConfig (79 rows), built by _gen_phone_contacts.py. // 为什么服务端只需要这么一点: // // // 通讯录条目的契约只有两个字段 —— PhoneContact.Remark + PhoneContact.PhoneNumber。 // CallPhoneUtils.GetConfigIdByPhoneNumber 拿号码回查本地 PhoneContactConfig, // 名字 / 头像 / 备注 / 台词本体全在客户端 (GetContactName / GetContactRemark / // GetSAvatarByConfigId),所以服务端发错名字也不会显示,只有号码必须对。 // // // 「能不能拨通」= PhoneContactUnlockConfig 的行是否出现在 module 3 (PhoneContact) 的 // FinishedTemplateIdList 里 —— EventConditionUtils.CheckHasUnlocked 的判据就是 // 这个列表包含 config.Id;原版靠任务推进,这里整表标完成。 // ⛑ 未解锁时客户端播的是 LockDialogId(号码忙),不是报错,日志里看不出来。 // // // 拨通之后的对话由客户端本地 ShowGeneralDialog(dialogId) 播放 // (CallPhoneUtils.ShowCallPhoneTimelineDialogPanel),服务端零参与 —— 这也是为什么 // 不用移植 T1-4 / T3-1 的 NPC 对话系统就能让电话打通。 // // // A missing or malformed file leaves the catalogue empty and LoadError explains why; // the caller then falls back to the two hardcoded summon quick-call numbers. // var ( PhoneContactCatalog4229938_cacheLock sync.RWMutex PhoneContactCatalog4229938_cached = false PhoneContactCatalog4229938_entries []any ) // LoadPhoneContactCatalog4229938 loads catalog records from client config directory func LoadPhoneContactCatalog4229938(configDir string) ([]any, error) { PhoneContactCatalog4229938_cacheLock.RLock() if PhoneContactCatalog4229938_cached { defer PhoneContactCatalog4229938_cacheLock.RUnlock() return PhoneContactCatalog4229938_entries, nil } PhoneContactCatalog4229938_cacheLock.RUnlock() PhoneContactCatalog4229938_cacheLock.Lock() defer PhoneContactCatalog4229938_cacheLock.Unlock() if PhoneContactCatalog4229938_cached { return PhoneContactCatalog4229938_entries, nil } filePath := filepath.Join(configDir, "PhoneContactCatalog.json.json") data, err := os.ReadFile(filePath) if err != nil { return nil, err } var raw struct { Records []json.RawMessage `json:"records"` } if err := json.Unmarshal(data, &raw); err != nil { return nil, err } var result []any for _, item := range raw.Records { var m map[string]any if err := json.Unmarshal(item, &m); err == nil { result = append(result, m) } } PhoneContactCatalog4229938_entries = result PhoneContactCatalog4229938_cached = true return result, nil } // 真正下发的号码:默认只发 AutoAdd 的(原版会自动进通讯录的那批)。 func PhoneContactCatalog4229938_AutoAddContacts() []PhoneContact4229938 { // Catalog helper PhoneContactCatalog4229938_AutoAddContacts return nil }