delted old debug window, added new version notice window, added new features to tracker module, updated locales, and updated main addon files.

This commit is contained in:
Torsten Brendgen
2026-04-25 17:33:32 +02:00
parent f1d2a761e4
commit 02e062d66b
12 changed files with 401 additions and 681 deletions

View File

@@ -288,16 +288,20 @@ HMGT.powerTracking = {
}
HMGT.pendingSpellPowerCosts = {}
HMGT.demoModeData = {}
HMGT.peerVersions = {}
HMGT.versionWarnings = {}
HMGT.versionWhisperWarnings = {}
HMGT.playerStatus = {}
HMGT.debugBuffer = {}
HMGT.debugBufferMax = 500
HMGT.enabledDebugScopes = {
General = true,
Debug = true,
Comm = true,
TrackedSpells = true,
TrackerCore = true,
TrackerSync = true,
TrackerUI = true,
TrackerBridge = true,
TrackerState = true,
PowerSpend = true,
}
HMGT.pendingReliableMessages = HMGT.pendingReliableMessages or {}
@@ -311,7 +315,11 @@ local DEBUG_SCOPE_LABELS = {
General = "General",
Debug = "Debug",
Comm = "Communication",
TrackedSpells = "Tracked Spells",
TrackerCore = "Tracker Core",
TrackerSync = "Tracker Sync",
TrackerUI = "Tracker UI",
TrackerBridge = "Tracker Bridge",
TrackerState = "Tracker State",
PowerSpend = "Power Spend",
RaidTimeline = "Raid Timeline",
Notes = "Notes",
@@ -340,8 +348,12 @@ end
function HMGT:GetTrackerDebugScope(tracker)
local trackerName = nil
local trackerId = nil
local trackerType = nil
if type(tracker) == "table" then
trackerName = tracker.name
trackerId = tonumber(tracker.id)
trackerType = tracker.trackerType
if (not trackerName or trackerName == "") and tracker.id then
trackerName = string.format("Tracker %s", tostring(tracker.id))
end
@@ -353,7 +365,106 @@ function HMGT:GetTrackerDebugScope(tracker)
if trackerName == "" then
trackerName = "Tracker"
end
return "Tracker: " .. trackerName
local prefix = "Tracker"
if trackerType == "group" then
prefix = "Tracker Group"
elseif trackerType == "normal" then
prefix = "Tracker Normal"
end
if trackerId then
return string.format("%s #%d: %s", prefix, trackerId, trackerName)
end
return prefix .. ": " .. trackerName
end
function HMGT:GetPlayerStatus(playerName, create)
local normalizedName = self:NormalizePlayerName(playerName)
if not normalizedName or normalizedName == "" then
return nil
end
self.playerStatus = self.playerStatus or {}
if create then
self.playerStatus[normalizedName] = self.playerStatus[normalizedName] or {}
end
return self.playerStatus[normalizedName]
end
function HMGT:SetPlayerVersionStatus(playerName, version, protocol, sourceTag)
local status = self:GetPlayerStatus(playerName, true)
if not status then
return nil
end
if version and version ~= "" then
status.version = tostring(version)
end
if tonumber(protocol) then
status.protocol = tonumber(protocol)
end
if sourceTag and sourceTag ~= "" then
status.versionSource = tostring(sourceTag)
end
status.mode = "hmgt"
return status
end
function HMGT:SetPlayerBridgeStatus(playerName, sourceName)
local source = tostring(sourceName or "")
if source == "" then
return nil
end
local status = self:GetPlayerStatus(playerName, true)
if not status then
return nil
end
status.bridgeSource = source
if not status.version or status.version == "" then
status.mode = "bridge"
end
return status
end
function HMGT:GetPlayerAddonStatus(playerName)
local status = self:GetPlayerStatus(playerName, false)
if not status then
return {
mode = "missing",
version = nil,
protocol = 0,
bridgeSource = nil,
}
end
local version = status.version
local protocol = tonumber(status.protocol) or 0
local bridgeSource = status.bridgeSource
local mode = status.mode
if version and version ~= "" then
mode = "hmgt"
elseif bridgeSource and bridgeSource ~= "" then
mode = "bridge"
else
mode = "missing"
end
return {
mode = mode,
version = version,
protocol = protocol,
bridgeSource = bridgeSource,
}
end
function HMGT:ClearPlayerStatus(playerName)
local normalizedName = self:NormalizePlayerName(playerName)
if not normalizedName or not self.playerStatus then
return false
end
if self.playerStatus[normalizedName] then
self.playerStatus[normalizedName] = nil
return true
end
return false
end
function HMGT:GetStaticDebugScopeOptions()
@@ -456,6 +567,10 @@ function HMGT:IsReliableCommType(msgType)
end
function HMGT:GetPeerProtocolVersion(playerName)
local status = self:GetPlayerStatus(playerName, false)
if status and tonumber(status.protocol) then
return tonumber(status.protocol) or 0
end
local normalizedName = self:NormalizePlayerName(playerName)
local peerProtocols = self.peerProtocols or {}
return tonumber(normalizedName and peerProtocols[normalizedName]) or 0
@@ -469,6 +584,7 @@ function HMGT:RememberPeerProtocolVersion(playerName, protocol)
end
self.peerProtocols = self.peerProtocols or {}
self.peerProtocols[normalizedName] = numeric
self:SetPlayerVersionStatus(normalizedName, nil, numeric, nil)
end
local function ParseVersionTokens(version)
@@ -747,7 +863,32 @@ function HMGT:SendDirectMessage(payload, target, prio)
end
function HMGT:DebugScoped(level, scope, fmt, ...)
return
local normalizedLevel = tostring(level or "info"):lower()
if not DEBUG_LEVELS[normalizedLevel] then
normalizedLevel = "info"
end
local normalizedScope = tostring(scope or "General"):match("^%s*(.-)%s*$")
if normalizedScope == "" then
normalizedScope = "General"
end
local ok, message = pcall(string.format, tostring(fmt or ""), ...)
if not ok then
message = tostring(fmt or "")
end
local line = string.format("%s [%s][%s] %s", date("%H:%M:%S"), string.upper(normalizedLevel), normalizedScope, tostring(message or ""))
self.debugBuffer = self.debugBuffer or {}
self.debugBuffer[#self.debugBuffer + 1] = line
local maxLines = tonumber(self.debugBufferMax) or 500
while #self.debugBuffer > maxLines do
table.remove(self.debugBuffer, 1)
end
if self.debugWindow and self.debugWindow.IsShown and self.debugWindow:IsShown() and self.RefreshDebugWindow then
self:RefreshDebugWindow()
end
end
function HMGT:Debug(fmt, ...)
@@ -764,7 +905,7 @@ end
function HMGT:RegisterPeerVersion(playerName, version, protocol, sourceTag)
if not playerName then return end
self.peerVersions[playerName] = version
self:SetPlayerVersionStatus(playerName, version, protocol, sourceTag)
self:RememberPeerProtocolVersion(playerName, protocol)
if self.versionNoticeWindow and self.versionNoticeWindow.IsShown and self.versionNoticeWindow:IsShown() and self.RefreshVersionNoticeWindow then
self:RefreshVersionNoticeWindow()
@@ -798,7 +939,7 @@ function HMGT:RegisterPeerVersion(playerName, version, protocol, sourceTag)
tostring(playerName), table.concat(details, " | "))
self:Print("|cffff5555HMGT|r " .. text)
self:ShowVersionMismatchPopup(playerName, table.concat(details, " | "), sourceTag)
self:Debug("info", "Version mismatch %s via=%s %s", tostring(playerName), tostring(sourceTag or "?"), table.concat(details, " | "))
self:DebugScoped("info", "TrackerCore", "Version mismatch %s via=%s %s", tostring(playerName), tostring(sourceTag or "?"), table.concat(details, " | "))
end
end
@@ -1091,7 +1232,7 @@ function HMGT:LogTrackedSpellCast(playerName, spellEntry, details)
self:DebugScoped(
"verbose",
"TrackedSpells",
"TrackerCore",
"%s -> %s von %s, %s",
GetTrackedSpellCategoryLabel(spellEntry),
GetSpellDebugLabel(spellEntry.spellId),
@@ -1699,22 +1840,9 @@ function HMGT:MigrateProfileSettings()
if #p.trackers == 0 and p.trackerModelVersion ~= TRACKER_MODEL_VERSION then
p.trackers = {
self:CreateTrackerConfig(1, CopyTrackerFields({
name = L["IT_NAME"] or "Interrupts",
trackerType = "normal",
categories = { "interrupt" },
}, p.interruptTracker or {})),
self:CreateTrackerConfig(2, CopyTrackerFields({
name = L["RCD_NAME"] or "Raid Cooldowns",
trackerType = "normal",
categories = { "raid" },
}, p.raidCooldownTracker or {})),
self:CreateTrackerConfig(3, CopyTrackerFields({
name = L["GCD_NAME"] or "Cooldowns",
trackerType = "group",
categories = { "defensive", "offensive", "tank", "healing", "utility", "cc", "lust" },
showChargesOnIcon = true,
}, p.groupCooldownTracker or {})),
self:BuildTrackerConfigFromPreset("interruptTracker", 1, CopyTrackerFields({}, p.interruptTracker or {})),
self:BuildTrackerConfigFromPreset("raidCooldownTracker", 2, CopyTrackerFields({}, p.raidCooldownTracker or {})),
self:BuildTrackerConfigFromPreset("groupCooldownTracker", 3, CopyTrackerFields({}, p.groupCooldownTracker or {})),
}
end
@@ -1732,11 +1860,7 @@ function HMGT:MigrateProfileSettings()
end
end
if #normalizedTrackers == 0 then
normalizedTrackers[1] = self:CreateTrackerConfig(1, {
name = L["IT_NAME"] or "Interrupts",
trackerType = "normal",
categories = { "interrupt" },
})
normalizedTrackers[1] = self:BuildTrackerConfigFromPreset("interruptTracker", 1)
end
p.trackers = normalizedTrackers
p.trackerModelVersion = TRACKER_MODEL_VERSION
@@ -2648,7 +2772,7 @@ function HMGT:OnGroupRosterUpdate()
if not validPlayers[name] then
self.playerData[name] = nil
self:ClearTrackerStateForPlayer(name)
self.peerVersions[name] = nil
self:ClearPlayerStatus(name)
self.versionWarnings[name] = nil
if self.peerProtocols then
self.peerProtocols[name] = nil
@@ -3040,127 +3164,6 @@ function HMGT:TestMode()
self:Print(L["TEST_MODE_ACTIVE"])
end
function HMGT:GetDemoEntries(trackerKey, database, settings)
local pool = {}
local poolByClass = {}
for _, entry in ipairs(database) do
if settings.enabledSpells[entry.spellId] ~= false then
pool[#pool + 1] = entry
for _, cls in ipairs(entry.classes or {}) do
poolByClass[cls] = poolByClass[cls] or {}
poolByClass[cls][#poolByClass[cls] + 1] = entry
end
end
end
if #pool == 0 then return {} end
local classKeys = {}
for cls in pairs(poolByClass) do
classKeys[#classKeys + 1] = cls
end
if #classKeys == 0 then classKeys[1] = "WARRIOR" end
local count = settings.showBar and math.min(8, #pool) or math.min(12, #pool)
local names = { "Alice", "Bob", "Clara", "Duke", "Elli", "Fynn", "Gina", "Hektor", "Ivo", "Jana", "Kira", "Lio" }
local spellIds = {}
for _, e in ipairs(pool) do spellIds[#spellIds + 1] = tostring(e.spellId) end
table.sort(spellIds)
local signature = table.concat(spellIds, ",") .. "|" .. tostring(settings.showBar and 1 or 0) .. "|" .. tostring(count)
local now = GetTime()
local cache = self.demoModeData[trackerKey]
if (not cache) or (cache.signature ~= signature) or (not cache.entries) or (#cache.entries ~= count) then
local entries = {}
for i = 1, count do
local cls = classKeys[math.random(1, #classKeys)]
local classPool = poolByClass[cls]
local spell = (classPool and classPool[math.random(1, #classPool)]) or pool[math.random(1, #pool)]
local duration = math.max(
1,
tonumber(HMGT_SpellData.GetBaseCooldown and HMGT_SpellData.GetBaseCooldown(spell)) or tonumber(spell.cooldown) or 60
)
local playerName = names[((i - 1) % #names) + 1]
-- start offset so demo entries do not all tick in sync
local offset = math.random() * math.min(duration * 0.85, duration - 0.1)
entries[#entries + 1] = {
playerName = playerName,
class = cls or ((spell.classes and spell.classes[1]) or "WARRIOR"),
spellEntry = spell,
total = duration,
cycleStart = now - offset,
currentCharges = nil,
maxCharges = nil,
}
end
cache = { signature = signature, entries = entries }
self.demoModeData[trackerKey] = cache
end
local out = {}
for _, e in ipairs(cache.entries) do
local total = math.max(1, tonumber(e.total) or 1)
local elapsed = math.max(0, now - (e.cycleStart or now))
local phase = math.fmod(elapsed, total)
local rem = total - phase
-- show zero briefly at cycle boundary, then restart immediately
if elapsed > 0 and phase < 0.05 then rem = 0 end
out[#out + 1] = {
playerName = e.playerName,
class = e.class,
spellEntry = e.spellEntry,
remaining = rem,
total = total,
currentCharges = e.currentCharges,
maxCharges = e.maxCharges,
}
end
return out
end
function HMGT:GetOwnTestEntries(database, settings, cooldownInfoOpts)
local entries = {}
local enabledSpells = settings and settings.enabledSpells or {}
local playerName = self:NormalizePlayerName(UnitName("player")) or "Player"
local classToken = select(2, UnitClass("player"))
if not classToken then
return entries, playerName
end
local specIdx = GetSpecialization()
local lookupSpec = (specIdx and specIdx > 0) and specIdx or 0
local talents = (self.playerData[playerName] and self.playerData[playerName].talents) or {}
local spells = HMGT_SpellData.GetSpellsForSpec(classToken, lookupSpec, database or {})
for _, spellEntry in ipairs(spells) do
if enabledSpells[spellEntry.spellId] ~= false then
local remaining, total, curCharges, maxCharges = self:GetCooldownInfo(playerName, spellEntry.spellId, cooldownInfoOpts)
local effectiveCd = HMGT_SpellData.GetEffectiveCooldown(spellEntry, talents)
local isAvailabilitySpell = self:IsAvailabilitySpell(spellEntry)
local spellKnown = self:IsTrackedSpellKnownForPlayer(playerName, spellEntry.spellId)
local hasPartialCharges = (tonumber(maxCharges) or 0) > 0
and (tonumber(curCharges) or tonumber(maxCharges) or 0) < (tonumber(maxCharges) or 0)
local hasActiveCd = ((remaining or 0) > 0) or hasPartialCharges
local hasAvailabilityState = isAvailabilitySpell and self:HasAvailabilityState(playerName, spellEntry.spellId)
if spellKnown or hasActiveCd or hasAvailabilityState then
entries[#entries + 1] = {
playerName = playerName,
class = classToken,
spellEntry = spellEntry,
remaining = remaining,
total = total > 0 and total or effectiveCd,
currentCharges = curCharges,
maxCharges = maxCharges,
}
end
end
end
return entries, playerName
end
-- ═══════════════════════════════════════════════════════════════
-- HILFSFUNKTIONEN
-- ═══════════════════════════════════════════════════════════════