Astral Realms Documentation Help

Functions

Inline functions are short, named utilities you can call from any string that already supports placeholders — menu item names, lore, titles, dialog labels, action arguments, requirement values, chat messages, anywhere the placeholder parser runs. They are designed for the same role that helper methods play in code: take a few values, return one value, drop the result back into the surrounding string.

Syntax

$name(arg1, arg2, ...)
  • The leading $ introduces a function call. $e(...) is reserved for arithmetic expressions (see Placeholders) and is not parsed as a function.

  • The name accepts [a-zA-Z0-9_-] characters — letters, digits, underscores, and hyphens, so dash-named built-ins like format-number, format-date, and random-int are inline-callable. Arguments are separated by commas (,); commas inside nested parentheses are not treated as separators.

  • Each argument is a normal placeholder string. Placeholders inside arguments resolve before the function runs, so you can freely mix raw values, %placeholder%, nested $other(...) calls, and $e(...) expressions.

  • The function's return value is converted to a string and substituted in-place — so a function call is just an inline value, exactly like a %placeholder%.

Every occurrence of a call is evaluated separately, in left-to-right order — identical calls are not computed once and copied. Writing $random-int(1, 6) twice on the same line therefore yields two independent rolls, and the same holds per lore line: each line is resolved on its own.

items: counter: item-stack: material: PAPER name: "<gold>Score: $add(%variables_score%, 1)" lore: - "<gray>Capped: $add(%variables_score%, 1, 100)" - "<gray>Rounded HP: $round(%player_health%, 1)"

Nesting and Evaluation Order

A token is always claimed whole: the parser scans for $name(, matches its closing parenthesis with a depth counter, and resumes scanning after that parenthesis instead of descending into it. Whatever sits inside is parsed by that token's own wrapper, so nesting works in either direction and at unlimited depth — $e(...) inside a function's arguments, $fn(...) inside an expression, or both at once:

lore: - "<gray>Cost: $format-number($e((150 + (0.02 * %level%) * 150) * (%bonus% / 100 + 1)))"

This scanning applies everywhere, including component fields (item names, lore lines, menu and dialog titles) and hover tooltips — a call written in hover text is resolved like any other. A %placeholder% sitting inside a claimed token is not resolved separately; it belongs to that token and is resolved by its wrapper.

Text that does not form a well-formed token is left alone as literal text: a $ not followed by a name and (, or a call whose parentheses are unbalanced, is never treated as a call.

Resolution Order

Functions are looked up in this order:

  1. The per-plugin function registry (anything registered by your own plugin).

  2. The global function registry shared across the network.

"Your own plugin" is the plugin whose configuration file the call was written in — captured while that file is deserialized, not looked up when the line is rendered. A call in an AstralItems blueprint therefore keeps seeing AstralItems' own registrations no matter which plugin's code triggers the render, and a string built outside any configuration read sees the global registry only.

Lookup is an exact, case-sensitive match on the registered name. If the name is not found, resolving the surrounding string fails with an error naming the unknown function — a loud signal that the registry was wrong or the plugin failed to load.

Built-in Functions

All built-ins live in the global registry, so they are available in every menu, dialog, and item string without any extra configuration.

Numbers

add(input, number, max?)

Adds number to input. If max is supplied, the result is capped: min(input + number, max).

- "[set-variable] qty $add(%variables_qty%, 1, 64)"

subtract(input, number, min?)

Subtracts number from input. If min is supplied, the result is floored: max(input - number, min).

- "[set-variable] qty $subtract(%variables_qty%, 1, 0)"

increment(value, max?)

Returns value + 1. If max is supplied and value >= max, returns value unchanged. Preserves the input's number type (int stays int, double stays double).

- "[set-variable] page $increment(%variables_page%, 9)"

decrement(value, min?)

Returns value - 1. If min is supplied and value <= min, returns value unchanged. Preserves the input's number type.

- "[set-variable] page $decrement(%variables_page%, 0)"

min(number1, number2)

Returns the smaller of two numbers as a double. If either is null it returns the other; if both are null it returns 0.0.

name: "<gray>Best price: $min(%parameters_offer%, %variables_listed%)"

max(number1, number2)

Returns the larger of two numbers as a double, with the same null handling as min.

name: "<gray>Floor: $max(%parameters_offer%, 100)"

random-int(min, max)

Returns a pseudo-random integer uniformly distributed in the inclusive range [min, max]. Null handling: both null → 0; only min null → returns max; only max null → returns min. A bound that was not written at all is treated the same way as one that resolves to null, so $random-int(5) returns 5 rather than failing.

Bounds written the wrong way round are normalised rather than trusted: $random-int(10, 5) draws from [5, 10]. The full int range is supported, so $random-int(-2147483648, 2147483647) really does draw across it.

- "[set-variable] roll $random-int(1, 6)" name: "<gray>Bonus damage: +$random-int(%variables_min%, %variables_max%)"

round(number, decimals?)

Rounds the value to decimals decimal places. decimals defaults to 0. Integers and longs pass through unchanged.

lore: - "<gray>HP: $round(%player_health%, 1)" - "<gray>Avg: $round(%variables_average%)"

format-number(value, pattern?)

Formats a number using a java.text.DecimalFormat pattern. Defaults to ###,##0.## when pattern is omitted, null, or blank — thousands are grouped, decimals are shown only when non-zero and capped at two places, and at most 30 integer digits are kept. So $format-number(1234.5) renders 1,234.5 and $format-number(1000) renders 1,000.

lore: - "<gray>Balance: $format-number(%vault_eco_balance%)" - "<gray>Stock: $format-number(%variables_stock%, #,###)"

roman-number(number)

Renders an integer as a Roman numeral. 1 becomes I, 4 becomes IV, 2026 becomes MMXXVI.

Edge cases are deliberate rather than errors: a null argument yields null (the token resolves to nothing), 0 and any negative number render as the string "0", and a value of 3999 or above is returned unchanged as its decimal digits — Roman numerals stop there.

lore: - "<gray>Sharpness $roman-number(%item_enchantment_sharpness_level%)" - "<gray>Tier <white>$roman-number(%variables_tier%)"

Collections

map(collection, field)

Projects every element of a collection through a template and returns the resulting list. The collection argument goes through the collection adapter, so it accepts a real Collection, an array, or a single value (wrapped into a one-element list); a missing value adapts to the empty list.

Each element is exposed to field as %parameter_entry%, so field drills into the element with the sub-keys that element's own placeholder understands.

Because the result is a list, writing the call on a lore: line expands it into one line per element (and an empty list removes the line entirely) — see List-valued placeholders.

lore: - "$map(%item_enchantments_list%, <gray>%parameter_entry_name%)"

Text

uppercase(value)

Uppercases the text of its argument and returns a plain string. The argument is resolved and MiniMessage-parsed first, so only the literal text is rewritten and the tags are left alone: $uppercase(<red>%player_name%) renders the name in caps, still red. Case conversion uses the root locale, so the result never depends on the server's default locale.

name: "<gold>$uppercase(%instance_blueprint_name%)"

lowercase(value)

The same, lowercased.

- "[compare] $lowercase(%parameters_tab%) == shop"

Transformers

apply-transformer(id)

Renders the transformer registered under id and substitutes its result. The result may be a single component (an ordinary inline value), a list of components (a lore line holding the call is expanded into one line per element), or an empty list (the line is removed).

The name is resolved against the registry of the plugin whose configuration the call was written in, falling through to the global registry — see Scope and lookup. An unknown id fails the surrounding string with Transformer not found: <id>.

lore: - "$apply-transformer(listing-status)"

Time

format-date(epoch-millis, mode?)

Formats a long containing milliseconds-since-epoch. mode selects the output:

mode

Output

format (default)

HH:mm:ss dd/MM/yyyy

remaining

Milliseconds between now and the timestamp (signed long).

remaining-format

Human-readable duration between now and the timestamp.

format-date

Date-only dd/MM/yyyy.

format-duration

Human-readable duration treating the value as a length, not a moment.

anything else

Used as a DateTimeFormatter pattern (e.g. yyyy-MM-dd HH:mm).

lore: - "<gray>Listed: $format-date(%aah_listing_created%)" - "<gray>Expires in: $format-date(%aah_listing_expires%, remaining-format)"

Misc

orElse(source, fallback)

Returns source when it resolves to a non-null value, otherwise fallback. Both arguments are required and untyped — the value passes through unchanged, with no numeric coercion. Any error raised while resolving source is swallowed and treated as null, so a failing placeholder falls back instead of breaking the string. fallback is only resolved when source was null, and it is not guarded: an error there propagates and fails the whole string's resolution.

In component fields (item names, lore lines, menu titles) keep MiniMessage tags outside the call: the string is deserialized as MiniMessage before function calls are detected, so a tag written inside the parentheses is consumed as formatting and splits the call in two, leaving it unresolved.

name: "<gray>Rank: <white>$orElse(%luckperms_prefix%, none)"

Built-in Function Summary

Name

Signature

Returns

add

(input, number, max?)

input + number, capped at max

subtract

(input, number, min?)

input - number, floored at min

increment

(value, max?)

value + 1, capped at max

decrement

(value, min?)

value - 1, floored at min

min

(number1, number2)

Math.min(...) as double

max

(number1, number2)

Math.max(...) as double

random-int

(min, max)

Random int in [min, max] inclusive

round

(number, decimals?)

Half-up rounded

format-number

(value, pattern?)

DecimalFormat output (default ###,##0.##)

roman-number

(number)

Roman numeral string

map

(collection, field)

List of field rendered once per element

uppercase

(value)

Argument's text, uppercased

lowercase

(value)

Argument's text, lowercased

apply-transformer

(id)

The named transformer's rendered result

format-date

(epoch-millis, mode?)

Formatted timestamp

orElse

(source, fallback)

source when non-null, else fallback

Registering Your Own Functions

See Custom Functions for the developer API.

Last modified: 03 September 2026