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 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
862758ca6c
commit
49992fc804
+103
-26
@@ -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
|
||||
|
||||
+5
-4
@@ -47,12 +47,13 @@
|
||||
end
|
||||
</OnMouseDown>
|
||||
<OnMouseUp>
|
||||
if (arg1 == "LeftButton" and this.isMoving) then
|
||||
this:StopMovingOrSizing();
|
||||
this.isMoving = false;
|
||||
OctoLatency_SavePosition();
|
||||
if (arg1 == "LeftButton") then
|
||||
OctoLatency_StopMoving(this);
|
||||
end
|
||||
</OnMouseUp>
|
||||
<OnHide>
|
||||
OctoLatency_StopMoving(this);
|
||||
</OnHide>
|
||||
</Scripts>
|
||||
</Frame>
|
||||
</Ui>
|
||||
|
||||
@@ -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.
|
||||
|
||||
Reference in New Issue
Block a user