# Save System

This page covers how to add save/load support to your custom plugin.

## Save Data Architecture

The Save plugin uses a registration pattern. Each plugin that needs persistence creates a save data class and registers it with the Save plugin. When a save is triggered, the Save plugin calls each registered provider to collect data. When loading, it distributes the saved data back.

## Creating Save Data for Your Plugin

### Step 1: Define the Save Data Class

Create a serializable class that holds your plugin's persistent state:

```csharp
[System.Serializable]
public class FactionSaveData
{
    public List<FactionReputationData> reputations = new List<FactionReputationData>();
}

[System.Serializable]
public class FactionReputationData
{
    public string factionId;
    public int reputation;
}
```

Keep save data simple and serializable. Use primitive types, strings, lists, and simple nested classes. Avoid Unity object references (they can't be serialized to a save file).

### Step 2: Implement Save Data Collection

Your plugin needs to collect its current state into the save data class and restore state from it:

```csharp
public class FactionPlugin : IFactionPlugin
{
    public FactionSaveData CollectSaveData(string characterId)
    {
        if (!SparkEntityRegistry.TryGetEntity(characterId, out SparkEntity entity))
            return null;

        var tracker = entity.GetSparkComponent<FactionTrackerEntity>();
        if (tracker == null)
            return null;

        var saveData = new FactionSaveData();
        foreach (var kvp in tracker.GetAllReputations())
        {
            saveData.reputations.Add(new FactionReputationData
            {
                factionId = kvp.Key,
                reputation = kvp.Value
            });
        }
        return saveData;
    }

    public void ApplySaveData(string characterId, FactionSaveData data)
    {
        if (data == null) return;

        if (!SparkEntityRegistry.TryGetEntity(characterId, out SparkEntity entity))
            return;

        var tracker = entity.GetSparkComponent<FactionTrackerEntity>();
        if (tracker == null)
            return;

        foreach (var rep in data.reputations)
        {
            tracker.SetReputation(rep.factionId, rep.reputation);
        }
    }
}
```

### Step 3: Register with the Save Plugin

Create a registration class that hooks into the Save plugin's lifecycle:

```csharp
public class FactionSaveDataRegistration
{
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    private static void Register()
    {
        var savePlugin = Spark.GetPlugin<ISavePlugin>();
        if (savePlugin == null) return;

        savePlugin.RegisterSaveProvider("factions", new FactionSaveProvider());
    }
}
```

The save provider implements the collection and application methods and is called by the Save plugin when saving and loading.

## Save Providers

A save provider wraps the collect/apply logic for the Save plugin to call:

```csharp
public class FactionSaveProvider
{
    public object CollectData(string characterId)
    {
        var plugin = Spark.GetPlugin<IFactionPlugin>() as FactionPlugin;
        return plugin?.CollectSaveData(characterId);
    }

    public void ApplyData(string characterId, object data)
    {
        if (data is FactionSaveData factionData)
        {
            var plugin = Spark.GetPlugin<IFactionPlugin>() as FactionPlugin;
            plugin?.ApplySaveData(characterId, factionData);
        }
    }
}
```

## Best Practices

* Use a unique string key when registering your save provider (e.g., `"factions"`). This key identifies your data in the save file.
* Handle missing data gracefully. If a player loads a save from before your plugin was installed, your data will be null.
* Keep save data flat and simple. Complex object graphs are harder to serialize and more prone to versioning issues.
* Test save/load with different scenarios: fresh start, mid-game save, save from older version without your plugin.
* Guard against the Save plugin not being available (`Spark.GetPlugin<ISavePlugin>()` returns null). Your plugin should still work, it just won't persist data.
* Don't store references to Unity objects in save data. Use entry IDs (strings) instead.


---

# Agent Instructions: 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:

```
GET https://docs.sparkframework.dev/documentation/developer-guide/core-systems/save.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
