In other languages:

Tutorial:Mod settings

From Official Factorio Wiki
Revision as of 13:40, 26 September 2020 by Darkfrei (talk | contribs) (→‎Usage)
Jump to navigation Jump to search

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 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.

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. 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, required)
  • type (string, required)
  • localised_name (localised string, optional)
  • localised_description (localised string, optional)
  • order (string, optional)

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

  • hidden (boolean, optional)
  • setting_type (string, required)

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:

  • bool-setting:
    • default_value (bool, required) - Defines the default value of the setting, in this case whether the checkbox is checked or not.
  • int-setting:
    • default_value (int, required) - Defines the default value of the setting.
    • minimum_value (int, optional) - Defines the lowest possible number.
    • maximum_value (int, optional) - Defines the highest possible number.
    • 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.
  • double-setting:
    • default_value (double, required) - 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(double), optional) - Makes it possible to force the player to choose between the defined numbers, creates a dropdown instead of a textfield.
  • string-setting:
    • default_value (string, required) - Defines the default value of the setting.
    • 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.
    • 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.

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.[1]

The setting_type property

The mod settings gui. Can be reached from the main menu -> Options -> 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 item>
my-mod-string-test-setting-item-1=Item 1 localized string

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

Variable collision layer for mod compatibility:

--	in settings.lua
data:extend({
	{
		type = "string-setting",
		name = "mymod-collision-layer",
		setting_type = "startup",
		default_value = "layer-11",
		allowed_values = {"layer-11", "layer-12", "layer-13", "layer-14", "layer-15"}
	}
})

--	in data.lua
local collision_layer = settings.startup["mymod-collision-layer"].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(game.get_player(event.player_index))["my-mod-kill-player-on-entity-built"].value
    if setting_value then
        game.players[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 = "bool-setting",
        name = "my-mod-ever-crafted-item",
        setting_type = "runtime-per-user",
        default_value = false
    },
    {
        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_player_crafted_item, function(event)
    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"}
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