Building with Custom Components
Create an Add-On with Custom Components
Note
The custom components feature is currently in preview and is available via beta APIs, but is targeted to be included with "stable APIs" in a future release of Minecraft - potentially 1.21.10. We recommend you use the latest version of Minecraft Preview as you try out this sample.
Custom blocks and items use various components, declared in their definitions, to augment the behavior of the block or item.
Until now, all components have been built-in to Minecraft, with various parameters used to control the component's behavior. With custom components, now in preview and more broadly available in an upcoming version of Minecraft, you can define your own behavior for blocks and items when combined with scripting! In this tutorial we will make a small block and item example using custom components and scripting by adding a new strawberry crop and strawberry item. The strawberry plants will have the behavior that, if they are not picked when they are ripe, can go bad. You'll have to time it just right to get fresh strawberries, and in this sample, you'll see the block components that manage crop growth rates in addition to custom item effects.
You can find the source of this example on the github.com/microsoft/minecraft-scripting-samples.
Prerequisites
Before you begin, you should have gone through the Getting Started with Add-Ons tutorial, the Introduction to Behavior Packs tutorial, and the Introduction to Scripting tutorial.
You'll want to be comfortable with how add-on folders are structured, what required files the behavior pack should contain, and how to use scripting with your behavior pack.
Some other good resources to know are how to make blocks and items in your add-on:
Item Custom Components
The strawberry item will utilize custom components to give the player that eats it night vision. First lets make the strawberry item itself.
{
"format_version": "1.21.10",
"minecraft:item": {
"description": {
"menu_category": {
"group": "itemGroup.name.crop",
"category": "nature"
},
"identifier": "example:strawberry"
},
"components": {
"minecraft:icon": "strawberry",
"minecraft:max_stack_size": 64,
"minecraft:use_modifiers": {
"use_duration": 1.6,
"movement_modifier": 0.35
},
"minecraft:food": {
"can_always_eat": true,
"nutrition": 1,
"saturation_modifier": 0.5
},
"minecraft:use_animation": "eat",
}
}
}
Adding Custom Components to Items
To add custom components to an item, add a minecraft:custom_components
component to the item which has an array of strings.
Within this array, the strings are the name of the component that you are adding. When an event occurs, the components will run some script, and this will run in the order defined in your JSON. This means that one item can run the same components in a different order than another item. For our strawberry, we will need only 1 component, but for your own items you can add more custom components to the array.
"minecraft:custom_components": [
"example:add_night_vision_on_consume"
]
The name you choose for your custom component requires a namespace before it. In the code above, the namespace is example
and the component name is add_night_vision_on_consume
.
Registering Item Custom Components in Script
Now that our item has been configured to have a custom component, we need to register the behavior for the component in script. The behavior we want is that when you eat the strawberry, the player receives night vision for some time. First, we register our component using the same name we defined in the JSON component to the ItemComponentRegistry
and provide a list of events that this component is listening for. In this case we are going to listen to the onConsume
event for item custom components which will run our code when the item is consumed by a player. We can then add the night vision effect to the player and our custom component is now ready to be used.
import { ItemComponentConsumeEvent, world } from "@minecraft/server";
world.beforeEvents.worldInitialize.subscribe(initEvent => {
initEvent.itemComponentRegistry.registerCustomComponent('example:add_night_vision_on_consume', {
onConsume(arg: ItemComponentConsumeEvent) {
arg.source.addEffect('minecraft:night_vision', 600);
}
});
});
Notice that the component code and name do not reference the strawberry item itself. You can reuse components on multiple items that have similar behavior.
Note
As of this writing, custom components are still in preview. For this reason you will want to use a -beta scripting module in the dependencies section of your behavior pack manifest.json.
"dependencies": [
{
"module_name": "@minecraft/server",
"version": "1.13.0-beta"
}
]
Block Custom Components
Block custom components work very similarly to item custom components. First we need our block definition:
{
"format_version": "1.21.10",
"minecraft:block": {
"description": {
"identifier": "example:strawberry_crop",
"states": {
"starter:crop_age": [ 0, 1, 2, 3, 4 ]
}
},
"permutations": [
{
"condition": "query.block_state('starter:crop_age') == 4",
"components": {
"minecraft:loot": "loot_tables/strawberry_grown_crop.json"
}
}
],
"components": {
"minecraft:geometry": "geometry.starter_crop_geo",
"minecraft:loot": "loot_tables/strawberry_seed.json",
"minecraft:collision_box": false,
"minecraft:placement_filter": {
"conditions": [
{
"allowed_faces": ["up"],
"block_filter": ["minecraft:farmland"]
}
]
},
"tag:minecraft:crop": {}
}
}
}
Adding Custom Components to Blocks
Similar to item custom components, we use the minecraft:custom_components
component in the block JSON to define what custom components this block has. In this case we are going to add 2 different components definitions: one in the base of the block and one within the permutation. When both a block permutation and base block use the minecraft:custom_components
component, only the components listed in the permutation will have their script code run. This allows you to reorder, remove, or add new custom components to specific permutations of a block. Since the first 4 permutations (age 0-3) of the strawberry crop all use the same custom components, we can just put this in the base block's component list. The last permutation (age 4) will have some additional functionality so we can harvest the fully grown strawberries, so it will need its own minecraft:custom_components
to override the one in the base block.
{
"format_version": "1.21.10",
"minecraft:block": {
"description": {
"identifier": "example:strawberry_crop",
"states": {
"starter:crop_age": [ 0, 1, 2, 3, 4 ]
}
},
"permutations": [
{
"condition": "query.block_state('example:crop_age') == 4",
"components": {
"minecraft:loot": "loot_tables/strawberry_grown_crop.json"
},
"minecraft:custom_components": [
"example:crop_harvest"
]
}
],
"components": {
"minecraft:geometry": "geometry.example_crop_geo",
"minecraft:loot": "loot_tables/strawberry_seed.json",
"minecraft:collision_box": false,
"minecraft:placement_filter": {
"conditions": [
{
"allowed_faces": ["up"],
"block_filter": ["minecraft:farmland"]
}
]
},
"tag:minecraft:crop": {},
"minecraft:custom_components": [
"example:crop_grow"
]
}
}
}
Registering Block Custom Components in Script
Similar to items, we register the component in script with a list of events that the component is listening to and the behavior that should be run when the event is raised. For items we used the ItemComponentRegistry
to do this; for blocks we use the BlockComponentRegistry
(or BlockTypeRegistry
in versions of Minecraft 1.21.0 or prior). In this case, we have two components to fill out. The item example above showed how to do this by placing your behavior in the registration statement. These two components will look at two alternate ways you can organize your code.
"example:crop_grow" Component
This component is designed to grow crops, which we can do by listening to the onRandomTick
event for block custom components, and changing the block's permutation to the next age. This component is registered by giving the event a function to run where the previous item custom component example had its code.
import {
BlockComponentRandomTickEvent,
world
} from "@minecraft/server";
function cropGrowRandomTick(event : BlockComponentRandomTickEvent) {
const age = event.block.permutation.getState('example:crop_age');
if (age === undefined || typeof age !== 'number') {
return;
}
else if (age === 4) {
return; // fully grown
}
event.block.setPermutation(event.block.permutation.withState('example:crop_age', age + 1));
}
world.beforeEvents.worldInitialize.subscribe(initEvent => {
initEvent.blockTypeRegistry.registerCustomComponent('example:crop_grow', {
onRandomTick: cropGrowRandomTick,
});
});
"example:crop_harvest" Component
The crop grown component exists on only the finished crop when it is ready to be harvested. This component will allow the player to interact with the block to harvest the strawberries without having to break the block. It then "replants" the strawberries by changing the block permutation back to the first growth stage. This component is registered by making a new class that implements the BlockCustomComponet object, giving you a third way to register components. The same can be done with items and the ItemCustomComponent object.
import {
BlockCustomComponent,
BlockComponentPlayerInteractEvent,
world
} from "@minecraft/server";
class BlockCropHarvestComponent implements BlockCustomComponent {
onPlayerInteract(event : BlockComponentPlayerInteractEvent) {
if (event.player === undefined) {
return;
}
const blockPos = event.block.location;
event.dimension.runCommand('loot spawn ' +
blockPos.x + ' ' +
blockPos.y + ' ' +
blockPos.z + ' loot strawberry_grown_crop'
);
event.block.setPermutation(event.block.permutation.withState('example:crop_age', 0));
}
};
world.beforeEvents.worldInitialize.subscribe(initEvent => {
initEvent.blockTypeRegistry.registerCustomComponent('example:crop_harvest', new BlockCropHarvestComponent());
});
Congratulations! You now have a block and an item using custom components. Check out our custom components sample at the Minecraft Scripting Samples repository.
Feedback
https://aka.ms/ContentUserFeedback.
Coming soon: Throughout 2024 we will be phasing out GitHub Issues as the feedback mechanism for content and replacing it with a new feedback system. For more information see:Submit and view feedback for