From d91975dac45a1d6837df4ab2ad1fa33aa1d9f11a Mon Sep 17 00:00:00 2001 From: unrealdreamz <132005717+unrealdreamz@users.noreply.github.com> Date: Mon, 18 May 2026 21:01:35 -0400 Subject: [PATCH 1/2] Ignore stale jewel socket item ids --- spec/System/TestPassiveSpec_spec.lua | 46 ++++++++++++++++++++++++++++ src/Classes/PassiveSpec.lua | 17 +++++----- 2 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 spec/System/TestPassiveSpec_spec.lua diff --git a/spec/System/TestPassiveSpec_spec.lua b/spec/System/TestPassiveSpec_spec.lua new file mode 100644 index 0000000000..ac402294a6 --- /dev/null +++ b/spec/System/TestPassiveSpec_spec.lua @@ -0,0 +1,46 @@ +describe("TestPassiveSpec", function() + before_each(function() + newBuild() + end) + + local function firstLoadedSocketNode(spec) + for nodeId in pairs(spec.tree.sockets) do + if spec.nodes[nodeId] then + return nodeId + end + end + end + + it("ignores stale jewel socket item ids when loading saved builds", function() + local spec = new("PassiveSpec", build, latestTreeVersion) + local socketNodeId = firstLoadedSocketNode(spec) + + spec:Load({ + attrib = { title = "Stale Socket Test" }, + { + elem = "Sockets", + { + elem = "Socket", + attrib = { + nodeId = tostring(socketNodeId), + itemId = "999999", + } + } + } + }, "stale_socket.xml") + + assert.is_nil(spec.jewels[socketNodeId]) + end) + + it("does not crash when radius helpers see a stale jewel socket item id", function() + local spec = new("PassiveSpec", build, latestTreeVersion) + local socketNodeId = firstLoadedSocketNode(spec) + spec.jewels[socketNodeId] = 999999 + + local ok, err = pcall(function() + return spec:NodesInIntuitiveLeapLikeRadius(spec.nodes[socketNodeId]) + end) + + assert.is_true(ok, err) + end) +end) diff --git a/src/Classes/PassiveSpec.lua b/src/Classes/PassiveSpec.lua index 294782679c..fb0cef12d1 100644 --- a/src/Classes/PassiveSpec.lua +++ b/src/Classes/PassiveSpec.lua @@ -117,11 +117,14 @@ function PassiveSpecClass:Load(xml, dbFileName) launch:ShowErrMsg("^1Error parsing '%s': 'Socket' element missing 'itemId' attribute", dbFileName) return true end - -- there are files which have been saved poorly and have empty jewel sockets saved as sockets with itemId zero. - -- this check filters them out to prevent dozens of invalid jewels - jewelIdNum = tonumber(child.attrib.itemId) - if jewelIdNum > 0 then - self.jewels[tonumber(child.attrib.nodeId)] = jewelIdNum + -- Some saved builds contain stale jewel socket item IDs after the + -- referenced item has been removed from the item list. Keep those + -- broken references out of the active tree state so later radius and + -- loadout code doesn't try to index a missing item. + local jewelIdNum = tonumber(child.attrib.itemId) + local nodeIdNum = tonumber(child.attrib.nodeId) + if nodeIdNum and jewelIdNum and jewelIdNum > 0 and self.build.itemsTab.items[jewelIdNum] then + self.jewels[nodeIdNum] = jewelIdNum end end end @@ -1075,9 +1078,9 @@ end function PassiveSpecClass:NodesInIntuitiveLeapLikeRadius(node) local result = { } if self.jewels[node.id] and self.jewels[node.id] > 0 then - local item = self.build.itemsTab.items[self.jewels[node.id]] - local radiusIndex = item.jewelRadiusIndex + local item = self:GetJewel(self.jewels[node.id]) if item and item.jewelData and item.jewelData.intuitiveLeapLike then + local radiusIndex = item.jewelRadiusIndex local inRadius = self.nodes[node.id].nodesInRadius and self.nodes[node.id].nodesInRadius[radiusIndex] for affectedNodeId, affectedNode in pairs(inRadius or {}) do if self.nodes[affectedNodeId].alloc then From 7042a2dcaed16a0faa2f1c6ec3b029fc34cbc057 Mon Sep 17 00:00:00 2001 From: LocalIdentity Date: Thu, 21 May 2026 19:45:18 +1000 Subject: [PATCH 2/2] Fix test --- src/Classes/PassiveSpec.lua | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Classes/PassiveSpec.lua b/src/Classes/PassiveSpec.lua index fb0cef12d1..bb96c912e4 100644 --- a/src/Classes/PassiveSpec.lua +++ b/src/Classes/PassiveSpec.lua @@ -1079,8 +1079,11 @@ function PassiveSpecClass:NodesInIntuitiveLeapLikeRadius(node) local result = { } if self.jewels[node.id] and self.jewels[node.id] > 0 then local item = self:GetJewel(self.jewels[node.id]) - if item and item.jewelData and item.jewelData.intuitiveLeapLike then - local radiusIndex = item.jewelRadiusIndex + if not item then + return result + end + local radiusIndex = item.jewelRadiusIndex + if item.jewelData.intuitiveLeapLike then local inRadius = self.nodes[node.id].nodesInRadius and self.nodes[node.id].nodesInRadius[radiusIndex] for affectedNodeId, affectedNode in pairs(inRadius or {}) do if self.nodes[affectedNodeId].alloc then