From 49992fc804a9357472519936e7d9767f8a39b4da Mon Sep 17 00:00:00 2001 From: kompolompo Date: Sat, 5 Sep 2026 11:41:42 +0200 Subject: [PATCH] Fix stuck drag on off-frame mouse-up, add error logging and slash commands - Remove dead VARIABLES_LOADED registration (no handler existed for it) - Extract OctoLatency_StopMoving and call it from OnHide too, so a drag can't get stuck if OnMouseUp never fires on the frame - Wrap tooltip/update/login handlers in pcall, logging failures to OctoLatencyDB.errorLog instead of raising a Lua error popup - Add /octolat errors and /octolat clearerrors slash commands - Update README with a short explanation of the addon and its options Co-Authored-By: Claude Sonnet 5 --- OctoLatency.lua | 129 ++++++++++++++++++++++++++++++++++++++---------- OctoLatency.xml | 9 ++-- README.md | 48 +++++++++++++----- 3 files changed, 145 insertions(+), 41 deletions(-) diff --git a/OctoLatency.lua b/OctoLatency.lua index 6da3ac7..931691c 100644 --- a/OctoLatency.lua +++ b/OctoLatency.lua @@ -6,40 +6,72 @@ local elapsedTimer = 0 local UPDATE_INTERVAL = 2.0 -- update every 2 seconds +local MAX_ERROR_LOG = 20 + +local function OctoLatency_EnsureDB() + if (not OctoLatencyDB) then + OctoLatencyDB = {} + end + if (not OctoLatencyDB.errorLog) then + OctoLatencyDB.errorLog = {} + end + return OctoLatencyDB +end + +-- Records an error to SavedVariables (visible in-game via /octolat errors, +-- and readable straight out of the WTF SavedVariables file after logout) +-- and pings the chat frame so it isn't silently swallowed. +function OctoLatency_LogError(context, err) + local db = OctoLatency_EnsureDB() + local entry = { + time = date("%Y-%m-%d %H:%M:%S"), + context = context, + message = tostring(err), + } + table.insert(db.errorLog, entry) + while (table.getn(db.errorLog) > MAX_ERROR_LOG) do + table.remove(db.errorLog, 1) + end + DEFAULT_CHAT_FRAME:AddMessage("|cffff5555OctoLatency error|r in " .. context .. ": " .. tostring(err) .. " (type /octolat errors)") +end + +-- Runs func in protected mode so one bad tick can't break the addon, +-- and captures the error for later reporting instead of losing it. +local function OctoLatency_SafeCall(context, func, ...) + local ok, err = pcall(func, ...) + if (not ok) then + OctoLatency_LogError(context, err) + end + return ok +end + function OctoLatency_OnLoad(self) - -- Register events - self:RegisterEvent("VARIABLES_LOADED") - -- Tooltip on hover self:SetScript("OnEnter", function() - GameTooltip:SetOwner(this, "ANCHOR_TOP") - GameTooltip:AddLine("OctoLatency", 0, 1, 1) - local bandwidth, latency, homeLatency, worldLatency = GetNetStats() - GameTooltip:AddLine("Home Latency: " .. homeLatency .. " ms", 1, 1, 1) - GameTooltip:AddLine("World Latency: " .. worldLatency .. " ms", 1, 1, 1) - GameTooltip:AddLine("Bandwidth: " .. string.format("%.2f", bandwidth) .. " KB/s", 1, 1, 1) - GameTooltip:AddLine("Left-click and drag to move.", 0.7, 0.7, 0.7) - GameTooltip:Show() + OctoLatency_SafeCall("OnEnter", function() + GameTooltip:SetOwner(this, "ANCHOR_TOP") + GameTooltip:AddLine("OctoLatency", 0, 1, 1) + local bandwidth, latency, homeLatency, worldLatency = GetNetStats() + GameTooltip:AddLine("Home Latency: " .. homeLatency .. " ms", 1, 1, 1) + GameTooltip:AddLine("World Latency: " .. worldLatency .. " ms", 1, 1, 1) + GameTooltip:AddLine("Bandwidth: " .. string.format("%.2f", bandwidth) .. " KB/s", 1, 1, 1) + GameTooltip:AddLine("Left-click and drag to move.", 0.7, 0.7, 0.7) + GameTooltip:Show() + end) end) - + self:SetScript("OnLeave", function() GameTooltip:Hide() end) end -function OctoLatency_OnUpdate(elapsed) - elapsedTimer = elapsedTimer + elapsed - if (elapsedTimer < UPDATE_INTERVAL) then - return - end - elapsedTimer = 0 - +local function OctoLatency_UpdateText() -- GetNetStats returns: bandwidth, latency, homeLatency, worldLatency local _, _, homeLatency, worldLatency = GetNetStats() - + -- We'll display world latency (or max of home/world) local latency = worldLatency or homeLatency or 0 - + -- Color thresholds: Green < 150ms, Yellow 150-350ms, Red > 350ms local colorHex if (latency < 150) then @@ -49,10 +81,28 @@ function OctoLatency_OnUpdate(elapsed) else colorHex = "|cffff0000" -- Red end - + OctoLatencyText:SetText(colorHex .. latency .. " ms|r") end +function OctoLatency_OnUpdate(elapsed) + elapsedTimer = elapsedTimer + elapsed + if (elapsedTimer < UPDATE_INTERVAL) then + return + end + elapsedTimer = 0 + + OctoLatency_SafeCall("OnUpdate", OctoLatency_UpdateText) +end + +function OctoLatency_StopMoving(self) + if (self.isMoving) then + self:StopMovingOrSizing() + self.isMoving = false + OctoLatency_SavePosition() + end +end + function OctoLatency_SavePosition() if (not OctoLatencyDB) then OctoLatencyDB = {} @@ -68,8 +118,35 @@ end local loadFrame = CreateFrame("Frame") loadFrame:RegisterEvent("PLAYER_LOGIN") loadFrame:SetScript("OnEvent", function() - if (OctoLatencyDB and OctoLatencyDB.point) then - OctoLatencyFrame:ClearAllPoints() - OctoLatencyFrame:SetPoint(OctoLatencyDB.point, UIParent, OctoLatencyDB.relativePoint, OctoLatencyDB.xOfs, OctoLatencyDB.yOfs) - end + OctoLatency_SafeCall("PLAYER_LOGIN", function() + if (OctoLatencyDB and OctoLatencyDB.point) then + OctoLatencyFrame:ClearAllPoints() + OctoLatencyFrame:SetPoint(OctoLatencyDB.point, UIParent, OctoLatencyDB.relativePoint, OctoLatencyDB.xOfs, OctoLatencyDB.yOfs) + end + end) end) + +-------------------------------------------------------------------------------- +-- Slash command: /octolat errors | clearerrors +-------------------------------------------------------------------------------- +SLASH_OCTOLATENCY1 = "/octolat" +SlashCmdList["OCTOLATENCY"] = function(msg) + local db = OctoLatency_EnsureDB() + msg = string.lower(msg or "") + + if (msg == "errors") then + if (table.getn(db.errorLog) == 0) then + DEFAULT_CHAT_FRAME:AddMessage("OctoLatency: no errors logged.") + return + end + DEFAULT_CHAT_FRAME:AddMessage("OctoLatency: last " .. table.getn(db.errorLog) .. " error(s):") + for i, entry in ipairs(db.errorLog) do + DEFAULT_CHAT_FRAME:AddMessage("[" .. entry.time .. "] " .. entry.context .. ": " .. entry.message) + end + elseif (msg == "clearerrors") then + db.errorLog = {} + DEFAULT_CHAT_FRAME:AddMessage("OctoLatency: error log cleared.") + else + DEFAULT_CHAT_FRAME:AddMessage("OctoLatency commands: /octolat errors, /octolat clearerrors") + end +end diff --git a/OctoLatency.xml b/OctoLatency.xml index 7ecf9bf..609e72e 100644 --- a/OctoLatency.xml +++ b/OctoLatency.xml @@ -47,12 +47,13 @@ end - if (arg1 == "LeftButton" and this.isMoving) then - this:StopMovingOrSizing(); - this.isMoving = false; - OctoLatency_SavePosition(); + if (arg1 == "LeftButton") then + OctoLatency_StopMoving(this); end + + OctoLatency_StopMoving(this); + diff --git a/README.md b/README.md index 628ad10..30f79ff 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,38 @@ -## OctoLatency Addon Optimization Review & Recommendations ---- +## OctoLatency -### New Addon: `OctoLatency` -We are creating a dedicated, highly optimized, lightweight Vanilla WoW addon called **OctoLatency**. -- It provides a movable, clean text indicator showing current latency (home and world latency via `GetNetStats()`). -- Color codes the latency (tuned for private servers where up to 250ms is common/acceptable): - - **Green**: < 150 ms (Good) - - **Yellow**: 150 - 350 ms (Warning / Moderate) - - **Red**: > 350 ms (Critical / High) -- Fully supports drag-and-drop repositioning with position saving via SavedVariables. -- Throttled updates (updates every 2 seconds rather than every frame) to ensure zero impact on game performance. +A lightweight latency monitor for Vanilla WoW (1.12.1). Shows a small, movable, +color-coded latency readout on your screen. + +### What it does + +- Displays your current world latency (falls back to home latency if world + isn't available) as `NNN ms` next to your screen. +- Color-codes the number so you can tell your connection quality at a glance: + - **Green** — under 150 ms (good) + - **Yellow** — 150-350 ms (moderate) + - **Red** — over 350 ms (critical) +- Hover the frame for a tooltip with home latency, world latency, and + bandwidth. +- Updates every 2 seconds (not every frame), so it has no meaningful impact on + game performance. + +### Options + +There's no settings panel — the addon is intentionally minimal. The only +"option" is position: + +- **Move it**: left-click and drag the frame anywhere on screen. Its position + is saved automatically and restored on your next login. + +### Error reporting + +If something inside the addon errors (e.g. an unexpected `GetNetStats()` +response), it's caught instead of spamming a Lua error popup, and logged for +you to report: + +- `/octolat errors` — print the last 20 logged errors (with timestamps) to + chat. +- `/octolat clearerrors` — clear the error log. + +The log is also saved to your `SavedVariables` file, so it survives +logout/relog.