Settings And Metadata
Add typed settings and UI pages, then store plugin data in SoulFire.
SoulFire uses typed properties for settings. The server sends these properties to the GUI and CLI as structured metadata. You define an option once, and SoulFire shows it in each client.
How the settings model works
The usual flow is:
- define
public static finalproperties inside aSettingsObject - listen for a settings registry event
- register a page with
SettingsPageRegistry - read values back through a
SettingsSource
The official example plugin uses this pattern.
Scopes
SoulFire uses three setting scopes:
| Scope | Source type | Meaning |
|---|---|---|
| server | SettingsSource.Server | one value for the whole SoulFire server |
| instance | SettingsSource.Instance | one value for an instance |
| bot | SettingsSource.Bot | one value per bot/account inside an instance |
For most plugin behavior, SettingsSource.Bot is the right default because it lets users override behavior per bot.
Property types
These are the current built-in property families:
| Property type | Use it for |
|---|---|
BooleanProperty | enable flags and simple toggles |
IntProperty | integer values with minimum and maximum limits |
DoubleProperty | decimal values |
StringProperty | text, URLs, prompts, commands, and other strings |
ComboProperty | enum-like selections |
StringListProperty | lists of strings |
MinMaxProperty | min/max pairs like random delay windows |
Each property is identified by namespace + key.
Use your plugin ID as the namespace.
Example settings object
@NoArgsConstructor(access = AccessLevel.NONE)
public final class MySettings implements SettingsObject {
private static final String NAMESPACE = "my-plugin";
public static final BooleanProperty<SettingsSource.Bot> ENABLED =
ImmutableBooleanProperty.<SettingsSource.Bot>builder()
.sourceType(SettingsSource.Bot.INSTANCE)
.namespace(NAMESPACE)
.key("enabled")
.uiName("Enable My Plugin")
.description("Turn the plugin on for this bot")
.defaultValue(false)
.build();
}SoulFire discovers page properties by reflecting over public static final fields on the class you register.
Registering a plugin page
Plugin pages are normally registered from InstanceSettingsRegistryInitEvent:
@EventHandler
public void onSettingsRegistryInit(InstanceSettingsRegistryInitEvent event) {
event.settingsPageRegistry().addPluginPage(
MySettings.class,
"my-plugin",
"My Plugin",
this,
"puzzle",
MySettings.ENABLED
);
}That page definition is exported to the GUI and CLI.
The iconId values are Lucide icon IDs, the same icon set SoulFire uses elsewhere.
Reading settings
Read settings through the relevant SettingsSource:
var enabled = connection.settingsSource().get(MySettings.ENABLED);If you use a ComboProperty, you can map it to an enum:
var mode = connection.settingsSource().get(MySettings.MODE, MyMode.class);If you use a MinMaxProperty, you can either read the full structure or ask SoulFire for a random value within the stored window.
Why the GUI and CLI can render your settings automatically
SettingsPageRegistry exports two kinds of data:
SettingsDefinition: the type, description, defaults, and validation rules for each propertySettingsPage: the grouping, ordering, icon, owning plugin, and optional enabled toggle
SoulFire sends these definitions in the server and instance gRPC responses. The GUI and CLI use them to build your plugin page.
Temporary and saved metadata
Plugin state does not always belong in settings. SoulFire also gives you metadata holders.
Use:
metadata()for temporary data in memorypersistentMetadata()for data that must survive restarts
Both instance and bot objects expose metadata holders.
Metadata keys
Always use a namespaced MetadataKey:
private static final MetadataKey<Integer> COUNTER =
MetadataKey.of("my-plugin", "counter", Integer.class);Then read or write it through the holder:
var count = bot.metadata().getOrSet(COUNTER, () -> 0);
bot.metadata().set(COUNTER, count + 1);Rules for saved metadata
Use persistent metadata for plugin data such as:
- cached dialog or workflow progress
- last completed objective
- sticky proxy or routing hints
- cooldown or deduplication state that must survive reconnects
It is not a substitute for user-facing configuration. If users edit the value in the GUI or CLI, use a typed setting instead.
MetadataKey forbids the minecraft namespace.
Use your plugin ID or another plugin-owned namespace instead.
Built-in plugins worth studying
These internal plugins are especially useful reference points:
- AntiAFK for a small settings page and scheduler usage
- CaptchaSolver for richer mixed property types
- KillAura for metadata and controlling-task coordination
- DialogHandler for persistent low-level bot state bridged into the API layer
Next steps
How is this page?
Last updated on
