In other languages:

Tutorial:Mod settings: Difference between revisions

From Official Factorio Wiki
Jump to navigation Jump to search
(→‎Usage: Created "Writing to settings")
(fixed link to playerindenfication concept)
(14 intermediate revisions by 3 users not shown)
Line 3: Line 3:


== Overview ==
== Overview ==
Each mod can specify settings that users can change. The values of these settings can be accessed from inside the data stage or the control stage depending on their [[#The_setting_type_property|setting_type]] and allow to conditionally create or modify prototypes and to add configuration options to control scripts. They were added in version 0.15 to allow modders to easily create a graphical interface for their configuration options, instead of relying on text files that had to be edited manually by the users.
Each mod can specify settings that users can change. The values of these settings can be accessed from inside the data stage or the control stage depending on their [[#The_setting_type_property|setting_type]] and allow to conditionally create or modify prototypes and to add configuration options to control scripts. They allow modders to easily create a graphical interface for their configuration options, instead of using on text files that have to be edited manually by users.


== Location ==
== Location ==


Mod settings are defined in the [http://lua-api.factorio.com/latest/Data-Lifecycle.html settings stage]. This stage is loaded before the data stage. There are three files in which settings can be defined:
Mod settings are defined in the [https://lua-api.factorio.com/latest/Data-Lifecycle.html settings stage]. This stage is loaded before the data stage. There are three files in which settings can be defined:
* settings.lua
* settings.lua
* settings-updates.lua
* settings-updates.lua
* settings-final-fixes.lua
* settings-final-fixes.lua


First the settings.lua file is called for each mod, in the order of their dependencies and then in the [[:Wikipedia:natural sort order|natural sort order]]. Next, the settings-updates.lua file is called for each mod <sup>(in the same order)</sup> and finally the settings-final-fixes.lua file is called for each mod <sup>(in the same order)</sup>. The settings are all defined in the same stage and their user defined values are not available, therefore mods cannot conditionally create settings depending on the values of other mod settings. Since the settings stage gets loaded first, there is also no prototype data or [http://lua-api.factorio.com/latest/LuaRemote.html remote interface] available.
First the settings.lua file is called for each mod, in the order of their dependencies and then in the [[:Wikipedia:natural sort order|natural sort order]]. After settings.lua has been called for all mods, the settings-updates.lua file is called for each mod <sup>(in the same order)</sup> and finally the settings-final-fixes.lua file is called for each mod <sup>(in the same order)</sup>. These 3 different phases of the settings stage allow to change settings of other mods without needing to rely on dependencies to load last. The settings are all defined in the same stage and their user defined values are not available, therefore mods cannot conditionally create settings depending on the values of other mod settings. Since the settings stage gets loaded first, there is also no prototype data or [http://lua-api.factorio.com/latest/LuaRemote.html remote interface] available.


The "mod-settings.json" file stored in the [[Application_directory#User_Data_directory|mods folder]] for the game contains the local players settings between game sessions similar to the player-data.json file.
The "mod-settings.dat" file stored in the [[Application_directory#User_Data_directory|mods folder]] for the game contains the local players settings between game sessions similar to the player-data.json file.


== Creation ==
== Creation ==


Mod settings are defined and modified by using the data table. This works [[Tutorial:Modding_tutorial/Gangsir#Prototype_creation|the same way as other prototypes]]. An example would be:
Mod settings are defined and modified by using the data table during the settings stage. This works [[Tutorial:Modding_tutorial/Gangsir#Prototype_creation|the same way as other prototypes]]. An example would be:
<syntaxhighlight lang="lua">
<syntaxhighlight lang="lua">
data:extend({
data:extend({
Line 31: Line 31:


As you can see, the setting has multiple properties. Each setting supports the following standard prototype properties:
As you can see, the setting has multiple properties. Each setting supports the following standard prototype properties:
* name ([http://lua-api.factorio.com/latest/Builtin-Types.html#string string], required)
* name - [[Types/string|string]] - Mandatory.
* type (string, required)
* type - [[Types/string|string]] - Mandatory.
* localised_name ([http://lua-api.factorio.com/latest/Concepts.html#LocalisedString localised string], optional)
* localised_name - [[Types/LocalisedString|LocalisedString]] - Optional.
* localised_description (localised string, optional)
* localised_description - [[Types/LocalisedString|LocalisedString]] - Optional.
* [[Types/Order|order]] (string, optional)
* [[Types/Order|order]] - [[Types/string|string]] - Optional.


In addition to the standard properties, mod settings also contain:
In addition to the standard properties, mod settings also contain:


* setting_type (string, required)
* hidden - [[Types/bool|bool]] - Optional.
* setting_type - [[Types/string|string]] - Mandatory.


=== The name property ===
=== The name property ===
Line 54: Line 55:
* string-setting - a string textfield (or selection dropdown)
* string-setting - a string textfield (or selection dropdown)


Depending on the type, the prototype also allows or requires additional properties:
Depending on the type, the prototype also allows or requires additional properties, these are listed below.


<div class="factorio-list">
==== bool-setting ====
* '''bool-setting''':
* default_value - [[Types/bool|bool]] - Mandatory.
** default_value ([http://lua-api.factorio.com/latest/Builtin-Types.html#boolean bool], required) - Defines the default value of the setting, in this case whether the checkbox is checked or not.
** Defines the default value of the setting, in this case whether the checkbox is checked or not.
* forced_value - [[Types/bool|bool]] - Optional.
** Only loaded if <code>hidden = true</code>. This forces the setting to be of this value. This can be useful for mod compatiblity.<sup>[https://forums.factorio.com/viewtopic.php?p=531322#p531322]
</sup>


* '''int-setting''':
==== int-setting ====
** default_value ([http://lua-api.factorio.com/latest/Builtin-Types.html#int int], required) - Defines the default value of the setting.
* default_value - [[Types/int64|int64]] - Mandatory.
** minimum_value (int, optional) - Defines the lowest possible number.
** Defines the default value of the setting.
** maximum_value (int, optional) - Defines the highest possible number.
* minimum_value - [[Types/int64|int64]] - Optional.
** allowed_values (array(int), optional) - Makes it possible to force the player to choose between the defined numbers, creates a dropdown instead of a texfield.
** Defines the lowest possible number.
* maximum_value - [[Types/int64|int64]] - Optional.
** Defines the highest possible number.
* allowed_values - [[Types/table|array]] of [[Types/int64|int64]] - Optional.
** Makes it possible to force the player to choose between the defined numbers, creates a dropdown instead of a texfield.
** If only one allowed value is given, the settings is forced to be of that value.


* '''double-setting''':
==== double-setting ====
** default_value ([http://lua-api.factorio.com/latest/Builtin-Types.html#double double], required) - Defines the default value of the setting.
* default_value - [[Types/double|double]] - Mandatory.
** minimum_value (double, optional) - Defines the lowest possible number.
** Defines the default value of the setting.
** maximum_value (double, optional) - Defines the highest possible number.
* minimum_value - [[Types/double|double]] - Optional.
** allowed_values (array(double), optional) - Makes it possible to force the player to choose between the defined numbers, creates a dropdown instead of a textfield.
** Defines the lowest possible number.
* maximum_value - [[Types/double|double]] - Optional.
** Defines the highest possible number.
* allowed_values - [[Types/table|array]] of [[Types/double|double]] - Optional.
** Makes it possible to force the player to choose between the defined numbers, creates a dropdown instead of a textfield.
** If only one allowed value is given, the settings is forced to be of that value.


* '''string-setting''':
==== string-setting ====
** default_value (string, required) - Defines the default value of the setting.
* default_value - [[Types/string|string]] - Mandatory.
** allow_blank (boolean, optional) - Defaults to false, defines whether it's possible for the user to set the textfield to empty and apply the setting.
** Defines the default value of the setting.
** allowed_values (array(string), optional) - Makes it possible to force the player to choose between the defined strings, creates a dropdown instead of a textfield. The strings inside the array can be localized (translated), see below.
* allow_blank - [[Types/bool|bool]] - Optional. - Default: false
</div>
** Defines whether it's possible for the user to set the textfield to empty and apply the setting.
* auto_trim - [[Types/bool|bool]] - Optional. - Default: false
** Whether values that are input by the user should have whitespace removed from both ends of the string.
* allowed_values - [[Types/table|array]] of [[Types/string|string]] - Optional.
** Makes it possible to force the player to choose between the defined strings, creates a dropdown instead of a textfield. The strings in the dropdown can be localized (translated) and can have a tooltip, see below.
** If only one allowed value is given, the settings is forced to be of that value.


=== The order property ===
=== The order property ===
Line 86: Line 105:


For more info on how to use the order string, see [[Types/Order]].
For more info on how to use the order string, see [[Types/Order]].
=== The hidden property ===
The hidden property can be used to hide mod settings from GUIs, so that they cannot be seen or changed by players. However, other mods can still access hidden settings.<sup>[https://forums.factorio.com/83316]</sup>


=== The setting_type property ===
=== The setting_type property ===
[[File:Mod_settings_gui.png|right|300px|thumb|The mod settings gui. Can be reached from the main menu -> Options -> Mod settings.]]
[[File:Mod_settings_gui.png|right|300px|thumb|The mod settings gui. Can be reached from the main menu → Settings → Mod settings.]]
There are the overall kinds of settings:
There are the overall kinds of settings:


Line 107: Line 130:


[string-mod-setting]
[string-mod-setting]
#<setting-name>-<dropdown-item-name>=<translated item>
#<setting-name>-<dropdown-item-name>=<translated dropdown item>
my-mod-string-test-setting-item-1=Item 1 localized string
my-mod-string-test-setting-item-1=Item 1 localized string
[string-mod-setting-description]
#<setting-name>-<dropdown-item-name>=<tooltip of dropdown item>
my-mod-string-test-setting-item-1=Item 1 localized tooltip


</pre>
</pre>
== Usage ==
== Usage ==
 
=== Reading settings ===
When accessing any mod setting, you will have to specifically access the ''value'' of the setting. The data type of the value depends on the type of the setting. For string settings that use a selection of allowed values, the value of the setting is one of the original string values defined in the prototype, the localization is ignored. See also: [http://lua-api.factorio.com/latest/Concepts.html#ModSetting ModSetting concept].
When accessing any mod setting, you will have to specifically access the ''value'' of the setting. The data type of the value depends on the type of the setting. For string settings that use a selection of allowed values, the value of the setting is one of the original string values defined in the prototype, the localization is ignored. See also: [http://lua-api.factorio.com/latest/Concepts.html#ModSetting ModSetting concept].


Line 131: Line 159:
data.raw.item["stone-wall"].stack_size = settings.startup["my-mod-stone-wall-stack-size"].value
data.raw.item["stone-wall"].stack_size = settings.startup["my-mod-stone-wall-stack-size"].value
</syntaxhighlight>
</syntaxhighlight>
<div class="plainlinks">
<div class="plainlinks">
In the control stage, the settings of the setting_type "runtime-global" can be accessed using <code>settings.global["setting-name"]</code>. Settings of the setting_type "runtime-per-user" are accessed using <code>settings.get_player_settings(game.get_player([http://lua-api.factorio.com/latest/Concepts.html#PlayerSpecification &lt;PlayerSpecification&gt;]))["setting-name"]</code> or <code>game.players[[http://lua-api.factorio.com/latest/Concepts.html#PlayerSpecification &lt;PlayerSpecification&gt;]].mod_settings["setting-name"]</code>.
In the control stage, the settings of the setting_type "runtime-global" can be accessed using <code>settings.global["setting-name"]</code>. Settings of the setting_type "runtime-per-user" are accessed using <code>settings.get_player_settings([https://lua-api.factorio.com/latest/Concepts.html#PlayerIdentification &lt;PlayerIdentification&gt;])["setting-name"]</code> or <code>game.players[[https://lua-api.factorio.com/latest/Concepts.html#PlayerIdentification &lt;PlayerIdentification&gt;]].mod_settings["setting-name"]</code>.
</div>
</div>
Example:
Example:
Line 142: Line 171:
         name = "my-mod-always-difficult",
         name = "my-mod-always-difficult",
         setting_type = "runtime-global",
         setting_type = "runtime-global",
         default_value = "yes"
         default_value = "yes",
         allowed_values = {"yes", "no"}
         allowed_values = {"yes", "no"}
     },
     },
Line 163: Line 192:


script.on_event(defines.events.on_built_entity, function(event)
script.on_event(defines.events.on_built_entity, function(event)
     local setting_value = settings.get_player_settings(game.get_player(event.player_index))["my-mod-kill-player-on-entity-built"].value
     local setting_value = settings.get_player_settings(event.player_index)["my-mod-kill-player-on-entity-built"].value
     if setting_value then
     if setting_value then
         game.players[event.player_index].die()
         game.get_player(event.player_index).die()
     end
     end
end)
end)
Line 177: Line 206:
--in settings.lua:
--in settings.lua:
data:extend({
data:extend({
    {
        type = "bool-setting",
        name = "my-mod-ever-crafted-item",
        setting_type = "runtime-per-user",
        default_value = false
    },
     {
     {
         type = "string-setting",
         type = "string-setting",
         name = "my-mod-always-difficult",
         name = "my-mod-always-difficult",
         setting_type = "runtime-global",
         setting_type = "runtime-global",
         default_value = "yes"
         default_value = "yes",
         allowed_values = {"yes", "no"}
         allowed_values = {"yes", "no"}
     }
     }
Line 193: Line 216:


--in control.lua:
--in control.lua:
script.on_event(defines.events.on_player_crafted_item, function(event)
script.on_event(defines.events.on_rocket_launched, function()
    settings.get_player_settings(game.get_player(event.player_index))["my-mod-ever-crafted-item"] = {value = true}
    -- We just allowed ourselves to check whether the player ever crafted an item in other game saves.
    -- Note that this only works if the player does not mess with the setting, and if their "keep mod settings per save" setting is off.
end)
 
script.on_event(on_rocket_launched, function()
     settings.global["my-mod-always-difficult"] = {value = "yes"}
     settings.global["my-mod-always-difficult"] = {value = "yes"}
end)
end)
Line 206: Line 223:
=== Tips ===
=== Tips ===


* Do not conditionally 'require(...)' things: This breaks the CRC checks and people will get errors trying to use your mod in multiplayer. 'require(...)' everything and then conditionally add the values to data.raw using the settings.
* Do not conditionally 'require(...)' things depending on mod settings: This breaks the CRC checks and people will get errors trying to use your mod in multiplayer. 'require(...)' everything and then conditionally add the values to data.raw using the settings.
* You should cache the settings table inside the event you use it in. Accessing it is relatively expensive (about as expensive as accessing game.*prototypes[...]), so when accessing it mutliple times within the same event, you should set a local variable to it (within the event) to improve performance.
* You should cache the settings table inside the event you use it in. Accessing it is relatively expensive (about as expensive as accessing game.*prototypes[...]), so when accessing it mutliple times within the same event, you should set a local variable to it (within the event) to improve performance.
** If you want to cache startup/and runtime settings outside of events, you will have to makes sure that the local variable of settings of the setting_type "runtime-global" are updated in the <code>on_runtime_mod_setting_changed</code> event.
** If you want to cache startup/and runtime settings outside of events, you will have to makes sure that the local variable of settings of the setting_type "runtime-global" are updated in the <code>on_runtime_mod_setting_changed</code> event.

Revision as of 18:04, 30 July 2021

This tutorial aims to explain how to create and use mod settings. Basic knowledge of modding is assumed, so you should have at least understood Gangsir's modding tutorial.

Overview

Each mod can specify settings that users can change. The values of these settings can be accessed from inside the data stage or the control stage depending on their setting_type and allow to conditionally create or modify prototypes and to add configuration options to control scripts. They allow modders to easily create a graphical interface for their configuration options, instead of using on text files that have to be edited manually by users.

Location

Mod settings are defined in the settings stage. This stage is loaded before the data stage. There are three files in which settings can be defined:

  • settings.lua
  • settings-updates.lua
  • settings-final-fixes.lua

First the settings.lua file is called for each mod, in the order of their dependencies and then in the natural sort order. After settings.lua has been called for all mods, the settings-updates.lua file is called for each mod (in the same order) and finally the settings-final-fixes.lua file is called for each mod (in the same order). These 3 different phases of the settings stage allow to change settings of other mods without needing to rely on dependencies to load last. The settings are all defined in the same stage and their user defined values are not available, therefore mods cannot conditionally create settings depending on the values of other mod settings. Since the settings stage gets loaded first, there is also no prototype data or remote interface available.

The "mod-settings.dat" file stored in the mods folder for the game contains the local players settings between game sessions similar to the player-data.json file.

Creation

Mod settings are defined and modified by using the data table during the settings stage. This works the same way as other prototypes. An example would be:

data:extend({
    {
        type = "bool-setting",
        name = "my-mod-test-setting",
        setting_type = "runtime-global",
        default_value = true
    }
})

As you can see, the setting has multiple properties. Each setting supports the following standard prototype properties:

In addition to the standard properties, mod settings also contain:

  • hidden - bool - Optional.
  • setting_type - string - Mandatory.

The name property

The name of the settings prototype should be unique to avoid mod conflicts since the mod settings are global across all mods. Because of that it is recommened to prefix mod settings with your mod name, "my-mod" in this example.

The type property

There are four types of mod settings:

  • bool-setting - a true/false checkbox
  • int-setting - a signed 64 bit integer textfield (or selection dropdown)
  • double-setting - a double precision floating point textfield (or selection dropdown)
  • string-setting - a string textfield (or selection dropdown)

Depending on the type, the prototype also allows or requires additional properties, these are listed below.

bool-setting

  • default_value - bool - Mandatory.
    • Defines the default value of the setting, in this case whether the checkbox is checked or not.
  • forced_value - bool - Optional.
    • Only loaded if hidden = true. This forces the setting to be of this value. This can be useful for mod compatiblity.[1]

int-setting

  • default_value - int64 - Mandatory.
    • Defines the default value of the setting.
  • minimum_value - int64 - Optional.
    • Defines the lowest possible number.
  • maximum_value - int64 - Optional.
    • Defines the highest possible number.
  • allowed_values - array of int64 - Optional.
    • Makes it possible to force the player to choose between the defined numbers, creates a dropdown instead of a texfield.
    • If only one allowed value is given, the settings is forced to be of that value.

double-setting

  • default_value - double - Mandatory.
    • Defines the default value of the setting.
  • minimum_value - double - Optional.
    • Defines the lowest possible number.
  • maximum_value - double - Optional.
    • Defines the highest possible number.
  • allowed_values - array of double - Optional.
    • Makes it possible to force the player to choose between the defined numbers, creates a dropdown instead of a textfield.
    • If only one allowed value is given, the settings is forced to be of that value.

string-setting

  • default_value - string - Mandatory.
    • Defines the default value of the setting.
  • allow_blank - bool - Optional. - Default: false
    • Defines whether it's possible for the user to set the textfield to empty and apply the setting.
  • auto_trim - bool - Optional. - Default: false
    • Whether values that are input by the user should have whitespace removed from both ends of the string.
  • allowed_values - array of string - Optional.
    • Makes it possible to force the player to choose between the defined strings, creates a dropdown instead of a textfield. The strings in the dropdown can be localized (translated) and can have a tooltip, see below.
    • If only one allowed value is given, the settings is forced to be of that value.

The order property

The order property can be used to change how the mod settings are ordered in the settings gui. Mod settings are sorted

  • first by mod
  • then by the setting "order" string
  • then finally by the setting name.

For more info on how to use the order string, see Types/Order.

The hidden property

The hidden property can be used to hide mod settings from GUIs, so that they cannot be seen or changed by players. However, other mods can still access hidden settings.[2]

The setting_type property

The mod settings gui. Can be reached from the main menu → Settings → Mod settings.

There are the overall kinds of settings:

  • startup: This kind of setting is available in the prototype stage, and can not be changed runtime. They have to be set to the same values for all players on a server.
  • runtime-global: This kind of setting is global to an entire save game and can be changed runtime. On servers, only admins can change these settings.
  • runtime-per-user: This kind of setting is only available runtime in the control.lua stage and each player has their own instance of this setting. When a player joins a server their local setting of "keep mod settings per save" determines if the local settings they have set are synced to the loaded save or if the save's settings are used.

This "setting_type" also determines in which tab the setting is showed in the mod settings menu.

Locale

The locale for mod settings works like any other locale in the game. The names of the groups for the setting name and description (tooltip) are "mod-setting-name" and "mod-setting-description". The dropdown items of a string setting can be localized in the "string-mod-setting" group, but the game falls back to just showing the name of the dropdown item if no localization is found. An example for mod setting localization that would be set within "locale/en/locale.cfg", is:

[mod-setting-name]
my-mod-test-setting=Localized test setting name

[mod-setting-description]
my-mod-test-setting=Localized test setting description

[string-mod-setting]
#<setting-name>-<dropdown-item-name>=<translated dropdown item>
my-mod-string-test-setting-item-1=Item 1 localized string

[string-mod-setting-description]
#<setting-name>-<dropdown-item-name>=<tooltip of dropdown item>
my-mod-string-test-setting-item-1=Item 1 localized tooltip

Usage

Reading settings

When accessing any mod setting, you will have to specifically access the value of the setting. The data type of the value depends on the type of the setting. For string settings that use a selection of allowed values, the value of the setting is one of the original string values defined in the prototype, the localization is ignored. See also: ModSetting concept.

In the prototype stage you can access settings of the setting_type "startup" using the settings table. Example:

--in settings.lua:
data:extend({
    {
        type = "int-setting",
        name = "my-mod-stone-wall-stack-size",
        setting_type = "startup",
        minimum_value = 1,
        default_value = 100
    }
})

--in data.lua:
data.raw.item["stone-wall"].stack_size = settings.startup["my-mod-stone-wall-stack-size"].value

Example:

--in settings.lua:
data:extend({
    {
        type = "string-setting",
        name = "my-mod-always-difficult",
        setting_type = "runtime-global",
        default_value = "yes",
        allowed_values = {"yes", "no"}
    },
    {
        type = "bool-setting",
        name = "my-mod-kill-player-on-entity-built",
        setting_type = "runtime-per-user",
        default_value = false
    }
})

--in control.lua:
script.on_init(function()
    if settings.global["my-mod-always-difficult"].value == "yes" then
        game.difficulty_settings.recipe_difficulty = 1
        game.difficulty_settings.technology_difficulty = 1
        game.difficulty_settings.technology_price_multiplier = 4
    end
end)

script.on_event(defines.events.on_built_entity, function(event)
    local setting_value = settings.get_player_settings(event.player_index)["my-mod-kill-player-on-entity-built"].value
    if setting_value then
        game.get_player(event.player_index).die()
    end
end)

Writing to settings

It is possible for mods to write to their own runtime (global or per player) mod settings. This is done by writing a new ModSetting table to the setting.

Example:

--in settings.lua:
data:extend({
    {
        type = "string-setting",
        name = "my-mod-always-difficult",
        setting_type = "runtime-global",
        default_value = "yes",
        allowed_values = {"yes", "no"}
    }
})

--in control.lua:
script.on_event(defines.events.on_rocket_launched, function()
    settings.global["my-mod-always-difficult"] = {value = "yes"}
end)

Tips

  • Do not conditionally 'require(...)' things depending on mod settings: This breaks the CRC checks and people will get errors trying to use your mod in multiplayer. 'require(...)' everything and then conditionally add the values to data.raw using the settings.
  • You should cache the settings table inside the event you use it in. Accessing it is relatively expensive (about as expensive as accessing game.*prototypes[...]), so when accessing it mutliple times within the same event, you should set a local variable to it (within the event) to improve performance.
    • If you want to cache startup/and runtime settings outside of events, you will have to makes sure that the local variable of settings of the setting_type "runtime-global" are updated in the on_runtime_mod_setting_changed event.
  • If you want to detect whether a certain mod is installed in the settings stage, you can use if mods["another-mods-name"] then, just like in the data stage.

See also