ASAR Modding Guide

Unlike the official version of shapez, the Community Edition uses a different approach to load mods. It is commonly called "ASAR modding", due to the mod files being ASAR archives. The mod loader is still quite unpolished, but is already usable enough for basic mods.

ASAR mods consist of a mod.json mod manifest, containing the basic mod metadata, the mod code and any additional assets. Mods are loaded as ES modules, so imports and top-level await can be used in the code.

Adding mod.json schema to VSCode

VSCode and its forks support autocomplete and basic verifying for JSON files using JSON Schema. A schema for mod.json manifests is temporarily available on skimnerphi.net and can be added to VSCode settings file:

"json.schemas": [
    {
        "fileMatch": ["/mod.json"],
        "url": "https://skimnerphi.net/asar_schema_v0.json"
    }
],

Creating a new mod

Pick or create an empty directory that will house all mod files to be loaded. For the simplest mod, you will need just the mod manifest and a single source code file.

Example mod manifest

{
    "format": 1,
    "id": "my-mod",
    "entry": "mod.js",
    "name": "My Mod",
    "description": "My first awesome mod.",
    "version": "0.0.1",
    "authors": [{ "name": "me", "website": "https://example.com" }],
    "savegameResident": false, // = mod can be added and removed at will
}

The manifest file must be always called mod.json and located in the root of your mod directory. entry field tells shapez CE what file exports the mod class. More fields are available — check the JSON Schema or shapez CE source code for details.

Mod class definition

export default class extends shapez.Mod {
    init() {
        alert("Hello, world!");
    }
}

(WIP) Getting shapez autocompletion

A package containing type definitions for the shapez global object are currently available on skimnerphi.net. To make use of the definitions, do the following:

  1. Install the shapez-ce-types package:
    npm i --save-dev https://skimnerphi.net/download/shapez-ce-types-0.0.0.tgz
    
  2. Ensure TypeScript knows about these type definitions by including this comment anywhere in your project:
    /// <reference types="shapez-ce-types" />
    

Please note that the package URL may change in the future. As this package contains types that are automatically generated from shapez CE source code, the type definitions may be partially incorrect or incomplete.

(WIP) Loading additional assets

Unlike modding in the official version in the game, assets do not have to be inlined as base64 URLs in the source file (even though this is still an option) — they can be loaded using the virtual mod:// protocol instead. While the utilities for this approach are still not finished, there are some helper functions already:

this.modInterface.addStylesheet("cool_styles.css");

const cat = this.modInterface.resolve("assets/cat.webp");
// cat = "mod://your-mod-id/assets/cat.webp"

All paths in these functions are resolved relative to mod directory root, not the current file.

Resolving files without ModInterface

In places where an instance of the "mod interface" is not available, import.meta.url can be used instead. This approach can resolve relative paths as well:

const cat = new URL("../assets/cat.webp", import.meta.url);
// import.meta.url = "mod://your-mod-id/src/images.js"
// cat = "mod://your-mod-id/assets/cat.webp"

Dynamic metadata

Some fields of the metadata may be modified before the mod is fully constructed:

const modNames = ["Awesome Mod", "Cool Mod"];

export default class extends shapez.Mod {
    constructor(metadata, ...args) {
        const name = shapez.randomChoice(modNames);
        super({ ...metadata, name }, ...args);
    }
    // ...
}

Not all fields may be changed to achieve desired effect, this is mostly useful for human-readable mod information fields.

Asynchronous mod initialization

shapez CE will await initialization of a mod if the init() method returns a thenable. This can be used to e.g. load additional assets, establish network connections, create and initialize Storage instances or wait for other external events.

async init() {
    // Storage API is WIP. Do not expect this to stay correct
    const storage = new shapez.Storage(this.app, "mod/" + this.id);
    await storage.initialize();

    const file = "boot_counter.bin";
    const counter = await storage.readFileAsync(file).catch(() => 0) + 1;
    await storage.writeFileAsync(file, counter);

    alert(`Game booted ${counter} times!`);
}

Loading a mod for development purposes

shapezio --load-mod="/path/to/mod/directory" --watch --dev

Packaging a mod for distribution

npx @electron/asar p "/path/to/mod/directory" "/path/to/output.asar"

Both packaged and unpackaged mods will be loaded from shapez-ce/mods (user data directory) unless the --safe-mode switch is passed. Only files and directories with names ending in .asar are discovered automatically.

(WIP) Looking for errors

Currently shapez CE does not display most mod errors anywhere. When a mod fails at the parsing step, debugging may be extremely confusing.

To check if your mod has any errors, inspect the following field using developer console:

shapez.MODS.mods.get("your-mod-id").mod.errors;

Using the built-in debugger

shapez CE makes use of the standard Chromium developer tools, which include a code editor and debugger ("Sources" tab). Developer tools are only enabled when running shapez CE with the --dev command line switch. The source code editor can modify code without reloading the page or resetting state, which is useful for experimenting.

Assuming your mod code was successfully fetched and parsed, the mod source code will appear in the sources tree. If no entries appear with your mod ID, check for any early errors as described above. If the mod does not appear in the mod list as an ErroredMod, its manifest (mod.json) might be invalid. Currently manifest validation errors are only logged to stderr, so they might not be as easy to see.

Linking developer tools to local files

As the mod loader does not transform any mod files, it is possible to create a mapping between loaded source code and files on the disk. In order to do this, switch to the Filesystem tab of the sources tree, then click "Add folder to workspace". Choose the directory that contains mod.json, then reload the game. Mod source code files on the Page tab should get a green dot on their icons, as long as they are loaded without errors.

Once local files are added to the developer tools workspace, editing JavaScript code and stylesheets will both apply changes to the page and save them to the disk. Therefore, it is not recommended to use this feature along with the --watch command line switch.