---
title: "Load game data from YAML in C#"
url: https://recursiveai.org/onegame/cookbook/load-game-data-from-yaml/
description: "Load game data from YAML in C# with onegame. Includes a runnable .NET project, sample output and the game integration details."
version: "1.26.0"
---

# Load game data from YAML in C#

**onegame** is a free, open-source game engine for .NET from Recursive AI. MIT or Apache-2.0, no editor required.

[NuGet](https://www.nuget.org/packages/RecursiveAI.OneGame.Host) · [GitHub](https://github.com/recursive-ai-oss/onegame) · [Docs](https://recursiveai.org/onegame/docs/) · [Download example (.zip)](https://recursiveai.org/onegame/cookbook/load-game-data-from-yaml/load-game-data-from-yaml.zip)

Load YAML balance data into an application type through onegame’s asset cache. Path-based loading is supported on Windows, Linux and macOS, not Android, iOS or the browser.

```shell
dotnet add package \
  RecursiveAI.OneGame.Host
```

Requires [.NET 10](https://dotnet.microsoft.com/download/dotnet/10.0) (net10.0); .NET 9 and earlier aren’t supported.

`data/ships.yaml`:

```yaml
name: Scout
gold: 25
```

```csharp
using OneGame.Host;
using OneYaml;

using var assets = new Assets();
var ships = assets.LoadData(
    "data/ships.yaml", GameYaml.Default.Ships);
Console.WriteLine(ships.Value.Name);
ships.Release();

public record Ships(string Name, int Gold);
[YamlSerializable(typeof(Ships))]
partial class GameYaml
    : YamlSerializerContext;
```

Prints:

```text
Scout
```

Unzip [the example](https://recursiveai.org/onegame/cookbook/load-game-data-from-yaml/load-game-data-from-yaml.zip), then `dotnet run`.

## The full program

```csharp
using OneGame.Host;
using OneYaml;

using var assets = new Assets();
var ships = assets.LoadData(
    "data/ships.yaml", GameYaml.Strict.Ships);
Console.WriteLine(
    $"{ships.Value.Name}: {ships.Value.Gold} gold");
ships.Release();

public sealed record Ships(string Name, int Gold);

[YamlSerializable(typeof(Ships))]
internal sealed partial class GameYaml
    : YamlSerializerContext
{
    internal static GameYaml Strict { get; } =
        new(new YamlSerializerOptions
    {
        UnmappedMemberHandling =
            YamlUnmappedMemberHandling.Disallow
    });
}
```

`dotnet run` prints:

```text
Scout: 25 gold
```

The generated `GameYaml` context supplies the schema. The full program adds strict options to the snippet at the top. The strict options reject keys that are not members of `Ships`. Keep the returned handle while the game uses it, read through `Value`, and release each acquired reference when finished.

Missing files, invalid YAML and unknown keys throw `AssetException`; a failed initial load caches nothing.

## Variations

### Share a cached handle

```csharp
// Before ships.Release() in the program above:
var again = assets.LoadData("data/ships.yaml", GameYaml.Strict.Ships);
Console.WriteLine(ReferenceEquals(ships, again));
Console.WriteLine(ships.References);
again.Release();
```

Prints these additional lines:

```text
True
2
```

## Related recipes

[Animate a glTF character](https://recursiveai.org/onegame/cookbook/animate-gltf-character/) · [Handle trigger events](https://recursiveai.org/onegame/cookbook/handle-physics-trigger-events/)

---

## The short answer

Use `assets.LoadData(path, typeInfo)` to load YAML into your game’s application type. Supply metadata generated by a OneYaml serializer context, keep the returned `Asset<T>` handle, and read its `Value` when using the data. Release the handle when that owner no longer needs it.

The standalone download creates an `Assets` service directly instead of starting a game host. When working with a `GameInstance` named `game`, use its existing `game.Assets` service so the host and development tools share the cache and reload state.

## How do I deserialize YAML into a C# game-data type?

Declare a type that matches the file’s shape, mark a partial serializer context with `YamlSerializable`, and pass the generated type metadata to `LoadData`. The sample’s `Ships` record has a name and a gold amount. Its input uses `name` and `gold`; these key spellings matter with the generated metadata and strict options shown here.

OneYaml comes with `RecursiveAI.OneGame.Host`; no separate YAML package installation is needed. Building the project generates `GameYaml.Strict.Ships` metadata from the context declaration; it is not a handwritten parser or a method you must implement. The program sets `UnmappedMemberHandling` to `Disallow`, so a typo does not silently leave a property at a default value.

Paths are relative to content folders and use forward slashes. They may not be absolute and may not contain `..`. With the default file source, the game looks beside the program, in a macOS bundle’s Resources directory, or in the working directory. Keeping `data/ships.yaml` beside the project makes the example runnable from the unzipped folder.

A failed initial load throws `AssetException` and caches nothing. Its code distinguishes failures such as a missing file, bad path, excessive size or bad format. Treat a parse failure as a data error to fix rather than continuing with a partially populated balance table. The default YAML file size limit is 8 MiB.

## How do I cache and reload YAML game data?

The asset cache shares a handle for repeated loads of the same path and counts references. Each successful load acquires a reference; each owner must call `Release()` once when finished. At zero references the asset leaves the cache. A handle is not `IDisposable`, so use `Release`, not a `using` statement around `Asset<Ships>`.

`Fixed` is a deterministic fixed-point number type; `FVec3` is a three-dimensional vector of those numbers. The sample’s balance record needs only a string and an integer.

Keep the handle rather than copying its `Value` into a field forever. YAML reloads replace the decoded object. Reading through the handle when applying game rules lets an intentional data update take effect at the chosen boundary without leaving some consumers on an older object.

Development YAML changes are staged, not swapped immediately. The changed file is parsed and checked, then the handle becomes pending while `Value` still returns the old data. Apply it with `assets.ApplyPendingData()`, the scenario `assets: apply-data` step, or the automatic boundary at scenario start. Never apply pending data while a recording is running: changing simulation inputs mid-recording would undermine replay and drift checks.

A bad reload preserves the old content, sets `Asset.Error`, and raises a failed event. Fixing the file permits another reload. `DataDigest` identifies the loaded data files so a replay can record which inputs it used. Exported games do not watch files; file watching belongs to development tools. Path loading is also not yet available on Android, iOS or the browser, where content needs the documented embedded route instead. See the [asset guide](https://recursiveai.org/onegame/docs/assets/) for handle ownership, staged reloads and platform coverage.
