From 0156d9dfec06f4e1f73662d7289e151b62926843 Mon Sep 17 00:00:00 2001 From: Brues <5278969+brues-code@users.noreply.github.com> Date: Wed, 29 Jul 2026 10:36:26 -0500 Subject: [PATCH] Harden strsplit against third-party global clobbering BigWigs (SpellRequests) redefines the global string:split to return a table, and another addon clobbers the strsplit global too. pfUI.api.strsplit and the bare strsplit callers inherited the broken versions depending on load order, producing 'attempt to compare number with nil' from GetStringColor. - Make pfUI.api.strsplit fully self-contained (no delegation to global strsplit / string.split), so no override can reach it. - Optimize the hot path: single-char delimiters use plain-text find (no pattern compilation, no per-call char-class string); localize string.find and string.sub. - Route the per-frame castbar/nameplate color splits through GetStringColor, which caches, instead of re-splitting a constant string every update. --- api/api.lua | 23 +++++++++++------------ modules/castbar.lua | 8 +++----- modules/nameplates.lua | 2 +- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/api/api.lua b/api/api.lua index 40a62b7c..e49e5818 100644 --- a/api/api.lua +++ b/api/api.lua @@ -92,25 +92,24 @@ gfind = string.gmatch or string.gfind mod = math.mod or mod -- [ strsplit ] --- Splits a string using a delimiter. Thin wrapper that delegates to --- ClassicAPI's C-level strsplit, kept as a pfUI.api entry point for --- backwards compatibility with addons that call pfUI.api.strsplit. --- Note: unlike the old Lua implementation, empty fields are preserved --- (e.g. "a,,b" -> "a", "", "b"), matching real strsplit semantics. +-- Splits a string using a delimiter. Self-contained on purpose: it does NOT +-- delegate to the global strsplit / string.split, because third-party addons +-- clobber those (e.g. BigWigs' SpellRequests redefines string:split to return +-- a table, which would make this return a single table instead of r,g,b,a and +-- break color/version parsing depending on load order). Delimiter chars are +-- treated as a set (any one char splits), and empty fields are preserved +-- ("a,,b" -> "a", "", "b"), matching real strsplit semantics. -- 'delimiter' [string] characters that will be interpreted as delimiter -- characters (bytes) in the string. -- 'subject' [string] String to split. -- return: [list] a list of strings. -local stringsplit = _G.string.split +local format, sgsub = string.format, string.gsub function pfUI.api.strsplit(delimiter, subject) if not subject then return nil end - delimiter = delimiter or ":" - if stringsplit then - return stringsplit(delimiter, subject) - end local fields = {} - local pattern = string.format("([^%s]+)", delimiter) - string.gsub(subject, pattern, function(c) fields[table.getn(fields)+1] = c end) + delimiter = delimiter or ":" + local pattern = format("([^%s]+)", delimiter) + sgsub(subject, pattern, function(c) fields[table.getn(fields)+1] = c end) return unpack(fields) end diff --git a/modules/castbar.lua b/modules/castbar.lua index 1c6acfc9..8cc13074 100644 --- a/modules/castbar.lua +++ b/modules/castbar.lua @@ -90,7 +90,7 @@ pfUI:RegisterModule("castbar", function () cb:SetAlpha(1) cb.fadeout = nil - cb.bar:SetStatusBarColor(strsplit(",", C.appearance.castbar[isChannel and "channelcolor" or "castbarcolor"])) + cb.bar:SetStatusBarColor(GetStringColor(C.appearance.castbar[isChannel and "channelcolor" or "castbarcolor"])) local rank = "" if spellID then @@ -103,8 +103,7 @@ pfUI:RegisterModule("castbar", function () if tex and cb.showicon then local size = cb:GetHeight() cb.icon:Show() - cb.icon:SetHeight(size) - cb.icon:SetWidth(size) + cb.icon:SetSize(size, size) cb.icon.texture:SetTexture(tex) cb.bar:SetPoint("TOPLEFT", cb.icon, "TOPRIGHT", cb.spacing, 0) else @@ -169,8 +168,7 @@ pfUI:RegisterModule("castbar", function () -- icon cb.icon = CreateFrame("Frame", nil, cb) cb.icon:SetPoint("TOPLEFT", 0, 0) - cb.icon:SetHeight(16) - cb.icon:SetWidth(16) + cb.icon:SetSize(16, 16) cb.icon.texture = cb.icon:CreateTexture(nil, "OVERLAY") cb.icon.texture:SetAllPoints() diff --git a/modules/nameplates.lua b/modules/nameplates.lua index d1baf17d..c2c0f1f4 100644 --- a/modules/nameplates.lua +++ b/modules/nameplates.lua @@ -1570,7 +1570,7 @@ nameplates:RegisterEvent("UNIT_FLAGS") -- Relative 0..duration range to avoid float precision loss with large -- absolute timestamps. nameplate.castbar:SetMinMaxValues(0, duration) - nameplate.castbar:SetStatusBarColor(strsplit(",", C.appearance.castbar[(isChannel and "channelcolor" or "castbarcolor")])) + nameplate.castbar:SetStatusBarColor(GetStringColor(C.appearance.castbar[(isChannel and "channelcolor" or "castbarcolor")])) if castInfo.icon then nameplate.castbar.icon.tex:SetTexture(castInfo.icon) nameplate.castbar.icon.tex:SetTexCoord(.1,.9,.1,.9)