package catalogs import ( "encoding/json" "os" "path/filepath" "sync" ) var _ = json.RawMessage{} var _ = sync.Mutex{} var _ = os.ReadFile var _ = filepath.Join // One comment attached to a post. The body lives in the client's TuiteCommentConfig; only the id and its author travel. type TuiteComment4229938 struct { Id uint32 `json:"id"` Publisher uint32 `json:"publisher"` } // One 眼界 (Tuite) post instance as the server publishes it, from Configs/TuitePosts.json. type TuitePost4229938 struct { Id uint32 `json:"id"` Publisher uint32 `json:"publisher"` Likes int32 `json:"likes"` Views int32 `json:"views"` Comments []TuiteComment4229938 `json:"comments"` Raid []uint32 `json:"raid"` AreaId uint32 `json:"areaId"` TuiteType uint32 `json:"tuiteType"` TaskEvent uint32 `json:"taskEvent"` HasVideo bool `json:"hasVideo"` } // // A publishing NPC's display identity, mirrored from the client's TuiteNPCConfig. // Only needed for comments: a post's author comes from TuiteConfig.Publisher and the client // resolves it locally (GetTuiteInfo -> TuiteNPCConfig.GetConfig(cfg.Publisher)), but a // comment's author is carried by the server in ClientTuiteComment.RoleInfo. // type TuiteNpc4229938 struct { Id uint32 `json:"id"` Name string `json:"name"` Avatar uint32 `json:"avatar"` } // // Reads Configs/TuitePosts.json — a projection of the client's own TuiteConfig (204 rows), // TuiteCommentConfig (1031 rows) and TuiteNPCConfig (562 rows), built by _gen_tuite_posts.py. // This is what fills the 眼界 (Tuite) app: every list in // SocialNetWorkUtils.lua (GetRecommendList:143 / GetExploreTuiteList:400 / // GetFollowList:431 / GetTuiteListByPubisher:445 / GetCollectionList:1687 / GetLocalTuiteList:3046) // reads gPlayerManager.infoMinor.bindData.playerTuiteInfo.TuiteList, and // GetTuiteInfo(v) then only does LTConfig.TuiteConfig.GetConfig(v.CfgId) to get the // body text, publisher, area and type. So the server owns just the instance — id, counters, // publish time and the collect/like/follow flags — and every body stays client-side. // The list is published through the login frame's // PlayerClientInfoMinor.PlayerTuiteInfo (PlayerInfoMinorData.lua:25), the same channel the // 趴卜 schedule uses, so no extra notify is needed. // ⛑ HasVideo is deliberately never published as HasTimeline: the timeline blob the // client feeds to PlayMultiVideo is one the client itself authors and uploads // (AskTuiteUpdateTimelineInfo, 102400-byte limit), so there is no server-side copy of the // five video posts' data. Marking them HasTimeline = true would make the client ask for a // blob we cannot produce; leaving it false costs only the video, never the post. // A missing or malformed file leaves the catalogue empty and LoadError explains why. // var ( TuiteCatalog4229938_cacheLock sync.RWMutex TuiteCatalog4229938_cached = false TuiteCatalog4229938_entries []any ) // LoadTuiteCatalog4229938 loads catalog records from client config directory func LoadTuiteCatalog4229938(configDir string) ([]any, error) { TuiteCatalog4229938_cacheLock.RLock() if TuiteCatalog4229938_cached { defer TuiteCatalog4229938_cacheLock.RUnlock() return TuiteCatalog4229938_entries, nil } TuiteCatalog4229938_cacheLock.RUnlock() TuiteCatalog4229938_cacheLock.Lock() defer TuiteCatalog4229938_cacheLock.Unlock() if TuiteCatalog4229938_cached { return TuiteCatalog4229938_entries, nil } filePath := filepath.Join(configDir, "TuiteCatalog.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) } } TuiteCatalog4229938_entries = result TuiteCatalog4229938_cached = true return result, nil } func TuiteCatalog4229938_Find(id uint32) *TuitePost4229938 { // Catalog helper TuiteCatalog4229938_Find return nil } // // Position of a post in the catalogue, or -1. Both the login frame and the detail handlers // derive PublishTime from it, so the "多久以前" a row shows in the list matches the one // on its detail page (the detail INVOKE carries no time of its own). // func TuiteCatalog4229938_IndexOf(id uint32) int32 { // Catalog helper TuiteCatalog4229938_IndexOf return 0 } // Same 10-minute spacing the login frame uses, newest (index 0) first. func TuiteCatalog4229938_PublishTimeFor(id uint32, now int64) uint32 { // Catalog helper TuiteCatalog4229938_PublishTimeFor return 0 } func TuiteCatalog4229938_Comments(node any) []TuiteComment4229938 { // Catalog helper TuiteCatalog4229938_Comments return nil } func TuiteCatalog4229938_U32List(node any, name string) []uint32 { // Catalog helper TuiteCatalog4229938_U32List return nil }