Trading
Peer-to-peer item trading between users in the same room.
Trade flow:
User A Server User B
│ │ │
│── [1481] TradeStart ──>│ │
│ (roomUnitId_B) │ find B by room unit ID │
│ │ create RoomTrade │
│ │ set /trd status for A+B │
│ │ → [1640] UserStatus ───>│ (both get /trd status)
│<─ [1640] UserStatus ───│ │
│ │ │
│ ┌── [2505] TradeStart #1 (to B) ────────────────>│
│ │ (userId_A, state=1 ; userId_B, state=1) │
│ │ server calls sendMessageToUsers() which │
│ │ iterates both trade participants │
│<─┘─ [2505] TradeStart #2 (to A) ──────────────────│
│ (same body: both userIds + state=1) │
│ │ │
│── [3107] OfferItem ───>│ (itemId:int) │
│ │ remove from A inventory │
│ │ add to A's trade slot │
│ │ clearAccepted() │
│ │ updateWindow() → │
│ ┌── [2024] TradeUpdate (to B) ──────────────────>│
│ │ (A's slot filled, B's slot empty) │
│<─┘─ [2024] TradeUpdate (to A) ────────────────────│
│ (same body, both see full updated offer state) │
│ │ │
│ │<── [3107] OfferItem ──── │ (B offers an item)
│ │ remove from B inventory │
│ │ clearAccepted() │
│ │ updateWindow() → │
│ ┌── [2024] TradeUpdate (to A) ──────────────────>│
│ │ (A's slot: A's item; B's slot: B's item) │
│<─┘─ [2024] TradeUpdate (to B) ────────────────────│
│ (same body broadcast to both) │
│ │ │
│── [3863] Accept ──────>│ (no body read) │
│ │ user.setAccepted(true) │
│ ┌── [2568] TradeAccepted (to B) ────────────────>│
│ │ (userId_A, state) │
│<─┘─ [2568] TradeAccepted (to A) ──────────────────│
│ (same body, both learn A accepted) │
│ │ │
│ │<── [3863] Accept ─────── │ (B accepts)
│ │ both accepted → true │
│ ┌── [2568] TradeAccepted (to A) ────────────────>│
│ │ (userId_B, state) │
│<─┘─ [2568] TradeAccepted (to B) ──────────────────│
│ │ │
│ ┌── [2720] WaitingConfirm (to A) ───────────────>│
│ │ (empty - prompt to click OK) │
│<─┘─ [2720] WaitingConfirm (to B) ─────────────────│
│ │ │
│── [2760] Confirm ─────>│ (no body read) │
│ │ user.confirm() │
│ ┌── [2568] TradeAccepted (to B) ────────────────>│
│<─┘─ [2568] TradeAccepted (to A) ──────────────────│
│ │ │
│ │<── [2760] Confirm ───────│ (B confirms)
│ │ both confirmed: │
│ │ tradeItems() → swap DB │
│ │ closeWindow() → │
│ ┌── [1001] TradeCloseWindow (to A) ─────────────>│
│<─┘─ [1001] TradeCloseWindow (to B) ───────────────│
│ │ │
│ ┌── [2369] TradeComplete (to A) ────────────────>│
│<─┘─ [2369] TradeComplete (to B) ──────────────────│
│ (items now swapped in DB + inventory) │
Key: every
sendMessageToUsers()call goes to both traders.RoomTrade.sendMessageToUsers()loopsthis.users(exactly 2RoomTradeUserentries) and callshabbo.getClient().sendResponse(message)for each. There is no concept of "sender only" or "other only" - the same composed message goes to both, with the same bytes. Recipients distinguish themselves by reading their ownuserIdout of the body of [2024]TradeUpdate, [2568]TradeAccepted, etc.[2720] TradingWaitingConfirm (ID
2720) is sent when both traders have clicked Accept but neither has yet clicked the final Confirm. It signals the client to show the "waiting for confirmation" UI state. This message was missing from the original diagram.
C→S 1481 - TradeStart
Nitro:
TRADESource:
TradeStartEvent,TradingOpenComposer
Body:
Initiates a trade request with another user in the same room by their account ID. The server opens a trading session and notifies both parties with S→C 2505 TradeOpen.
C→S 3107 - TradeOfferItem
Nitro:
TRADE_ITEMSource:
TradeOfferItemEvent,TradingListAddItemComposer
Adds a single inventory item to the user's side of the active trade offer by item ID.
Body: (empty)
C→S 1263 - TradeOfferMultipleItems
Nitro:
TRADE_ITEMSSource:
TradeOfferMultipleItemsEvent,TradingListAddItemsComposer
Adds multiple inventory items to the user's side of the active trade offer in a single request.
Body:
count: int
C→S 3845 - TradeCancelOfferItem
Nitro:
TRADE_ITEM_REMOVESource:
TradeCancelOfferItemEvent,TradingListItemRemoveComposer
Removes a specific item from the user's current trade offer by item ID.
Body: (empty)
C→S 3863 - TradeAccept
Nitro:
TRADE_ACCEPTSource:
TradeAcceptEvent,TradingAcceptComposer
Signals that the user accepts the current trade offer. Both parties must accept before the confirmation step becomes available.
Body: (empty)
C→S 1444 - TradeUnAccept
Nitro:
TRADE_UNACCEPTSource:
TradeUnAcceptEvent,TradingUnacceptComposer
Withdraws the user's acceptance of the current trade offer, reverting to the negotiation state.
Body: (empty)
C→S 2760 - TradeConfirm
Nitro:
TRADE_CONFIRMSource:
TradeConfirmEvent,TradingConfirmationComposer
Sends the final confirmation after both traders have accepted, committing the trade. The server executes the item swap once both parties confirm.
Body: (empty)
C→S 2551 - TradeClose
Nitro:
TRADE_CLOSESource:
TradeCloseEvent,TradingCloseComposer
Closes the trade window on the client side without cancelling the session. Used when the trade completes normally.
Body: (empty)
C→S 2341 - TradeCancel
Nitro:
TRADE_CANCELSource:
TradeCancelEvent,TradingCancelComposer
Body:
(empty)
Cancels the active trading session, closing it for both participants. No parameters; the server broadcasts S→C 1373 TradeClosed.
S→C 2505 - TradeOpen
Nitro:
TRADE_OPENSource:TradeStartComposer,TradingOpenEvent
| Field | Type | Notes |
|---|---|---|
| firstUserId | int | First trader account ID |
| firstUserState | int | First trader state (0=not accepted, 1=accepted) |
| secondUserId | int | Second trader account ID |
| secondUserState | int | Second trader state |
Notifies both traders that a trading session has been opened, including each trader's ID and initial state.
S→C 217 - TradeOpenFailed
Nitro:
TRADE_OPEN_FAILEDSource:TradeStartFailComposer,TradingOpenFailedEvent
| Field | Type | Notes |
|---|---|---|
| reason | int | 7 = you are already trading; 8 = other user is already trading |
| otherUserName | string | Username of the other party involved |
Notifies that opening a trade failed because one of the parties is already in another trade.
S→C 3128 - TradeNotOpen
Nitro:
TRADE_NOT_OPENSource:UnknownTradeComposer,TradingNotOpenEvent
(no Arcturus composer)
Sent when the trading session is not open.
Notifies the client that a trade action was attempted while no trading session was open.
Body: (empty)
S→C 2024 - TradeListItem
Nitro:
TRADE_LIST_ITEMSource:TradeUpdateComposer,TradingListItemEvent
| Field | Type | Notes |
|---|---|---|
| firstUserId | int | First trader account ID |
| firstUserItemCount | int | Number of items offered by first trader |
| [firstUserItemCount × item] | TradeItem | Item data (itemId, typeId, count, etc.) |
| firstUserNumItems | int | Total item count for first trader |
| firstUserNumCredits | int | Credits offered by first trader |
| secondUserId | int | Second trader account ID |
| secondUserItemCount | int | Number of items offered by second trader |
| [secondUserItemCount × item] | TradeItem | Item data |
| secondUserNumItems | int | Total item count for second trader |
| secondUserNumCredits | int | Credits offered by second trader |
Delivers the current item offers from both traders in the active trading session.
S→C 2369 - TradeComplete
Source:
TradeCompleteComposer,TradeCompleteEvent
Signals that the item swap is complete and both traders' inventories have been updated in the database.
Body: (empty)
Sent to both traders after both have confirmed the trade (following
TRADE_COMPLETEDS→C 1001). Signals that items have been swapped in the database. The client uses this as the final "done" signal to clear the trade UI state.
S→C 2873 - TradeNoSuchItem
Nitro:
TRADE_NO_SUCH_ITEMSource:TradingNoSuchItemEvent
(no Arcturus composer)
Sent when a trade offer references an item that no longer exists.
Signals that a trade offer referenced an item that no longer exists in the user's inventory. Empty body; Nitro-only, no Arcturus composer.
Body: (empty)
S→C 2568 - TradeAccepted
Nitro:
TRADE_ACCEPTEDSource:TradeAcceptedComposer,TradingAcceptEvent
| Field | Type | Notes |
|---|---|---|
| userId | int | Account ID of the trader who accepted/unaccepted |
| accepted | int | > 0 = accepted; 0 = unaccepted |
Delivers the updated accept/confirm state for both traders after one of them clicks Accept.
S→C 2720 - TradeConfirmation
Nitro:
TRADE_CONFIRMATIONSource:TradingWaitingConfirmComposer,TradingConfirmationEvent
(no Arcturus composer)
Both traders have confirmed - waiting for final acceptance.
Signals that both traders have accepted and the trade is now awaiting final confirmation from both sides. Empty body.
Body: (empty)
S→C 1254 - TradeOtherNotAllowed
Nitro:
TRADE_OTHER_NOT_ALLOWEDSource:OtherTradingDisabledComposer,TradingOtherNotAllowedEvent
(no Arcturus composer)
Sent when the other trader is not allowed to trade (e.g. trading ban).
Notifies the initiating trader that the other party has trading disabled.
Body: (empty)
S→C 3058 - TradeYouNotAllowed
Nitro:
TRADE_YOU_NOT_ALLOWEDSource:YouTradingDisabledComposer,TradingYouAreNotAllowedEvent
(no Arcturus composer)
Sent when the current user's trade permissions do not allow trading.
Notifies the client that their own account does not have trading permission.
Body: (empty)
S→C 1373 - TradeClosed
Nitro:
TRADE_CLOSEDSource:TradingCloseEvent
(no Arcturus composer)
Sent when the trade session is closed (cancelled or completed).
Signals that the trading session has been closed, either by cancellation or after completion. Clients should dismiss the trade UI on receipt.
Body: (empty)
S→C 1001 - TradeCompleted
Nitro:
TRADE_COMPLETEDSource:TradeCloseWindowComposer,TradingCompletedEvent
(no Arcturus composer)
Sent when both traders confirm and the trade completes.
Sent when both traders have confirmed; instructs the client to close the trade window and finalise.
Body: (empty)