Overview
Hyperscape uses manifest-driven design where game content is defined in TypeScript data files rather than hardcoded in logic. This enables content creation without modifying game systems.Design Philosophy
Separation of Concerns
Benefits
- Content creators can add items, NPCs, areas without deep coding
- Designers iterate quickly on balance
- Developers focus on systems, not data
- Modders can extend content easily
Asset Management
Local Development:- Assets are auto-downloaded during
bun installviapostinstallhook - Full clone with Git LFS (~200MB) for complete asset access
- Located at
packages/server/world/assets/
- Assets cloned via
ensure-assets.mjsscript (shallow, no LFS) - Manifests-only clone for server startup (models served from CDN)
- Prevents manifest divergence between repos
- Manifests were being committed directly to hyperscape repo to fix CI, causing divergence with assets repo
- Assets directory is now the single source of truth for all game content
- Eliminates dual-maintenance burden and sync issues
Manifest Files
All manifests are inpackages/server/world/assets/manifests/ (cloned from HyperscapeAI/assets):
Manifest Structure
NPCs (npcs.json)
NPC data is loaded from JSON manifests at runtime by DataManager:
NPC definitions are in
world/assets/manifests/npcs.json, not hardcoded in TypeScript.Items Directory
Items are now organized into separate JSON files by category for better maintainability:items.json format.
Tools (tools.json)
Tools include atool object specifying skill, priority, and optional bonus mechanics:
Tool Properties
The bonus roll is determined server-side to maintain determinism and prevent client/server desyncs.
Tools with
equipSlot: "weapon" can be equipped and used for combat. The tier system automatically derives level requirements from tier-requirements.json.Inventory Actions
Items can define explicitinventoryActions for OSRS-accurate context menus:
If
inventoryActions is not specified, the system falls back to type-based detection using item-helpers.ts.
Gathering Resources (gathering/)
gathering/woodcutting.json - Trees and log yields:
Mining Depletion (Updated March 2026): Depletion is now manifest-driven via
depleteChance:depleteChance: 1.0— Always depletes on success (regular ores)depleteChance: 0.0— Never depletes (rune essence rocks, OSRS-accurate)depleteChance: 0.125— 1/8 chance to deplete (special rocks)
ResourceSystem.processRespawns(). The legacy setTimeout-based respawn was removed in PR #1099.Gathering Resources
Resource gathering data is split by skill for better organization:Processing Recipes
Processing recipes are organized by skill:Station Configurations
World stations (anvils, furnaces, ranges, banks) are configured instations.json:
modelScale and modelYOffset properties control the visual appearance of stations in the world.
Stations (stations.json)
Defines crafting stations and interactive objects in the world:
- Anvil — Smith bars into equipment
- Furnace — Smelt ores into bars
- Range — Cook food with reduced burn chance
- Bank — Store items
DataManager
Smithing, cooking, and other processing skills use recipe manifests: Smelting Recipe (recipes/smelting.json):
recipes/smithing.json):
Tier Requirements (tier-requirements.json)
Defines level requirements by equipment tier:
Prayers (prayers.json)
Defines prayer bonuses, drain rates, and conflicts:
id— Unique prayer ID (lowercase, underscores, max 64 chars)name— Display namedescription— Effect description for tooltipicon— Emoji icon for UIlevel— Required Prayer level (1-99)category— “offensive”, “defensive”, or “utility”drainEffect— Drain rate (higher = faster drain)bonuses— Combat stat multipliers (attackMultiplier, strengthMultiplier, defenseMultiplier)conflicts— Array of prayer IDs that conflict with this prayer
Prayer bonuses are multipliers applied to base stats. A value of 1.05 means +5%, 1.10 means +10%.
Station Configuration (stations.json)
Defines world stations with 3D models:
Data Providers
The manifest system uses specialized data providers for efficient lookups:DataManager
TheDataManager class in packages/shared/src/data/DataManager.ts loads all manifests from JSON files and populates runtime data structures:
Manifest Loading
DataManager supports two loading modes:- Filesystem (server-side): Loads from
packages/server/world/assets/manifests/ - CDN (client-side): Fetches from
http://localhost:8080/assets/manifests/
Adding Content
Step 1: Choose the Right Manifest
Determine which manifest file to edit based on content type:- Items:
manifests/items/weapons.json,tools.json,resources.json,food.json, ormisc.json - NPCs/Mobs:
manifests/npcs.json - Gathering Resources:
manifests/gathering/woodcutting.json,mining.json, orfishing.json - Processing Recipes:
manifests/recipes/cooking.json,firemaking.json,smelting.json, orsmithing.json - Stations:
manifests/stations.json - World Areas:
manifests/world-areas.json
Step 2: Edit Manifest
Add your content following the existing structure. Use tier-based requirements for equipment:Step 3: Restart Server
Manifests are loaded at server startup. Restart to apply changes:Step 4: Verify
Check the game to ensure content appears correctly. Use the Skills panel to verify level requirements.Validation
Manifests are JSON files validated at runtime by DataManager:- Schema validation: Invalid fields logged as warnings
- Duplicate detection: Duplicate item IDs across files cause errors
- Reference checking: Invalid itemId/npcId references caught at runtime
- Atomic loading: Items directory loads all files or falls back to legacy format
Data Providers
The manifest system uses specialized data providers for efficient lookups:
These providers build optimized lookup tables at startup for fast runtime queries.
PrayerDataProvider Usage
Access prayer definitions at runtime:- Loaded by
DataManagerat startup - Validates prayer ID format, bonuses, and conflicts
- Builds optimized lookup tables by level and category
- Provides type-safe access methods
StationDataProvider Usage
Access station configurations at runtime:- Models loaded via
ModelCachewith transform baking modelYOffsetraises model so base sits on ground- Graceful fallback to blue box placeholder if model fails
- Shadows and raycasting layers configured automatically
Prayers (prayers.json)
The prayers.json manifest defines OSRS-accurate prayer abilities with stat bonuses and drain mechanics:
id— Unique prayer identifiername— Display name shown in prayer bookdescription— Effect description for tooltipicon— Icon asset path for prayer book UIlevel— Prayer level required to unlockcategory— Prayer type:offensive,defensive, orutilitydrainEffect— Drain rate (higher = faster drain)bonuses— Stat multipliers applied when activeattackMultiplier— Attack bonus (e.g., 1.05 = +5%)strengthMultiplier— Strength bonusdefenseMultiplier— Defense bonus
conflicts— Array of prayer IDs that cannot be active simultaneously
- Offensive — Attack and strength bonuses (Burst of Strength, Clarity of Thought)
- Defensive — Defense bonuses (Thick Skin, Rock Skin, Steel Skin)
- Utility — Special effects (future: Protect from Melee, Rapid Heal)
Prayer drain rates follow OSRS formulas. The
drainEffect value determines how quickly prayer points deplete while the prayer is active.Model Bounds (model-bounds.json)
The model-bounds.json manifest contains pre-calculated bounding box data for all 3D models in the game. This data is used for spatial calculations, collision detection, and tile-based placement:
bounds— Minimum and maximum coordinates of the model’s bounding boxdimensions— Calculated width (x), height (y), and depth (z) of the modelfootprint— Tile-based footprint for placement (width × depth in tiles)generatedAt— Timestamp of when bounds were calculatedtileSize— Base tile size used for footprint calculations (typically 1.0)
- Placement Validation — Ensure entities fit within available space
- Collision Detection — Fast AABB checks for physics and interactions
- Tile Occupancy — Calculate which tiles an entity occupies
- Spatial Queries — Optimize raycasting and proximity checks
Best Practices
- Use descriptive IDs:
bronze_swordnotsword1 - Follow naming conventions: snake_case for IDs
- Organize by category: Use the directory structure (items/, recipes/, gathering/)
- Test after changes: Verify in-game before committing
- Keep data flat: Avoid deep nesting in manifest structures
- Use tier system: Leverage TierDataProvider for equipment requirements instead of hardcoding
- Validate JSON: Use a JSON validator before committing to catch syntax errors
Manifest Loading Order
DataManager loads manifests in this order:- Tier requirements (
tier-requirements.json) - Needed for item normalization - Model bounds (
model-bounds.json) - Needed for station footprint calculation - Items (
items/directory oritems.jsonfallback) - NPCs (
npcs.json) - Gathering resources (
gathering/*.json) - Recipe manifests (
recipes/*.json) - Skill unlocks (
skill-unlocks.json) - Prayers (
prayers.json) - Stations (
stations.json) - Uses model bounds for footprints - World areas (
world-areas.json) - Stores (
stores.json)
Build-Time Manifests
Model Bounds Extraction
Themodel-bounds.json manifest is auto-generated during build:
- Scans
world/assets/models/**/*.glbfiles - Parses glTF position accessor min/max values
- Calculates bounding boxes and footprints at scale 1.0
- Writes to
world/assets/manifests/model-bounds.json
StationDataProviderloads this manifest at startup- Combines model bounds ×
modelScalefromstations.json - Calculates final collision footprint for each station type
- No manual footprint configuration needed