Skip to content

Scheduled events

An event is a scheduled, bounded, audited change to a live game world. You write it once as a sequence of phases, publish a version of it, put it on the calendar, and it runs — at four in the morning if that is when you scheduled it, with nobody watching.

That last clause is the whole reason this feature is shaped the way it is. Everything below that looks like extra ceremony — the switchboard, the caps, the dry run, the ledger — is there because the thing being automated is somebody’s game world, and the person who authored the change is asleep when it happens.

Core owns the engine; the installed module owns the meaning. Core decides whether an action is permitted, when it runs, in what order, how many times, within what budget, what it created and who is told. The module says which verbs exist and performs them. Core never learns a game word: every label you see in the step editor came from the module that registered it.

Admin → Events, its own group in the sidebar:

Row Who sees it
Events — the definitions, and their runs Admin, editor, moderator
Calendar — month and list view, with series Admin, editor, moderator
Actions — what this deployment permits, and the caps Admin only
My participation — your own attendance Everyone

Reading is staff-wide on purpose. A moderator’s power over this feature is the run console — the screen you open when an event is doing something wrong at two in the morning — and hiding it from the one role that exists for incident response would be a strange way to build an incident tool. The narrower gates are on the actions, not the rows: authoring is admin and editor, publishing a version and starting a run are admin only, and all of it is enforced on the server rather than by hiding a button.

A definition is the thing that gets listed, searched, scheduled and audited: a title, a slug, a storyline, a schedule, and an ordered list of phases. Each phase holds steps, and a step is one action with its parameters.

A phase advances on a condition — after a duration, or when something happens in the game a given number of times. The vocabulary of “something that happens” is the trigger catalog the installed module already ships, so a module gains phase conditions by declaring one more entry in a list it already had.

Versions are immutable, and a run pins one

Section titled “Versions are immutable, and a run pins one”

Publishing takes a snapshot. The run that starts on Saturday holds the version that was published, not the one you edited on Friday — which is what makes a run reproducible and an audit answerable after a change. A running event cannot be edited; you edit the definition, publish a new version, and the next run picks it up.

Admin → Events → Actions lists every action the installed modules registered, and everything above a notification arrives switched off. Installing a module must never start doing things to your world.

Each row has two controls: whether the action is permitted on this deployment at all, and its per-run caps — how much of a budget dimension one run may consume. Dimensions are declared by the module (uo.creatures, uo.bosses, uo.rewards and so on), and consumption is counted in the database with a conditional update, not checked in application code.

That distinction matters more than it sounds. A stolen admin session has already passed every role check there is; it still cannot exceed the cap, because the cap is a condition on the UPDATE that spends the budget.

Verify materialises the whole plan without touching the world: every step is dispatched with a verify flag, and you get back what would happen and what it would cost against the caps, in the module’s own words. Refusals show up here, before the calendar entry exists.

A definition that has never been verified is exactly the one worth not scheduling. Verifying is cheap, and it is the last point a human sees the plan.

Runs start on the schedule, or by hand. A series groups definitions into an arc, so a three-part story reads as one thing on the calendar rather than three unrelated entries.

The run console shows live status, the steps and their attempts, the budget consumed against each cap, any failures, and the cleanup. Its controls are:

  • Pause and resume — resume carries a run past any settled step, including one that failed or was refused.
  • Skip, retry and confirm a single step. Confirm is how a human-cue step advances: the run posts the instruction, waits, and moves on when somebody says they did it.
  • Advance a phase by hand.
  • Cancel, with or without cleanup.

Every one of those is logged with the person who did it.

What an event does to a world, and how it is undone

Section titled “What an event does to a world, and how it is undone”

Two different things, and the difference is the whole safety story.

What it owns. Creatures, bosses, oracle NPCs, decoration, a temporary gate — things the run created. Each one is written to a resource ledger as it is made, with the run and step that made it.

What it borrows. A spawner’s respawn timer, a starting skill cap, a seasonal flag — values that already existed and are being changed for the duration. Those are leases: the game keeps the original, the site records both halves, and the lease carries its own deadline.

They live on the shard host, outside the site’s reach, and the site cannot turn them on.

EventsEnabled is off by default, and it is a different switch from AdminWriteEnabled. Turning the admin plane on is consenting to staff moderation driven from a screen somebody is looking at. Turning this on is consenting to the site changing and watching your world unattended. One switch could not honestly express both.

Beside it sit the game’s own ceilings — how many creatures one call may spawn, how long a gate may stand, how much one run may own in total, how often the world may be saved. They refuse rather than clamp, for the same reason the caps do: a quietly shortened request leaves the two halves disagreeing about what actually happened. See Bridge.cfg for every key.

The public calendar at /site/events carries what is scheduled, what is happening now, what finished recently, and published results. A run that was cancelled says so — “Did not happen” — rather than quietly disappearing.

Listing is separate from publishing. A definition has its own listed switch, because publishing is what makes an event runnable and a surprise invasion should not have to be advertised a fortnight in advance in order to be allowed to happen. Unlisting hides the definition, its runs and its results from the public pages and from a participant’s own history; it hides nothing from staff.

Where a module can tell who took part, a run can keep a participation ledger — scores and ranks, published as a results table when the run finishes. Ranks are computed at publication and stored, so somebody added afterwards does not silently renumber a table people have already read. A signed-in person sees their own attendance under their account, and staff see theirs on the same screen.

  • degraded is not failed. If the game server disappears mid-run, the run degrades, world-changing steps park unattempted, and it recovers when the connection does. The public page does not say so — that is operator information.
  • refused means a bound said no, and it is reported with the numbers.
  • The run log answers “why did phase 3 not start?” as a query, not by reading a wall of text. It is kept for 90 days after a run reaches a terminal state — and a run still in flight keeps every line it has, however old, because the question it answers is still open.
  • Cleanup can be re-run from the run console if a teardown was interrupted.

website/EVENTS.md is the design of record — the model, the data, the security argument and what was deliberately left out. website/MODULE_API.md is the contract a module registers its verbs against, and link/ADMIN_CONTROLS.md is what the site may ask a game to do at all.

For how the engine is put together, see Events architecture.