From 46a407a5ee4c143056901475bc0bbf03997fdf6e Mon Sep 17 00:00:00 2001 From: Dominick Sidiropoulos Date: Sun, 15 Feb 2026 21:36:11 +0100 Subject: [PATCH 1/6] clean (focus.lua): remove redundant parameter from SlashCmdList.PFSWAPFOCUS() --- modules/focus.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/focus.lua b/modules/focus.lua index a2e245827..7621c9627 100644 --- a/modules/focus.lua +++ b/modules/focus.lua @@ -95,7 +95,7 @@ function SlashCmdList.PFCASTFOCUS(msg) end SLASH_PFSWAPFOCUS1, SLASH_PFSWAPFOCUS2 = '/swapfocus', '/pfswapfocus' -function SlashCmdList.PFSWAPFOCUS(msg) +function SlashCmdList.PFSWAPFOCUS() if not pfUI.uf or not pfUI.uf.focus then return end local oldunit = UnitExists("target") and strlower(UnitName("target")) From 95803f275b14cc71298d8ca559edded4e91c2d28 Mon Sep 17 00:00:00 2001 From: Dominick Sidiropoulos Date: Sun, 15 Feb 2026 21:38:48 +0100 Subject: [PATCH 2/6] clean (api.lua): trivial neutral cleanups in pfUI.api.isempty() and pfUI.api.RunOOC() to resolve some static-analysis warnings about var-names --- api/api.lua | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/api/api.lua b/api/api.lua index e4a944026..e1a22a44e 100644 --- a/api/api.lua +++ b/api/api.lua @@ -27,7 +27,7 @@ end -- return: [boolean] result of the check. function pfUI.api.isempty(tbl) if not tbl then return true end - for k, v in pairs(tbl) do + for _ in pairs(tbl) do return false end return true @@ -80,8 +80,12 @@ function pfUI.api.RunOOC(func) if not frame then frame = CreateFrame("Frame") frame:SetScript("OnUpdate", function() - if InCombatLockdown and InCombatLockdown() then return end - for key, func in pairs(queue) do func(); queue[key] = nil end + if InCombatLockdown and InCombatLockdown() then return end + + for k, f in pairs(queue) do + f() + queue[k] = nil + end end) end From fc0c303bc06f426606d71ebe8f17bc45bf1f2875 Mon Sep 17 00:00:00 2001 From: Dominick Sidiropoulos Date: Sun, 15 Feb 2026 21:39:46 +0100 Subject: [PATCH 3/6] feat (api.lua): introduce TryMemoizedFuncLoadstringForSpellCasts() which is meant to be used in order to memoize lua-func-strings passed on to various spell-casting utilities in the next few commits (/pfcast, /castfocus etc) --- api/api.lua | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/api/api.lua b/api/api.lua index e1a22a44e..03a200c96 100644 --- a/api/api.lua +++ b/api/api.lua @@ -7,6 +7,62 @@ setfenv(1, pfUI:GetEnvironment()) gfind = string.gmatch or string.gfind mod = math.mod or mod +local loadstring_cache = {} +local loadstring_cache_size = 0 +local LOADSTRING_CACHE_MAX_SIZE = 1024 -- in practice such a max-size is more than enough we will rarely have to wipe the cache during the session +-- [ TryMemoizedFuncLoadstringForSpellCasts ] +-- Returns a function for the given string if the given msg is a string and a valid +-- lua-function can indeed be loaded from it, otherwise nil. +-- +-- If msg is an actual raw-lua-function is passed it is returned as-is without any processing. +-- This allows to use both raw functions and string-based scriptlets with the same API. +-- +-- If msg is any other type (number, boolean, ...) then nil is returned. +-- +-- The function is memoized for better performance on repeated calls with the same string. +-- +-- The cache is automatically cleared when it exceeds a certain size (1024 entries) to prevent abuse. +-- +-- In practice it is very unlikely to have more than a few dozen different scriptlets during a session, +-- so cache-nuking should be a very rare event. +function pfUI.api.TryMemoizedFuncLoadstringForSpellCasts(msg) + local msgType = type(msg) + if msgType == "function" then --10 order + return msg -- return as-is + end + + if msgType ~= "string" then --20 order + return nil -- numbers and the like are not valid for loadstring + end + + local result = loadstring_cache[msg] + if result ~= nil then -- already seen? + return result + end + + result = loadstring(msg) + if result == nil then -- invalid code + return nil + end + + if loadstring_cache_size > LOADSTRING_CACHE_MAX_SIZE then --90 just in case + loadstring_cache = {} + loadstring_cache_size = 0 + end + + loadstring_cache[msg] = result -- order + loadstring_cache_size = loadstring_cache_size + 1 -- order + + return result + + --10 special cases for when using /pfcast with funcs or scriptlets if a raw func is passed we dont need to + -- to loadstring it all + -- + --20 if the user passes a direct integer-spell-id or something else, we should not attempt to loadstring it + -- + --90 prevent abuse by capping the cache size +end + -- [ strsplit ] -- Splits a string using a delimiter. -- 'delimiter' [string] characters that will be interpreted as delimiter From 0018083805afea2951535daea47512f65fc57f81 Mon Sep 17 00:00:00 2001 From: Dominick Sidiropoulos Date: Sun, 15 Feb 2026 21:43:10 +0100 Subject: [PATCH 4/6] perf (superwow.lua): add support for superwow-aware SlashCmdList.PFCASTFOCUS() just like we did with SlashCmdList.PFFOCUS --- modules/superwow.lua | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/modules/superwow.lua b/modules/superwow.lua index af30210db..a29fbc527 100644 --- a/modules/superwow.lua +++ b/modules/superwow.lua @@ -194,6 +194,34 @@ pfUI:RegisterModule("superwow", "vanilla", function () return guid end + -- optimize the builtin /castfocus and /pfcastfocus slash commands when possible by using superwow + -- to cast directly to the focus-target-unit-guid thus skipping the need for complex target-swapping + local legacy_cast_focus = SlashCmdList.PFCASTFOCUS + function SlashCmdList.PFCASTFOCUS(msg) + local func = pfUI.api.TryMemoizedFuncLoadstringForSpellCasts(msg) --10 caution + if func then --10 caution + legacy_cast_focus(func) + return + end + + if not pfUI.uf.focus.label then --50 + UIErrorsFrame:AddMessage(SPELL_FAILED_BAD_TARGETS, 1, 0, 0) + return + end + + CastSpellByName(msg, pfUI.uf.focus.label) --90 + + --10 if the spellcast is in fact raw lua-function we cant cast by guid we have to fallback + -- to the legacy method which does support func-scriptlets + -- + --50 the superwow-approach requires just the unit-guid-label it doesnt care about the focus.id + -- which is typically dud anyway + -- + --90 by using superwow to cast directly to a unit-guid we completely sidestep the complex mechanics + -- of target-swapping altogether which is the entire point here for vastly improved ui-performance + -- when spamming spells + end + -- extend the builtin /focus slash command local legacyfocus = SlashCmdList.PFFOCUS function SlashCmdList.PFFOCUS(msg) From 747a47adfe75dc514bc7ae32ed3d9c8977036abd Mon Sep 17 00:00:00 2001 From: Dominick Sidiropoulos Date: Sun, 15 Feb 2026 21:45:34 +0100 Subject: [PATCH 5/6] perf (focus.lua, mouseover.lua, superwow.lua): replace loadstring() calls in _G.SlashCmdList.PFCAST() and SlashCmdList.PFCASTFOCUS() with pfUI.api.TryMemoizedFuncLoadstringForSpellCasts() to enjoy better runtime performance when the user passes a lua-func-string --- modules/focus.lua | 2 +- modules/mouseover.lua | 2 +- modules/superwow.lua | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/modules/focus.lua b/modules/focus.lua index 7621c9627..2857f4cbc 100644 --- a/modules/focus.lua +++ b/modules/focus.lua @@ -77,7 +77,7 @@ function SlashCmdList.PFCASTFOCUS(msg) end end - local func = loadstring(msg or "") + local func = pfUI.api.TryMemoizedFuncLoadstringForSpellCasts(msg) if func then func() else diff --git a/modules/mouseover.lua b/modules/mouseover.lua index 918a39768..c33a6b5b8 100644 --- a/modules/mouseover.lua +++ b/modules/mouseover.lua @@ -34,7 +34,7 @@ pfUI:RegisterModule("mouseover", "vanilla", function () _G.SLASH_PFCAST1, _G.SLASH_PFCAST2 = "/pfcast", "/pfmouse" function SlashCmdList.PFCAST(msg) local restore_target = true - local func = loadstring(msg or "") + local func = pfUI.api.TryMemoizedFuncLoadstringForSpellCasts(msg) local unit = "mouseover" if not UnitExists(unit) then diff --git a/modules/superwow.lua b/modules/superwow.lua index a29fbc527..e0008b0b4 100644 --- a/modules/superwow.lua +++ b/modules/superwow.lua @@ -49,7 +49,7 @@ pfUI:RegisterModule("superwow", "vanilla", function () -- Add native mouseover support if SUPERWOW_VERSION and pfUI.uf and pfUI.uf.mouseover then _G.SlashCmdList.PFCAST = function(msg) - local func = loadstring(msg or "") + local func = pfUI.api.TryMemoizedFuncLoadstringForSpellCasts(msg) local unit = "mouseover" if not UnitExists(unit) then From 8941ae2b33fc58fd135e92120a3b72dcac2c1f65 Mon Sep 17 00:00:00 2001 From: Dominick Sidiropoulos Date: Sun, 15 Feb 2026 21:47:44 +0100 Subject: [PATCH 6/6] clean (superwow.lua): trivial neutral cleanups to get rid of unused variables --- modules/superwow.lua | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/superwow.lua b/modules/superwow.lua index e0008b0b4..7d71c32cc 100644 --- a/modules/superwow.lua +++ b/modules/superwow.lua @@ -36,7 +36,7 @@ pfUI:RegisterModule("superwow", "vanilla", function () QueueFunction(function() local pfCombatText_AddMessage = _G.CombatText_AddMessage _G.CombatText_AddMessage = function(message, a, b, c, d, e, f) - local match, _, hex = string.find(message, ".+ %[(0x.+)%]") + local _, _, hex = string.find(message, ".+ %[(0x.+)%]") if hex and UnitName(hex) then message = string.gsub(message, hex, UnitName(hex)) end @@ -90,7 +90,7 @@ pfUI:RegisterModule("superwow", "vanilla", function () local config = pfUI.uf.player.config local mana = config.defcolor == "0" and config.manacolor or pfUI_config.unitframes.manacolor local r, g, b, a = pfUI.api.strsplit(",", mana) - local rawborder, default_border = GetBorderSize("unitframes") + local _, default_border = GetBorderSize("unitframes") local _, class = UnitClass("player") local width = config.pwidth ~= "-1" and config.pwidth or config.width @@ -257,7 +257,7 @@ pfUI:RegisterModule("superwow", "vanilla", function () superdebuff:RegisterEvent("UNIT_CASTEVENT") superdebuff:SetScript("OnEvent", function() -- variable assignments - local caster, target, event, spell, duration = arg1, arg2, arg3, arg4 + local caster, target, event, spell = arg1, arg2, arg3 -- skip other caster and empty target events local _, guid = UnitExists("player") @@ -270,7 +270,7 @@ pfUI:RegisterModule("superwow", "vanilla", function () local unitlevel = UnitLevel(target) local effect, rank = SpellInfo(spell) local duration = libdebuff:GetDuration(effect, rank) - local caster = "player" + caster = "player" -- add effect to current debuff data libdebuff:AddEffect(unit, unitlevel, effect, duration, caster) @@ -306,11 +306,11 @@ pfUI:RegisterModule("superwow", "vanilla", function () if arg3 == "START" or arg3 == "CAST" or arg3 == "CHANNEL" then -- human readable argument list local guid = arg1 - local target = arg2 + -- local target = arg2 local event_type = arg3 local spell_id = arg4 local timer = arg5 - local start = GetTime() + -- local start = GetTime() -- get spell info from spell id local spell, icon, _