Skip to main content

Automations (autonomous processes / NPCs)

Game models let you put your rules and state on the server, but a model function only runs when a client invokes it. Automations are server-driven processes that invoke your model functions on their own — on a schedule or in reaction to model activity — so you can build NPCs, spawners, ticking world systems, and economy jobs that run with no client connected.

client.gameModel wraps the full automation surface. It is a studio-admin surface that runs on the Game API: every call needs an app-scoped token for the target app (mint one with identity.portal.mintAppToken(appId)) and a logged-in user who holds the manage_apps permission on the app's organization — a studio admin can mintAppToken for their own app even without player entitlement. Drive it from a trusted admin context — a studio backend or an admin-only / authenticated web tool (a browser is fine) — and just keep that privileged token out of the untrusted game client you ship to end users. The examples below assume client holds that app's app-scoped token. For the full conceptual model (triggers, selectors, the safety budget, and circuit breakers) see Game API → Autonomous processes (NPCs).

1. Mark the entry-point function autonomous

An automation runs a normal model function "as the server". The function must opt in with autonomousInvocable: true:

await client.gameModel.upsertFunction({
appId: '1',
name: 'wanderNpc',
invokeScope: 'server',
autonomousInvocable: true,
mutations: [
{ target: 'self', property: 'x', expression: 'self.x + randInt(-1, 1)' },
],
});

2. Create the automation

await client.gameModel.upsertAutomation({
appId: '1',
name: 'npc-wander',
functionName: 'wanderNpc',
targetMode: 'type', // run against every container of a type
targetTypeName: 'Npc',
triggerType: 'schedule',
scheduleKind: 'interval',
intervalMs: 1000,
// safety budget (bounds the work each tick may do):
maxTargets: 50,
gasLimit: 100000,
runTimeoutMs: 2000,
maxRunsPerMinute: 120,
});

Automations are idempotent on (appId, name), so calling upsertAutomation again updates the existing one.

Event-triggered automations

Instead of (or in addition to) a schedule, fire an automation in reaction to model activity:

await client.gameModel.upsertAutomationTrigger({
appId: '1',
automationName: 'npc-react',
onEvent: 'property_changed',
containerTypeName: 'Player',
propertyKey: 'health',
debounceMs: 250,
});

3. Enable, run, and tune

await client.gameModel.setAutomationEnabled({ appId: '1', name: 'npc-wander', enabled: true });
await client.gameModel.runAutomation({ appId: '1', name: 'npc-wander' }); // run once now (testing)
await client.gameModel.setAutomationPolicy({ appId: '1', killSwitch: false, maxAutomations: 100 });

Re-enabling a tripped automation also resets its circuit breaker.

4. Monitor

const stats = await client.gameModel.automationStats({ appId: '1', windowMinutes: 60 });
const runs = await client.gameModel.automationRuns({ appId: '1', automationName: 'npc-wander', limit: 50 });
const diag = await client.gameModel.appDiagnostics({ appId: '1' });

automationStats is the "what are my NPCs doing" dashboard (throughput, failure rate, compute, per-automation breakdown); automationRuns is the per-run audit trail; appDiagnostics is a snapshot of your app's whole game-model footprint.

Method summary

AreaMethods
AuthorupsertAutomation, deleteAutomation, setAutomationEnabled, upsertAutomationTrigger, deleteAutomationTrigger, setAutomationPolicy
RunrunAutomation (manual one-shot)
Read / monitorautomations, automation, automationTriggers, automationPolicy, automationRuns, automationStats, appDiagnostics

Automations that notify players

An automation tick is just a function invocation, so a function with notification effects will push realtime notifications to clients on every tick — that is how an NPC's movement or a world event reaches players. See Model-driven notifications.

For ready-made NPC archetypes (behavior functions + automations deployed together, plus spawn/read helpers), see the Game Kit. Selectors can also filter targets by runtime grid permissions (selfPermissionWhere / candidatePermissionWhere, game-api v0.13.12+) — see Autonomous processes → Permission predicates and the typed KitSelectorSpec in the Game Kit.