Skip to main content

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() loops this.users (exactly 2 RoomTradeUser entries) and calls habbo.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 own userId out 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: TRADE

Source: 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_ITEM

Source: 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_ITEMS

Source: 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_REMOVE

Source: TradeCancelOfferItemEvent, TradingListItemRemoveComposer

Removes a specific item from the user's current trade offer by item ID.

Body: (empty)

C→S 3863 - TradeAccept

Nitro: TRADE_ACCEPT

Source: 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_UNACCEPT

Source: 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_CONFIRM

Source: 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_CLOSE

Source: 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_CANCEL

Source: 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_OPEN Source: TradeStartComposer, TradingOpenEvent

FieldTypeNotes
firstUserIdintFirst trader account ID
firstUserStateintFirst trader state (0=not accepted, 1=accepted)
secondUserIdintSecond trader account ID
secondUserStateintSecond 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_FAILED Source: TradeStartFailComposer, TradingOpenFailedEvent

FieldTypeNotes
reasonint7 = you are already trading; 8 = other user is already trading
otherUserNamestringUsername 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_OPEN Source: 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_ITEM Source: TradeUpdateComposer, TradingListItemEvent

FieldTypeNotes
firstUserIdintFirst trader account ID
firstUserItemCountintNumber of items offered by first trader
[firstUserItemCount × item]TradeItemItem data (itemId, typeId, count, etc.)
firstUserNumItemsintTotal item count for first trader
firstUserNumCreditsintCredits offered by first trader
secondUserIdintSecond trader account ID
secondUserItemCountintNumber of items offered by second trader
[secondUserItemCount × item]TradeItemItem data
secondUserNumItemsintTotal item count for second trader
secondUserNumCreditsintCredits 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_COMPLETED S→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_ITEM Source: 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_ACCEPTED Source: TradeAcceptedComposer, TradingAcceptEvent

FieldTypeNotes
userIdintAccount ID of the trader who accepted/unaccepted
acceptedint> 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_CONFIRMATION Source: 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_ALLOWED Source: 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_ALLOWED Source: 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_CLOSED Source: 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_COMPLETED Source: 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)