> For the complete documentation index, see [llms.txt](https://docs.allout.game/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.allout.game/scripting/game-frame-lifecycle.md).

# Game/Frame Lifecycle

In contrast to event driven scripting like Roblox, games on All Out use a frame lifecycle model that allows you to run code when the game starts, each frame, and at the end of a session.

All Out games run on a simple lifecycle: some callbacks happen once at startup, and some happen every frame.

You’ll implement these callbacks either:

* **Globally** (top-level `ao_*` procs in `main.csl`), or
* **On components** (methods like `ao_update` inside `Player` or your own `Component` subclasses)

{% hint style="info" %}
If you’re looking for the “template” `main.csl`, see [Getting Started with CSL](/scripting/syntax.md).
{% endhint %}

## Global lifecycle (scene-wide)

These are top-level procs in `main.csl`:

### `ao_before_scene_load`

Runs after the empty `Scene` is created but before its serialized entities and components are loaded. This is where you typically register definitions needed while the scene loads:

* Item definitions (inventory system)
* Currencies (economy system)
* Global config values/constants

### `ao_start`

Runs once when the scene starts.

Common uses:

* Spawn runtime entities/prefabs
* Initialize global systems
* Read game-wide save data

### `ao_update(dt)`

Runs every frame.

Common uses:

* Game timers, wave managers
* Spawning logic that depends on time
* Gameplay rules

### `ao_late_update(dt)`

Runs every frame after `ao_update`.

Common uses:

* Scene-wide work that depends on final positions/state for the frame
* “Cleanup” work after updates

Player UI belongs in `Player.ao_late_update`, not this global callback.

### `ao_draw(dt)`

Cosmetic-only; runs on submissions that may render and skips resim. `dt` is the submitted sim delta; use `get_time()` for absolute time.

Never put gameplay or interactive UI here. Use `ao_update`/`ao_late_update` (usually `Player.ao_late_update` for UI).

## Component lifecycle (per-entity)

Components (including `Player`) can implement:

* `ao_start()`
* `ao_update(dt)`
* `ao_late_update(dt)`
* `ao_draw(dt)` (cosmetic-only; renderable frames)
* `ao_end()`
* `ao_on_state_sync()` (called when network state is synchronized)

{% hint style="warning" %}
`ao_start()` is not replayed for every client. When a client joins after an object has already started on the server, the synchronized component can arrive with its start lifecycle already complete. Do not use `ao_start()` for client-local presentation setup that every joining client must run; rebuild that presentation from synchronized state in `ao_on_state_sync()` instead.
{% endhint %}

Example:

```go
Orbiter :: class : Component {
    ao_start :: method() {
        // init
    }

    ao_update :: method(dt: float) {
        // per-frame behavior
    }

    ao_end :: method() {
        // cleanup (when destroyed)
    }
}
```

## Where to put code: a rule of thumb

* **Per-player state/logic**: put it on `Player` (see [Adding Player Logic](/scripting/player-model.md))
* **Reusable entity behaviors**: put them on custom components (see [Entities and Components](/scripting/entities-and-components.md))
* **Global coordination / rules**: put it in the global lifecycle (`ao_*` procs)

## Server vs local

Global and component gameplay callbacks run on both the predicting client and the server. Do not guard gameplay with `Game.is_server()`.

Draw all player UI from that player's `ao_late_update` call stack, wrapped in `is_local_or_server()`. Use `is_local()` only for player-specific visual overrides. State changed only under `is_local()` is replaced by the next server sync.

These are methods on `Player_Base`. Call them on a player reference, or as `is_local_or_server()` / `is_local()` inside a `Player` method (implicit `this`).
