Appearance
Appearance
Runtime Options are defined in code, along with their default values. You can then override these values in a base configuration file that applies to any servers that you run - these are the core values like ApplicationName
.
For values that need to be applied only to certain Metaplay Environments, you can overload these base values with environment-specific config files. For example, you might want to configure Debug
level logging in your local environment, but Information
level in production.
Some values are auto-injected to the Runtime Options system by our infrastructure stack. An example of this is database configuration: for your local environment you'll most likely have an SQLite database configured, but in production, you'll use a cloud-based MySQL database. MySQL server configuration details are not known during build-time and instead get passed in by the infrastructure scripts during deployment.
Finally, you can pass configuration values directly from the command line. This is handy in the local environment where you want to quickly and temporarily to test a particular setting on your server without modifying the underlying config files.
Runtime Options can be defined in multiple sources and they have a fixed order of precedence for deciding which value gets selected.
In order of preference (top value overrides anything below):
METAPLAY_EXTRA_OPTIONS
.Game.Server/Config/Options.*.yaml
, specified with METAPLAY_OPTIONS
environment variable, which defaults to ./Config/Options.base.yaml;./Config/Options.local.yaml
. Options.local.yaml
for a local environment.Options.dev.yaml
for an environment named ”dev”.Options.stage.yaml
for the staging environment.Options.prod.yaml
for the production environment.Options.base.yaml
for all environments.Runtime Options support hot-loading of new configuration values during runtime. File edits are automatically detected and cause the appropriate Runtime Options classes to be reloaded without the need to restart the server.
Hot-loading is controlled on a class-by-class basis - not all of the Metaplay core Runtime Options classes support hot loading. See the discussion below for how to mark a class as dynamic (ie: hot-loadable) or static (ie: not hot-loadable) and why you might want to make the distinction. You can easily view the value of this setting for each class in the LiveOps Dashboard, which is also discussed below.
We use Runtime Options extensively in the Metaplay SDK and you can use them too. There are a few simple steps to follow if you want to add your cu:
Declaring your options and giving them default values is simple: just create a class that derives from RuntimeOptionsBase
and give it the [RuntimeOptions(..)]
attribute:
// All Options live in the 'MyFoo' container in .yaml files.
// Declared with isStatic==false, which enabled hot-loading when input files
// change.
[RuntimeOptions("MyFoo", isStatic: false, "Configuration examples.")]
public class MyFooOptions : RuntimeOptionsBase
{
// Declare basic variables (can be configured via .yaml files)
public string SomeString { get; private set; } = "default value";
public int Integer { get; private set; } = 10;
public bool Enabled { get; private set; } = true;
public string AssetsBasePath { get; private set; } = "./";
public string GfxAssetsPath { get; private set; } = "gfx";
public string AudioAssetsPath { get; private set; } = "audio";
[MetaDescription("This `string` appears in the Dashboard as a description.")]
public string AnotherString { get; private set; } = "something else";
// Computed variables cannot be defined in .yaml, but can still be
// accessed in code and are shown in the Dashboard. Properties without
// a setter and properties marked with [ComputedValue] are computed variables.
public FullGfxAssetsPath => AssetsBasePath + GfxAssetsPath;
[ComputedValue]
public FullAudioAssetsPath { get; private set; } // Set value in OnLoadedAsync
}
Pro tip
You should take some time to add descriptions to your options classes and their variables. These descriptions not only help to document your options in code, but they also get displayed in the LiveOps Dashboard. As shown in the example code above, a description for the options class itself can be set inside the RuntimeOptions
attribute, and descriptions for individual variables can be set by using MetaDescriptions
attributes. Both of these description fields support Markdown formatting.
⚠️ Heads up
Pay attention when specifying collections (arrays, lists, dictionaries) on multiple levels in Options, as they may behave unexpectedly. This is a limitation caused by the underlying .NET Configuration system.
Runtime Options expose a powerful way to validate values through the OnLoadedAsync()
hook. The hook is called when the Game Server loads in the initial configuration values. It is also called during hot-loading of new configuration values.
public class MyFooOptions : RuntimeOptionsBase
{
public string SomeImportantValue { get; private set; } = null;
public string FirstName { get; private set; }
public string LastName { get; private set; }
[ComputedValue]
public string FullName { get; private set; }
public override Task OnLoadedAsync()
{
if (string.IsNullOrEmpty(SomeImportantValue))
throw new InvalidOperationException("SomeImportantValue must be provided");
FullName = $"{FirstName} {LastName}";
return Task.CompletedTask;
}
}
This hook can also be used to compute derived values, eg., fetch secrets from external sources:
[RuntimeOptions("MySecret", isStatic: true)]
public class MySecretOptions : RuntimeOptionsBase
{
// Source variable, configured in .yaml
public string SecretSource { get; private set; }
// Internal value, not shown on the dashboard
[IgnoreDataMember] string _secretValue;
public override Task OnLoadedAsync()
{
// Resolve the secret value based on the source. This Options class is only visible to
// users after the OnLoadedAsync() has completed successfully.
_secretValue = await ResolveSecretAsync(SecretSource);
}
}
Values can be accesses via the RuntimeOptionsRegistry
instance:
MyFooOptions opts = RuntimeOptionsRegistry.Instance.GetCurrent<MyFooOptions>();
Debug.Log("Gfx asset path: {0}", opts.FullGfxAssetsPath);
⚠️ Heads up
If your Options class is marked as not static then hot-loading may mean that the value returned changes from call to call. If you were to cache these values in some part of your code but not others, you may encounter sync issues. Derived values (as described above) are a good solution to this problem. Think carefully about how you will handle this in your code before marking your class as isStatic = false
.
To configure your option values in .yaml files, you'll need to use the name of the class followed by the name of the value. For example, the following options:
[RuntimeOptions("MyFoo", isStatic: false)]
public class MyFooOptions : RuntimeOptionsBase
{
[EnvironmentVariable("MY_ENV_VAR")]
public string SomeString { get; private set; } = "default value";
[CommandLineAlias("-MyFoo")]
public bool Enabled { get; private set; } = true;
}
...could be configured with:
# Options.xxx.yaml:
MyFoo:
SomeString: some string value
Enabled: false
Setting values from the command line is also possible:
dotnet run --MyFoo:SomeString="some string value" -MyFoo=false
As is setting values with the environment variables:
MY_ENV_VAR="some string value" Metaplay_MyFoo__Enabled=false dotnet run
Pro Tip
The default environment variable for each property is Metaplay_
followed by the path to the property using underscores (__
) as the path separator. For example, Player.DebugConfig.ServerConsistencyChecks
becomes Metaplay_Player__DebugConfig__ServerConsistencyChecks
and System.ClientPorts[0]
becomes Metaplay_System__ClientPorts__0
.
With multiple configuration sources and derived values, the flexibility of the Runtime Options system can seem overwhelming. Luckily, the LiveOps Dashboard provides a handy place where you can check what values your Metaplay Deployment is running with.
On this page are a series of expandable sections. Each tab corresponds directly to a single Runtime Options class in the server code. Expanding a section shows you the configuration values for that Runtime Options class and their descriptions. All Runtime Options classes are automatically visible in the Dashboard.