SoulFire LogoSoulFire

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:

  1. define public static final properties inside a SettingsObject
  2. listen for a settings registry event
  3. register a page with SettingsPageRegistry
  4. read values back through a SettingsSource

The official example plugin uses this pattern.

Scopes

SoulFire uses three setting scopes:

ScopeSource typeMeaning
serverSettingsSource.Serverone value for the whole SoulFire server
instanceSettingsSource.Instanceone value for an instance
botSettingsSource.Botone 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 typeUse it for
BooleanPropertyenable flags and simple toggles
IntPropertyinteger values with minimum and maximum limits
DoublePropertydecimal values
StringPropertytext, URLs, prompts, commands, and other strings
ComboPropertyenum-like selections
StringListPropertylists of strings
MinMaxPropertymin/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 property
  • SettingsPage: 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 memory
  • persistentMetadata() 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

On this page