Skip to content

Commit 3156518

Browse files
authored
Merge pull request #1968 from LarryThiessen/fix-1967-corrupt-seq-tree-blank
FIX: don't blank the editor tree on a corrupt sequence; flag it instead
2 parents fcc13ba + 6e88439 commit 3156518

3 files changed

Lines changed: 121 additions & 14 deletions

File tree

GSE/API/Storage.lua

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,18 @@ function GSE.EnsureSequenceLoaded(classid, sequenceName)
221221
end
222222
end
223223

224+
-- ponytail: drop a seq from the corrupt list so the editor tree stops flagging
225+
-- it the moment it's deleted. Both delete paths call this.
226+
function GSE.ForgetCorruptSequence(classid, name)
227+
if type(GSE.CorruptSequences) ~= "table" then return end
228+
for i = #GSE.CorruptSequences, 1, -1 do
229+
local c = GSE.CorruptSequences[i]
230+
if c and tonumber(c.classid) == tonumber(classid) and c.name == name then
231+
table.remove(GSE.CorruptSequences, i)
232+
end
233+
end
234+
end
235+
224236
--- Remove a corrupt sequence from both compressed storage and the live library.
225237
function GSE.DeleteCorruptSequence(classid, name)
226238
if type(GSESequences) == "table" and type(GSESequences[classid]) == "table" then
@@ -229,6 +241,7 @@ function GSE.DeleteCorruptSequence(classid, name)
229241
if type(GSE.Library) == "table" and type(GSE.Library[classid]) == "table" then
230242
GSE.Library[classid][name] = nil
231243
end
244+
GSE.ForgetCorruptSequence(classid, name)
232245
GSE.Print(string.format(L["Corrupt sequence '%s' (class %d) deleted."], name, classid))
233246
end
234247

@@ -267,6 +280,7 @@ end
267280
function GSE.DeleteSequence(classid, sequenceName)
268281
GSE.Library[tonumber(classid)][sequenceName] = nil
269282
GSESequences[tonumber(classid)][sequenceName] = nil
283+
GSE.ForgetCorruptSequence(classid, sequenceName)
270284

271285
-- Remove any actionbar overrides that reference this sequence
272286
local overrideChanged = false

GSE_GUI/Editor_Tree.lua

Lines changed: 85 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -599,10 +599,35 @@ local function onRightClick_KEYBINDINGS(editframe, container, group, unique)
599599
)
600600
end
601601

602+
-- ponytail: true if a seq is corrupt/unusable — decode-broken (in
603+
-- GSE.CorruptSequences) or loaded-but-structurally-broken. Matches what the tree
604+
-- flags red; used to give a Delete-only right-click menu (the normal items call
605+
-- FindSequence, which DecodeMessages the broken data and errors).
606+
local function isBrokenSeq(classid, name)
607+
local cid = tonumber(classid)
608+
if not cid or GSE.isEmpty(name) then return false end
609+
for _, c in ipairs(GSE.CorruptSequences or {}) do
610+
if tonumber(c.classid) == cid and c.name == name then return true end
611+
end
612+
local libSeq = GSE.Library[cid] and GSE.Library[cid][name]
613+
return libSeq ~= nil and GSE.IsSequenceStructurallyBroken(libSeq)
614+
end
615+
602616
local function onRightClick_Sequences(editframe, container, group, unique, classid, sequencename)
603617
MenuUtil.CreateContextMenu(
604618
editframe.frame,
605619
function(ownerRegion, rootDescription)
620+
-- ponytail: a flagged corrupt/broken seq can't be edited/duplicated/
621+
-- exported (those call FindSequence -> DecodeMessage on broken data and
622+
-- crash). Offer Delete only.
623+
if not GSE.isEmpty(sequencename) and isBrokenSeq(classid, sequencename) then
624+
rootDescription:CreateTitle(L["Corrupt Sequence"])
625+
rootDescription:CreateButton(L["Delete"], function()
626+
editframe.GUIDeleteSequence(classid, sequencename)
627+
if editframe.ManageTree then editframe.ManageTree() end
628+
end)
629+
return
630+
end
606631
rootDescription:CreateTitle(L["Sequence Editor"])
607632
rootDescription:CreateButton(L["New"], function()
608633
if editframe.loaded then
@@ -1139,6 +1164,14 @@ end
11391164

11401165
local function onClick_Sequences(editframe, container, group, unique, path, key, classid, sequencename)
11411166
if #unique < 3 then return end
1167+
-- ponytail: never load a corrupt/broken seq into the editor — the decode
1168+
-- crashes (Serialisation DecodeMessage on unreadable data). Surface it and
1169+
-- stop; use right-click -> Delete to remove it.
1170+
if not GSE.isEmpty(sequencename) and isBrokenSeq(classid, sequencename) then
1171+
GSE.Print("The sequence '" .. tostring(sequencename) ..
1172+
"' is corrupt and cannot be opened. Right-click it and choose Delete.")
1173+
return
1174+
end
11421175
SaveLastSequenceEditorPath(group, unique)
11431176
ReleaseEditorFooterButtons(editframe)
11441177

@@ -1407,9 +1440,12 @@ local function ManageTree(editframe)
14071440
}
14081441

14091442
local classtree = {}
1443+
local seenSeq = {}
14101444
local names = GSE.GetSequenceNames()
14111445

14121446
for k, _ in GSE.pairsByKeys(names, GSE.AlphabeticalTableSortAlgorithm) do
1447+
-- ponytail: isolate each sequence so one corrupt record can't blank the whole tree
1448+
local ok, err = pcall(function()
14131449
local elements = GSE.split(k, ",")
14141450
local tclassid = tonumber(elements[1])
14151451
local specid = tonumber(elements[2])
@@ -1440,20 +1476,58 @@ local function ManageTree(editframe)
14401476

14411477
GSE.EnsureSequenceLoaded(tclassid, elements[3])
14421478
local loadedSeq = GSE.Library[tclassid] and GSE.Library[tclassid][elements[3]]
1443-
if loadedSeq then
1444-
for i, j in ipairs(loadedSeq["Versions"]) do
1445-
table.insert(node.children, {
1446-
value = i,
1447-
text = editframe.BuildVersionLabel(tostring(i), j.Label)
1448-
})
1479+
-- ponytail: flag ONLY when the record is actually in the Library and
1480+
-- structurally broken (the real corruption). loadedSeq==nil is NOT proof
1481+
-- of corruption — it usually just means the comma-split key didn't resolve
1482+
-- (e.g. a name that contains a comma), so flagging on nil would false-flag
1483+
-- a healthy seq. A flagged node gets red text + flag icon and no version /
1484+
-- "New Version" children (a click can't re-enter the broken editor);
1485+
-- right-click -> Delete still works (keyed by class+name, not Versions).
1486+
if loadedSeq and GSE.IsSequenceStructurallyBroken(loadedSeq) then
1487+
node.text = "|cFFFF3030" .. tostring(elements[3]) .. " |r"
1488+
node.icon = "Interface\\DialogFrame\\UI-Dialog-Icon-AlertNew" -- swap for any flag texture
1489+
else
1490+
if loadedSeq then
1491+
for i, j in ipairs(loadedSeq.Versions) do
1492+
table.insert(node.children, {
1493+
value = i,
1494+
text = editframe.BuildVersionLabel(tostring(i), j.Label)
1495+
})
1496+
end
14491497
end
1498+
table.insert(node.children, {
1499+
text = L["New"] .. " " .. L["Version"],
1500+
value = "newversion",
1501+
icon = Statics.ActionsIcons.Add
1502+
})
14501503
end
1451-
table.insert(node.children, {
1452-
text = L["New"] .. " " .. L["Version"],
1453-
value = "newversion",
1454-
icon = Statics.ActionsIcons.Add
1455-
})
14561504
table.insert(classtree[tclassid][specid], node)
1505+
seenSeq[tclassid .. "|" .. tostring(elements[3])] = true
1506+
end)
1507+
if not ok then
1508+
GSE.PrintDebugMessage("Skipped malformed sequence '" .. tostring(k) .. "': " .. tostring(err), "EDITOR")
1509+
end
1510+
end
1511+
1512+
-- ponytail: also surface load-corrupt seqs (the decode-broken ones behind the
1513+
-- corrupt-sequence popup) in the tree, red-flagged + deletable, so a user who
1514+
-- Skips/dismisses the popup can still find and remove them. They live in
1515+
-- GSE.CorruptSequences, not the Library, so the loop above never sees them.
1516+
for _, corrupt in ipairs(GSE.CorruptSequences or {}) do
1517+
local cid, cname = tonumber(corrupt.classid), corrupt.name
1518+
if cid and cname and not seenSeq[cid .. "|" .. cname] then
1519+
seenSeq[cid .. "|" .. cname] = true
1520+
classtree[cid] = classtree[cid] or {}
1521+
classtree[cid][0] = classtree[cid][0] or {}
1522+
table.insert(classtree[cid][0], {
1523+
value = cid .. ",0," .. cname .. ",0",
1524+
text = "|cFFFF3030" .. tostring(cname) .. " |r",
1525+
icon = "Interface\\DialogFrame\\UI-Dialog-Icon-AlertNew",
1526+
children = {
1527+
{ text = L["Configuration"], value = "config", icon = Statics.ActionsIcons.Settings }
1528+
}
1529+
})
1530+
end
14571531
end
14581532

14591533
local subtree = {

GSE_Utils/Utils.lua

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,18 @@ end
745745

746746
--- Inspects one sequence for structural and content issues.
747747
-- Returns a list of human-readable issue strings (empty = no problems).
748+
-- ponytail: the "unusable structure" subset of checkSeqStructure's early-return
749+
-- cases below. True = the editor can't load it AND /gse checksequencesforerrors
750+
-- can't repair it -> the tree flags it red for manual deletion. Benign notices
751+
-- (altered-from-export, missing SpecID) are NOT broken and stay unflagged.
752+
function GSE.IsSequenceStructurallyBroken(seq)
753+
if type(seq) ~= "table" then return true end
754+
if type(seq.MetaData) ~= "table" then return true end
755+
if seq.Macros ~= nil and seq.Versions == nil then return true end -- pre-#1853 schema
756+
if type(seq.Versions) ~= "table" then return true end
757+
return false
758+
end
759+
748760
local function checkSeqStructure(classlibid, seqname, seq) -- luacheck: ignore classlibid seqname
749761
local issues = {}
750762

@@ -1190,12 +1202,19 @@ function GSE.ProcessNextCorruptSequence()
11901202
end
11911203

11921204
--- Build the dialog queue from GSE.CorruptSequences and show the first dialog.
1193-
-- Drains the global list so repeated calls do not double-present the same entries.
1205+
-- ponytail: does NOT drain GSE.CorruptSequences anymore — the editor tree reads
1206+
-- that list to flag corrupt seqs, so a dismissed/Skipped popup still leaves them
1207+
-- findable. Dedup keeps the popup from double-presenting the same entry.
11941208
function GSE.ProcessCorruptSequences()
1209+
local queued = {}
1210+
for _, e in ipairs(corruptQueue) do queued[e.classid .. "|" .. e.name] = true end
11951211
for _, entry in ipairs(GSE.CorruptSequences or {}) do
1196-
table.insert(corruptQueue, entry)
1212+
local key = entry.classid .. "|" .. entry.name
1213+
if not queued[key] then
1214+
table.insert(corruptQueue, entry)
1215+
queued[key] = true
1216+
end
11971217
end
1198-
GSE.CorruptSequences = {}
11991218
if #corruptQueue > 0 then
12001219
GSE.Print(string.format(L["%d corrupt sequence(s) found \226\128\148 showing resolution options."], #corruptQueue))
12011220
GSE.ProcessNextCorruptSequence()

0 commit comments

Comments
 (0)