Gears

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

TypeParsed fromWritten asFormatter string
intStringParsers.ParseSInt32ToString(fmt)applied, so "000" gives 042
floatStringParsers.ParseFloat, where a % suffix divides by 100, so 5% gives 0.05ToString(fmt)applied, so "0%" gives 5%
doubleStringParsers.ParseDoubleToString(fmt)applied, but no Selector, Slider or Switch exists for double
boolStringParsers.ParseBoolTrue or Falseignored
stringas writtenas writtenignored
UnityEngine.ColorStringParsers.ParseColor32, such as 255,128,0R,G,Bignored
any enumthe member name, case-insensitivelythe member nameignored

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 no Attribute suffix. 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 InvalidOperationException at 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 logs Setting '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>>(...) throws InvalidOperationException with 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.

On this page