A server-side Forge mod for Minecraft 1.20.1 that adds a 64-bit scoreboard system with fixed-point decimals, backported 1.21+ function macros with control flow, persistent variables, and NBT integration — all designed to supercharge datapacks.
Installation
- Install Minecraft Forge for 1.20.1 (loader 47+).
- Place
dphelper-1.0-SNAPSHOT.jarinto your server'smods/folder. - Start the server — no client-side mod is needed.
Vanilla clients can connect without any modifications.
What DPHelper Provides
- score64 — A 64-bit scoreboard-style system with fixed-point decimal support.
- Function Macros — Backported
function ... withmacros from 1.21+, including$lines,^context forwarding, andreturnstatements. - Persistent Variables —
/varcommand and@var!/@loaddirectives that survive server restarts. - Control Flow —
@if/@else/@endif,@switch/@case/@endswitch,@while/@endwhile,@for/@endfor, and@breakdirectly in.mcfunctionfiles. - Player Data Commands — Backported
/datacommands for player entities from 1.21.2+. - Display Entity Commands —
/display opacityand/display transformfor manipulating display entities with unsigned values and interpolation. - Falling Block Command —
/fallingblockto spawn configurable falling block entities. - NBT Integration — Read, write, and do arithmetic between score64 values and entity/block/storage NBT.
score64 System
Core Concepts
Vanilla scoreboards are limited to signed 32-bit integers (±2,147,483,647). The score64 system provides a parallel 64-bit alternative:
| Feature | Vanilla Scoreboard | score64 |
|---|---|---|
| Value range | −2³¹ to 2³¹−1 | −2⁶³ to 2⁶³−1 |
| Decimal support | ❌ | ✅ Fixed-point |
| Precision | Integer only | Configurable per-objective |
| Persists with world | ✅ | ✅ |
| Works with vanilla clients | ✅ | ✅ |
| Sidebar / nametag display | ✅ Native | Via sync bridge |
score64 does NOT replace or modify the vanilla scoreboard. It is a completely parallel system.
Objectives
Each score64 objective has:
- Name — A unique string identifier (e.g.
energy,currency,distance) - Scale — A long integer ≥ 1 that defines decimal precision
| Scale | Decimal Places | Example |
|---|---|---|
1 |
0 (integers only) | 42 |
100 |
2 | 42.50 |
1000 |
3 | 42.500 |
1000000 |
6 | 42.500000 |
Holders
Holders are the entities or names that have scores — identical to vanilla scoreboard holders:
- Player names —
Steve,Alex - Entity selectors —
@a,@p,@e[type=zombie] - Fake players —
#temp,$counter,MyVar - Arbitrary strings — anything used by datapacks
Fixed-Point Decimals
Values are stored as raw long integers, scaled by the objective's scale factor:
raw = round(input × scale)
For example, with scale = 1000:
- Input
2.5→ stored as2500 - Input
3.141→ stored as3141 - Input
-0.001→ stored as-1
No floating-point math is ever used. All parsing and arithmetic uses pure integer operations with BigInteger for intermediate computations, ensuring deterministic results. Rounding mode: HALF_UP.
Objective Management
All commands are under /score64 and require operator permission level 2.
| Command | Description |
|---|---|
/score64 objectives add <name> |
Create integer objective (scale=1) |
/score64 objectives add <name> <scale> |
Create fixed-point objective |
/score64 objectives list |
List all objectives |
/score64 objectives remove <name> |
Remove objective + all its data |
Examples:
/score64 objectives add coins
/score64 objectives add energy 1000
/score64 objectives add precise_pos 1000000
Player / Holder Values
| Command | Description |
|---|---|
/score64 players set <targets> <obj> <value> |
Set value |
/score64 players add <targets> <obj> <value> |
Add to value |
/score64 players remove <targets> <obj> <value> |
Subtract from value |
/score64 players get <target> <obj> |
Display value |
/score64 players reset <targets> <obj> |
Remove score from objective |
/score64 players reset <targets> |
Remove all scores |
<targets>: @a, @s, @p, @e[...], player name, fake player (#var)
<value>: "42", "3.14", "-0.5", ".25"
> Note: Decimal values must be quoted as strings in commands.
Examples:
/score64 players set @s energy "100.5"
/score64 players set #temp coins "999999999999"
/score64 players add @s energy "1.5"
/score64 players remove @s energy "0.25"
/score64 players get @s energy
Output example:
Steve has energy = 2.020 (raw: 2020, scale: 1000)
Arithmetic Operations
/score64 players operation <target> <targetObj> <op> <source> <sourceObj>
All operations work on raw stored values (not formatted decimals).
| Op | Meaning |
|---|---|
= |
target = source |
+= |
target += source |
-= |
target −= source |
*= |
target ×= source |
/= |
target ÷= source (integer division, 0 = no change) |
%= |
target %= source (modulo, 0 = no change) |
< |
target = min(target, source) |
> |
target = max(target, source) |
>< |
swap target and source |
min |
target = min(target, source) |
max |
target = max(target, source) |
> Operators containing special characters (=, *, %, <, >) must be quoted: "*=", "+=", "<", "><", etc.
> Word-only operators (min, max) can be unquoted.
> Important: When using *= or /= with fixed-point objectives, operations work on raw values. Multiplying two scale-1000 values produces a value at scale 1000000. You may need a follow-up /= by the scale to correct the decimal point.
Examples:
/score64 players operation @s total += @s income total
/score64 players operation #result math *= #factor math
/score64 players operation @s health min #max_hp health
Math Library
All math commands: /score64 math ...
Unary Operations
| Command | Description |
|---|---|
/score64 math abs <target> <obj> |
Absolute value |
/score64 math neg <target> <obj> |
Negate |
/score64 math sign <target> <obj> |
Sign (−1, 0, or 1) |
/score64 math sqrt <target> <obj> |
Scale-aware square root (negative → 0) |
sqrt is scale-aware: for scale S and raw value R, it computes floor(sqrt(R × S)), correctly representing sqrt(R/S) in fixed-point.
Binary Operations (literal value or holder reference)
/score64 math <func> <target> <obj> <value>
/score64 math <func> <target> <obj> from <source> <sourceObj>
| Function | Description |
|---|---|
add |
target += operand |
sub |
target −= operand |
mul |
target ×= operand |
div |
target ÷= operand |
mod |
target %= operand |
min |
target = min(target, operand) |
max |
target = max(target, operand) |
Advanced Operations
| Command | Description |
|---|---|
/score64 math pow <target> <obj> <exponent> |
Integer power (O(log n)) |
/score64 math clamp <target> <obj> <min> <max> |
Clamp to range |
/score64 math random <target> <obj> <min> <max> |
Random long in [min, max] |
/score64 math atan2 <target> <obj> <source> <sourceObj> |
Angle in radians × scale |
Examples:
/score64 math abs @s velocity
/score64 math pow @s power 3
/score64 math clamp @s health 0 100000
/score64 math random @s loot_roll 1 100
/score64 math add @s coins "10.50"
/score64 math mul @s damage from #multiplier damage
NBT Integration
Read, write, and perform arithmetic between score64 values and Minecraft NBT data.
Read NBT → score64
/score64 nbt get <holder> <obj> entity <selector> <path>
/score64 nbt get <holder> <obj> block <pos> <path>
/score64 nbt get <holder> <obj> storage <id> <path>
Float/double NBT values are truncated toward zero, matching vanilla behavior.
Scaled mode (preserves float/double precision):
/score64 nbt get <holder> <obj> entity <selector> <path> scaled
/score64 nbt get <holder> <obj> entity <selector> <path> <scale>
When scaled is specified, float/double values are multiplied by the objective's scale then truncated: raw = (long)(nbt_value × scale).
An explicit numeric scale can also be provided instead.
| Example | Scale | NBT Value | Stored Raw |
|---|---|---|---|
Rotation[0] |
100000 | -0.00061 |
-61 |
Pos[1] |
1000 | 64.5 |
64500 |
Health |
1 | 20.0 |
20 |
Write score64 → NBT
/score64 nbt set entity <selector> <path> <holder> <obj>
/score64 nbt set block <pos> <path> <holder> <obj>
/score64 nbt set storage <id> <path> <holder> <obj>
Default: writes raw value as TAG_Long.
Scaled write mode (as_scaled):
/score64 nbt set entity <selector> <path> <holder> <obj> as_scaled
Divides by the objective's scale and writes as TAG_Double: value_to_write = raw / scale.
Arithmetic with NBT operand
/score64 nbt op <holder> <obj> <op> entity <selector> <path>
/score64 nbt op <holder> <obj> <op> block <pos> <path>
/score64 nbt op <holder> <obj> <op> storage <id> <path>
Ops: "=" "+=" "-=" "*=" "/=" "%="
Entity Yaw & Pitch
Convenient shortcuts to read an entity's rotation:
/score64 nbt yaw <holder> <obj> entity <selector>
/score64 nbt yaw <holder> <obj> entity <selector> <scale>
/score64 nbt yaw <holder> <obj> entity <selector> scaled
/score64 nbt pitch <holder> <obj> entity <selector>
/score64 nbt pitch <holder> <obj> entity <selector> <scale>
/score64 nbt pitch <holder> <obj> entity <selector> scaled
| Variant | Behavior |
|---|---|
| No modifier | Truncates to integer (e.g. 45.7 → 45) |
<scale> |
(long)(rotation × scale) |
scaled |
Uses the objective's scale |
Examples:
score64 nbt yaw @s facing entity @s
score64 objectives add facing 1000
score64 nbt yaw @s facing entity @s scaled
score64 nbt pitch @s look entity @s 100
NBT Examples:
/score64 nbt get @s health entity @s Health
/score64 nbt set storage mypack:data Result.value #result math
/score64 nbt op @s damage "*=" entity @s AttackDamage
score64 nbt get @s rotation entity @s Rotation[0] scaled
score64 nbt set entity @s Rotation[0] @s rotation as_scaled
Result Storage
score64 store command
/score64 store <target> <obj> run <command>
Runs <command>, stores its integer return value into score64. For decimal objectives, the result is multiplied by the scale.
execute store integration
execute store result score64 <target> <obj> run <command>
execute store success score64 <target> <obj> run <command>
result: stores the command's integer return value × scalesuccess: stores scale (= 1.0 in display) if succeeded, 0 if failed
Examples:
/score64 store #count enemies run execute if entity @e[type=zombie]
/score64 store @s xp_level run xp query @s levels
execute store result score64 @s energy run data get entity @s Air
execute store success score64 @s flag run say test
Vanilla Scoreboard Sync
Bridge score64 values to the vanilla scoreboard for sidebar/nametag display.
| Command | Description |
|---|---|
/score64 sync <obj64> <obj32> <target> |
Copy one holder to vanilla objective |
/score64 syncall <obj64> <obj32> |
Copy all holders to vanilla objective |
Values are clamped to ±2,147,483,647 (32-bit). A yellow warning appears if clamping occurs. The score64 value is never modified by syncing.
Example workflow:
scoreboard objectives add energy_display dummy
/score64 objectives add energy 1000
/score64 syncall energy energy_display
Execute Conditions
Range match
execute if score64 <target> <obj> matches <range> run ...
execute unless score64 <target> <obj> matches <range> run ...
| Range | Meaning |
|---|---|
"5" |
Exactly 5 |
"3..7" |
3 to 7 inclusive |
"..7" |
At most 7 |
"3.." |
At least 3 |
> Ranges use raw values. For scale=1000: checking ≥ 2.5 → "2500.."
Comparison
execute if score64 <target> <obj> <cmp> <source> <sourceObj> run ...
execute unless score64 <target> <obj> <cmp> <source> <sourceObj> run ...
Comparators: < <= = >= >
Examples:
execute if score64 @s energy matches "1000.." run say I have at least 1.0 energy
execute if score64 @s health < #threshold health run say Low health!
execute if score64 @s score = #target_score score run say Matched!
Predicates (JSON)
DPHelper registers custom loot conditions usable in advancements, loot tables, and execute if predicate.
dphelper:score64_range
Check whether a score64 value falls within a range.
{
"condition": "dphelper:score64_range",
"target": "this",
"objective": "energy",
"min": 0,
"max": 1000000
}
| Field | Required | Description |
|---|---|---|
target |
✅ | "this", "killer", "direct_killer", or any string (fake player) |
objective |
✅ | score64 objective name |
min |
❌ | Minimum raw value (inclusive) |
max |
❌ | Maximum raw value (inclusive) |
dphelper:score64_compare
Compare two score64 holders.
{
"condition": "dphelper:score64_compare",
"target": "this",
"objective": "energy",
"other_target": "killer",
"other_objective": "energy",
"operation": ">="
}
Supported operations: ==, !=, <, <=, >=, >
dphelper:nbt_numeric_range
Check a numeric NBT value on an entity.
{
"condition": "dphelper:nbt_numeric_range",
"target": "this",
"path": "CustomData.energy",
"min": 0,
"max": 1000
}
| Field | Required | Description |
|---|---|---|
target |
✅ | "this", "killer", or "direct_killer" |
path |
✅ | Dot-separated NBT path (e.g. "CustomData.level") |
min |
❌ | Minimum value (inclusive) |
max |
❌ | Maximum value (inclusive) |
Function Macros
> These features are backported from 1.21.2+ to 1.20.1 Forge by DPHelper. > They use the same syntax as vanilla 1.21+ so datapacks port seamlessly.
How It Works
- Write a
.mcfunctionfile with macro lines — lines starting with$ - Use
$(variable)placeholders inside those lines - Call the function with
/function <id> with ...to pass in the variables
Macro Invocation Syntax
/function <id> with args <compound>
/function <id> with entity <selector> [<path>]
/function <id> with block <pos> [<path>]
/function <id> with storage <id> [<path>]
/function <id> with var <namespace>
| Source | Description |
|---|---|
args <compound> |
Inline NBT compound (e.g. {key:"value"}) |
entity <selector> [<path>] |
Read NBT from an entity, optionally at a sub-path |
block <pos> [<path>] |
Read NBT from a block entity, optionally at a sub-path |
storage <id> [<path>] |
Read NBT from command storage, optionally at a sub-path |
var <namespace> |
Read from persistent DPHelper variables (see /var command) |
The top-level keys of the resolved compound become macro variables. String tags are unwrapped (no quotes); numeric tags become decimal strings; other types use SNBT.
Special Prefixes in .mcfunction Files
| Prefix | Description |
|---|---|
$ |
Macro line — $(var) placeholders are substituted at runtime, then the command is executed |
^ |
Macro call — calls another function, automatically forwarding the current macro context |
return |
Return statement — stops the function and returns a value (return <n>, return run <cmd>, return fail) |
@var |
Local variable — @var name = value sets a variable in the current scope |
@var! |
Persistent variable — @var! ns:id key = value saves to disk (survives restart) |
@load |
Load namespace — @load ns:id loads all persistent variables into scope |
@if |
Conditional block — @if <condition> starts a conditional |
@else |
Else branch — flips the current @if condition |
@endif |
End conditional — closes the @if block |
@switch |
Switch block — @switch <value> starts a switch on a value |
@case |
Case branch — @case <value> executes if the switch value matches |
@default |
Default branch — executes if no @case matched |
@endswitch |
End switch — closes the @switch block |
@while |
While loop — @while <condition> repeats the body while condition is true |
@endwhile |
End while — closes the @while block |
@for |
For loop — @for <var> <start> <end> [<step>] iterates a counter |
@endfor |
End for — closes the @for block |
@break |
Break — exits the innermost @for or @while loop early |
Lines without $ or ^ are parsed normally at load time (like vanilla).
Writing Macro Functions
Example function file (mypack:greet.mcfunction):
# This line is parsed at load time — no macros
say Starting greeting...
# These lines are macro lines — parsed at run time after substitution
$say Hello $(player)! You earned $(amount) diamonds.
$give $(player) minecraft:diamond $(amount)
Calling it:
function mypack:greet with args {player:"Steve",amount:"5"}
Result:
say Hello Steve! You earned 5 diamonds.
give Steve minecraft:diamond 5
From command storage:
data merge storage mypack:args {player:"Alex",amount:"10"}
function mypack:greet with storage mypack:args
From entity NBT (sub-path):
# If the entity has {data:{target:"Steve",count:3}}
function mypack:greet with entity @s data
The ^ Prefix — Forwarding Macro Context
Use ^ at the start of a line to call another function with the same macro variables automatically forwarded. No need to set up storage or pass with args again.
Three forms are supported:
# 1. Bare function id — call directly with forwarded context
^namespace:function_name
# 2. Execute chain — conditions, selectors, positioning, etc.
^execute <modifiers...> run namespace:function_name
# 3. Dynamic function name — $(var) in the id itself
^mypack:$(branch)
Example — Simple forwarding:
mypack:main.mcfunction:
$say Player $(name) has $(score) points
^mypack:reward
mypack:reward.mcfunction:
$say Rewarding $(name) for $(score) points!
$give $(name) minecraft:diamond $(score)
function mypack:main with args {name:"Steve",score:"5"}
Output:
[Server] Player Steve has 5 points
[Server] Rewarding Steve for 5 points!
Example — Conditional execution with execute if:
meteor:tick.mcfunction:
$say State check for $(target)
^execute if score temp meteor_states matches 1.. run meteor:apply_effect
Example — Execute as/at for all players:
mypack:broadcast.mcfunction:
$say Broadcasting event: $(event)
^execute as @a at @s run mypack:per_player_effect
Example — Dynamic function name:
mypack:dispatcher.mcfunction:
$say Running mode: $(mode)
^mypack:modes/$(mode)
Example — Chained ^ calls (multi-step pipeline):
mypack:pipeline.mcfunction:
$say === Pipeline for $(target) ===
^mypack:step1
^execute if score $(target) mypack_hp matches ..5 run mypack:step_heal
^mypack:step_final
Each ^ call shares the same macro variables without any manual forwarding.
Nested Macro Calls
Macro contexts are stack-based. A macro function can call another macro function with different variables — each call pushes a new scope, and it's popped when the function returns. Variables from outer calls don't leak into inner calls.
# outer.mcfunction
$say Outer: $(msg)
$function mypack:inner with args {msg:"inner message"}
$say Back to outer: $(msg)
# inner.mcfunction
$say Inner: $(msg)
Calling function mypack:outer with args {msg:"hello"} produces:
Outer: hello
Inner: inner message
Back to outer: hello
The ^ prefix, on the other hand, shares the same scope — $(msg) stays "hello" in both functions.
Missing variables: If a $(variable) has no matching key in the provided compound, it is replaced with an empty string. No error is thrown.
Function Return Values
Backports the 1.20.4+ return statement to 1.20.1. Stops function execution and returns a value to the caller.
return <value> # Stop the function and return an integer value
return run <command> # Stop the function and return the command's result
return fail # Stop the function and return 0 (failure)
When return is executed, all remaining lines in the function are skipped. The returned value becomes the function's result (usable by execute store, score64 store, etc.).
$(var) placeholders are resolved in return lines when inside a macro context.
Examples:
# mypack:get_five.mcfunction
return 5
say This line never runs
# mypack:get_health.mcfunction
return run data get entity @s Health 1
# Conditional early return
execute if score @s my_obj matches 0 run return fail
say Processing...
return run scoreboard players get @s my_obj
# Chained return with execute store
execute store result score @s doubled run function mypack:double_health
Variables
DPHelper adds a persistent variable system. Variables are stored in namespaces (like command storage) and survive server restarts.
/var Command
/var set <namespace> <key> <value>
/var get <namespace> [<key>]
/var remove <namespace> [<key>]
/var list [<namespace>]
| Command | Description |
|---|---|
var set mypack:config max_hp 100 |
Set variable max_hp to "100" in namespace mypack:config |
var get mypack:config max_hp |
Print the value of max_hp |
var get mypack:config |
Print all variables in the namespace |
var remove mypack:config max_hp |
Delete a single variable |
var remove mypack:config |
Delete the entire namespace |
var list |
List all namespaces |
var list mypack:config |
List all variables in a namespace |
All values are stored as strings. The return value of var get is the string length (for execute store).
@var — Local Variables
Set a variable in the current function scope. Available as $(name) in subsequent lines.
@var greeting = Hello World
$say $(greeting)
# Output: Hello World
@var count = 5
$give @s minecraft:diamond $(count)
# Compose from other variables
@var full = Player $(name) has $(count) items
$say $(full)
@var! — Persistent Variables
Set a variable that persists across restarts. Also available as $(key) immediately.
# Save to disk AND set in current scope
@var! mypack:stats kill_count = 42
$say Kills: $(kill_count)
@load — Load Persistent Vars into Scope
Load all variables from a namespace into the current scope.
@load mypack:config
# Now $(max_hp), $(difficulty), etc. are all available
$say Max HP: $(max_hp), Difficulty: $(difficulty)
with var — Call Function with Persistent Vars
function mypack:setup with var mypack:config
This loads all variables from mypack:config as macro variables for the function.
Example — Config system (set once, use everywhere):
# Setup (run once)
var set mypack:config max_hp 100
var set mypack:config spawn_rate 5
var set mypack:config difficulty hard
mypack:apply_config.mcfunction:
@load mypack:config
$say Max HP: $(max_hp), Spawn rate: $(spawn_rate), Mode: $(difficulty)
$scoreboard players set #max_hp config $(max_hp)
Example — Dynamic function selection based on persistent config:
var set mypack:config mode hard
function mypack:run_mode with var mypack:config
mypack:run_mode.mcfunction:
$say Running mode: $(mode)
^mypack:modes/$(mode)
Control Flow
Conditional Blocks (@if / @else / @endif)
If/else control flow directly inside .mcfunction files. No more splitting logic into dozens of separate functions.
@if <condition>
# commands to run if true
@else
# commands to run if false
@endif
The <condition> uses the same syntax as execute if — everything that follows execute if in a vanilla command:
entity <selector>score <target> <objective> matches <range>score <target> <objective> <op> <source> <sourceObjective>block <pos> <block>blocks <start> <end> <dest> <mode>data entity|block|storage ...predicate <id>
The @else branch is optional. Blocks can be nested.
Example — Basic if/else:
@if entity @s[tag=vip]
say Welcome, VIP!
give @s minecraft:diamond 10
@else
say Welcome!
give @s minecraft:diamond 1
@endif
Example — Score check:
@if score @s level matches 10..
say You are level 10 or higher!
effect give @s minecraft:strength 60 1
@endif
Example — Nested conditions:
@if entity @s[tag=admin]
say You are an admin
@if score @s power matches 100..
say And you have max power!
effect give @s minecraft:glowing 10 0
@else
say But your power is below 100
@endif
@else
say You are not an admin
@endif
Example — With macros (dynamic conditions):
$@if score $(target) kills matches $(threshold)..
$say $(target) has enough kills!
$give $(target) minecraft:diamond 1
$@endif
> Use $@if when the condition itself contains $(var) — the $ triggers macro substitution first.
Example — Combined with @var and @load:
@load mypack:config
@if score @s level matches 10..
@var reward = 10
@else
@var reward = 1
@endif
$give @s minecraft:diamond $(reward)
$say You received $(reward) diamonds!
Switch Blocks (@switch / @case / @default / @endswitch)
Match a value against multiple cases without chaining if/else blocks.
@switch <value>
@case <value1>
# commands if value matches value1
@case <value2>
# commands if value matches value2
@default
# commands if no case matched
@endswitch
The <value> in @switch is evaluated once (with macro substitution if using $@switch). Only the first matching @case executes. The @default branch executes only if no @case matched. Switch blocks can be nested.
Example — Basic switch on a variable:
@load mypack:config
@switch $(mode)
@case easy
say Easy mode — full healing
effect give @s minecraft:regeneration 30 2
@case normal
say Normal mode — standard gameplay
@case hard
say Hard mode — good luck!
effect give @s minecraft:hunger 60 1
@default
say Unknown mode: using normal
@endswitch
Example — Switch with macros (dynamic values):
$@switch $(class)
$@case warrior
$say $(player) is a warrior — bonus strength!
$effect give $(player) minecraft:strength 60 1
$@case mage
$say $(player) is a mage — bonus speed!
$effect give $(player) minecraft:speed 60 1
$@default
$say $(player) has no class assigned
$@endswitch
Example — Switch for dynamic function dispatch:
@load mypack:config
@switch $(difficulty)
@case peaceful
^mypack:setup/peaceful
@case normal
^mypack:setup/normal
@case hardcore
^mypack:setup/hardcore
@endswitch
While Loops (@while / @endwhile)
Repeat a block of commands as long as a condition is true.
@while <condition>
# commands to repeat
@endwhile
The <condition> uses the same syntax as execute if. The condition is re-evaluated before each iteration. A safety limit of 65,536 iterations prevents infinite loops.
Example — Countdown with scoreboard:
@while score @s temp matches 1..
$say Counter: $(counter)
scoreboard players remove @s temp 1
@endwhile
say Done!
Example — Process items in storage:
@while data storage mypack:queue Items[0]
say Processing next item...
data remove storage mypack:queue Items[0]
@endwhile
say Queue empty!
Example — Nested while loops:
scoreboard players set @s outer_count 3
@while score @s outer_count matches 1..
scoreboard players set @s inner_count 3
@while score @s inner_count matches 1..
say Inner iteration
scoreboard players remove @s inner_count 1
@endwhile
say Outer iteration
scoreboard players remove @s outer_count 1
@endwhile
If a @while loop exceeds 65,536 iterations, it breaks automatically and a warning is logged.
For Loops (@for / @endfor)
Counter-based loops with a variable you can reference.
@for <var> <start> <end> [<step>]
# commands to repeat — $(var) is available
@endfor
| Parameter | Description |
|---|---|
<var> |
Variable name — available as $(var) inside the loop body |
<start> |
Starting value (inclusive) |
<end> |
Ending value (inclusive) |
<step> |
Optional step increment. Defaults to 1 if start ≤ end, or -1 if start > end |
A safety limit of 65,536 iterations prevents infinite loops.
Example — Count from 1 to 10:
@for i 1 10
$say Number: $(i)
@endfor
Example — Countdown from 5 to 1:
@for i 5 1
$say Countdown: $(i)
@endfor
say Go!
Example — Custom step (even numbers):
@for i 2 20 2
$say Even: $(i)
@endfor
Example — Spawn entities at intervals:
@for x 0 10
$summon minecraft:armor_stand $(x) 64 0
@endfor
Example — With macros (dynamic range):
$@for i 1 $(max_count)
$say Iteration $(i) of $(max_count) for player $(player)
$give $(player) minecraft:diamond 1
$@endfor
Example — Nested for loops (grid pattern):
@for x 0 5
@for z 0 5
$setblock $(x) 64 $(z) minecraft:stone
@endfor
@endfor
Example — Combined with @if:
@for i 1 20
$scoreboard players set #temp obj $(i)
@if score #temp obj matches 10..
$say $(i) is 10 or higher
@else
$say $(i) is below 10
@endif
@endfor
Example — Tower builder:
@for y 64 74
$setblock 100 $(y) 200 minecraft:stone
@endfor
say Built a 10-block tower!
Break Statement (@break)
@break exits the innermost @for or @while loop early. When reached, the remaining body entries in the current iteration are skipped and no further iterations run.
@break only affects the innermost enclosing loop. In nested loops, the outer loop continues normally.
Example — Search and stop:
@for i 1 20
$execute as @e[tag=target,limit=1,sort=nearest,distance=..$(i)] run tag @s add found
@if entity @e[tag=found]
$say Found target within $(i) blocks
@break
@endif
@endfor
Example — While loop with early exit:
@while score @s attempts matches 1..
scoreboard players remove @s attempts 1
@if data storage mypack:state {done:"true"}
say Finished early!
@break
@endif
say Still trying...
@endwhile
Example — Nested loops (break only exits inner loop):
@for x 0 10
@for z 0 10
$setblock $(x) 64 $(z) minecraft:stone
@if score @s limit matches ..0
@break
@endif
scoreboard players remove @s limit 1
@endfor
$say Finished row $(x)
@endfor
Player Data Commands
Backports the 1.21.2+ change that allows /data commands to target player entities.
In vanilla 1.20.1, all /data commands on players fail with "Can't modify player data".
With DPHelper installed, they work — matching vanilla 1.21.2+ behavior.
What's Unlocked
| Command | Vanilla 1.20.1 | With DPHelper |
|---|---|---|
/data get entity @s |
❌ Blocked | ✅ Works |
/data get entity @s Health |
❌ Blocked | ✅ Works |
/data merge entity @s {Health:10f} |
❌ Blocked | ✅ Works |
/data modify entity @s Inventory[0].Count set value 64 |
❌ Blocked | ✅ Works |
/data modify entity @s Motion[1] set value 20d |
❌ Blocked | ✅ Works |
/data remove entity @s Inventory[0] |
❌ Blocked | ✅ Works |
/data get entity @e[type=pig,limit=1] |
✅ Works | ✅ Works |
Restricted Tags (Safety)
To match vanilla 1.21.2+ behavior, certain identity-critical tags are filtered on writes to prevent crashes, desyncs, or exploits:
| Tag | Reason |
|---|---|
UUID, UUIDMost, UUIDLeast |
Player identity — changing breaks everything |
Pos |
Position desync with client |
Dimension |
Forced dimension transfer — crashes/exploits |
RootVehicle |
Vehicle binding — duplication exploits |
WorldUUIDMost, WorldUUIDLeast |
Forge world tracking |
id |
Entity type — cannot change a player into a pig |
These tags are silently stripped from the data before it's applied. Reading them with /data get still works fine.
Live Field Sync
When writing player data, certain fields are re-applied to the entity's live state after the NBT load to ensure they take effect immediately:
| Field | Behavior |
|---|---|
Motion |
Applied via setDeltaMovement() + synced to client with unclamped float precision |
Rotation |
Applied via setYRot() / setXRot() |
FallDistance |
Applied directly to the entity field |
Fire |
Applied via setRemainingFireTicks() |
Velocity sync note: Vanilla's ClientboundSetEntityMotionPacket clamps velocity to ±4.096 blocks/tick. DPHelper uses ClientboundExplodePacket to sync velocity with float precision, allowing values like Motion[1] = 2000d to actually launch the player.
Player Data Examples
# Read player health
data get entity @s Health
# Read a specific inventory slot
data get entity @s Inventory[{Slot:0b}]
# Set player health
data merge entity @s {Health:1f}
# Modify inventory count
data modify entity @s Inventory[{Slot:0b}].Count set value 64
# Launch a player upward
data modify entity @s Motion[1] set value 20d
# Copy data between player and storage
data modify storage mypack:data SavedItem set from entity @s Inventory[{Slot:0b}]
data modify entity @s Inventory[{Slot:0b}] set from storage mypack:data SavedItem
# Give a player custom tags
data merge entity @s {Tags:["vip","admin"]}
# Remove a specific effect
data remove entity @s active_effects[{id:"minecraft:poison"}]
# Combine with score64 — read health with decimal precision
score64 objectives add hp 100
score64 nbt get @s hp entity @s Health scaled
# Health 18.5 → stored as 1850, displays as 18.50
# Write a score64 value back to player health
score64 players set @s hp "10.00"
score64 nbt set entity @s Health @s hp as_scaled
# 1000 / 100 = 10.0 → written as TAG_Double 10.0
Falling Block Command
Spawns a FallingBlockEntity with any block's appearance. It falls like sand and places the block where it lands. An optional NBT compound lets you configure motion, fall damage, gravity, and more.
Syntax
/fallingblock <block>
/fallingblock <block> <pos>
/fallingblock <block> <pos> <nbt>
/fallingblock convert <blockPos>
/fallingblock convert <blockPos> <nbt>
| Parameter | Description |
|---|---|
<block> |
Any block id, including block states (e.g. minecraft:oak_stairs[facing=east]) |
<pos> |
Spawn position (supports ~ ~ ~ relative coords). Defaults to executor position |
<nbt> |
Optional NBT compound to configure the entity |
<blockPos> |
Position of an existing block in the world to convert into a falling entity |
The convert variant reads the block at the given position, removes it from the world (replaces with air), and spawns a falling block entity with that block's state.
Supported NBT Tags
| Tag | Type | Default | Description |
|---|---|---|---|
Motion |
[double, double, double] |
[0d, 0d, 0d] |
Initial velocity (X, Y, Z) |
FallDistance |
float |
0.0f |
Starting fall distance for damage calculation |
HurtEntities |
byte |
0b |
1b to deal damage to entities on impact |
FallHurtMax |
int |
40 |
Max damage dealt on impact |
FallHurtAmount |
float |
2.0f |
Damage per block fallen |
DropItem |
byte |
0b |
1b to drop as item if it can't place |
Time |
int |
1 |
Ticks the entity has existed |
NoGravity |
byte |
0b |
1b to disable gravity (floats in place) |
Glowing |
byte |
0b |
1b to apply glowing outline |
Tags |
string list |
[] |
Scoreboard tags for targeting with selectors |
Falling Block Behavior
- Falls with gravity (unless
NoGravity:1b) - When it hits a solid surface, it places the block at the landing position
- If it can't place (e.g. another block is there), it disappears silently (unless
DropItem:1b)
Falling Block Examples
# Basic — spawn a falling diamond block above you
fallingblock minecraft:diamond_block ~ ~10 ~
# With motion — launch a glass block sideways
fallingblock minecraft:glass ~ ~5 ~ {Motion:[2.0d,1.0d,0d]}
# Damaging anvil drop — hurts entities on impact
fallingblock minecraft:anvil ~ ~30 ~ {HurtEntities:1b,FallHurtAmount:10.0f,FallHurtMax:100}
# Pre-charged fall distance — instant kill anvil
fallingblock minecraft:anvil ~ ~2 ~ {HurtEntities:1b,FallDistance:50.0f,FallHurtMax:999}
# Floating glowing block — no gravity, tagged for targeting
fallingblock minecraft:sea_lantern ~ ~3 ~ {NoGravity:1b,Glowing:1b,Time:-2147483648,Tags:["light","decoration"]}
# Any block state — falling stairs with specific orientation
fallingblock minecraft:oak_stairs[facing=east,half=top] ~ ~20 ~
# Meteor effect — fast downward magma that damages on impact
fallingblock minecraft:magma_block ~ ~50 ~ {Motion:[0d,-3.0d,0d],HurtEntities:1b,FallHurtAmount:5.0f}
# Combine with macros — dynamic block type and height
$fallingblock $(block) ~ ~$(height) ~ {HurtEntities:$(hurt)}
# Convert a block at a position — make it fall like sand
fallingblock convert 100 64 200
# Convert with motion — launch the block sideways
fallingblock convert ~ ~-1 ~ {Motion:[0d,1.0d,2.0d]}
# Convert with damage — make a block fall and hurt on impact
fallingblock convert 50 80 50 {HurtEntities:1b,FallHurtAmount:8.0f}
# Collapse a column — convert multiple blocks in a loop
execute positioned 100 70 200 run fallingblock convert ~ ~ ~
execute positioned 100 71 200 run fallingblock convert ~ ~ ~
execute positioned 100 72 200 run fallingblock convert ~ ~ ~
Display Entity Commands
Commands for manipulating display entities (text_display, block_display, item_display). Designed for smooth animations and transitions using Minecraft's display entity interpolation system.
Syntax
/display opacity <targets> <value>
/display opacity <targets> <value> <interpolation>
/display transform <targets> <nbt>
/display transform <targets> <nbt> <interpolation>
| Parameter | Description |
|---|---|
<targets> |
Entity selector — only display entities are affected, others are skipped |
<value> |
Unsigned opacity byte (0 = fully transparent, 255 = fully opaque) |
<nbt> |
NBT compound to merge onto the display entity |
<interpolation> |
Optional interpolation duration in ticks for smooth transition |
Opacity
Sets the text_opacity field on display entities using an unsigned value (0–255). Vanilla stores this as a signed byte (-128 to 127), but the command accepts the more intuitive unsigned range.
| Value | Effect |
|---|---|
0 |
Fully transparent (invisible) |
1 |
Nearly invisible |
128 |
Half transparent |
255 |
Fully opaque (default) |
When <interpolation> is provided, interpolation_duration is set and start_interpolation is set to 0 so the transition begins immediately.
Transform
Merges any NBT compound onto the targeted display entities. This is a general-purpose command — it can modify transformation, billboard, shadow_radius, view_range, glow_color_override, or any other display entity property.
When <interpolation> is provided, the entity smoothly transitions from its current state to the new one over the specified number of ticks.
Display Entity Examples
# Fade a text display to nearly invisible over 2 seconds
display opacity @s 1 40
# Make a text display fully opaque instantly
display opacity @e[type=text_display,tag=label] 255
# Half transparency with 1-second interpolation
display opacity @s 128 20
# Scale a display entity up smoothly over 2 seconds
display transform @s {transformation:{scale:[5f,5f,1f]}} 40
# Change billboard mode instantly
display transform @s {billboard:"center"}
# Move and rotate with interpolation
display transform @s {transformation:{translation:[0f,2f,0f],left_rotation:[0f,0.707f,0f,0.707f]}} 60
# Combine with macros — dynamic opacity
$display opacity @s $(opacity) $(fade_time)
# Combine with for loop — sequential fade
@for i 0 255 5
$display opacity @e[tag=fade_target] $(i) 2
@endfor
Datapack Usage Examples
Economy System (2 Decimal Places)
# init.mcfunction (load)
score64 objectives add coins 100
# give_coins.mcfunction
score64 players add @s coins "10.50"
# buy_item.mcfunction
score64 players remove @s coins "3.99"
# check_balance.mcfunction
score64 players get @s coins
# transfer.mcfunction (from @s to #shop)
score64 players operation #shop coins "+=" @s coins
score64 players set @s coins "0"
Precise Position Tracking (6 Decimal Places)
score64 objectives add pos_x 1000000
score64 objectives add pos_z 1000000
score64 store @s pos_x run data get entity @s Pos[0] 1000000
High-Score Counter Beyond 32-bit
score64 objectives add total_xp
score64 players add @s total_xp "1"
# Show on vanilla sidebar (clamped to 32-bit for display)
scoreboard objectives add xp_display dummy
score64 syncall total_xp xp_display
Conditional Logic
# Only run if player has ≥ 1.00 coins (raw 100 with scale 100)
execute if score64 @s coins matches "100.." run say Rich!
# Compare two players
execute if score64 @s score > @p[tag=opponent] score run say I'm winning!
Random Loot Rolls
score64 objectives add roll
score64 math random @s roll 1 100
execute if score64 @s roll matches "90.." run say Legendary drop!
execute if score64 @s roll matches "70..89" run say Rare drop!
Math Library — Distance Calculation
# Compute 2D distance: sqrt(dx² + dy²)
score64 objectives add dist 1000
score64 objectives add dx 1000
score64 objectives add dy 1000
score64 objectives add temp 1000
# temp = dx * dx
score64 players operation #calc temp = #calc dx
score64 math mul #calc temp from #calc dx
# dist = dy * dy
score64 players operation #calc dist = #calc dy
score64 math mul #calc dist from #calc dy
# dist = dx² + dy²
score64 math add #calc dist from #calc temp
# dist = sqrt(dist)
score64 math sqrt #calc dist
Using score64 Random with Macros
score64 math random @s temp 1 100
score64 nbt set storage mypack:args value @s temp
function mypack:use_random with storage mypack:args
mypack:use_random.mcfunction:
$say Random result: $(value)
Predicate in an Advancement
Create data/mypack/predicates/has_energy.json:
{
"condition": "dphelper:score64_range",
"target": "this",
"objective": "energy",
"min": 1000
}
Use with execute if predicate mypack:has_energy.
Technical Details
Persistence
- Data is stored via Minecraft's
SavedDatasystem - Attached to the overworld dimension
- Saved automatically when the world saves
- File location:
<world>/data/dphelper_score64.dat - NBT format: objectives list + nested compound tags for values
- Safe across server restarts and crashes (uses Minecraft's atomic save)
Overflow Behavior
All arithmetic uses Java long semantics — modular wraparound on overflow.
Long.MAX_VALUE + 1 = Long.MIN_VALUE
9223372036854775807 + 1 = -9223372036854775808
This is deterministic and matches how vanilla int scoreboards behave (just with 64 bits).
Performance
- O(1) lookup for any value (HashMap-based)
- No per-tick overhead — data is only accessed when commands are executed
- No world scanning — holders are string identifiers, not entity references
- Efficient for thousands of holders per objective
setDirty()is called only on mutations, not reads
Compatibility Table
| Feature | Vanilla 1.21.2+ syntax | DPHelper on 1.20.1 |
|---|---|---|
/function <id> with args {...} |
✅ Native | ✅ Identical syntax |
/function <id> with storage <id> |
✅ Native | ✅ Identical syntax |
/function <id> with entity <sel> |
✅ Native | ✅ Identical syntax |
/function <id> with block <pos> |
✅ Native | ✅ Identical syntax |
/function <id> with var <ns> |
❌ Not in vanilla | ✅ DPHelper exclusive |
$ macro lines in .mcfunction |
✅ Native | ✅ Identical syntax |
^ context-forwarding calls |
❌ Not in vanilla | ✅ DPHelper exclusive |
return / return run in functions |
✅ Native (1.20.4+) | ✅ Identical syntax |
@var / @var! local/persistent vars |
❌ Not in vanilla | ✅ DPHelper exclusive |
@load persistent var loading |
❌ Not in vanilla | ✅ DPHelper exclusive |
@if / @else / @endif |
❌ Not in vanilla | ✅ DPHelper exclusive |
@switch / @case / @default / @endswitch |
❌ Not in vanilla | ✅ DPHelper exclusive |
@while / @endwhile |
❌ Not in vanilla | ✅ DPHelper exclusive |
@for / @endfor |
❌ Not in vanilla | ✅ DPHelper exclusive |
@break |
❌ Not in vanilla | ✅ DPHelper exclusive |
/var set/get/remove/list |
❌ Not in vanilla | ✅ DPHelper exclusive |
/data get entity @s |
✅ Native (1.21.2+) | ✅ Identical syntax |
/data merge entity @s {...} |
✅ Native (1.21.2+) | ✅ Identical syntax |
/data modify entity @s ... |
✅ Native (1.21.2+) | ✅ Identical syntax |
| Motion velocity (unclamped) | ⚠️ Clamped in vanilla | ✅ Float precision via explosion packet |
| Restricted tags on player writes | ✅ Filtered | ✅ Filtered (same set + Forge extras) |
/fallingblock <block> [pos] [nbt] |
❌ Not in vanilla | ✅ DPHelper exclusive |
/display opacity <targets> <value> [interp] |
❌ Not in vanilla | ✅ DPHelper exclusive |
/display transform <targets> <nbt> [interp] |
❌ Not in vanilla | ✅ DPHelper exclusive |
Datapacks using vanilla 1.21+ features can be ported directly to DPHelper on 1.20.1 with no syntax changes.
Server Compatibility
| Requirement | Status |
|---|---|
| Server-only mod | ✅ |
| Vanilla clients can join | ✅ (IGNORE_SERVER_VERSION) |
| No client-side code | ✅ |
| No vanilla scoreboard modifications | ✅ |
| No networking protocol changes | ✅ |
| Forge 47+ / MC 1.20.1 | ✅ |
| Java 17 | ✅ |

