Authoring Mods: Migrating a 1.50 Mod¶
Version 2.00 preserves important 1.50 gameplay use cases, but it does not reproduce the 1.50 loader or raw Lua API. Migration requires a package manifest and source changes. Loose CFG, LBX, and Lua files from 1.50 are not automatically compatible.
Structural comparison¶
| Area | Version 1.50 | Version 2.00 |
|---|---|---|
| Mod identity | Configuration metadata and paths without one enforced package boundary | Stable package ID, version, engine range, and strict mod.json5 |
| Installation | Loose profiles, scripts, and replacement files | data/mods/<id>/ or data/mods/<id>.zip |
| Activation | Launcher/profile selection, path accumulation, and loader settings | Explicit enable <id>; plus manifest dependencies |
| Ordering | Textual CFG order and mod_order |
Explicit enable priority, dependency graph, load_after, and registration order |
| Configuration | Flattened binary-era names and offsets | Typed hierarchical CFG paths generated from semantic schema |
| Assets and LBX | Per-file substitution parameters and DOS paths | Package data/ projection, whole-file VFS replacement, or NAME.LBX/NNN |
| Lua entry points | Configurable paths, pregame/postprocessor lists, MAIN0.LUA through MAIN9.LUA |
One package-root main.lua with private modules and semantic registrations |
| Lua state | Fresh state per script invocation | One isolated persistent state per active Lua package and session |
| Lua data model | Flat globals and copied raw native tables | Versioned roots, stable proxies, immutable payloads, and semantic methods |
| Mutation | Broad get_*, set_*, rget_*, and raw table replacement |
Validated event, rule, service, prototype, and object operations |
| Persistence | Results mainly written directly into native state; no per-mod storage schema | Engine-owned per-mod storage, explicit schema versions, and migrations |
| RNG | random, get_seed, and set_seed, including scripts that checkpoint and restore the native seed |
Shared engine RNG through game.rng; preserve intentional checkpoint/restore behavior around migrated random work |
| Failure | Earlier raw writes could survive a later script error | Strict staged activation; lifecycle failure stops the owner, with owner-specific staging where implemented |
| Multiplayer | Script timing around legacy autosave and synchronization | Exact source/order negotiation and an explicit network hook allowlist |
Convert the package first¶
- Choose a stable lowercase ID.
- Create
data/mods/<id>/mod.json5andconfig/main.cfg. - Move translated CFG assignments into the package CFG and includes.
- Move public assets below the package's
data/directory. - Add
main.luaandluametadata only if behavior still requires scripting. - Express required package relationships as dependencies or conflicts.
- Enable the package explicitly and test it as a directory before creating a ZIP.
Do not retain absolute DOS paths. Package CFG, Lua modules, migrations, and public assets all use safe package-relative paths.
Convert 1.50 configuration¶
Do not mechanically rename every old variable. Search the current configuration reference for the player-visible rule or data concept. Version 2.00 uses hierarchical names, records, arrays, enums, and masks rather than preserving flattened 1.50 spellings.
Examples of intentional structural replacements include:
mod_id,mod_name, andmod_desc-> manifestid,name, anddescription;mod_classandmod_order-> dependencies, conflicts,load_after, and explicit priority;scan_mods-> explicitenablestatements; and- per-LBX substitution variables -> VFS files or LBX entry overrides.
If no current configuration path represents a legacy behavior, do not invent an alias. Confirm whether the behavior belongs to CFG, Lua, native UI, or a missing semantic engine feature.
Convert Lua entry points¶
| 1.50 mechanism | Version 2.00 direction |
|---|---|
pregame_script |
Setup-time prototype work or a supported semantic new-game event. |
newgame_postprocessor_script |
Supported generated-object events or galaxy_generator. |
| Semicolon-separated paths | One main.lua, private modules, dependencies, and registrations. |
MAIN0.LUA through MAIN9.LUA |
No Lua migration; presentation and reports belong to native UI. |
Configurable main.lua filename template |
Fixed package-root main.lua. |
LUA full-access cheat |
No mod equivalent. Use supported lifecycle capabilities or separate developer tools. |
Move top-level one-shot behavior into the semantic event that owns it:
events.on("new_game.player_setup", function(event)
local player = game.players:get(event.player_id)
-- Apply supported initial player changes.
end)
Do not choose an event merely because its timing looks close to the old hook. Choose it because its documented payload, mutation capability, save behavior, and multiplayer ownership match the feature.
Convert raw APIs¶
| 1.50 API pattern | Version 2.00 direction |
|---|---|
get_conf |
Generated CFG reference, defines, or a domain-owned semantic object; no arbitrary Lua CFG path access |
get_game |
game roots and immutable callback payloads |
get_players, get_planets, get_stars, get_ships, get_leaders |
Stable collections using get(id) and deterministic all() |
set_*, add_*, del_* |
Validated properties and semantic methods in a lifecycle that permits them |
rget_*, rset_*, radd_*, rdel_* |
No raw-layout equivalent |
get_current_player_id |
No simulation Lua equivalent; process-local UI identity is not simulation state |
msgbox |
No simulation Lua equivalent; presentation belongs to native UI |
print |
Use structured diagnostics where available rather than player UI |
random, set_seed, and get_seed |
Use game.rng:integer, game.rng:set_seed, and game.rng:get_seed. The unsigned 32-bit seed belongs to the shared native stream. Preserve intentional checkpoint/restore behavior used by legacy mods such as VDC, including restoring the seed after errors, rather than replacing it with a mod-private stream. |
Legacy tables often use sparse, zero-based native IDs. Review every pairs, ipairs, rawlen, length operator, and numeric index. Version 2.00 collections return deterministic one-based sequences from all() and use semantic IDs for get(id).
Legacy mutation:
local planets = get_planets()
planets[planet_id].minerals = 4
set_planets(planets)
Semantic mutation:
local planet = game.planets:get(planet_id)
planet.minerals = defines.minerals.rich
The new operation validates identity, lifecycle, domain, and native invariants. There is no generic partial-table setter.
Convert language assumptions¶
- Replace
/with//after checking intended rounding. - Review negative division because
//floors. - Remove floating-point literals and exponentiation.
- Review overflow assumptions: 1.50 used signed 32-bit numbers; API version 1 uses signed 64-bit Lua integers and checked native conversions.
- Remove debug, IO, OS, clock, native module, and dynamic source-loading assumptions.
- Convert source text and literals to UTF-8 where necessary.
- Replace accidental table iteration order with a documented sequence.
Convert state and ordering¶
Move persistent mod-private history from globals into storage. Leave saved engine values in their engine-owned state instead of duplicating them. Add a storage schema version and migration chain before changing distributed storage structure.
Convert script-path and filename order into:
- declared package dependencies;
- explicit package priority;
- source registration order within
main.lua; and - deterministic iteration supplied by the API.
Do not rely on archive order, filesystem enumeration, hash-table order, or concatenated path order.
Known shipped-script dispositions¶
The version 2.00 API was checked against all Lua files shipped with the audited 1.50 patch:
- deposit adjustment uses
planet.generatedand stable planet objects; - player technology repairs use
new_game.player_setupand semantic technology methods; - homeworld changes use
new_game.home_system_generated; - RandomTech-style global tree changes use
new_game.technology_setup; - mirror variants use the exclusive
galaxy_generatorservice; and - interactive
MAIN*.LUAreports remain native-UI concerns rather than simulation Lua.
This mapping proves those categories can be migrated; it does not make the old source API-compatible.
The current Lua API was built first to cover these audited 1.50 use cases through safer semantic interfaces. It remains a minimal foundation. Mods that need unrelated game phases or subsystems may require new events, rules, services, prototypes, or stable object operations before they can be migrated cleanly.
Migration checklist¶
- Classify each feature as CFG data, VFS asset, prototype, event, rule, service, storage, or native UI.
- Create a strict package and explicit relationships.
- Translate configuration by semantics, not old spelling.
- Replace substitution paths with public VFS paths.
- Consolidate Lua setup into
main.luaand private modules. - Replace raw globals and tables with the implemented semantic API.
- Convert arithmetic and encoding assumptions.
- Move persistent history into versioned
storage. - Make ordering explicit.
- Test new game, save/load, migration, failure, package collisions, and multiplayer allowlisting.
- If an operation is absent, request a semantic API extension instead of exposing native layout.