feat: Trading #109
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/trade"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The rest of it: the request handshake, the offer and confirm screens, the acceptances, the exchange and every way a trade ends. The swap is the barrier report's fourth tenant, and the distinction worth keeping is that the other three are things that happened while this is a claim. A player who has accepted the confirm screen reports their own offer and their partner's seat; the world matches the reports after every player has moved and hands each offer to the other side. So no session ever performs half a swap, because no session performs any of it — there is no in-flight window where one side has committed and the other has not, which is the window a write-ahead journal exists to close. It also deletes the initiator asymmetry, and with it the simultaneous-accept hang that has to be fixed separately once a completion has one. The {low, high} in matches/1 is deduplication of a symmetric fold and is deliberately not that initiator returning. Items move only where two independent layers agree: the seats, which only the world knows, and the accounts, which only the sessions stated. A forged or desynchronised half satisfies neither on its own. Somebody has to remember a trade request and it cannot be either session — one may not call the other and neither may call the world. So the world holds them, with an expiry, and a match consumes both: without that, a finished trade reopens the moment either player clicks anything. The bug the suite caught is a contract-shape one. Contract.Trade.accept/2 answers given that this player has just accepted, and the first wiring called it from the partner's acceptance too, so one click from either side advanced a player who had accepted nothing to the screen that arms the swap. Each side now asks only when it already accepted, and both ask independently the moment they can see two — which is also why two clicks in the same tick advance both rather than hanging both. Offer-X and Remove-X are answered rather than omitted, because script 763 sets Remove-X unconditionally and a mask does not gate a script-built grid: an op that appears must work. Scripts 1216 and 1217 are the value tooltip and nothing in the cache calls them, so the server does. The teardown has one entry point, Modal.close/1, because it is the only close in the game that moves items — and why the trade ended travels on the trade, since none of the five callers knows. A delivery that will not fit goes to the bank rather than the floor: nothing here may destroy an item and a floor pile can be walked away from. Autosave is skipped for the length of a trade, which is what makes a server crash a clean revert rather than a loss, and the logout save returns the offer first. The residual risk is single-sided loss on a delivery, which is the correct trade: a loss is recoverable by an operator and a duplication is not. The tests seat both players in one process, so every cross-session send lands in the test's mailbox and is applied by hand. That is the design made visible rather than a shortcut around it, and the swap they exercise is the world's own pure matcher. Most of them are written as attacks, and the two that matter count items across the whole pair before and after.