Standalone Python app that scans your installed Road to Vostok mods, detects
load-order conflicts, and writes a recommended order into Metro Mod Loader's
mod_config.cfg.
Must have Metro Mod Loader installed (3.3.0+ recommended). Mod Configuration Menu recommended.
v1.1.0+ targets Metro Mod Loader's profile-based
mod_config.cfgformat (v3.1.0 and later). If you're still on MML v2, use v1.0.0 instead. The two formats aren't compatible and this version won't read the old one.
Two ways to run it:
- Prebuilt exe - download
RtV_LoadOrder_Editor.exefrom the Releases page and double-click. No Python needed. First launch is a bit slow while the onefile bundle unpacks to a temp dir. - From source - install Python 3.11+ (tick "Add to PATH" or use the MS
Store), then from the project folder:
pip install -r requirements.txt python main.py
On first launch the app tries to locate the mods folder automatically via
Steam's library data (checks all configured Steam library paths, so
multi-drive setups are covered). If it can't find the game it falls back to
a folder picker. Either way the path is saved and you won't be asked again
unless it becomes invalid.
On every launch a small loading window shows the scan progress; the main window appears fully painted when ready, instead of building piece by piece.
Typical flow: Refresh to scan → Analyze to get a recommended order →
adjust enabled state / priority as needed → Save to write
mod_config.cfg. Analyze shows a short progress overlay, then applies the
result. Use Missing Update Links if any mods are missing the [updates]
block needed for in-game update checks. Use Rename .zip → .vmz to
bulk-convert legacy .zip archives to the newer .vmz extension. Use
Fix MCM Configs if you run Mod Configuration Menu 2.9 or newer and some
mods have stopped remembering their settings.
Whichever button is the next thing worth pressing pulses, one at a time: Analyze first, then Fix MCM Configs if any mod needs it, then Save. Save stays disabled until those mod patches are done, because they rewrite mod archives and an order written first would describe files that are about to change. Hovering any button explains what it does.
Restore Backup lists those rolling backups by date and puts one back; whatever you had at the time is kept as the new newest backup, so a restore can itself be undone.
The ? button by the title opens a Help window (usage guide + links); it also pops up automatically the first time you run a new version.
Conflicts and load-order notes appear in a collapsible Notes & Warnings panel - it opens automatically after Analyze, and either the Notes button at the bottom-right or the – in the panel's own header toggles it (drag its top edge to resize; the size is remembered). Findings are grouped under MCM Config Fix (only when a mod is affected), Mod Conflicts and Required Load Order, and each entry leads with a severity icon (⛔ won't boot · ⚠ silent loss · 🔢 load order · ℹ info). When several big mods heavily overlap (e.g. three AI overhauls that can't coexist), a "keep one" card lists them with a Keep button each - pick one and the rest are disabled for you. Patching a mod from one of these cards leaves the panel as you had it, rather than reopening it and making you re-run Analyze.
Stale cfg entries are dropped automatically on load. These are left behind
when a mod is updated or removed outside the editor, e.g. via the in-game
loader, which leaves an old mod-id@<old-version> key pointing at a file
that no longer exists. Click Save to persist the cleaned cfg.
- Scan - opens each
.vmz/.zipin the mods folder and extracts:mod.txtmetadata:name,id,version,priority,[autoload]entries,[updates]/modworkshop=<id>,[dependencies] required=andoptional=, and[mod] provides=rename aliases if present. The opt-in sections[registry],[hooks], and[script_extend]are detected too- Every
.gdscript'sextends "res://Scripts/X.gd"base,class_namedeclaration, and per-functionsuper()usage take_over_path()targets - resolved three ways: literal string args,parent.resource_pathpatterns, and script-named callees (fallback). Only scripts that actually calltake_over_pathare flagged, not every.gdin a mod that happens to contain one somewhere- RTVModLib hook registrations (
.hook("stem-method", …)) and registry writes (lib.register/override/patchonlib.Registry.<KIND>, plus theregister_weapon/etc. aggregators) - Any reference to
res://ModConfigurationMenu/(soft dependency on MCM) - The full list of files shipped by each archive (for path-collision detection)
- Analyze - builds a constraint graph from:
- Function chains: a mod that overrides F with
super()must load AFTER any mod that overrides F withoutsuper(), otherwise the second mod is silently lost. Takeovers participate in this check too - Godot still walks theextendschain through atake_over_pathd script - Takeover ordering: a mod calling
take_over_path()onres://Scripts/B.gdreplaces B at that path, so any mod thatextendsB must load AFTER the takeover. Multiple mods taking over the same base are not a hard conflict - they chain through inheritance in load order; this is surfaced as an informational note, not a warning class_nameconflicts: two mods declaring the sameclass_name X, or a mod declaring one the base game already uses, is a hard conflict (Godot refuses to load the project). A mod thattake_over_paths a vanillaclass_namescript is flagged as a crash risk (Godot bug #83542)- Replace-hook collisions: two mods registering the same bare hook
(no
-pre/-post/-callbacksuffix) - only the first to load wins, the rest are silently rejected.-pre/-posthooks compose and are left alone - Registry conflicts: two mods
register/overrideing the same(registry, id)- or the same AI zone - and two modspatching the same field of the same entry (xEdit-style; different fields compose). Also warns when registry calls need a[registry]opt-in that's missing - Declared dependencies:
required=entries must be installed and load first, so a missing one is flagged and the order is constrained to put each dependent above its dependency.optional=entries only affect order and never block. Matching follows MML: case-insensitive,provides=rename aliases resolve to the mod providing them, and the loader's own ids (mml,modloader, …) always count as present - MCM soft dependency: mods referencing MCM must load after MCM
- Heavy-overlap clusters: when three or more mods all
take_over_paththe same base script and collide on many of its functions (e.g. several full AI overhauls), they're grouped into one "keep one" recommendation instead of a wall of per-function warnings. AI/spawn conflicts are also phrased in gameplay terms ("enemy AI", "enemy spawns") rather than raw script names - One mod replacing another mod's script: a mod that does
extends "res://<other mod>/X.gd"and installs itself at that path is a compatibility patch pinned to one version of the other mod. It keeps loading after that mod updates, and misbehaves silently. Loading another mod's.tresresource is the supported route and is not flagged - Also detects: duplicate mod IDs, duplicate autoload names, shared file
paths across archives (higher-priority archive wins at mount), and
archives that fail to scan (corrupt zip / unreadable
mod.txt)
- Function chains: a mod that overrides F with
- Recommend - topologically sorts the graph and assigns priority values
in steps of 5 to mods without a declared priority. Mods that declare
priority=Nin theirmod.txtare locked at that value, as are mods you have locked yourself - the difference being that a declared "load last" value gets bumped clear of the free mods, while a number you picked is used exactly as typed. A final pass guarantees every mod ends on a unique load number (MML breaks ties by name, which is unstable across renames), spreading them across the full valid range if needed. Only enabled mods are analysed - a disabled mod isn't loaded, so it can't conflict; mods it would clash with are flagged for you to disable rather than switched off automatically. - Fix cards - for problems diagnosed precisely enough to repair, a card
offers to correct the mod or turn it off. The list is short and explicit
rather than a heuristic; the editor never rewrites a mod on a guess.
Currently one entry: Faction Warfare's Mod Configuration Menu patch stores
the menu under the wrong property name (
MCM_Menu, where MCM declaresMCMMenu), so on MCM 2.9+ the settings screen draws but nothing responds - and every mod's page looks empty, not just its own. The original archive is kept as.vmz.bak-preshim, and the same card undoes the fix. The card is gated on that exact defect, not on "replaces MCM's menu code" - any other mod doing the same is flagged red in the list for you to disable, since the editor has no diagnosed repair for it. - Edit - manually adjust enabled state, priority value, or order. Each row
has a lock chip (left of the number) to pin its priority so Analyze
won't move it: blue = click to lock, gold = locked, greyed = locked by the
mod's author (a declared
priority=). Hovering a mod's name says which it is, and for an author-locked mod what number they asked for and whether that means load early or load late. Lock state is saved tosettings.json, and a lock you set is honoured by Analyze itself - the other mods are numbered and ordered around it, rather than renumbered and then overwritten. - Missing Update Links - lists mods whose
mod.txthas no[updates]/modworkshop=<id>block (needed for the in-game loader's update check). Paste the mod's ModWorkshop URL per row; the numeric ID is extracted,mod.txtis patched, and the.vmzis rewritten. The original archive is kept as.vmz.bak. - Rename .zip → .vmz - opens a checklist of every
.zipmod in the folder. Tick the ones to convert and click Rename - originals are copied to arenamed modssubfolder as backup, then the.zipfiles are renamed to.vmzin place. - Fix MCM Configs - MCM 2.9 changed where it looks for a mod's settings.
It now builds the folder name from the name the mod registers under, and
ignores the path the mod passes in. A mod written for 2.8 that uses two
different names for those saves to one file while MCM writes to another, so
its settings page opens normally but nothing you change there sticks. This
finds those mods and renames what they register under to match the folder
their settings already live in, so you keep the settings you had. It only
does anything on MCM 2.9+, and it skips any mod that uses that name for more
than its settings, since renaming it there could change how the mod behaves.
Each original is kept as
.vmz.bak-premcmpath. - Save - writes back to
%APPDATA%\Road to Vostok\mod_config.cfgusing MML's profile format ([profile.<active>.enabled]/[profile.<active>.priority], keyed bymod-id@version, orzip:<filename>for mods that declare no id at all). Only the active profile is edited, but the rest of the file is preserved exactly as found - other profiles, MML 3.3'sdep_ignoreand per-profile settings, and the modpack-managed profile slots all survive untouched. If the game has rewritten the file since you opened the editor, Save warns before overwriting. The previous file is rotated intomod_config.cfg.bak.1(up to 10 backups kept).
Detection is static - it can't catch runtime or version-mismatch breakage
(e.g. a mod that targets an older RtV release and crashes regardless of load
order). Scripts packed inside RTV.pck aren't cross-referenced yet, so
overrides of removed/renamed engine scripts may slip through.
| File | Purpose |
|---|---|
main.py |
Entry point |
rtv_editor/__init__.py |
Package marker + __version__ |
rtv_editor/gui.py |
customtkinter window, Help/overlay windows |
rtv_editor/paths.py |
Settings, AppData paths, mods-folder dialog |
rtv_editor/vmz_scanner.py |
Read archives + parse .gd overrides |
rtv_editor/analyzer.py |
Conflict graph + topological sort |
rtv_editor/mod_patcher.py |
Rewrite entries inside a .vmz - ModWorkshop update links, and the two known MCM repairs |
rtv_editor/config_io.py |
mod_config.cfg read/write, rolling backups, restore |
assets/RtV_LoE.ico |
App/exe icon (used by the release build) |
settings.json |
Auto-created on first run (in AppData) |
Nate Kartier found both bugs fixed in v1.5.0 by using the editor on a real install: the MCM 2.9 config-folder problem, and Analyze handing a locked mod's number to another mod. They also hand-patched a dozen affected mods, which is what pinned down the first one.
Every v* tag pushed to GitHub triggers a Windows build via
.github/workflows/release.yml, which publishes the exe and its SHA256 to the
Releases page.
- Check for updates - a button in the Help window that checks the GitHub Releases page and tells you when a newer version is available.
- Post-run log analysis - read the game's
godot.logafter a session to show the real load order, which overrides actually won, and any script errors - the one thing static analysis can't catch. - Cross-reference vanilla scripts to catch overrides of functions that no
longer exist. Not by reading
RTV.pckdirectly, which stores scripts as compiled bytecode: Metro Mod Loader already decompiles the ones it needs touser://modloader_hooks/vanilla/Scripts/, and 3.3+ writes a full script index alongside them. Reading those would also replace the hand-copied vanillaclass_namelist inanalyzer.py. - Test with more mods, fix any remaining bugs.