Skip to main content

Overview

The Duel Arena is a server-authoritative PvP system where players challenge each other to combat with customizable rules and stakes. Inspired by Old School RuneScape’s Duel Arena, it provides a safe environment for player-vs-player combat with economic stakes. Key Features:
  • 6 dedicated duel arenas with automatic pooling
  • 10 combat rule toggles (no ranged, no food, etc.)
  • 11 equipment slot restrictions
  • Item staking with anti-scam protections
  • 3-2-1 countdown before combat
  • Forfeit mechanics with rule enforcement
  • Comprehensive audit logging

Duel Flow

1. Challenge Phase

Players must be in the Duel Arena zone to initiate challenges:
Requirements:
  • Both players must be within 15 tiles of each other
  • Neither player can be in an existing duel
  • Challenge expires after 30 seconds (50 ticks)
Challenge UI:
  • Challenger: Sees “Challenge sent” notification
  • Target: Receives DuelChallengeModal with Accept/Decline buttons
  • Chat: Red clickable message “PlayerA wishes to duel with you”

2. Rules Screen

Both players negotiate combat rules and equipment restrictions: Combat Rules (10 toggles):
  • No Ranged
  • No Melee
  • No Magic
  • No Special Attack
  • No Prayer
  • No Potions
  • No Food
  • No Movement (freeze at spawn points)
  • No Forfeit (fight to the death)
  • Fun Weapons (cosmetic items allowed)
Equipment Restrictions (11 slots):
  • Head, Cape, Amulet, Weapon, Body, Shield, Legs, Gloves, Boots, Ring, Ammo
Rule Validation:
  • noForfeit + noMovement is invalid (prevents softlocks)
  • Both players must accept before proceeding

3. Stakes Screen

Players stake items from their inventory: Staking Mechanics:
  • Left-click inventory item: Stake 1
  • Right-click: Context menu (Stake 1, 5, 10, All)
  • Click staked item: Remove from stakes
  • Maximum 28 staked items per player
Anti-Scam Features:
  • Acceptance resets when either player modifies stakes
  • Warning banner when opponent modifies stakes
  • Value imbalance warning (>50% difference, >10k gp)
  • Staked items are locked (cannot be dropped or traded)

4. Confirmation Screen

Final read-only review before combat: Displays:
  • Active rules summary
  • Disabled equipment summary
  • “If you win, you receive:” (opponent’s stakes)
  • “If you lose, they receive:” (your stakes)
  • Both players’ acceptance status
Warning Banner:
“This is your final chance to review before the duel begins!”
Both players must accept to proceed to countdown.

5. Countdown Phase

Arena Reservation:
  • System reserves one of 6 available arenas
  • Returns “No arena available” if all arenas occupied
Teleportation:
  • Players teleported to arena spawn points (north/south)
  • Facing each other automatically
  • Disabled equipment slots are unequipped
Countdown:
  • 3-2-1-FIGHT overlay with color-coded numbers
  • Players frozen during countdown (cannot move)
  • Countdown runs on server tick (1 second per tick)

6. Fighting Phase

Combat Begins:
  • Players can attack each other
  • Rules are enforced server-side
  • Arena walls prevent escape (collision-based)
  • DuelHUD displays opponent health and active rules
Rule Enforcement:
Forfeit Mechanics:
  • Click forfeit pillar in arena (if allowed)
  • Confirmation required (click twice)
  • Instant loss, opponent wins all stakes
Disconnect Handling:
  • 30-second reconnection timer (50 ticks)
  • Auto-forfeit if player doesn’t reconnect
  • Instant loss if noForfeit rule active

7. Resolution

Death:
  • Loser’s health reaches 0
  • 8-tick delay (4.8 seconds) for death animation
  • Winner receives all stakes
  • Both players restored to full health
  • Both teleported to duel arena lobby
Forfeit:
  • Forfeiting player loses immediately
  • Same stake transfer and teleportation
Result Modal:
  • Victory trophy (🏆) or defeat skull (💀)
  • Items won/lost with gold values
  • Total value summary

Server Architecture

DuelSystem

Location: packages/server/src/systems/DuelSystem/index.ts Main orchestrator for duel sessions (1,609 lines):

PendingDuelManager

Location: packages/server/src/systems/DuelSystem/PendingDuelManager.ts Manages challenge requests before duel sessions begin:
  • 30-second expiration timer
  • Distance validation (15 tiles max)
  • Disconnect cleanup
  • Prevents duplicate challenges

ArenaPoolManager

Location: packages/server/src/systems/DuelSystem/ArenaPoolManager.ts Manages 6 duel arenas with automatic pooling: Arena Layout:
  • 2×3 grid of rectangular arenas
  • Each arena: 20 tiles wide × 24 tiles long
  • 4-tile gap between arenas
  • Base coordinates: (70, 0, 90)
Arena Configuration:
Collision System:
  • Registers wall collision on initialization
  • Blocks perimeter ring OUTSIDE arena bounds
  • Players can walk to visual wall but not through it
  • Uses CollisionFlag.BLOCKED in collision matrix

DuelSessionManager

Location: packages/server/src/systems/DuelSystem/DuelSessionManager.ts CRUD operations for duel sessions:

DuelCombatResolver

Location: packages/server/src/systems/DuelSystem/DuelCombatResolver.ts Handles duel outcomes and stake transfers:
Resolution Process:
  1. Set session state to FINISHED
  2. Transfer loser’s stakes to winner
  3. Restore both players to full health
  4. Teleport to lobby (different spawn points for winner/loser)
  5. Emit duel:completed event
  6. Audit log for economic tracking
  7. Clean up session and release arena

Client UI Components

DuelPanel

Location: packages/client/src/game/panels/DuelPanel/ Main duel interface with screen switching: Screens:
  • RulesScreen.tsx - Rules and equipment negotiation
  • StakesScreen.tsx - Item staking with inventory
  • ConfirmScreen.tsx - Final read-only review
Modal Dimensions:
  • Rules screen: 450px width
  • Stakes screen: 650px width (3 panels)
  • Confirm screen: 520px width (2 columns)

DuelHUD

Location: packages/client/src/game/panels/DuelPanel/DuelHUD.tsx In-combat overlay showing:
  • Opponent health bar (large, prominent)
  • Active rule indicators with icons
  • Forfeit button (if allowed)
  • Disconnect status with countdown
Positioning: Fixed at top center, z-index 9000

DuelCountdown

Location: packages/client/src/game/panels/DuelPanel/DuelCountdown.tsx Full-screen countdown overlay:
  • Large centered number (200px font)
  • Color-coded: 3=red, 2=orange, 1=yellow, 0=green
  • Expanding ring pulse effect
  • “FIGHT!” display on 0
  • Auto-hides after fight starts

DuelResultModal

Location: packages/client/src/game/panels/DuelPanel/DuelResultModal.tsx Post-duel result display:
  • Victory trophy (🏆) or defeat skull (💀)
  • Animated entrance (icon pop, title slide)
  • Items won/lost with gold values
  • Total value summary
  • Forfeit indicator

Configuration

Timing Constants

Location: packages/server/src/systems/DuelSystem/config.ts All timing values in game ticks (600ms each):

Distance Constants

Arena Configuration

Spawn Locations


State Machine

The duel system uses an exhaustive state machine:
State Transitions:
Exhaustive Switch:

Network Events

Server → Client

Client → Server


Security Features

Server-Authoritative

All duel logic runs on the server:
  • Client cannot modify rules, stakes, or outcomes
  • Arena bounds enforced via collision matrix
  • Stake transfers use database transactions
  • Rate limiting on all duel operations

Anti-Scam Protections

  1. Acceptance Reset: Any modification resets both players’ acceptance
  2. Opponent Modified Banner: Warning when opponent changes stakes
  3. Value Imbalance Warning: Alert when risking >50% more than opponent
  4. Duplicate Slot Prevention: Cannot stake same inventory slot twice
  5. Read-Only Confirmation: Final screen is non-editable

Audit Logging

Location: packages/server/src/systems/ServerNetwork/services/AuditLogger.ts All duel events are logged:

Testing

Unit Tests

Location: packages/server/src/systems/DuelSystem/__tests__/ Comprehensive test coverage:
  • DuelSystem.test.ts (1,066 lines) - Full state machine testing
  • ArenaPoolManager.test.ts (233 lines) - Arena pooling
  • PendingDuelManager.test.ts (456 lines) - Challenge management
Test Utilities:
Example Test:

Integration with Other Systems

Combat System

Location: packages/shared/src/systems/shared/combat/CombatSystem.ts The combat system checks duel rules before allowing actions:

Death System

Location: packages/shared/src/systems/shared/death/PlayerDeathSystem.ts Death handling differs for duel deaths:

Inventory System

Staked items are locked during duels:

Manifest Configuration

Duel Arena Config

Location: manifests/duel-arenas.json Centralized configuration for arena layout and zones:
Arena Layout:
  • 6 arenas arranged in a 2×3 grid
  • Each arena is 16×16 tiles
  • Spawn points positioned 4 tiles from center (north/south)
  • 4 trapdoor positions per arena (forfeit pillars)
  • Lobby area: 60×30 tiles at (250, 295)
  • Hospital area: 20×15 tiles at (210, 295)

Rule Definitions

Equipment Slot Labels


Error Codes


Performance Considerations

Arena Pooling

  • 6 arenas support up to 6 concurrent duels
  • Arena reservation is O(n) where n=6 (negligible)
  • Arena release is O(1) by arena ID or O(n) by duel ID

Session Management

  • Player-to-session mapping uses Map<string, string> for O(1) lookups
  • Session cleanup runs every 17 ticks (~10 seconds)
  • Expired sessions (>30 minutes in setup) are auto-cancelled

Collision Matrix

  • Arena walls registered once on initialization
  • Uses existing collision system (no performance overhead)
  • Wall collision is bitmask-based (very fast)

OSRS Accuracy

The duel system faithfully recreates OSRS mechanics:

Future Enhancements

Potential additions (not yet implemented):
  • Ranked Duels: ELO rating system
  • Tournament Mode: Bracket-style competitions
  • Spectator Mode: Watch ongoing duels
  • Duel History: Track wins/losses
  • Leaderboards: Top duelists
  • Custom Arenas: Player-created arena layouts
  • Obstacles: Arena hazards (OSRS had this)

Combat System

Core combat mechanics used in duels

Death System

How death is handled differently in duels

Inventory System

Item staking and inventory locking

Collision System

Arena wall collision implementation