> 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/data-and-persistence/ao-serialize.md).

# @ao\_serialize

`@ao_serialize` is the annotation you place on struct/class fields to opt them into the engine's serialization system. Adding this allows you to edit script fields in the editor for easy customization and is also required if you intend to use JSON serializations/save data for structs.&#x20;

```go
Enemy :: class : Component {
    // Saved, shown in inspector, included in scene data
    max_health: int @ao_serialize;
    patrol_radius: float @ao_serialize;

    // Runtime-only — not saved, not in inspector
    current_target: v2;
    aggro_timer: float;
}
```

## What it does

When you mark a field with `@ao_serialize`, the engine will:

1. **Show it in the inspector** so you can edit it on entities in the editor.
2. **Include it in JSON serialization** (`Save.set_json` / `Save.try_get_json`).

Fields without `@ao_serialize` exist only in memory at runtime. They start at their default (zero) value each time the component is created or the scene is loaded.

## Supported types

`@ao_serialize` works with all common CSL types:

| Type              | Example                                                              |
| ----------------- | -------------------------------------------------------------------- |
| Integers          | `s8`, `s16`, `s32`, `s64` / `int`                                    |
| Unsigned          | `u8`, `u16`, `u32`, `u64` / `uint`                                   |
| Floats            | `f32` / `float`, `f64`                                               |
| Booleans          | `bool`                                                               |
| Strings           | `string`                                                             |
| Vectors           | `v2`, `v3`, `v4`                                                     |
| Enums             | Any user-defined enum                                                |
| Fixed arrays      | `[N]T`                                                               |
| Dynamic arrays    | `[..]T`                                                              |
| Structs / classes | Nested types (their `@ao_serialize` fields are included recursively) |

{% hint style="info" %}
For nested structs/classes, only the fields marked `@ao_serialize` inside the nested type are serialized. The annotation doesn't cascade — you must mark each field individually.
{% endhint %}

## Using with components

The most common use is on component fields. These become editable in the editor's inspector and are saved as part of the scene.

```go
Chest :: class : Component {
    capacity: int @ao_serialize;
    loot_table: string @ao_serialize;
    is_locked: bool @ao_serialize;

    // Runtime state — no need to serialize
    has_been_opened: bool;
}
```

You can set `capacity`, `loot_table`, and `is_locked` per-entity in the editor. When the scene loads, those values are restored automatically.

## Using with the Save system

`@ao_serialize` also controls which fields are included when you use the JSON save APIs. Only marked fields are written to JSON.

```go
Player_Progress :: class {
    version: s64 @ao_serialize;
    xp: s64 @ao_serialize;
    level: s64 @ao_serialize;
    unlocked_skins: [..]string @ao_serialize;
}

// Save
Save.set_json(player, "progress", ref progress);

// Load
progress: Player_Progress;
if !Save.try_get_json(player, "progress", ref progress) {
    // New player — set defaults
    progress.version = 1;
    progress.level = 1;
}
```

For full details on the Save system, see [Save System](/data-and-persistence/save.md) and [JSON Serialization](/data-and-persistence/json.md).

## Using with standalone JSON

You can serialize any annotated type to/from a JSON string, independent of the Save system:

```go
config: My_Config;
config.difficulty = 3;

json_str := JSON.serialize(ref config);
// json_str contains only @ao_serialize fields

loaded: My_Config;
JSON.try_deserialize(json_str, ref loaded);
```

## Default values and zero-filling

{% hint style="warning" %}
**Inline defaults are not supported with `@ao_serialize`.** Writing `health: int = 100 @ao_serialize` is a compile error. Serialized fields are always zero-initialized, then overwritten by the deserialized value. Set non-zero defaults in `ao_start`:

```go
max_health: int @ao_serialize;

ao_start :: method() {
    if max_health == 0 {
        max_health = 100;
    }
}
```

{% endhint %}

When deserializing, fields that are missing from the data (e.g. you added a new field after players already have saves) are **zero-filled**:

* Numbers → `0`
* Booleans → `false`
* Strings → `""`
* Arrays → empty

If zero isn't a sensible default, check and fill in your own defaults after loading:

```go
if !Save.try_get_json(player, "stats", ref stats) {
    stats.version = 1;
    stats.max_health = 100;  // zero would be a bad default
}
```

## What NOT to serialize

Not every field should be serialized. Leave `@ao_serialize` off fields that are:

* **Derived at runtime** (positions computed each frame, cached lookups)
* **Temporary state** (timers, cooldown counters, frame-local flags)
* **Large data that changes every frame** (unnecessary save overhead)

A good rule of thumb: if the value is set once (in the editor or on load) and rarely changes, serialize it. If it's recomputed every frame, don't.

## Common patterns

### Enums for mode selection

```go
AI_Mode :: enum {
    IDLE;
    PATROL;
    CHASE;
}

Guard :: class : Component {
    mode: AI_Mode @ao_serialize;
    patrol_speed: float @ao_serialize;
}
```

### Nested structs

```go
Spawn_Point :: struct {
    position: v2 @ao_serialize;
    radius: float @ao_serialize;
}

Spawner :: class : Component {
    points: [..]Spawn_Point @ao_serialize;
    spawn_interval: float @ao_serialize;
}
```

### Asset references

Some fields reference engine assets (textures, prefabs, sounds). These are serialized as asset identifiers and resolved automatically on load. See the built-in components (like `Sprite_Renderer`) for examples.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.allout.game/data-and-persistence/ao-serialize.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
