Use Custom Value Types
The value types Gears handles without registration, how to use your own enums, how to register a parser and formatter for another type, and the limit on which types can back a setting.
A setting's value type decides how Gears turns the text in ModSettings.xml and in the saved
settings file into a value, and back again. Use this page to work out which types you can use, and
how to add one of your own.
Types Gears handles without registration
| Type | Parsed from | Written as | Formatter string |
|---|---|---|---|
int | StringParsers.ParseSInt32 | ToString(fmt) | applied, so "000" gives 042 |
float | StringParsers.ParseFloat, where a % suffix divides by 100, so 5% gives 0.05 | ToString(fmt) | applied, so "0%" gives 5% |
double | StringParsers.ParseDouble | ToString(fmt) | applied, but no Selector, Slider or Switch exists for double |
bool | StringParsers.ParseBool | True or False | ignored |
string | as written | as written | ignored |
UnityEngine.Color | StringParsers.ParseColor32, such as 255,128,0 | R,G,B | ignored |
any enum | the member name, case-insensitively | the member name | ignored |
Use your own enum
Any enum in any loaded assembly works as a value type, with no code on your side.
namespace MyMod
{
public enum Difficulty { Easy, Normal, Hard }
}<Selector name="Mode" displayKey="myModMode" type="MyMod.Difficulty" defaultValue="Normal" />
<Switch name="Hardcore" displayKey="myModHardcore" type="MyMod.Difficulty, MyMod" defaultValue="Easy">
<Buttons left="Easy" right="Hard" />
</Switch>Write type as the full name, the assembly-qualified name, or just Difficulty. Gears matches it
case-insensitively.
An enum Selector starts with every declared member as its allowed values. An enum Switch takes the
first two declared members unless <Buttons> says otherwise.
From C#:
ISelectorGlobalSetting<Difficulty> mode =
category.GetSetting<ISelectorGlobalSetting<Difficulty>>("Mode");
Difficulty current = mode.SettingValue;Name an enum declared inside a class
An enum nested in a class works the same way, but you must separate the class from the enum with a plus sign, not a dot. That is the name .NET reflection uses, and it is what Gears looks the type up by.
namespace MyMod
{
public class PlayerController
{
public enum Difficulty { Easy, Normal, Hard }
}
}<!-- Correct: PlayerController+Difficulty -->
<Selector name="Mode" displayKey="myModMode" type="MyMod.PlayerController+Difficulty" defaultValue="Normal" />
<!-- Also correct, with the assembly name after a comma -->
<Switch name="Hardcore" displayKey="myModHardcore" type="MyMod.PlayerController+Difficulty, MyMod" defaultValue="Easy">
<Buttons left="Easy" right="Hard" />
</Switch>Write one plus sign per level of nesting, such as MyMod.PlayerController+Settings+Difficulty.
Writing MyMod.PlayerController.Difficulty with a dot does not resolve. Gears drops the setting
and logs has unknown type, and nothing appears in the menu.
The bare name Difficulty also resolves, but it matches the first type with that name in any loaded
assembly. If your mod declares both a top-level Difficulty and a nested one, the bare name may pick
either, and Gears logs nothing about the choice. Write the + form for a nested enum so you get the
one you meant.
In C#, refer to the enum the way C# always does, with a dot:
ISelectorGlobalSetting<PlayerController.Difficulty> mode =
category.GetSetting<ISelectorGlobalSetting<PlayerController.Difficulty>>("Mode");Register a parser and formatter for another type
Tag a class with
[SettingsSerializationProvider] and
give it two static methods:
using GearsAPI.Attributes;
using UnityEngine;
[SettingsSerializationProvider]
public static class MyModSerialization
{
// Signature must be: static T Method(string)
[SettingParser(typeof(Vector2))]
public static Vector2 ParseVector2(string raw)
{
string[] parts = raw.Split(',');
return new Vector2(float.Parse(parts[0]), float.Parse(parts[1]));
}
// Signature must be: static string Method(T, string)
// The second argument is the formatter string, which may be null.
[SettingFormatter(typeof(Vector2))]
public static string FormatVector2(Vector2 value, string fmt)
{
return value.x.ToString(fmt) + "," + value.y.ToString(fmt);
}
}The rules:
- The class attribute is spelled
[SettingsSerializationProvider], with noAttributesuffix. The method attributes are[SettingParser(typeof(T))]and[SettingFormatter(typeof(T))]. - Gears scans every loaded assembly for provider classes at
GameAwake, before it creates any setting, so your provider works for settings declared in XML as well as in code. - Methods must be static and declared on the provider class itself, not inherited. Public and private both work, and a static class is fine.
- A method with the wrong signature throws
InvalidOperationExceptionat scan time, naming the expected signature, for example[SettingParser(typeof(Vector2))] on 'MyMod.MyModSerialization.ParseVector2' has an invalid signature. Expected: static Vector2 ParseVector2(string). - Gears catches a parser that throws at the point of use. It logs the bad value, and the setting keeps its previous value.
Fix a provider that fails to load before you ship
Nothing catches the InvalidOperationException from a parser or formatter with the wrong
signature. Gears scans providers first, before it loads any mod's settings, so the exception stops
Gears loading settings for every mod the player has installed, not only yours. Start the game
with your mod installed and check the log for has an invalid signature before you release it.
Registration is game-wide and last-wins
Gears keeps one parser and one formatter per value type for the whole game, not per mod. Whichever
provider is scanned last replaces the ones before it, with nothing in the log. Registering int or
float replaces Gears' own handler for every mod, and two mods that both register a shared type
such as UnityEngine.Vector2 silently overwrite each other. Register only types your own mod owns.
A parser is not enough to create a setting
Parsing and formatting is only half of a setting. The other half is a Selector, Slider or Switch class that holds a value of that type, and those exist only for the built-in types and for enums. Registering a parser does not make a new type usable as a setting.
With the Vector2 provider above:
type="UnityEngine.Vector2"in XML resolves and has a parser, but Gears then logsSetting 'x' of type 'UnityEngine.Vector2' could not be created: No ISelectorGlobalSetting implementation registered for value type 'UnityEngine.Vector2'and drops the setting.CreateSetting<ISelectorGlobalSetting<Vector2>>(...)throwsInvalidOperationExceptionwith the same message.
To offer a setting over a type that is neither built in nor an enum, model it as one that is. Use an
enum of named presets, a string selector whose allowed values you parse yourself, or several
numeric settings.
Attributes reserved for Gears
GearsAPI also declares
[SettingFactory],
[SettingFactoryEnum] and
[SettingFactoryFixed], which are how
Gears registers its own setting classes.
They are public, but Gears scans only its own assembly for them, so a mod cannot add a setting class through them today. Treat them as reserved.
Bind Settings with Attributes
Tag static fields, properties and methods with a setting path, then call BindSettingsClass to assign the settings and subscribe to their events.
Upgrade to GearsAPI 3
Convert a V1 ModSettings.xml to the V2 schema, move C# from GearsAPI 2.x to 3.0, and check the result in the game log.