Loops and Functions¶
For: advanced users repeating work.
<for> and <while> loops plus run-local <def> functions. The one-line forms live in the cheatsheet.
Loops¶
| Tag | Meaning |
|---|---|
<for:list,body> |
Run the body per item with the item behind <i>; text the body produces is discarded. |
<while:cond,body> |
Repeat the body while the condition holds. |
<i> |
Innermost for-loop item, else null. |
<for:list,body> walks a list with each item behind <i>, and
<while:condition,body> repeats while its condition holds. Bodies
run for side effects (flag writes, messages, sounds, <run>
commands) and any text they produce is discarded; both loops leave
nothing behind on success. Like <if>, loops see flag writes
earlier on the line (see Conditions):
- '<gflag:out,[]>'
- '<for:<range:3>,<list.append:<gflag:out>,item<i>>>'
- 'say <gflag:out>'
- '<for:<active-players:HUNTER>,<run:effect give <i> minecraft:glowing 10>>'
This says [item0, item1, item2], then gives every hunter ten
seconds of glowing: <run:> dispatches its evaluated body from
the console per iteration, with the blacklist enforced and exit
holding no special meaning inside it.
This says [item0, item1, item2]. Nested loops work, with <i>
always reading the innermost item; outside any for loop <i> is a
silent null. The while condition re-resolves every iteration and
accepts a bare true/false word or a full condition (<gflag:n>
lt 3); anything else warns and the whole tag becomes null.
Two tripwires keep loops honest. A for loop over a flag reference
snapshots the flag and cancels to null with a warning when the
body changes it mid-loop (literals cannot change, so they never
cancel). And every line gets 20000 shared steps, split between loop
iterations and function calls: past that the tag becomes null,
the match is cancelled, the console logs JMHScript loop exceeded
20000 steps at plus the modifier, behavior, list, and line, and
the players are told to contact the administrator. A line that
overflows the stack before the budget trips cancels the same way
but logs JMHScript stack exhausted evaluating instead, so the
two failures are easy to tell apart. Escape literal < and >
inside loop bodies (see Escaping Special Characters): quotes never
hid tags from the scanner.
Functions¶
| Tag | Meaning |
|---|---|
<def:name,body,params...> |
Run-local function; the body stores verbatim until called. |
<run:command> |
Run a console command, empty (exit holds no special meaning inside). |
<runblock:id,args...> |
Run a behavior block of this modifier, empty; extra parts rebind <args:N> inside. |
<def:name,body,params...> defines a run-local function; the body
stores verbatim and never evaluates at definition time. Calling
<name:args...> substitutes each param with its arg, then evaluates
the body like any command line, so calls nest and recurse:
- '<def:double,x+x,x>'
- 'say <double:21>'
- '<def:countdown,<if:n le 0,done,<countdown:n-1>>,n>'
- 'say <countdown:3>'
This says 42, then done. Names are case-insensitive, use
letters, digits, and _, and must not collide with builtin tags
(redefining one warns and is ignored). All params are optional:
missing args bind null with no warning, while extra args warn and
are ignored. Params substitute as bare case-sensitive tokens
outside quotes, in one pass, so an arg holding a param name is
never re-substituted; quoted text is left alone. Like every tag,
substitution is textual: math bodies should parenthesize params
((B)**2 rather than B**2) so negative args bind as one value,
and bare math still needs no-space runs. Definitions live and die
with the run like <lflag>. The creator editor collects every
<def:name> in the edited line's function scope, so calls to
defined functions (including recursive self-calls and calls from
other lines in the same scope) never warn; genuinely unknown tags
still warn, and still resolve at runtime.
Recursion terminates through <if> base cases, since dead
branches never run. Every call costs one of the line's 20000 shared
steps, and the budget never refunds: leaving a loop cannot launder
steps back, so runaway recursion always ends at the limit above
instead of crashing the server.