Skip to content

Installer

Tom_XV edited this page Sep 26, 2026 · 3 revisions

English | 日本語

This came in with core 1.1.0 (2026-09-15). You'll find the installer files in the framework's release zip, under installer/.

Every Drag'n Wash mod can ship the same installer: Install.exe for Windows and install-steamdeck.sh for the Steam Deck and Linux. They install BepInEx, Drag'n Wash ModFramework and your mod, and can uninstall them again. They follow the same rules for every mod, so one mod's install never breaks another's. Everything specific to your mod comes from a single file, mod-install.json.

If you're a player, you'll find Install.exe and install-steamdeck.sh at the top of a mod's release zip, next to mod-install.json. Extract the zip anywhere, then double-click Install.exe on Windows, or run bash install-steamdeck.sh in Desktop Mode on the Steam Deck or Linux. To remove the mod, run it again and choose Uninstall. You can also uninstall a mod in the game, from Options → Mods (see For players).

Your release zip

Install.exe                  from installer/ in the framework's release zip
install-steamdeck.sh         from installer/ in the framework's release zip
mod-install.json             yours
BepInEx/plugins/<YourMod>/...
README.md

From 1.5.0 your zip doesn't need to carry the framework. Name the ModFramework release you built against in mod-install.json (schema 2, below), and the installer fetches it from the framework's own GitHub release and checks it before installing. A zip that still carries BepInEx/plugins/DragNWash.ModFramework*/ and the preloader (schema 1, or schema 2 with the framework inside) installs as before.

Ship the installer files unchanged. Install.exe is built deterministically, so its hash stays the same across every mod that ships it, and so does the reputation antivirus software gives it.

mod-install.json

{
  "schema": 2,
  "name": "My Mod",
  "version": "1.2.0",
  "website": "https://github.com/me/mymod",
  "icon": "MyMod/icon.png",
  "plugins": ["MyMod"],
  "keep": ["MyMod/UserData"],
  "configFiles": ["com.example.mymod.cfg"],
  "choices": [
    {
      "id": "difficulty",
      "label": { "en": "Difficulty", "ja": "難しさ", "zh": "难度" },
      "config": { "file": "com.example.mymod.cfg", "section": "General", "key": "Difficulty" },
      "options": [
        { "value": "Normal", "name": "Normal" },
        { "value": "Hard", "name": "Hard" }
      ],
      "default": "Normal"
    }
  ],
  "framework": {
    "version": "1.6.0",
    "sha256": "2fb8862835c84a707f5f4c37ea01c7d439d434926dbb8fcebb063d37f4c7d25f",
    "size": 2326308,
    "needs": {
      "DragNWash.ModFramework": "1.2.0",
      "DragNWash.ModFramework.ToolWindow": "1.5.0"
    }
  }
}
Field
schema 2 (from 1.5.0), or 1 when your zip carries the framework and there's no framework block. An installer refuses a schema newer than it knows, with "Use the installer from the mod's release".
name, version, website Shown in the installer window. website must be https://.
plugins Your folders under BepInEx/plugins, as plain folder names. Never a DragNWash.ModFramework* folder.
icon Optional, from 1.6.0. A PNG or JPEG inside your payload (a relative path, about 512 KB at most). The installer and the Launcher show it while your mod is installed or updated; without it they draw a letter tile from your mod's name.
keep Paths inside your folders that hold the player's own data. They're kept when uninstalling, unless the player asks to remove everything.
configFiles Your files in BepInEx/config, removed when uninstalling.
choices Optional. Each one is a question in the installer, and the answer is written to a BepInEx config entry (file, section, key). You give label per language (en, ja, zh). default is an option value, or "ui-language" to pick the option whose value matches the system's language (ja, zh-Hans, pt-BR, ...). A value that's already in the config file always wins, so updating keeps what the player chose.
framework Schema 2. The ModFramework release your mod is built against: version, the sha256 and size of its zip (both are in the release's SHA256SUMS and on the release page), and needs, the lowest version of each DragNWash.ModFramework* plugin folder your mod uses.

Every path is checked, so nothing in the manifest can point outside your own folders and files. In the framework block, the version has to be digits and dots, the hash 64 lowercase hex digits, the size above 0 and at most 20 MB, and every name in needs has to start with DragNWash.ModFramework.

Where the framework comes from

When your zip has no framework, the installer fetches the release you pinned from https://github.com/TomXV/dragnwash-modframework/releases/download/v<version>/DragNWash.ModFramework-<version>.zip. It builds that address from the version alone, never from anything else in the manifest.

  • It asks first, every time a download is needed, and only then. The window says where it connects (github.com and release-assets.githubusercontent.com, plus BepInEx's GitHub when BepInEx is downloaded too), why, what gets sent (an ordinary HTTPS request with the User-Agent DragNWash.Installer/<version>; GitHub sees the IP address) and how the file is checked. Choose zip... takes a zip you downloaded yourself, checked the same way.
  • The download stops as soon as its size differs from size, and the file is only used when its SHA-256 matches sha256.
  • It installs the core, the preloader and only the libraries in needs, never Inspector, Overrides, Bridge or Graphs. A part is fetched when it's missing, below your minimum, or (for the core and the preloader) older than the pinned release; after that, a part is only replaced by a newer one, so a newer framework another mod brought stays.
  • When everything is already there, nothing is downloaded and no connection is made.
  • When a download fails, the window says why in plain words (no internet, GitHub not answering or limiting downloads, the release not published yet, a size or hash that doesn't match) and leaves the game folder as it was. If the installed framework already meets every minimum, it offers to install only your mod.

A draft release can't be downloaded, so publish the framework release you pin before you publish your mod.

What the installer does

Install and update

  1. Finds the game through Steam, or lets the player choose the folder. Stops if the game is running, and stops before changing anything when another mod loader is in the game folder (KrazenLabs' dnw-modloader, or any Doorstop setup that doesn't start BepInEx): only one loader can be installed per game folder.
  2. Installs BepInEx 5.4.23.5 if it's missing. The download is checked against a pinned SHA-256, and if it doesn't match, nothing is installed.
  3. Installs the framework: from your zip if it carries it, otherwise from the pinned release (see above). An older copy never replaces a newer framework another mod brought.
  4. Copies your folders over what's already there. An update never deletes files, so anything the player added stays.
  5. Writes the choices to your config file.
  6. Switches your mod back on if it was switched off on the Mods screen, and cancels an uninstall waiting in the game.
  7. Leaves a copy of mod-install.json in your first plugin folder, for the Mods screen and later installers.
  8. On the Steam Deck and Linux, sets executable_name in run_bepinex.sh and the launch option ./run_bepinex.sh %command%. It asks first, then closes Steam briefly to do this.
  9. On Windows (from 1.6.0), puts the Launcher into BepInEx\DragNWash.Installer with the framework, and, while Check for mod updates when the game starts is ticked, sets Steam's launch option to start the game through it. Steam rewrites its settings while it runs, so when Steam is open, a question comes up first: Close Steam for me, close it yourself, or install without the launch option. Only that one value in Steam's localconfig.vdf changes, and the file is copied to localconfig.vdf.dnw-backup first. Launch options you already had stay.

Every file an install replaces or deletes is copied to BepInEx\DragNWash.Installer\backup\<date_time> first. If a step fails halfway (a file in use, a folder that can't be written to), everything goes back the way it was. After a successful install only that install's backup is kept, and the log ends with where it is.

Uninstall

  1. Removes your folders, except keep (the player can choose to remove those too), and your configFiles.
  2. Removes the framework only when no other mod is left, and BepInEx/SaveHistory only when the player does not keep their data.
  3. Removes BepInEx only when the player asks and no other mod or patcher is left. On the Steam Deck the launch option is taken out when no mod is left.
  4. On Windows, when the framework goes, the launcher comes out of Steam's launch options first (asking to close Steam, as above), and BepInEx\DragNWash.Installer goes with it.

The window

From 1.6.0, Install.exe shows its window with WebView2, in the launcher's look: the logo, then one board at a time (setup, working, done), each step with its own picture. It's still one file: the WebView2 parts are inside it, and WebView2Loader.dll is written once to %LOCALAPPDATA%\DragNWash ModFramework\Installer and checked by its SHA-256 before every use; nothing is written next to Install.exe. Without the WebView2 runtime, or when the new window can't come up, the old window opens as before and its log says why. DNW_INSTALLER_CLASSIC=1 opens the old window on purpose.

The installers write a log. On Windows it's in the window, and on Linux it's in ~/.local/state/dragnwash-installer/installer.log.

Uninstalling in the game

The Mods screen has an Uninstall button for every mod except the framework and its libraries. The first press explains what will happen and names the mods that need this one, and the second press confirms. A mod can't be removed while the game is running, so the framework's preloader patcher removes it the next time the game starts, before any mod loads. Until then, Cancel uninstall takes it back.

What gets removed is the mod's folder under BepInEx/plugins (or its DLL, if it sits there directly). If the folder has a mod-install.json, the keep paths stay and the configFiles go. If it doesn't, the config file named after the plugin's GUID goes. BepInEx and the framework stay, so use the installer to remove those.

Command line

For testing, both installers can run without asking any questions:

Install.exe --install [--game-dir <folder>] [--choice <id>=<value>] [--launch-option on|off|keep]
Install.exe --uninstall [--game-dir <folder>] [--remove-data] [--remove-bepinex]
bash install-steamdeck.sh --yes --install [--game-dir <folder>] [--choice <id>=<value>] [--no-launch-option]
bash install-steamdeck.sh --yes --uninstall [--remove-data] [--remove-bepinex]

--bepinex-zip <file> uses a local BepInEx zip, which is still checked against the pinned SHA-256. --framework-zip <file> uses a local ModFramework zip, checked against sha256 and size the same way. --no-download never goes online: if BepInEx or ModFramework would have to be downloaded, it stops with nothing changed. --keep-framework installs only the mod and keeps the framework that's there. --install counts as agreeing to the download, as it does for BepInEx. --launch-option sets, takes out or leaves alone the launcher in Steam's launch options (on is the default for --install); the command line never closes Steam, so while Steam runs the launch options are left alone and the log says so. Install.exe --help lists everything.

Clone this wiki locally