Downloads

← Back Basic 3D example (SaveLoad merge settings)

Save/Load settings

Let's look at the save/load setting to understand how they can be used to save/load your data during gameplay. You can find more information about Save/Load here

Using "Row level controller" to add new scenes after game release

Staring with BGDatabase v.1.8.12 there is a built-in controller, which implements the same approach, and it can be used without any additional code. Use the code below as an example for row level controller implementation

As you can see on the screenshot above, "Collectables" table has "Add missing" and "Removed orphaned" toggles turned on in the settings. It means, that after loading saved data with SaveLoad addon, all "Collectables" rows will be replaced with the saved rows. But what if we want to add new scenes with new collectables? These new collectables are missing in the saved data, but we do not want them to be removed after game loading.

In such cases, a row controller can be used to prevent certain rows from removing. Below is in-depth guide how to achieve it.

  1. First of all we need to add "Version" table with a single row and single "version" int field to keep track of database version
    and add this table to SaveLoad merge settings
    Now, whenever you save the data, we can figure out which database version was used.
  2. Now let's say our game was already released, and now we want to add a new scene "BEExample3". We add a "version" int field to the "Scene" table. For existing scenes we keep the value=0, but for the new scene, we set the value to 1 (which means the scene was added to database version 1) We also set "Version.version" field value to 1.
    The saved data has the "Version.version" value of 0 (or maybe the saved data does not contain "Version" table, in this case we also assume the version=0) and we can tell, that all "Collectables" rows, related to scenes with version > saved "Version.version" (which is currently equal to 0) should not be removed. This trick with increasing "Version.version" and adding new scenes with the new version will also work fine if you want to add new scenes in the future. So next time you want to add a new scene ("BEExample4") you can set "Version.version" to 2 and "BEExample4.version" to 2 etc.
  3. Now we need to add a row controller, which compare the saved version with the current version and if the scene's current version > saved "Version.version" value, controller should prevent this scene's "Collectables" rows from removing. Controller class can be assigned via "Controller Type" field.

Below is the full controller C# code with comments.

Full C# code for row controller (click to view)
using System.Collections.Generic;
using BansheeGz.BGDatabase;

//this is SaveLoad row level controller implementation without using CodeGen classes
public class SaveLoadController  : BGMergeSettingsEntity.IMergeReceiver, BGMergeSettingsEntity.IUpdateMatchingReceiver, BGMergeSettingsEntity.IRemoveOrphanedReceiver
{
    //this field is used to detect if it's loading or saving
    private bool loading;
    //this is a set of scene IDs with version > saved version
    private readonly HashSet<BGId> newScenesId = new HashSet<BGId>();
    //this is a "Collectable.Scene" field, which can be used to retrieve the related Scene row for Collectable row
    private BGFieldRelationSingle sceneRelation;

    //this method is called before repos data merging
    public bool OnBeforeMerge(BGRepo from, BGRepo to)
    {
        //here we want to know, if it's loading or saving? If target repo is default repo- it means loading
        loading = to == BGRepo.I;

        if (loading)
        {
            //let's find out the saved version
            var savedVersion = 0;
            var savedVersionMeta = from.GetMeta("Version");
            var savedVersionField = (BGFieldInt)savedVersionMeta?.GetField("version", false);
            if (savedVersionField != null) savedVersion = savedVersionField[0];

            //now let's iterate the scenes and find the scenes with version > savedVersion and add their IDs to newScenesId hashset
            var sceneMeta = to.GetMeta("Scene");
            var versionField = (BGFieldInt)sceneMeta.GetField("version");
            sceneMeta.ForEachEntity(entity => newScenesId.Add(entity.Id), entity => versionField[entity.Index] > savedVersion);

            //retrieve "Collectable.Scene" field from default repo (we will use it later)
            sceneRelation = (BGFieldRelationSingle) to.GetMeta("Collectable").GetField("Scene");
        }
        return false;
    }

    public void OnAfterMerge(BGRepo from, BGRepo to)
    {
    }

    //this method is called before row removal, if it returns true, it means the removal is cancelled
    public bool OnBeforeDelete(BGEntity toEntity)
    {
        if (loading)
        {
            //if it's Collectable row
            if (toEntity.MetaName == "Collectable")
            {
                //get related scene ID
                var sceneId = sceneRelation[toEntity.Index].Id;
                //if scene is new, skip Collectable row removal
                if (newScenesId.Contains(sceneId)) return true;
            }
        }
        return false;
    }

    //this method is called before matching row update
    public bool OnBeforeUpdate(BGEntity from, BGEntity to)
    {
        if (loading)
        {
            //we want to use the latest version from default database, skip update
            if (from.MetaName == "Version") return true;
        }
        return false;
    }
}
How to see it in action
  1. Download the example project
  2. Run it with 2 scenes and save the game
  3. Add a new scene, add new Collectable rows to it, set "version" field to 1 for the new scene, change "Version.version" to 1
  4. Run with 3 scenes and load the game. Collectable rows for the new scene should be preserved
← Back to Downloads