Save Load

What can I do with Save/Load addon?

  • Save/load the state of the database to/from byte array during runtime on players devices
  • Define which table and which fields you want to be saved/loaded using merge settings (it won't work if you skip this step)
  • Create C# class for fine-grained row-level control.
  • You can save the whole database without Save/Load addon (click)

How can I enable it?

Chose Addons->Save Load, click "Enable", mark tables/fields for saving/loading and save the database.

How it works?

SaveLoad addon can create multiple, configurable snapshots of database data and it can restore database state from these snapshots. Snapshots are constructed from the data, you marked as changeable, using Merge settings, described below. (Why it works like this?) Please, make sure you marked all data you want to be saved.

Why it works like this?

Here is the quick explanation and main reasons why it works the way it works and why it's much better then simply saving the whole database.

  1. we want to be able to change database between public game releases
  2. we want to make sure the old saves, which are made with old version of database will remain compatible with new database
  3. As a result of loading old save, we want to keep all saved data + we want to keep all data we made to default database (shipped with a game)

Here is the detailed illustration about what we want to achieve:

And here is the detailed illustration about how we want to achieve it and how save/load addon actually works:

For more information about:
  • How to change your database and keep it compatible with old save files- read the section below .
  • How to implement row-level control over merging- read Row-level controller section below.

How to use?

//to save
byte[] bytes = BGRepo.I.Addons.Get<BGAddonSaveLoad>().Save();

//to load
BGRepo.I.Addons.Get<BGAddonSaveLoad>().Load(bytes);

Is it possible to save/load the whole database?

// !!!! This is not recommended !!!!
//to save
byte[] bytes = BGRepo.I.Save();

//to load
BGRepo.I.Load(bytes);

Using Merge settings

Using Merge settings you can chose which data to save/load. You want as less data to be saved as possible. You do not need to include data, that is "static" and can not be changed during play session.

The rules are very simple:

  1. If you have some field, which value can be changed during the play session and you want this change to be persistent, you need to mark this field to be included to save/load. Example from our example scenes: we want the amount of gold, player gathered to be remembered between game sessions- so we marked this field to be saved and loaded.
  2. If you have some table, whose rows can be changed during gameplay and you want these changes to be remembered, you need to mark this table to be included to the save/load by setting following setting to true: "Add missing", "Remove Orphaned", "Update matching". These setting will ensure, that all rows from this table will be saved and loaded. Example from our example scenes: Collectable table rows can be changed, so we marked them as described above.

Merge settings for this addon is the same as merge settings for Export/Import. You can learn more about merge settings here

How to change your database and keep it compatible with old save files?

  • Do not include static data (the data, which can not be changed during game session) to save/load merge settings
  • While developing new, public version of your game- you can change anything in your static data with one simple exception: you can not delete rows which may be referenced from saved data. Well, actually you can, but in this case you need to make sure your code should handle null relation values properly.(and they will be null if you remove the rows)
  • As for the data, which is saved within database snapshot (save file),
    • if in merge settings you mark the field for updating it will be updated during loading (if target row stills exist in default database),
    • if in merge settings you mark the rows for adding/deleting ("Add missing"/"Remove Orphaned") the whole table will be updated with values from the saved file (if this table still exists in default database).
    • if you want fine-grained, row-level control over loading - you can do it with C# controller class (read the next section for more details).

Here is an example from our default database and default example scenes:

For more information about:

Row-level control over loading

Merge settings give you control over which tables/fields you want to save. But what about particular rows? What if you want to update/add/delete only those rows which comply to some condition?

This can be done with C# controller class, which can cancel update operation before it actually takes place. Currently there is no any merging on saving data, only on loading (but we reserved "merging on saving" option for future releases)

Here is a quick example of custom controller class, which uses the default example database, shipped with our package. For detailed explanation about how our example works, please, read example breakdown article.

The main objective of this example controller is to prevent deleting Collectable rows for added scenes (scenes which was added between public game releases)

Here is the code

using BansheeGz.BGDatabase;

    // Warning!!! this controller is implemented without code generation
    //and may contains ugly boilerplate code
 public class MyCustomSaveLoadController : BGMergeSettingsEntity.IMergeReceiver,BGMergeSettingsEntity.IRemoveOrphanedReceiver
 {
     private bool loading;

     public bool OnBeforeMerge(BGRepo from, BGRepo to)
     {
         //here we want to know, if it's loading or saving?
         //... Note (currently there is no merging on saving, but it can be implemented in future releases)
         loading = to == BGRepo.I;
         return false;
     }

     public void OnAfterMerge(BGRepo from, BGRepo to)
     {
         //no need to write anything here
     }

     //this callback is invoked before row removal. Return true to cancel this operation
     public bool OnBeforeDelete(BGEntity toEntity)
     {
         if (loading)
         {
             if ("Collectable".Equals(toEntity.Meta.Name))
             {
                 //before deleting Collectable row, we want to make sure- it's not connected to newly added scene
                 var scene = toEntity.Get<BGEntity>("Scene");
                 //if scene's version more than 0 - cancel removing by returning true
                 return scene != null && scene.Get<int>("version") > 0;
             }
         }

         return false;
     }
 }

To see this controller in action:

  • Run example scene (Assets\BansheeGz\BGDatabase\Examples\Scenes\BGExample1.unity) using default database. (If you already created your own database, here is some information about how to temporarily revert it to default)
  • Collect some objects and save the game
  • We pretend this was made by a real player with our game version 1, and now we want to develop version 1.1 and we want to add a new scene. We could add a new scene without any controller and without any issue, but we want to populate this scene with Collectable rows, and we do not want them to be deleted while loading save file. Without this controller, all Collectable rows will be overriden with the data from the save file, cause in SaveLoad merge settings we marked this table for "addingMissing" and "removingOrphaned" (which basically mean the whole table will be restored from save file)
  • At this point we start to develop game version 1.1. We will omit real scene creating, cause all we want- is to make sure our save file will be loaded properly and it will not erase our newly added collectables for new scene
  • Open BGDatabase window, chose Configuration->Scene and add new int field, call it "version"
  • Then chose Database->Scene and add a new scene
  • For newly created scene assign "version" field to 1
  • Chose "Collectable" field for new scene and add some Collectables
  • Now chose Addons->SaveLoad, and assign "Controller Type" field to MyCustomSaveLoadController (this is a class name of our controller)
  • Save the database and run BGExample1.unity scene again
  • Load the game, chose Database->Scene and make sure all saved Collectables remain intact, just like newly added Collectables for our new scene. Also, you can run the same example without controller assigned to see the difference
For more information about:

BeforeSave event

You can declare you MonoBehavior script to be a receiver of BeforeSave event, so it could update the database before saving (code from the example project)
//this class implement BGAddonSaveLoad.BeforeSaveReciever interface,
//so OnBeforeSave method will be called before saving
public class BGPlayer : BGM_Player, BGAddonSaveLoad.BeforeSaveReciever
{
    ...
    //this method is called before saving
    void BGAddonSaveLoad.BeforeSaveReciever.OnBeforeSave()
    {
        //save current position , rotation and scene to the database
        m_position = transform.position;
        m_rotation = transform.rotation;
        m_scene = BGE_Scene.FindEntity(scene => string.Equals(scene.Name, SceneManager.GetActiveScene().name));
    }
}