Lifecycle

Plugin lifecycle

Plugins export free functions the host calls at load and unload:

export function onLoad(prev?: State) {
  // first load: prev is undefined
  // hot-reload: prev is the value returned from the previous onUnload
}

export function onUnload(): State {
  return { reloads: (prevReloads ?? 0) + 1, trackedPawn };
}

Hot reload handoff

On a same-id file-watch Reload, the old instance’s onUnload() return is serialized (JSON + EntityRef revival) and passed to the new onLoad(prev).

  • Primitives, strings, arrays, and nested objects round-trip.
  • EntityRef values revive live and serial-gated in the new context (isValid() === false if the entity died in the gap).
  • Carry 64-bit values as decimal stringsbigint cannot be JSON.stringify‘d and would drop the whole handoff.

Vanished (delete the .s2sp) clears any pending handoff. A later re-add is a fresh onLoad(undefined).

Map changes

Plugins persist across changelevelonLoad does not re-fire per map. Subscribe to Server.onMapStart (or poll Server.mapName on a frame throttle) for map-aware work:

import { Server } from '@s2script/server';

Server.onMapStart((mapName) => {
  console.log('map start', mapName);
});

Teardown

The host ledger owns persistent resources (commands, event subs, DB handles, websockets, imported interfaces). Unload walks the ledger in reverse-dependency order — plugins should still return useful state from onUnload, but cleanup does not depend on the plugin running correctly.

See Authoring and Server.

s2script — Source 2 plugin framework

GitHub