File Details
1.5-release
- R
- Mar 22, 2026
- 393.76 KB
- 32
- 12.0.1
- Retail
File Name
CombatAnalytics-1.5-release.zip
Supported Versions
- 12.0.1
tag acb536d0a645e12e37c315d04d12a6e6228bc4a2 1.5-release
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sun Mar 22 03:07:48 2026 +0200
Major fixes + build comparator
commit 35c4ce6f5dd95e6806c124fb8234d020224ae324
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sun Mar 22 03:05:17 2026 +0200
fix: guard secret numeric comparisons in UNIT_AURA handler
Midnight arena forbids comparison operators (>, <=, ~=) on secret number
values just as it forbids using them as table keys. Three sites affected:
- isProcCandidate: wrap auraData.duration > 0 / <= 20 in pcall; secret
duration fields now silently skip the short-duration proc heuristic.
- OpenAuraWindow: pcall-guard the stacksObserved arithmetic and
math.max(maxStacksObserved, stackCount) — stackCount is auraData.applications
which may be secret; math.max uses comparison internally.
- applyAuraData: simplify the maxStacksObserved update to a direct pcall
around math.max(aggregate.maxStacksObserved, auraData.applications or 0);
the previous pcall only guarded the read, not the comparison.
commit b67a51a81a140b11e3c3abcc1bc3ad7f5134c742
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sun Mar 22 02:57:34 2026 +0200
feat: show spec name + PvP talents in Build Comparator label
CombatStore.lua:
- When a build bucket is first created, stamp it with specName, specId,
classFile, pvpTalents (spell ID array), and heroTalentSpecId from the
session's playerSnapshot. Written once; the hash guarantees these never
change for a given bucket key.
UI/BuildComparatorView.lua:
- Replace opaque "Build 4bc8f064 (2F)" label with readable metadata.
- resolvePvpTalentNames(): looks up spell names synchronously via
C_Spell.GetSpellName() (PvP talents are always cached by the client).
- snapshotFromLastSession(): self-heal for existing buckets that predate
the metadata stamp — looks up the stored lastSessionId session.
- buildLabel() priority: bucket.specName → last-session snapshot → hash.
- Format: "Havoc — Mortal Coil, Precognition, Oppressor (2F)"
- Widen name FontStrings from 240 → 340px to accommodate full label.
commit 629b6e88bc51e309188ec30f71fe6f524f979fe7
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sun Mar 22 02:49:32 2026 +0200
fix: revert CLEU frame registration + guard secret aura keys in Midnight arena
Constants.lua / Events.lua:
- Remove COMBAT_LOG_EVENT_UNFILTERED from ROUTER_EVENTS and TRACKER_EVENT_MAP.
Frame:RegisterEvent() on CLEU is forbidden in Midnight arena (raises lua error).
Update comments to reflect that damage comes from C_DamageMeter instead.
CombatTracker.lua:
- Add safeMakeKey() helper: probes a value as a table key inside pcall; returns
the original value if safe, nil if secret ("table index is secret" in Midnight).
- isProcCandidate: pcall-guard Constants.SPELL_CATEGORIES[auraData.spellId].
- OpenAuraWindow / CloseAuraWindow: run auraId through safeMakeKey before any
table-index operation; bail early if secret.
- removeAuraState: run auraInstanceId through safeMakeKey; bail if secret.
- applyAuraData: extract safeInstanceID and safeSpellId at entry; skip tracking
for auras whose IDs are secret. Guard applications comparison with pcall.
- isFullUpdate block: use safeMakeKey before writing to seenAuraInstanceIds.
These fixes eliminate "table index is secret" errors from UNIT_AURA handlers
for arena opponent units in Midnight arena.
commit ad35e35b6c01d40d2dd80143e2f30a0aaa1303c1
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sun Mar 22 02:38:44 2026 +0200
fix: wire COMBAT_LOG_EVENT_UNFILTERED and harden bucket label sanitization
CLEU was never dispatched — Frame:RegisterEvent() is not a protected
function and cannot raise ADDON_ACTION_BLOCKED; the original comment was
wrong. Add the event to ROUTER_EVENTS and map it to HandleCombatLogEvent
in TRACKER_EVENT_MAP. NormalizeCombatLogEvent already sanitizes every
field via SanitizeString/SanitizeNumber so restricted arena events
(secret src/dst values) are handled safely with zeroed-out amounts.
Also extend getOrCreateBucket to sanitize the label parameter with the
same double-pcall pattern used for key. opponent.name is a secret string
in arena; storing it in bucket.label would crash any UI code that calls
FontString:SetText() on it. Falls back to the already-sanitized key.
Root causes of "arena matches not recorded / win rate broken":
- Zero damage: CLEU never fired → UpdateSpellStats never called
- Win rate 0%: UpdateAggregatesForSession crashed in the C_Timer callback
because getOrCreateBucket compared a secret opponent.guid (already fixed)
and secret opponent.name in bucket.label (fixed here)
commit 3e187f8804772f8901542bebb6629e6be4179461
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sun Mar 22 02:12:44 2026 +0200
fix(ui): split tab strip into two rows to prevent overflow
11 visible tabs at ~73-80px each exceed the 828px available in a single row.
Split into row 1 (first 6: Summary-Dummy) and row 2 (last 5: Rating-Cleanup).
tabStrip height: 28 → 62 (two 28px rows + 6px gap); contentShell shifts down
automatically since it anchors to tabStrip BOTTOMLEFT.
commit ac952629b3801228aff05bef8ff7b2d9bb472877
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sun Mar 22 02:08:00 2026 +0200
fix: strengthen isSecretValue to catch concat-survives-but-still-tainted case
On WoW Midnight, tostring() and .. propagate the secret flag rather than
stripping it. The previous single-pcall test (tostring(v) .. "") succeeded
for many tainted strings, making IsSecretValue return false and letting
tainted values reach table-key assignments (SnapshotService.lua:385:
"table index is secret").
Add a second pcall that tests asStr == "" — comparison to a plain string
reliably throws for any tainted value, even ones that survived concatenation.
This matches the double-pcall pattern already applied in getOrCreateBucket.
All four IsSecretValue call sites (SnapshotService, CombatTracker,
ArenaRoundTracker, ApiCompat.GetCreatureIdFromGUID) benefit automatically.
commit d4250c4938f0adc235f663c03a90788fe7510a67
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sun Mar 22 02:05:52 2026 +0200
fix: correct two bugs found in post-sprint review
ReplayView._label: SetTextColor(cr or unpack(...)) only passed the R channel
when cr was non-nil. Fix with proper if/else so all four channels are
forwarded; drop redundant color args from the empty-state caller.
CombatStore: remove GetSessionById — an exact duplicate of the existing
GetCombatById (line 534). Update CombatHistoryView Replay button to use
the canonical GetCombatById.
commit 04e7730592f31ba4ad0924f88dc6cf732510f830
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sun Mar 22 01:58:32 2026 +0200
feat(ui): register Builds tab in MainFrame; add BuildComparatorView to TOC
Inserts the Builds tab between Counters and Cleanup. BuildComparatorView.lua
added to load order so it is available when MainFrame registers tabs.
commit 9a87eb0cb038abf08f7af36ee17f7f9aed88f9ef
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sun Mar 22 01:55:39 2026 +0200
feat(ui): add BuildComparatorView — side-by-side build performance comparison
Tab panel with < / > selectors for Build A and Build B. Comparison table
shows Record, Win Rate, Avg Pressure, Avg Damage, Avg Deaths, Avg Dmg Taken.
Winning side highlighted green. Values prefixed ~ when < 5 fights. Verdict
line summarises which build wins more metrics. Empty state for no history.
commit 12d25fddb768a8ff0c60b96563d3fff05d8ac46d
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sun Mar 22 01:53:44 2026 +0200
fix: pcall-guard GetCreatureIdFromGUID against secret arena GUIDs
On WoW Midnight, C_CreatureInfo.GetCreatureID and strsplit are C functions
that explicitly reject secret string arguments with a hard error. Wrap both
paths in pcall so UNIT_AURA handlers don't crash when the unit's GUID is
tainted.
commit 012bde18a0de3488e8e6a5dd8556b0347e35935d
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sun Mar 22 01:52:03 2026 +0200
feat(ui): wire Replay button in history view; add ReplayView to TOC
Add one small Replay button per history row. On click it calls
CombatStore:GetSessionById (new helper) then ns.ReplayView:Show(session).
Buttons show/hide alongside their row in Refresh. ReplayView.lua added
to load order in CombatAnalytics.toc.
commit a201e8afa7b9c6f0b7ea19b81e443d7875cc934b
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sun Mar 22 01:50:16 2026 +0200
feat(ui): add ReplayView — session timeline canvas with 4 lanes and coaching cards
Stateless floating frame. 4 lanes: offensive casts, defensive casts (cooldowns),
CC received bars, kill window bars. Death marker vertical line. Time axis with
tick labels. 3 coaching cards: Opener, CC Pressure, Death Context.
All data sourced from session.rawEvents / ccReceived / killWindows — no CLEU.
commit dea0b17cd30ee6c00017e68f34b034239fcf3cd7
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sun Mar 22 01:47:59 2026 +0200
fix(P0): replace broken playerSnapshot field reads with canonical accessor
Three consumers read runtime.playerSnapshot (never written); buildHash was
always nil, disabling build-personalised guide decisions and pre-match advisory.
Replace all three with ns.Addon:GetLatestPlayerSnapshot() which reads the
correct runtime.latestPlayerSnapshot field.
Also fix CounterGuideView getSpecWinLoss to use GetAggregateBucketByKey so
W/L badges populate from real history instead of silently returning zeros.
commit c2c1e69facf1661e4c763f55dfc159d491e42415
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sun Mar 22 01:47:28 2026 +0200
fix(P0): replace map-index aggregate lookup in SuggestionEngine
SPEC_WINRATE_DEFICIT rule was indexing GetAggregateBuckets result as a map,
always getting nil and never firing. Switch to GetAggregateBucketByKey.
commit 7597c21068cc9f499e31c467a810dbb0e999b840
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sun Mar 22 01:47:12 2026 +0200
fix(P0): replace map-index aggregate lookups in StrategyEngine
GetAggregateBuckets returns a list, not a map. Indexing by specKey always
returned nil, zeroing out historicalWinRate and breaking HasSufficientData.
Swap both call sites to GetAggregateBucketByKey which does a key scan.
commit 2aa70d836811ac835ed4570751b9c495eaa1d032
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sun Mar 22 01:46:36 2026 +0200
fix: guard secret-string comparison in getOrCreateBucket
On WoW Midnight, secret arena strings survive "" concatenation but still
throw on comparison with plain strings. Add a second pcall around the
safeKey ~= "" check so the crash at line 173 never reaches the Lua VM.
commit c306c9706f30008c272f75cc6b999d951d0a03df
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sun Mar 22 01:40:11 2026 +0200
feat(store): add GetAggregateBucketByKey and GetSpecBucket helpers
commit 0280c49c23749b0c8c7014354d626ee904701c2e
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sun Mar 22 01:33:55 2026 +0200
docs: add Sprint B implementation plan (9 tasks, P0 fixes + timeline + build comparator)
commit fd5619f696d86e3298b4d05926f52b84ecf0e845
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sun Mar 22 01:17:47 2026 +0200
docs: add Sprint B design doc (P0 fixes + timeline replay + build comparator)
Covers two P0 correctness fixes (aggregate lookup helpers, snapshot accessor
inconsistency) and two new features (Visual Timeline Replay, Build Comparator).
commit f3462953829ad10307669b2a77aef3ea3bc8274a
Author: Lazar Dilov <ldilov@yahoo.com>
Date: Sun Mar 22 00:37:24 2026 +0200
fix(arena): prevent player's own name appearing as arena opponent
Three defensive guards against the "played against myself" bug:
1. GetPrimaryEnemy: skip slots whose GUID matches the local player.
After a match ends, arena unit tokens can briefly resolve to the
player's own GUID during unit-frame transition states.
2. HandleArenaOpponentUpdate: reject buildUnitSnapshot results whose
GUID is the local player before storing into the slot or guidToSlot
map. Prevents the contaminated slot from reaching GetPrimaryEnemy.
3. ResolveOpponentName: when entry.guid is nil (harvested while still
secret, or not available), fall back to name comparison to skip the
player's own score entry. nil ~= anyGUID is always true, so
without this guard the player's own entry passes the old check and
their name is returned as the opponent name.