-
Notifications
You must be signed in to change notification settings - Fork 0
Getting Started
This is a hands-on guide for modpack developers. It builds a real interaction from scratch and adds one feature at a time, so by the end you can write almost anything. Every snippet is copy-paste-ready.
Prefer to read the exhaustive field list instead? See interaction-format.md. Prefer to copy finished files? See
configExamples/interactions/v2.
Contents
- Setup
- Your first interaction
- Requiring a tool in hand
- Drops: weighted, counts, guaranteed, rolls, fortune
- Choosing the target: blocks, states, fluids, air
- Transforming the block
- Costs: damage and hunger
- Effects, sounds and particles
- Conditions: biome, time, weather, Y, light, player
- Matching item / block NBT
- Organising a pack
- Cookbook: common recipes
- Troubleshooting
-
Install Forge 1.20.1, JEI, and PunchThemAll.
-
Launch the game once. This creates the folder:
config/punchthemall/interactions/ -
Put
.jsonfiles in that folder (subfolders are fine). Each file is one interaction. -
After editing files, run
/reloadin-game to apply changes. No restart needed. -
Open JEI and look at the Interaction category to see what loaded.
Always start a v2 file with
"schema_version": 2. It gives you strict JSON validation and the clearest error messages, and unlocks every feature in this guide.
If a file fails to load, the game log shows a line beginning with
PunchThemAll - Incorrect Json format - <file> - <reason>. Keep the log open while authoring.
The smallest useful interaction: left-click dirt to get coarse dirt.
config/punchthemall/interactions/coarse_dirt.json
{
"schema_version": 2,
"type": "left_click",
"target": { "kind": "block", "match": "minecraft:dirt" },
"rewards": {
"weighted": [
{ "match": "minecraft:coarse_dirt", "weight": 1 }
]
}
}-
type— which click triggers it:left_click,right_click,shift_left_click,shift_right_click. -
target.match— the block you click. One id, a list, or a#tag. -
rewards.weighted— the item(s) you get.
Run /reload, left-click some dirt, and coarse dirt pops out.
Most interactions want a specific tool. Let's require any shovel and damage it each time.
{
"schema_version": 2,
"type": "shift_left_click",
"hand": {
"hand": "main",
"match": "#minecraft:shovels",
"consume": { "mode": "durability", "chance": 1.0 }
},
"target": { "kind": "block", "match": "minecraft:gravel" },
"rewards": {
"weighted": [
{ "match": "minecraft:flint", "weight": 25, "count": 1 },
{ "match": "minecraft:air", "weight": 75 }
]
}
}-
hand.hand—main,off, orany. -
hand.match— required item(s)/tag. Leavehandout entirely to require an empty hand. -
hand.consume.mode:-
durability— damage a damageable tool (breaks when it runs out), -
shrink— consume one from the stack (good for ingredients like buckets or seeds), -
none— don't spend it.
-
-
hand.consume.chance— probability of spending it (e.g.0.5= half the time).
The
"minecraft:air"entry with weight 75 is a "nothing" filler: 75% of clicks yield nothing. Weights are relative, so25vs75means a 25% chance of flint.
Everything about outputs lives in rewards.
"rewards": {
"weighted": [
{ "match": "minecraft:iron_nugget", "weight": 20, "count": { "min": 1, "max": 2 } },
{ "match": "minecraft:air", "weight": 80 }
]
}count accepts three shapes: 3, { "count": 3 }, or { "min": 1, "max": 3 }.
Items in guaranteed are always given, on top of the weighted picks:
"rewards": {
"guaranteed": [ { "match": "minecraft:cobblestone", "count": 1 } ],
"weighted": [ { "match": "minecraft:coal", "weight": 15 }, { "match": "minecraft:air", "weight": 85 } ]
}rolls draws the weighted pool several times (guaranteed drops are given once):
"rewards": { "rolls": 3, "weighted": [ { "match": "minecraft:coal", "weight": 15 }, { "match": "minecraft:air", "weight": 85 } ] }Add extra items to weighted picks based on an enchantment on the held item:
"rewards": {
"weighted": [ { "match": "minecraft:diamond", "weight": 100 } ],
"fortune": { "enchant": "minecraft:fortune", "factor": 1.0 }
}With Fortune III and factor: 1.0, each weighted pick gets round(3 × 1.0) = 3 extra items.
"target": {
"kind": "block",
"match": "minecraft:furnace",
"state": {
"whitelist": { "lit": "true" },
"blacklist": { "waterlogged": "true" }
}
}State values are compared as text: use "true", "north", "3", etc.
Enable fluid interactions in the config (on by default), then:
"target": { "kind": "fluid", "match": "minecraft:water" }"target": { "kind": "air" }Air interactions require an empty hand or a matching hand item and are great for "cast a rod", "swing a tool", etc.
"target": { "kind": "any", "match": "#minecraft:logs" }Turn the clicked block/fluid into another one after a successful interaction:
"transformation": {
"chance": 0.7,
"into": { "kind": "block", "id": "minecraft:sand", "state": { "facing": "copy_state_value" } },
"sound": "minecraft:block.gravel.break",
"particles": "minecraft:sand"
}-
chance— probability of the transformation happening. -
into.kind—block,fluid, orair(air = break the block). -
into.state— set specific state values, or"copy_state_value"to keep the original block's value. -
particles— a block id (block-break particles).
Transformations obey the allow_transformations config gate and happen at most once per click.
Make the interaction cost the player something:
"costs": {
"damage": { "chance": 1.0, "amount": 1 },
"hunger": { "chance": 0.5, "amount": { "min": 2, "max": 8 } }
}-
damage.amountis in half-hearts (2= one heart). -
hunger.amountis in hunger points. -
amountaccepts the same shapes ascount(3,{count},{min,max}).
These respect the allow_player_damage / allow_food_consumption config gates.
Grant potion effects and play feedback on the interaction itself:
"effects": [
{ "id": "minecraft:haste", "duration": 200, "amplifier": 0, "chance": 0.3 }
],
"sound": "minecraft:entity.player.levelup",
"particles": "minecraft:sugar_cane"-
durationis in ticks (20= 1 second);amplifier0= level I. -
sound/particleshere fire on every success (transformations have their own sound/particles).
Gate an interaction so it only fires in the right situation. Everything is optional — add only what you need.
"conditions": {
"biomes": { "whitelist": ["minecraft:desert"], "blacklist": [] },
"time": "night",
"weather": ["clear", "rain"],
"y_range": [-64, 40],
"light": { "max": 7 },
"requires_sneaking": true,
"player_state": { "min_food": 6, "min_xp_levels": 1 }
}-
biomes— exact biome or dimension ids (minecraft:the_netherworks too). Use whitelist or blacklist, not both. -
time—any,day, ornight. -
weather— any ofclear,rain,thunder. Omit for "any weather". -
y_range—[minY, maxY]. -
light— block lightmin/max(0–15). -
requires_sneaking—true/false. -
player_state— minimum food and XP levels the player must have.
Two ways to match item or block-entity data. You can use both together.
Clean, validated, and shown in JEI:
"nbt_predicates": [
{ "path": "Damage", "int_range": [0, 500] },
{ "path": "Enchantments[].lvl", "int_range": [2, 7], "where": "{id:\"minecraft:unbreaking\"}" }
]-
path— dotted path; a segment ending in[]iterates a list. -
int_range—[min, max]; omit to only test that the value exists. -
where— an SNBT string that filters which list elements count. - All predicates in the list must pass (AND).
Put nbt_predicates on hand (to match the held item) or on target (to match a block entity such
as a chest's contents).
A predicate on an absent tag fails. A brand-new tool has no
Damagetag until it's used, so aDamagepredicate won't match it.
For exact-shape matching, write NBT as an SNBT string:
"nbt": {
"whitelist": "{Damage:{RangeTag:[0,500]}}",
"blacklist": "{Enchantments:[{id:\"minecraft:silk_touch\"}]}"
}The {RangeTag:[min,max]} helper still works inside these strings.
-
Folders become ids.
interactions/create/crushing/gravel.json→pta:create/crushing/gravel. Keep filenames lowercase with underscores. - One interaction per file. It keeps ids meaningful and JEI readable.
-
Toggle without deleting. Add
"enabled": falseto a file to skip it. -
Ship in a datapack (optional). Set
Loader.load_from_datapacks = trueinconfig/punchthemall/pta-common.toml, then place files atdata/<namespace>/pta/interaction/*.json. Datapack files override config files with the same id and are synchronised to clients automatically. -
Global tuning (cooldowns, click/target gates, fake players, drop physics) lives in
pta-common.toml— see configuration.md.
Hammer crushing (cobble → gravel → sand → dust), consuming durability:
{
"schema_version": 2,
"type": "shift_left_click",
"hand": { "hand": "main", "match": "#forge:tools/hammers", "consume": { "mode": "durability" } },
"target": { "kind": "block", "match": "minecraft:cobblestone" },
"transformation": { "chance": 1.0, "into": { "kind": "block", "id": "minecraft:gravel" }, "particles": "minecraft:gravel" },
"rewards": { "weighted": [ { "match": "minecraft:air", "weight": 1 } ] }
}Night-only, deep, dark mining bonus with Fortune:
{
"schema_version": 2,
"type": "shift_left_click",
"hand": { "hand": "main", "match": "#minecraft:pickaxes", "consume": { "mode": "durability" } },
"target": { "kind": "block", "match": "minecraft:deepslate" },
"rewards": {
"weighted": [ { "match": "minecraft:raw_gold", "weight": 20 }, { "match": "minecraft:cobbled_deepslate", "weight": 80 } ],
"fortune": { "enchant": "minecraft:fortune", "factor": 1.0 }
},
"conditions": { "time": "night", "y_range": [-64, 8], "light": { "max": 7 } }
}Bottle water for a chance at clay (consume the bottle):
{
"schema_version": 2,
"type": "right_click",
"hand": { "hand": "main", "match": "minecraft:glass_bottle", "consume": { "mode": "shrink" } },
"target": { "kind": "fluid", "match": "minecraft:water" },
"rewards": { "weighted": [ { "match": "minecraft:clay_ball", "weight": 15 }, { "match": "minecraft:air", "weight": 85 } ] }
}"Ritual" empty-hand click in the air that costs food and grants an effect:
{
"schema_version": 2,
"type": "right_click",
"target": { "kind": "air" },
"rewards": { "weighted": [ { "match": "minecraft:air", "weight": 1 } ] },
"costs": { "hunger": { "chance": 1.0, "amount": 4 } },
"effects": [ { "id": "minecraft:regeneration", "duration": 100, "amplifier": 0, "chance": 1.0 } ],
"sound": "minecraft:block.enchantment_table.use"
}More single-feature examples: configExamples/interactions/v2.
The file doesn't load.
Check the log for Incorrect Json format - <file> - <reason>. Common causes: invalid JSON (a
trailing comma, a missing quote), an unknown item/block id, or an unknown tag. Fix and /reload.
It loads but never triggers.
- Turn on
Debug.log_skipped_interactionsinpta-common.tomlto see why a click is skipped. - Confirm the
typematches how you're clicking (sneaking or not; left vs right). - Check
hand— if you omitted it, the interaction requires an empty hand. - Check
conditions(time/weather/biome/Y/light/food/XP) and the global config gates (allow_left_click,allow_block_interactions, …). - Remember the per-player cooldown (
Interactions.cooldown_ticks).
An NBT predicate never matches.
The tag is probably absent (e.g. Damage on a fresh tool). Loosen the predicate or match a tag that
actually exists on the item.
JEI shows nothing / wrong recipes on a dedicated server.
The server syncs its interactions to clients on join and on /reload. Reconnect or /reload on the
server. On singleplayer this is automatic.
I see a "legacy format" deprecation warning.
That file has no schema_version (or 1). It still works, but consider migrating to
schema_version: 2 — see the migration table in interaction-format.md.
- Full field reference: interaction-format.md
- Config keys & presets: configuration.md
- Loading, IDs, JEI, multiplayer: interactions.md
-
Copy-paste examples:
configExamples/interactions/v2
We would like to thank our community for their support and contributions to PunchThemAll. Your feedback helps us improve the mod and create a better experience for all users.
PunchThemAll is free and open source under the MIT License. See the LICENSE file for details.