Per-command configuration
Every command receives a props.Configuration instance, a small in-memory key-value store scoped to that command, this plugin session.
Run = function(props)
local count = props.Configuration:Get("RunCount", 0)
props.Configuration:Set("RunCount", count + 1)
props:SendNotification(string.format("Run %d times this session", count + 1))
end
Why this exists
It's tempting to reach for a module-level local instead:
local runCount = 0 -- DON'T rely on this surviving between executions
return {
Run = function(props)
runCount += 1
props:SendNotification(string.format("Run %d times", runCount))
end,
}
That counter resets to 0 on every Execute press. Reason: the plugin requires your command via RequireNoCache — it clones the ModuleScript into a scratch container, requires the clone, runs Run, then throws the clone away. Roblox's normal require cache (which is what would ordinarily preserve module-level state across invocations) never gets populated, because the clone is a different Instance every time.
Configuration is the workaround: it lives on the plugin's ScriptHandler singleton, outside any cloned module. So :Get / :Set survive across Run invocations because they're not stored on the script itself.
If you ever migrate a command off CommandRunner and into a plain ModuleScript that's required normally, you can replace Configuration with module-level locals and they'll behave the same way. Inside CommandRunner specifically, Configuration is the only correct place to put cross-Run state.
API
| Method | Returns | Notes |
|---|---|---|
props.Configuration:Get(key, default) | any | default is returned when key is unset (default itself defaults to nil). |
props.Configuration:Set(key, value) | nil | Stores value under key. |
Lifetime
Configuration is in-memory only. It survives:
- Multiple Execute presses within one widget session.
- Switching between commands and back.
It does not survive:
- Plugin reload (Studio restart, "Disable plugin" → "Enable plugin").
- The widget being closed and reopened, actually it does survive widget toggle, since the Configuration instance lives on the
ScriptHandlersingleton, not on the UI. But treat it as session-scoped to be safe.
If you need durable per-command settings, use props.Plugin:SetSetting(key, value) directly. It's namespaced per-plugin and persists across sessions.
When to reach for it
Good fits:
- A counter that resets when the user reloads the plugin.
- A "previous value" cache so a command can compute a delta.
- Memoizing an expensive computation across multiple Execute presses.
Bad fits:
- Anything you'd be sad to lose on plugin reload, use
plugin:SetSettingfor that. - Anything that needs to be visible across commands,
Configurationis per-command.
A worked example
A toggle that remembers its last "on" timestamp so you can compute idle time:
return {
Arguments = {
Toggle = {
Type = "switch",
Default = false,
Run = function(props, value)
if value then
props.Configuration:Set("LastOnAt", os.clock())
else
local on = props.Configuration:Get("LastOnAt")
if on then
props:SendNotification(
string.format("Was on for %.1fs", os.clock() - on)
)
end
end
end,
},
},
DisableExecuteButton = true,
Run = function() end,
}