Skip to content

Add custom save file support for serializable current run data.#168

Draft
lamali292 wants to merge 2 commits intoAlchyr:masterfrom
lamali292:customrunsave
Draft

Add custom save file support for serializable current run data.#168
lamali292 wants to merge 2 commits intoAlchyr:masterfrom
lamali292:customrunsave

Conversation

@lamali292
Copy link
Copy Markdown
Contributor

@lamali292 lamali292 commented Apr 16, 2026

This PR introduces support for per-mod saves of current runs.

Each mod now can register its own isolated save file stored alongside the base run save.

current_run.save
mods/<mod_id>/current_run.save

To use the new system, tag a static field in your mod with the [ModSave] attribute. The system will automatically handle the loading and saving of this field during the standard run persistence cycle.

public static class MySaveManager
{
    [ModSave] public static MyRunData RunData = new();
}

Your save class should implement ISaveSchema and IPacketSerializable to maintain compatibility with the existing serialization engine. This structure mirrors the internal SerializableRun used by the base game.

public class MyRunData : ISaveSchema, IPacketSerializable
{
    [JsonPropertyName("schema_version")]
    public int SchemaVersion { get; set; } = 1;
    
    [JsonPropertyName("player_data")]
    public List<MyPlayerData> PlayerData { get; set; } = [];
    
    public void Serialize(PacketWriter writer)
    {
        writer.WriteInt(SchemaVersion);
        writer.WriteInt(PlayerData.Count);
        foreach (var pData in PlayerData)
        {
            pData.Serialize(writer);
        }
    }

    public void Deserialize(PacketReader reader)
    {
        SchemaVersion = reader.ReadInt();
        var count = reader.ReadInt();
        PlayerData = [];
        for (var i = 0; i < count; i++)
        {
            var pData = new MyPlayerData();
            pData.Deserialize(reader);
            PlayerData.Add(pData);
        }
    }
}

You can nest any custom data types within your schema, provided they implement IPacketSerializable.

public class MyPlayerData: IPacketSerializable
{
    [JsonPropertyName("net_id")]
    public ulong NetId { get; set; }

    [JsonPropertyName("saved_cards")]
    public List<SerializableCard> SavedCards { get; set; } = [];

    [JsonPropertyName("my_saved_value")]
    public int SavedValue{ get; set; }
    
    public void Serialize(PacketWriter writer)
    {
        writer.WriteULong(NetId);
        writer.WriteInt(SavedValue);
        writer.WriteList(SavedCards );
    }

    public void Deserialize(PacketReader reader)
    {
        NetId = reader.ReadULong();
        SavedValue= reader.ReadInt();
        SavedCards = reader.ReadList<SerializableCard>();
    }
}

(My downfall save data is no longer in Relics, yay! Maybe this can be reused here. Sorry for the many pull requests, but these are features that might be beneficial.)

  • Make it completly multiplayer safe

lamali292 and others added 2 commits April 17, 2026 00:13
Create per-mod save files alongside current_run.save under mods/<mod_id>/current_run.save
@lamali292
Copy link
Copy Markdown
Contributor Author

and obviously access it like this

public static MyPlayerData FromPlayer(Player player)
{
    var netId = player.NetId;
    var data = MyRunData.PlayerData.Find(p => p.NetId == netId);
    if (data != null) return data;
     data = new MyPlayerData { NetId = netId };
     MyRunData.PlayerData.Add(data);
     return data;
}

public static List<CardModel> GetSavedCards(Player player) =>
       MyPlayerData.FromPlayer(player).SavedCards.Select(CardModel.FromSerializable).ToList();

public static void AddSavedCard(Player player, CardModel card) =>
       MyPlayerData.FromPlayer(player).SavedCards.Add(card.ToSerializable());

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant