-
Notifications
You must be signed in to change notification settings - Fork 1
Mod System
SparkEngine provides a mod loading and management system for user-created content. Mods can include scripts, assets, entity definitions, and configuration overrides, loaded at runtime with dependency resolution.
Source: SparkEngine/Source/Engine/Modding/ModSystem.h
| Class | Responsibility |
|---|---|
ModSystem |
Discovers, loads, manages, and persists mod configuration |
ModInfo |
Metadata for a single mod (name, author, version, dependencies) |
MyMod/
+-- mod.json (metadata: name, version, author, dependencies)
+-- Scripts/ (AngelScript files)
+-- Assets/ (meshes, textures, sounds)
+-- Data/ (JSON config overrides)
+-- Preview.png (mod preview image)
enum class ModState {
Available, // Found on disk, not loaded
Loading, // Currently being loaded
Active, // Loaded and active
Error, // Failed to load
Disabled // Explicitly disabled by user
};ModSystem mods;
// Scan for available mods
size_t found = mods.ScanForMods("Data/Mods/");
// Enable and load mods
mods.EnableMod("weapon_pack_01");
mods.EnableMod("custom_maps");
mods.LoadEnabledMods(); // Loads in dependency order
// Check mod status
if (mods.IsModActive("weapon_pack_01")) {
// Mod content is available
}// Set explicit load order
mods.SetLoadOrder({"base_mod", "weapon_pack_01", "custom_maps"});
// Check if dependencies are satisfied
if (mods.AreDependenciesMet("custom_maps")) {
mods.LoadMod("custom_maps");
}mods.OnModLoaded([](const std::string& modId) {
LOG("Mod loaded: " + modId);
});
mods.OnModUnloaded([](const std::string& modId) {
LOG("Mod unloaded: " + modId);
});Save and load the user's mod configuration (enabled/disabled state, load order):
mods.SaveConfig("Data/Config/mods.json");
mods.LoadConfig("Data/Config/mods.json");The mod.json file is the manifest for every mod. All fields are described below:
{
"id": "weapon_pack_01",
"name": "Weapon Pack: Volume 1",
"version": "1.2.0",
"engineVersion": ">=0.9.0 <2.0.0",
"author": "StudioName",
"authorUrl": "https://example.com",
"description": "Adds 12 new weapons with custom animations and sounds.",
"category": "gameplay",
"tags": ["weapons", "combat", "fps"],
"previewImage": "Preview.png",
"dependencies": [
{ "id": "base_content", "version": ">=1.0.0" }
],
"conflicts": ["incompatible_mod_id"],
"loadPriority": 100,
"scripts": [
"Scripts/WeaponInit.as",
"Scripts/WeaponLogic.as"
],
"assetOverrides": true,
"dataOverrides": [
"Data/weapons.json",
"Data/balance.json"
],
"permissions": [
"filesystem:read",
"network:none",
"engine:entities",
"engine:audio"
],
"maxSizeMB": 500,
"license": "MIT"
}| Field | Type | Description |
|---|---|---|
id |
string | Unique identifier (lowercase, alphanumeric + underscores) |
name |
string | Display name shown in the mod manager UI |
version |
string | Semantic version (major.minor.patch) |
author |
string | Author or organization name |
| Field | Type | Default | Description |
|---|---|---|---|
engineVersion |
string | "*" |
Semver range of compatible engine versions |
description |
string | "" |
Long description for the mod browser |
category |
string | "misc" |
One of: gameplay, cosmetic, map, total_conversion, tool, misc
|
tags |
string[] | [] |
Searchable tags for the mod browser |
dependencies |
object[] | [] |
Other mods that must be loaded first |
conflicts |
string[] | [] |
Mod IDs that cannot be active simultaneously |
loadPriority |
int | 100 |
Lower values load first; ties broken alphabetically |
permissions |
string[] | all | Sandbox permission whitelist (see below) |
maxSizeMB |
int | 1024 |
Advisory size limit for distribution |
license |
string | "" |
License identifier (SPDX format recommended) |
Mods execute in a restricted sandbox to protect the player's system and the engine's integrity.
- Mods can only read files within their own directory and the shared
Data/directory. - Mods cannot write to any location outside
Data/Mods/<mod_id>/UserData/. - Attempts to access paths outside the sandbox throw a
ModSecurityExceptionand are logged.
AngelScript mod scripts run with a restricted API surface:
- Allowed: Entity creation/destruction, component access, audio playback, UI widget creation, event subscription, math/utility functions.
- Blocked: Direct filesystem I/O, network sockets, OS process spawning, raw memory access, engine internals (renderer, physics solver internals).
- Execution limits: Scripts are subject to an instruction count limit per frame (default 1,000,000 instructions). Exceeding the limit pauses the script and logs a warning.
ModSandbox sandbox;
sandbox.SetInstructionLimit(1'000'000);
sandbox.SetMemoryLimit(64 * 1024 * 1024); // 64 MB per mod
sandbox.AllowPermission(ModPermission::Entities);
sandbox.AllowPermission(ModPermission::Audio);
sandbox.DenyPermission(ModPermission::Network);| Permission | Description |
|---|---|
filesystem:read |
Read files in the mod directory and shared data |
filesystem:write |
Write to the mod's UserData/ directory |
network:none |
No network access (default) |
network:http |
HTTP GET/POST to whitelisted domains |
engine:entities |
Create, destroy, and modify entities and components |
engine:audio |
Play sounds and music |
engine:ui |
Create custom UI panels and widgets |
engine:input |
Register custom input bindings |
Mods use semantic versioning (semver) for both their own version and engine compatibility ranges:
- Major version bump: Breaking changes to the mod's public API or data formats.
- Minor version bump: New features, backward-compatible additions.
- Patch version bump: Bug fixes only.
The engineVersion field in mod.json specifies which engine versions the mod supports using npm-style range syntax:
| Range | Meaning |
|---|---|
">=1.0.0 <2.0.0" |
Any 1.x release |
"~1.2.0" |
>= 1.2.0 and < 1.3.0 |
"^1.2.0" |
>= 1.2.0 and < 2.0.0 |
"*" |
Any engine version (not recommended) |
If the running engine version is outside the specified range, the mod is flagged with a compatibility warning but can still be force-loaded by the user.
When two mods declare a conflict (via the conflicts array) or both override the same asset/data file, the mod system applies conflict resolution:
-
Explicit conflicts: If mod A lists mod B in its
conflictsarray, enabling both simultaneously shows a warning in the mod manager. The user must choose which to keep active. -
Asset conflicts: When two mods override the same base asset, the mod with the higher
loadPriority(lower numerical value) wins. The losing override is silently discarded. - Data merge conflicts: For JSON data overrides, the engine attempts a deep merge by default. Array fields are concatenated; object fields are merged recursively. If a key appears in both, the higher-priority mod's value wins.
- Script conflicts: If two mods register the same script hook or entity type name, the second mod's registration fails and logs an error.
SparkEngine provides a Workshop integration layer for publishing and subscribing to mods:
// Upload a mod to Steam Workshop
WorkshopUploader uploader;
uploader.SetModPath("Data/Mods/weapon_pack_01/");
uploader.SetTitle("Weapon Pack: Volume 1");
uploader.SetDescription("12 new weapons...");
uploader.SetPreviewImage("Data/Mods/weapon_pack_01/Preview.png");
uploader.SetTags({"weapons", "combat"});
uploader.SetVisibility(WorkshopVisibility::Public);
uploader.Upload([](WorkshopResult result) {
if (result.success) LOG("Published: " + result.workshopId);
});
// Download subscribed mods at startup
WorkshopManager workshop;
workshop.SyncSubscribedMods("Data/Mods/"); // Downloads/updates subscribed modsThe Workshop manager synchronizes subscribed items on startup and can optionally check for updates periodically during gameplay.
# Use the mod scaffolding tool
SparkModTool create --id my_mod --name "My First Mod" --author "PlayerName"
# Creates Data/Mods/my_mod/ with mod.json, empty Scripts/, Assets/, Data/ directories- Place AngelScript files in
Scripts/, assets inAssets/, data overrides inData/. - Run the game with
--mod-dev my_modto enable hot-reload for the mod under development. - Use the in-editor Mod Inspector panel to view loaded state, errors, and performance stats.
# Validate mod structure and metadata
SparkModTool validate --path Data/Mods/my_mod/
# Run the automated mod test suite
SparkModTool test --path Data/Mods/my_mod/ --sandbox strict# Package for distribution (creates .sparkmod archive)
SparkModTool package --path Data/Mods/my_mod/ --output Releases/my_mod_1.0.0.sparkmod
# Publish to Steam Workshop
SparkModTool publish --path Data/Mods/my_mod/When a mod provides an asset with the same relative path as a base game asset, the mod's version takes precedence. Priority is determined by load order:
Base Game Assets (lowest priority)
↑ Mod A assets (loadPriority 100)
↑ Mod B assets (loadPriority 50) ← wins over Mod A if both override same asset
↑ Mod C assets (loadPriority 10) ← highest priority
Lower loadPriority values indicate higher priority. The asset pipeline resolves overrides at load time by searching mod directories in priority order before falling back to the base game.
If the base game has Assets/Weapons/pistol.fbx and a mod at Data/Mods/weapon_pack_01/Assets/Weapons/pistol.fbx, the mod's pistol mesh is used whenever Weapons/pistol.fbx is requested through the asset system.
When running with --mod-dev <mod_id>, the engine watches the mod directory for file changes:
| File Type | Hot-Reload Behavior |
|---|---|
.as (AngelScript) |
Script is recompiled and re-executed; existing entity instances are patched |
.json (Data) |
Data is re-parsed and merged; affected systems are notified via events |
.fbx, .obj (Mesh) |
Mesh is reimported; existing instances update on next frame |
.png, .jpg, .dds (Texture) |
Texture is reloaded into GPU memory; material references update automatically |
.wav, .ogg (Audio) |
Audio buffer is replaced; currently playing sounds finish before switching |
mod.json |
Metadata is refreshed; dependency graph is re-evaluated |
Hot-reload latency is typically under 500ms for scripts and textures, and 1--3 seconds for mesh assets depending on complexity.
The mod system includes built-in profiling to identify poorly performing mods:
const ModProfile& profile = mods.GetProfile("weapon_pack_01");
float scriptMs = profile.GetAverageScriptTime(); // ms per frame
float memoryMB = profile.GetMemoryUsageMB(); // Current allocation
size_t entityCount = profile.GetEntityCount(); // Entities owned by mod
size_t assetCount = profile.GetLoadedAssetCount(); // Assets currently loadedmod_profile <mod_id> # Show per-frame timing for a specific mod
mod_profile_all # Show aggregate timing for all active mods
mod_memory # Show memory usage per mod
mod_budget # Show resource budgets and current utilization
Each mod is allocated a default budget to prevent any single mod from degrading the game:
| Resource | Default Budget |
|---|---|
| Script CPU time | 2 ms per frame |
| Memory | 64 MB |
| Entities | 500 |
| Draw calls (additional) | 50 |
| Audio sources | 8 |
Exceeding a budget triggers a warning in the console and mod inspector. Persistent overages (5+ consecutive seconds) can optionally auto-disable the mod if mod_enforce_budgets 1 is set.
| Distribution Channel | Recommended Max | Hard Limit |
|---|---|---|
| Steam Workshop | 500 MB | 2 GB |
| Manual download | 200 MB | No limit |
.sparkmod archive |
500 MB | 4 GB |
- Compress textures using BCn/DXT formats rather than shipping uncompressed PNG/TGA.
- Share base assets by declaring dependencies on content packs rather than duplicating assets.
- Use LODs for custom meshes; provide at least 2 LOD levels for models over 5,000 triangles.
- Minimize script complexity -- avoid per-entity per-frame scripts when system-level logic suffices.
- Test with other popular mods enabled to catch conflicts early.
- Version your mod with meaningful semver bumps so dependent mods can specify compatible ranges.
- Provide a clear README inside the mod directory explaining installation, features, and known issues.
-
Use the sandbox validator (
SparkModTool validate) before every release to catch permission violations and structural errors.
mod_status # Show mod system status
mod_list # List all discovered mods with state
mod_enable <mod_id> # Enable a mod
mod_disable <mod_id> # Disable a mod
mod_reload <mod_id> # Unload and reload a specific mod
mod_reload_all # Reload all active mods
mod_info <mod_id> # Show detailed mod metadata
mod_conflicts # List all detected mod conflicts
mod_validate <mod_id> # Run validation checks on a mod
The mod system integrates with the VirtualFileSystem (VirtualFileSystem.h) which provides priority-based mount layering. Assets from mods override game assets, which override engine assets.
SparkPak (.spk) archives can be mounted as VFS providers via ArchiveResourceProvider, allowing mods and DLC to be distributed as single archive files instead of loose directories. See Asset Format Specifications for the archive format details.
Source: SparkEngine/Source/Engine/Modding/VirtualFileSystem.h, SparkEngine/Source/Engine/Modding/ArchiveResourceProvider.h
- Scripting with AngelScript — Mod scripts
- Asset Pipeline — Loading mod assets
- Asset Format Specifications — SparkPak archive format
- Save System — Persisting mod configuration
Published from 8fd2c5bdc5cd. Edit the canonical source in wiki/.
- Documentation
- Docs route
- Wiki index
- Guides
- Tutorials
- Samples
- Examples
- API Reference
- API route
- Reference
- Build Guide
- Dependencies
- FAQ
- Changelog
- Roadmap
- Contributing
- Code of Conduct
- Home
- FAQ
- Getting Started
- Quick-Start Tutorial
- Making Your First Game
- Making Your First Multiplayer Game
- Artist Workflow Guide
- Editor Walkthrough
- Migration Guide
- How SparkEngine Works
- Architecture Overview
- Engine Architecture Flowchart
- Creating a Game Module
- Game Modules (catalog)
- Entity Component System
- Rendering and Graphics
- Physics
- Cloth Simulation
- Audio
- Input System
- Camera System
- Scripting with AngelScript
- Visual Scripting
- AI and Navigation
- Animation
- 2D Systems
- Networking
- Dedicated Server
- Multiplayer Quick Start
- Area Server Architecture
- Scene Management
- Large World Support
- Collaborative Editing
- Coroutine System
- Event System
- Event Response System
- Job System
- UI System
- UI Layout Extensions
- Localization
- Dialogue System
- Destruction System
- Replay System
- Achievement System
- Loading System
- Mod System
- Content Delivery
- Tween System
- Memory Integrity
- Gameplay Systems
- Terrain and Procedural Generation
- Save System
- Persistence System
- Day Night Cycle and Weather
- Cinematic Sequencer
- Runtime Prefabs
- SparkEditor
- Editor Tutorials
- SparkConsole
- SparkDaemon
- Shader Pipeline
- Asset Pipeline
- Asset Validation
- Asset Migration
- Game Packaging
- Online Services
- DataTable System
- Loot and Crafting System
- CSG System
- Font System
- Timer Manager
- Movie Render Pipeline
- HLOD and World Partition
- Remote Debug System
- Selection Manager
- Asset Dependency Graph
- Editor Automation
- File Watcher
- Project Templates
- System Requirements
- VR Support
- Mobile Platform
- Accessibility
- Platform Input
- Cross-Compilation: Wine Testing
- RHI Abstraction Layer
- D3D11 Backend
- D3D12 Backend
- Vulkan Backend
- OpenGL Backend
- Metal Backend
- DXR Raytracing
- Hybrid Ray Tracing
- Upscaling (DLSS/FSR)
- Render Graph
- Shader Graph
- GPU Particles
- GPU-Driven Rendering
- Volumetric Fog
- Volumetric Clouds
- Global Illumination
- Virtual Texturing
- Water Rendering
- Clustered Lighting
- Material System
- Post-Processing
- Shadow System
- Particle System
- Decal System
- Sky and Atmosphere
- Foliage System
- Mesh Shaders
- Neural Rendering
- Configuration Reference
- Performance Tips
- Benchmark Framework
- Threading Model
- Memory Safety
- Memory Management Patterns
- Build System and CMake Modules
- Profiler and Debugging
- Performance Profiling Guide
- Telemetry System
- Golden Image Testing
- Utilities
- Testing
- Codebase Statistics
- Codebase Health
- Error Handling Patterns
- Hot Reload Overview
- Troubleshooting
- Contributing
- Workflow Patterns
- Build Optimizations
- CI Reproducible Builds
- GitHub API and PR Checks
- Git Rebase Conflicts
- Clang-Format
- Code Quality Violations
- AI Bloat Pattern
- MinGW + Wine Cross-Compilation
- Live Editor Testing
- Engine & Renderer Landscape
- DuetOS Portability Catalog
- Five-Engine Analysis
- Eleven-Engine Analysis
- ThorVG / Unity Graphics Analysis
- Advanced Techniques Catalog
- Third-Party Library Evaluation
- Engine Viability Evaluation
- Engine Feature Recommendations
- Project Recommendations
- Mac Compatibility Analysis
- Codebase Observations
- Codebase Bloat Audit
- Test Suite Audit
- Documentation Coverage Audit
- ThirdParty Dependencies Audit
- Load Test Baseline
- Gameplay Systems Status
- SparkGame Module Status
- Stub and Abandoned Features
- Memory Integrity System
- Memory Safety Evaluation
- Hardware Acceleration Systems
- Jolt Physics Integration
- GPU/CPU Separation Plan
- Daemon Services Architecture
- Reflection & Polymorphism Refactoring Plan
- SparkBuild In-Tree
- Wine No-JobSystem Breakthrough
- Wine Role and Fallback Tiers