Guides › Development

Plugin settings

How settings work

A plugin's settings live in an ordinary AzerothCore module config. Two files describe them:

File Read by Purpose
conf/<name>.conf.dist the server The options, their defaults and comments. Named by config in plugin.json.
settings.json the launcher Which of those options the Settings tab shows, with labels, types and limits.

The launcher never talks to your code. It writes values into the plugin's config file; the server reads that file through its config manager. A plugin without config gets no Settings group, whatever its settings.json says.

This guide extends the example.greeter plugin from Your first plugin. The full field reference is Launcher settings.

1. Add the config

conf/greeter.conf.dist:

#
# example.greeter
#

[worldserver]

#
#    Greeter.Enable
#        Description: Greet players when they log in.
#        Default:     1 - (Enabled)
#                     0 - (Disabled)
#

Greeter.Enable = 1

#
#    Greeter.Message
#        Description: The text sent on login.
#        Default:     "Welcome to the server!"
#

Greeter.Message = "Welcome to the server!"

and name it in plugin.json:

"config": "conf/greeter.conf.dist"

AddPlugin copies the conf folder into the laid-out plugin. The config's name is the file name without .dist: greeter.conf.

2. Read the values

#include "Chat.h"
#include "Config.h"
#include "Player.h"
#include "ScriptMgr.h"

class GreeterPlayerScript : public PlayerScript
{
public:
    GreeterPlayerScript() : PlayerScript("GreeterPlayerScript", { PLAYERHOOK_ON_LOGIN }) { }

    void OnPlayerLogin(Player* player) override
    {
        if (!sConfigMgr->GetOption<bool>("Greeter.Enable", true))
            return;
        ChatHandler(player->GetSession()).SendSysMessage(
            sConfigMgr->GetOption<std::string>("Greeter.Message", "Welcome to the server!"));
    }
};

Where the values come from, later sources winning:

  1. the .dist in the plugin folder, which declares every option;
  2. modules/greeter.conf beside the config the server was started with (-c), when it exists, key by key: a key it leaves out keeps the .dist value. In a LonelyIce server folder that is configs/modules/greeter.conf. Only when there is no modules folder beside the config does the core look in its default config directory;
  3. an environment variable AC_<KEY> (Greeter.Message becomes AC_GREETER_MESSAGE), fixed when the server process starts.

A plain worldserver of the fork reads the same files; nothing here needs the launcher. reload config reads 1 and 2 again; 3 still wins afterwards.

3. Describe the Settings group

settings.json next to plugin.json:

{
  "group": { "en": "Greeter", "ru": "Приветствие" },
  "hint":  { "en": "What players see when they log in", "ru": "Что видят игроки при входе" },
  "fields": [
    { "key": "Greeter.Enable", "type": "bool", "apply": "reload",
      "label": { "en": "Greet on login", "ru": "Приветствовать при входе" } },
    { "key": "Greeter.Message", "type": "string", "apply": "reload",
      "label": { "en": "Message", "ru": "Сообщение" },
      "hint": { "en": "Sent as a system message", "ru": "Отправляется системным сообщением" } }
  ]
}

The launcher finds settings.json in the plugin folder by itself. "settings" in plugin.json can instead name another file or hold the same object inline. A bare array of fields is also accepted; the group is then named after the plugin and its hint is the plugin's description.

The group appears in the Settings tab under Plugins, sorted by group name. On the Plugins page the plugin gets a gear button that opens it.

Field types

type Control Written to the config
bool checkbox In the style the file already uses for that key: 1/0, true/false or yes/no.
int number field Parsed, clamped to min/max, rounded to an integer. A value that is not a number is refused.
float number field Clamped to min/max when either is given; otherwise written as typed.
string text field As typed.
choice drop-down of options The chosen option's value.

Other keys of a field:

Key Meaning
key The config option. Required.
label, hint Localized strings (an object of language → text, or a plain string). label defaults to the key.
min, max Numbers, for int and float.
options For choice: [ { "value": "0", "label": { "en": "Off" } }, ... ]. Values may be written as numbers or strings.
default Shown when neither the config nor the .dist has the key.
apply now, reload or restart (the default); see below.

A field without key or with an unknown type is left out without a message. A settings.json that is not valid JSON adds an entry to the launcher's events, and the plugin gets no group.

label, hint, group and option labels are shown in the launcher's language, then English, then any language present. A real example with every common type is settings.json of playerbots or lonelyice.citizens.

What saving does

  1. For every changed value, the launcher opens modules/<name>.conf beside the server's config (configs/modules/<name>.conf), the file the server reads. If it does not exist yet, it is created as a copy of the plugin's .dist first. (The setup wizard also copies the .dist of every installed plugin there.)
  2. The value is replaced in place, keeping the line's quotes and the file's comments and order. A key missing from the file is appended at the end, quoted for string and choice.
  3. If the server is running, the launcher acts on the strongest apply among the changed fields:
apply After saving
restart The server is restarted.
reload The launcher sends reload config to the server console.
now Nothing.

When the server is not running, the values are simply read on its next start.

reload works only if your code reads the value again after a reload: call sConfigMgr->GetOption where the value is used, or re-read your cached values in a WorldScript hook OnAfterConfigLoad(bool reload), as lonelyice.citizens does. The core's config manager keeps what it read in memory and re-reads the files only on reload config or a restart, so a now value saved through the launcher reaches sConfigMgr->GetOption only at the next reload (another field's reload, or reload config typed in the console) or start. now suits only values your code reads from the file by itself; for server options use reload or restart.

When the config is elsewhere

If the server's worldserver.conf is not in <server folder>/configs (a custom config path in lonelyice.ini), nothing changes for a plugin: the launcher passes the config by its absolute path, the core reads the module configs from the modules folder beside it, and Settings writes to that same folder. reload works there as well; no module setting is passed as an AC_* variable.

Checklist

  • Every key in settings.json exists in the .dist, with a sensible default.
  • min and max match what your code accepts; the code still validates, since the file can be edited by hand.
  • apply is restart unless your code really re-reads the value.
  • Texts have at least an en entry.