March 2026 UI & Systems Update
Comprehensive documentation for major UI panel redesign, prayer system fixes, equipment improvements, and combat optimizations merged to main in late March 2026.Overview
This document covers four major pull requests merged between March 25-26, 2026:- PR #1090: Prayer Login Sync Fix
- PR #1088: Comprehensive UI Panel Upgrade
- PR #1089: Equipment Panel Cross-Player Leak Fix
- PR #1087: Inventory UI Fixes for Firemaking and Targeting Mode
Prayer Login Sync Fix (PR #1090)
Problem
Prayer state was not hydrating correctly on login or reconnect. The client bootstrap path was overwriting authoritative cached prayer state with local entity fallback values before real prayer data had finished hydrating, causing the UI to stay stale until the player toggled a prayer again.Solution
Preserve Authoritative Cache During Hydration:- Only use entity prayer fallback when both
prayerPointsandmaxPrayerPointsare explicitly present and finite - Added
isFiniteNumber()guard to prevent NaN/Infinity from overwriting cache - Rerun initial hydration when local player becomes available via
PLAYER_SPAWNEDevent
PrayerSystemnow re-emitsPRAYER_STATE_SYNConPLAYER_JOINEDevent- Ensures client receives authoritative prayer snapshot for new session after join
- Handles both initial login and reconnection scenarios
Implementation
packages/client/src/hooks/usePlayerData.ts:Testing
New Tests:packages/client/tests/unit/hooks/usePlayerData.test.ts- Prayer cache preservation, PLAYER_SPAWNED hydration, finite number guardspackages/shared/src/systems/shared/character/__tests__/PrayerSystem.sync.test.ts- PLAYER_REGISTERED → PLAYER_JOINED flowpackages/client/tests/e2e/prayer-sync.spec.ts- Full login/reload/reconnect flow
Impact
- Prayer HUD and prayer panel now reflect persisted server state immediately after login or reconnect
- No more stale prayer UI requiring manual prayer toggle to sync
- Robust handling of entity data timing races during bootstrap
Comprehensive UI Panel Upgrade (PR #1088)
Overview
Major UI overhaul with 4,211 additions and 2,320 deletions across 33 files. Introduces unified panel layout constants, reusable tooltip component, combat panel redesign with heraldic shields, equipment panel with live 3D paperdoll portrait, and tab persistence improvements.Unified Panel Layout Constants
New File:packages/client/src/constants/panelLayout.ts
Single source of truth for icon-grid panel dimensions used by Inventory, Equipment, Prayer, Spells, and Skills panels.
Constants:
- Eliminates scattered magic numbers across 5+ panel components
- Ensures visual consistency across all icon-grid panels
- Single place to adjust spacing/sizing for all panels
CursorTooltip Component
New File:packages/client/src/ui/core/tooltip/CursorTooltip.tsx
Reusable portal-based mouse-following tooltip that auto-measures and flips orientation when clipping viewport edges.
Features:
- Automatic dimension measurement via
useTooltipSizehook - Viewport-edge flipping with
calculateCursorTooltipPosition - Standardized dark gradient theme across game
- Portal rendering to document.body for z-index safety
- InventoryPanel tooltip (manual portal + positioning)
- EquipmentPanel tooltip (manual portal + positioning)
- PrayerPanel tooltip (manual portal + positioning)
- SkillsPanel tooltip (manual portal + positioning)
- SpellsPanel tooltip (manual portal + positioning)
- Eliminates ~200 lines of duplicated tooltip code
- Consistent tooltip behavior across all panels
- Easier to maintain and extend
Combat Panel Redesign
Heraldic Shield Banners: Replaced vertical combat style list with horizontal SVG shield banners featuring:- Protruding icons in circular badges above shields
- Theme-derived gradients (background colors)
- Style-colored tint overlays when active
- Filled game-style icons (accurate, aggressive, defensive, controlled, rapid, longrange, autocast)
- Outer shadow edge for depth
- Base fill with theme gradient
- Active color tint overlay (when selected)
- Border stroke (gold inactive, style color active)
- Top edge highlight for polish
- Inner bevel for depth
- Combat style banners moved to top
- HP + Combat Level below banners
- Target health (when in combat)
- Stats row (Attack/Strength/Defense)
- Auto-retaliate pinned to bottom
packages/client/src/game/panels/CombatPanel.tsx(+1,103/-1,427 lines)
Equipment Panel with Live 3D Paperdoll Portrait
Paperdoll Layout: Equipment slots arranged around a live 3D character preview showing equipped gear in real-time. Grid Layout:- Real-time equipment visualization
- Drag to rotate character
- Scroll to zoom (0.4x - 1.15x)
- Smooth camera interpolation
- Automatic framing based on avatar bounds
- Loading/fallback states with stylized silhouette
EquipmentPaperdollPortrait
packages/client/src/game/panels/equipment/EquipmentPaperdollPortrait.tsx (765 lines)
Features:
- Dedicated WebGPU renderer for portrait (separate from main game renderer)
- Avatar loading from player’s VRM URL
- Equipment visual attachment using
EquipmentVisualHelpers - Interactive camera controls (drag rotate, scroll zoom)
- Three portrait modes:
loading,live,fallback - Automatic camera framing with zoom compensation
- Smooth rotation/zoom interpolation (15x lerp factor)
packages/shared/src/systems/client/EquipmentVisualHelpers.ts
Extracted shared equipment attachment logic from EquipmentVisualSystem for reusability:
Exported Functions:
resolveEquipmentVisualData(options)- Get item metadataresolveEquipmentVisualUrls(options)- Resolve primary/fallback URLsattachEquipmentVisualToVRM(options)- Attach model to VRM bonesremoveEquipmentVisual(visuals, slot)- Clean up equipment visualextractEquipmentAttachmentData(gltf)- Parse attachment metadata
EquipmentVisualModelData- Item metadata structureEquipmentVisualUrlResolution- URL resolution resultEquipmentVisualStore- Visual storage by slotEquipmentAttachmentData- Attachment metadata
- Equipment preview system and main equipment system share same attachment logic
- Eliminates code duplication
- Easier to maintain and test
- Enables future equipment preview features
- Removed item name labels from slots (icon-only)
- Quantity badges for stackable items (arrows)
- Hover tooltips show full item details
- Empty slot tooltips show slot name
- Improved visual hierarchy
- Stats button (opens stats panel)
- On Death button (shows death mechanics)
packages/client/src/game/panels/EquipmentPanel.tsx(+549/-538 lines)packages/client/src/game/panels/equipment/EquipmentPaperdollPortrait.tsx(new, 765 lines)packages/shared/src/systems/client/EquipmentVisualHelpers.ts(new, 400+ lines)packages/client/src/game/character/avatarPreviewViewport.ts(new, 130 lines)
Tab Persistence
Problem: Switching between tabs (e.g., Prayer ↔ Spells) unmounted inactive tabs, losing scroll position and component state. Solution: Render all tabs simultaneously withdisplay: none/flex toggling instead of unmounting.
Implementation:
packages/client/src/game/interface/InterfacePanels.tsx:
- Scroll position preserved across tab switches
- Component state (expanded sections, selections) maintained
- Smoother user experience
- No re-mounting overhead when switching tabs
SpellsPanel Added to Default Layout
Schema Migration v17: Added Spells tab alongside Prayer in the default right-column window. packages/client/src/game/interface/DefaultLayoutFactory.ts:Quest UI Theme Improvements
Themed Components:- Quest list items use
getInteractiveTileStylefor consistent hover/active states - Category groups use
getPanelInsetStylefor section headers - Detail popup uses
getWindowSurfaceStylefor immersive game window feel - Compact pill badges for meta info (category, level, status, progress)
- State indicator dots with glow effect
- Category icons (crown, scroll, sun, calendar, star)
- Progress bars inline with quest items
- Themed section cards with inset styling
- Improved mobile layout with touch-friendly targets
packages/client/src/game/components/quest/QuestLog.tsx(+236/-202 lines)packages/client/src/game/panels/QuestsPanel.tsx(+77/-66 lines)
Panel Size Adjustments
Equipment Panel:- Min size: 235×290 → 235×340 (taller for portrait)
- Preferred size: 260×360 → 260×390
- Max size: 390×550 → 340×520
- Min height: 34px → 32px (more compact)
- Padding: reduced for tighter window bars
Equipment Panel Cross-Player Leak Fix (PR #1089)
Problem
Equipment panel was displaying other players’ gear (including AI agents’ weapons) becauseequipmentUpdated broadcasts hit all players and the UI had no playerId filter.
Solution
Filter Equipment UI Updates by Local Player ID: packages/client/src/hooks/usePlayerData.ts:Combat Damage Deduplication
Problem:sendToNearby publishes to 9 region topics, causing players near region boundaries to receive the same combatDamageDealt packet 2-3 times, resulting in duplicate damage splats.
Solution: Tick-based deduplication with periodic sweep.
packages/shared/src/systems/client/ClientNetwork.ts:
- Eliminates duplicate damage splats near region boundaries
- Bounded memory usage (max 200 entries)
- Efficient periodic sweep (only when needed)
- Handles rolling deploys gracefully (tick fallback)
Optimistic UI Updates for Combat
Attack Style Switching:- UI updates instantly on click (no server round-trip delay)
- Server confirmation overwrites optimistic value with authoritative state
- Matches OSRS zero-delay feel
- Same optimistic pattern as attack style
- Instant visual feedback
- Server-authoritative confirmation
Attack Style Cooldown Removal
Removed Dead Code:STYLE_CHANGE_COOLDOWN = 0(was always zero)styleChangeTimersMap and all timer managementcombatStyleHistoryarray (write-only, never displayed)lastStyleChangetimestamp trackingcanPlayerChangeStyle()method (always returned true)getRemainingStyleCooldown()method (always returned 0)getPlayerStyleHistory()method (always returned [])
- Simplified codebase (~200 lines removed)
- No functional change (cooldown was already 0ms)
- Cleaner attack style system
Auto-Style Switching on Weapon Change
OSRS-Accurate Behavior: When weapon changes, validate current style is still available. If not, auto-switch to the first valid style for the new weapon type. Example: Switching from staff (autocast) to sword → auto-select “accurate” packages/shared/src/systems/shared/character/PlayerSystem.ts:Auto-Initialize Attack Style and Auto-Retaliate
Event Ordering Race Condition: If a player changes attack style or toggles auto-retaliate beforeonPlayerRegister fires (event ordering race), their in-session choice was being overwritten by the DB-saved value.
Solution: Auto-initialize on first use if player exists but onPlayerRegister hasn’t fired yet.
packages/shared/src/systems/shared/character/PlayerSystem.ts:
- Player’s in-session choice takes precedence over DB-saved value
- Updated value persists on next periodic save
- Handles event ordering races gracefully
Item Rename: bronze_sword → bronze_shortsword
Consistency Update: Renamed starter weapon across all systems for consistency with weapon categorization. Files Changed:packages/shared/src/systems/shared/character/InventorySystem.tspackages/shared/src/systems/shared/character/PlayerSystem.tspackages/shared/src/systems/shared/entities/ItemSpawnerSystem.ts
- Consistent weapon naming across codebase
- Aligns with weapon type categorization (SHORTSWORD, LONGSWORD, SCIMITAR, 2H_SWORD)
Inventory UI Fixes (PR #1087)
Firemaking Optimistic Inventory Removal
Problem: Logs didn’t disappear from inventory until server confirmed firemaking action (~100-200ms delay). Solution: Optimistic removal using consolidatedClientNetwork API.
packages/shared/src/systems/shared/interaction/InventoryInteractionSystem.ts:
ClientNetwork.applyOptimisticRemoval() automatically snapshots before mutation and manages rollback internally:
Targeting Mode Improvements
Immediate Targeting State Clear: packages/client/src/game/panels/InventoryPanel.tsx:- No stale highlights on all targets after selection
- Cleaner UX with instant state reset
- Prevents visual confusion
- Prevents grey highlight flash on all filled slots when entering targeting mode
- Simpler hover logic
- Better visual feedback
Fire Model Asset Path Fix
Corrected Path: packages/shared/src/systems/shared/interaction/ProcessingSystem.ts:Optimistic Inventory Consolidation
Moved to ClientNetwork: All optimistic inventory logic consolidated from module-level state inInventoryActionDispatcher to ClientNetwork instance methods.
Benefits:
- Eliminates module-level mutable state (
trackedWorld,prunerInterval,inventoryTracker) - Proper lifecycle management (cleanup on disconnect)
- Shared tracker for all optimistic actions (eat, drop, bury, firemaking)
- Single pruner interval instead of multiple
InventoryActionDispatcher(eat, drop, bury)InventoryInteractionSystem(firemaking)
Combat Rotation Race Condition Fix
Problem:PlayerLocal.update() (hot entity) runs BEFORE TileInterpolator.update() (system), so they fought over base.quaternion every frame. PlayerLocal wrote combat rotation, then TileInterpolator overwrote it with stale state.quaternion, or vice versa.
Solution: Route combat rotation through TileInterpolator when it controls the entity.
packages/shared/src/entities/player/PlayerLocal.ts:
- Combat rotation flows through TileInterpolator’s state, matching remote players
- Eliminates race condition between hot entity and system
- Consistent rotation behavior for local and remote players
API Changes
ClientNetwork Public Methods
New Public Methods:Equipment Visual Helpers
New Exports from@hyperscape/shared:
PlayerAttackStyleState Type
Removed Fields:Migration Guide
For Developers
Panel Layout Constants: If you’re creating new icon-grid panels, use the shared constants:CursorTooltip:
ClientNetwork public API instead of direct cache manipulation:
For Players
Prayer Sync:- Prayer state now persists correctly across login/reconnect
- No manual prayer toggle needed to sync UI
- New shield banner design for attack styles
- Instant feedback on style changes (no delay)
- Auto-retaliate toggle responds immediately
- Live 3D character preview showing equipped gear
- Drag to rotate, scroll to zoom
- Click slots to unequip (no modal)
- Hover for item stats
- Now included in default layout alongside Prayer
- Access via right-column window tabs
- Logs disappear from inventory instantly when lighting fire
- Smoother visual feedback
Performance Considerations
Tab Persistence Trade-off
Before: Only active tab mounted (minimal memory, re-mount overhead on switch) After: All tabs mounted simultaneously (higher memory, instant switching) Impact:- Increased initial render cost (all panels mount at once)
- Higher memory usage (all panel state in memory)
- Better UX (instant tab switching, preserved state)
Equipment Portrait WebGPU Context
Consideration: Each equipment panel creates its own WebGPU renderer, animation loop, and avatar scene. This is a second WebGPU context alongside the main game renderer. Impact:- Additional GPU memory usage
- Potential context loss on lower-end GPUs (especially mobile)
- Separate render loop overhead
Optimistic UI Pattern
Benefits:- Instant visual feedback (matches OSRS zero-delay feel)
- Better perceived performance
- Reduced user frustration
- UI can show incorrect state if server rejects action
- Requires rollback mechanism for correctness
- Increased complexity in state management
Testing
New Test Files
Prayer Sync:packages/client/tests/unit/hooks/usePlayerData.test.ts(172 lines)packages/shared/src/systems/shared/character/__tests__/PrayerSystem.sync.test.ts(114 lines)packages/client/tests/e2e/prayer-sync.spec.ts(118 lines)
packages/client/tests/unit/EquipmentPanel.test.tsx(204 lines)packages/client/tests/e2e/panels.spec.ts(updated with paperdoll tests)
Test Coverage
Total Tests: 1,569 passing, 85 skipped New Coverage:- Prayer cache preservation during hydration
- PLAYER_SPAWNED re-hydration
- Finite number guards for prayer points
- PLAYER_REGISTERED → PLAYER_JOINED sync flow
- Equipment paperdoll rendering
- Equipment slot interactions
- Mobile paperdoll layout
- Portrait stability during equipment changes
Breaking Changes
None
All changes are backward compatible. Existing layouts will be migrated to schema v17 automatically (clears old layouts to include Spells tab).Configuration
No New Environment Variables
All changes use existing configuration.Known Issues
Equipment Portrait Performance
Issue: Equipment portrait creates second WebGPU context, which may cause context loss on lower-end GPUs. Workaround: Portrait only renders when equipment panel is visible. Dispose when panel is closed. Future Fix: Consider sharing main game renderer via render-to-texture or readPixels.Tab Persistence Memory Usage
Issue: All tabs mounted simultaneously increases memory usage. Workaround: Heavy components should pause expensive operations when tab is hidden. Future Fix: Lazy-mount tabs on first activation, keep alive after first visit.References
Pull Requests
- PR #1090 - Fix prayer login sync
- PR #1088 - feat(ui): comprehensive UI panel upgrade
- PR #1089 - Fix/equipment panel cross player leak
- PR #1087 - fix(client): inventory UI fixes for firemaking and targeting mode
Related Documentation
- Panel Layout Constants
- CursorTooltip Component
- Optimistic UI Patterns
- Equipment Visual System
- Prayer System
- Combat System
Credits
Contributors:- @SYMBaiEX - UI panel redesign, equipment portrait, quest theme improvements
- @dreaminglucid - Prayer sync fix, equipment leak fix, inventory optimizations, combat rotation fix
- Multiple rounds of thorough code review ensuring quality and correctness
- Comprehensive test coverage for all major changes
- Performance profiling and optimization