038931fcfb
Match the layout convention used by every other multi-addon-shape fork in Exiles/ (Bagnon/, Kui_Nameplates/, ShadowedUnitFrames/, etc.) — the addon's own files live in a subfolder named after the addon, with only the repo-level README files at the root. All moves are pure git renames (history preserved). Toc references are relative to the toc location so nothing inside the addon changes.
142 lines
5.4 KiB
Lua
142 lines
5.4 KiB
Lua
-- OmenSync.lua
|
|
--
|
|
-- Addon-channel sync for party/raid threat values.
|
|
--
|
|
-- Why this exists
|
|
-- ---------------
|
|
-- Omen reads threat data via UnitDetailedThreatSituation(unit, mob).
|
|
-- That API only returns data the server pushes to the client. On the
|
|
-- Conquest of Azeroth realm (Vol'jin server) party-member threat is
|
|
-- not pushed — UnitDetailedThreatSituation("party1", "target")
|
|
-- returns nil even mid-combat, so Omen draws only the local player's
|
|
-- bar. The same Omen install on Bronzebeard (classic+) shows the
|
|
-- whole party because that core does push the data.
|
|
--
|
|
-- This module fills the gap by sending each player's own player+pet
|
|
-- threat over SendAddonMessage("OMSYNC", …, "PARTY"|"RAID"). Receivers
|
|
-- store incoming values keyed by senderGUID and serve them to
|
|
-- updatethreat() in Omen.lua as a fallback whenever the client API
|
|
-- returns nil. When the server *does* push data (Bronzebeard) the
|
|
-- API path wins and the sync values are simply unused — making this
|
|
-- file a no-op on healthy realms.
|
|
--
|
|
-- Wire format (pipe-separated, terse to stay under the 255-byte
|
|
-- AddonMessage payload cap):
|
|
-- <subjectGUID>|<mobGUID>|<threatValue>|<isTanking 0|1>
|
|
-- The sender's own name comes from CHAT_MSG_ADDON's `sender` arg, so
|
|
-- it doesn't need to be in the message; we only need the subject GUID
|
|
-- because each player broadcasts both their player and pet GUIDs.
|
|
--
|
|
-- Both peers must run this fork (or any addon that emits/consumes the
|
|
-- "OMSYNC" prefix). Other Omen installs ignore the prefix silently.
|
|
|
|
local Omen = LibStub("AceAddon-3.0"):GetAddon("Omen")
|
|
if not Omen then return end
|
|
|
|
local PREFIX = "OMSYNC"
|
|
local THROTTLE = 0.4 -- min seconds between sends per (subject, mob)
|
|
local STALE = 8 -- seconds; entries older than this are ignored
|
|
local MIN_DELTA = 0.05 -- 5%; smaller changes don't trigger a send
|
|
|
|
-- incomingThreat[subjectGUID][mobGUID] = { value, isTanking, time }
|
|
local incomingThreat = {}
|
|
-- lastSend[subjectGUID][mobGUID] = { value, time }
|
|
local lastSend = {}
|
|
|
|
local function nowGetTime() return GetTime() end
|
|
|
|
local function packMsg(subject, mob, value, isTanking)
|
|
return string.format("%s|%s|%d|%d", subject, mob, value, isTanking and 1 or 0)
|
|
end
|
|
|
|
local function unpackMsg(msg)
|
|
local subject, mob, val, tank = string.match(msg, "^([^|]+)|([^|]+)|(%-?%d+)|([01])$")
|
|
if not subject then return nil end
|
|
return subject, mob, tonumber(val), tank == "1"
|
|
end
|
|
|
|
local function inGroup()
|
|
return GetNumPartyMembers() > 0 or GetNumRaidMembers() > 0
|
|
end
|
|
|
|
local function pruneOlderThan(t, limit)
|
|
for k, v in pairs(t) do
|
|
if v.time and v.time < limit then t[k] = nil end
|
|
end
|
|
end
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
-- Public API: called from Omen.lua's local updatethreat()
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
-- Look up the most recent synced threat value for `subjectGUID` on
|
|
-- `mobGUID`, or nil if we don't have a fresh entry. Returns
|
|
-- (value, isTanking).
|
|
function Omen:SyncGetThreat(subjectGUID, mobGUID)
|
|
local byMob = incomingThreat[subjectGUID]
|
|
if not byMob then return nil end
|
|
local entry = byMob[mobGUID]
|
|
if not entry then return nil end
|
|
if nowGetTime() - entry.time > STALE then
|
|
byMob[mobGUID] = nil
|
|
return nil
|
|
end
|
|
return entry.value, entry.isTanking
|
|
end
|
|
|
|
-- Broadcast our own (player or pet) threat to the party/raid. Throttled
|
|
-- per (subject, mob); silently no-ops outside groups.
|
|
function Omen:SyncBroadcastThreat(subjectGUID, mobGUID, value, isTanking)
|
|
if not inGroup() then return end
|
|
if not subjectGUID or not mobGUID or not value then return end
|
|
if value < 0 then return end -- ignore the temporary-negative encoding
|
|
|
|
lastSend[subjectGUID] = lastSend[subjectGUID] or {}
|
|
local prev = lastSend[subjectGUID][mobGUID]
|
|
local now = nowGetTime()
|
|
if prev then
|
|
local age = now - prev.time
|
|
local maxV = math.max(value, prev.value, 1)
|
|
local pct = math.abs(value - prev.value) / maxV
|
|
if age < THROTTLE and pct < MIN_DELTA then return end
|
|
end
|
|
lastSend[subjectGUID][mobGUID] = { value = value, time = now }
|
|
|
|
local channel = GetNumRaidMembers() > 0 and "RAID" or "PARTY"
|
|
SendAddonMessage(PREFIX, packMsg(subjectGUID, mobGUID, value, isTanking), channel)
|
|
end
|
|
|
|
-- ---------------------------------------------------------------------------
|
|
-- Receiver
|
|
-- ---------------------------------------------------------------------------
|
|
|
|
local f = CreateFrame("Frame")
|
|
f:RegisterEvent("CHAT_MSG_ADDON")
|
|
f:RegisterEvent("PLAYER_LEAVING_WORLD")
|
|
f:RegisterEvent("PARTY_MEMBERS_CHANGED")
|
|
f:RegisterEvent("RAID_ROSTER_UPDATE")
|
|
f:SetScript("OnEvent", function(self, event, ...)
|
|
if event == "CHAT_MSG_ADDON" then
|
|
local prefix, msg, _, sender = ...
|
|
if prefix ~= PREFIX then return end
|
|
if sender == UnitName("player") then return end -- ignore self-echo
|
|
|
|
local subject, mob, value, isTanking = unpackMsg(msg)
|
|
if not subject then return end
|
|
|
|
local byMob = incomingThreat[subject]
|
|
if not byMob then
|
|
byMob = {}
|
|
incomingThreat[subject] = byMob
|
|
end
|
|
byMob[mob] = { value = value, isTanking = isTanking, time = nowGetTime() }
|
|
|
|
-- Cheap GC: prune stale mob entries off the same subject.
|
|
pruneOlderThan(byMob, nowGetTime() - STALE)
|
|
else
|
|
-- World transitions / roster changes invalidate everything.
|
|
wipe(incomingThreat)
|
|
wipe(lastSend)
|
|
end
|
|
end)
|