app: a held press cannot time out while something still guards it
The queue stamped each entry's deadline when the press was made. That reads right and has a hole in it: a cast pushed back twice (`SMSG_SPELL_DELAYED`) ends after the window a press made at its start was given, so the entry times out while the thing it is waiting for is still running — and the press vanishes with no packet, no error line and no cast. That is the one failure mode a player cannot describe, and it is exactly the class of bug the press queue was supposed to remove rather than introduce. The same hole swallows a press held through a cast whose global cooldown then outlasts the window, which is the ordinary case rather than an edge one: a 500 ms window, a cast that ends, and 1.5 s of global cooldown behind it. So a slot now keeps the window and the base it is measured from, and the drain ratchets that base forward — only ever forward — past every live guard: the cast's reopening, the channel's trimmed end, and the global cooldown's. An entry gives up when nothing is holding it back any more and the window has run out since, which is what "how long past the guard's end" meant all along. Scripts keep a fixed deadline. Their contract is "after queued spells", and a script whose spells never fired has nothing left to run after. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
This commit is contained in:
@@ -1040,6 +1040,14 @@ a narrower set is the safe direction to be wrong in.
|
||||
| `NP_OnSwingBufferCooldownMs` | stored | A rate limit on re-queueing an on-swing strike. The slot is live; what is missing is anything to rate-limit, since a re-press of the queued strike already takes the reference's own silent bail (`6e4d43`) and so cannot queue twice |
|
||||
| `NP_ChannelLatencyReductionPercentage` | **LIVE** | How much of the measured round trip comes off a channel's named end before the queue stops treating it as a guard, so a press held through a channel is not sent a round trip late. The arithmetic is the cast buffer's again, and worth writing down because "send early" sounds reckless: the server starts the channel at `T` and ends it at `T + duration`, we learn of it half a trip later, so our own end is already half a trip *after* the server is free — trimming 75 % of `L` puts a packet at the server a quarter of a trip after its own end. The named end, not the slack-inflated one, for the same reason. With no pong measured yet nothing is trimmed |
|
||||
|
||||
**A held entry's window is measured from the guard's *live* end, not the one stamped when the
|
||||
press was made.** A fixed deadline reads right and has a hole in it: a cast pushed back twice
|
||||
(`SMSG_SPELL_DELAYED`) ends after the window a press made at its start was given, so the entry
|
||||
would time out while the thing it is waiting for is still running — and the press would vanish with
|
||||
no packet and no error line, which is the one failure mode a player cannot describe. The same hole
|
||||
swallows a press held through a cast whose global cooldown then outlasts the window. So the base
|
||||
ratchets forward while anything still guards, and only ever forward.
|
||||
|
||||
**One behaviour worth knowing before testing.** Pressing a spell during a long cast holds it for the
|
||||
whole cast and fires it at the end — the last press wins, so pressing three different spells during
|
||||
a Pyroblast fires the third. That is upstream's `Replacing queued nonGcd spell X with Y` applied to
|
||||
|
||||
Reference in New Issue
Block a user