Mod Scripts (modScript.lua)
How modScript.lua works — the entry point for your mod that controls which extensions persist across level changes.
Without a mod script, your extensions vanish every time the player switches maps. The mod script (modScript.lua) is loaded before anything else in your mod — it tells the engine which extensions should stay alive across level changes, career transitions, and map switches.
What is a Mod Script?
Every mod should have a modScript.lua file. The mod manager (core_modmanager) discovers and executes these files during initialization, before any extensions are loaded. This is where you:
- Register your extensions with
setExtensionUnloadMode - Call
loadManualUnloadExtensions()to load them
File Location
your_mod.zip (or mods/unpacked/your_mod/)
scripts/
your_mod_name/
modScript.lua <-- This file
lua/
ge/
extensions/
your_mod_name/
feature.lua <-- Your extensions
ui.lua
data.luaThe mod script lives at scripts/<your_mod_name>/modScript.lua. The game scans /scripts/ for all modScript.lua files recursively.
Basic Mod Script
-- scripts/mymod/modScript.lua
-- Register extensions that should persist across level changes
setExtensionUnloadMode("mymod_feature", "manual")
setExtensionUnloadMode("mymod_ui", "manual")
setExtensionUnloadMode("mymod_data", "manual")
-- Load all registered manual-unload extensions
loadManualUnloadExtensions()That's it. This ensures your extensions are loaded early and stay loaded when the player switches maps or enters/exits career.
How It Works Internally
The mod manager does the following when loading mod scripts:
- Finds all
modScript.luafiles in/scripts/(recursively) - Executes each one with
dofile() - During execution,
setExtensionUnloadMode(name, "manual")registers the extension name in a manual-unload list loadManualUnloadExtensions()callsextensions.load()on everything in that list- These extensions will NOT be unloaded during level transitions
The mod manager also temporarily wraps extensions.load() during modScript execution so that any extensions loaded directly also get marked as manual.
Real-World Example
Here's how a mod with multiple systems would set up its mod script:
-- scripts/mymod/modScript.lua
-- Core systems (always loaded)
setExtensionUnloadMode("mymod_core", "manual")
setExtensionUnloadMode("mymod_config", "manual")
-- Feature modules
setExtensionUnloadMode("mymod_economy", "manual")
setExtensionUnloadMode("mymod_missions", "manual")
setExtensionUnloadMode("mymod_vehicles", "manual")
-- UI extensions
setExtensionUnloadMode("mymod_phone", "manual")
setExtensionUnloadMode("mymod_hud", "manual")
-- Load everything
loadManualUnloadExtensions()The Extensions
-- lua/ge/extensions/mymod/core.lua
-- Extension name: mymod_core
local M = {}
local function onExtensionLoaded()
log('I', 'mymod', 'Core system loaded')
end
local function onExtensionUnloaded()
log('I', 'mymod', 'Core system unloaded')
end
M.onExtensionLoaded = onExtensionLoaded
M.onExtensionUnloaded = onExtensionUnloaded
return MNote: The extension itself does NOT call setExtensionUnloadMode. That's handled by the mod script.
Important Rules
:::danger[Critical Mistake]
Do NOT put setExtensionUnloadMode inside your extension. It must go in modScript.lua.
:::
Do NOT use onInit for setExtensionUnloadMode
-- WRONG - don't do this
M.onInit = function()
setExtensionUnloadMode(M, "manual")
end-- RIGHT - do this in modScript.lua instead
setExtensionUnloadMode("mymod_feature", "manual")
loadManualUnloadExtensions()The mod script runs before extensions are loaded, ensuring everything is set up in the correct order.
Always call loadManualUnloadExtensions()
Without this call, your setExtensionUnloadMode registrations don't actually load anything. This is the function that triggers the actual loading.
Extension names match file paths
Remember the naming convention:
lua/ge/extensions/mymod/feature.lua -> mymod_feature
lua/ge/extensions/mymod/ui/phone.lua -> mymod_ui_phoneUse these exact names in your setExtensionUnloadMode calls.
Mod Script vs Extension Loading
| Approach | When to Use |
|---|---|
| modScript.lua + setExtensionUnloadMode | Your main mod extensions that need to persist |
| extensions.load() at runtime | Dynamic loading (e.g., loading a feature only when needed) |
| loadModulesInDirectory() | Loading an entire folder of sub-modules |
Most mods should use the mod script for their core extensions. Use runtime loading only for optional features that don't need to be always active.
Discovery Order
- Game starts, loads
lua/ge/main.lua - Core extensions load (engine internals)
core_modmanagerinitializes- Mod manager scans
/scripts/*/modScript.lua - Each modScript executes (registers extensions)
loadManualUnloadExtensions()loads all registered extensions- Extensions receive
onExtensionLoadedcallbacks - Game is ready
Your mod script runs at step 4-6, which is early enough that your extensions are available before any gameplay starts.
Debugging Mod Scripts
If your mod script fails to load, check the console for:
[E] initDB.modScript - Failed to execute scripts/mymod/modScript.luaCommon issues:
- Wrong file path (must be in
scripts/<name>/modScript.lua) - Typo in extension name (must match the file path exactly)
- Missing
loadManualUnloadExtensions()call - Extension file doesn't exist at the expected path
See Also
- Getting Started - First mod setup
- Creating Extensions - Extension reference
- Module Loading - Loading folders of extensions
- Boot Sequence - Full startup order
- Mod Loading - How mods are discovered and mounted