Gears

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.

Gears 8 loads only the V2 ModSettings.xml schema and ships GearsAPI 3.0.0. A mod written for the old wiki, with a V1 ModSettings.xml or C# against GearsAPI 2.x, needs both halves brought forward.

Work through the parts that apply to your mod:

  1. Convert ModSettings.xml from V1 to V2.
  2. Move C# from GearsAPI 2.x to 3.0.
  3. Check your work.

Part 1: Convert ModSettings.xml from V1 to V2

V2 moved almost everything off attributes and onto named child elements, made a setting's value type explicit instead of guessing it from which property you used, and made defaultValue mandatory.

In short: add version="2", add type, give every value setting a defaultValue, turn tooltipKey, <Property>, leftValue and rightValue, and wrap into child elements, rename the Color defaultColor to defaultValue, and delete value and color.

Gears ignores V1 markup it does not recognize without logging it. A leftover <Property>, leftValue, wrap or value loads cleanly and is simply dropped, so a clean log does not prove the conversion is complete. See Part 3.

Mark the file as V2

<ModSettings>                 <!-- V1: rejected -->
<ModSettings version="2">     <!-- V2 -->

The value must be exactly 2. A root with no version, which is every V1 file, is rejected with an error from [ModSettingsFromXml], and so is any other value. Gears skips the whole file, so check this first when a mod vanishes from the Mods menu.

Add type to every Selector, Slider and Switch

Gears drops a Selector, Slider or Switch with no type at load. The error goes to the log, and nothing appears in the menu.

Work out the type from the V1 property you used:

V1 gave the type away withV2 type
<Property type="incrementalValuesInt" ...>int
<Property type="incrementalValuesFloat" ...>float
<Property type="arrayValues" ...>string
leftValue and rightValue of true and falsebool
leftValue and rightValue of anything elsestring

Gears accepts these aliases: int (Int32, integer), float (Single), string, bool (boolean), and Color (Color32). Matching is case-insensitive, and whitespace is trimmed.

Binding takes no type. Color accepts one and ignores it. Sliders support int and float only, and Gears drops a string or bool slider.

You can also name an enum your mod declares:

<Selector name="Difficulty" type="MyMod.Difficulty, MyMod" defaultValue="Normal" />

The namespace-qualified name, the assembly-qualified name and the bare type name all resolve, case-insensitively. An enum Selector seeds its allowed values with every declared member, so it needs no <List>. An enum Switch takes the first two members as left and right unless you say otherwise.

Give every value setting a defaultValue

This requirement is new in Gears 8. V1 tolerated a Selector, Slider, Switch or Color with no default, and V2 drops it:

[Gears] [XmlSettingsParserV2] Slider 'fov' has no defaultValue; defaultValue is required, skipping.

Add a defaultValue to every setting that lacks one. Binding is exempt. An empty defaultValue="" counts as missing.

Turn tooltipKey into a Description element

On a category and on every setting element, the tooltipKey attribute becomes a <Description> child:

<!-- V1 -->
<Category name="Display" displayKey="myModCatDisplay" tooltipKey="myModCatDisplayDesc">

<!-- V2 -->
<Category name="Display" displayKey="myModCatDisplay">
	<Description key="myModCatDisplayDesc" />

If the old tooltipKey was empty, drop it rather than writing an empty key.

A <Tab> is the exception. A tab is not a setting, and it keeps its tooltipKey attribute:

<Tab name="General" displayKey="myModTabGeneral" tooltipKey="myModTabGeneralTip">   <!-- unchanged in V2 -->

V2 also gives settings and categories a short <Caption>, which V1 had no equivalent for:

<Caption key="myModSettingCaption" />

Turn the V1 properties into elements

A V1 <Property type="x" ... /> becomes an element named after what that type said:

V1V2
<Property type="incrementalValuesInt" increment minValue maxValue /><Incremental increment minValue maxValue />
<Property type="incrementalValuesFloat" increment minValue maxValue /><Incremental increment minValue maxValue />
<Property type="arrayValues" allowedValues="a,b,c" /><List allowedValues="a,b,c" />
<Property type="formatter" formatString="0.0" /><Formatter ui="0.0" />

Both incremental variants collapse into one <Incremental>, because the int and float distinction now lives in type. On a Selector, increment must be greater than 0 and minValue no greater than maxValue, or Gears logs an error and ignores the range. A Slider does not check its range, so check those values yourself.

<Formatter> gained a second half. ui is what the player sees, and serialize is what Gears writes to the saved settings file and returns from modsetting(). You may leave out either one:

<Formatter ui="0.0" serialize="0.00" />

V1 had only the first, so formatString always becomes ui. Two things V1 did not make clear: <Formatter> is legal on Selector and Slider only and is ignored on Switch, Color and Binding; and when a Selector has both <List> and <Incremental>, Gears applies the range last, so it wins.

Move the Switch values onto a Buttons element

<!-- V1 -->
<Switch name="ShowHints" displayKey="myModShowHints" defaultValue="Off" leftValue="Off" rightValue="On" />

<!-- V2 -->
<Switch name="ShowHints" displayKey="myModShowHints" type="string" defaultValue="Off">
	<Buttons left="Off" right="On" />
</Switch>

Both sides are required. When only one of left and right is given, Gears applies neither. A bool switch does not need the element: it is false on the left and true on the right.

Rename the Color default

<Color name="Accent" defaultColor="255,0,0" />   <!-- V1 -->
<Color name="Accent" defaultValue="255,0,0" />   <!-- V2 -->

defaultValue is spelled the same way on every setting type, and the R,G,B notation is unchanged. The V1 color attribute, which held the current value, goes away entirely. See the next step.

Delete the value attribute

V1 wrote the player's current value back into the mod's own ModSettings.xml:

<Selector name="Quality" value="3" defaultValue="5" />    <!-- V1 -->
<Selector name="Quality" type="int" defaultValue="5" />   <!-- V2 -->

V2 has no value attribute. ModSettings.xml describes only what a setting is. The player's value lives in the saved settings file under their user data folder, and Gears restores it after load. A setting with no saved value starts at defaultValue.

Move wrap onto an element

<Selector name="DayOfWeek" wrap="true" />                        <!-- V1 attribute -->

<Selector name="DayOfWeek" type="string" defaultValue="Monday">   <!-- V2 element -->
	<Wrap wrap="true" />
</Selector>

Gears ignores anything that is not true or false, and wrapping stays off. <Wrap> is Selector-only.

Optional: what V2 adds

None of this is required to convert a V1 file.

Preview images. A <Preview> block with an <Image> that has no value is the setting's own image. An image that carries a value is shown while that value is selected.

<Selector name="Quality" type="int" defaultValue="1">
	<List allowedValues="1,2" />
	<Preview>
		<Image value="1" path="@modfolder(MyMod)://Textures/low.png" />
		<Image value="2" path="@modfolder(MyMod)://Textures/high.png" />
	</Preview>
</Selector>

Binding, Category and Color take only the value-less form. Gears skips a valued <Image> on a Color with a warning, and ignores one on a Binding or Category.

Restart scope. Tells the player what a change costs: None, Reload for a world reload, or Restart for a game restart, in any case.

<RestartScope scope="Restart" />

Global settings only. Gears ignores it silently on anything under <World> and on a category, so a leftover one does no harm.

Localization prefix. Gears prepends this to a Selector's or Switch's value when it looks the value up for display, with no separator, so metric renders through the key myModUnitsmetric.

<LocalizationPrefix prefix="myModUnits" />

Percentages everywhere. V1 already accepted a % suffix on a float's increment, minValue and maxValue. V2 accepts it on every float value, including defaultValue, <List> entries and preview values, and stores it as a fraction, so 50% becomes 0.5. It is a different thing from <Formatter ui="0%" />, which only changes how the stored number is displayed.


Part 2: Move C# from GearsAPI 2.x to 3.0

GearsAPI 2.x stored every value as a string. GearsAPI 3.0 is typed: each setting interface takes its value type as a generic parameter, and the single CurrentValue became three typed values.

Compare the old and new code

Here is the most common pattern, reading a value and reacting to a change:

using GearsAPI.Settings.Global;

public void OnGlobalSettingsLoaded(IModGlobalSettings modSettings)
{
    ISliderGlobalSetting fov = modSettings.GetTab("General")
        .GetCategory("Display")
        .GetSetting<ISliderGlobalSetting>("Fov");

    MyMod.Fov = int.Parse(fov.CurrentValue);
    fov.OnSettingChanged += Fov_OnSettingChanged;
}

private void Fov_OnSettingChanged(IGlobalModSetting setting, string newValue)
{
    MyMod.Fov = int.Parse(newValue);
}

Most 3.0 code needs using GearsAPI.Settings; beside GearsAPI.Settings.Global or GearsAPI.Settings.World, because the typed IValueModSetting<T> lives there. The binding and listener attributes need using GearsAPI.Attributes;.

Behavior changes to check for

  • GetSetting<T> needs the closed generic. GetSetting<ISliderGlobalSetting>("Fov") no longer compiles. GetSetting<ISliderGlobalSetting<int>>("Fov") returns null when the setting is a different type, so check for null.
  • Decide which value you want. CurrentValue was the applied value, which is now SettingValue. If you read it during a menu callback to preview a change, you probably want SelectedValue now.
  • OnSettingChanged is now OnSettingApplied, and carries no value. Read SettingValue from the setting argument, or switch to OnValueChanged, which carries it, typed.
  • Call RefreshUI() after changing a value from code. Assigning SelectedValue or SettingValue does not redraw the row, and Apply does not commit it, until you call RefreshUI() on the setting. See Change a setting from code.
  • Categories now appear in setting lists. A category is a setting in 3.0, so a category's GetAllSettings() returns the category itself first, followed by its settings. The GetAllGlobalSettings(), GetAllWorldSettings() and tab GetAllModSettings() lists include every category too. A 2.x loop that treats each entry as a value setting now meets categories, so skip any entry that is an IGlobalModSettingsCategory or IWorldModSettingsCategory.
  • World settings have no OnValueChanged or OnSettingApplied. Read SettingValue in OnWorldSettingsLoaded, which fires every time a world's values arrive. A [SettingOnValueChanged] on a world path does work, but only as an invoke-only listener that SyncSettingsToClass calls — it is never raised. Write it with includeInSync: true. Without the flag it never runs, and Gears logs nothing.
  • InitMod now runs after ModSettings.xml is read, not before. Settings you create in code there land on top of whatever the XML declared, so where the two disagree your code now wins where the XML used to; and GetOrCreateSetting<T> throws ArgumentException if the XML declared that setting as a different type. Every setting exists by then, so BindSettingsClass can be called from InitMod for global and world settings alike.
  • Loading values raises no change events. Restoring the player's saved global values, loading a world's values, and resetting them to defaults are all silent. Use SyncSettingsToClass to hand the loaded values to your listeners. See Loading a value in raises nothing.
  • Settings you create in code need all three values set. DefaultValue, SettingValue and SelectedValue all start at default(T).
  • TooltipKey on a setting is now DescriptionKey. On a tab it is still TooltipKey.
  • CreateTab, CreateCategory and CreateSetting throw on a duplicate name, which matters now that your XML may declare the same names. Use the GetOrCreate… form when both sides may create it.
  • A callback that throws no longer silences your other IGearsModApi classes, but Gears still logs it as an error naming the type. Look for [GearsMod] Error thrown in.

The member map

GearsAPI 2.xGearsAPI 3.0
ISelectorGlobalSetting, ISliderGlobalSetting, ISwitchGlobalSetting (non-generic)ISelectorGlobalSetting<T>, ISliderGlobalSetting<T>, ISwitchGlobalSetting<T>
ISelectorWorldSetting, ISliderWorldSetting, ISwitchWorldSettingISelectorWorldSetting<T>, ISliderWorldSetting<T>, ISwitchWorldSetting<T>
IGlobalValueSetting.CurrentValue : stringIGlobalValueSetting<T>.SettingValue : T (applied), SelectedValue : T (the menu), DefaultValue : T
IGlobalValueSetting.DefaultValue : stringDefaultValue : T
IWorldModSetting.CurrentValue and DefaultValue : stringon the typed interfaces through IValueModSetting<T>; IWorldModSetting keeps only Category
string in, string out everywhereIValueModSettingBase.SetSettingValueFromString(string) and GetSerializedFormattedSettingValue() for code that must stay stringly typed
OnSettingChanged, an OnSettingChangedEvent(IGlobalModSetting setting, string newValue)OnSettingApplied, an OnSettingAppliedEvent(IGlobalModSetting setting), with no value argument
—ValueChangedEvent<T>(IValueModSetting<T> setting, T newValue) through OnValueChanged, global only
—OnSelectedChangedEvent<T>(IValueModSetting<T> setting, T newValue) through OnSelectedChanged
ISelectorGlobalSetting.SetAllowedValues(string), a comma listgone; split it yourself and call SetAllowedValues(T[])
SetAllowedValues(string[])SetAllowedValues(T[])
SetAllowedValues(int inc, int min, int max) and (float, float, float)SetAllowedValues(T increment, T min, T max)
GetAllowedValues() : string[]GetAllowedValues() : T[], plus GetAllowedValuesDisplay() : string[]
FormatterString on Selector and SliderUiFormatter and SerializationFormatter
—LocalizationPrefix and SelectByIndex(int) on Selector
ISliderGlobalSetting.GetAllowedValues()gone; use Increment, Min and Max
ISwitchGlobalSetting.LeftValue and RightValue : string, SetSwitchValues(string, string)LeftValue and RightValue : T, SetSwitchValues(T, T), plus GetLeftText(), GetRightText(), SelectButton and GetSelectedButton
IColorSelectorGlobalSetting, string-basedIColorSelectorGlobalSetting : IGlobalValueSetting<UnityEngine.Color>
IModSetting.TooltipKey, HasToolTip(), GetToolTipText()DescriptionKey, HasDescription(), GetDescriptionText(), plus the new CaptionKey, HasCaption() and GetCaptionText(). TooltipKey survives only on IGlobalModSettingsTab.
IGlobalModSettingsCategory : IModSettingIGlobalModSettingsCategory : IGlobalModSetting
IWorldModSettingsCategory : IModSettingIWorldModSettingsCategory : IWorldModSetting
—IModSetting.IsChanged, IsDefault, ResetToDefault(), DiscardCurrentChange(), ApplyCurrentChange(), RefreshUI(), the OnEnabled event, HasPreviews(), GetCurrentPreview(), AddPreview(string)
—IValueModSetting<T>.GetPreview(T) and AddPreview(T, string) for per-value images
—IGlobalModSetting.RestartScope : SettingRestartScope
—BindSettingsClass(Type) and SyncSettingsToClass(Type) on both IModGlobalSettings and IModWorldSettings, the [Setting] and [SettingPlayerAction] binding attributes, and the [SettingOnValueChanged], [SettingOnSelectedChanged], [SettingOnApplied] and [SettingOnEnabled] attributes
—[SettingsSerializationProvider], [SettingParser] and [SettingFormatter]
UnchangedIGearsModApi, IGearsMod, GearsSettingsManager.GetMods() and GetGearsMod(name), all Get*, Create*, GetOrCreate*, Add* and Remove* on tabs and categories, SaveSettings(), IControlBindingSetting.PlayerAction and ClearBinding(), and IModSetting.Enabled

Part 3: Check your work

Start the game and search the log for these lines. Each one names your mod, a setting, a type or an assembly:

Log lineWhat it means
[Gears] [ModSettingsFromXml]The whole file was rejected. Almost always the version="2" is missing or wrong.
[Gears] [XmlSettingsParserV2]That setting was dropped, most often for a missing defaultValue (Step 3) or a missing or unknown type (Step 2).
[Gears] [GearsMod] '<Mod>' is using an outdated GearsAPIThe C# still targets 2.x and reached a type that no longer exists. Rebuild against the shipped GearsAPI.dll.
[Gears] [GearsMod] Error thrown in <Callback>() by <Type>, with a MissingMethodExceptionThe code calls a 2.x member that was removed from a type that still exists, such as TooltipKey or OnSettingChanged.
[Gears] Failed iterating types in assemblyA type in your assembly could not load, often because a field still uses a removed 2.x type. Gears found no IGearsModApi there, so none of its callbacks ran.

The log does not catch everything. Gears ignores leftover V1 markup without a word, and drops a Color or Binding under <World> the same way. Search your ModSettings.xml for <Property, leftValue, rightValue, wrap=, a value or color attribute on a setting, and tooltipKey on anything but a <Tab>.

Then open the Mods menu and confirm every setting you expect is there, with the text you expect. A raw key such as myModFov means a missing Localization.csv row.

See Troubleshooting for every log line and its fix.

On this page