Skip to main content

Module Manager

moduleManager is the live registry of every module, both built-in and scripted. It's a top-level export and the same object the client itself uses, so your script can inspect other modules and watch their state.

import { moduleManager } from "@protohax/userscript";

You don't need it for your own module: read your options directly and use ctx. Use it when your module needs to work with another module, e.g. read its enabled state or options, or react when they change.

ModuleManager​

MemberTypeDescription
getModule(id)ModuleDefinition | undefinedLook up a live module instance by id.
getModules()MapIterator<ModuleDefinition>Iterate over all live module instances.
listenChanges(id, callback)() => voidCall callback whenever module id spawns or one of its options changes. Returns an unsubscribe function.
eventsEventEmitter<ModuleEvents>Lifecycle and configuration events.

ModuleEvents​

EventPayload
spawn(module: ModuleDefinition)
kill(module: ModuleDefinition)
option_update(module, option, path)
config_loaded()

ModuleDefinition​

What getModule and getModules return:

MemberTypeDescription
idstringUnique id. For a built-in's primary instance, this is its name. For a script module, it's script:<name>.
aliasstringDisplay alias set by the user.
triggerModeModuleTriggerModeToggle or WhileHeld.
stateOption<boolean>Enabled state, as an option. Read or set .value; use .subscribe to watch it.
get moduleInfoModuleInfoStatic identity: { name, category, mode }. category is a ModuleCategory, and is always Script for modules registered by a userscript.
get isPrimarybooleanWhether this is the instance created at registration, which the user can't remove. false for clones of an Atomic module.
options()MapIterator<Option<any>>Iterate over the module's options.
getOption(name)Option<any> | undefinedLook up one option by its display name (as shown in the menu).
subscribeOptions(callback)() => voidWatch every option in the module's tree, including nested children and enum values. callback receives (option, path). Returns an unsubscribe function.
import { moduleManager } from "@protohax/userscript";

// is the built-in "Kill Aura" module enabled right now?
// Built-in ids are display names, spaces included; script modules are
// namespaced, e.g. "script:My Module".
const killaura = moduleManager.getModule("Kill Aura");
if (killaura?.state.value) {
// ...
}

// react to another module toggling
const unsubscribe = moduleManager.listenChanges("Kill Aura", () => {
console.log("Kill Aura changed");
});

Reading and writing a module's options​

Use getOption(name) to get one option by its menu display name, or options() to iterate over all of them. Each returned Option<T> works just like your own: read .value, set .value (as if the user changed it in the menu), or call .subscribe(...) to watch it.

import { moduleManager } from "@protohax/userscript";

const killaura = moduleManager.getModule("Kill Aura");

// read/write a single option by name
const swing = killaura?.getOption("Swing"); // Option<any> | undefined
if (swing) {
console.log("swing enabled:", swing.value);
swing.value = false;
}

// or walk every option on the module
for (const opt of killaura?.options() ?? []) {
console.log(opt.name, "=", opt.value);
}
Values are untyped

getOption and options return Option<any>, because another module's options aren't known at compile time. That makes .value an any. Cast or check it where you use it if you need a specific type.

Nested options​

An option can hold a whole subtree: nested child options, drawers, and mode configurables. Two members on Option let you reach them:

MemberTypeDescription
childConfigurable | undefinedThe option's nested configurable. For an enum or mode selector, this follows the selected value's configurable.
getConfigurableValues()Configurable[]Every child configurable the option can reach. For a selector, one per choice.

Each Configurable also has options(), getOption(name) and subscribeOptions(...), so you can walk or watch a whole tree by display name:

const velocity = moduleManager.getModule("Velocity");

// the mode selector, and the selected mode's options
const mode = velocity?.getOption("Mode");
const ticks = mode?.child?.getOption("Ticks");
if (ticks) ticks.value = 0;

// or observe everything under the module at once
const unsub = velocity?.subscribeOptions((option, path) => {
console.log(path.map((p) => p.name).join("/"), "=", option.value);
});

You don't need any of this for your own module. The schema already gives you typed handles for the whole tree (see Options).

ModuleTriggerMode​

enum ModuleTriggerMode {
Toggle = "Toggle",
WhileHeld = "While Held",
}