-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathSecretChecklistValidate.lua
More file actions
152 lines (138 loc) · 5.23 KB
/
Copy pathSecretChecklistValidate.lua
File metadata and controls
152 lines (138 loc) · 5.23 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
-- =================================================================
-- SecretChecklistValidate.lua
-- Data self-check, run with /secrets validate.
--
-- The entry schema is implicit -- around twenty recognised fields on an entry
-- and as many again on a step, documented only by how the panels happen to read
-- them. A contributor adding a secret has to reverse-engineer it from
-- neighbouring entries, and a typo produces a silently wrong row rather than an
-- error.
--
-- This checks the whole data set against what the code actually requires and
-- reports in one pass. It also answers questions that can only be asked from a
-- live client, such as which waypoint maps the game will accept.
-- =================================================================
local SC = _G.SecretChecklist
if not SC then return end
local KNOWN_KINDS = {
mount = true, pet = true, toy = true, achievement = true,
quest = true, transmog = true, housing = true, mystery = true, manual = true,
}
-- The id each kind needs before its status can be resolved at all.
local REQUIRED_ID = {
mount = { "mountID", "itemID" },
pet = { "speciesID" },
toy = { "itemID" },
achievement = { "achievementID" },
quest = { "questID" },
transmog = { "itemID" },
housing = { "itemID" },
}
local function HasAnyField(entry, fields)
for _, f in ipairs(fields) do
if entry[f] ~= nil then return true end
end
return false
end
-- Walks every waypoint on a step, including substep and faction variants.
local function ForEachWaypoint(step, fn)
local function visit(wp)
if type(wp) == "table" and wp.mapID then fn(wp) end
end
visit(step.waypoint)
for _, wp in ipairs(step.waypoints or {}) do visit(wp) end
if type(step.factionWaypoint) == "table" then
visit(step.factionWaypoint.alliance)
visit(step.factionWaypoint.horde)
end
local subLists = { step.substeps }
if type(step.factionSubsteps) == "table" then
subLists[#subLists + 1] = step.factionSubsteps.alliance
subLists[#subLists + 1] = step.factionSubsteps.horde
end
for _, list in ipairs(subLists) do
for _, sub in ipairs(list or {}) do
visit(sub.waypoint)
for _, wp in ipairs(sub.waypoints or {}) do visit(wp) end
if type(sub.factionWaypoint) == "table" then
visit(sub.factionWaypoint.alliance)
visit(sub.factionWaypoint.horde)
end
end
end
end
local function Report(problems, label)
if #problems == 0 then return 0 end
print(("|cffff4444%s (%d):|r"):format(label, #problems))
for i = 1, math.min(#problems, 15) do
print(" " .. problems[i])
end
if #problems > 15 then
print((" ... and %d more"):format(#problems - 15))
end
return #problems
end
function SC:ValidateData()
local entries = SC.entries or {}
local byName = {}
for _, e in ipairs(entries) do
if type(e.name) == "string" then byName[e.name] = true end
end
local schema, refs, waypoints = {}, {}, {}
local blockedMaps, totalWaypoints = {}, 0
local canCheckMaps = C_Map and C_Map.CanSetUserWaypointOnMap
for i, e in ipairs(entries) do
local id = type(e.name) == "string" and e.name or ("entry #" .. i)
if type(e.name) ~= "string" or e.name == "" then
schema[#schema + 1] = ("entry #%d has no name"):format(i)
end
if not KNOWN_KINDS[e.kind] then
schema[#schema + 1] = ("%s: unknown kind %q"):format(id, tostring(e.kind))
elseif REQUIRED_ID[e.kind] and not HasAnyField(e, REQUIRED_ID[e.kind]) then
schema[#schema + 1] = ("%s: kind %q needs one of %s")
:format(id, e.kind, table.concat(REQUIRED_ID[e.kind], " / "))
end
-- Cross-references must name an entry that exists, or the Guides pane
-- renders them as dead grey text with no indication of the typo.
for _, field in ipairs({ "requires", "requiredFor", "stepsRef", "partOf" }) do
local value = e[field]
local list = (type(value) == "table" and value)
or (type(value) == "string" and { value })
or nil
for _, target in ipairs(list or {}) do
if not byName[target] then
refs[#refs + 1] = ("%s: %s -> %q does not match any entry"):format(id, field, target)
end
end
end
for si, step in ipairs(e.steps or {}) do
if type(step.label) ~= "string" or step.label == "" then
schema[#schema + 1] = ("%s: step %d has no label"):format(id, si)
end
ForEachWaypoint(step, function(wp)
totalWaypoints = totalWaypoints + 1
if canCheckMaps and not C_Map.CanSetUserWaypointOnMap(wp.mapID) then
if not blockedMaps[wp.mapID] then
blockedMaps[wp.mapID] = true
local info = C_Map.GetMapInfo and C_Map.GetMapInfo(wp.mapID)
waypoints[#waypoints + 1] = ("%s: step %d -> mapID %d (%s) rejects user waypoints")
:format(id, si, wp.mapID, (info and info.name) or "unknown map")
end
end
end)
end
end
print(("|cffffcc00SecretChecklist:|r validating %d entries, %d waypoints..."):format(#entries, totalWaypoints))
local total = Report(schema, "Schema problems")
+ Report(refs, "Broken cross-references")
+ Report(waypoints, "Maps that reject Blizzard waypoints")
if total == 0 then
print("|cff00ff00SecretChecklist:|r data is clean.")
else
print(("|cffffcc00SecretChecklist:|r %d problem(s) found."):format(total))
end
if not canCheckMaps then
print("|cffffcc00SecretChecklist:|r C_Map.CanSetUserWaypointOnMap unavailable; waypoint maps not checked.")
end
return total
end