Skip to content

Tags: Basics

Quick Start: Spice it up with twists.

Straight to the most useful ones: <random-num:4,12> rolls a whole number, <random-pick:a,b> picks one item, and interval triggers (see Behaviors) run lines on a timer. Everything below builds from there. Paste tricky lines into the Mod Validator to check tag syntax as you write.

Placeholders in Commands

Modifier commands run through JMHScript (JMHS), the tag engine: tags evaluate to text, then the full line runs as one command.

Every tag looks like <name> or <name:arg1,arg2>, with lowercase names and comma-separated args; quote an arg when it holds commas, and nest tags freely (they evaluate inside out).

Commands can use these tags:

Tag Replaced with
<p> The participating player's name. Use this in player and role commands.
<random-mob> A random spawnable living entity type in lowercase (e.g. zombie, creeper). Under pick-random PER_INVOKE one mob is rolled per activation for everyone; under PER_EXECUTOR every executor rolls their own.
<random-item> A random item material in lowercase (e.g. diamond_sword, bread). Same scope rule as <random-mob>.
<random-block> A random placeable block in lowercase (e.g. stone, torch). Same scope rule as <random-mob>.
<random-solid-block> paid A random collidable block in lowercase (e.g. packed_ice, oak_stairs). Same scope rule as <random-mob>.
<random-sound:[0.8,1.2],[1.0]> paid A random sound primitive [true, id, pitch, volume] from block, entity, and item sounds. Pitch draws from arg one ([0.8, 1.2] default), volume from arg two (1.0 default); both take singles or [min, max] pairs. Same scope rule as <random-mob>.
<random-effect:[0,2],[30]> paid A random effect triple [id, amplifier, seconds]. Amplifier draws from arg one (0 default), seconds from arg two (30 default); whole numbers only, singles or [min, max] pairs. Same scope rule as <random-mob>.
<random-enchant:[1,3]> paid A random enchant pair [id, level] (cursed and treasure enchants included). Level draws from the one arg (1 default), a single or [min, max] whole pair. Same scope rule as <random-mob>.
<random-biome:world?> paid A random generating biome key in lowercase (e.g. plains, ice_spikes). With a world arg it samples loaded chunks around that world's spawn instead, falling back to any biome when the world is unknown or nothing loaded is found. Same scope rule as <random-mob>.
<random-color> A random hex color as six lowercase digits without a prefix (e.g. 6b4c33), ready for dust colors and broadcast formats. Same scope rule as <random-mob>.
<all-players> Every participating player in this match, as a name list like [Alice, Bob]. [] when nobody matches. Spectator-gamemode players are excluded.
<all-players:HUNTER> Same, but only hunters. SPEEDRUNNER works too.
<random-player> One random participating player in this match. Same scope rule as <random-mob>. Spectator-gamemode players are never drawn.
<random-hunter> One random hunter in this match (null with no hunters). Same scope rule as <random-mob>. Spectator-gamemode players are never drawn.
<random-runner> One random speedrunner in this match (null with no speedrunners). Same scope rule as <random-mob>. Spectator-gamemode players are never drawn.
<random-num:4,12> A random whole number between 4 and 12. Order does not matter: <random-num:12,4> works the same.
<random-pick:coal, "dirt", 'sand'> One random item from the list. Items can be bare, "double-quoted", or 'single-quoted', and may hold spaces. A bracketed list works too (<random-pick:[coal, dirt]>), but it splits every comma, so quoted commas need the a,b form.
<setting:base-y> One config setting of the running modifier (<s:base-y> works too). Dotted paths reach folder children (<setting:palette.accent>); missing paths read null.
<modcolor> The running modifier's broadcast color as plain #ffffff, defaulting to the brand color outside modifiers. Wrap it for mid-message coloring: \<<modcolor>\>.
<gerror:text> Broadcasts an error line as {prefix} plus the configured error-color plus the text. <perror:player,text> tells one player and <rerror:role,text> tells a role; every form also takes [enabled,text] to gate on a toggle. Longer aliases: <gerrormessage>, <perrormessage>, <rerrormessage> (see Messages).

Tags evaluate from the inside out, so they nest. The bundled random-start-resources modifier uses this to hand out a random ore stash:

- "give <p> <random-pick:coal <random-num:4,12>, iron_ingot <random-num:3,9>, gold_ingot <random-num:3,9>, diamond <random-num:1,3>>"

Each <random-num> rolls first, then <random-pick> chooses one item amount pair, giving coal (4-12), iron (3-9), gold (3-9), or diamonds (1-3).

If a <random-pick> item is malformed (mixed quotes, two quoted strings in one item), it is skipped with a console warning and another item is tried.

Raw @a, @r, @p, and @s selectors work like vanilla and are the recommended default for plain commands: they convert automatically and stay scoped to the match, so commands never leak into other matches. @a runs the command once per participant; a team= argument on @a[...] survives as a role filter, while other vanilla selector arguments are dropped. @r draws one random participant, and @p/@s resolve to the executing player. Inside tag arguments only JMHS tags (<p>, <random-player>) resolve; bare @ selectors there stay verbatim.

Escaping Special Characters

A backslash before any char makes it literal text: \<yellow\> survives tag parsing and renders MiniMessage yellow, say a\,b keeps one arg with a comma, and \\ collapses to \. Escaped text never evaluates as tags, math, conditions, lists, selectors, or tildes.

- '<pmessage:<p>,\<yellow\>This is yellow text!>'
- '<pmessage:<p>,a\,b>'

Write escaped lines in single-quoted YAML: double-quoted YAML eats backslashes before the engine sees them. Escapes hide chars from JMHScript parsing only: PlaceholderAPI expansion still sees the restored literal text afterwards. Bare MiniMessage tags pass through untouched at runtime and validate silently, so <red> works directly; escapes remain for literal angle brackets and arg structure.

Extended Tags

Besides the placeholders above, JMHS understands computing tags for branching, messages, sounds, stats, math, lists, loops, and flags. They nest inside each other and inside the basic tags, and the creator editor validates them as you type. A taste:

Tag Meaning
<if:"7 le 5","yes","no"> no: yes when the condition holds, else no (the else branch may be omitted).
<min:8,3> The smaller number: 3.
<max:8,3> The larger number: 8.
<clamp:8,1,5> 8 clamped into 1..5: 5.
<id> The name of the modifier (or trigger) running the commands.
<gmessage:"hi"> Sends hi to every participant; the tag itself leaves nothing behind.
<pmessage:Alex,yo> Sends yo to Alex only. Message tags take literal preset text (MiniMessage), never message keys from the lang files.
<gsound:block.stone.break> Plays the sound for every participant.
<psound:Alex,block.stone.break,0.5,2> Plays the sound for Alex, with pitch 0.5 and volume 2 (both default to 1).
<args:0> The trigger's first event arg (see Event Args); bare <args> reads index 0.
<len:[a,b]> List tools (list.get, list.set, list.append, and more; see Lists).
<range:1,5> The list [1, 2, 3, 4], Python style (see Lists).
<active-players:HUNTER> Eligible hunters as a list, like [Alex, Bo] (ALL lists both sides).
<phasitem:Alex,golden_apple,2> true when Alex holds at least 2 golden apples, else false (the count is 1 when omitted).
<plocation:Alex> Alex's spot as [x, y, z, world, pitch, yaw].
<for:[a,b],...> Repeats the body per item with the item behind <i> (see Loops).
<run:say hi> Runs say hi from the console like a command list entry; the tag leaves nothing behind.
<format:"{0} found {1}",[Alex,gold]> Alex found gold. Plain {n} substitution; missing indexes, {x}, and stray braces stay verbatim silently. No conversion specifiers.
<rmessage:hunter,push!> Tells the named role only (ALL tells both).

Everything else, one line per tag, lives in the Tag Cheatsheet, with full rules in Conditions, Flags, Lists, Strings, Math, Messages and Sounds, Players, Vectors, Cooldowns, Effects, Loops and Functions, Match Control, Shared Max Health, and Teleport and Fill.

Stats

<pstat:player,key> reads one player's numbers; <gstat:key> reads match- or world-wide ones; <rstat:role,key> sums one key across a role. Keys match case-blindly:

Tag Meaning
<pstat:<p>,health> The player's health, normally 0-20.
<pstat:Steve,hunger> The player's hunger, 0-20.
<pstat:<p>,max-health> The player's effective max health, normally 20.
<pstat:Steve,mobs-killed> Mobs the player killed this match.
<pstat:Steve,achievements-gained> Non-recipe advancements earned this match.
<pstat:Steve,exp-level> Vanilla experience level.
<pstat:Steve,damage-dealt> Damage dealt this match in health points (20 is 10 hearts).
<pstat:Steve,crafts> Craft actions this match (one per craft, regardless of result size).
<pstat:Steve,blocks-broken> Blocks broken this match (one per break, placed blocks count).
<gstat:duration> Whole seconds since the match began.
<gstat:daytime> The main world clock in ticks.

<pstat> and <rstat> also read every vanilla statistic: write the key as bukkit.<name>, b.<name>, or bare (<pstat:Steve,bukkit.damage_taken>, <pstat:Steve,b.damage_taken>, and <pstat:Steve,DAMAGE_TAKEN> all read the same number). Stats that count a block, item, or entity take a qualifier: <pstat:Steve,b.mine_block,diamond_ore> reads diamond ore mined this match, <rstat:hunter,b.kill_entity,zombie> sums zombie kills across the hunters. Qualifiers match case-blindly with an optional minecraft: prefix. A typed stat without its qualifier is a syntax error; a qualifier on any other key warns. Vanilla stats wipe to zero for every hunter and speedrunner when they join the match, so these reads are match-only.

<rstat> takes HUNTER, SPEEDRUNNER, or ALL (both sides, never spectators) and sums the online active members.

Unknown keys warn and yield nothing, listing the valid keys. Reading a stat of an offline player warns and yields nothing. duration outside a live match warns and yields 0, and so does <rstat> without a live match. Every <pstat> key yields -1 for an eliminated player, so -1 reliably means out of the game; <rstat> skips those players instead of summing the -1.

Roster and locations

<active-players:HUNTER> (or SPEEDRUNNER, case does not matter) lists the players currently eligible for interval commands: online, active, and neither respawning nor held. ALL lists both sides as one list. <prole:Alex> reads one player's side, and <plocation:Alex> reads their spot as [x, y, z, world, pitch, yaw], the same shape as the respawn event arg. Unknown or offline players yield null silently, as do spectators for <prole>. <peliminated:Alex> reads false only while Alex is active in a live match; eliminated, left, and never-joined names all read true, and spectator-gamemode players read true even while their role still participates, with no live match needed. <mvp> reads the live top scorer's name once they clear the MVP threshold; when the leader left the game the runner-up resolves instead, and null means nobody cleared the bar yet. It needs a live match and takes no arguments.

<overlap-players:origin,role,radius,max> and <nearby-players:player,role,radius,max> list names within radius blocks, nearest first with name order breaking ties, capped at max names. The role filters like <active-players> and accepts ALL. A bare [x, y, z] origin searches every world; a full [x, y, z, world, pitch, yaw] origin searches that world only. <nearby-players> centers on the named player (whose own world scopes the search) and drops the executing player from the list, so console lists still see everyone. Bad shapes warn and yield null; an unknown center player yields null silently, and an empty search yields [].

<pworld:Alex> reports the world alias: nether for the nether, end for the end, else the raw world name. <world:Alex> is the same tag. Behind ghost and display tags the end alias resolves back to the executing match's End-cell world when it has one, so locations round-trip to the right cell; elsewhere the aliases read the first world of that environment. <px:Alex>, <py:Alex>, <pz:Alex>, <pyaw:Alex>, and <ppitch:Alex> read single numbers from the live spot. Unknown or offline players yield null silently.

<distance> measures between two spots with only the x, y, z entries, ignoring pitch and yaw. Two full primitives in different dimensions yield null silently, while short lists carry no dimension and always compare by coordinates: <distance:<plocation:Alex>,[0, 64, 0]> is the blocks from Alex to spawn. Null, blank, and unparseable sides (an offline player's spot, for example) also yield null silently. Genuinely malformed non-null sides warn and stop the line.

Bare math

Any no-space token that fully parses as math evaluates: give <p> egg 1+1 hands out 2 eggs. Parentheses, **, //, and % work; division by zero warns and yields 0. Quote a token to protect it: say "2026-09-26" stays a date, while a bare 2026-09-26 computes to 1991. For fallback values use <default:value,fallback> (see Flags).

Placeholders

After tags and math resolve, raw %...% spans expand through PlaceholderAPI when it is installed (any expansion works, including %jmanhunt_match_kills%), or in-house for %jmanhunt_*% spans when it is not. <placeholder:key> is the same lookup as a tag, which matters inside math: <clamp:<placeholder:x>-1, 0, 9> computes on the number, while a raw span would only expand after math ran.

Placeholders need an executor player, so console lists skip the raw pass silently; the tag form warns without an executor instead. Unknown keys stay verbatim.

Stopping a list

A lone exit line stops the command list: later lines never run. It is checked after tags expand, so <if:"1 == 2","exit","say hi"> skips the rest only when the branch hits. exit with anything else on its line is skipped with a warning.

Null lines

A line that resolves to exactly null (lowercase, nothing else on it) is never dispatched: the console logs a warning naming the modifier, behavior, list, and line instead. Anything else holding null runs as usual.