Latest Releases
This changelog syncs automatically with GitHub Releases.
Subscribe via RSS for updates.
🏔️ Terrain Height Parameter Refactoring (commit e54476f)
Extracted all terrain generation constants to single source of truth for consistency between main thread and web workers:- TerrainHeightParams.ts: New centralized parameter file
- All noise layer definitions (continent, ridge, hill, erosion, detail)
- Island configuration (radius, falloff, base elevation)
- Pond configuration (radius, depth, center position)
- Coastline noise parameters for irregular shorelines
- Mountain boost configuration
- Worker Code Generation:
buildGetBaseHeightAtJS()function- Injects TypeScript constants into inline worker string
- Ensures worker heights match main thread exactly
- Prevents parameter drift between threads
- Synchronized Parameters: TerrainSystem imports constants directly
- Worker receives same values via code injection
- Eliminates manual synchronization errors
- Single source of truth for all terrain generation
⚡ Terrain Worker Performance Optimization (commit ef54a70)
Moved height and normal computation to web workers for 63x reduction in main-thread noise evaluations:- Worker Height Computation: Full height calculation in worker threads
- Synced noise weights to match TerrainSystem (hillScale 0.012→0.02, persistence 0.5→0.6)
- Added shoreline adjustment to worker (calculateBaseSlopeAt, adjustHeightForShoreline)
- Worker heights now fully correct for tiles without flat zones
- Worker Normal Computation: Normals computed in worker via overflow grid
- (resolution+2)² overflow grid with centered finite differences
- Eliminates ~16,384 noise calls per tile on main thread
- 63x reduction in noise evaluations for normal calculation
- Conditional Fallback: Main-thread recomputation only for tiles with flat zones
- Tiles without flat zones use worker data directly (zero noise calls)
- Tiles overlapping buildings/stations recompute on main thread
- Ensures flat zone heights are respected in final geometry
- Performance Impact: Terrain generation now runs in parallel across CPU cores
- Main thread free for rendering and input handling
- Smooth frame rates during terrain streaming
🎮 Duel Arena Gameplay Fixes (commit 5e5c7c9)
Resolved 6 critical bugs affecting agent duel gameplay:- 2H Sword Attack Timing: DuelCombatAI now attacks every weapon-speed cycle
- Previously waited for re-engagement interval between attacks
- Seeds first tick to attack immediately (startCombat() has no auto-attack)
- Fixes slow 2H weapon attacks in duels
- Teleport During Fight: Health restore now has quiet parameter
- Skips PLAYER_RESPAWNED/PLAYER_SET_DEAD events during fight-start HP sync
- Added suppressEffect flag to teleportPlayer/teleportToArena
- Prevents visual teleport effects during proximity corrections
- Stale Avatars: endCycle() chains cleanup→delay→new cycle via .finally()
- INTER_CYCLE_DELAY_MS ensures proper cleanup before next cycle
- Cleanup always teleports both agents to lobby
- Prevents avatars stuck in arena between cycles
- FIGHT Text Display: CountdownOverlay stays mounted 2.5s into FIGHTING phase
- Fade-out animation (opacity + scale)
- Ensures “FIGHT” text visible at combat start
- Arena Fences: Replaced solid walls with fence posts + rails
- Better visibility into arena
- Maintains collision boundaries
- Health Bars: Inline HP sync in handleEntityDamaged
- updateContestantHp called before every broadcast
- Eliminates health bar desync during combat
🎨 Duel Arena Visual Enhancements
Lit Torches with Fire Particles (commit cef09c5)
- Torch Placement: Torches at all 4 corners of each arena
- PointLights with flicker animation
- “torch” glow preset (6 riseSpread particles per torch)
- Tight 0.08 spread for concentrated flame effect
- Preset-Aware Respawn: Particle spread varies by preset type
- Torch particles use tight spread
- Other presets use default spread values
Procedural Stone Tile Texture (commit f8c585e)
- Arena Floor Texture: Canvas-generated sandstone tile pattern
- Replaces flat-colored arena floors
- OSRS medieval aesthetic
- Each arena gets unique randomized texture
- Grout lines, color variation, and speckle noise
- Procedurally generated per arena for variety
🔧 Build System Improvements
Circular Dependency Resilience (commit 5666ece)
- Build Resilience: procgen and plugin-hyperscape builds now handle circular deps
- Both packages have circular dependencies with @hyperscape/shared
- Used ‘tsc || echo’ pattern so build exits 0 even with circular dep errors
- Packages still produce partial output sufficient for downstream consumers
- Prevents clean build failures when dist/ doesn’t exist yet
CI Test Reliability
- Benchmark Thresholds (commit 98ebfa7): Relaxed NPCTickProcessor thresholds for CI variance
- Geometry Validation (commit 04248b7): Increased timeout to 120s for CI
- Intensive Generation Tests (commit 832286c): Increased timeouts for procgen tests
- Vitest Timeout (commit 7e4d2cc): Global timeout increase to fix CI flakiness
- RoadNetworkSystem Performance (commit 474d29d): Fixed flaky performance test
- Manifest Validation (commit aa1ea2d, 1f4551c, 6f1d3a7): Fixed invalid manifest JSONs causing DataManager errors
- Corrected .gitignore manifest negation
- Fixed JSON syntax errors in manifest files
- Ensures CI tests have valid data
🚀 Deployment & Infrastructure
Vast.ai GPU Server Deployment
- Python 3.11+ Support (commit 621ae67): Upgraded to bookworm-slim for vastai-sdk compatibility
- PEP 668 Compliance (commit d9e9111): Added —break-system-packages for pip3 on Debian 12
- CLI Package (commit 781f3d7): Installed vastai CLI package and reverted to binary invocation
- SSH Key Generation (commit 3ce7d64): Ensured vastai CLI on PATH and generated SSH keys in Docker
- US-Based Filtering (commit 48bd8e7): Filter for US-based machines for lower latency
- CUDA Docker Image (commit c6540358): Switched from KVM to standard CUDA image
- Runtime Environment (commit dc9b475): Inject all runtime env vars into provisioned game server
CI/CD Enhancements
- Foundry Installation (commit 8083993): Install Foundry for MUD contracts tests
- Playwright Dependencies (commit 59204671): Added playwright deps and ffmpeg for streaming
- DNS Configuration (commit fd172485): Overwrite resolv.conf with Google DNS instead of appending
- Build Chain (commit cbcbb2f, f0d2fae): Added all internal packages to build chain including decimation
- Asset Checks (commit 87b3c1a): Added git-lfs for asset checks
- Native Modules (commit bd2aad9): Added build-essential for native module compilation on minimal Docker images
- Declaration Generation (commit eb722bb): Always use —skipLibCheck for circular dependency handling
- CI Environment (commit 28bf9fb): Set CI=true to skip asset download in production
- CORS Configuration (commit 7256b07, fb3b9cd): Allow all origins and add CORS max-age for betting app
⚔️ Melee Weapon System Overhaul (PR #894)
Major weapon categorization update with shortsword rename, 2H weapon support, and batch export workflow:- Shortsword Rename: Generic sword items renamed to shortsword across entire codebase
- Item IDs:
bronze_sword→bronze_shortsword,iron_sword→iron_shortsword, etc. - Display names updated: “Bronze Sword” → “Bronze Shortsword”
- All tests updated (72 files changed with 1888 additions, 522 deletions)
- Affects: inventory, bank, duel, quest, smithing, and combat systems
- Item IDs:
- 2H Sword Combat Emotes: New two-handed weapon animations
2h_idlestance emote for idle state2h_slashattack emote for combat- Full client pipeline integration with emote system
- Weapon-aware idle emote reset (2H weapons return to
2h_idleinstead ofidle)
- Weapon Type Expansion: New weapon categories in
WeaponTypeenumSHORTSWORD: One-handed short blades (bronze, iron, steel, mithril, adamant, rune)LONGSWORD: One-handed long blades (future content)SCIMITAR: Curved one-handed blades (future content)2H_SWORD: Two-handed great swords (future content)
- Combat Animation Mapping: All melee weapon types now map to
sword_swinganimation- Unified animation system in
CombatAnimationManager - Consistent attack visuals across weapon types
- Replaces weapon-specific animation routing
- Unified animation system in
- Weapon Style Configuration: Updated size and proportion constants
MIN_WEAPON_SIZES: Shortsword 0.35m, Longsword 0.5m, Scimitar 0.45m, 2H Sword 0.8mMAX_WEAPON_SIZES: Shortsword 2.0m, Longsword 3.0m, Scimitar 2.5m, 2H Sword 4.5mBASE_WEAPON_PROPORTIONS: Shortsword 45% of height, Longsword 65%, Scimitar 55%, 2H Sword 90%
- Batch Apply Fitting: Apply fitting configuration to all weapons of same subtype
- New
/api/assets/batch-apply-fittingendpoint - Applies
hyperscapeAttachmentmetadata to multiple assets in one operation - Updates position, rotation, scale, grip point, and avatar height for all selected weapons
- Progress overlay with current asset name and completion percentage
- New
- Batch Review & Export: Step through weapons and export aligned GLBs
- New batch review mode with navigation bar (prev/next/skip)
- Visual mesh swapping without reloading transforms (preserves fitting)
- Export current weapon or export all remaining weapons
- Progress tracking with green checkmarks for exported weapons
- Auto-advance to next un-exported weapon after export
- Weapon Subtype Grouping: Equipment list now groups weapons by subtype
- Collapsible sections: Shortswords, Longswords, Scimitars, 2H Swords, etc.
- Chevron icons for expand/collapse
- Counts displayed per group
- Geometry Normalization: Advanced transform flattening for batch consistency
flattenSceneTransforms(): Bakes all intermediate node transforms into mesh geometry- Eliminates GLTF hierarchy differences between weapons
- Axis alignment: Rotates geometry so longest axis matches reference weapon
- Flip detection: Uses vertex centroid bias to detect 180° flips
- Position alignment: Shifts bbox center to match reference weapon
- Ensures consistent grip offset across all weapons in batch
- Swap Equipment Content: New
swapEquipmentContent()method onEquipmentViewerRef- Replaces visual mesh without touching transforms
- Preserves normalization state (grip offset, bbox center, longest axis)
- Enables batch review without full reload
- Maintains scale, rotation, and position from original weapon
- Save Aligned GLB: New
/api/assets/:id/save-alignedendpoint- Saves aligned (fitted) GLB model to server
- Updates metadata.json with
alignedModelPathandalignedAttimestamp - Returns relative path for download
- UI Components:
BatchProgressOverlay: Simple spinner for batch apply fittingBatchReviewBar: Navigation bar with prev/next/export controls- Progress dots showing current weapon and export status
- Export All button with remaining count
- Done button to exit review mode
🎯 OSRS-Accurate Combat Pathfinding (PR #886)
BFS primary pathfinding with multi-destination search and line-of-sight validation:- BFS as Primary Pathfinder: Player movement now uses BFS instead of naive diagonal-first
- Eliminates visible diagonal zigzag when walking to attack targets
- Matches OSRS “smartpathing” behavior
- Optimal paths around obstacles
findPath()now callsfindBFSPath()directly (no naive fallback)
- Multi-Destination BFS: New
findPathToAny()method for combat- Generates ALL valid attack tiles (melee cardinal, ranged/magic with LoS)
- Terminates at first valid tile reached
- Naturally finds shortest path to closest reachable position
- No need to pre-pick “best” tile with heuristics
- Line of Sight: Ranged and magic attacks now require LoS
- Bresenham line trace against
BLOCKS_RANGEDcollision flags - Prevents attacking through walls when in Chebyshev range
hasLineOfSight()function inTileSystem.ts- Used by both pathfinding and attack validation
- Bresenham line trace against
- Valid Tile Generation: New helper functions
getValidRangedTiles(): All tiles in range with LoS to targetgetValidMeleeTiles(): Cardinal tiles for range 1, Chebyshev for range 2+- Pre-filters tiles before pathfinding for efficiency
- NPC Chase Pathfinding: NPCs still use naive diagonal pathing
findNaivePath()exposed publicly forChasePathfinding.ts- Enables safespotting mechanics (OSRS-accurate)
- Players use BFS, mobs use naive (intentional difference)
- Pending Attack Manager: Updated range checks with LoS validation
- Ranged/magic attacks verify LoS before executing
- Melee attacks use cardinal-only for range 1
- Prevents attacks through walls
- Collision Masks: New
BLOCKS_RANGEDmask- Combines
BLOCK_LOSandBLOCKEDflags - Used for LoS checks in combat and pathfinding
- Combines
- Testing: All 608 combat tests pass (1 pre-existing unrelated failure)
- Ranged and magic attacks now require line of sight (cannot attack through walls)
- Player movement paths may differ from previous naive diagonal-first behavior
🗺️ Quest Minimap Icons (PR #885)
Quest-state-aware icons on minimap for quest giver NPCs:- Quest Icon System: Blue icons for quest NPCs
- Blue ”!” for available quests (not started)
- Blue ”?” for in-progress quests
- Icons disappear when all quests completed
- NPC Quest Configuration: New
questIdsfield inNPCEntityConfig- Array of quest IDs the NPC offers
- Passed through spawn → network → client pipeline
- Minimap fetches quest statuses from server
- Quest Status Sync: Client requests quest statuses for minimap
- New
getQuestStatusesnetwork message - Server responds with status map (not_started, in_progress, completed)
- Minimap updates icons based on quest state
- New
- Service Detection Fix: Fixed broken NPC service detection in Minimap
servicesisstring[], not{ types: string[] }- Also fixes bank/shop icon detection
- Proper type casting for service array
- Icon Rendering: Quest icons drawn with filled circle and symbol
- ”!” symbol for available quests
- ”?” symbol for in-progress quests
- Cyan color (#00ffff) for visibility
🔧 Instant Item & Coin Pickup (PR #888)
Visually instant pickup by deferring database persistence:- Item Pickup: Ground entity removed before database write
addItem()now acceptssilentflag to skip DB persist and emitpickupItem()adds to memory, removes ground entity, emits update, THEN awaits DB persist- Player sees instant feedback while persistence is still guaranteed
- No item loss or duplication risk (pickup lock held throughout)
- Coin Pickup: Same responsive pattern for coins
addCoins()now acceptsskipPersistflag- Coin ground entity removed before database write
persistCoinsImmediate()made public for manual persistence- Coin persist happens after removal, still guaranteed before lock release
- Performance Impact: Eliminates 500ms+ database round-trip blocking visual feedback
- Security: DB persist still awaited (not fire-and-forget), pickup lock prevents races
- Testing: All 58 inventory tests pass (pickup, moveItem, transactionLock, coinPersistence, coinPouch)
🎒 Stackable Equipment Merge (PR #887)
Direct quantity merge when equipping same stackable item already equipped:- Optimization: Arrows (or other stackables) that match equipped slot merge directly
- Eliminates unequip → inventory → re-equip cycle
- Prevents brief inventory flash showing combined count before equip
- Adds quantity directly to equipped stack
- Same-Type Detection:
isSameStackableMergeflag checks:- Item is stackable
- Slot already has an item
- Item IDs match (String comparison for safety)
- Conditional Unequip: Only unequip if NOT a same-type stackable merge
- Different item or empty slot → unequip current item first
- Same stackable → merge quantity directly
- Code Path: Simplified equipment flow with early merge detection
- Reduces network packets and database writes
- Improves responsiveness for arrow equipping
🐛 Multiplayer Sync Fixes (PR #884)
Mob position desync and stuck combat rotation fixes:- Combat Rotation Clearing: Clear
inCombatRotationflag on movement start inTileInterpolator- Was never cleared, causing mobs to keep combat-facing after combat ended
- Mobs now return to normal AI-driven rotation when moving
- Mob Rotation Routing: Only route
entityModifiedtosetCombatRotationfor players, not mobs- Mobs handle combat rotation locally in
MobEntity.clientUpdate() - Prevents server rotation packets from conflicting with client-side mob AI
- Mobs handle combat rotation locally in
- Damage Splat Positioning: Prefer entity visual position over server position
- Damage splats appear where mob visually is (interpolated position)
- Fixes splats appearing at server position during client interpolation
- Movement Packet Optimization: Replace redundant
entityModifiedwithemoteontileMovementEnd- Reduces packet duplication
- Cleaner network protocol
- Movement Cancellation Fix:
cancelMovementsendstileMovementEndinstead ofentityModified- Client
TileInterpolatorproperly stops interpolating old path - Fixes visual desync when movement is cancelled
- Client
⚔️ Mob Magic & Ranged Attacks (PR #826)
Mobs can now use magic and ranged attacks with full projectile support and visual weapon attachments:- Attack Type Configuration: NPC manifest fields for combat type
attackType:\"melee\"(default),\"ranged\", or\"magic\"spellId: Required for magic mobs (e.g.,\"wind_strike\",\"fire_bolt\")arrowId: Required for ranged mobs (e.g.,\"bronze_arrow\",\"iron_arrow\")heldWeaponModel: Optional weapon GLB (bow, staff) attached to hand bonecombatRange: Attack range in tiles (1 for melee, 7-10 for ranged/magic)
- Combat Handler Routing: Mobs route through same handlers as players
MagicAttackHandler- Mob magic path with infinite runes, no equipment bonusesRangedAttackHandler- Mob ranged path with infinite arrows, no equipment bonuses- Separate damage calculation using mob’s
magicorrangedstat from manifest - Players can still use prayer bonuses to defend against mob attacks
- Projectile System: Spell and arrow projectiles for mobs
- OSRS-accurate hit delays based on distance
- Element-based spell visuals (air, water, earth, fire)
- Arrow projectiles with metal-colored tips
- Launch delays: 600ms for spells, 400ms for arrows
- Shared
emitMagicProjectile()andemitRangedProjectile()methods
- Visual Weapon Attachment: Held weapons on mob VRM avatars
- Static weapon cache shares GLB scenes across mob instances
clone(true)for per-mob instances with shared geometry/materials- Proper cleanup on world teardown via
clearWeaponCache() - Supports Asset Forge V1 and V2 attachment metadata
- Deduplicates concurrent fetches via
_pendingLoadsmap - Prevents weapon attachment to destroyed mobs via
_destroyedflag
- Animation System: Attack-type-specific emotes
- Magic mobs:
SPELL_CASTanimation - Ranged mobs:
RANGEanimation - Melee mobs with weapons:
SWORD_SWINGanimation - Emote matching switched from substring to exact equality (more robust)
- Magic mobs:
- Auto-Attack Integration: Tick-based follow-up attacks
weaponTypestored in combat state for auto-attack routingCombatTickProcessorroutes by stored weapon type- Retaliation uses correct attack type (magic/ranged mobs retaliate with projectiles)
- Mob retaliators resolve weapon type from config for proper handler routing
- Shared Utilities:
prepareMobAttack()inAttackContext.ts- Consolidates entity resolution, range checks, cooldown, animation
- Eliminates ~150 lines of duplication between magic and ranged handlers
- Returns validated
MobAttackContextor null - Accepts
preResolvedparameter to avoid redundant entity lookups
- Constants: New combat constants in
CombatConstants.tsMAGIC_RANGE: 10- Magic attack range in tilesSPELL_LAUNCH_DELAY_MS: 600- Spell cast animation wind-upARROW_LAUNCH_DELAY_MS: 400- Arrow draw animation wind-up
- Testing: 996 lines of integration tests
- Magic/ranged attack routing and projectile emission
- Missing spellId/arrowId graceful handling with console warnings
- Out-of-range rejection
- Event-carried vs NPC-data spell/arrow resolution
- Auto-attack tick paths for both magic and ranged
- Default-to-melee fallback when attackType missing
🔄 Multiplayer Sync Fixes (PR #875, #882, #884)
Critical fixes for multiplayer synchronization affecting remote player rendering and combat:- Position Sync Fix (PR #875, #882): Remote players now face correct direction
- Sync
base.quaternioninPlayerRemoteto fix sideways-facing players - Force transform update after async avatar load to prevent T-pose ghosts
- Position and animate remote avatar before making visible (prevents 1-frame flash at origin)
- Update spatial index after teleport/respawn so
sendToNearbyreaches players at new location - Broadcast authoritative position to all players on join
- Fixes invisible combat movement in duels (players couldn’t see opponent moving)
- Sync
- Equipment Visibility on Load (PR #875): Equipment now appears when VRM loads
- Added
getAvatar()helper to resolve VRM from bothPlayerLocalandPlayerRemote - Subscribe to
AVATAR_LOAD_COMPLETEto replay cached equipment when VRM loads - Re-send equipment on socket reconnect (packets lost during disconnect)
- Send existing players’ equipment to joiner and broadcast joiner’s equipment
- Use all 11
EQUIPMENT_SLOT_NAMESinstead of hardcoded 6 slots - Gate equipment
UI_UPDATEto local player only (prevents duplicate UI updates)
- Added
- PvP Ranged XP Fix (PR #875): Ranged attacks now grant correct XP in duels
- Detect weapon type (bow/crossbow/staff) in
PlayerDeathSystemfor PvP kills - Previously used player’s melee attack style, incorrectly granting Strength XP for ranged kills
- Now matches
MobEntitylogic: inspect actual weapon type for XP calculation - Fixes ranged attacks incorrectly granting Strength XP in duels
- Detect weapon type (bow/crossbow/staff) in
- Mob Position Desync Fix (PR #884): Mobs no longer get stuck facing combat direction
- Clear
inCombatRotationflag on movement start inTileInterpolator - Only route
entityModifiedtosetCombatRotationfor players, not mobs - Mobs handle combat rotation locally in
MobEntity.clientUpdate() - Prefer entity visual position over server position in
DamageSplatSystem - Replace redundant
entityModifiedwithemoteontileMovementEndpacket - Fix
cancelMovementto sendtileMovementEndinstead ofentityModified
- Clear
🛡️ Duel Health Restore Fix (PR #875)
Fixed critical bug where duel losers could get stuck in arena with frozen physics:- Split Health Restores: Individual try/catch blocks for winner and loser
- Previously both
restorePlayerHealthcalls were in single try/catch - If winner’s restore triggered exception in any
PLAYER_RESPAWNEDhandler, loser’s restore was skipped - Left loser with frozen physics,
isDying=true, and noplayerRespawned/playerSetDeadpackets playerTeleportstill arrived (separate try/catch) but client couldn’t act on it
- Previously both
- Error Isolation: Each restore individually wrapped, matching teleport pattern
- Winner restore failure no longer affects loser
- Both players guaranteed to receive health restoration
- Prevents players stuck in arena unable to move or respawn
🤝 Player-to-Player Trading (PR #850)
Complete trading system with OSRS-style two-screen confirmation flow:- Trade Panel Integration: TradePanel wired into modal system
- Trade event handlers in
useModalPanelshook (trade, tradeUpdate, tradeConfirm, tradeClose) - Rendered in
InterfaceModalsfollowing DuelPanel pattern - Server emits
UI_UPDATEevents with trade data - TradePanel component already built, now fully connected
- Trade event handlers in
- Trade Flow: Request → Offer Screen → Confirm Screen → Complete
- Screen 1: Add/remove items, accept offers
- Screen 2: Final confirmation with value totals
- Both players must accept on each screen
- Acceptance resets when either player modifies offer
- UI Components:
TradePanel.tsx- Main trade window with two screensTradeRequestModal.tsx- Incoming trade request modalcomponents/TradeSlot.tsx- Individual trade offer slotscomponents/InventoryMiniPanel.tsx- Inventory grid for item selectionmodals/QuantityPrompt.tsx- Offer-X quantity inputhooks/useRemovedItemTracking.ts- Anti-scam tracking for removed items
- Anti-Scam Features:
- Wealth transfer indicators (value difference warnings)
- Removed item tracking (highlights items opponent removed)
- Screen 2 shows final offer values
- Both players must re-accept after any modification
- Network Events:
trade,tradeUpdate,tradeConfirm,tradeClose- Server-authoritative trade state management
- Real-time offer synchronization
- Proximity validation (2-tile range)
- Interface blocking during trade
🎨 Visual Polish
- Mob Sword Swing Animation (PR #848): Mobs with held weapons use sword_swing emote
- Guards with bronze swords now play proper sword swing animation
- Replaces default punch animation for melee mobs with weapons
- Checks for
heldWeaponModelin mob config
- Duel Stake Icons (PR #846): Item icons in duel stake panels
- Replaced truncated text names with actual ItemIcon rendering
- Staked items in inventory appear dimmed (40% opacity)
- Visual indicator for offered items
- Arrow Projectile Spawn (PR #845): Fixed arrow spawn position
- Arrows now spawn at bow position instead of player center
- Offset 1.2 units forward toward target
- Height adjusted to upper torso level (1.4m)
- Prevents arrows appearing to come from inside player’s head
- Equipment Panel Icons (PR #825): Shows actual item icons instead of SVG placeholders
- Uses
ItemIconcomponent with proper asset URLs - Consistent with inventory panel styling
- Uses
🗺️ Minimap Visual Upgrade (PR #830)
RS3/OSRS-accurate minimap with location icons and improved color scheme:- Dot Color Update: Swapped to RS3 standard colors
- White: Players (was green/blue)
- Yellow: NPCs and mobs (was red)
- Red: Ground items (was yellow)
- White square: Local player (was green circle)
- Destination Marker: Red flag icon (RS3-style)
- Thin pole with filled triangle flag
- Replaces previous red dot marker
- More visible and distinctive
- Location Icons: Custom icons for key locations
- Bank: Gold coin ($) symbol
- Shop: Open bag icon
- Prayer Altar: White cross
- Runecrafting Altar: Purple circle with “R”
- Anvil: Dark anvil silhouette
- Furnace: Orange circle with flame
- Cooking Range: Brown circle with steam
- Fishing Spot: Cyan circle with fish
- Mining Rock: Brown circle with pickaxe
- Tree: Green circle
- Quest NPC: Cyan circle with ”?”
- Icon Detection: Automatic subtype detection
- Station types (bank, furnace, anvil, range, altar)
- NPC service types (bank, shop, quest)
- Resource types (fishing_spot, mining_rock, tree)
- Size Hierarchy: Compact dots (6px) for entities, larger icons (12px) for locations
- Implementation:
drawMinimapIcon()function with switch-based rendering- Returns true if icon drawn, false for default dot fallback
- Clean filled glyphs with 1px dark outline
- ~8px icon size for readability
🗄️ Database Performance Fix (PR #823)
Inventory write coalescing to prevent connection pool starvation:- Write Coalescing: Collapses concurrent inventory writes
- At most 2 DB transactions per player (one active + one queued)
- Latest snapshot wins (intermediate states discarded)
- All waiting callers resolve when batch completes
- Performance Impact:
- Before: 100 fletching completions = 200 sequential transactions
- After: 100 fletching completions = 2 transactions per player
- Eliminates “200 pending operations” warnings
- Prevents game freezes during batch crafting
- Graceful Shutdown: Orphaned waiters rejected on
DatabaseSystem.destroy() - Code Changes:
- Replaced
inventoryWriteLockswithinventoryWriteActive+inventoryWriteQueued - Removed redundant
getPlayerAsyncchecks (repository handles missing players) - Recursive drain of queued batches after active write completes
- Replaced
🎥 Client Rendering Fixes (PR #829)
Camera and post-processing improvements:- Camera Initialization Fix: Camera now faces behind player on fresh load
- Changed
thetafrom0toMath.PIin spherical coordinates - Fixes backwards movement controls on first spawn
- Applied to both initial state and reset camera method
- Changed
- Post-Processing Color Grading Fix: LUT intensity zeroed when disabled
- Prevents color grading from leaking into outline-only rendering
- Entity highlights now work correctly with post-processing disabled
- Rendering routes through composer even when post-processing is off
intensityUniform.value = 0when LUT is “none”
🌐 Website Updates (PR #822)
- Gold Token Page: Refactored into section components
- TokenHero, ValueProps, HowItWorks, OpenSource, GoldCTA
- Error boundaries and loading states
- SEO improvements with opengraph images
- Accessibility enhancements (semantic HTML, ARIA labels)
- Solana Wallet Integration: Updated dependencies
@privy-io/react-authto 3.13.1@privy-io/server-authto 1.32.5@solana-mobile/wallet-standard-mobilefor MWA support- Multi-chain wallet detection (Saga and Seeker devices)
- Code Review: Updated to Claude Opus 4.6
⚔️ Mob Magic & Ranged Attacks (PR #826)
Mobs can now use magic and ranged attacks, not just melee:- Attack Type Configuration: NPC manifest fields for combat type
attackType:"melee"(default),"ranged", or"magic"spellId: Required for magic mobs (e.g.,"wind_strike","fire_bolt")arrowId: Required for ranged mobs (e.g.,"bronze_arrow","iron_arrow")heldWeaponModel: Optional weapon GLB (bow, staff) attached to hand bone
- Combat Handler Routing: Mobs route through same handlers as players
MagicAttackHandler- Mob magic path with infinite runesRangedAttackHandler- Mob ranged path with infinite arrows- Separate damage calculation for mobs (no equipment bonuses)
- Uses mob’s
magicorrangedstat from NPC manifest
- Projectile System: Spell and arrow projectiles for mobs
- OSRS-accurate hit delays based on distance
- Element-based spell visuals (air, water, earth, fire)
- Arrow projectiles with metal-colored tips
- Launch delays: 600ms for spells, 400ms for arrows
- Visual Weapon Attachment: Held weapons on mob VRM avatars
- Static weapon cache shares GLB scenes across mob instances
clone(true)for per-mob instances with shared geometry- Proper cleanup on world teardown via
clearWeaponCache() - Supports Asset Forge V1 and V2 attachment metadata
- Animation System: Attack-type-specific emotes
- Magic mobs:
SPELL_CASTanimation - Ranged mobs:
RANGEanimation - Melee mobs:
COMBATorSWORD_SWINGanimation - Emote matching switched from substring to exact equality
- Magic mobs:
- Auto-Attack Integration: Tick-based follow-up attacks
weaponTypestored in combat state for auto-attack routingCombatTickProcessorroutes by stored weapon type- Retaliation uses correct attack type (magic/ranged mobs retaliate with projectiles)
- Shared Utilities:
prepareMobAttack()inAttackContext.ts- Consolidates entity resolution, range checks, cooldown, animation
- Eliminates duplication between magic and ranged handlers
- Returns validated
MobAttackContextor null
- Constants: New combat constants in
CombatConstants.tsMAGIC_RANGE: 10- Magic attack range in tilesSPELL_LAUNCH_DELAY_MS: 600- Spell cast animation wind-upARROW_LAUNCH_DELAY_MS: 400- Arrow draw animation wind-up
- Testing: 996 lines of integration tests
- Magic/ranged attack routing and projectile emission
- Missing spellId/arrowId graceful handling
- Out-of-range rejection
- Event-carried vs NPC-data spell/arrow resolution
- Auto-attack tick paths for both magic and ranged
🗺️ Minimap Visual Upgrade (PR #830)
RS3/OSRS-accurate minimap with location icons and improved color scheme:- Dot Color Update: Swapped to RS3 standard colors
- White: Players (was green/blue)
- Yellow: NPCs and mobs (was red)
- Red: Ground items (was yellow)
- White square: Local player (was green circle)
- Destination Marker: Red flag icon (RS3-style)
- Thin pole with filled triangle flag
- Replaces previous red dot marker
- More visible and distinctive
- Location Icons: Custom icons for key locations
- Bank: Gold coin ($) symbol
- Shop: Open bag icon
- Prayer Altar: White cross
- Runecrafting Altar: Purple circle with “R”
- Anvil: Dark anvil silhouette
- Furnace: Orange circle with flame
- Cooking Range: Brown circle with steam
- Fishing Spot: Cyan circle with fish
- Mining Rock: Brown circle with pickaxe
- Tree: Green circle
- Quest NPC: Cyan circle with ”?”
- Icon Detection: Automatic subtype detection
- Station types (bank, furnace, anvil, range, altar)
- NPC service types (bank, shop, quest)
- Resource types (fishing_spot, mining_rock, tree)
- Size Hierarchy: Compact dots (6px) for entities, larger icons (12px) for locations
- Implementation:
drawMinimapIcon()function with switch-based rendering- Returns true if icon drawn, false for default dot fallback
- Clean filled glyphs with 1px dark outline
- ~8px icon size for readability
🎨 Visual Polish
- Mob Sword Swing Animation (PR #848): Mobs with held weapons use sword_swing emote
- Guards with bronze swords now play proper sword swing animation
- Replaces default punch animation for melee mobs with weapons
- Checks for
heldWeaponModelin mob config
- Duel Stake Icons (PR #846): Item icons in duel stake panels
- Replaced truncated text names with actual ItemIcon rendering
- Staked items in inventory appear dimmed (40% opacity)
- Visual indicator for offered items
- Arrow Projectile Spawn (PR #845): Fixed arrow spawn position
- Arrows now spawn at bow position instead of player center
- Offset 1.2 units forward toward target
- Height adjusted to upper torso level (1.4m)
- Prevents arrows appearing to come from inside player’s head
- Equipment Panel Icons (PR #825): Shows actual item icons instead of SVG placeholders
- Uses
ItemIconcomponent with proper asset URLs - Consistent with inventory panel styling
- Uses
🗄️ Database Performance Fix (PR #823)
Inventory write coalescing to prevent connection pool starvation:- Write Coalescing: Collapses concurrent inventory writes
- At most 2 DB transactions per player (one active + one queued)
- Latest snapshot wins (intermediate states discarded)
- All waiting callers resolve when batch completes
- Performance Impact:
- Before: 100 fletching completions = 200 sequential transactions
- After: 100 fletching completions = 2 transactions per player
- Eliminates “200 pending operations” warnings
- Prevents game freezes during batch crafting
- Graceful Shutdown: Orphaned waiters rejected on
DatabaseSystem.destroy() - Code Changes:
- Replaced
inventoryWriteLockswithinventoryWriteActive+inventoryWriteQueued - Removed redundant
getPlayerAsyncchecks (repository handles missing players) - Recursive drain of queued batches after active write completes
- Replaced
🎥 Client Rendering Fixes (PR #829)
Camera and post-processing improvements:- Camera Initialization Fix: Camera now faces behind player on fresh load
- Changed
thetafrom0toMath.PIin spherical coordinates - Fixes backwards movement controls on first spawn
- Applied to both initial state and reset camera method
- Changed
- Post-Processing Color Grading Fix: LUT intensity zeroed when disabled
- Prevents color grading from leaking into outline-only rendering
- Entity highlights now work correctly with post-processing disabled
- Rendering routes through composer even when post-processing is off
intensityUniform.value = 0when LUT is “none”
🎨 UI Improvements
- Equipment Panel Icons (PR #825): Shows actual item icons instead of SVG placeholders
- Uses
ItemIconcomponent with proper asset URLs - Consistent with inventory panel styling
- Fallback to placeholder only when icon missing
- Uses
🌐 Website Updates (PR #822)
- Gold Token Page: Refactored into section components
- TokenHero, ValueProps, HowItWorks, OpenSource, GoldCTA
- Error boundaries and loading states
- SEO improvements with opengraph images
- Accessibility enhancements (semantic HTML, ARIA labels)
- Solana Wallet Integration: Updated dependencies
@privy-io/react-authto 3.13.1@privy-io/server-authto 1.32.5@solana-mobile/wallet-standard-mobilefor MWA support- Multi-chain wallet detection (Saga and Seeker devices)
- Code Review: Updated to Claude Opus 4.6
⚔️ Combat System Improvements
Persistent Combat Follow (PR #737)
OSRS-accurate continuous target tracking during combat:- Target Movement Detection: Tracks last known target tile per attacker
- Pre-computes follow path when target moves (even if in range)
- Zero-delay pursuit when target steps out of range
- Prevents stutter pattern where players stand still until target escapes
- Race Condition Fixes:
- Clears action queue when clicking new target (prevents ground-click override)
- Stops active follow when switching targets
- Stops remaining walk path when entering combat range
- Memory Management:
lastCombatTargetTilemap cleaned up on combat end/disengage
Combat Rotation System (PR #732)
Players now automatically face their combat target during ranged/magic attacks:- Face Target Event:
COMBAT_FACE_TARGETnetwork packet- Server emits when attack is processed
- Client rotates player toward target
- Essential for stationary ranged/magic attacks
- Rotation Clearing: Proper cleanup when combat ends
- Clears rotation when target dies
- Clears rotation when player starts moving
- Prevents facing dead targets or old combat directions
Staff Melee/Magic XP (PR #734)
OSRS-accurate staff combat mechanics:- Dual Combat Styles: Staves/wands now have both melee and magic styles
- Melee (no spell): Bash/Pound/Focus grant Attack/Strength/Defense XP
- Magic (with spell): Autocast grants Magic XP
- XP Grant Logic: Validates attack style to prevent wrong XP type
- Autocast Panel: Selecting autocast style automatically opens Spells panel
- Weapon Styles: Updated for STAFF and WAND:
["accurate", "aggressive", "defensive", "autocast"]
🎨 Visual Enhancements
Spell Projectile Upgrade (PR #722)
Multi-layer spell projectiles with WebGPU-compatible rendering:- Multi-Layer Structure: Outer glow + core orb + orbiting sparks (bolt-tier)
- Impact Burst Particles: 4-6 particles burst outward on spell hit
- WebGPU Compatibility: DataTextures with color baked into pixels
- Visual Tuning: Increased spell sizes and glow intensity
- Strike spells: 0.25 → 0.5 size, glow 0.3-0.5 → 0.7
- Bolt spells: 0.35 → 0.7 size, glow 0.5 → 0.8
- Trail System: Element-colored billboard meshes replace white sprites
Bow Models & Arrow Sizing (PR #719)
- Bow Models: All bow items now display 3D models when equipped
- Arrow Size Reduction: Length 0.6 → 0.35, width 0.15 → 0.08 for better scale
Ground Item Snapping (PR #718)
- Bbox Snapping: Automatic bottom-of-model alignment to terrain
- Ground Offset System: New
groundOffsetandmodelScaleitem properties - Fire Models: Fires now sit flush on terrain using bbox calculation
- Ashes Spawn: Fires spawn ashes when they burn out (OSRS-accurate)
🔧 Firemaking Improvements (PR #736)
OSRS-accurate firemaking behavior:- Movement Stopping: Player movement automatically stopped before lighting
- Movement Cancellation: Lighting cancelled if player moves (0.5 unit threshold)
- Fire Placement: Fire spawns at cached start position, not current position
- Network Events: New
FIRE_LIGHTING_CANCELLEDevent
🐛 Bug Fixes
- Mob Respawn (PR #729): Mobs respawn at random spawn point instead of death location
- UI Theme (PR #701): Fixed quest log black background, aligned all panel colors
- Mobile Breakpoint (PR #701): Aligned with breakpoints.md (640px)
- Test Fixes (PR #701): Updated 4078 tests to match current implementation
🎨 Three New Artisan Skills
Complete implementation of Crafting, Fletching, and Runecrafting with OSRS-accurate mechanics:Crafting Skill (PR #698)
- Leather Armor: Needle + thread + leather for basic armor
- 6 leather items: gloves, boots, cowl, vambraces, body, chaps
- Thread consumption system (5 uses per thread)
- Levels 1-18 for full leather set
- Dragonhide Armor: High-level ranged gear from dragon leather
- Green d’hide vambraces (L57), chaps (L60), body (L63)
- Requires needle + thread + dragon leather
- Jewelry Crafting: Gold bars + gems at furnaces
- Requires specific moulds (ring, necklace, amulet, bracelet)
- Moulds not consumed (permanent tools)
- 12 jewelry items from gold rings to diamond bracelets
- Gem Cutting: Chisel + uncut gems
- 4 gem types: sapphire, emerald, ruby, diamond
- Levels 20-43 for all gems
- Tanning System: Convert hides to leather at tanner NPCs
- Cowhide → leather (1 gp)
- Dragon hides → dragon leather (20 gp)
- Instant conversion, no XP granted
- UI Features:
- Category grouping (leather, studded, dragonhide, jewelry, gem_cutting)
- Auto-select when only one recipe matches input item
- Make-X with quantity memory (localStorage)
- Thread usage tracking (5 uses before consumption)
- Database: New
craftingLevelandcraftingXpcolumns (migration 0029) - Testing: 19 unit tests covering crafting lifecycle, cancellation, and edge cases
Fletching Skill (PR #699)
- Arrow Crafting: Multi-output arrow production
- Arrow shafts: 15 per log (knife + logs)
- Headless arrows: 15 per action (arrow shafts + feathers)
- Finished arrows: 15 per action (headless arrows + arrowtips)
- Bronze, Iron, Steel, Mithril, Adamant arrows
- Bow Crafting: Shortbows and longbows
- Unstrung bows: knife + logs
- Strung bows: unstrung bow + bowstring (item-on-item)
- 5 wood types: normal, oak, willow, maple, yew
- Item-on-Item Interactions: Stringing bows and assembling arrows
- Client interaction handler with raycast routing
- Server network handler with target validation
- Packet definition for secondary item ID
- Multi-Output Support: outputQuantity field in recipes
- 15 arrow shafts per log
- 15 arrows per arrowtip set
- Quantity selection refers to actions, not output items
- UI Features:
- Category grouping (arrow_shafts, shortbows, longbows, stringing, arrows)
- Output quantity display (e.g., “Arrow shafts (x15)”)
- Auto-select for single-recipe interactions
- Make-X with quantity memory
- Database: New
fletchingLevelandfletchingXpcolumns (migration 0030) - Smithing Integration: 6 arrowtip recipes (15 per bar) added to smithing
- Testing: 19 unit tests covering fletching system and multi-output recipes
Runecrafting Skill (PR #703)
- Instant Conversion: Click altar to convert ALL essence in inventory
- No tick delay (instant crafting)
- Server-authoritative runeType validation
- Two Essence Types:
- Rune essence: basic runes only (air, mind, water, earth, fire, body)
- Pure essence: all runes (includes cosmic, chaos, nature, law, death)
- Multi-Rune Crafting: Higher levels grant bonus runes per essence
- Air runes: 2x at L11, 3x at L22, up to 10x at L99
- Mind runes: 2x at L14, 3x at L28, up to 8x at L98
- Water runes: 2x at L19, 3x at L38, up to 6x at L95
- Earth runes: 2x at L26, 3x at L52, 4x at L78
- Fire runes: 2x at L35, 3x at L70
- Altar Entities: RunecraftingAltarEntity with mystical particle effects
- Per-altar rune type (air altar, water altar, fire altar, etc.)
- Color-coded particle systems (4 layers: pillar, wisps, sparks, base)
- Mesh-aware particle spawning from actual geometry vertices
- Billboard particles with camera-facing orientation
- Database: New
runecraftingLevelandrunecraftingXpcolumns (migration 0031) - Testing: 19 unit tests covering runecrafting system, multipliers, and validation
🔥 Visual Improvements
Fire Models (PR #715)
- GLB Fire Model: Replaced orange cube placeholder with 3D fire model
- Model path:
fire.glbwith scale 1.0 - Proper lighting and shadows
- Model path:
- Billboard Particle System: Realistic flame effects
- 4 particle layers: pillar, wisps, sparks, base
- Color-baked glow textures for WebGPU compatibility
- Mesh-aware particle spawning from actual geometry
- Camera-facing billboards with quaternion copy
- Additive blending for glow effects
- Particle State Management: Efficient per-particle tracking
- Typed arrays for particle properties (ages, lifetimes, angles, speeds)
- Reusable geometry (shared CircleGeometry for all particles)
- Surface point sampling from fire model mesh
- Bounding box calculation for spawn area
- Performance: Particles added to world.stage.scene (not entity node)
- Registered with world.setHot() for frame updates
- Proper cleanup in destroy() method
Ground Item Snapping (PR #718)
- Bbox-Snap to Terrain: Items now align to ground using bounding box
- Calculates bbox.min.y and offsets mesh position
- Compensates for GroundItemSystem’s 0.2m terrain offset
- Named constant:
GROUND_ITEM_TERRAIN_OFFSET = 0.2 - Applies to all ground items and fire models
- Grounded Items: Items with
groundOffset ≤ 0use bbox snapping- No floating animation for grounded items
- Prevents clipping through terrain
- Consistent placement across all item types
- Documentation: Clarified groundOffset behavior in ItemEntity.ts
Mining Rock Materials (PR #710)
- PBR Materials: Realistic rock appearance with metalness=0
- Prevents overly shiny/metallic rocks
- Proper roughness values for stone
- Depleted Rock Alignment: Bbox snapping for depleted rock models
- Ensures stumps/depleted rocks sit flush with ground
- Consistent with active rock placement
Headstone Model (PR #710)
- GLB Model: Replaced placeholder box with headstone.glb
- Proper 3D gravestone model
- Scale and positioning for visibility
🎮 UI Enhancements
Skill Guide Panel (PR #711)
- Click-to-Open: Click skill icon in skills panel to open guide
- Shows all unlocks for that skill sorted by level
- Visual indicators: ✓ unlocked, ➤ next, 🔒 locked
- Progress tracking (X/Y unlocked)
- Levels to next unlock display
- Client-Side Loading: Unlock data loaded from skill-unlocks.json manifest
- No server round-trip required
- Instant panel opening
- Cached unlock data per skill
- ModalWindow Integration: Consistent styling with other modals
- Themed colors and spacing
- Scrollable unlock list
- Loading state with spinner
- Unlock Types: item, ability, area, quest, activity
- Type badges with color coding
- Next unlock highlighted with “NEXT” badge
🐛 Bug Fixes
- Fishing Spots (PR #712): Aligned water threshold to 9.0m so spots spawn at shoreline instead of underwater
- Gathering Emote (PR #713): Prevented gathering animation from playing when player lacks required tool
- Tool validation before emote trigger
- Prevents visual desync
🧹 Code Cleanup
- Dead Code Removal (PR #715): Removed unused ProcessingSystemBase, FiremakingSystem, and CookingSystem
- Legacy systems replaced by unified ProcessingSystem
- Reduced codebase complexity
⚔️ Ranged Combat System
Complete implementation of OSRS-style ranged combat with bows and arrows:- Ranged Weapons: Shortbow, Oak shortbow, Willow shortbow, Maple shortbow
- Ammunition: Bronze, Iron, Steel, Mithril, and Adamant arrows
- Arrows equipped in new ammo slot
- 100% consumption rate (no Ava’s device yet)
- Arrow strength bonuses affect damage
- Combat Styles: Accurate (+3 Ranged), Rapid (-1 tick speed), Longrange (+2 range, Ranged+Defense XP)
- Damage Calculation: OSRS-accurate formulas with equipment bonuses
- Effective Ranged = floor(rangedLevel × prayer) + styleBonus + 8
- Max Hit = floor(0.5 + effectiveRanged × (arrowStrength + 64) / 640)
- Projectile System: 3D arrow meshes with arc trajectories
- OSRS-accurate hit delay: 1 + floor((3 + distance) / 6)
- Arrows rotate to face travel direction
- Metal-colored arrow tips based on arrow type
- Attack Range: 10 tiles (Chebyshev distance)
- XP Distribution: 4 XP per damage to Ranged, 1.33 XP to Constitution
✨ Magic Combat System
Complete implementation of F2P magic with Strike and Bolt spells:- Spells Panel: New spellbook UI with spell grid
- Click to select autocast spell
- Right-click for context menu (Autocast/Cast)
- Element-colored spell icons with glow effects
- Level requirements displayed
- Combat Spells: 8 F2P spells across 2 tiers
- Strike: Wind (L1, 2 dmg), Water (L5, 4 dmg), Earth (L9, 6 dmg), Fire (L13, 8 dmg)
- Bolt: Wind (L17, 9 dmg), Water (L23, 10 dmg), Earth (L29, 11 dmg), Fire (L35, 12 dmg)
- Rune System: Air, Water, Earth, Fire, Mind, and Chaos runes
- Runes consumed on each cast
- Elemental staves provide infinite runes (Staff of Air/Water/Earth/Fire)
- Staffless Casting: Can cast spells without a staff (OSRS-accurate)
- Staff provides magic attack bonus and infinite runes
- Spells work with any weapon or no weapon
- Combat Styles: Accurate (+3 Magic), Longrange (+2 range, Magic+Defense XP), Autocast
- Damage Calculation: OSRS-accurate formulas
- Effective Magic = floor(magicLevel × prayer) + styleBonus + 8
- Max Hit = spell base damage (no equipment modifiers in F2P)
- Magic Defense = floor(0.7 × magicLevel + 0.3 × defenseLevel) for players
- Projectile Visuals: Colored spell orbs with element-specific effects
- OSRS-accurate hit delay: 1 + floor((1 + distance) / 3)
- Spell trails with glow and pulse effects
- Database: New
magicLevel,magicXp, andselectedSpellcolumns - XP Distribution: Base spell XP + (2 × damage) to Magic, 1.33 XP per damage to Constitution
🎯 Combat System Enhancements
- Attack Type Detection: Automatic detection of MELEE/RANGED/MAGIC from equipped weapon or selected spell
- Pathfinding Updates: Ranged/magic use Chebyshev distance, melee uses cardinal-only for range 1
- Pending Attack Manager: Updated to support all three attack types with appropriate range checks
- Equipment Panel: New ammo slot for arrows (top-right position)
- Menu Bar: Added Spells button (🔮) to menu bar
- Combat Panel: Updated with icons for Rapid, Longrange, and Autocast styles
- Timestamp Validation: Required timestamp validation on all combat packets (prevents replay attacks)
🧪 Testing
- Unit Tests: Comprehensive tests for ranged/magic damage calculators
- Integration Tests: Full combat flow testing for all three attack types
- Type Guards: Event payload validation throughout combat handlers
⚔️ Player vs Player Dueling
Complete OSRS-style duel arena system with rules negotiation and stakes:- Duel Flow: Challenge → Rules → Stakes → Confirm → Countdown → Fight → Result
- Challenge System: Walk to player and send duel challenge (like trading)
- 30-second timeout for acceptance
- Distance-based cancellation (15 tiles)
- Clickable chat messages for acceptance
- Rules Screen: 10 combat rule toggles
- No Ranged, No Melee, No Magic, No Special Attack
- No Prayer, No Potions, No Food, No Movement, No Forfeit
- Equipment slot restrictions (11 slots)
- Both players must accept to proceed
- Stakes Screen: Item staking with anti-scam features
- Left-click to stake 1, right-click for quantity menu
- Value imbalance warnings
- Opponent modification warnings
- Both players must accept to proceed
- Confirmation Screen: Final read-only review
- Shows all rules, equipment restrictions, and stakes
- Both players must accept to start
- Arena System: 6 arenas in 2×3 grid layout
- Arena pooling with automatic reservation
- Collision walls prevent escape during combat
- Flattened terrain for fair combat
- Forfeit pillars for surrendering (if allowed)
- Combat Resolution: Winner receives all stakes
- Death or forfeit determines winner
- Automatic health restoration
- Teleport to lobby after duel
- Disconnect handling with 30-second timeout
- UI Components:
- DuelPanel with screen transitions
- DuelChallengeModal for incoming challenges
- DuelCountdown overlay (3-2-1-FIGHT)
- DuelResultModal showing items won/lost
- DuelHUD during combat with opponent health
🏗️ Architecture
- DuelSystem: Main orchestrator for duel state machine
- DuelSessionManager: Session CRUD operations
- PendingDuelManager: Challenge tracking and timeouts
- ArenaPoolManager: Arena reservation and collision
- DuelCombatResolver: Combat resolution and stake transfers
- Audit Logging: Economic tracking for all duel outcomes
🧪 Testing
- 1,066 lines of DuelSystem unit tests
- 456 lines of PendingDuelManager tests
- 233 lines of ArenaPoolManager tests
- Full coverage of state machine transitions and edge cases
🔒 Security Enhancements
Comprehensive security improvements across authentication and input validation:- Secure Token Storage: Configurable auth storage (localStorage, sessionStorage, or memory)
AuthStorageTypeenum for storage selection- Memory-only mode for maximum security
- Proper cleanup on logout (clears all storage types)
- URL Parameter Validation: Removed authToken from URL params (security vulnerability)
- BREAKING: Tokens must now be passed via postMessage, not URL
- Schema-based validation for embedded config parameters
- Prevents token exposure in browser history and server logs
- CSP Violation Monitoring: Content-Security-Policy violation event handling
- Throttled reporting to prevent flooding
- Report-URI endpoint for security monitoring
- Documented CSP directives and security implications
- Async Token Provider: Fresh token retrieval for API calls
- Registered with API client for automatic token refresh
- Prevents stale token usage
- Timestamp Validation: Required on all combat packets
- ±30 second window for replay attack prevention
- Missing timestamps now rejected (was optional)
- Type Guards: Comprehensive event payload validation
isUIUpdateEvent,isPlayerStatsData,isSkillsUpdateEventisPrayerPointsChangedEvent,isPrayerStateSyncEvent- Runtime validation at system boundaries
🎨 UI/UX Improvements
- Viewport Scaling: Proportional panel resize on viewport changes
- ViewportScaler component for design resolution-based scaling
- Grid snapping utility for window alignment
- Left/right column panel scaling with proper stacking
- Minimap Overhaul:
- Independent width/height resizing (no longer forced square)
- Extracted MinimapOverlayControls component
- Fixed pip desync using cached projection-view matrices
- Default zoom set to 10 for better visibility
- Panel Layout Redesign: Right column flush stacking
- Minimap (top) → gap → Combat → Skills → Inventory → Menubar (bottom)
- Consistent panel widths (235px) for alignment
- No overlaps, touching edges
- Combat Panel: Attack style buttons changed from 2×2 grid to 1×3 row for better mobile UX
⚡ Performance Optimizations
- Object Pooling: Memory optimization utilities
- ArrayPool for reusable temporary arrays
- EventDataPool for pooled event objects
- PositionPool for position reuse
- withPooledArray helper for scoped pool usage
- Notification Helpers: Convenient error handling functions
- showErrorNotification, showNetworkErrorNotification, showSuccessNotification
- Consistent error logging with console output
🧪 Testing
- Security Tests: URL parameter validation, error boundaries
- E2E Test Utilities: Entity introspection, visual testing helpers
- getEntitiesByType, getPlayerInventory, getPlayerEquipment, getPlayerSkills
- clickAtWorldPosition for world coordinate input simulation
- Unit Tests: Expanded test patterns in vitest config
🎯 Static Tile Collision System
Production-quality OSRS-accurate collision system for static world objects and entities:- CollisionMatrix: Zone-based collision storage with bitmask flags
- World divided into 8×8 tile zones (Int32Array[64] = 256 bytes per zone)
- Lazy allocation: zones created on first write
- Memory efficient: 1000×1000 tile world = ~4MB
- O(1) tile lookups with zero allocations in hot paths
- Collision Flags: OSRS-accurate bitmask flags
- Static objects:
BLOCKED,WATER,STEEP_SLOPE - Entity occupancy:
OCCUPIED_PLAYER,OCCUPIED_NPC - Directional walls: 8 directions for future dungeons/buildings
- Combined masks:
BLOCKS_WALK,BLOCKS_MOVEMENT,BLOCKS_RANGED
- Static objects:
- Automatic Footprint Detection: Build-time model bounds extraction
- New
extract-model-bounds.tsscript parses GLB files - Extracts bounding boxes from glTF position accessors
- Generates
model-bounds.jsonmanifest automatically - Footprints calculated from model dimensions × scale
- Turbo caching avoids rebuilding when models unchanged
- New
- Multi-Tile Entities: Stations and large resources occupy multiple tiles
- Furnaces, anvils, banks support 2×2 footprints
- Center-based registration (footprint centered on entity position)
- OSRS-style interaction from any adjacent tile
tilesWithinRangeOfFootprint()for multi-tile range checks
- Pathfinding Integration: Mobs and players respect collision
- BFSPathfinder checks
CollisionMatrixfor static objects - Safespotting: players can hide behind trees/rocks
- Depleted resources remain solid (stumps block movement)
- BFSPathfinder checks
- Network Synchronization: Zone-based collision sync
- Base64 serialization for efficient network transport
- Only allocated zones sent to clients
getZonesInRadius()for area-based sync
- Testing: 1,118 lines of comprehensive unit tests
- CollisionMatrix: zone allocation, flag operations, negative coordinates
- CollisionFlags: bitmask uniqueness, directional walls
- TileSystem: multi-tile footprint calculations
💰 Coin Pouch Withdrawal System
RS3-style money pouch with secure withdrawal to inventory:- Money Pouch: Protected coin storage (characters.coins column)
- Doesn’t use inventory slots
- Separate from physical coin items
- Displayed in inventory panel footer
- Withdrawal Flow: Pouch → inventory as stackable coins item
- Click coin pouch to open withdrawal modal
- Enter amount or use preset buttons
- Coins added to existing stack or new inventory slot
- Server-authoritative with atomic database transactions
- Security Features:
- Rate limiting: 10 requests/second with auto-cleanup
- Timestamp validation: prevents replay attacks (±30s window)
- Input validation: positive integers, max 2.1B coins
- Overflow protection: prevents MAX_COINS stack overflow
- Row-level locking:
FOR UPDATEin transactions - Audit logging: tracks large withdrawals (≥1M coins)
- UI Components:
- Extracted
CoinPouchcomponent with keyboard accessibility - Hoisted styles prevent re-render allocations
CoinAmountModalreused from bank system- Enter/Space key support for accessibility
- Extracted
- Error Handling: Graceful inventory sync with fallback
- Transaction commits before sync
- Sync failures logged but don’t fail withdrawal
- Players can relog to resync if needed
- Testing: 32 comprehensive unit tests
- Input validation (NaN, Infinity, negative, overflow)
- Insufficient coins, inventory full, stack overflow
- New stack creation, existing stack updates
- Atomicity simulation
🎨 Hover Tooltips & Click-to-Unequip
RS3-style hover tooltips and improved equipment interaction:- Hover Tooltips: Item stats on hover (equipment and inventory)
- Shows item name, bonuses (attack/defense/strength), and quantity
- Portal-based rendering avoids z-index issues
- Suppressed when context menu is open
- Separate from targeting mode tooltips
- Click-to-Unequip: Direct equipment slot clicks unequip items
- Removed modal popup (faster workflow)
- Sends
unequipItempacket directly to server - Consistent with OSRS/RS3 behavior
- Context Menu Coordination: Smart tooltip suppression
contextmenu:closeevent dispatched on menu close- Hover tooltips re-enabled after menu closes
- Memory leak fix: uses ref instead of state setter for event dispatch
- Code Quality: Extracted tooltip rendering functions
renderItemHoverTooltip()andrenderEquipmentHoverTooltip()- Improved testability and maintainability
- Eliminated 80+ line IIFEs in JSX
🗺️ Walk Here Context Menu
OSRS-style terrain interaction with right-click menu:- Terrain Context Menu: Right-click empty terrain shows “Walk here”
- Separate from entity context menus
- Includes “Cancel” option
- Uses same
ContextMenuActionstructure
- Type Safety: New
ContextMenuTargetTypeunion- Separates entity types from UI menu types
InteractableEntityTyperemains strict (entities only)ContextMenuTargetType = InteractableEntityType | "terrain"- Prevents semantic type errors
- Null Handling:
ContextMenuController.showMenu()accepts null target- Terrain clicks pass
nullfor target - Synthetic values used for menu display
- Terrain clicks pass
🐛 Bug Fixes & Improvements
- Fishing Animation: Restored fishing emote instead of idle placeholder (#554)
- Store Transactions: Wrapped with
executeInventoryTransactionto prevent race conditions (#553) - Bank Stacking: Stackable base items now stack when withdrawing from bank (#553)
- Removed 28-item withdrawal limit for stackable items
- Coins, ores, logs, fish now stack properly
- Mob Combat Animations: Fixed idle/walk emotes overriding combat animations (#544)
- Added
isPriorityEmote()andisCombatEmote()helper methods - Death animation timing consolidated to tick-based constant (7 ticks)
- Moved
emoteMapto instance constant (performance optimization)
- Added
- Nametag Removal: Removed overhead nametags for OSRS accuracy (#547)
- Cleaned up orphaned nametag files and JSDoc references
🧪 Test Suite Maintenance
- Vitest Imports: Fixed incorrect
bun:testimports →vitest - Stale Assertions: Updated hardcoded counts to match current implementation
- Item counts: 82→86 base items, 17→19 tools, 35→36 resources
- Combat timeout: 8→17 ticks (now uses
COMBAT_CONSTANTS) - Food count: 11→12 types
- Performance Thresholds: Adjusted for CI environment variance
- Heap growth: 500KB→5000KB (allows GC timing variance)
- Scaling ratios: 10x→50x (allows parallel test contention)
- Added documentation of observed values (local vs CI)
- Mock Fixes: Added missing
entities.playersto mock worlds
📦 Asset System Enhancements
New manifest additions and improvements to the asset pipeline:- Model Bounds Manifest: New
model-bounds.jsonwith pre-calculated bounding boxes for all 3D models- Contains bounds (min/max coordinates), dimensions (x/y/z), and tile footprints
- Used for spatial calculations, collision detection, and placement validation
- Auto-generated from actual model geometry
- Covers 50+ models including tools, weapons, armor, resources, NPCs, and stations
- Fishing Content: Complete fishing system with tools and spots
- New models:
fishing-rod-baseandfishing-rod-standard - 7 fishing spots with OSRS-accurate catch rates (net, bait, fly, cage, harpoon, monkfish, shark)
- Catch rate formulas with
catchLowandcatchHighvalues for level-based success - Secondary requirements (bait, feathers) for certain fishing methods
- New models:
- Mining Expansion: Additional ore rocks with depleted states
- Copper rock with depleted model variant
- Mithril rock model added
- Runite rock with depleted variant
- All rocks include proper model paths and scale values
- Crafting Stations: 3D models for smithing and smelting
- Anvil model (
anvil.glb) with scale 0.5 and yOffset 0.2 - Furnace model (
furnace.glb) with scale 1.5 and yOffset 1.0 - Station manifest updated with proper model paths and positioning
- Anvil model (
🛡️ Combat Session Management
OSRS-accurate behavior where being attacked closes bank/store/dialogue interfaces:- Session Interruption: Bank, store, and dialogue UIs automatically close when player is attacked
- OSRS-Accurate: Even splash attacks (0 damage) interrupt banking - being in combat matters
- Server-Authoritative: Server sends close packets with
reason: "combat" - Defensive Guards: Malformed events handled gracefully (missing targetId/targetType)
- Session Close Reasons: Added
"combat"toSessionCloseReasontype - Testing: 342 lines of comprehensive unit tests covering all session types and edge cases
⚡ Performance Optimizations
Raycast proxy optimization for fast entity click detection:- Raycast Proxy: Invisible capsule meshes for instant click detection on NPCs and mobs
- Performance Gain: Avoids expensive VRM SkinnedMesh raycast (~700-1800ms per click)
- Implementation: Proxy added directly to THREE.Scene, bypasses Node system
- VRM Optimization: Disabled raycast on all VRM/GLB meshes (
child.raycast = () => {}) - Constants: Centralized
RAYCAST_PROXYconstants (radius, height, segments, Y-offset) - Memory Management: Proper cleanup in
destroy()methods
🍖 Food Consumption & Healing System
OSRS-accurate food consumption with 3-tick eat delay and combat integration:- Eat Delay: 3-tick (1.8s) cooldown between eating foods
- Combat Integration: Eating during combat adds 3-tick attack delay (only if already on cooldown)
- Server-Authoritative: All validation happens server-side with comprehensive security
- Rate limiting (3 requests/second)
- Input validation (slot bounds, item ID mismatch detection)
- Exploit prevention (
MAX_HEAL_AMOUNT: 99caps healing) - Consume-after-check (food only removed after validation passes)
- OSRS Behavior: Food consumed even at full health, message format: “You eat the shrimp.”
- Event-Driven Health: Client health bars now use
PLAYER_HEALTH_UPDATEDevents (eliminates race conditions) - New Classes:
EatDelayManagerfor per-player cooldown tracking - Testing: 30 unit tests covering eat delay and combat integration
⛏️ Mining System Improvements
OSRS-accurate mining mechanics with correct depletion and success rates:- 100% Rock Depletion: Rocks always deplete after yielding one ore (OSRS-accurate)
- Accurate Success Rates: Updated to match OSRS wiki formulas
- Copper/Tin: ~39.5% at L1, 100% at L62
- Iron: ~52% at L15, 100% at L63
- Coal: ~16.4% at L30, ~39.5% at L99
- Runite: ~6.64% at L85, ~7.42% at L97+
- Pickaxe Bonuses: Dragon/Crystal pickaxe bonus speed implemented
- Dragon: 1/6 chance for 2-tick roll (avg 2.83 ticks)
- Crystal: 1/4 chance for 2-tick roll (avg 2.75 ticks)
- Server-side roll for determinism (prevents client/server desync)
- Model Scale Fix: Normalized non-uniform scales in GLTF models (fixes “squished” rocks)
- Depleted Models: Generalized depleted model swapping for all resource types
🎨 Context Menu Enhancements
Continued improvements to OSRS-style context menus:- Centralized Constants: All context menu colors now use
CONTEXT_MENU_COLORSfromGameConstants.ts - Consistency: Eliminated hardcoded color values across all interaction handlers
- Resource Colors: Resources now use cyan (object color) instead of yellow (NPC color) for OSRS accuracy
- Performance: Added
useMemoanduseCallbackfor render optimization inInventoryPanel - Testing: 333 lines of tests for
InventoryActionDispatcher
🐛 Bug Fixes
- UI Border Conflicts: Fixed border shorthand conflicts in
SettingsPanelandCombatPanel(#510) - Noted Tools: Noted items now correctly rejected as valid tools for skilling (#508)
- Quaternion Normalization: Fixed squished resources by using
config.rotation.winstead of hardcoded 1 (#507) - Auto-Retaliate Guards: Added server guards to prevent client-side rotation conflicts (#511)
- Bank Placeholders: Fixed unique constraint violation on “release all placeholders” using two-phase slot update (#502)
🎮 UX Improvements
- F5 Debug Toggle: Added F5 keybind to toggle FPS debug panel (matches Minecraft) (#505)
- Invalid Target Feedback: “Nothing interesting happens.” message when using item on invalid target
- Cancel Option: OSRS-style Cancel option always shown last in context menus
- Debug Panel: F5 or backslash key now toggles FPS/performance stats
⚒️ Smithing Skill System
Complete implementation of the Smithing skill with OSRS-accurate mechanics:- Smelting: Convert ores into bars at furnaces
- 6 bar types: Bronze, Iron, Steel, Mithril, Adamant, Rune
- Iron ore has 50% success rate (OSRS-accurate)
- Coal requirements for higher-tier bars
- Tick-based timing (4 ticks per bar)
- Smithing: Convert bars into equipment at anvils
- 40+ smithable items across weapons, armor, and tools
- Requires hammer in inventory
- Organized by category in UI
- “Make X” functionality with quantity memory
- Database: New
smithingLevelandsmithingXpcolumns - Manifests: New
recipes/smelting.jsonandrecipes/smithing.json
🎨 OSRS-Style Context Menus
Authentic context menu system with colored entity names:- Color Coding: Orange for items (#ff9040), yellow for NPCs (#ffff00), cyan for objects (#00ffff)
- Inventory Actions: Manifest-driven actions with heuristic fallback
- Items can define
inventoryActionsarray - First action becomes left-click default
- Supported: Eat, Drink, Wield, Wear, Bury, Use, Drop, Examine
- Items can define
- Centralized Dispatching:
InventoryActionDispatchereliminates code duplication - Item Helpers: Type detection utilities (
isFood,isPotion,isBone,usesWield,usesWear) - Cancel Option: Always shown last (OSRS standard)
🏗️ Manifest Architecture Improvements
- Items Directory: Items split into 5 category files (weapons, tools, resources, food, misc)
- Gathering Manifests: Separate files for woodcutting, mining, fishing
- Complete fishing spot definitions with OSRS-accurate catch rates
- Mining rocks with 3D model paths and depleted states
- Recipe Manifests: Dedicated files for cooking, firemaking, smelting, smithing
- Station Manifests: 3D models and configs for anvils, furnaces, ranges
- Anvil model with scale 0.5 and yOffset 0.2
- Furnace model with scale 1.5 and yOffset 1.0
- Tier System: Centralized
tier-requirements.jsonfor equipment levels - Skill Unlocks:
skill-unlocks.jsondefines what unlocks at each level - Atomic Loading: All category files must exist or system falls back to legacy format
🎣 New 3D Models
- Fishing Tools: Base and standard fishing rod models with metadata
- Mining Rocks: Copper, mithril, and runite ore rocks with depleted variants
- Stations: Anvil and furnace models for crafting
🔧 Data Providers
New specialized providers for efficient data access:- ProcessingDataProvider: Cooking, firemaking, smelting, smithing recipes
- TierDataProvider: Equipment level requirements by tier
- StationDataProvider: Station models and configurations
- Pre-allocated Buffers: Memory optimization for inventory counting
🐛 Bug Fixes
- Resource Spam Fix: Prevent duplicate harvesting on spam-click (#494)
- Equipment System: Tools with
equipSlot: "weapon"can now be equipped (hatchets, pickaxes) - Firemaking: Level requirement validation added
- Cooking: Level requirement check before starting action
📊 Testing
- 843 new test lines: Comprehensive coverage for inventory actions and item helpers
- Unit Tests: InventoryActionDispatcher (333 lines), item-helpers (510 lines)
- Integration Tests: ProcessingDataProvider, skill unlocks, data integrity
🎮 Core Features
- Tick-based Combat — Authentic OSRS-style 600ms tick combat system
- AI Agents — ElizaOS integration with 17 actions and 6 state providers
- Skill System — 11 skills: Combat (Attack, Strength, Defense, Constitution, Ranged, Prayer), Gathering (Woodcutting, Fishing, Mining), Artisan (Firemaking, Cooking, Smithing)
- Economy — Banks, shops, inventory, and loot system
🏗️ Architecture
- Turbo monorepo with 7 packages
- Three.js + PhysX WASM for 3D physics
- Fastify + WebSocket multiplayer
- React 19 + Vite client
📦 Packages
@hyperscape/shared— Core 3D engine@hyperscape/server— Game server@hyperscape/client— Web client@hyperscape/plugin-hyperscape— ElizaOS plugin@hyperscape/physx-js-webidl— PhysX bindings3d-asset-forge— AI asset generation