Appearance
Using Multiple Game Config Sources
This page will guide you on configuring your game config to be built from multiple data sources.
Appearance
This page will guide you on configuring your game config to be built from multiple data sources.
If your game configs become too large, you might want to split them into multiple different spreadsheets or files to make them easier to manage and work with. You might have custom tooling to manage parts of your configs and want to load the output of your tool separately. This can easily be achieved with game config sources.
We'll start by defining the source in the GameConfigBuildParameters
. If you've configured building game configs from the dashboard, the newly defined source will automatically show up. In this example, we'll separate game config libraries by LiveOps content and game configuration, but feel free to pick your own separation.
[MetaSerializableDerived(1)]
public class MyGameConfigBuildParameters : GameConfigBuildParameters
{
[MetaMember(1)]
public GameConfigBuildSource LiveOpsSource;
}
Next up, we'll specify which libraries will be loaded from the new source. Simply add the configBuildSource
parameter to the GameConfigEntry
attribute:
public class SharedGameConfig : SharedGameConfigBase
{
...
[GameConfigEntry("HappyHours")]
[GameConfigEntry("HappyHours", configBuildSource: nameof(MyGameConfigBuildParameters.LiveOpsSource))]
public GameConfigLibrary<HappyHourId, HappyHourInfo> HappyHours { get; private set; }
}
The game config build process will now automatically load the libraries from LiveOpsSource
instead.
Since your spreadsheets will now have different contents, they will not necessarily be compatible with the other sources. Therefore, you can override the values shown on the dashboard by overriding the GetAvailableGameConfigBuildSources
method.
public class MyGameConfigBuildIntegration : GameConfigBuildIntegration
{
public override IEnumerable<GameConfigBuildSource> GetAvailableGameConfigBuildSources(string sourceProperty)
{
if (sourceProperty is nameof(MyGameConfigBuildParameters.LiveOpsSource))
{
return new[] { new GoogleSheetBuildSource(name: "LiveOps sheet", spreadsheetId: "1wXKv4SPD....") };
}
return new[] { new GoogleSheetBuildSource(name: "Master sheet", spreadsheetId: "PKyCatM....") };
}
}
Loading from disk
Instead of using Google Sheets, you can also load a spreadsheet from disk. Simply return FileSystemBuildSource
instead of GoogleSheetBuildSource
. Note that Unity will load spreadsheets from the PROJECT_ROOT/GameConfigs
folder by default, but you can customize this by overriding DefaultUnityGameConfigBuildIntegration.CreateFetcherConfig()
and returning a custom FetcherConfig
.
Files in your repository or Unity project are often not available in your server deployment, instead you can use the incremental build feature instead. Incremental building uses some of the libraries of the current active game config, and rebuilds the other libraries.
First, exclude the sources that load from the LiveOpsSource
. In our case, that is HappyHours
.
public class MyGameConfigBuild : GameConfigBuildTemplate<SharedGameConfig, ServerGameConfig, MyGameConfigBuildParameters>
{
protected override ConfigEntryBuilder? GetEntryBuilder(Type configType, string entryName)
{
// Don't output content for libraries using the LiveOpsSource if the source is not available.
if (entryName == "HappyHours")
{
if (!BuildParameters.BuildLiveOps)
return null;
}
}
}
Next, configure the IsIncremental
field in the game config build parameters. We only use incremental building if we don't have a valid value for LiveOpsSource
. BuildLiveOps
will only be true in Unity, as we expect to have the file sources available.
[MetaSerializableDerived(1)]
public class MyGameConfigBuildParameters : GameConfigBuildParameters
{
[MetaMember(1)]
public GameConfigBuildSource LiveOpsSource;
public bool BuildLiveOps => LiveOpsSource != null;
public override bool IsIncremental => !BuildLiveOps;
}
Lastly, we'll hide the LiveOpsSource
from the dashboard by adding the MetaFormNotEditable
attribute.
[MetaSerializableDerived(1)]
public class MyGameConfigBuildParameters : GameConfigBuildParameters
{
[MetaMember(1)]
[MetaFormNotEditable]
public GameConfigBuildSource LiveOpsSource;
public bool BuildLiveOps => LiveOpsSource != null;
public override bool IsIncremental => !BuildLiveOps;
}
If you're manually invoking GameConfigBuild as described in Configuring Unity Config Builds, you should also set a valid value to the LiveOpsSource
.
public static class UnityGameConfigBuilder
{
...
[MenuItem("Config Builder/Build GameConfig", isValidateFunction: false)]
public static void BuildFullGameConfig()
{
// Get or create the source fetcher configuration.
IGameConfigSourceFetcherConfig fetcherConfig = IntegrationRegistry.Get<IUnityGameConfigBuildIntegration>().CreateFetcherConfig();
// Get the game-specific GameConfigBuildIntegration implementation.
GameConfigBuildIntegration integration = IntegrationRegistry.Get<GameConfigBuildIntegration>();
DefaultGameConfigBuildParameters buildParams = new DefaultGameConfigBuildParameters
{
// Get the first source returned by GetAvailableGameConfigBuildSources for DefaultSource.
DefaultSource = integration.GetAvailableGameConfigBuildSources(nameof(GameConfigBuildParameters.DefaultSource)).First(),
LiveOpsSource = integration.GetAvailableGameConfigBuildSources(nameof(GameConfigBuildParameters.LiveOpsSource)).First(),
};
...
}
}
And, we're done!
You should now have a game config building setup that uses 2 different data sources to build your game config libraries, and if you followed step 4, part of these are loaded from a file in your Unity project and the other part is loaded from Google Sheets. Building from the dashboard still works using incremental building even though not all files are available.