Commands
AstralStats registers a single ACF alias, /stats, split across three command classes that share it: StatsCommand (base subcommands), ModifiersCommand (the modifiers group), and ResourceCommand (the resource group). Each class carries its own class-level @CommandPermission, so the permission required for a subcommand depends on which class declares it — see the tables below. Every subcommand that mutates state (rather than just reporting a value) has its success message suppressed when silent-command-success is true in config.yml (see Configuration).
/stats
Class-level permission: stats.command.
Subcommand | Syntax | Completion | Description |
|---|---|---|---|
|
|
| Prints the computed stat value for a player. |
|
|
| Sets the stat's base value. |
|
|
| Resets the base value to the blueprint default. |
|
| — | Reloads the plugin configuration. |
/stats value <player> <statType>
Prints StatService.statValue(target, statType) — the fully computed value (base plus every active modifier, clamped to the blueprint's bounds; see Modifier computation) — to the sender as Stat value for <player> (<statType>): <value>. If <statType> has no loaded blueprint for that player, the computed value is -1.0 (see Value lookup); this always prints, regardless of silent-command-success.
/stats set <player> <statType> <value>
Calls StatService.setStatBaseValue(target, statType, value), which overwrites the stat's stored baseValue and immediately re-applies its handler (no need to wait for the next update tick). If <statType> has no matching StatInstance for the player, the call is a silent no-op. Success message: Set stat <statType> for <player> to <value> (suppressed by silent-command-success).
/stats reset <player> <statType>
Calls StatService.resetStatBaseValue(target, statType), which restores the stat's baseValue to its blueprint's base field and re-applies it immediately. Also a silent no-op if the stat isn't loaded for the player. Success message: Reset stat <statType> for <player> to its base value (suppressed by silent-command-success).
/stats reload
Sends the reloading message, calls AstralStats#loadConfiguration() (reloads config.yml, resources.yml, blueprints, and messages.yml — see Configuration), then sends reloaded on success or reload-failed on exception. This subcommand is not gated by silent-command-success — the reload messages always send. On failure the exception is logged to the server console.
/stats modifiers
Class-level permission: stats.command.modifiers.
Subcommand | Syntax | Completion | Description |
|---|---|---|---|
|
|
| Creates and permanently attaches a |
|
|
| Creates a |
|
|
| Removes every modifier on the player keyed |
<type> is a ModifierType: FLAT, RELATIVE, or PERCENTAGE (see Modifier computation for how each is folded into the stat's value).
/stats modifiers add <player> <modifierKey> <statType> <type> <value>
Builds a StatModifier(modifierKey, statType, ModifierSource.OTHER, EquipmentSlot.SADDLE, value, modifierType) and adds it via StatService.addModifier(...). Command-created modifiers always use ModifierSource.OTHER and a fixed EquipmentSlot.SADDLE — they aren't tied to any equipment slot the way item/armor-granted modifiers are. The modifier is permanent until removed with /stats modifiers remove or by code. Success message (modifier-added, suppressed by silent-command-success): You gave <player> a <type> <value> <stat> modifier with key <key>.
/stats modifiers addtimed <player> <modifierKey> <statType> <type> <value> <durationMs>
Aliases: addtimed, addtemp. Same modifier shape as add (ModifierSource.OTHER, EquipmentSlot.SADDLE), but routed through TemporaryEffectService.addTimedStatModifier(...) instead of being added directly — it auto-expires and is removed after durationMs milliseconds, and survives the target disconnecting and reconnecting before it expires (re-applied on join; see Modifier model). Validated before creating the modifier:
Check | Rejection message |
|---|---|
|
|
|
|
Success message (inline, not from messages.yml, suppressed by silent-command-success): Added timed modifier <key> to <player> for <duration>ms.
/stats modifiers remove <player> <modifierKey>
Calls StatService.removeModifier(target, modifierKey), which iterates every stat on the player's StatContainer and removes any modifier whose own key equals <modifierKey> — a single key removes it from every stat it was attached to, not just one. The @statModifiers completion lists only the target player's currently active modifier keys (via ModifierCompletionHandler, which reads the OnlinePlayer context value resolved earlier in the same command). Success message (modifier-removed, suppressed by silent-command-success): You removed the <key> modifier from <player>.
/stats resource
Class-level permission: stats.command.resource.
Subcommand | Syntax | Completion | Description |
|---|---|---|---|
|
|
| Lists the player's resources with current/max amounts. |
|
|
| Adds a delta to a resource's current amount. |
|
|
| Applies a delta that reverses after |
<resourceType> is a ResourceType name: HEALTH, MANA, STAMINA, SHIELD, or CHARGE (see Resources).
/stats resource list <player>
If the player has a loaded ResourceContainer, prints one line per resource: <TYPE>: <amount> / <max>, where <max> is StatService.statValue(target, resource.blueprint().maxStat()) — the resource's configured max-value stat, both formatted to two decimal places. If the player has no resource container loaded, replies No resources found for player <player> instead. Always prints — not gated by silent-command-success.
/stats resource add <player> <resourceType> <amount>
HEALTH is special-cased to the player's vanilla health rather than AstralStats' resource tracking: the command adds amount to target.getHealth() directly, capping the result at the player's Attribute.MAX_HEALTH value. No lower bound is applied on this path — a sufficiently negative amount is passed straight to Player#setHealth, which throws if the result is below 0. Every other ResourceType goes through ResourceService.resourceValue(target, resourceType, d -> d + amount), adding the delta to the stored resource amount (see Resources for clamping behavior). Success message (inline, suppressed by silent-command-success): Added <amount> of resource <resourceType> to player <player>.
/stats resource timed <player> <resourceType> <amount> <durationMs>
Aliases: timed, temp. Applies amount to the resource now and schedules the exact inverse (-amount) to run when the effect expires, via TemporaryEffectService.addTimedResourceEffect(...). Like timed modifiers, a timed resource effect survives the target going offline: the apply/reverse operators only run against an online player, and pending effects are re-applied on reconnect (or discarded if they expired while offline). Validated before scheduling:
Check | Rejection message |
|---|---|
|
|
|
|
|
|
Success message (inline, suppressed by silent-command-success): Applied timed resource effect to <player>: <+/-amount> <resourceType> for <duration>ms.
Argument resolution
<statType>resolves to an AdventureKeythrough the customKeyContextResolver, registered withregisterContext(Key.class, ...)inAstralStats#onEnable. It pops the raw argument and callsKey.key(input)directly — any stringKey.keyaccepts is allowed syntactically, but a stat only exists for a player once a matching blueprint is loaded, so in practice<statType>should be a fully namespaced blueprint key such asastralstats:attack_damage(see Stat catalog). An unparsable key ("Invalid key:<input>") is rejected before the command method runs.<resourceType>and modifier<type>resolve via ACF's built-in enum context handling, againstResourceTypeandModifierTyperespectively — neither has a custom context resolver registered.<player>resolves to ACF'sOnlinePlayercontext — the target must be online; the command fails before the method runs if they aren't.
Registered completions
Registered in AstralStats#onEnable alongside the commands:
Completion | Handler | Source |
|---|---|---|
|
| Every loaded blueprint's key ( |
|
| The target player's currently active modifier keys, across all of their stats. Requires the |
|
| The |
@players and @resourceTypes are not custom handlers: @players is ACF's built-in online-player completion, and @resourceTypes resolves automatically from the ResourceType enum via ACF's enum completion support.