v0.1.0
What's new
EasyWarCrates
v0.1.0 (2026-09-26)
Full Changelog Previous Releases
- Describe the release as it actually is
The changelog was written before a day of work that changed what this addon
does: it reads the countdown line a raid leader posts, keeps the cycle after
somebody else's crate is looted, marks Spectral Battle Chests, stays quiet in
chat by default, and reports a locale gap rather than failing silently in it.
A first release gets read by people deciding whether to install it. Listing six
things it did yesterday would have been the wrong six. - Give the chest its own row whenever the zone's row is already busy
It was splitting only when a crate was in the air or coming down. A countdown
was not counted as the row having something to say, so once Slayer's Rise had
one -- which it now does the moment a raid posts theirs -- the chest went back
to riding along, and the row read "SR 45 spectral" beside a pair of times with
nothing saying the chest was on the ground at all.
The numbers on that row are about the crate. The chest has no countdown and
wants none: it is there now and gone in a minute or two. So it rides along only
when the zone's row would otherwise be blank, which is what "nothing else to
say" was always meant to mean.
The spec for the quiet case was seeding a timer, so it had been testing the
busy case under the wrong name. Its Scanner stub was also missing ActiveZones,
which is what earns a zone a row when nothing is timed there -- so the quiet
case had no row to check at all and passed by accident. - Keep the cycle a raid hands you after its crate is gone
Dmitrii never flew to Zul'Aman. The raid watched the transport, the parachute
and the landing there, this addon showed every stage of it, and then the row
went blank as though nobody had seen anything -- while the leader was standing
over the crate he had just looted.
A received sighting of a crate on the ground is worth showing for ninety
seconds, and that is right: a row still saying ON THE GROUND ten minutes on
sends somebody across a zone for something already looted. But the sighting
carries two facts and only one of them expires. The crate is gone; the moment
it landed is not, and that pins the shard's cycle for hours.
So a landed report settles into an anchor instead of evaporating, at the time
it actually landed. Which exposed the other half: anchors drew nothing at all.
Remote.For refuses them on purpose -- an anchor says a crate existed, not that
one is in the air, and a live row claiming otherwise sends a raid somewhere
nothing is happening. True, and it left anchors feeding only the shard lookup.
An anchor cannot make a live row and can perfectly well make a countdown, which
is now what it makes, in the same columns a timer of our own fills.
A zone gets that row even with nothing timed there, which is the case being
fixed. Otherwise a countdown only appears once this client has been to the zone
itself -- and the whole point of a raid that splits up is that it does not have
to be.
The row carries the dot that marks somebody else's word, because their anchor
is about the copy of the zone they were in. Our own timer still wins outright.
This is also what makes the countdown line read last night worth anything. It
was parsed into anchors, and anchors drew nothing, so it had been quietly
achieving very little. - Survive a locale this addon has never seen
An audit before publishing, because most players are not on an English realm.
The logic holds: vignettes are matched by id and never by name, zones by map
id, the faction comes back as an English token, and GetLocale is never read.
Nothing decides anything from localised text. Two places degrade, though, and
one of them degrades silently.
Zone names were shipped English and shown as such. A German player reading
"Eversong Woods" in a window while their game says Immersangwald is being told
about somewhere else, and the game answers the question for free. Asked once
per zone and cached, falling back to the shipped name when it will not.
The silent one is the announcer. Its name is localised as much as its lines
are, and only English and Russian are written down -- so on any other client
the name test fails on every line and the yell anchor, one of the three things
the empty window promises, never fires. Worse, the refusal happened before the
log, so /ewc yells showed nothing and "no announcer on this locale" was
indistinguishable from "nobody has spoken".
Speakers a crate zone hears that are not known announcers are now counted on
the way past, with the line they said, and /ewc yells reports them: if a cycle
started when one of those spoke, that line is what this addon needs. Bounded to
a tracked zone and a dozen names, which is a ceiling against a surprise rather
than a budget.
Same shape as the zone names heard on the wire, and that one has already paid
for itself twice this evening. - Read more of what the raid is already saying
Two gaps in the same place: RCT posts plenty in chat and this addon was reading
one line of it.
The countdown, every cycle, for every zone the raid is watching:
Next Crate: Zul'Aman - 1258 in 02:46
Next Crate: Slayer's Rise - 45 in - 20 s
Zone, shard and time remaining -- a timer, in plain text, from whoever is
leading. Only the "Flying in X" alert was being read, and that fires for the
one zone happening now. Dmitrii sat in Slayer's Rise for two hours while a raid
broadcast the above for five zones, and watched his rows age out one reload at
a time.
Turned into an anchor rather than carried as a countdown: a spawn one interval
before the announced drop puts the same cycle in the same phase, and everything
downstream already knows what to do with a spawn. Filed as somebody else's word
like every received report -- their arithmetic, not a crate anyone saw -- so it
cannot overrule a sighting or become a stored timer on its own. Refused past a
whole cycle, because beyond one interval it is not counting to the next drop
and an anchor from it would put the phase somewhere it has never been.
And two zone names, which the same raid supplied. RCT sends its alert using the
sender's own localised name and the game will only tell a client the names in
its own locale, so a German raider's report was unreadable here. Leerensturm
and Schlaechteranhoehe are now written down, each resolved from the wire rather
than translated: the name arrived carrying a shard, and that shard was one this
client had independently confirmed for a zone in its own timers and phase
memory at the same moment. The morphology agrees separately, which is two lines
of evidence rather than one.
The weakness is recorded beside them. Shard ids are not unique across zones and
Slayer's Rise is nested inside Voidstorm, so the record of untracked vignettes
shows both ids under both names. What makes it tolerable is the shape of the
mistake available: confusing those two sends somebody to a zone they are
standing in the parent of, where the same error between Eversong and Harandar
would send them across a continent. Anything unresolved is still collected
rather than dropped, which is how these two arrived. - Leave room for the row the chest now takes
Six zones is a full rotation and already six rows. Giving the chest one of its
own makes seven, and a zone with a transport overhead but nothing timed takes
one as well -- so a cap of eight was one surprise away from dropping a row
without saying so, which reads as the addon not having seen something.
Raised rather than made clever. The window grows with what it has to show and
ten is past anything a rotation produces. - Give the chest its own row once the zone has a crate as well
Two cases, and they want different answers. On a quiet zone the chest rides on
that zone's row: one place, one line, and the row has nothing else to say. Once
a transport is in the air or a crate is coming down there, one row is being
asked to carry two things that are collected separately, at different times --
and a click on it can only mean one of them.
Split, each row says one thing and a click does what the row says. The crate
row drops the chest when the split happens, so they cannot both claim it.
The chest row is ranked with a crate already on the ground, because both are
there to be collected now. A transport three minutes out is not, which is what
put "transport in the air" into a raid warning while a chest lay in the zone.
Its row draws and describes itself as a chest rather than as a crate: the live
entry it carries exists to rank it and fill the bar, and reading that as a
crate would have printed ON THE GROUND, OURS about somebody else's chest.
Also fixes the record of untracked vignettes, which this uncovered. It
deduplicated by GUID, and a vignette's GUID churns while the object stays put
-- which is already why the transport is announced per zone rather than per
track. Slayer's Rise reported eighty-seven sightings of one set of remains and
"repeats about every 0s", which is not a measurement, it is the poll rate. Keyed
by what the object is and where it is now, with five minutes before a sighting
counts as a new one, and a period is only reported when the readings cluster
and the figure could be a cycle at all.
On Dmitrii's own data that now reads: Spectral Battle Chest, about every 1800s,
give or take 24. Thirty minutes, which is what the wiki says, arrived at from
his sightings rather than copied from it. - Restore ns.Print, which a blanket rename had taken with it
Moving the event narration onto ns.Say was done with a string replacement over
the whole of Core/Main.lua, and "ns.Print(" appears in that file in one place
that is not a call: its own definition. So ns.Print stopped existing, all 147
calls in Core/Commands.lua became nil calls, and every /ewc command went quiet.
Quiet, not broken-looking. The dispatcher catches a handler that throws and
reports it -- through ns.Print. So the report failed the same way the command
did, and a player typing /ewc scan got nothing at all: no output, no error, no
reason.
The harness could not have caught it, because the harness was the thing hiding
it. FRESH() replaced ns.Print with its own collector before anything ran, which
is to say it supplied a working copy of the one function that had gone missing.
It now leaves all three alone, captures the global print instead -- where a
command's answer actually ends up -- and checks outright that Print, Say and
Debug exist before calling anything.
Renaming ns.Print away again was watched turning it red, naming both the
missing function and the commands that fell over, and green again once put back. - Stop talking in chat unless asked to
An addon that narrates unprompted is one people route into a spare tab or
uninstall, and a first-time player reading forty lines about shards and descent
readings has been handed a diagnostic channel, not a feature. Off by default
now, with a checkbox and /ewc chatter.
The split is between what the addon says on its own and what it says because
somebody just did something. Every event-driven line in Core/Main.lua and
Detect/Scanner.lua moves to ns.Say and can be silenced. The commands, the
window and the panels keep ns.Print: those are answers, and an answer that does
not arrive is a bug.
Two exceptions, both deliberate. The load line stays loud -- one line a session,
and the only place a new player is told there are commands at all. And verbose
implies this, because turning on the detailed mode to find out what is
happening and getting silence would be absurd.
The log is never silenced. db.log is what a failure gets diagnosed from after
the fact, and it is how Dmitrii's logs reach me at all; a switch that emptied
it would trade a real capability for a cosmetic one. tools/firstrun.py drives
all three switch positions and checks the chat lines and the log lines
separately -- silencing the log alongside the chat was watched failing it
before this was committed. - Shift-click the title to name the addon, and nothing more than name it
Dmitrii asked for this and then asked for the download sites in it as well.
Blizzard's addon policy is explicit on the second part: an addon may not be
used to advertise goods or services, and pointers to where it is distributed
belong on its own site rather than in the game. So the message is the name and
what it does, with no store and no link.
The line is worth drawing because the two cases are genuinely different. A
player telling their raid what they are running is that player speaking, and
the addon is composing the sentence for them. An addon putting store links into
chat by itself is the thing the policy names. Keeping to the first costs
nothing here -- anyone who wants it can search the name.
Shift held, because the title is the natural place to grab a window by and
moving it must not speak to twenty people. The button over the title forwards
dragging for the same reason: taking that away to gain a click would be a poor
trade.
Never a raid warning, even from a lead who could send one. This does not
warrant interrupting a raid, and the row announce is where that channel earns
its place. A minute's cooldown, because a button that talks to a group and can
be held down is a button that will be.
tools/firstrun.py drives it from a raid lead and checks both the returned
channel and the one the send actually used; inverting the rule so it took the
warning was watched failing before this was committed. - Point the packager at the Wago project too
Both stores are now named in the .toc, which is where the packager decides
where a token uploads. Wago matters more than a second listing: CurseForge
refused WowUp's application for API access and Wago partnered with WowUp
instead, so this id is how a WowUp user reaches this addon at all. - A template for the tokens, and a check that does not cry wolf
.env.example documents the two names and where each one really belongs, with no
value in it. The workflow cannot read a .env at all -- it runs on GitHub's
servers -- so the file is only for releasing from a machine, and the comment
says so rather than leaving someone to find out by watching a release do
nothing.
The check flagged the template. Its own fault: \s crosses newlines, so an empty
CF_API_KEY= ran on and matched WAGO_API_TOKEN on the line below as though that
name were a value. Spaces and tabs only now. A rule that reports a file with no
secret in it gets switched off, and then it is worth nothing.
Both directions watched again afterwards: the template green, a .env forced
into the index with a real-shaped token red, and green once more with it gone. - Refuse a token rather than rely on the ignore file
Dmitrii wants a local copy of the upload tokens in a .env, which the BigWigs
release script reads and which is the supported way to release from a machine
rather than from CI. Reasonable, and it is also the one mistake in this
repository that a later commit cannot undo: a token in a public repo has to be
revoked and reissued, and every install between those two moments had it.
.gitignore alone is not the guard.git add -fwalks past it, andgit add -A
has swept unintended files into commits twice on this project in a day.
So the suite refuses one. Shapes rather than entropy, because a CurseForge or
Wago token is a UUID and reads as ordinary text -- what gives it away is the
name it is assigned to. The workflow's ownCF_API_KEY: ${{ secrets.CF_API_KEY }}
is not a leak and is not reported.
Watched failing on all three shapes before this was committed: a .env forced
into the index with both token names, and a GitHub token pasted into a tracked
file. Then removed, and watched going green again. - Point the packager at the CurseForge project, and write the first changelog
X-Curse-Project-ID is what decides where a token uploads. Without it that site
is skipped without complaint, which is the kind of silence worth removing now
rather than reading a green workflow run and wondering why nothing appeared.
The changelog is written rather than generated. With no earlier tag the
packager has ninety commits to summarise and would hand a reader all of them;
what someone deciding whether to install this wants is the six things it does
and the one thing it refuses to do.
X-Wago-ID follows when that project exists. Same rule: no id, no upload there. - A logo drawn from what the addon does
CurseForge requires one to create a project at all, at 1:1 and no smaller than
400 across, and refuses a solid or gradient colour, a game logo or a
trademarked asset. So this is the crate under its parachute with the spot it is
predicted to land on, in the three colours the addon already prints in: the
teal of its own chat prefix, the amber of a countdown, the green of a call it
is sure about.
Generated rather than drawn by hand, so it is reproducible and the geometry is
readable as numbers. tools/make_logo.py renders at four times and downsamples,
then reopens the file and checks what actually landed -- square, at least 400,
and enough distinct colours that it cannot be the flat fill the rules refuse.
It stays out of the published addon. .pkgmeta ignores assets and the .toc never
listed them, so it belongs to the listing page and not to the download. - Say how to work on this before anyone asks
What a contributor needs that the code does not say: that lupa is the only
dependency, that the suite runs the addon's real Lua rather than a mock of it,
and that the line in the .toc where files stop being testable is the line where
they start creating frames at load.
Two traps get their own section because each cost a shipped bug. The test
interpreter is Lua 5.5 and the game is a 5.1 dialect, so goto parses green here
and dies in the client. And a local read above its own declaration is a nil
global rather than an error, which fails in game and nowhere else -- the lint
catches it now because it did not catch it the first time.
The house rules are the ones this project actually runs on: show a check red
before trusting it green, look at the artefact rather than the exit code,
measure rather than assume, keep the vignette ids in one file, bundle nothing.
Each is there because ignoring it cost something. - Rank the chest by when it can be taken, and say where it is
Dmitrii clicked the Slayer's Rise row with a chest lying in it and the raid was
told "transport in the air". Mine: I ranked a crate this client can see above
the chest on the reasoning that both mean go now. A transport in the air does
not mean go now, it means in three minutes -- his own log has one at 3:03 --
while the chest is on the ground and gone in one or two. A crate already on the
ground still outranks it, because that one really is there to collect and is
why anyone is in the zone.
The line now carries coordinates, which a crate line deliberately does not. A
crate leans on the pin travelling with it, and the pin only yields a clickable
link when the waypoint readback agrees about the map. A crate survives that
failing: it has a countdown and a zone, and it will still be there. The chest
has neither, so "SPECTRAL CHEST in Slayer's Rise" with no link has told the
reader nothing they can reach in time.
What is documented about it is now written beside its id, because there is very
little: object 527903, added in 12.0.1, spawning every thirty minutes in
Slayer's Rise for up to five players of either faction. Whether that half hour
runs from the spawn or from the last loot is not recorded anywhere, nor where
in the zone it appears, nor how long it stays. The wiki page has no screenshot.
So the figure sits in a comment with its source and nothing is built on it, and
the record of untracked vignettes measures the rest -- the way every interval
in this addon was arrived at. - Credit the characters the numbers were measured on
Dmitrii asked for his accounts in the credits, and framed as what they actually
are they belong there: nearly every figure this addon relies on -- the descent
times, the cycle lengths, the release bias, the drop points -- was read off
those characters across four realms and both factions rather than assumed or
copied from another addon. That is the part worth saying.
The copyright line stays one holder. A licence names who to ask, not who
played. - Say who wrote this and on what terms
A public repository with no licence is all rights reserved by default, which
for an addon is the wrong answer by accident: nobody could fork it, send a
patch, or redistribute it, and CurseForge asks for one when a project is
created. MIT, which is what the addon world runs on and what WarCrateTracker
uses -- the addon the Credits already borrow a convention from.
Nothing here is anybody else's to license. LibDeflate and AceSerializer are
borrowed at runtime through LibStub from whichever addon already loaded them,
so none of it ships and none of it carries terms into this.
One author, not a roster. Dmitrii offered all eleven of his characters and the
copyright line takes a holder rather than a list -- and a cross-realm roster in
a public repository lands in every user's addon folder, where it cannot be
taken back. Dazm is the main and matches the handle the commits already carry.
X-Curse-Project-ID and X-Wago-ID stay out until those projects exist. They are
what tells the packager where to upload, and a wrong id uploads this over
somebody else's addon. - Publish to both stores from one tag
CurseForge refused WowUp's application for API access and Wago partnered with
WowUp instead, so the two are not alternatives: a player running WowUp can only
reach this addon through Wago, and CurseForge's own client only through
CurseForge. Publishing to one leaves half the audience unable to install it.
The BigWigs packager does both, plus a GitHub release, from one annotated tag.
Which project each token uploads to comes from the .toc -- X-Curse-Project-ID
and X-Wago-ID -- so a missing id means that site is silently skipped, which is
worth knowing before reading the first run's log and wondering.
.pkgmeta gained assets, dist and .github. The screenshots belong on the listing
page rather than inside the download, and dist is the archive this repo builds
for itself. tools/package.py refuses the same names and then reads its own
archive back, so a name missed in one is still caught by the other before
anything is uploaded.
No tokens here or anywhere in this repository. They go in the repository's
Actions secrets, where the workflow reads them and nobody else does. - Ignore the build output
tools/package.py writes the publishable archive to dist/. Built from the repo
on demand and never committed: the thing that gets uploaded should be produced
from a known commit, not carried alongside one and liable to drift from it.
tools/ itself is already ignored, so the script lives beside deploy.py and
loglens.py where the other things that are not the addon live. - Screenshots for publication
Dmitrii's, taken in Midnight 12.2: the window in each of the states it has
(inbound, landing, on the ground), the settings and data panels, and the
Slayer's Rise row carrying a Spectral Battle Chest.
Tracked rather than ignored, unlike tmp/ and tools/. These are part of what
gets published, not notes that must not be. - Let a click announce the chest, and pin what it actually said
Dmitrii saw one, the row marked it, and clicking said "nothing is known about
that zone worth announcing". That refusal was mine: the tooltip only offers a
click when Model.Announcement produces a line, and nothing here produced one for
a row carrying only a chest.
It is the thing most worth announcing of anything on that row. It lasts a
minute or two, it is drawn from anywhere in the zone so anyone in there can
reach it, and unlike the crate nobody else's addon is broadcasting it.
Ranked where it belongs rather than bolted on the end. A crate this client can
see outranks it, since both mean go now and the crate is why people are there.
A crate that arrived through RCT does not: that was a raid warning everyone has
already read, and the chest was not in it. And a chest on the ground beats a
countdown to a crate that is not there yet.
Announcement now names its subject and AnnounceRow pins that one. A row can
carry a crate and a chest at once, and a line about one with a pin on the other
sends the reader somewhere else with our own link as the evidence -- the same
fault as the tooltip not naming its channel, and worth closing the same way. - Mark the chest on the zone's own row, and catch the bug that shipped doing it
Dmitrii asked for the Slayer's Rise row to carry something like "spectral"
beside the shard, and that is the right shape: the chest is a second object in
the same copy of the same zone, not a stage of the crate. Two rows for one place
is the RCT complaint. So it rides on the row, beside the shard, and the
countdown column stays the crate's.
Its id is kept out of the crate stage table deliberately. Nothing that reasons
about crates should be able to mistake it for one -- there is no transport to
fit a heading to, no parachute to time, and no release. What it needs is to be
noticed, placed, and counted.
A zone with a chest and no crate timer still earns a row, through the same path
that gives one to a zone with a transport overhead and nothing timed. The grace
before it is believed gone is short, fifteen seconds rather than the crate's
ninety, because Dmitrii reports the vignette is drawn from anywhere in the zone:
absence is an answer here, not the player having flown out of range.
Two things fixed on the way. A row that exists only for the chest has nothing to
post, and the tooltip was promising a left-click would post it anyway -- the
same fault as not naming the channel, so the line only appears when there is
something to say. And the record of untracked vignettes keeps filing this one
even though it is now recognised, because that record is what measures its
cycle and being able to draw it is no reason to stop learning.
The lint gained the rule that should have caught what I shipped mid-change.
liveSpectral was declared beside the tables it belongs with, six hundred lines
below the function that iterates it, and pairs(liveSpectral) is a call to pairs
with a nil global as its argument: tests green, lint green, deploy green, /ewc
show dead. The old rule only caught a local being CALLED before its
declaration. Reading one is the same bug, it now reports it, and moving the
declaration back down was watched turning it red. - Trust a stranger's position only on its own zone map
The crate path checks that a vignette's position resolved against the zone map
before using it, because a vignette can answer against a different map and the
coordinates then point somewhere the player is not. The new stranger record
stored whatever came back. Guarded the same way, and a reading refused is
counted rather than dropped silently -- if 6892 turns out never to give a
position this client can use, that is the thing worth knowing about it. - Keep a record of the vignettes we do not track
Dmitrii asked how to track Spectral Battle Chest, vignette 6892, which also
drops in Slayer's Rise. Nothing anywhere can answer that yet: none of RCT,
WarCrateTracker or CrateTrackerZK tracks it, and this addon had no memory of any
id it does not already know.
Three facts decide whether it can be tracked and all three are observations,
not deductions. Which zones it appears in. Whether it repeats. On what period.
His own account is that it simply appears on the ground, which if it holds means
there is no transport to fit a heading to and no parachute to time -- a spawn
catalogue and a cycle would be the whole of it, and none of the flight machinery
applies.
So every vignette with an unknown id is now filed as it appears: name, atlas,
which zones, where, and the shard each sighting came from. Deduplicated by GUID,
because a vignette stays drawn for minutes and every poll would otherwise read
as another spawn. /ewc scan prints the history under the live sweep and computes
a period once three gaps on one shard agree, using the same densest-run
estimator the intervals use.
The shard travels with each sighting for the reason it matters everywhere else
here: two copies of a zone run their own cycles, so a gap paired across them
measures nothing.
Worth having beyond this chest. Every id in Data/Vignettes.lua came from three
addons built against interface 120100, and if Midnight renumbers them the crate
stops being recognised -- at which point this record is what says what the new
numbers are doing. - Keep the shard when the zone name is in a language we cannot read
RCT broadcasts its chat alert using the sender's own localised zone name, and
the game will only ever tell a client the names in its own locale. So a German
raider's "Leerensturm" resolves to nothing here and the whole report was
dropped, shard and all.
One public raid produced three of them: Leerensturm and Schlaechteranhoehe from
a German client, and a Russian name besides. Those senders' flying calls are
exactly the reports worth having, and they were the only ones being thrown away.
The shard survives the name, and it is the handle on which zone was meant, so
FromAlert hands it back with the name and Comm files the name against the
shards it arrived with. A shard that also turns up in a report whose zone IS
readable settles the mapping.
Deliberately not resolved from the shard automatically. Shard 39 has been seen
in Harandar, Slayer's Rise and Voidstorm on one account, so a low id matches
more than one zone, and a name bound to the wrong zone is worse than a name
that does not resolve at all: the first sends people to another continent and
the second only loses a report. The addon collects the evidence and a human
weighs it.
/ewc comm prints each unreadable name with its shards, so one line can be sent
on when the sample is conclusive. - Measure the claim from the last sighting, not from when we got round to looking
The linger store has been empty since it was added, and it is the reason a
claimed crate says ON THE GROUND, OURS with no countdown beside it: the
countdown waits for three readings that agree and there has never been one.
Two constants conspired. A claimed crate's marker is not believed gone until
LIVE_STALE, ninety seconds, because it stops being drawn before the crate stops
being lootable -- that grace is the whole point. The claim was then required to
be less than CLAIM_MEMORY, a hundred and twenty seconds, old. Asked from now,
those compose: the marker had to vanish within thirty seconds of the claim for
anything to be recorded, and every reading from thirty to a hundred and
nineteen was dropped.
Which is precisely the range the store exists to measure. The question it was
added to answer is whether the marker outlasts the claim, and it could only ever
record the cases where it barely did.
The comment three lines below already said what to do -- dated from the last
sighting rather than from now, because the grace is this client waiting and not
the crate lying there. The lingered arithmetic obeyed it. The test that decided
whether to do the arithmetic at all did not.
No countdown appears from this commit. It appears once three readings cluster,
and now they can be taken. - Name the audience a click would reach, before it is clicked
A left-click on a row is not gated on the announce setting or on being
privileged, and that is deliberate: the setting governs the addon speaking by
itself, and a click is the player choosing to speak. The two gates are for two
different things.
Which is exactly why the tooltip has to be specific. It said "say this to your
group", and the same click is party chat, raid chat or a raid warning depending
on where you are and what you are -- and raid chat in somebody else's raid is
twenty strangers reading it. Dmitrii is about to join a public raid on a clean
profile to watch the sync work, so "your group" is the wrong amount of detail.
One function decides the channel now and returns the wording with it, because
the tooltip promising one audience while the send reaches another is worse than
either alone. tools/firstrun.py drives all four group states and compares what
the tooltip would say against the channel the send actually used; hardcoding
the send to PARTY was watched failing it before this was committed. - Ask the map question by its real name, and let a command say when it broke
C_Map.CanSetUserWaypoint does not exist. The function is
CanSetUserWaypointOnMap, and the wrong name has been in this file for as long
as the file has. It went unnoticed because of where it sat: the happy path in
SetCratePin stopped asking permission months ago, so the only surviving callers
were the branch that explains why a pin did not take, and /ewc pin. Pins kept
taking, so the explanation never ran.
Which means the branch written to say why nothing appeared would itself have
thrown, and /ewc pin -- the diagnostic that exists precisely because a pin
failed twice with nothing on screen to say why -- died after two lines. Twice,
on the clean profile, and the only trace was a truncated log.
Asked by its real name now, and guarded: nil when the client will not answer,
so the failure line says it cannot tell rather than throwing. A diagnostic that
throws is worse than one that admits it does not know.
The wider fix is the dispatcher. Every /ewc command now runs under pcall and a
broken one prints which command broke and where. That is what should have been
on screen instead of two lines and silence, and it holds for the next API that
gets renamed under us. - Take the release correction from the zone, not from the average of all of them
The correction was pooled across zones, on the stated grounds that the cause is
not zone-specific -- an average speed read over the fit window against a
transport that slows on approach would not care which zone it flew over.
98 readings say it does care. Slayer's Rise runs +42s, Voidstorm +33s, Harandar
+30s, Eversong +21s and Zul'Aman +18s, and the pooled figure every one of them
received was +26s. Zul'Aman's own 26 readings span ten seconds, so its mean is
well determined and it was being dragged eight seconds wide by zones it has
nothing to do with. Slayer's Rise was sixteen short in the other direction.
A zone's own readings now win once it has BIAS_MIN_N of them, and pooling is
the fallback for a zone that has not been timed enough to stand on. ETA already
carried the zone id, so nothing above it changes.
The spread travels with the figure, so a zone whose readings disagree still
says so rather than presenting a countdown as precise: Zul'Aman reports a
ten-second spread and Voidstorm a seventy-three. - Say where the pin went once, then move it quietly
Only the vignette path checked whether the pin had actually moved before
announcing it. The two prediction paths pinned on every sweep and said so each
time, so one transport that held a single answer for a minute printed six of
those lines for two decisions -- and two of the six had no decision line near
them at all, because nothing had changed.
Where the pin went is on the line above it either way. All three automatic
paths now go through one gate, which says it once per zone and then moves the
pin without narrating. A landing still re-pins however small the drift: that is
the one position nobody had to guess at.
/ewc pin and announcing stay loud. Both are things the player just asked for. - Fix /ewc route, and catch the shape of mistake that broke it
Route.Plan grew a shardOf parameter ahead of its now. UI/Model.lua was updated
and Core/Commands.lua was not, so /ewc route went on passing four arguments
and the timer it then tried to call as a function was the number now.
Lua does not check arity, so nothing saw it. The suite was green, the deploy
gate was green, and the command was dead for anyone who had set a route --
which is the same command, so /ewc route ZA ES followed by /ewc route was
enough. Dmitrii's route is empty, the loop never ran, and he never hit it.
The lint now reads every ns.Module.Func call against its definition and reports
a bare name sitting in a parameter of another name. Arity alone cannot do this:
trailing arguments are dropped deliberately all over this addon, and Phase
.Recall is called with six of its seven on purpose. A name at the wrong
position is different -- it is what a call looks like after a signature grew in
the middle and the caller did not.
It earned itself immediately: fixing the first one with a blanket substitution
put a fourth argument into Timers.Sorted, whose third is now. Green suite,
again, and /ewc timers would have shown nothing. So the rule ignores arity
entirely and reports the position, and both shapes were watched going red
before this was committed. - Say how well the starting point was known, not just the cycle length
The drift term answers how well a zone's cycle LENGTH is known. Nothing was
answering how well its starting point was, and the two are independent: a
mid-fall join looks like a falling sighting and is worth nothing like one,
because the crate left the transport an unknown time earlier.
Three of four descents on 24 Sep were mid-fall joins, so most of what the
memory holds is anchored by the weakest source there is, and every one of them
was being reported to two seconds. On the same data the yell-anchored shards
now read five or six seconds and the mid-fall ones forty-seven to
seventy-seven, which is the difference between a countdown and a direction.
The back-computed sources share one figure because they share one cause: each
subtracts an assumed descent from when the crate was found, and the readings
run 79 to 134, so the assumption can be most of a minute out.
A recalled phase re-enters the timers as "memory", so Absorb now skips those.
Filing one back would relabel the mid-fall join it came from as an anchor with
no error at all. - Stop counting a resample as a decision
A track that cannot choose between two drop points narrated sixty identical
lines in a minute on 24 Sep: same reason, same coordinates, one line a second
for the whole approach. The change-detection was already there and the sample
count was in its key, so every roll of the fitting window looked like news.
The count belongs in the line, where it says how much evidence the reading
rests on, and not in the key, where it only says the clock ticked. The same
minute now prints two lines, one per point it actually considered.
Also guards the nameplate unit token. A plate being torn down has none, and
UnitGUID on nil is an error rather than a nil return -- which is how it left
four of them in BugGrabber. - Show what is remembered, and what the countdown is really using
Two things the addon now knows and had no way to say.
/ewc phase prints the remembered cycle position for every shard, how many
cycles it would be extrapolated across and how far out that leaves it. The
error is the column that matters: on real data Eversong shard 1448 comes back
two cycles old at a second, and Harandar shard 58 comes back eleven cycles old
at eighty-eight, because Harandar's interval has never been measured on this
client and the drift falls back to the pessimistic figure. Same memory, same
age, and only one of them is worth believing.
/ewc interval was still printing only the mean after GetZoneInterval stopped
using it. Eversong reads mean 1107 against the core's 1094, so the one number
on screen was the one number not in use. The core line now sits between them.
Also fixes a format of a fractional second count, which 5.1 truncates quietly
and 5.5 refuses outright -- harmless in the client, and it stopped the handler
dead under the test interpreter. - Remember where a shard's cycle sits, and take the measure from the core
Prune deletes a timer after six cycles, which is under two hours, and rightly:
a wall of rows for shards nobody will stand in again is the whole complaint
against RCT. But it was also the only record of where each shard's cycle sits,
so an evening's break left the addon blind in zones it had already learned.
Dmitrii logged in to six rows of dashes.
Legibility is a display problem. The phase is kept separately now, absorbed
before anything prunes, and recalled the moment this client stands in a shard
it has stood in before. On his own data every one of eight remembered shards
comes back, including one nine cycles old.
What degrades is the arithmetic, not the anchor. Extrapolating across n cycles
multiplies the interval's own uncertainty by n, so Recall computes that error
and refuses once it passes what an answer is worth. A recalled phase is ranked
where nothing seen since can be overruled by it.
Which exposed a worse problem. The first drift estimate used the full range of
the observed gaps, and range is set by the worst pairing in the pile and grows
wider the more readings arrive -- so more evidence made the addon trust itself
less. Zul'Aman came out at nineteen seconds a cycle on gaps whose core spans
eight, and every remembered shard was refused.
The same fault ran through the interval itself. GapStats averages everything,
which is right for weighting a four-cycle observation as four times the
evidence and wrong for ignoring a bad pairing. Eversong reads 1087 to 1095 ten
times and then 1137, 1151 and 1183: its mean was 1106 and its core is 1094, and
the mean was what the countdown used.
So both now come from the densest run of readings that agree -- the estimator
the descent has used since the day Eversong reported sixty-four seconds -- and
that estimator moves to Core/Init.lua where the three modules that need it can
share one copy. Drift is then the standard error of the figure in use, so it
falls as readings accumulate instead of rising.
On his saved data: Eversong 1106 to 1094, Zul'Aman 1092 to 1095, and the drift
from nineteen seconds a cycle to seven tenths. - Stop the addon talking over itself, and say what it is refusing
Four things, all of them the addon being noisier or vaguer than it knew.
A crate under its parachute drifts, and the pin followed it at a hundredth of
a percent of the map -- so every sweep counted as a new place to point at, and
said so. Forty identical lines in eighty seconds. The pin still follows, now at
four tenths of a percent, and only the first placement is worth a line. A
landing always re-pins whatever the drift, because that is the one position
nobody had to guess at.
A descent reading refused as partial said only "joined mid-fall", and three
different checks can refuse one. They are not equally likely to be right: one
of them is this addon's own re-shard guard, added days ago and never tested
against a live case. A fall watched from three tenths of a percent away came
back flagged, which is the shape of that guard misfiring. The reason is
recorded now and printed, so the next one says which check did it.
Announcements carried coordinates beside a map pin, which is the same fact
twice. Time replaces them -- and the state that carried no time at all was the
one where time decides the answer: a raid will cross a zone for a crate that
landed five seconds ago and not for one that landed two minutes ago.
And the deploy script would ship Lua that does not parse. It checked that the
files matched the .toc and never that they were readable, so a file with an
unfinished string went into the live AddOns folder and the only thing that
would have noticed was the game, by silently not loading the addon. It refuses
now. Shown red against a deliberately broken file and green again after. - Say a row to the group, and say whose the crate was
Left-click a row and it goes to the raid: what is on the ground, what is
landing, what is in the air, or the two countdowns. With a clickable pin when
there are coordinates, which costs moving your own waypoint, because the only
link chat keeps clickable is the one made from it.
Gated far more loosely than the automatic announce and on purpose. That one
fires by itself on every client running this addon, so it is held to the
leader; this is one person choosing once, so a raid warning is fair where the
game allows one. What arrived as an RCT raid warning is refused, though --
the raid has already read it, and echoing it back is spam dressed as help.
The faction half. The game draws only your own side's claimed marker, so
seeing either claimed id means your side took it, whichever one arrived. The
6067-Alliance, 6068-Horde table is a guess: WarCratePredict shipped it from one
player's sample and had it contradicted live, announcing "claimed by Horde" on
a Horde character while an Alliance player was watched looting the crate. The
faction is read off the player instead.
Which gives the useful half for free: a crate that vanishes with no claim of
ours drawn, in a zone that did not re-shard, was taken by the other side. That
is the case worth naming, because the window would otherwise go on saying ON
THE GROUND to somebody about to cross a zone for nothing.
And the opposite case, which Dmitrii watched happen: a crate claimed by your
own side is still there to loot. The marker flags which side captured it, not
that anybody carried it off -- WarCratePredict says the same, and it is why
their claim used to fire at drop time. Clearing the row on that marker told the
player it was over while they could still go and take it. It now stays, and
says it is yours. - Date a crate found on the ground back to when it spawned
Finding one lying there seeded the timer at the moment somebody noticed it,
which is late by a flight and a fall every single time -- around two and a half
minutes on the readings to hand.
Both legs are measured now, so the known part of that error comes off. What
cannot be known is how long it lay there before anybody looked, so the reading
is still not called precise: this removes the part of the error that is known
and leaves the part that is not. RCT does the same and calls it a spawn offset.
Refused unless both legs rest on readings rather than the shipped guess. Built
out of two guesses the correction would be a guess in a correction's clothes,
and it would move every timer without anybody having measured anything. Shown
red against a build with that check removed. - Read RCT's own traffic, without bundling anything to do it
Their payload is AceSerializer, then LibDeflate, then EncodeForPrint -- their
Sync/wire.lua, read from the copy on disk. Nothing is encrypted. The signature
beside it is theirs to check rather than ours to satisfy: reading a broadcast
is not the same as claiming to be one of them.
What stopped this before was weight. Between them the two libraries are most of
the weight this addon exists without. They are borrowed instead: LibStub hands
out whatever any installed addon has already loaded, and both are everywhere --
Details, BugSack and several more carry them. With neither present the message
stays undecoded and says so, exactly as before.
Their capture states map onto ours, including Monster Say, which is their
announcer catch and means a crate is in the air. A state they do not name
becomes an anchor, ranked as weakly as it deserves. A spot with no zone, or
with their shard reading "N/A", is refused rather than turned into a report
with nothing to key it by.
Reassembly matters more than it looked. AceComm splits anything over a packet
and RCT's bulk sync carries its whole database: 145, 255 and 255 bytes from one
client in one burst. Refusing split messages threw away most of what there was
to read. The framing is handled in the pure module so the ordering, two senders
interleaving, joining mid-sync, a stalled run and an endless one are all under
test.
Token traffic is counted rather than logged. One client sent seventeen requests
in ten seconds and two hundred and thirty-four inside a couple of minutes,
which would have pushed every real sighting out of a twenty-line log before
anybody could read it. The count still proves the channel is alive, which is
all a handshake was ever evidence of. TOKEN_REQ carries no payload at all and
was being filed as "not theirs" until it arrived nine bytes at a time.
/ewc comm now also checks that each prefix is registered rather than assuming
it. A client caps how many prefixes every addon may hold between them, the
refusal is silent, and without the check "nobody is broadcasting" and "we never
subscribed" read identically.
Also here, because Comm is the transport for it: a row clicked in the window is
sent to the group. - Say where a row's information came from, and whose it is
Two things the window was saying that it did not know.
A zone with no timers at all read "new shard", which blames the copy of the
zone when the zone itself has never been watched. Dmitrii saw it in Harandar,
where nothing has ever been timed in any copy. They are different news: one is
a gap in what the addon knows, the other is a fact about right now. A zone is
only a new shard if it has been timed somewhere else.
And a crate somebody else reported looked exactly like one this client saw.
That matters more than it reads: a report is about the copy of the zone THEY
are standing in, so flying to it is a decision taken on somebody else's word.
Those rows carry a dot now, and the tooltip names who said it and which addon
carried it.
Checked against the real saved variables rather than by reasoning: an RCT raid
alert for Zul'Aman, decoded while standing in Harandar, floats Zul'Aman to the
top of the window as inbound and supplies its shard. - Show the timer for the copy of the zone you are actually in
A timer belongs to one shard. The window was showing the freshest entry for a
zone whatever shard it came from, so after re-sharding it presented another
copy's countdown as yours. Three states now, and they say different things: a
timer for the shard you are in, "new shard" when the shard is known and nothing
has been timed in it, and a marked guess when the shard is not known at all.
Blank is the point. Another copy's countdown in that place looks exactly like
knowledge, and a raid flies out on the strength of it and finds nothing. This
is the commonest complaint in crate farming and it has nothing to do with the
crate failing to spawn.
Knowing the shard therefore matters more than it did, so it is read from
anything available: any vignette rather than only a crate's -- a Renown
Quartermaster answered it in Eversong -- nameplates already on screen rather
than only newly added ones, and a target, mouseover or pet.
And from the raid. A player standing in a zone now reports which copy they are
in, which is the one thing nobody can find out about a zone they are not in. A
scout parked in Zul'Aman knows its shard; everybody else finds out on arrival,
which is too late to have chosen. Their report answers for that zone until
this client has its own reading.
Last: a crate that stops being drawn while you stand in its zone is reported,
and the line says whether the shard moved under you. Dmitrii watched one land
in Slayer's Rise, flew over and found nothing, and an ordinary loot and a
re-shard look identical from a distance. The claimed vignette separates them,
and its absence is the interesting case. - Answer when the crate lands, and stop guessing when to leave
The window said "drop" over a countdown that was really the spawn -- the moment
the transport appears -- and "leave" over advice nobody asked for. Now it says
both of the things a farmer is deciding between: when the transport arrives,
and when the crate is on the ground.
The second needs the first leg of the flight, which was computed from the
fitted speed and ran +16 to +64 seconds out depending on the zone, because the
transport slows on approach and circles before letting go. An average speed
cannot know that. It is timed now, per zone, on the same estimator as the fall:
the median of the densest run of readings that agree.
Only an announcer anchors it cleanly, because only an announcement is really
the spawn. Catching the transport in the air says when it came into range,
which is a lower bound by however long it had already been flying; those are
kept, flagged, and left out of the figure, exactly as a parachute joined
mid-fall is. So this fills slowly, and the column is provisional until a zone
has three readings that agree.
Travel times go with the leave column, and so does everything they touched:
the field in Data/Zones.lua, GetZoneTravel, db.travel, /ewc travel, the
planner's leaveIn, and the go and missed statuses that were only ever derived
from them. They were estimates by their own admission, and which drop point a
crate picks moves the number as much as which zone does. It was advice wearing
a number. - Write down what this is, for anyone who is not me
There was no tracked documentation at all. The brief and the open-threads list
had just moved under tmp/ and out of git, so a clone carried the addon and
nothing that says what it does or why it refuses to do certain things.
The README covers the features, every slash command, and a section on what it
deliberately does not do, which is the part that explains the design: it will
not tell you when to set off, it will not keep strangers' data on disk, and it
will not present a guess as a measurement. - Say when a countdown belongs to a different copy of the zone
The commonest complaint in crate farming, whichever addon people run: the raid
flies to a zone the timer says is due and no transport comes. It is not that
the crate failed to spawn. A timer belongs to a shard, entering a zone hands
you one you did not choose, and the crate on the shard you landed on is simply
at another point in its cycle.
The wiki is explicit that group members are pulled to the leader's shard only
when joining and only when already in the leader's zone, so flying somewhere
together guarantees nothing. Entering or leaving a group, toggling War Mode or
Chromie Time all move a player, and players on the Blizzard forums report the
same after a battleground. Nothing about offline members, which was the other
theory going around.
So the row now says it. A countdown learned on a shard other than the one this
client is standing in is marked apart from the ~ that means "seeded from a
crate on the ground", and the tooltip explains it in words. Three specs,
including that not knowing the shard is not a mismatch: silence is not evidence
of being in the wrong place.
Knowing the shard is what makes that possible, and it now comes from anything
available. Any vignette at all, not only a crate's -- a rare elite standing
about answers the question, and Voidstorm's elites read 207300 beside a
creature GUID saying 207300. Nameplates already on screen are read directly
rather than waited on, which covers the case that caught this out: after a
reload the plates are up but the event that announces them fired before the
addon loaded, and an announcer spoke into exactly that gap.
Asking the game to target something would be the obvious way and is not
available: TargetNearestEnemy and its relatives are protected and run only from
a keypress.
A descent reading is also refused now when the zone's shard changed while it
was falling. The check that decides whether a fall was watched from the start
is kept per zone while the reading is keyed per zone and shard, so a re-shard
made "this parachute was not there a moment ago" trivially true and recorded
half a fall as a whole one. - Wire the evening's work into the scanner, the commands and the load order
The plumbing for the four changes that landed beside it. Kept together because
the same four files carry all of it and splitting them further would mean
commits that never built.
Scanner learns which shard this client is standing in, from a crate vignette or
from any creature the game will name. The announcer needs it: the chat event
for a monster's speech carries no GUID at all, not a secret string but nil, so
the speaker cannot supply it. A creature and a vignette give the same number,
which /ewc shard settled after an hour of doubting it.
Scanner also measures how far the player stood from each landing, and the
announcement handler reports whether a timer was actually anchored rather than
whether a phrase matched. The first live announcement said "anchored the timer"
having anchored nothing, which is the worst kind of diagnostic.
Main keeps a log of everything this addon says, in the saved variables.
/chatlog does not capture it: that records the CHAT_MSG_ stream, and an addon's
print goes straight to the frame through AddMessage without ever becoming a
chat message. WarCratePredict keeps its own log for the same reason. It costs
a /reload to read and misses nothing.
Main also reports a sighting to the group and offers the group's own reports a
place in the timers, both guarded: everything below those two lines is the
addon's core work, and a nil call there would stop all of it without a word --
no pin, no prediction score, no release measurement, no learned spot.
Commands gain /ewc data, /ewc yells, /ewc comm and /ewc announce, and the
airtime readout now shows how far away each reading was taken from. - Take the descent from the readings that agree with each other
The median could not survive the data. Eversong held 14, 43, 84 and 92, so its
median was 64 -- a time no crate there has ever taken -- and the countdown was
being driven by it. Slayer's Rise read 103 the same way.
The spread is not the fall varying. Zul'Aman measured 85s and 44s at 49.0,
69.3, and Slayer's Rise 91s and 134s within half a percent of one spot, so the
same crate landing in the same place reads forty seconds apart. Splitting the
figure per drop point, as RCT does, would not have helped.
Both tails have one mechanism seen from opposite ends: a parachute noticed late
reads short, an on-ground vignette noticed late reads long. Set them aside and
every zone measured so far holds a cluster near 86.
So the figure is now the median of the densest run of readings within ten
seconds. Fewer than three readings keeps a plain median, because two cannot be
shown to disagree. A zone with several that do not agree borrows the cluster
from every zone pooled and says so, in the readout and on the prediction line,
rather than presenting a borrowed number as a measured one. On the readings to
hand every zone lands on 86, including the two that were reporting 64 and 103.
The estimator finds agreement, it does not assume the answer: a spec pins a
cluster at 122 being reported as 122.
Each reading now records how far the player stood from where it landed. That is
the instrument for the question this leaves open -- whether a distant parachute
is simply drawn late -- and it needed the argument tail to become a table,
which the function's own comment had already asked for. - Take what other players and other addons can already see
A raid is spread across zones and the information exists; it just never reaches
the person deciding where to fly. Everything here is learned by sitting through
cycles, so one player watching one zone is blind everywhere else.
What is decoded, in the order it is worth anything:
RCT announces a flying crate to raid chat, and assembles that line in English
regardless of locale, so the frame around the two values is fixed. Read as
ordinary chat with nothing decrypted and nothing installed. Worked live on
20 Sep, four times in an evening, each one improving a timer this client would
otherwise have had to guess at. Only the zone name inside it is localised, so
zone names are matched from C_Map on this client.
WarCrateTracker broadcasts SPOT_V2 in the clear to guild and party, carrying
the same vignette ids this addon already reads.
RCT's own prefix and CrateTrackerZK's are registered and logged but not
decoded: their payloads are serialised and deflated, and reading them means
bundling LibDeflate and AceSerializer, which is most of the weight this addon
exists without. HGLog, which RCT bundles, shares the same anchors in the clear
on HGLOG1, so there is nothing behind the compression worth the libraries.
Remote data is not local data, and that is the whole complaint against RCT: it
takes every sighting from every group and guild member into its saved database
and never cleans it, so an evening leaves a wall of rows for shards nobody will
stand in again. Here a report lives while the raid does, carries who sent it,
and never reaches disk. It moves into db.crates at one moment: when this client
confirms the crate itself. Promoting it then is not sentiment, it is that a
scout who caught the transport holds a precision-3 anchor while this client
arriving to a crate on the ground holds a 1.
An HGLog row is an anchor, not a sighting, and has its own stage so that a
fifteen-minute-old row from somebody's database can never be drawn as a crate
in the air. Seeing the crate ourselves retires what we were told about it: a
report outlived its crate once and the window went on offering "inbound" for a
zone that was finished with.
The receive gate is the one WarCratePredict had to add after the fact. A
whisper is never a legitimate transport, or anyone on the realm can hand you a
crafted message and move a timer with no group, guild or acquaintance. - Anchor the cycle on the NPC who announces it
The line is scripted to the spawn, so it dates the timer as well as catching
the transport does and arrives earlier, before the transport is near enough to
draw a vignette. That is what should make Eversong and Voidstorm work, where it
is still out of range when the cycle starts. It also needs no addon on anybody
else's machine, which makes it the cheapest source of data there is.
The name gates the phrases and both are required. Without the gate a boss taunt
anchors a cycle, which WarCratePredict hit as Decimus saying "I hunger for the
OPPORTUNITY"; without the phrases an announcer's idle line does. Ranked with a
flying catch, because that is what it is.
Every locale is matched at once instead of switching on GetLocale, since the
names are localised too and the gate holds either way. Russian names and
phrasings come from CrateTrackerZK's ruRU locale.
Lua's string.lower only touches bytes below 128, so a Cyrillic phrase lowered
and compared never matches. Matched against the raw text as well, with a spec
that fails without it. That spec is also what exposed the test runner: Windows
hands stdout a cp1252 encoder, so a failure message containing Cyrillic took
the whole run down with a traceback instead of naming the test that failed. A
suite that cannot report a failure is worse than one that has none.
Heard live on 20 Sep: Vidious in Voidstorm and Ziadan in Zul'Aman, both
matched. The shard is a separate problem and is handled in its own commit. - Let the player see and correct what the addon has learned
A bad reading could only be removed by typing /ewc airtime drop HA 5, which
means reading an index out of a chat log. Evidence that can only be corrected
from a command line does not get corrected, and this addon exists because RCT
accumulates data nobody can clean.
Six lists behind one button in the settings panel: timers per zone and shard,
descent readings, observed intervals, drop points learned beyond the shipped
catalogue, the route, and travel overrides. Any row can go; a zone or a whole
list can be cleared behind a confirmation.
Every row carries a stamp of the record it stands for, and Remove refuses when
the stamp no longer matches. A row holds an index into a list that keeps
growing while the panel is open, so a descent reading arriving between painting
a row and clicking its button would otherwise delete a different record than
the one on screen, silently. Three specs cover that and were shown red against
a build with the guard removed.
The panel does not tick. The timers list sorts by how soon each drops, so a
one-second repaint would reorder rows under a cursor on its way to a remove
button. A stale countdown in a management panel costs nothing; deleting the row
below the one you aimed at costs a measurement.
UI/Manage.lua decides every row and is covered by tests; UI/DataPanel.lua only
paints them. The settings panel itself is now switches only, in four sections.
Anything that is a list of records lives behind the button, because that API
draws checkboxes and sliders and has nowhere to put a row with its own delete.
RCT puts 39 controls on one page, including the warning frame's font and a
donation link, and the three settings that change how it farms are lost there. - Stop tracking the working notes and the local tooling
TODO.md and PROMPT.md are how this project thinks, not part of the addon, and
they sat in the repository root where publishing would carry them along. They
move to tmp/, which is ignored. Worth knowing: git mv would have tracked them
there anyway, because .gitignore has no effect on a file already in the index.
tools/ goes the same way. deploy.py targets one machine and loglens.py reads
that machine's game folder; neither is anything a stranger cloning this could
use. A fresh clone therefore has no deploy script, which is the accepted cost.
tests/ deliberately stays: there is nothing local in it and it is the part of
this project most worth reading.
.pkgmeta covers the other meaning of publishing. deploy.py already copies the
.toc and exactly the files it lists, so the deployed folder is precisely what
the game loads; this makes the packaged zip agree with it under the same rule
rather than under a second list kept in step by hand.
The notes remain in the history. Publishing the repository would expose them in
old commits, and rewriting history to change that is a separate decision. - Report the middle descent, and stop trusting a parachute that drifted into view
Two faults, both visible in one night's logs.
Eversong recorded 14 seconds as a complete descent. The crate had been falling
before the player arrived; two sweeps of the zone had already happened by the
time its parachute came into vignette range, so "it was not here a moment ago"
was satisfied by a crate that had been in the air the whole time. A vignette
entering range and a vignette appearing are identical from here.
The only unambiguous evidence is having watched the transport reach its drop
point, which is the moment the crate was let go, and that now fires reliably
because the prediction commits within five samples. Failing that, a fall is
only trusted if the zone had been under observation for longer than any
possible fall before the parachute showed.
And the figure itself is now the middle reading rather than the mean. Harandar
has measured 86, 86, 87, 61, 19, 86, 86: five of seven agree, and the mean is
73, which is not a time any crate there has ever taken. Both tails are genuine
-- a crate can catch on a branch and be down early, and the game can leave the
parachute drawn for half a minute after it lands -- so no correction removes
them and only the middle survives them.
Three tests, all red against the mean. - Translate a fallback position instead of passing it off as zone coordinates
A vignette position comes back in the space of the map it was asked about.
When the zone map yields nothing the lookup falls back to the player's own map,
and two of its three callers dropped the second return value -- so those
coordinates went to the catalogue, the prediction and the waypoint as though
they were zone-map coordinates. Standing in The Den, a sub-zone of Harandar,
that is a different place entirely.
The fallback now round-trips through world coordinates into zone space. If that
conversion fails the reading is refused rather than used, and the two tracking
callers check which map they were handed either way. Only /ewc scan may print a
position from elsewhere, because it names the map beside it.
Found while working out why a transport crossed Slayer's Rise untracked. That
is not explained yet -- both vignettes in that scan had no position on any map,
which the previous commit made audible -- but this was in the same lookup. - Say when a crate cannot be placed instead of skipping it in silence
A scan in Slayer's Rise returned two vignettes the game would give no position
for. They were War Mode Quartermasters, which this addon does not track, so
nothing was lost -- but it showed what happens when the lookup fails: the
vignette is skipped by an "if pos then" with no else, and nothing is said.
For a quartermaster that is correct. For a transport or a crate it is the
addon going blind and reporting success, which is the exact failure it exists
to prevent. A tracked stage with no resolvable position now says so, once a
minute per zone rather than once a second.
No fix to the lookup itself: it already tries the zone map and then the
player's raw map, and a vignette the game will not place on either is a
vignette with nowhere to be. - Fix the clock mismatch that flagged every descent as a fragment
The sweep marker was stored as tNow -- GetTime(), seconds since the client
started -- and compared against stamp, a unix timestamp from GetServerTime().
About 1.7 billion between them, so "did we sweep this zone recently" was never
satisfied and every descent reading in every zone came out a fragment. Slayer's
Rise was watched from transport to parachute and still lost its reading.
The audit of the rest of the file is clean: tNow only meets recentDrop, spotted
and tr.lastSeen; stamp only meets fallingSince, arrivedAt and atlasFlip.
The suite cannot see Scanner.lua at all, so the only check this had was a
player reading a log. The linter now carries the rule: it learns which tables
hold which clock, follows that through multiple assignment and into the locals
that read them, and refuses arithmetic across the two.
Both earlier drafts of the rule were green against the live bug -- one matched
only single assignment, the other never reached the local where the comparison
sits. A check that has only ever passed has not been tested, so this one is
shown red, restored, and green again. - Call the drop point a circling transport is already sitting on
Flying into Zul'Aman on a fresh shard found the transport orbiting its drop
point. The addon held six samples of it and said "not far enough yet to read a
heading" -- refusing to answer while the answer was directly underneath.
A track with samples but no baseline is not a track with no information. It is
a transport that has arrived. Its mean position, snapped to the nearest
catalogued spot within 3% of the map, is the drop point; no ray and no bearing
are involved, so none are computed. It commits, prints where and how long the
crate will take to come down, and pins it.
This is also the case with the least warning -- the crate can leave at any
moment -- so it is the one where saying nothing costs most.
Five tests. The snap radius is load-bearing: widened, the "circling nowhere
near a catalogued spot" case goes red. - A parachute joined mid-fall must not anchor the cycle timer either
Zul'Aman reported a 989-second cycle on shard 97 against a true figure near
1090. Both ends of that gap were crates already under their parachutes when the
player arrived: each timestamp is late by however much of the fall had already
run, and the gap carries both errors.
The addon already knew both sightings were fragments -- it printed "joined
mid-fall" for each -- and told the timer nothing. A parachute picked up partway
through says only that a crate spawned some minutes ago, which is exactly what
finding one on the ground says, so it is ranked with it. It still refines to a
proper sighting when one arrives, and a parachute actually seen to leave the
transport still measures the cycle.
Three tests, two red against ranking it as a real falling sighting.
/ewc interval now numbers its observations and takes drop ZONE N and
reset ZONE, the way /ewc airtime does. The 989 is already recorded. - Judge a descent by whether we watched it start, not by a heading fit
The previous gate threw away good readings. Slayer's Rise was watched from
"transport spotted" through commit to the crate appearing, and Voidstorm
produced 86 seconds -- the most accurate figure in the whole set -- and both
were discarded as fragments.
The gate asked whether the track had reached its drop point, and that needs a
live heading fit. A transport circling its drop point has no baseline to fit,
so Evaluate returns early and arrived is never set. Neither log carries a
"transport reached" line; the flag simply never fires in the case it was meant
to certify.
The question is now asked directly: had we swept this zone in the last minute
without this parachute in it? If so the fall began while we were watching. A
parachute present in the first sweep of a zone was up before we arrived, and
Scanner.Reset clears the sweep record on every zone change, so flying in
mid-fall cannot look like anything else.
Also fixed: flip was computed from fallingSince one line after it was set to
nil, which is arithmetic on nil. It has not fired yet only because no crate has
changed its art, so the first one to do it would have thrown.
The overlap flag is gone from the descent report as well. Nineteen readings out
of nineteen carried it. - Watch the parachute's art for the landing the vignette id hides
Dmitrii: a crate can catch on a branch and reach the ground early, so a short
reading is not automatically a broken one -- which retires the advice to delete
them. The joined-mid-fall flag is the only honest discriminator and it exists
only for readings taken since it was added.
And: a landed crate can keep its parachute drawn for 30 to 54 seconds. The
unexplained excess in the long readings is 44 and 43 seconds, inside that range
at both ends, so the ground vignette arriving late is very likely the whole of
it.
If the id stays 2967 through that but the art changes to the crate, the real
landing is observable. Each fall now records whether atlasName changed and how
far in, so /ewc airtime can show it against the reading. Nothing acts on it
yet: this establishes whether the signal exists. - Measure the circling leg, because the fall does not explain the spread
Nineteen readings, and the means are not trustworthy: Harandar sits at 86-87
four times over, Eversong at 84, but Zul'Aman splits 83/85 against 129/129 and
Slayer's Rise gave 91 and 134 at what is effectively one spot -- 0.4% of the
map apart. Terrain cannot explain two readings 43 seconds apart in the same
place, so the elevation theory for Zul'Aman is dead.
Two observation-loss theories are dead too. Leaving a zone fires
ZONE_CHANGED_NEW_AREA, which calls Scanner.Reset and clears the fall marks
outright, so a reading that survives a zone change does not exist. And the
overlap flag -- the parachute still drawn when the crate is found down -- fired
on 19 readings out of 19, which means the parachute was visible at every
landing and sight of it was never lost. It also means the flag separates
nothing, so it is no longer printed.
What has never been measured is the leg between the transport reaching its
point and the parachute appearing. The release estimate already runs 35 to 38
seconds short in Harandar, which is the order of the unexplained spread, and
the arrival is now a known moment. Each reading records how long the transport
circled first, and /ewc airtime prints it. If the long falls carry long circles
it is release timing; if they do not, the fall really does vary.
That is instrumentation, not a fix. Two guesses have already failed here and a
third would not be worth more than the measurement. - Let one bad descent reading be dropped without losing the zone
A reading costs a full crate cycle to obtain -- about eighteen minutes -- so
clearing a whole zone to remove one mis-paired figure is an expensive way to
fix it. Harandar holds five good readings and a 19; Zul'Aman four and a 44.
The listing is numbered and /ewc airtime drop ZONE N removes that one. - Pin the crate you can see, not only the one you predicted
Flying into Zul'Aman with a crate already under its parachute left the map
bare. The pin was only ever placed from a prediction, and there is nothing left
to predict once the transport has dropped -- so the one position that needed no
guessing at all, printed on the line above it, was the one position never
pinned.
An observed crate now sets the pin, overriding whatever the prediction put
there, and holds it until the crate moves or is claimed. - A fall counts as measured only if we watched the transport arrive
Harandar's descent mean read 71s across six readings spanning 19 to 87, against
a true figure of roughly 86 that four zones agree on. A 19-second fall is not
physical; the reading was a fragment counted as a whole.
The test for "we saw this crate leave the plane" was "is a track open in this
zone", which is a different question. A transport keeps circling after it
drops, so flying into a zone mid-fall shows you one, opens a fresh track, and
qualifies your fragment of a fall as a complete measurement. The guard meant to
suppress those tracks keys off recentDrop, and recentDrop is only set once a
crate has been seen -- so on the first sweep after entering a zone the outcome
comes down to whether the transport's vignette is listed before the crate's.
That is why it corrupted some readings and not others.
It now requires the track to have reached its drop point, which is the event
that actually starts the fall. Readings from a flight we never saw arrive are
marked partial: counted, shown, kept out of the mean.
This runs through Detect/Scanner.lua, the one file the suite cannot reach, so
the evidence is the code path and the 19-second reading, not a red test.
/ewc airtime reset now takes a zone, because a contaminated zone should not
cost the other five their history. - Call Eversong ES and Harandar HA, the way raids actually say them
The displayed abbreviations came from WarCrateTracker, which writes EW and HD.
Dmitrii's raids call out ES and HA, and since the whole point of this table is
to match what someone says over voice, the spoken form wins.
Nothing changes about input: ES/EW and HA/HD were all in the alias table from
the start and all still resolve. Only the canonical form the addon writes back
moves, which is what the route editor echoes and what the window prints.
RCT is no help here -- it has no abbreviations at all and draws full zone
names -- so there was nothing to copy, only a convention to match. - Answer as soon as there is an answer, not once the rivals fly out of range
A Zul'Aman flight held the correct spot in first place from its fifth sample
and said nothing for 62 seconds, committing 20 seconds before the drop. Two
faults, one in the maths and one in the policy.
The maths. Candidates were separated by ANGLE against a tolerance of
SCATTER/range, which grows without bound as the transport closes in, so the
verdict became less decided the nearer it got and could only resolve when a
rival passed behind the plane and left the candidate list. Spots are now scored
by sideways distance from the ray in units of what could plausibly put them
there -- the landing scatter, plus the bearing error over that range -- and
turned into a posterior. That sharpens on approach, which is the right
direction. The fit's own error is floored at 0.23 degrees: the regression
reports 0.00 for a dead-straight transport and taking that literally would
trust a 20-second bearing absolutely.
The policy. Zul'Aman's 48.9,69.2 and 46.9,62.3 sit on one line to within
0.001% of the map from a northern entry. No bearing separates them and none
ever will, so refusing to speak is refusing for the whole flight. The addon now
names the NEAREST spot still in contention, because the transport reaches them
in order: if the crate is there you have arrived, and if not the next is
straight on. It says how sure it is and names what else is on the line, and it
redraws when the evidence moves -- which on that flight is a firm call at 100%
with 2:42 still to fall. Fanned candidates, as opposed to collinear ones, still
produce nothing: there the near one is not on the way to anything.
Simulated against the real catalogue and that flight's geometry, the first
answer arrives 76 seconds earlier than it did.
Four tests cover it, all red against the previous model. - Clear the old waypoint first, and stop asking permission to place one
Two differences from RCT, whose pins do appear, and both were mine.
RCT calls C_Map.ClearUserWaypoint before setting. This did not. An existing
waypoint -- the player's own, another addon's, or this addon's from the
previous flight -- can stop a new one taking, and nothing reports it.
RCT never calls CanSetUserWaypoint. This gated on it and silently declined
whenever it returned false, which is quietly refusing to place a pin that
might well have worked. It now attempts the placement and reads the waypoint
back to find out what actually happened, mentioning what CanSetUserWaypoint
claimed only when the attempt failed.
The second is a rule this addon states in its own comments and then broke:
look at the artefact, not the return code. Asking an API for permission is not
the same as finding out whether the thing happened. - Add /ewc pin, and stop success being silent
The map pin has now failed to appear twice with nothing on screen either time.
The diagnostic added for it only spoke on failure, so "no message" meant either
that it had worked or that the player was still on an older build -- and those
needed telling apart before anything else could be worked out. Success says so
now.
/ewc pin takes the question away from crate timing altogether. The prediction
path cannot be re-run on demand, since it needs a transport in the air, so
until now every attempt cost a whole flight to observe. This places a pin where
the player stands and reports the setting, the map they are on, the zone it
resolves to, whether the game permits a pin on either, whether the call took,
and whether it is supertracked -- which is the part that puts an arrow on the
minimap.
One lead it should settle: the last failure happened while standing on map 2576,
the housing area inside Harandar, with the pin being placed on 2413. That is
correct for the coordinates, but a pin on a map the player is not looking at may
not behave the same way. - Hide the headline when there is nothing to say
It read 'no transport in the air' whenever one was not, which is the normal
state and informs nobody, and the rows now carry the same news with the zone
attached. What the line still adds over a row is the coordinates and how sure
the call is, so it stays for that and takes its space back otherwise. - Report a transport that is there, and say why a map pin was not placed
Two silences from one flight, both of them the addon knowing something and not
saying it.
The window read "no transport in the air" with one plainly in the zone. The
prediction returned nothing whenever the heading could not be fitted, which
conflates having a transport with having a heading for it. A heading is missing
for the first seconds after one appears and again once it reaches its drop
point and circles -- the beginning and the end of every flight, and both are
moments someone looks at the window. It now reports the transport either way,
and says which of the two it is.
The call was made, announced and acted on, and no map pin appeared, with
nothing on screen to say whether the addon had chosen not to place one or tried
and been refused. It now says which, and reads the waypoint back rather than
trusting the call -- a pin that did not take is exactly as useful as no pin.
Also: the settings panel passed a hardcoded default of true for every checkbox,
so "Narrate tracking" and "Verbose log" -- both diagnostics, both off by design
-- would have defaulted on. They take their defaults from DEFAULTS now. - Give a zone with something happening a row, at the top
Flying into a zone whose timer you do not have -- somewhere new, or somewhere
you have returned to on a different shard -- produced no row at all, so the
window was silent at exactly the moment there was something in the air or on
the ground there. The live-crate state existed but could only be shown on a row
that already had a reason to exist.
Any zone with activity now gets one. A transport still in the air counts as
activity in its own right: it is the reason to stay put, and until now the only
sign of one was a line of text that says nothing about which zone unless you
happen to be stood in it.
Live rows float above the countdowns, most urgent first -- a crate on the
ground can be taken now, one under a parachute shortly, a transport eventually
-- while everything else keeps the order it was built in. - Do not average a descent that was joined halfway down
Flying into Slayer's Rise while a crate was already on its parachute produced a
reading of 115 seconds -- from the moment the player arrived to the landing,
with no way to know how long it had been falling before that. The real descent
is longer by an unknown amount, so the figure is a lower bound rather than a
measurement, and averaging lower bounds with complete readings pulls the answer
down every time it happens.
Whether the whole fall was seen is knowable: it depends on whether the
transport that dropped it was being tracked when its parachute appeared. That
flag is now captured before the track is torn down, and flagged readings are
counted and displayed but excluded from the mean and the range.
This matters right now because Slayer's Rise and Zul'Aman are the two zones
whose descents look unlike the other four, and 115 was about to become evidence
for that when it is not evidence of anything. - Record where each descent was measured, and stop calling a new track "circling"
Zul'Aman has now produced descents of 83 and 129 seconds where four other zones
sit inside 84 to 87. That is not measurement noise, it is the zone behaving
differently, and elevation is the obvious suspect: lower ground under the drop
point means a longer fall, and Zul'Aman has a temple complex above a jungle.
Both 129s came from the same spot.
Which would mean splitting the descent by drop point after all, as RCT does and
as this deliberately did not. It cannot be settled yet, because the readings did
not record which spot they came from -- so that is what changed. Instrument
first, decide once the data can answer.
Also: "it has stopped moving -- circling its drop point" was printed two seconds
after a transport was first spotted, when it had not moved far enough to read a
heading yet. Same condition, opposite meaning, and the track already knows which
it is by whether it has ever fitted. - Keep a crate that is down or falling on the window while it lasts
The timer resets the instant a crate is released, so the row jumped from 0:05
to 18:10 at exactly the moment there was a crate lying there to go and collect.
The countdown was right and useless.
A crate in the air or on the ground is now tracked per zone, separately from
the timer, because they answer different questions. Its row shows ON THE GROUND
or a landing countdown in place of the wait for the next one, with the bar
filled, and the tooltip says which. The timer is still underneath -- the next
drop has not stopped mattering.
Cleared when the crate is claimed, when its zone is left, or after ninety
seconds without seeing it while standing there, since by then it has been taken
or was never really there.
Also removed Scanner.FallingSince, which had been dead since descents started
being keyed by zone and shard rather than zone alone. - Label the window's columns and explain a row on hover
The first person to see the window asked what the two countdowns meant and
what "x2" was. That is the answer about the design, not a question to answer in
chat: three unlabelled values on one row is not a readout.
There is a column header now -- and it drops the leave column when no route is
set, since without one there is nothing to leave for. Hovering a row gives the
zone, the shard, both countdowns in words, the travel time behind the leave
figure, and what the tilde and the missed cycles actually mean.
The missed count came off the row. It was an unexplained "x2" beside two
unexplained countdowns, and the dimming already says "do not trust this one"
without needing jargon; the number itself is worth having, so it moved to the
tooltip where there is room to say what it implies. - Say why a heading cannot be fitted, instead of blaming the sample count
A live flight in Eversong reported "tracking 35 samples, not enough to fit
yet" against a threshold of five. The count was never the problem: the
transport had reached its drop point and begun circling, so the first and last
readings in the window sat almost on top of each other and the baseline
collapsed. Refusing to fit was right; explaining it as a shortage of data was
not, and the two look nothing alike to a player.
Fit now returns why, and the readout distinguishes "still gathering" from "it
has stopped going anywhere".
The everFit guard that should have suppressed the line was set only in the
branch for a track that has a fit and has not committed. A track that commits
before that branch ever runs -- which this one did, at five samples -- never
set it. Set now whenever a fit exists at all. - Add the window, a minimap button and a settings panel
What to show is decided in UI/Model.lua, which is pure and covered; the frame
code receives a list and paints it. A frame cannot be tested and a table can,
so as little as possible is decided in the parts that cannot be.
The window leads with the rotation when one is set, because "when do I leave"
is the question a rotation asks and a bare countdown does not answer, and
zones off the route follow so nothing known is hidden. A stale row is dimmed
and carries its missed-cycle count: RCT draws a timer nobody has confirmed for
hours exactly like a live one, which is how its Eversong countdown keeps
promising drops that never come. An empty database says what to do rather than
sitting blank, since that is the normal state for a new install.
The headline shows the leading candidate greyed even while the call is
uncertain. A Zul'Aman flight held the correct spot in first place for a full
minute before the margin cleared, and saying nothing for that minute is worse
than saying "probably here". The waypoint still waits for confidence.
The minimap button is hand-rolled rather than pulled in with LibDBIcon, which
would mean bundling LibDataBroker and LibStub too -- three libraries for one
button. Settings uses Blizzard's own API: RCT carries AceConfig and AceGUI,
including fifteen widget files, to draw checkboxes the game has drawn natively
since 10.0. Both register on PLAYER_LOGIN in their own frames, so a future API
change takes out a panel rather than the tracker.
Also dropped minimapAngle and window from DEFAULTS. They hold where the player
dragged something, so unset is their real state and every reader has its own
fallback -- a default of nothing is not a default. The defaults spec caught
that, correctly. - Stop a bad pairing inventing an interval no zone has
/ewc offsets reported Zul'Aman running an 825-second cycle. It came from a
timer seeded off a crate found already lying on the ground: that timestamp is
when the crate was SEEN, any point after it landed, so the gap to the next drop
came to about 1650 seconds. The cycle arithmetic read 1.5 as 2 and halved it.
825 then went into GetZoneInterval and out into the countdown, so the measured
figure was worse than the shipped one it replaced -- the exact failure the
measurement was meant to avoid.
Two holes, both here. A gap is now only reported when both ends are
spawn-anchored, and a gap must sit within fifteen percent of a whole number of
cycles to count as one. Real readings land within four percent, so that is
three times the worst observed and still rejects anything near the midpoint.
Neither existing test would have caught either, which is its own finding. Both
are covered now and both were checked against the broken code first.
/ewc interval reset clears measurements, since any database that has been
running already holds the bad Zul'Aman reading. - Add /ewc offsets to test whether zone cycles sit at a fixed phase
Raids rotate between three and five zones and that only works because the
drops do not coincide, so the zones are offset. Whether that offset is
CONSTANT is not something a player can see by eye, and it matters a great deal:
if it is, catching one drop tells you when every other zone is due, and the
cold-start problem -- roughly an hour of waiting to learn six zones solo --
disappears without any player-to-player sync at all.
This prints each known timer's phase within its cycle and the gap between every
pair. Run across sessions, a repeating gap for the same pair means the offset
is fixed; a wandering one kills the idea for the price of looking.
Recorded honestly alongside it: a shard number is per-zone, so equal numbers in
two zones mean nothing, which is a real obstacle to reading this and not one I
can see past yet. - Admit the computed release estimate is the weak leg
A fifth flight ran 54 seconds late against a 27-second estimate. The errors now
read +16, +22, +29, +31, +54 -- late every time, but across a 38-second spread,
which a mean cannot represent.
The cause is that the computation assumes the transport flies straight to its
drop point at the speed it has been holding, and it does neither reliably. The
case made here for computing rather than measuring was that measuring needs a
warm-up, and in exchange it bought an estimate that can be out by a factor of
three. RCT measures this leg vignette to vignette and by construction has
neither the bias nor the spread. That was the wrong trade and the comments now
say so; replacing it is the next piece of work on this file.
The correction stays, since late-but-corrected beats late, but it needs five
observations before it applies and reports its spread so the countdown is not
read as precise.
Also fixed a line that printed the measured interval labelled as the shipped
one, which is how Harandar came to report "shipped: 1092s" when the table says
1100. - Record why the descent is not inferred, and split the interval readout
Watched side by side, RCT's landing countdown reached "on the ground" with
about thirty seconds of falling left. Its code explains it: rather than use the
crate's on-ground vignette it decides a crate has landed when the parachute has
not been seen for sixty seconds, dating the landing to the last sighting plus
fifteen. That fires whenever a player simply moves out of range of a crate
still in the air, so every such flight teaches its model a descent shorter than
the real one -- short, which is the direction observed.
We measure from the on-ground vignette instead, which one check against a
player watching a crate land agreed with to a second. Written down so it does
not get "improved" into an inference later without evidence that readings are
actually being lost.
Single-cycle interval readings have come in consistently below multi-cycle
ones: 1054 and 1060 against 1086 and 1097, with no overlap. Probably because
arriving mid-flight dates the first drop late while the second, waited for, is
caught promptly. /ewc interval now splits the two so the pattern stays visible;
if it holds, single-cycle readings should be excluded rather than weighted down. - Trust the measured interval over the shipped one
Harandar has now been timed twice, once across four drops and once across one,
and they agree at 1086 and 1085. Voidstorm came out at 1097 across four. So the
1100 every addon ships is not right, and the eleven seconds between those two
zones says it may not even be one number. Assuming 1100 where the truth is 1086
puts a countdown almost a minute out over four cycles, which for a raid
deciding when to leave is the whole point of the timer.
GetZoneInterval prefers what this client has observed once a zone has three
cycles of evidence behind it. One cycle is not enough -- it carries the whole
detection error at both ends, which is what made the lone Slayer's Rise reading
of 1054 an outlier against everything measured since.
Hardcoding the new figures would be the same mistake with better inputs, so
they stay in the comment as the reason and out of the table.
I was wrong twice on the way here and both are worth recording: "1100 is wrong"
off a single one-cycle reading was premature, and the retraction that followed
was over-cautious. The four-cycle readings settled it. - Correct the release estimate by its own measured bias
Two flights promised release in 8 seconds and took 30 and 37 -- late by 22 and
29, both in the same direction, which is a bias rather than noise and a large
one against an eight-second estimate. Most likely the transport slows on
approach while the heading fit reports an average over its whole window,
though the cause does not matter for correcting it.
The correction is the mean of what has actually been observed, pooled across
zones because the cause is not zone-specific and the samples are few. It only
applies from two observations on, so one odd flight cannot swing it.
The raw estimate is what gets remembered and scored against. Scoring the
corrected figure would drive the measured bias towards zero and quietly remove
the correction that earned it -- a self-cancelling feedback loop that would
look like the model getting better. - Flag a descent measured while the parachute was still drawn
The game keeps drawing a parachute for a crate that is already down. Observed
live in Zul'Aman, lingering eight to ten seconds, which is very likely the
whole difference between the 98s measured there and the 87s measured cleanly
in Harandar. Two zones that looked like they behaved differently may simply be
the same descent measured twice, once through a lagging vignette.
The scan now notes which zone and shard show a parachute before acting on
anything, and a landing recorded while one is still up carries the flag. It is
not rejected: whether those readings really run long is something a handful of
samples will show and a guess will not, and discarding every reading from a
lifecycle that misbehaves regularly would leave almost no data.
Descents are kept as individual readings rather than a running sum, as the
interval gaps already are. With one reading per zone a mean is not a
measurement, and /ewc airtime now lists them so the spread and the flags are
visible rather than averaged away. - Weight the interval by cycles, and number GUID fields correctly
Two interval measurements now exist and they disagree: a one-cycle Slayer's
Rise gap of 1054s and a four-cycle Harandar gap averaging 1086s. The longer one
deserves more weight, because detection error at each end is divided by the
number of cycles it spans -- four drops is a quarter of the error and therefore
four times the evidence. The mean is total elapsed over total cycles rather
than the average of the per-cycle figures, which would have treated them as two
equal votes and landed between them for no reason.
The GUID field dump numbered fields 1, 3, 5, 7 because "[^-]*" matches the
empty run between every pair of separators as well as the fields themselves.
Shard.FromGUID was reading the right positions all along -- shard 39, instance
2694 are genuinely fields 5 and 4 -- so only the diagnostic lied, which is
still worth fixing since the point of a diagnostic is to be believed.
Settled while here: the transport has a creature GUID but UnitPosition returns
nothing for it, checked three times. There is no altitude to read, so the
descent has to be measured rather than computed. That question is closed. - Pair the descent by shard, and learn from every landing
Two bugs from one log, and the first is the worse kind because it produced a
plausible number. Two crates landed in Slayer's Rise ninety seconds apart on
different shards -- a Horde raid's on one, an Alliance one on the other, after
the player was re-sharded out of a group. The descent was timed from one
crate's parachute to the other's landing and came out as 154 seconds. The true
figure was 117. It is keyed by zone and shard now, with a cap on how old a
parachute may be to belong to a landing.
The second: Learn.Note only ran when the timer moved, so a crate seen falling
and then on the ground contributed nothing to the catalogue -- the timer
correctly ignored the second sighting as the same crate, and the landing
position went with it. That position is the best evidence there is of where
crates land, and it was being discarded in the common case. Updating a
countdown and learning a drop point are separate jobs and are no longer tied.
Also fixed a false positive in tests/lint.py, which read %s inside a format
string as a call to a function named s. It reported one against Main.lua. A
linter that cries wolf gets switched off, and is then worth nothing. - Measure the interval, and score the release-time estimate too
First measurement, in Slayer's Rise: 1054 seconds between drops on one shard.
Every shipped figure misses it -- CrateTrackerZK 1100, RCT 1098-1099,
WarCrateTracker 1095 -- and HGLog would have thrown it away, since it accepts
an observation only between 1090 and 1105. This is exactly why the bounds here
are wide: a model that only accepts what it expects cannot find out that the
expectation is wrong. One observation is not proof of 1054, but it is enough to
show 1100 is not right.
The landing countdown on the same flight promised release in 8 seconds and it
took 30. The transport may slow on approach while the heading fit reports an
average over its whole window, but that is a guess, so the addon now scores its
own release estimate against the actual release and keeps the running bias --
the same treatment the position estimate already gets, which is the only reason
anyone knows it lands within a third of a percent. - Report world height in /ewc shard, to settle whether descent is computable
Vignettes carry x and y only, and the API has no way to ask what the ground is
doing at an arbitrary point -- no raycast, no GetGroundHeight, deliberately.
None of the three competing addons reads a height at all.
UnitPosition is the one call that reports a z, and only for something with a
unit id. The transport's atlas is Vehicle-Air-Occupied, so it may well be a
targetable vehicle rather than a bare map marker. If it is, its altitude is
readable, the ground under a drop point is readable by standing on it, and the
descent becomes arithmetic instead of something to measure.
That reduces to one question answerable by mousing over the plane, so this
prints the answer rather than arguing about it. - Add the landing countdown, computing the leg that can be computed
RCT shows two legs -- transport to drop point, then parachute to ground -- and
learns both, per zone and per drop point, with a running mean it will not trust
until four crates have been timed at that exact spot. Until then it says
"Learning".
Only one of those legs needs learning. The heading fit already reports the
transport's speed and the prediction already knows which spot it is flying at,
so the time to release is a division. It works on the first flight, in a zone
nobody has ever visited.
The descent cannot be computed -- nothing observable says how high the crate
was released -- so it is measured, as the gap between the parachute appearing
and the crate being on the ground. Per zone rather than per drop point: RCT
splits it by point, which collects data an order of magnitude more slowly, and
whether the spread within a zone justifies that is a question the samples can
answer later.
An unmeasured zone still shows a figure rather than "Learning", flagged as
resting on a guess. Bounds are 5 to 240 seconds, wide enough to admit the 90 to
120 observed live. - Track one transport per zone, not one per vignette GUID
The readout went back to alternating COMMIT with "4 samples, not enough to fit
yet" on a Slayer's Rise flight, which the everFit guard was supposed to have
settled. It had not, because those were two different tracks: the transport's
vignette GUID churns, disappearing and returning under a new id every few
seconds, and keying on it built a fresh track each time with its own sample
count and its own idea of whether it had committed. One would commit while a
sibling sat at four samples.
The GUID cooldown, the everFit guard and the empty-track sweep were each
treating a symptom of this. A zone has one crate per cycle and therefore one
transport, so the zone is the honest key; the current GUID is carried on the
track and replaced as it moves. A GUID going quiet no longer ends anything,
since the event hands the track its replacement.
Prediction reads the zone's track directly rather than scanning for it, and
reports the committed call. - A called transport keeps its answer instead of re-guessing past it
In Coiled Isle the tracker called 46.7, 73.8, held it for a minute at zero
heading error, and then -- the moment the plane flew through the point -- began
talking about 57.6, 77.5. A transport carries one crate and drops it once, so
re-reading its heading afterwards is reading a plane that has finished its job.
Once called, the call stands. The only question left is whether it got there,
and saying so earns its place: on that flight the crate's vignette never
appeared at all, so telling the player the transport reached the point it was
called for is the only word they get about where the crate went.
Dropped the announced table with it. It existed to stop a steady prediction
repeating, which the commitment now does, leaving it written to nil in four
places and read in none. - Line up the route columns and prune dead shard timers
The countdown was unpadded, so 1:55 and 14:55 shunted everything after them
sideways and the leave column never lined up.
The shard re-rolls every time you leave a zone and fly back, so entries pile up
for shards nobody will stand in again -- three Zul'Aman shards inside one
evening's farming. They still predict their own shard correctly; the problem is
that returning to it is chance, so what they contribute is rows to read past.
Anything more than six cycles stale, nearly two hours, is dropped on login.
Also removed a defensive expression in the login path that guarded against
GetZoneInterval being absent. Data/Zones.lua loads before Core/Main.lua, so it
cannot be, and the guard was the kind of noise this addon exists to avoid. - HD for Harandar, and let Describe be the canonical spelling
Parsing stays forgiving -- hd, HD, harandar all resolve -- while Describe
writes the one form the addon uses. The round-trip test asserted the input
spelling came back unchanged, which only held while the two happened to match. - Ship travel estimates and the route commands
Silvermoon has portals to Harandar, Voidstorm and Coiled Isle; Eversong and
Zul'Aman go by mount, and Eversong is the short one because Silvermoon sits
inside it. Nothing observed exceeds two minutes.
The estimates are coarse on purpose. The spread WITHIN a zone -- which of its
drop points the crate picks, relative to where the portal leaves you -- is
about as wide as the spread between zones, so a precise per-zone figure would
be false precision. /ewc travel overrides any of them.
Erring low is deliberate and the asymmetry is real: overstating travel makes
the planner call a reachable drop 'missed' and the player skips a crate they
would have caught, while understating it sends them on a flight they were
going to make anyway and the grace period absorbs arriving a little late.
Also note that travel only decides anything in the last two minutes before a
drop. On an eighteen-minute cycle the rest of the time the answer is obvious. - Measure the respawn interval instead of assuming it
Three addons ship three different figures -- CrateTrackerZK 1100, RCT 1098-1099,
WarCrateTracker 1095 -- and none of them measured. HGLog does accumulate
observations, but only accepts one between 1090 and 1105, so its learning can
confirm the number it was given and can never discover a different one. That is
an assumption wearing a measurement's clothes.
The gap between two drops in one zone on one shard is the only direct
observation of the interval anybody gets. Timers.Record now returns it, and
NoteGap files it with bounds wide enough (240-7200s) not to prejudge the
answer. A gap spanning drops that were missed is divided down rather than
discarded.
Raw observations are kept rather than folded into a running mean: the question
is what the interval is, and a mean cannot show whether the values cluster
tightly or scatter. /ewc interval prints them. - Stop tracking a transport in a zone whose crate has already landed
The transport does not despawn when it drops its cargo, it circles, so it goes
on reporting itself as flying with nothing left to predict. Ending its track on
the drop was not enough: its vignette GUID churns, so the very next scan built
a brand new track with fresh state, which then narrated its own sample count
from scratch -- 2, 3, 2, 3 for as long as the plane stayed in the air.
A zone is left alone for two minutes after a crate comes down there. The next
one is around eighteen minutes out, so the silence costs nothing.
Also worth recording from the same flight: heading error sits at 0.00 degrees
on a straight run and climbs past 2.5 to 4 as the transport turns. No separate
turning threshold is needed after all -- the acceptance cone already carries
2.5 sigma of that error, so a circling plane widens it until nothing can meet
the margin and it simply stops committing. That was the behaviour I wanted to
see measured before trusting, and it holds. - Add the rotation planner: when to leave, not just what is soonest
Corrected model, from how raids actually run this. A crate gets taken, the raid
takes a mage portal to the capital, and flies out from there to the next zone
on a route agreed beforehand -- three to five zones, cycled. Nobody flies zone
to zone, so travel is one capital-to-zone number per zone rather than a matrix
over every pair, and the earlier geometry probe was measuring the wrong thing.
What a sorted timer list gets wrong follows directly: a drop 40 seconds out in
a zone four minutes away is not 'next' in any useful sense. It is the drop you
are guaranteed to miss, sitting at the top of the list above the one you could
have caught. Route.Plan marks each as go, wait, missed or unknown, and Next
returns the soonest one still reachable.
Carries a grace period: arriving a few seconds after a crate lands is ordinary
farming, not a miss.
Travel times are a placeholder 60s for now and are meant to be measured, not
guessed at -- the number is deliberately one value so it is obvious it has not
been earned yet. - Move a comment back onto the function it describes
- Add zone abbreviations and a geometry probe for routing
Abbreviations are what farmers actually say and what a route editor has to
accept as input. Taken from WarCrateTracker (MIT, Samuel Colburn), which
carries the same set. Parsing is deliberately forgiving -- ES and EW both mean
Eversong, since this comes out of a text box.
/ewc geo asks C_Map where each of the six zones sits on its parent map and
prints the distances between them. Routing needs to know how long a hop takes,
and the first thing to establish is whether the zones share a coordinate space
at all rather than assuming they do. - Stop the readout alternating COMMIT with 'not enough samples'
Samples age out of the window and arrive again, so a tracked transport's count
oscillates across the fit threshold for its whole flight. The previous guard
only suppressed a falling count, which does not cover 5, 4, 5, 4 -- half a
92-second flight's log was that alternation.
The waiting line now belongs strictly to acquiring a track: said once per count
on the way up, and never again once the track has fitted. - Quieten a dying track, and bury it when it is empty
Flying out of a transport's range now counts the samples back down as they age
out of the window, which is the trim working, but narrating '4, 3, 2, 1' on the
way to a death already decided is noise. The waiting line is reported only
while the count is climbing.
A track with no samples left is dropped at once rather than sitting out
TRACK_STALE so it can be narrated at.
Written without goto deliberately: the syntax pass runs Lua 5.5 via lupa and
WoW runs a 5.1-based dialect, so a 5.2+ construct parses green here and proves
nothing about the client. Said so in the runner next to the check. - Correct the sample-rate note: the cadence varies, it is not a fixed 5.5s
One Voidstorm flight yielded a position about every 5.5 seconds; a Zul'Aman
one reached thirty samples in eighteen. The event feeds the track alongside
the poll and fires far more often for some flights than others, so the poll is
a floor under the rate rather than what sets it. The old note stated the slow
case as if it were the API's fixed behaviour, which is exactly the kind of
measurement someone would later tune MIN_SAMPLES against. - Stop discarding the target the moment the transport reaches it
A flight in Zul'Aman predicted 48.9, 69.2 and the crate landed at 46.9, 62.2 --
7.2% of the map out. The log shows the addon had ranked the correct spot first
for a solid minute beforehand, then changed its mind at the last moment and
committed to the wrong one.
MIN_AHEAD dropped any spot nearer than 4% of the map from the candidate list,
on the reasoning that there is nothing to predict about a spot you are already
on top of. What it actually did was remove the transport's real target exactly
as it arrived, leaving the next spot along the same bearing as the unopposed
front runner. A prediction that gets worse the closer it gets is worse than no
prediction.
Candidates are now everything ahead of the transport. Arrival is a separate
test made on distance rather than bearing -- close to a spot and on the ray
means that spot, which is also where the angular test stops meaning anything,
since the cone is SCATTER/along and blows up as along goes to zero.
Ties in the ranking now go to the nearer spot. Two spots exactly on the
bearing cannot be told apart by angle and the transport reaches the near one
first; leaving that to table.sort is not something a waypoint should rest on.
Also: "transport spotted" is announced per zone with a cooldown. A transport's
vignette GUID churns and each reappearance built a fresh track, which printed
it twenty-five times in forty-five seconds for one plane. - Fix two faults a live flight exposed, and stop the readout spamming
The game yields a new vignette position only about every 5.5 seconds, measured
over a 69-second flight. Everything below follows from that, and none of it
was visible from outside the game.
Trim stopped at MIN_SAMPLES so a briefly-quiet transport would keep its
heading. But a track sits at exactly the minimum almost all the time at that
sample rate, so nothing was ever dropped: a landed transport went on
reporting a fit spanning 86 seconds against a 20-second window. It now trims
unconditionally and Fit returns nil when too little recent data is left,
which is the honest answer.
A crate's vignette carries a different GUID from the transport that dropped
it, and the transport does not despawn -- it circles its drop point. Clearing
the track by the crate's own GUID therefore never touched it, and it narrated
"nothing-ahead" forever. Any crate vignette now ends every transport track in
that zone.
The watch readout printed once a second, which meant 47 identical COMMIT
lines for one flight. It reports on change only, and is off by default now
that the first flights have been watched.
Poll drops to 1Hz. At 4Hz it was asking four times for each position the game
bothers to update, and the sample rate was never ours to raise. - Use the real map tree in the zone tests
Read out of the live client with /ewc map instead of guessed at. Two rows
carry the whole normalisation rule, and neither was obvious from outside the
game:
Silvermoon City is mapType 3 (Zone) sitting directly under Eversong Woods,
also mapType 3 and a tracked crate zone. A walk that does not stop at the
first Zone it meets reports a sanctuary as Eversong, then reads a shard there.
The Den, Midnight's housing area, is mapType 5 (Micro) under Harandar. The
same walk has to keep going for that one, or the addon goes blind wherever
housing is.
Satisfying both at once is why the rule is "stop at the first Zone" rather
than a table of special cases, and it means no alias is needed for housing in
any of the six zones. Also confirms Quel'Thalas 2537 is mapType 2, a
continent, so excluding it from the tracked list was right. - Initial commit: crate tracking and drop-point prediction for Midnight
A War Supply Crate tracker built from scratch. The prediction model is the
part worth explaining.
The transport enters a zone from varying map edges, not one fixed launch
point, so matching a flight against a per-zone origin cannot work. Instead
this casts a ray from the transport's live position along its measured
heading and takes the nearest catalogued landing spot. That needs no launch
point and no learned route database.
Drop locations turn out to be a discrete fixed set: 5-16 spots per zone,
with repeat drops landing a median 0.12% of the map apart and distinct spots
sitting over 4% apart. Established from Wowhead's recorded spawns for object
290129, cross-checked against a competitor's independently farmed list, which
agrees on 106 of its 117 points. Data/DropPoints.lua ships 220 spots across
31 zones; Track/Learn.lua adds the ones that data misses, which Voidstorm
proved it does.
Heading comes from regressing x and y on time across the whole sample window,
not from differencing two samples. Accuracy depends on it almost entirely:
simulated, picking the right spot the moment a transport appears scores 90%
at half a degree of error and 35% at eight. Predict refuses to commit when
the best and second candidates are too close, which is roughly one geometry
in six and cannot be resolved by flying further.
Verified live in Zul'Aman: predicted 32.8, 77.5 about two minutes ahead, the
crate landed at 32.7, 77.6.
Everything down to Track/ runs under a plain Lua interpreter, so tests/
covers the maths outside the game. 83 checks, plus a syntax pass and a lint
for locals used before they are declared, which caught a real nil-global call
in the scanner.
All Relations
- All Relations
- Embedded Library
- Optional Dependency
- Required Dependency
- Tool
- Incompatible
- Include
This mod has no related projects

