# Events and Runners

Code can execute designer-authored logic through an existing **Event** component or a serialized **Runner** field. Use an Event for scene-authored behavior and a Runner to expose extension points on your own component.

## The Event component

The Event component implements the runtime and cancellation contracts used by its Instructions. Its class declaration, with the body omitted, is:

```cs
public class Event : MonoBehaviour, ICancelToken, IRun
```

`IRun` starts or schedules the sequence, while `ICancelToken` stops it when the Event is disabled or destroyed. Choose between immediate return and awaiting completion through these members:

| Member | Does |
| :--- | :--- |
| `Run()` | Runs the Instructions with a default context. Fire and forget. |
| `Run(Args args)` | Runs them with the context you supply. |
| `Schedule()` | Returns the `Awaitable` for awaiting completion. |
| `Schedule(Args args)` | Same, with a supplied context. |
| `Stop()` | Cancels the running sequence. |
| `IsRunning` | Whether it's mid-sequence. |
| `RunningIndex` | Which Instruction is executing. |

Use `Schedule(args)` when the caller must wait for the sequence to finish:

```cs
Event myEvent = target.GetComponent<Event>();

using Args args = Args.Get(this.gameObject, target);
await myEvent.Schedule(args);
```

The `await` waits for `myEvent` to finish before the caller continues. Use `Run` when the caller can continue immediately.

### Events

These notifications let your component observe the sequence as it runs:

| Event | Fires |
| :--- | :--- |
| `EventInstructionStartRunning` | When the sequence starts. |
| `EventInstructionEndRunning` | When it finishes or is canceled. |
| `EventInstructionRun` | Each time an Instruction begins, with its index. |

An Event that's already running ignores further requests to start rather than queueing them.

## Host logic with Runners

`RunnerInstructions` adds a designer-authored Instructions list to your component:

```cs
using UnityEngine;
using VisualScript.Runtime;

public class MyDoor : MonoBehaviour
{
    [SerializeField] private RunnerInstructions m_OnOpen = new RunnerInstructions();
    [SerializeField] private RunnerConditions m_CanOpen = new RunnerConditions();

    public void TryOpen()
    {
        using Args args = Args.Get(this.gameObject, this.gameObject);

        if (!this.m_CanOpen.Run(args)) return;
        this.m_OnOpen.Run(args).Fire(null, this);
    }
}
```

The Inspector shows Instructions and Conditions lists on `MyDoor`. When `TryOpen` runs, `m_CanOpen` checks whether opening is allowed; if it returns `true`, `m_OnOpen` runs the configured response.

!!! tip "Runner fields keep host components configurable"
    A hard-coded door has one response. A door with `RunnerInstructions` can run a sound, camera shake, or quest update without changing `MyDoor`.

    Add a runner at the moments where designers need to attach project-specific behavior.

### Running

The Instructions runner offers overloads for cancellation and instance reuse:

| Method | Does |
| :--- | :--- |
| `Run(args)` | Runs the Instructions, returning an `Awaitable`. |
| `Run(args, cancelToken)` | Same, with an explicit cancellation token. |
| `Run(args, poolId)` | Same, reusing a pooled runner instance. |

`RunnerConditions.Run(args)` returns a `bool` and is synchronous. An empty Conditions list returns `true`.

### Pooling runners

Each execution needs a runner instance. The plain overload creates one, while the pool overload reuses an instance. Add a shared pool ID to `MyDoor`:

```cs
private static readonly IdString POOL_ID = new IdString("my-door");
```

Then pass it to `Run` inside `TryOpen`:

```cs
this.m_OnOpen.Run(args, POOL_ID).Fire(null, this);
```

`POOL_ID` groups executions that can reuse the same runner allocation.

Use the pooled overload for frequent execution, such as each projectile, hit, or frame. For a door that opens twice a session, the plain overload avoids maintaining a pool ID for infrequent allocations.

### Fire

`Fire(onComplete, context)` starts an `Awaitable` without awaiting it, attaching a Unity object as context so the framework can report errors against the right object.

Use `Fire` when you're starting a sequence from a non-async method and don't need the result. In an async method, you can await `Run` directly when the next operation depends on completion.

## Cancellation

`ICancelToken` is the framework's cancellation contract — a single `Get` property meaning *stop*.

Pass a token to `Run` when your own object's lifetime should govern the sequence. Implementing it on your component means the Instructions stop when your component decides they should, in the same way an Event's Instructions stop when it's disabled.

!!! warning "Always give long sequences a token"
    Instructions started from code with no cancellation token keep running after the object that started them is destroyed. If later Instructions access that object, they can produce a null reference error several frames after its destruction.

## Where to go next

- **[Args](../args.md)** — build the context runners need.
- **[Custom Instructions](../extending/instructions.md)** — write the operations runners execute.
- **[Events](../../manual/events/index.md)** — inspect Event lifecycle from the designer's perspective.
