160 lines
5.2 KiB
Go
160 lines
5.2 KiB
Go
package common
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"sort"
|
|
)
|
|
|
|
type PreviewActivitySchedule struct {
|
|
ModuleID, PanelID uint32
|
|
DataHandler string
|
|
}
|
|
|
|
func LoadPreviewActivitySchedules() ([]PreviewActivitySchedule, error) {
|
|
raw, err := ReadResourceFile("resources/ActivityPreviewPolicy.json")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var policy struct {
|
|
Source string `json:"source"`
|
|
Panels []struct {
|
|
Prefab string `json:"prefab"`
|
|
DataHandler string `json:"data_handler"`
|
|
} `json:"panels"`
|
|
// Display-item panels are withheld by default because their reward
|
|
// previews previously triggered invalid-item errors. A panel listed
|
|
// here is published deliberately so its real data callbacks can be
|
|
// identified from the Client's own requests.
|
|
AllowDisplayItemPanels []uint32 `json:"allow_display_item_panels"`
|
|
}
|
|
if err = json.Unmarshal(raw, &policy); err != nil {
|
|
return nil, err
|
|
}
|
|
if policy.Source == "" {
|
|
return nil, fmt.Errorf("activity preview policy missing")
|
|
}
|
|
if len(policy.Panels) == 0 {
|
|
return nil, nil
|
|
}
|
|
raw, err = ReadResourceFile("resources/PreviewGameplayCatalog.json")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var catalog struct {
|
|
Evidence string `json:"evidence_id"`
|
|
Tables struct {
|
|
Panels []struct {
|
|
ID uint32 `json:"PanelID"`
|
|
Prefab string `json:"UIPrefab"`
|
|
Icon string `json:"TabIcon"`
|
|
DisplayItems []json.RawMessage `json:"DisplayItemList"`
|
|
} `json:"panels"`
|
|
Activities []struct {
|
|
ID uint32 `json:"ActivityID"`
|
|
PanelID uint32 `json:"ActivityPanelID"`
|
|
Residents []uint32 `json:"ResidentModuleList"`
|
|
Modules []uint32 `json:"ActivityModuleIDList"`
|
|
} `json:"activities"`
|
|
} `json:"tables"`
|
|
}
|
|
if err = json.Unmarshal(raw, &catalog); err != nil {
|
|
return nil, err
|
|
}
|
|
if catalog.Evidence == "" {
|
|
return nil, fmt.Errorf("activity preview catalog provenance missing")
|
|
}
|
|
availabilityRaw, err := ReadResourceFile("resources/PreviewAssetAvailability.json")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var availability struct {
|
|
Source string `json:"source"`
|
|
Paths map[string]bool `json:"paths"`
|
|
}
|
|
if err = json.Unmarshal(availabilityRaw, &availability); err != nil {
|
|
return nil, err
|
|
}
|
|
if availability.Source == "" || availability.Paths == nil {
|
|
return nil, fmt.Errorf("event asset verification missing")
|
|
}
|
|
prefabs := map[string]string{}
|
|
for _, p := range policy.Panels {
|
|
if p.DataHandler != "GetFiveDimMiniGameData" && p.DataHandler != "GetActiveActivityData" && p.DataHandler != "QuestState" && p.DataHandler != "GetMultipleDropInfo" && p.DataHandler != "ComicDessertPreview" {
|
|
return nil, fmt.Errorf("unsupported preview data handler %s", p.DataHandler)
|
|
}
|
|
if p.DataHandler == "ComicDessertPreview" {
|
|
if _, err := LoadComicDessertCatalog(); err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
prefabs[p.Prefab] = p.DataHandler
|
|
}
|
|
allowDisplayItems := map[uint32]bool{}
|
|
for _, id := range policy.AllowDisplayItemPanels {
|
|
allowDisplayItems[id] = true
|
|
}
|
|
panels := map[uint32]struct{ Handler, Prefab string }{}
|
|
for _, p := range catalog.Tables.Panels {
|
|
if handler := prefabs[p.Prefab]; handler != "" && availability.Paths[p.Prefab] && availability.Paths[p.Icon] && (len(p.DisplayItems) == 0 || allowDisplayItems[p.ID]) {
|
|
panels[p.ID] = struct{ Handler, Prefab string }{handler, p.Prefab}
|
|
}
|
|
}
|
|
// Rerun activities can reuse one module ID across panel generations, so a
|
|
// module may resolve to several panels. Publish one schedule per module and
|
|
// only when every candidate panel agrees on handler and prefab; otherwise
|
|
// the association would be an accidental first match.
|
|
modulePanels := map[uint32][]uint32{}
|
|
for _, activity := range catalog.Tables.Activities {
|
|
panel := activity.PanelID
|
|
if panel == 0 {
|
|
panel = activity.ID
|
|
}
|
|
if panels[panel].Handler == "" {
|
|
continue
|
|
}
|
|
for _, list := range [][]uint32{activity.Modules, activity.Residents} {
|
|
for _, id := range list {
|
|
if id == 0 {
|
|
continue
|
|
}
|
|
if _, duplicate := modulePanels[id]; !duplicate {
|
|
modulePanels[id] = []uint32{panel}
|
|
continue
|
|
}
|
|
already := false
|
|
for _, existing := range modulePanels[id] {
|
|
if existing == panel {
|
|
already = true
|
|
break
|
|
}
|
|
}
|
|
if !already {
|
|
modulePanels[id] = append(modulePanels[id], panel)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if len(modulePanels) == 0 {
|
|
return nil, fmt.Errorf("no supported activity-preview modules in captured catalog")
|
|
}
|
|
moduleIDs := make([]uint32, 0, len(modulePanels))
|
|
for id := range modulePanels {
|
|
moduleIDs = append(moduleIDs, id)
|
|
}
|
|
sort.Slice(moduleIDs, func(i, j int) bool { return moduleIDs[i] < moduleIDs[j] })
|
|
var schedules []PreviewActivitySchedule
|
|
for _, id := range moduleIDs {
|
|
panelsForModule := modulePanels[id]
|
|
sort.Slice(panelsForModule, func(i, j int) bool { return panelsForModule[i] < panelsForModule[j] })
|
|
first := panels[panelsForModule[0]]
|
|
for _, panel := range panelsForModule[1:] {
|
|
if panels[panel] != first {
|
|
return nil, fmt.Errorf("activity module %d maps to conflicting panels %v", id, panelsForModule)
|
|
}
|
|
}
|
|
schedules = append(schedules, PreviewActivitySchedule{ModuleID: id, PanelID: panelsForModule[0], DataHandler: first.Handler})
|
|
}
|
|
return schedules, nil
|
|
}
|