Decorator Reference
Complete reference for all decorators in RedScript.
@load
Runs the function when the datapack is loaded or reloaded.
Syntax: @load
@load
fn init() {
say("Loaded!");
}Compiles to: Adds function to #minecraft:load function tag.
Use cases:
- Initialize scoreboards
- Set up teams
- Reset game state
- Display welcome messages
@tick
Runs the function every game tick (20 times per second).
Syntax: @tick
@tick
fn update() {
// runs 20 times per second
}Compiles to: Adds function to #minecraft:tick function tag.
WARNING
Use @tick sparingly. Running complex logic 20 times per second can cause lag. Prefer @throttle(ticks=N) for periodic work that does not need every-tick precision.
@tick(rate=N)
Parsed for compatibility with older examples, but the current emitter registers the function as a normal @tick root and does not use rate for throttling. Do not rely on @tick(rate=N) for runtime cadence.
Use @throttle(ticks=N) instead:
@throttle(ticks=20)
fn every_second() {
// runs once per second through the generated throttle dispatcher
}
@throttle(ticks=1200)
fn every_minute() {
// runs once per minute
}Compiles to: @tick(rate=N) currently compiles like @tick; @throttle(ticks=N) generates a tick-registered dispatcher with a scoreboard counter.
@function_tag
Registers the function in a Minecraft function tag. This is the generic primitive behind tag-based entry points.
Syntax: @function_tag("namespace:path")
@function_tag("minecraft:load")
fn init() {
say("Loaded through a function tag");
}
@function_tag("rs:on_player_death")
fn on_player_death() {
scoreboard_add(@s, "deaths", 1);
}Compiles to: Adds the function reference to data/<namespace>/tags/function/<path>.json.
@load is equivalent to @function_tag("minecraft:load"), and @tick is equivalent to @function_tag("minecraft:tick"). If both forms target the same tag, the compiler merges and de-duplicates the tag entries.
@on_trigger
Runs when a player activates a trigger scoreboard.
Syntax: @on_trigger("trigger_name")
@on_trigger("menu")
fn open_menu() {
tellraw(@s, "Menu opened!");
}Player activation: /trigger menu
Compiles to:
- Creates a trigger scoreboard objective
- Detects when score changes from 0
- Resets the trigger after execution
Use cases:
- Player-activated menus
- Shop systems
- Voting systems
- Custom commands
Legacy specialized event decorators
The parser still recognizes the older specialized event decorator names:
@on_death@on_login@on_advancement("advancement_id")@on_craft("item_id")@on_join_team("team_name")
These names are kept for compatibility and tooling, but they are not the current runtime-backed event API. Do not use them for new event logic or assume that they generate death/login/advancement/craft/team detection assets.
Use one of these instead:
@on(PlayerDeath)/@on(PlayerJoin)/@on(EntityKill)/@on(ItemUse)for compiler-known runtime events.@function_tag("namespace:path")plus an explicit Minecraft or stdlib dispatcher for custom event wiring.
@on(EventType)
Handles a compiler-known runtime event type. The event runtime injects the executor context, so a zero-argument handler should normally use @s for the triggering player/entity. A legacy single Player parameter is still accepted for compatibility, but it should be treated as an alias for the event executor rather than a normal function argument.
Syntax: @on(EventType)
Supported event types:
| Event | Description | Detection | @s context |
|---|---|---|---|
PlayerDeath | A player dies | Scoreboard/runtime asset | Player |
PlayerJoin | A player joins | Tag/runtime asset | Player |
EntityKill | A player kills an entity | Scoreboard/runtime asset | Player |
ItemUse | A player uses an item | Scoreboard/runtime asset | Player |
@on(PlayerDeath)
fn handle_player_death() {
scoreboard_add(@s, "deaths", 1);
}
// Legacy-compatible form: the parameter is lowered as the event executor.
@on(PlayerJoin)
fn handle_player_join(player: Player) {
title(player, "Welcome to the Server!");
}Compiles to: Adds the handler to the event's function tag (for example rs:on_player_death) and automatically includes the required event runtime asset from the standard library. For unsupported events, use @function_tag plus an explicit stdlib/runtime dispatcher.
@schedule
Schedules a function to run once after a fixed delay (in ticks) from the moment the datapack loads or after the decorated function is otherwise triggered.
Syntax: @schedule(ticks=N)
| Parameter | Description |
|---|---|
ticks=N | Number of ticks to wait before executing the function. |
@schedule(ticks=100)
fn delayed_start() {
// Runs 5 seconds (100 ticks) after datapack load
say("Game starting!");
}
@schedule(ticks=1200)
fn end_game() {
// Runs 60 seconds (1200 ticks) after datapack load
say("Time's up!");
scoreboard_display("sidebar", "kills");
}Compiles to: a compiler-generated scheduling wrapper that runs schedule function <ns>:<name> <ticks>t from the startup path.
TIP
For recurring scheduled tasks, use @throttle(ticks=N). Use @schedule only for one-shot startup delays.
@keep
Prevents the dead code elimination (DCE) optimizer from removing a function.
Syntax: @keep
By default, functions whose names start with _ are treated as private and may be removed if unreachable. @keep forces them to be included in the compiled output regardless.
@keep
fn _internal_helper() {
// Kept even though name starts with _
}Use cases:
- Utility functions called only via
/functionin-game - Functions that should survive aggressive DCE
@coroutine
Marks a function as a coroutine — a long-running task that can yield control back to Minecraft at loop back-edges, preventing server timeout on expensive computations.
Syntax: @coroutine or @coroutine(batch=N) or @coroutine(onDone="fn_name")
@coroutine(batch=10, onDone="on_done")
fn process_all_players() {
foreach (p in @a) {
heavy_computation(p);
// yields every 10 iterations at the loop back-edge
}
}
fn on_done() {
say("Processing complete!");
}Parameters:
| Parameter | Description |
|---|---|
batch=N | Number of loop iterations to execute per tick before yielding. Default: 1 (yield every iteration). |
onDone="fn" | Name of a no-arg function to call when the coroutine finishes all iterations. |
Behaviour:
- Yields at loop back-edges (end of each
foreach/for/whilebody iteration). - Uses
schedule function ... 1tto resume on the next tick. - State is preserved between ticks in a dedicated scoreboard.
- Only one instance of a coroutine runs at a time; starting a new one while it is running is a no-op.
Use cases:
- Processing large entity lists without causing
/tick warptimeouts - Spreading heavy map generation over multiple ticks
- Chunked bulk operations (inventory scans, world edits)
@inline
Hints the compiler to inline this function at call sites.
Syntax: @inline
@inline
fn add(a: int, b: int): int {
return a + b;
}Use cases:
- Performance-critical small functions
- Avoiding function call overhead
@deprecated
Marks a function as deprecated. Compiler emits a warning when called.
Syntax: @deprecated or @deprecated("message")
@deprecated("Use new_api() instead")
fn old_api() { }@singleton
Marks a struct as global scoreboard-backed state and exposes synthetic StructName::get() / StructName::set(value) helpers.
Syntax: @singleton on a struct
@singleton
struct GameState {
phase: int,
tick_count: int,
}
@keep
fn update_state() {
let state = GameState::get()
state.tick_count = state.tick_count + 1
GameState::set(state)
}@watch
Runs a parameterless handler when a player's value in a watched scoreboard objective changes.
Syntax: @watch("objective")
@watch("rs.kills")
fn on_score_change() {
say("Score changed!")
}@config
Injects numeric compile-time configuration values into global let declarations.
Syntax: @config("key", default: N)
@config("max_players", default: 16)
let MAX_PLAYERS: int@profile
Enables performance profiling for this function.
Syntax: @profile
@profile
fn expensive_calculation() {
// Timing data emitted to game output
}@throttle
Rate-limits function execution to once per N ticks.
Syntax: @throttle(ticks=N)
@throttle(ticks=20)
fn rate_limited() {
// Only runs once per second, even if called more often
}@retry
Auto-retries the function on failure up to N times.
Syntax: @retry(N)
@retry(3)
fn unstable_operation() {
// Retries up to 3 times if scoreboard check fails
}@memoize
Caches function results for single-arg int functions (LRU-1).
Syntax: @memoize
@memoize
fn fibonacci(n: int): int {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
}@benchmark
Enables tick-level benchmarking, outputs timing stats.
Syntax: @benchmark
@benchmark
fn heavy_work() {
// Timing stats logged to game output
}@test
Marks a function as a test. Run with redscript test.
Syntax: @test
@test
fn test_addition() {
assert(1 + 1 == 2);
}Use cases:
- Unit testing datapack logic
- CI/CD validation
Decorator Summary
| Decorator | Trigger | @s Context |
|---|---|---|
@load | Datapack load / /reload | Server |
@tick | Every tick (20/sec) | Server |
@tick(rate=N) | Legacy-compatible spelling; currently behaves like @tick | Server |
@function_tag("namespace:path") | Function tag registration | Caller / tag runtime |
@on_trigger("x") | Player runs /trigger x | Triggering player |
@on(EventType) | Runtime-backed event fires | Event executor (Player for built-in events) |
legacy @on_* event decorators | Parser compatibility only | Do not use for runtime events |
@schedule(ticks=N) | Runs function once N ticks after datapack load | Server |
@keep | (Optimizer hint, no runtime effect) | — |
@coroutine | Marks function as a coroutine (yields at loop back-edges) | — |
@inline | Inline at call sites | — |
@deprecated | Emit warning on use | — |
@singleton | Struct-backed global state | — |
@watch | Per-player scoreboard change | Player (@s) |
@config | Numeric compile-time config | — |
@profile | Performance timing | — |
@throttle(ticks=N) | Rate limit to 1/N ticks | — |
@retry(N) | Auto-retry N times | — |
@memoize | Cache results | — |
@benchmark | Tick-level timing | — |
@test | Test function | — |