ModuleSystem API

Estimated reading time: 3 minutes

Overview

ModuleSystem handles module registration, dependency ordering, and lifecycle scheduling. Most content mods should not register framework modules—use addonPlugin script entries instead. Only use maplebirch.register when you intentionally extend internal framework capabilities.

For architecture and boot flow see Core Architecture.


Registering modules

register(name, module, dependencies?)

Registers a module (delegates to ModuleSystem.register).

ParameterDescription
nameModule name
moduleModule object with optional lifecycle methods; module.dependencies merges with the third argument
dependenciesOptional dependency name array
  • @return booleantrue on success; false if the name already exists.

Extension modules get a source (owning mod name) recorded automatically when AddonPlugin loads your module scripts—no fourth source argument or runWithSource wrapper required.

maplebirch.register("myModule", {
  Init() {
    console.log("initialized");
  },
});

maplebirch.register(
  "myModule2",
  {
    dependencies: ["tool"],
    Init() {
      console.log("depends on tool and npc");
    },
  },
  ["npc"],
);

get(name)

Returns a registered module instance.

const npcModule = maplebirch.get("npc");

dependencyGraph

Dependency metadata per module:

FieldDescription
protectedProtected module (cannot be turned off in the disable UI)
mountedPart of the core mount list
earlyMounted during pre-initialization
dependenciesDirect dependencies
dependentsModules that depend on this one
allDependenciesTransitive dependencies
stateCurrent state (e.g. 'MOUNTED')
sourceOwning mod name (extension modules)
const graph = maplebirch.dependencyGraph;
console.log(graph.npc);

Exposed modules (EXPOSED)

Set exposed: true on the module object. After registration the state is EXPOSED and the object is mounted at maplebirch[name].

maplebirch.register("myApi", {
  exposed: true,
  hello() {
    return "Hello";
  },
});

maplebirch.myApi.hello();
EXPOSED behavior

Exposed modules do not run the normal lifecycle (preInit / Init / loadInit / postInit) and do not appear in the disable UI. The dependency graph treats EXPOSED modules as satisfied dependencies. Registration fails if maplebirch[name] is already taken.


Module states

StateValueDescription
REGISTERED0Registered, waiting for init
MOUNTED1Main initialization finished
ERROR2Initialization failed
EXPOSED3Exposed on the maplebirch root
DISABLED4Disabled, skipped

Pre-init completion is tracked separately in an internal preInitialized set; modules move to MOUNTED after main init.


Lifecycle methods

MethodWhenPurpose
preInit()After afterInjectEarlyLoad, IndexedDB/logging readyEarly setup (no setup / V yet)
Init()First normal-game :passagestartMain init (setup and V available)
loadInit()After loading a saveRestore save-related state
postInit()Every passage start, after Init or loadInitPer-passage refresh
class MyModule {
  dependencies = ["tool"];

  async preInit() {
    this.cache = new Map();
  }

  async Init() {
    this.setup();
  }

  async loadInit() {
    this.restoreFromSave();
  }

  async postInit() {
    this.refreshPassageState();
  }

  setup() {}
  restoreFromSave() {}
  refreshPassageState() {}
}

maplebirch.register("myModule", new MyModule(), ["npc"]);

Dependency rules

  1. A module initializes only after all dependencies are satisfied (transitive closure applies).
  2. EXPOSED modules count as satisfied dependencies.
  3. If a dependency is ERROR or DISABLED, dependents do not initialize.
  4. Circular dependencies are detected at registration time.

Notes

  1. Prefer script over framework module registration for content mods (v3.2.5 simplified registration—see changelog).
  2. Use mod-prefixed names to avoid clashes with built-ins or other mods.
  3. A failed module does not block others.
  4. Protected modules (protected) cannot be disabled from the UI; extension modules remain toggleable when source is tracked by the framework.