ThreatLogger-v0.2.0-classic
What's new
Changelog
[v0.2.0] - 2026-10-05
Fixed
- Upgrading from v0.1.x would error on load. v0.1.x saved
ClassicThreatLoggerDBas{ version, encounters }, and the new code read alogit did not have. Superseded by the move to per-character recordings below: the account-wide table is no longer read at all. /ctl instance offdid not survive a reload.InitDBloaded each saved setting asSettings.x or Config.x, so a savedfalsefell through to thetruedefault and instance auto-start switched itself back on at every login. Settings now load when they are notnil.- The log grew without limit, was loaded by every character, and slowed every login.
maxEntriesonly printed a warning, twice a second for the rest of the session. The whole log was one account-wide SavedVariables table, which the client reads into memory for every character at every login and/reload; one real save file had reached 101,470 lines and 21 MB. The log is now meant to live for a day or two, for a raid's record keeper to copy out (see Changed). Whether this was a measurable part of the slow startup is not verified. - Rows of zeros for units with no threat. When
UnitDetailedThreatSituationreturned nothing (the unit is not on the mob's threat table) the poll still wrote a row of zeros andfalse: 58,628 of the 101,470 lines in that save file. Nothing is written for them now. /ctl listcut timestamps at a fixed 26 characters. The timestamp's length depends on the timezone offset (-6vs+10), so the list showed a stray character or lost one. It now takes everything before the two-space separator.- A threat value past 2^31 would raise. The threat column was written with
%d, which raises "integer overflow" past 231, and nothing bounds the value the threat API returns. It is now written with%.0f, which prints the same digits. Whether a real fight reaches 231 is not verified. - "Active Mob Targets" in
/ctl statuswas always 0. It counted a table nothing ever filled. Status now shows "Mobs in last poll", the number of distinct mobs the latest poll saw. - A large recording would have saved a file that does not load, losing everything (peer review
of this release). Each line was its own string, and a SavedVariables file is one Lua chunk, which
Lua 5.1 refuses past 2^18 - 1 distinct strings ("constant table overflow"; reported for WoW saved
files on forum.buffed.de, and reproduced by a spec). That is about 262,000 lines, near 50 MB, under
the 170 MB default. A file that fails to load arrives as
nil,InitDBreplaced it with an empty table, and the next logout saved that over every recording. Lines are now stored in 16 KB blocks (see Changed), which puts the largest limit, 1000 MB, at 64,000 strings. That the WoW client keeps stock Lua's limit is confirmed by player reports, not by a test in the client. - The UTC offset was an hour off during daylight saving time.
FormatTimestamppasseddate("!*t")totime()withisdst = false, so the UTC fields were read as standard time:-6in a US Central summer where the client's combat log writes-5. The local daylight flag is now carried over, and a spec pins-5. - Timestamps could run backwards within a second. The whole second came from
time()and the milliseconds fromGetTime(), two clocks with no shared second boundary. Every stamp is nowGetTime()plus one offset (CTL:Now), which only ever moves forward, except that a drop of more than 2 seconds (the PC's clock set back) is followed rather than leaving every later stamp ahead. - Combat auto-start stopped recordings it had not started. Leaving combat stopped any recording,
so with both auto-starts on a raid's recording ended at the first pull and every later pull became
its own session; a manual
/ctl startended with the first fight. Each recording now remembers what started it: combat ends only a fight's recording, leaving an instance ends a fight's or the instance's, and a manual one runs until/ctl stop. /ctl intervalhad no upper bound, so/ctl interval 1e999handed the ticker infinity. It now takes 0.1 to 10 seconds./ctl listsplit every stored line to print the last few, a freeze on a large raid save. It now skips whole recordings by their line count. The Live log also reads the block being written from memory instead of re-splitting 16 KB twice a second.- The Live log dropped a selection twice a second. It is no longer refreshed while it has focus, so Ctrl+A, Ctrl+C can copy; it catches up once you click away.
Changed
- Breaking: recordings are per character, one per session, and delete themselves.
ClassicThreatLoggerDB(account-widelog) is replaced byClassicThreatLoggerCharDB(## SavedVariablesPerCharacter):{ version = 3, sessions = { { started, ended, bytes, lines, blocks } } }.linesis the line count andblocksthe lines themselves, about 16 KB of lines joined by\nper string (see Fixed). A version 2 recording, one string per line, is repacked into blocks at load. An alt no longer loads the raider's recordings. The old account table is deleted at load and, being no longer listed in the TOC, is not saved again.- Kept by age: a recording older than
retentionHours(default 48, 1 to 720,/ctl keep) is deleted at login and atPLAYER_LOGOUT, the last event before the file is written. - Kept by size:
maxMB(default 170, 10 to 1000,/ctl max) is measured from the recordings' actual saved size (CTL:LineBytes: the line, its escaped quotes and backslashes, and the\nthe client writes between lines; a spec checks the running total against the file's exact text), and the oldest finished recordings are deleted past it. The default is calculated for a whole 40-player raid night: 40 x 2 readings a second x 3 hours x 200 bytes, rounded up to 10 MB. 200 bytes covers a measured 191-byte raid line; a spec fails if a real line outgrows it. - The recording in progress is never deleted, however large, so a raid is always captured whole.
- The line-count cap (
maxEntries,TrimLog) from earlier in this release is gone.
/ctl flush(and/ctl clear) ask before deleting, through LibAceGUIWidgets' dialog.- Breaking: the log follows the client's combat log layout (log format version 2). Each line
is a date-time stamp, two spaces, an event, then a comma-separated payload, as in
Logs\WoWCombatLog-*.txt.- The stamp is the combat log's own: month and day are no longer zero-padded (
8/7/2026, not08/07/2026), matching8/17/2026 10:10:26.692-5in a combat log on this box. - Every session opens with
CTL_LOG_VERSION,2,ADDON_VERSION,…,BUILD_VERSION,…,PROJECT_ID,…, modelled onCOMBAT_LOG_VERSION, and closes withCTL_SESSION_END. - Zone and subzone moved out of every row into
ZONE_CHANGE,instanceID,"zone",difficultyID(the combat log's own fields) andSUBZONE_CHANGE,"subzone", written only when they change. THREAT_SNAPSHOTis nowunitGUID,"unitName",mobGUID,"mobName",isTanking,status,scaledPct,rawPct,threat: unit then mob, as combat log events order their units, then the values in the threat API's return order.isTankingis1/0instead oftrue/false.
- The stamp is the combat log's own: month and day are no longer zero-padded (
- Removed the
<unit>focuslookups. Every poll also readplayerfocus,party1focus,raid3focusand so on. Those are not unit tokens: the wiki's UnitToken page listsfocusonly as the player's own base token, so the lookups never matched. Not checked in-game. - Removed dead code: the unused
UnitThreatSituationlocal, the never-readcombatStartTime, the never-filledactiveEncounters, andCountEntriesInLog, a duplicate ofGetLogEntryCount. - The addon title in the AddOns list is now in the brand orange.
Added
- A settings window (
/ctl options,ClassicThreatLoggerUI.lua), built on AceGUI-3.0 and LibAceGUIWidgets (ClearFrame, LAGW-Stepper, ShowDialog): the two recording switches, the keep time, the size limit, Delete all recordings, a status line of what is stored, and a Live log (AceGUI MultiLineEditBox) of the newest 30 lines exactly as saved, refreshed after every poll and at start and stop, for checking the format in game. Every control calls the same setter as its slash command (SetRetentionHours,SetMaxMB,SetAutoStart, ...). /ctl keep [hours]and/ctl max [MB];/ctl statusshows both and the stored size.- VersionCheck-1.0: registered at load with
VC:Enable("ClassicThreatLogger"), the library's documented wiring, so guildmates are told when a newer version is out. - Dependencies:
## Dependencies: Ace3, VersionCheck-1.0, LibAceGUIWidgets, and the same three asrequired-dependenciesin.pkgmetaso CurseForge installs them; a spec pins the two together. - An offline test suite on the WoWAPITesting harness (
Tests/wowapisubmodule,Tests/threatlogger_spec.lua): 90 specs, 100% line coverage of both Lua files, loading the installed AceGUI-3.0, LibAceGUIWidgets and VersionCheck-1.0. Run it withlua Tests/wowapi/run.luafrom the addon root. Harness pinned atcc6a10c. .luacheckrc, with the WoW globals the addon reads.- An MIT
LICENSE, the same text as the other Pimptasty addons. The README's "personal use only" line now points to it.
Packaging
.pkgmeta: comments removed,Testsignored, and the entries that could not work dropped..git/,.github/,.vscode/anddocs/had trailing slashes, which the packager turns into patterns that match nothing; dot-folders are pruned by the packager anyway.**/*.ps1missed root-level files.docs,*.txt,*.ps1and*.batmatched no file in the repo.
This mod has no additional files

