Appearance
Renaming Config Items
How to remove an Game Config Item or change its identifier without compatibility issues.
Appearance
How to remove an Game Config Item or change its identifier without compatibility issues.
Renaming or removing Config Items will cause any persisted data (the PlayerModel, for example) that has a reference to the item to no longer deserialize correctly.
For these situations, the GameConfigLibrary API supports specifying ConfigKey aliases. An alias is simply another ConfigKey for an existing config item for purposes of deserialization. After deserializing via an alias mapping, no trace of the alias is stored in the result. This means that upon re-serialization, the current ConfigKey will be written out rather than the old alias value.
An Alias can be registered programmatically with a RegisterAlias() method. ConfigKey aliases should be registered upon loading a GameConfigLibrary before any deserialization involving the game config takes place. A good place for this is SharedGameConfig.OnLoaded() or ServerGameConfig.OnLoaded().
Let's consider an example of changing the Producer type id for the "Vue" Producer to "VueX" in the Idler reference project. The updated game config will contain an entry for "VueX" but no entry for "Vue". To have existing players that have unlocked the "Vue" producer migrate their progress to the "VueX" producer, we'll introduce an alias to the Producers config library:
public class SharedGameConfig : SharedGameConfigBase
{
[GameConfigEntry("ProducerData")]
public GameConfigLibrary<ProducerTypeId, ProducerInfo> Producers { get; private set; }
protected override void OnLoaded()
{
base.OnLoaded();
ProducerTypeId oldKey = ProducerTypeId.FromString("Vue");
ProducerTypeId newKey = ProducerTypeId.FromString("VueX");
Producers.RegisterAlias(id: newKey, alias: oldKey);
}
}The SpreadSheetParser implementation in the Metaplay SDK supports declaring ConfigKey aliases conveniently from Google Sheets. This is done by adding a special column by name /Aliases that contains one or more aliases (a.k.a. old names) for the config element. As part of the game config build, the alias information will be parsed and serialized into the resulting Game Config Archive and then automatically registered on the Game Config Library when the Archive is imported.

The alias resolution mechanism is used only for deserializing references to Game Config Items. Any direct use of renamed ConfigKey values must be handled by custom logic. In our example case, the Idler PlayerModel in fact stores a Dictionary of ProducerModel objects keyed by ProducerTypeId values. To carry over a "Vue" ProducerModel to the renamed Producer type id, we'll add a MetaOnDeserialized function to PlayerModel.
Specifically, we only changed references of "Vue" to refer to the Game Config Item "VueX" with the alias. To make the operation a rename, we must now update all "Vue" references to use "VueX" instead:
[MetaMember(112)] public MetaDictionary<ProducerTypeId, ProducerModel> Producers;
[MetaOnDeserialized]
public void OnDeserialized()
{
Dictionary<ProducerTypeId, ProducerTypeId> renames = new Dictionary<ProducerTypeId, ProducerTypeId>();
foreach ((ProducerTypeId key, ProducerModel producer) in Producers)
{
// The ProducerInfo reference to an old config item will be updated
// to a renamed config item by alias resolution. We can use this to detect
// the case where our separately stored key is no longer the current key for
// the item.
if (key != producer.Info.ConfigKey)
renames[key] = producer.Info.ConfigKey;
}
foreach (KeyValuePair<ProducerTypeId, ProducerTypeId> rename in renames)
{
Producers[rename.Value] = Producers[rename.Key];
Producers.Remove(rename.Key);
}
}The renaming mechanism can also be used for gracefully deleting Config Items. This is done by introducing a "sentinel" config item that represents obsoleted config entries and registering aliases for the sentinel item for deleted config keys. In the MetaOnDeserialized handler, the game code can then handle any occurrences of the sentinel item as appropriate.
The sentinel can be created dedicating an well known Id for it:
[MetaSerializable]
public class RoomId : StringId<RoomId>
{
public static RoomId DeletedId = RoomId.FromString("_deleted");
}
[MetaSerializable]
public class RoomInfo : IGameConfigData<RoomId>
{
[MetaMember(1)] public RoomId Id { get; private set; }
[MetaMember(2)] public string Name { get; private set; }
[MetaMember(3)] public MetaRef<RoomInfo> NextRoom { get; private set; }
public RoomId ConfigKey => Id;
}[MetaSerializable]
public class RoomData
{
[MetaMember(1)] public RoomInfo Info;
[MetaMember(2)] public MetaTime UnlockedAt;
}[MetaMember(113)] public MetaDictionary<RoomId, RoomData> UnlockedRooms;
[MetaOnDeserialized]
public void OnDeserialized()
{
List<RoomId> deletes = new List<RoomId>();
foreach ((RoomId key, RoomData room) in UnlockedRooms)
{
// Delete when key is aliased to the sentinel.
if (key == RoomId.DeletedId)
deletes.Add(key);
}
foreach (RoomId delete in deletes)
{
UnlockedRooms.Remove(delete);
}
}With the above logic, we can safely data references by removing the Item from Game Config, and adding the deleted item's identifier as an alias for the _deleted sentinel.
Alternatively we can mark the config item as a sentinel with a field.
[MetaSerializable]
public class RoomId : StringId<RoomId>
{
public static RoomId DeletedId = RoomId.FromString("_deleted");
}
[MetaSerializable]
public class RoomInfo : IGameConfigData<RoomId>
{
[MetaMember(1)] public RoomId Id { get; private set; }
[MetaMember(2)] public string Name { get; private set; }
[MetaMember(3)] public MetaRef<RoomInfo> NextRoom { get; private set; }
[MetaMember(4)] public bool IsDeleted { get; private set; }
public RoomId ConfigKey => Id;
}[MetaOnDeserialized]
public void OnDeserialized()
{
foreach ((RoomId key, RoomData room) in UnlockedRooms)
{
// Delete when key is aliased to the sentinel.
if (key == RoomId.DeletedId)
if (room.Info.IsDeleted)
deletes.Add(key);
}
}Similarly, we can safely data references by removing the Item from Game Config, and adding the deleted item's identifier as an alias for the _deleted sentinel. The config row for _deleted sentinel item is no longer special, and merely has the IsDeleted flag set in the data.