Skip to content

The Helper

Mark Rogers edited this page Oct 18, 2026 · 1 revision

mpn.py sits in skills/madnesswright/scripts/. It needs Python 3.9 or newer and nothing else. The skills run it as python <madnesswright folder>/scripts/mpn.py; the commands below are run from a scratch folder as python mpn.py.

Every output on this page was printed by the mpn.py in the 0.1.1 release zip on Windows. The game folders it read were stand-ins built for the run. steam/ holds an app manifest for app 488860 and nothing else of Steam. game-data/ stands in for the game's LocalLow data folder. The mod folders are the repository's fixtures or small folders written for these pages. On Linux and macOS the paths in the output print with forward slashes.

Options and exit codes

python mpn.py --help
usage: mpn.py [-h] [--strict] [--data-dir DATA_DIR] [--steam STEAM]
              {where,backup,check-mod,deploy,log,scene-lint} ...

MADNESS: Project Nexus modding helper (one JSON object out)

positional arguments:
  {where,backup,check-mod,deploy,log,scene-lint}

options:
  -h, --help            show this help message and exit
  --strict              exit 1 when the result has errors
  --data-dir DATA_DIR   the game's LocalLow data folder (holds Mods/)
  --steam STEAM         Steam root folder

Exit code 0.

Command Does
where finds the Steam install, build, branch, data and Mods folders, the mod log, the enabled list, Player.log and the build it ran, and the newest SceneTools and ModTools builds
backup [--out DIR] zips the game's data folder (Getting started)
check-mod <dir> lints a mod folder (Mods and MadCards)
deploy <dir> [--force] checks the mod, then copies it into the Mods folder
log [--lines N] the build, the enabled list, what loaded, and the problem lines and tail of mods.log and Player.log (Troubleshooting); --lines defaults to 30
scene-lint <scene.unity> [--project DIR] checks a SceneTools scene (Levels with SceneTools)
  • Each command prints one JSON object on stdout and exits 0, problems or not. errors holds what must be fixed, warnings what to read, and notes what to do next.
  • --strict makes a command exit 1 when errors is not empty. It is an option of mpn.py itself, so it goes before the command name, like --data-dir and --steam.
  • An unexpected failure inside a command still prints one JSON object, with the exception's type and message in errors.
  • A mistyped command line is the exception: Python's argument parser prints usage text to stderr and exits 2, with no JSON. --strict after the command name is one:
python mpn.py check-mod broken-mod --strict
usage: mpn.py [-h] [--strict] [--data-dir DATA_DIR] [--steam STEAM]
              {where,backup,check-mod,deploy,log,scene-lint} ...
mpn.py: error: unrecognized arguments: --strict

Exit code 2.

where

Without options, where looks for Steam in the places Steam installs to: on Windows the SteamPath registry value and Program Files (x86)\Steam; on macOS ~/Library/Application Support/Steam; on Linux ~/.steam/steam, ~/.local/share/Steam and the Flatpak folder. It reads each root's libraryfolders.vdf for further libraries, then appmanifest_488860.acf. The data folder is %USERPROFILE%\AppData\LocalLow\Gibbing Tree\Madness Project Nexus on Windows, or the same path inside the Proton prefix (steamapps/compatdata/488860/pfx/drive_c/users/steamuser/...) on Linux. --steam DIR and --data-dir DIR (or the MPN_DATA_DIR variable) override them. Not tested for these pages: the search itself, since every run passed both options.

On the public branch, after a launch of the release build:

python mpn.py --steam steam --data-dir game-data where
{
  "steam_roots": [
    "steam"
  ],
  "install": {
    "fully_installed": true,
    "download": null,
    "library": "steam",
    "manifest": "steam\\steamapps\\appmanifest_488860.acf",
    "install_dir": "steam\\steamapps\\common\\Madness Project Nexus 2",
    "build_id": "25615280",
    "beta_key": "",
    "last_updated": "2026-10-17",
    "branch": "public (release)"
  },
  "data_dir": "game-data",
  "data_dir_exists": true,
  "mods_dir": "game-data\\Mods",
  "mods": [],
  "mods_log": null,
  "enabled": null,
  "player_log": "game-data\\Player.log",
  "game_version": {
    "status": "Release",
    "version": "1.11.a",
    "mods_allowed": false
  },
  "tool_outputs": {
    "scenetools": {
      "folder": "Madness Project Nexus SceneTools\\SceneObjs",
      "exists": true,
      "newest": [
        {
          "file": "arena_pg.sobj",
          "modified": "2026-10-17T18:30:00"
        }
      ]
    },
    "modtools": {
      "folder": "Madness Project Nexus ModTools\\MadObjs",
      "exists": false,
      "newest": []
    }
  },
  "notes": [
    "Not on the releasebeta branch. As of 2026-10 official modding (Workshop, SceneTools, ModTools, Harmony) runs only on releasebeta; back up first (mpn.py backup), then Steam > Properties > Betas.",
    "Player.log says the last launch ran a Release build (v1.11.a): the game loads mods only when this reads Beta. Relaunch after switching branch."
  ],
  "errors": []
}

Exit code 0.

branch reads public (release) when Steam has no beta key, and both notes say what to do. last_updated is the manifest's date in UTC. fully_installed turns false, with a note and a download count, while Steam is still downloading. tool_outputs lists up to five of the newest builds in each tool's output folder, next to the game's data folder.

After switching to releasebeta, deploying a mod and launching once:

python mpn.py --steam steam --data-dir game-data where
{
  "steam_roots": [
    "steam"
  ],
  "install": {
    "fully_installed": true,
    "download": null,
    "library": "steam",
    "manifest": "steam\\steamapps\\appmanifest_488860.acf",
    "install_dir": "steam\\steamapps\\common\\Madness Project Nexus 2",
    "build_id": "25615280",
    "beta_key": "releasebeta",
    "last_updated": "2026-10-17",
    "branch": "releasebeta"
  },
  "data_dir": "game-data",
  "data_dir_exists": true,
  "mods_dir": "game-data\\Mods",
  "mods": [
    "faster-reload",
    "green-hank",
    "old-mod"
  ],
  "mods_log": "game-data\\Mods\\mods.log",
  "enabled": {
    "file": "game-data\\Mods\\enabled.txt",
    "enabled": [
      "green-hank",
      "faster-reload"
    ]
  },
  "player_log": "game-data\\Player.log",
  "game_version": {
    "status": "Beta",
    "version": "2.20.a",
    "mods_allowed": true
  },
  "tool_outputs": {
    "scenetools": {
      "folder": "Madness Project Nexus SceneTools\\SceneObjs",
      "exists": true,
      "newest": [
        {
          "file": "arena_pg.sobj",
          "modified": "2026-10-17T18:30:00"
        }
      ]
    },
    "modtools": {
      "folder": "Madness Project Nexus ModTools\\MadObjs",
      "exists": false,
      "newest": []
    }
  },
  "notes": [],
  "errors": []
}

Exit code 0.

deploy

deploy runs check-mod, then copies the folder into <data folder>/Mods/<folder name>:

python mpn.py --data-dir game-data deploy green-hank
{
  "mod": "<scratch>\\green-hank",
  "errors": [],
  "warnings": [],
  "info": {
    "name": "Tough Agents, Green Hank",
    "cards": 2,
    "card_files": 2,
    "card_list": [
      {
        "file": "agents.txt",
        "type": "Character",
        "filename": "Nexus1_Agent",
        "root": "Nexus1_Agent"
      },
      {
        "file": "hank.json",
        "type": "Character",
        "filename": "Hank",
        "root": "Hank"
      }
    ],
    "bundles": [],
    "preview": false
  },
  "deployed": true,
  "dest": "game-data\\Mods\\green-hank",
  "next": "In game: gear icon (main menu, bottom left) > select the mod > ENABLED > Apply. Then run mpn.py log."
}

Exit code 0.

mod is printed as a full path (masked here as <scratch>). Deploying the same folder again is refused:

python mpn.py --data-dir game-data deploy green-hank
{
  "mod": "<scratch>\\green-hank",
  "errors": [
    "game-data\\Mods\\green-hank exists; pass --force to replace it"
  ],
  "warnings": [],
  "info": {
    "name": "Tough Agents, Green Hank",
    "cards": 2,
    "card_files": 2,
    "card_list": [
      {
        "file": "agents.txt",
        "type": "Character",
        "filename": "Nexus1_Agent",
        "root": "Nexus1_Agent"
      },
      {
        "file": "hank.json",
        "type": "Character",
        "filename": "Hank",
        "root": "Hank"
      }
    ],
    "bundles": [],
    "preview": false
  },
  "deployed": false
}

Exit code 0.

--force replaces the copy in Mods:

python mpn.py --data-dir game-data deploy green-hank --force
{
  "mod": "<scratch>\\green-hank",
  "errors": [],
  "warnings": [],
  "info": {
    "name": "Tough Agents, Green Hank",
    "cards": 2,
    "card_files": 2,
    "card_list": [
      {
        "file": "agents.txt",
        "type": "Character",
        "filename": "Nexus1_Agent",
        "root": "Nexus1_Agent"
      },
      {
        "file": "hank.json",
        "type": "Character",
        "filename": "Hank",
        "root": "Hank"
      }
    ],
    "bundles": [],
    "preview": false
  },
  "deployed": true,
  "dest": "game-data\\Mods\\green-hank",
  "next": "In game: gear icon (main menu, bottom left) > select the mod > ENABLED > Apply. Then run mpn.py log."
}

Exit code 0.

A mod with errors is not copied:

python mpn.py --data-dir game-data deploy broken-mod
{
  "mod": "<scratch>\\broken-mod",
  "errors": [
    "info.txt: curly quotes found; the game parses this file as strict JSON, so use straight quotes (examples pasted from the Google Docs carry curly ones)",
    "info.txt does not parse: Expecting property name enclosed in double quotes: line 1 column 2 (char 1)",
    "agents.json: not valid JSON (Illegal trailing comma before end of object: line 1 column 82 (char 81)). Standard .json must be strict; save shorthand as .txt",
    "the mod has no MadCards, no .mobj/.sobj bundles and no assembly; it would do nothing",
    "not deployed: fix the errors or pass --force"
  ],
  "warnings": [],
  "info": {
    "cards": 0,
    "card_files": 1,
    "card_list": [],
    "bundles": [],
    "preview": false
  },
  "deployed": false
}

Exit code 0.

--force also deploys a mod that has errors, so use it only to replace a copy. If the folder you deploy is already the one inside Mods, deploy copies nothing and says so in note.

What it writes

backup writes one zip, and deploy writes into the game's Mods folder. No other command writes anything, and none of them sends anything over the network.

Clone this wiki locally