From 1f4781165f29ec0adfbd64fa239255c7c236c96d Mon Sep 17 00:00:00 2001 From: Jrc13245 Date: Thu, 27 Nov 2025 21:01:25 -0500 Subject: [PATCH] fix channeltime --- Conditionals.lua | 44 +++++++- Core.lua | 154 +++++++++++++++++++++++++- Extensions/MacroLengthWarn.lua | 196 ++++++++++++++++++++++++++++++--- 3 files changed, 372 insertions(+), 22 deletions(-) diff --git a/Conditionals.lua b/Conditionals.lua index aae5dd1..b40b3f8 100644 --- a/Conditionals.lua +++ b/Conditionals.lua @@ -1978,18 +1978,56 @@ CleveRoids.Keywords = { end, channeltime = function(conditionals) - local playerCast = CleveRoids.spell_tracking[CleveRoids.playerGuid] + -- Try both "player" literal and playerGuid for tracking lookup + local playerCast = CleveRoids.spell_tracking["player"] or CleveRoids.spell_tracking[CleveRoids.playerGuid] if not playerCast or playerCast.type ~= "CHANNEL" then + if CleveRoids.ChannelTimeDebug then + if not playerCast then + DEFAULT_CHAT_FRAME:AddMessage("|cffffff00[channeltime]|r No tracking data for player or playerGuid: " .. tostring(CleveRoids.playerGuid)) + -- Show what keys exist + local keys = "" + for k,v in pairs(CleveRoids.spell_tracking) do + keys = keys .. tostring(k) .. " " + end + if keys ~= "" then + DEFAULT_CHAT_FRAME:AddMessage("|cffffff00[channeltime]|r Tracking keys: " .. keys) + end + else + DEFAULT_CHAT_FRAME:AddMessage("|cffffff00[channeltime]|r Wrong type: " .. tostring(playerCast.type)) + end + end return false end + -- FOUND TRACKING - Debug this path too! local timeLeft = playerCast.expires - GetTime() local check = conditionals.channeltime - if type(check) == "table" and check.operator and check.amount then - return CleveRoids.comparators[check.operator](timeLeft, check.amount) + -- channeltime is stored as an array by the parser, get the first element + if type(check) == "table" and type(check[1]) == "table" then + check = check[1] end + if CleveRoids.ChannelTimeDebug then + DEFAULT_CHAT_FRAME:AddMessage(string.format("|cff00ff00[channeltime FOUND]|r expires=%.2f, timeLeft=%.2f", playerCast.expires, timeLeft)) + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[channeltime FOUND]|r check type=" .. type(check) .. ", value=" .. tostring(check)) + if type(check) == "table" then + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[channeltime FOUND]|r operator=" .. tostring(check.operator) .. ", amount=" .. tostring(check.amount)) + end + end + + if type(check) == "table" and check.operator and check.amount then + local result = CleveRoids.comparators[check.operator](timeLeft, check.amount) + if CleveRoids.ChannelTimeDebug then + DEFAULT_CHAT_FRAME:AddMessage(string.format("|cff00ff00[channeltime RESULT]|r %.2fs %s %.2f = %s", + timeLeft, check.operator, check.amount, result and "TRUE" or "FALSE")) + end + return result + end + + if CleveRoids.ChannelTimeDebug then + DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[channeltime ERROR]|r check structure invalid, returning false") + end return false end, diff --git a/Core.lua b/Core.lua index 01e9faf..b587385 100644 --- a/Core.lua +++ b/Core.lua @@ -1693,11 +1693,20 @@ function CleveRoids.DoWithConditionals(msg, hook, fixEmptyTargetFunc, targetBefo end function CleveRoids.DoCast(msg) + if CleveRoids.ChannelTimeDebug then + if msg and string.find(msg, "Arcane") then + DEFAULT_CHAT_FRAME:AddMessage("|cffff00ff[/cast BUTTON PRESS]|r " .. msg) + end + end + local handled = false for k, v in pairs(CleveRoids.splitStringIgnoringQuotes(msg)) do -- Define a custom action that handles both regular and pet spells local castAction = function(spellName) + if CleveRoids.ChannelTimeDebug and string.find(v, "channeltime") then + DEFAULT_CHAT_FRAME:AddMessage("|cffff00ff[/cast]|r Processing: " .. v) + end -- First try regular spell local spell = CleveRoids.GetSpell(spellName) if spell then @@ -3012,11 +3021,11 @@ end function CleveRoids.Frame:ADDON_LOADED(addon) -- keep your existing init for CRM: - if addon == "CleveRoidMacros" then + if addon == "CleveRoidMacros" or addon == "SuperCleveRoidMacros" then CleveRoids.InitializeExtensions() end -- (re)attempt hook when either addon arrives - if addon == "SuperMacro" or addon == "CleveRoidMacros" then + if addon == "SuperMacro" or addon == "CleveRoidMacros" or addon == "SuperCleveRoidMacros" then CRM_SM_InstallHook() end end @@ -3024,17 +3033,54 @@ end function CleveRoids.Frame:UNIT_CASTEVENT(caster,target,action,spell_id,cast_time) if action == "MAINHAND" or action == "OFFHAND" then return end + -- Debug channel tracking + if CleveRoids.ChannelTimeDebug then + local spellName = spell_id and SpellInfo and SpellInfo(spell_id) or "Unknown" + if string.find(spellName, "Arcane") then + DEFAULT_CHAT_FRAME:AddMessage(string.format("|cff00ff00[UNIT_CASTEVENT]|r %s: %s (ID:%s) caster=%s player=%s", + action, spellName, tostring(spell_id), tostring(caster), tostring(CleveRoids.playerGuid))) + end + end + -- handle cast spell tracking local cast = CleveRoids.spell_tracking[caster] if cast_time > 0 and action == "START" or action == "CHANNEL" then CleveRoids.spell_tracking[caster] = { spell_id = spell_id, expires = GetTime() + cast_time/1000, type = action } + + -- ALSO store under "player" literal for easier lookup + if caster == CleveRoids.playerGuid then + CleveRoids.spell_tracking["player"] = CleveRoids.spell_tracking[caster] + end + + if CleveRoids.ChannelTimeDebug and caster == CleveRoids.playerGuid then + DEFAULT_CHAT_FRAME:AddMessage(string.format("|cff00ff00[Tracking]|r Set spell_tracking[%s] AND [player]: type=%s, expires=%.2f", + tostring(caster), action, GetTime() + cast_time/1000)) + end + -- Always show for channels if debug is on + if CleveRoids.ChannelTimeDebug and action == "CHANNEL" then + DEFAULT_CHAT_FRAME:AddMessage(string.format("|cff00ff00[Tracking]|r caster=%s, playerGuid=%s, match=%s", + tostring(caster), tostring(CleveRoids.playerGuid), tostring(caster == CleveRoids.playerGuid))) + end elseif cast and ( (cast.spell_id == spell_id and (action == "FAIL" or action == "CAST")) or (GetTime() > cast.expires) ) then + if CleveRoids.ChannelTimeDebug and caster == CleveRoids.playerGuid then + local reason = "" + if cast.spell_id == spell_id and (action == "FAIL" or action == "CAST") then + reason = string.format("spell_id match (%s) and action=%s", tostring(spell_id), action) + elseif GetTime() > cast.expires then + reason = string.format("expired (%.2f > %.2f)", GetTime(), cast.expires) + end + DEFAULT_CHAT_FRAME:AddMessage(string.format("|cffff0000[Tracking]|r CLEARING spell_tracking - %s", reason)) + end CleveRoids.spell_tracking[caster] = nil + -- Also clear "player" literal if this is the player + if caster == CleveRoids.playerGuid then + CleveRoids.spell_tracking["player"] = nil + end end -- handle cast sequence (SuperWoW) @@ -3468,6 +3514,8 @@ SlashCmdList["CLEVEROID"] = function(msg) DEFAULT_CHAT_FRAME:AddMessage("/cleveroid forget - Forget learned duration(s)") DEFAULT_CHAT_FRAME:AddMessage("/cleveroid debug [0|1] - Toggle learning debug messages") end + DEFAULT_CHAT_FRAME:AddMessage("/cleveroid macrodebug - Toggle macro length warning debug") + DEFAULT_CHAT_FRAME:AddMessage("/cleveroid macrostatus - Check macro length warning status") DEFAULT_CHAT_FRAME:AddMessage("|cffffaa00Immunity Tracking:|r") DEFAULT_CHAT_FRAME:AddMessage('/cleveroid listimmune [school] - List immunity data') DEFAULT_CHAT_FRAME:AddMessage('/cleveroid addimmune "" [buff] - Add immunity') @@ -3491,6 +3539,8 @@ SlashCmdList["CLEVEROID"] = function(msg) DEFAULT_CHAT_FRAME:AddMessage('/cleveroid clearproc [spell|all] - Clear reactive proc(s)') DEFAULT_CHAT_FRAME:AddMessage("|cffffaa00Casting Detection:|r") DEFAULT_CHAT_FRAME:AddMessage('/cleveroid testcasting - Test [selfcasting]/[noselfcasting] conditionals') + DEFAULT_CHAT_FRAME:AddMessage('/cleveroid testchannel - Test [channeltime] conditional tracking') + DEFAULT_CHAT_FRAME:AddMessage('/cleveroid channeldebug - Toggle [channeltime] conditional debug output') return end @@ -3580,6 +3630,106 @@ SlashCmdList["CLEVEROID"] = function(msg) return end + -- macrodebug (toggle macro length warning debug messages) + if cmd == "macrodebug" then + CleveRoids.MacroLengthDebug = not CleveRoids.MacroLengthDebug + CleveRoids.Print("Macro length warning debug " .. (CleveRoids.MacroLengthDebug and "enabled" or "disabled")) + return + end + + -- macrostatus (check macro length warning status) + if cmd == "macrostatus" then + CleveRoids.Print("|cff00ff00=== MacroLengthWarn Status ===|r") + + -- Check if file loaded + if CleveRoids.MacroLengthWarnLoaded then + CleveRoids.Print("File: |cff00ff00LOADED|r") + else + CleveRoids.Print("File: |cffff0000NOT LOADED|r - Check for Lua errors!") + end + + -- Check if extension is registered + local ext = CleveRoids.Extensions and CleveRoids.Extensions["MacroLengthWarn"] + if ext then + CleveRoids.Print("Extension: |cff00ff00REGISTERED|r") + + -- Check if OnLoad was called + if ext.ShowMessages then + CleveRoids.Print("OnLoad: |cff00ff00CALLED|r") + + -- Call the status function + ext.ShowMessages() + else + CleveRoids.Print("OnLoad: |cffff0000NOT CALLED|r") + end + else + CleveRoids.Print("Extension: |cffff0000NOT REGISTERED|r") + CleveRoids.Print("The MacroLengthWarn extension failed to register!") + end + + -- Check key functions + CleveRoids.Print(" ") + CleveRoids.Print("Function Status:") + CleveRoids.Print(" EditMacro: " .. (EditMacro and "|cff00ff00EXISTS|r" or "|cffff0000MISSING|r")) + CleveRoids.Print(" MacroFrame_SaveMacro: " .. (MacroFrame_SaveMacro and "|cff00ff00EXISTS|r" or "|cffffff00NOT LOADED YET|r")) + + return + end + + -- channeldebug (toggle channeltime conditional debug) + if cmd == "channeldebug" then + CleveRoids.ChannelTimeDebug = not CleveRoids.ChannelTimeDebug + CleveRoids.Print("Channel time debug " .. (CleveRoids.ChannelTimeDebug and "enabled" or "disabled")) + if CleveRoids.ChannelTimeDebug then + CleveRoids.Print("You will see messages every time [channeltime] is evaluated") + CleveRoids.Print("This shows if your macro is re-evaluating during the channel") + end + return + end + + -- testchannel (debug channel time detection) + if cmd == "testchannel" or cmd == "channeltest" then + CleveRoids.Print("|cff00ff00=== Channel Time Test ===|r") + + -- Check spell tracking + local playerCast = CleveRoids.spell_tracking[CleveRoids.playerGuid] + if not playerCast then + CleveRoids.Print("|cffffff00No spell tracking data for player|r") + CleveRoids.Print("You must be casting or channeling a spell for this to show data") + else + CleveRoids.Print("Spell tracking found:") + CleveRoids.Print(" Type: " .. tostring(playerCast.type)) + CleveRoids.Print(" Spell ID: " .. tostring(playerCast.spell_id)) + if playerCast.spell_id and SpellInfo then + local spellName = SpellInfo(playerCast.spell_id) + CleveRoids.Print(" Spell Name: " .. tostring(spellName)) + end + if playerCast.expires then + local timeLeft = playerCast.expires - GetTime() + CleveRoids.Print(" Expires at: " .. tostring(playerCast.expires)) + CleveRoids.Print(" Time left: " .. string.format("%.2f", timeLeft) .. "s") + + -- Test conditionals + CleveRoids.Print(" ") + CleveRoids.Print("Conditional tests:") + CleveRoids.Print(" [channeltime:<0.5]: " .. (timeLeft < 0.5 and "|cff00ff00TRUE|r" or "|cffff0000FALSE|r")) + CleveRoids.Print(" [channeltime:<1.0]: " .. (timeLeft < 1.0 and "|cff00ff00TRUE|r" or "|cffff0000FALSE|r")) + CleveRoids.Print(" [channeltime:>2.0]: " .. (timeLeft > 2.0 and "|cff00ff00TRUE|r" or "|cffff0000FALSE|r")) + else + CleveRoids.Print(" Expires: NOT SET") + end + end + + CleveRoids.Print(" ") + CleveRoids.Print("|cff00ffffInstructions:|r") + CleveRoids.Print("1. Start channeling Arcane Missiles") + CleveRoids.Print("2. Run /cleveroid testchannel while channeling") + CleveRoids.Print("3. Check if spell tracking is working") + + CleveRoids.Print("|cff00ff00=== End Test ===|r") + return + end + -- testcasting (debug casting state detection) if cmd == "testcasting" or cmd == "casttest" then CleveRoids.Print("|cff00ff00=== Casting State Test ===|r") diff --git a/Extensions/MacroLengthWarn.lua b/Extensions/MacroLengthWarn.lua index cf17f1d..e55bfe8 100644 --- a/Extensions/MacroLengthWarn.lua +++ b/Extensions/MacroLengthWarn.lua @@ -4,38 +4,200 @@ ]] local _G = _G or getfenv(0) local CleveRoids = _G.CleveRoids or {} --- CleveRoids.mouseoverUnit = CleveRoids.mouseoverUnit or nil local Extension = CleveRoids.RegisterExtension("MacroLengthWarn") -local edit_orig = EditMacro +-- Set a global flag so we can check if this file loaded +CleveRoids.MacroLengthWarnLoaded = true -function Extension.SafeEditMacro(macro_id, x, y, body) - if SuperMacroFrame ~= nil and x and y and body == nil then - -- super will handle macro - edit_orig(macro_id, x, y, body) - return - end +-- Maximum line length for macros (including spaces) +local MAX_LINE_LENGTH = 261 - if not body then -- can't be too long without a body - edit_orig(macro_id, x, y, body) - return +-- Store original functions +local edit_orig = nil +local macroframe_save_orig = nil + +-- Validation function - returns true if valid, false if too long +local function ValidateMacroBody(body, macroName) + if not body then + return true -- No body means it's valid end for line in string.gfind(body, "([^\n]+)") do - if string.len(line) > 261 then - DEFAULT_CHAT_FRAME:AddMessage("ERROR: A line in the macro < |cffffffff"..GetMacroInfo(macro_id).."|r > is too long and will |cffff0000CRASH|r the client and may |cffff0000DELETE|r all macros on exit! Max line length: |cffffffff261|r",1,1,0) + local lineLen = string.len(line) + if lineLen > MAX_LINE_LENGTH then + local name = macroName or "Unknown" + DEFAULT_CHAT_FRAME:AddMessage(string.format( + "|cffff0000ERROR:|r A line in the macro < |cffffffff%s|r > is |cffff0000%d characters|r long (max: |cffffffff%d|r).", + name, lineLen, MAX_LINE_LENGTH + ), 1, 0.82, 0) + DEFAULT_CHAT_FRAME:AddMessage("|cffff0000This will CRASH the client and may DELETE all macros!|r Macro NOT saved.", 1, 0.82, 0) + DEFAULT_CHAT_FRAME:AddMessage("Line: |cffffffff" .. string.sub(line, 1, 100) .. "...|r", 1, 1, 1) + return false + end + end + return true +end + +-- Hook for EditMacro API function +function Extension.SafeEditMacro(macro_id, x, y, body) + if CleveRoids.MacroLengthDebug then + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[MacroLengthWarn]|r EditMacro called for ID: " .. tostring(macro_id)) + end + + -- SuperMacro compatibility - bypass validation if SuperMacro is handling it + if SuperMacroFrame ~= nil and x and y and body == nil then + if edit_orig then edit_orig(macro_id, x, y, body) end + return + end + + -- Get macro name for error messages + local macroName = GetMacroInfo(macro_id) + + -- Validate the macro body + if not ValidateMacroBody(body, macroName) then + -- Validation failed - do NOT save + if CleveRoids.MacroLengthDebug then + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[MacroLengthWarn]|r BLOCKED save - line too long") + end + return + end + + -- Validation passed - call original + if edit_orig then + if CleveRoids.MacroLengthDebug then + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[MacroLengthWarn]|r Validation passed, saving...") + end + edit_orig(macro_id, x, y, body) + end +end + +-- Hook for MacroFrame_SaveMacro (Blizzard UI function) +function Extension.SafeMacroFrameSave() + if CleveRoids.MacroLengthDebug then + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[MacroLengthWarn]|r MacroFrame_SaveMacro called") + end + + -- This is called by the macro UI, so we need to get the text from the edit box + if MacroFrameText and MacroFrame.selectedMacro then + local body = MacroFrameText:GetText() + local macroName = GetMacroInfo(MacroFrame.selectedMacro) + + -- Validate before allowing the save + if not ValidateMacroBody(body, macroName) then + if CleveRoids.MacroLengthDebug then + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[MacroLengthWarn]|r BLOCKED MacroFrame save") + end + -- Don't call original - block the save return end end - edit_orig(macro_id, x, y, body) + + -- Validation passed or no text to validate + if macroframe_save_orig then + if CleveRoids.MacroLengthDebug then + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[MacroLengthWarn]|r MacroFrame validation passed") + end + macroframe_save_orig() + end end +-- Install hooks when macro frame loads +function Extension.OnMacroFrameLoad() + if Extension.macroFrameHooked then + return + end + + if CleveRoids.MacroLengthDebug then + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[MacroLengthWarn]|r Hooking MacroFrame_SaveMacro") + end + + -- Hook MacroFrame_SaveMacro if it exists + if MacroFrame_SaveMacro then + macroframe_save_orig = MacroFrame_SaveMacro + MacroFrame_SaveMacro = function(...) + local success, err = pcall(Extension.SafeMacroFrameSave, unpack(arg)) + if not success then + DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[MacroLengthWarn]|r MacroFrame hook error: " .. tostring(err)) + -- Call original on error + if macroframe_save_orig then + return macroframe_save_orig(unpack(arg)) + end + end + end + Extension.macroFrameHooked = true + + if CleveRoids.MacroLengthDebug then + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[MacroLengthWarn]|r MacroFrame_SaveMacro hooked successfully") + end + end +end + +function Extension.OnAddonLoaded() + if arg1 == "Blizzard_MacroUI" then + Extension.OnMacroFrameLoad() + end +end function Extension.OnLoad() - -- how do I use this with a global function? - -- CleveRoids.RegisterMethodHook("MacroLengthWarn", _G, "EditMacro", "SafeEditMacro", true) - EditMacro = Extension.SafeEditMacro + -- Schedule messages to show after UI is ready + local function ShowMessages() + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[MacroLengthWarn]|r Extension loaded (Max line length: " .. MAX_LINE_LENGTH .. ")") + + if not EditMacro then + DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[MacroLengthWarn]|r ERROR: EditMacro function not found!") + else + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[MacroLengthWarn]|r EditMacro hook: " .. (edit_orig and "SUCCESS" or "FAILED")) + end + + if MacroFrame_SaveMacro then + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[MacroLengthWarn]|r MacroFrame_SaveMacro hook: " .. (Extension.macroFrameHooked and "SUCCESS" or "PENDING")) + else + DEFAULT_CHAT_FRAME:AddMessage("|cffffff00[MacroLengthWarn]|r MacroFrame_SaveMacro: Will hook when Blizzard UI loads") + end + + DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00[MacroLengthWarn]|r Use /cleveroid macrodebug to toggle debug output") + end + + -- Check if EditMacro exists + if not EditMacro then + CleveRoids.Print("[MacroLengthWarn] ERROR: EditMacro not found!") + return + end + + -- Capture and hook EditMacro immediately with error protection + edit_orig = EditMacro + EditMacro = function(...) + local success, err = pcall(Extension.SafeEditMacro, unpack(arg)) + if not success then + DEFAULT_CHAT_FRAME:AddMessage("|cffff0000[MacroLengthWarn]|r Hook error: " .. tostring(err)) + -- Call original on error to prevent breaking macros + if edit_orig then + return edit_orig(unpack(arg)) + end + end + end + + -- Listen for macro UI loading + Extension.RegisterEvent("ADDON_LOADED", "OnAddonLoaded") + + -- Also try to hook MacroFrame_SaveMacro if it already exists + if MacroFrame_SaveMacro then + Extension.OnMacroFrameLoad() + end + + -- Register PLAYER_LOGIN to show status messages + Extension.RegisterEvent("PLAYER_LOGIN", "OnPlayerLogin") + + -- Store the message function for later + Extension.ShowMessages = ShowMessages +end + +function Extension.OnPlayerLogin() + -- Status messages disabled by default - use /cleveroid macrostatus to check + -- if Extension.ShowMessages then + -- Extension.ShowMessages() + -- end end _G["CleveRoids"] = CleveRoids \ No newline at end of file