Trading System
The trading system enables secure player-to-player item exchange using OSRS-style mechanics with a two-screen confirmation flow and comprehensive anti-scam features.Trading code lives in:
packages/server/src/systems/TradingSystem/- Server-side trade managementpackages/client/src/game/panels/TradePanel/- Client-side UI componentspackages/client/src/hooks/useModalPanels.ts- Trade event handlers
Overview
The trading system implements OSRS-accurate player-to-player trading with:- Two-screen confirmation flow (Offer → Confirm)
- Anti-scam features (value warnings, modification tracking)
- Server-authoritative state management
- Proximity validation (2-tile range)
- Interface blocking during active trades
Trade Flow
1. Trade Request
Players initiate trades by right-clicking another player and selecting “Trade with”:- Both players must be within 2 tiles (Chebyshev distance)
- Neither player can be in another trade
- Neither player can be in combat
- 30-second timeout for acceptance
- Target receives
TradeRequestModalwith accept/decline buttons - Clicking accept opens trade panel for both players
- Declining or timeout cancels the request
Screen 1: Offer Screen
Players add/remove items and accept the offer.Adding Items
Left-click: Offer 1 item Right-click: Open quantity menu (Offer-1, Offer-5, Offer-10, Offer-All, Offer-X)Removing Items
Left-click trade slot: Remove 1 item Right-click trade slot: Open quantity menu (Remove-1, Remove-5, Remove-All, Remove-X)Acceptance
Both players must click “Accept” to proceed to Screen 2:- Acceptance is cleared when either player modifies their offer
- Both players must re-accept after any change
- Prevents “bait and switch” scams
Screen 2: Confirm Screen
Final read-only review before trade completion.Confirmation Display
Shows both players’ final offers with total values:Final Acceptance
Both players must click “Accept” on Screen 2 to complete the trade:- Server validates both players accepted on Screen 2
- Items transferred atomically in database transaction
- Trade panel closes for both players
- Success message displayed
Anti-Scam Features
Value Imbalance Warnings
The trade panel shows warnings when offer values are significantly different:Removed Item Tracking
TheuseRemovedItemTracking hook highlights items that the opponent removed from their offer:
- Removed items flash red in the trade offer grid
- Helps players notice when opponent removes valuable items
- Clears when player modifies their own offer
Modification Warnings
Screen 2 shows a warning if the opponent modified their offer after you accepted Screen 1:Network Protocol
Trade Events
| Event | Direction | Data | Description |
|---|---|---|---|
tradeRequest | Client → Server | targetPlayerId, timestamp | Initiate trade |
tradeAcceptRequest | Client → Server | tradeId, timestamp | Accept incoming request |
tradeDeclineRequest | Client → Server | tradeId, timestamp | Decline incoming request |
tradeAddItem | Client → Server | tradeId, inventorySlot, quantity, timestamp | Add item to offer |
tradeRemoveItem | Client → Server | tradeId, inventorySlot, quantity, timestamp | Remove item from offer |
tradeAccept | Client → Server | tradeId, screen, timestamp | Accept current screen |
tradeDecline | Client → Server | tradeId, timestamp | Cancel trade |
UI_UPDATE | Server → Client | component: "trade", data | Trade state updates |
UI_UPDATE Components
The server sends trade updates viaUI_UPDATE events with different component types:
Trade Open:
Server Architecture
TradingSystem
Location:packages/server/src/systems/TradingSystem/index.ts
Server-authoritative system managing all trade state:
createTradeRequest()- Initiate trade with proximity checkacceptTradeRequest()- Accept incoming requestaddItemToOffer()- Add item with validationremoveItemFromOffer()- Remove item and reset acceptanceacceptOffer()- Accept current screencompleteTrade()- Execute atomic item transfercancelTrade()- Cancel trade and cleanup
Trade Handlers
Location:packages/server/src/systems/ServerNetwork/handlers/trade/
Modular handlers for trade operations:
| File | Purpose |
|---|---|
request.ts | Trade initiation with proximity checks |
items.ts | Add/remove items from offers |
acceptance.ts | Accept/decline logic |
swap.ts | Screen transitions (Screen 1 ↔ Screen 2) |
helpers.ts | Shared utilities and validation |
types.ts | Type definitions |
- Proximity validation (2-tile range)
- Inventory validation (item exists, sufficient quantity)
- Duplicate prevention (can’t offer same item twice)
- Atomic transfers (database transaction)
- Audit logging for all trades
Client Architecture
TradePanel Component
Location:packages/client/src/game/panels/TradePanel/
Modular React components for trade UI:
| Component | Purpose |
|---|---|
TradePanel.tsx | Main trade window with screen switching |
TradeRequestModal.tsx | Incoming request modal |
components/TradeSlot.tsx | Individual trade slot with item icon |
components/InventoryItem.tsx | Clickable inventory item |
components/InventoryMiniPanel.tsx | Inventory grid for item selection |
modals/ContextMenu.tsx | Right-click menu for quantity selection |
modals/QuantityPrompt.tsx | Offer-X quantity input |
hooks/useRemovedItemTracking.ts | Anti-scam tracking |
utils.ts | Formatting and parsing |
types.ts | TypeScript interfaces |
useModalPanels Hook
Location:packages/client/src/hooks/useModalPanels.ts
Centralized hook for all modal panel events including trade:
Trade Constants
Database Integration
Trade History
Completed trades are logged to thetrades table for audit purposes:
insertTradeAsync()- Log completed tradequeryTradesAsync()- Query trade history with filterscountTradesAsync()- Get trade countcleanupOldTradesAsync()- Remove old records (90-day retention)
Security Features
Proximity Validation
Trades are cancelled if players move too far apart:Atomic Transfers
Item transfers use database transactions to prevent duplication:Interface Blocking
Players cannot open other interfaces during an active trade:UI Components
TradePanel
Main trade window with two screens:TradeRequestModal
Modal for incoming trade requests:Error Handling
Trade Errors
The server emitsduelError network events for trade failures:
- “Trade request timed out”
- “Player is too far away”
- “Player is already in a trade”
- “Insufficient inventory space”
- “Item no longer in inventory”
Testing
Integration Tests
Location:packages/server/tests/integration/trade/
Comprehensive tests covering:
- Trade request flow
- Item addition/removal
- Acceptance logic
- Screen transitions
- Proximity validation
- Atomic transfers
- Error handling
Related Documentation
- Inventory System
- Economy Overview
- Database Schema
- Duel Arena (similar two-screen flow)