|
1 | 1 | # Changelog |
2 | 2 |
|
| 3 | +## [v3.1.14] - 2026-08-29 |
| 4 | + |
| 5 | +### Fixed |
| 6 | +- **The account's session key was written to every log.** `GameHandler::connect` printed all forty bytes at INFO on each world login, and `computeAuthHash` printed them again at DEBUG along with the hash input, which ends with the same forty. That key is the shared secret the world handshake proves knowledge of and the header cipher is keyed from - anyone holding it can authenticate as the account and decrypt its traffic - and the log is the file players are routinely asked to attach to a bug report. The lengths are logged instead, which is all an AUTH_REJECT diagnosis ever needed. Thanks to @fabge (#134) |
| 7 | +- **Nothing in the bank window could be dragged, and then it moved the wrong thing.** Two faults, one behind the other. `bankframe.lua` picks an item up with `PickupContainerItem(BANK_CONTAINER, id)` and `BANK_CONTAINER` is -1; both halves of that function read the container by hand and knew two of the six, so the pickup answered "nothing there" for every occupied square and the drop asked `wornBagContainer` for container 17. With those routed through the shared helpers the drag reached the *source* mapping, where -1 is two different places: the paperdoll records it for a worn item, and the bank's own slots are numbered with it. Every negative source read as equipment, so bank slot five went out as equipment slot four. The flag that tells the two apart was already beside the cursor and was not being read; the same hole made a worn helm dropped on bank slot one read as "back where it came from" and do nothing |
| 8 | +- **The bank's bag row addressed the wrong slots, and the button that buys one did not exist.** A bag button's id is the container number of that bank bag - five through eleven, which `bankframe.lua` says itself by greying out an unbought slot with `(button:GetID() - 4) > GetNumBankSlots()` - and it was counted from one, so the row sat four slots along and a bag in the first slot had no button showing its icon. `PutItemInBag` took the four worn slots only, so a bag clicked onto a bank slot opened whatever was already there. And `BankFramePurchaseButton` was never created: Blizzard declares it `virtual="true"` while nesting it inside another frame's `<Frames>`, which the real client builds regardless - a template is declared at the top of a file, and a nested element has a parent, which is what an instance has. Six frames in the shipped interfaces are written that way and all six are real, `MovieProgressBar` among them, which `movierecordingprogress.lua` drives by name |
| 9 | +- **The bank is not the same shape in every expansion.** Vanilla has 24 general slots and 6 bank bags where 2.0 onward have 28 and 7, and everything after the general slots moves with the count: turtle's `Player.h` puts `BANK_SLOT_BAG_START` at 63 and `KEYRING_SLOT_START` at 81 where 3.3.5 has 67 and 86. All of it was fixed at WotLK's figures, so on vanilla every bank bag operation named a slot four along and every keyring one five along. Only the sending side was affected - contents come off update fields, which are already per-expansion - and the 24 general slots were right by luck, both layouts starting at 39 |
| 10 | +- **Selling a stack at a vendor sold one of it.** All four sell sites asked for a count of one with the slot's own stack count in hand, and the auto-sell sweep added one unit's price to the total it reported. Thanks to @fabge (#136) |
| 11 | +- **A chat message could be built out of nothing.** The declared length was checked against a ceiling and not against what the packet actually held, and `readUInt8` answers zero at the end of the buffer without advancing - so a length longer than the packet produced a NUL-padded line and reported success, and a length of 8192 or more skipped the message and read its first byte as the chat tag. Two `readSizedString` results were also discarded, where a false means the declared name length is a lie and reading on consumes the guid behind it. Thanks to @fabge (#135) |
| 12 | +- **Rearranging bags on the bar with their windows open left them unusable.** `swapBagSlots` moved the bags and told the interface nothing, so an open `ContainerFrame` went on naming the container it was built for and a click in it acted on that one. The real client fires `BAG_CLOSED` and `ContainerFrame_OnEvent` hides that frame, which is the whole of what closing and reopening was doing by hand |
| 13 | +- **The client's own addons have never been in a release.** The build copies `addons/` next to the binary and every staging step copied the binary, the extractor, the scripts, the assets and `Data` - so `WoweeAllBags`, which is the combined bag window and the Sort Bags button, existed in a local build and in no download. Both were drawn by this client until the bags were handed to FrameXML, which has neither |
| 14 | +- **The login screen's buttons were drawn over the sheet's own edge.** 3.1.13 moved the rule under each heading down to clear the descenders and left the card's height computed off the em, so the sheet came out about a third of a title's height short of what was drawn into it and the footer, the settings gear and the close cross went over the bottom. Both halves read one measurement now |
| 15 | +- **A stack could not be split by typing the number.** `StackSplitFrame`'s key handler passes digits straight through on purpose - they belong to OnChar, which builds the number up ten at a time - and OnChar was never dispatched: typed text went to the focused edit box and was dropped when there was none. It reaches the frame that asked for the keyboard now, one character at a time |
| 16 | +- **Disenchanting asked whether to bind the item, and offered to destroy things it could not touch.** `completeItemUseOnItem` read the target and never the spell, so a bind-on-equip target raised "Enchanting this item will bind it to you" in front of a cast that turns it to dust. It asks the spell what it does now - disenchant, prospecting and milling take an item apart rather than change it - with the effect ids read off the client's own Spell.dbc rather than recalled. A disenchant asks its own question instead, and only about uncommon-or-better weapons and armour, which is the rule the server enforces |
| 17 | +- **Shift-hovering compared nothing for an item whose info had not arrived.** The comparison reaches the worn item through `GameTooltip:GetItem()`, which answers from a field only one of the three tooltip-fill paths was setting - and that same path was the only one firing `OnTooltipSetItem`, which is where the comparison is asked for. So it was not comparing badly; it was never asked |
| 18 | +- **A bag said "Container" and nothing else.** The slot count was not reachable from the tooltip builder, so `CONTAINER_SLOTS` - the client's own "%d Slot %s" - could not be written. The subclass names the kind, so a quiver and an ammo pouch read as themselves |
| 19 | +- **A burst of warnings cost a write syscall per line.** Every warning flushed the stream by itself, and this client puts its diagnostics at warning on purpose: one session's log was 1,270 lines in 48 seconds and all but five of them warnings, including 112 while FrameXML loads and 200 for a single takeover check. Warnings take the same 250ms interval as everything else now, which turns a burst into one write while still writing a lone warning immediately; errors and worse still flush at once, because a crash runs no destructor |
| 20 | +- **The server's time was thrown away before WotLK.** `SMSG_QUERY_TIME_RESPONSE` was refused unless it carried eight bytes and turtle's own server builds it four long, so the whole response was dropped and the client never learned the time from the one packet that carries it |
| 21 | +- **A quest in progress had no marker on a 1.12 client.** `IncompleteQuestIcon.blp` is 2.x's art; a vanilla `GossipFrame` holds `ActiveQuestIcon` and `AvailableQuestIcon` and nothing else, so the load failed twice and the head stayed bare. It falls back to the active mark |
| 22 | +- **An inspect on a 1.12 realm reported itself as a truncated packet.** The guid is the whole answer there, which is the shape rather than a short packet, and every inspect wrote a warning into a log that is read for faults |
| 23 | +- **`SMSG_SPELL_GO`'s target lists are full guids.** Restoring the order after #135 turned it round: turtle's own `WriteSpellGoTargets` writes both lists with the operator that streams a `uint64`, and packed is written explicitly through `GetPackGUID` for exactly the two fields at the head - the two this parser already reads as packed |
| 24 | +- **A sweep probe that could not see its subject failed instead of skipping.** Building the headless FrameXML runner on a checkout that has never extracted the game turned eleven probes on against an interface that had not loaded, and five failed on frames that do not exist. `Data/` is committed while the interface is not, so they ask for `Data/interface` now - what `missing_input` already requires of the sweeps beside them |
| 25 | + |
3 | 26 | ## [v3.1.13] - 2026-08-28 |
4 | 27 |
|
5 | 28 | ### Fixed |
|
0 commit comments