I found a clever trick for datapacks using a combination of scoreboards and player's NBT data to make custom crafts where the output item comes with custom NBT data.
In this blog, I will outline the steps to do so and how I implemented it to create a custom set of tools worst than wood.
Step 0Simply put this inside your namespace recipes folder and it will add a brand new recipe for a stone sword.
{ "type": "crafting_shapeless", "ingredients": [ { "item": "minecraft:flint" }, { "item": "minecraft:stick" } ], "result": { "item": "minecraft:stone_sword", "count": 1 } } |
Step 1
Step one, you want to create two scoreboards inside your reload function. For this example, I will make a flint knife, which will be a weaker version of a wooden sword.
scoreboard objectives create knife minecraft.crafted:minecraft.stone_sword |
scoreboard objectives create success dummy |
Here, we create an objective to know when a standard stone sword has been crafted. Right now, it adds a point to the score every time any sword is crafted, we will fix that later. The dummy objective is technically optional, but without it, it's entirely possible that items get either deleted or duplicated.
Step 2Step two, you want to check for changes in the scoreboard in your tick function. Simply insert the following line:
execute if @a[scores={knife=1..}] run function <namespace>:knife |
What this will do is every time someone crafts a stone sword, it runs a separate function called knife.
In this step, you also want to create a function called knife.
Step 3Step three, since our flint knife is pre-wood technology, we assume the player does not have access to a crafting table, and as such must use the crafting menu. The following line of code will be put in the knife function and will check for that exact scenario.
execute at @a[scores={knife=1..}] store success score @p[scores={knife=1..},nbt={isGuiOpen=1b}] success byte 1 if @p[scores={knife=1..},nbt={isGuiOpen=1b}] run clear @p[scores={knife=1..},nbt={isGuiOpen=1b}] stone_sword 1 |
This is a complicated one, and as such I will explain it one subcommand at a time.
Firstly, the "at" subcommand. It's standard practice to use at with an @a and then changing it to @p when dealing with NBT data in order to always select a single player, this is because NBT manipulation does not work on target selectors such as @a and @e.
Next, the "store" subcommand. This one checks if the stone sword that was crafted actually got cleared so that we can replace it with our new one. We need this as some people don't shift-click the item out of their crafting grid, but instead drag it, taking time after it has been crafted. If you didn't add the optional scoreboard on step one, you can remove this subcommand.
With this part of the command, the function will run once every tick until the player has finished dragging the sword out of their crafting grid.
Next, the "if" subcommand simply checks if both a knife has been crafted and the player is inside their inventory. If one of those is false, the command won't run.
Lastly, "run" simply runs a command deleting a single stone sword from their inventory.
Step 4This part will also go in the knife function and will stop the function from looping if the sword was crafted in a crafting table.
execute at @a[scores={knife=1..}] unless @p[scores={knife=1..},nbt={isGuiOpen=1b}] run scoreboard players remove @p[scores={knife=1..},nbt={isGuiOpen=0b}] knife 1 |
This piece is simple, if the sword was crafted in a crafting table, it removes the score you gained when you crafted it.
Step 5This piece of the datapack is also rather complicated, as it will give the player a stone sword with a custom name and NBT tag attached to it. Again, paste this into knife.
execute if @a[scores={success=1}] run give @a[scores={success=1}] minecraft:stone_sword{display:{Name:'[{"text":"Flint Knife"}]'},Tags:["flint_knife"]} 1 |
Here we have the standard start when dealing with NBT data, with "at" and "if", but where it becomes a bit complicated is with the "run", where we put two pieces of NBT data on the sword, a name and a tag. This tag will help us differentiate this sword from the others without players being able to cheat by changing the name. Currently, this sword deals as much damage as a normal stone sword, but before fixing that, there is another step to do.
Step 6This step is simple, we just remove the knife score from the player as well as the success score.
execute if @a[scores={success=1}] run scoreboard players remove @a[scores={success=1}] knife 1 |
execute if @a[scores={success=1}] run scoreboard players set @a[scores={success=1}] success 0 |
It's almost the same as step 4, but with an added command removing the success score.
Step 7This time, for the last step, we change it up and add a line to the tick command, which will weaken the knife. The command is a bit complicated and different from the others.
execute at @a if @p[nbt={SelectedItem:{tag:["flint_knife"]}}] run effect give @p[nbt={SelectedItem:{tag:["flint_knife"]}}] weakness 1 0 false |
This one simply detects if anyone is holding the knife and if so gives them temporary weakness, as to make the knife weaker than other weapons.
ConclusionThere are definitely ways to make this run with better performance, and I may upgrade this blog in the days to come with performance upgrades, a complete copy+pastable recipe for the knife and complete copy+pastable functions, but I've been writing for an hour now and I'm developing carpal tunnel.
Changelog:Edit 1: Removed "art" tag (whoops) and fixed structure and colour issues.
Edit 2: Added copy + pastable recipe and cleaned up a lot of the commands where it checked for both success=1 and knife=1.., even though you need knife=1.. to have success=1. Also removed unneeded "at" subcommands where it wasn't dealing with NBT data.
Step 1: Add recipe
"type": "crafting_shapeless",
"ingredients": [
{
"item": "minecraft:flint"
},
{
"item": "minecraft:stick"
}
],
"result": {
"item": "minecraft:knowledge_book",
"count": 1
}
}
Step 2: Add advancements
"Unlocked": {
"trigger": "minecraft:recipe_unlocked",
"conditions": { "recipe": "knife:kniferecipe" }
}
},
"rewards": {
"function": "knife:knife/recipe"
}
}
Step 3: Do a loop function
advancement revoke @s only knife:kniferecipeadv
give @s minecraft:stone_sword{display:{Name:'[{"text":"Flint Knife"}]'},AttributeModifiers:[{AttributeName:"generic.attack_damage", Name:"generic.attack_damage", Amount:-1.0, Operation:0, UUID:[I; 42853, 1689024593, -201178, -1559272105]
clear @s minecraft:knowledge_book 1
Yes, I know mine has some janky things such as adding a cheaper stone sword recipe for crafting tables, but I'd still use my way over using the knowledge book.
Performance isn't as big a concern for me over making the game experience feel better.
Though it's not actually infinite, it's possible that the weakness rubs off on another slot, so good point! :D
I'll update my blog when I get the chance. Should be updated by tomorrow.