Tutorial:Mod settings: Difference between revisions
(-> mod-settings.dat) |
m (→Changing another mod's settings: better settings change example) |
||
(12 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 | 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 [ | 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 | ||
Line 18: | Line 18: | ||
== 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 | * name - [[Types/string|string]] - Mandatory. | ||
* type - [[Types/string|string]] - Mandatory. | |||
* localised_name | * localised_name - [[Types/LocalisedString|LocalisedString]] - Optional. | ||
* localised_description - [[Types/LocalisedString|LocalisedString]] - Optional. | |||
* [[Types/Order|order]] | * [[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 | * 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. | ||
==== bool-setting ==== | |||
* default_value - [[Types/bool|bool]] - Mandatory. | |||
** 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 ==== | |||
* default_value - [[Types/int64|int64]] - Mandatory. | |||
** Defines the default value of the setting. | |||
* minimum_value - [[Types/int64|int64]] - Optional. | |||
** 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 ==== | |||
* default_value - [[Types/double|double]] - Mandatory. | |||
** Defines the default value of the setting. | |||
* minimum_value - [[Types/double|double]] - Optional. | |||
** 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 ==== | |||
* default_value - [[Types/string|string]] - Mandatory. | |||
** Defines the default value of the setting. | |||
** allowed_values | * allow_blank - [[Types/bool|bool]] - Optional. - Default: false | ||
** 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 | [[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 === | === 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]. | ||
In the prototype stage you can access settings of the setting_type "startup" | In the prototype stage you can access settings of the setting_type "startup" by indexing <code>settings.startup</code> with the name of the setting. Example: | ||
<syntaxhighlight lang="lua"> | <syntaxhighlight lang="lua"> | ||
--in settings.lua: | --in settings.lua: | ||
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 | In the control stage, the settings of the setting_type "runtime-global" can be accessed using <code>settings.global["setting-name"]</code> (see [https://lua-api.factorio.com/latest/LuaSettings.html LuaSettings]). 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 <PlayerIdentification>])["setting-name"]</code> or <code>game.players[[https://lua-api.factorio.com/latest/Concepts.html#PlayerIdentification <PlayerIdentification>]].mod_settings["setting-name"]</code>. | ||
</div> | </div> | ||
Example: | Example: | ||
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 | 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. | game.get_player(event.player_index).die() | ||
end | end | ||
end) | end) | ||
</syntaxhighlight> | </syntaxhighlight> | ||
=== Writing to settings === | === Writing to your own 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 [https://lua-api.factorio.com/latest/Concepts.html#ModSetting ModSetting] table to the setting. | It is possible for mods to write to their own runtime (global or per player) mod settings. This is done by writing a new [https://lua-api.factorio.com/latest/Concepts.html#ModSetting ModSetting] table to the setting. | ||
Line 177: | Line 206: | ||
--in settings.lua: | --in settings.lua: | ||
data:extend({ | data:extend({ | ||
{ | { | ||
type = "string-setting", | type = "string-setting", | ||
Line 193: | Line 216: | ||
--in control.lua: | --in control.lua: | ||
script.on_event(defines.events. | script.on_event(defines.events.on_rocket_launched, function() | ||
settings. | settings.global["my-mod-always-difficult"] = {value = "yes"} | ||
end) | end) | ||
</syntaxhighlight> | |||
=== Changing another mod's settings === | |||
After creating a setting in the settings stage, it is stored in data.raw until the end of the stage, so it can be altered from another mod. | |||
Example: | |||
<syntaxhighlight lang="lua"> | |||
--in settings-updates.lua: | |||
data.raw["string-setting"]["my-mod-always-difficult"].order = "abc" | |||
</syntaxhighlight> | |||
If the intent is to force the setting to be of a certain value that cannot be modified by players, the properties [[#The_hidden_property|hidden]], allowed_values and forced_value can be used. Modifying existing settings of other mods can be useful for mod packs or other mod compatibility goals.[https://forums.factorio.com/viewtopic.php?p=531322#p531322] | |||
Example: | |||
<syntaxhighlight lang="lua"> | |||
--in settings-updates.lua: | |||
data.raw["string-setting"]["my-mod-always-difficult"].hidden = true | |||
data.raw["string-setting"]["my-mod-always-difficult"].allowed_values = {"no"} | |||
data.raw["string-setting"]["my-mod-always-difficult"].default_value = "no" | |||
</syntaxhighlight> | </syntaxhighlight> | ||
=== 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. |
Latest revision as of 12:09, 6 December 2022
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:
- name - string - Mandatory.
- type - string - Mandatory.
- localised_name - LocalisedString - Optional.
- localised_description - LocalisedString - Optional.
- order - string - Optional.
In addition to the standard properties, mod settings also contain:
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]
- Only loaded if
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 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
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" by indexing settings.startup
with the name of the setting. 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
In the control stage, the settings of the setting_type "runtime-global" can be accessed using settings.global["setting-name"]
(see LuaSettings). Settings of the setting_type "runtime-per-user" are accessed using settings.get_player_settings(<PlayerIdentification>)["setting-name"]
or game.players[<PlayerIdentification>].mod_settings["setting-name"]
.
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 your own 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)
Changing another mod's settings
After creating a setting in the settings stage, it is stored in data.raw until the end of the stage, so it can be altered from another mod.
Example:
--in settings-updates.lua:
data.raw["string-setting"]["my-mod-always-difficult"].order = "abc"
If the intent is to force the setting to be of a certain value that cannot be modified by players, the properties hidden, allowed_values and forced_value can be used. Modifying existing settings of other mods can be useful for mod packs or other mod compatibility goals.[3]
Example:
--in settings-updates.lua:
data.raw["string-setting"]["my-mod-always-difficult"].hidden = true
data.raw["string-setting"]["my-mod-always-difficult"].allowed_values = {"no"}
data.raw["string-setting"]["my-mod-always-difficult"].default_value = "no"
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 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
- 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.