Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explain blocks and blockstates in more depth #21

Merged
merged 31 commits into from
Nov 25, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
40e3d13
explain blocks and blockstates in more depth
IchHabeHunger54 Oct 17, 2023
4c87eac
update headers
IchHabeHunger54 Oct 17, 2023
b81bf08
implement feedback, add reference.md
IchHabeHunger54 Oct 17, 2023
4326538
add reference for methods in Block.java
IchHabeHunger54 Oct 18, 2023
f5ca59b
add interactionpipeline.md
IchHabeHunger54 Oct 18, 2023
2dcc08c
add documentation for methods of BlockBehaviour
IchHabeHunger54 Oct 18, 2023
a5d4da1
add common block pipelines
IchHabeHunger54 Oct 18, 2023
eeb617a
Update docs/blocks/index.md
IchHabeHunger54 Oct 22, 2023
d2b5bd0
Update docs/blocks/index.md
IchHabeHunger54 Oct 22, 2023
93a5b31
Update docs/blocks/index.md
IchHabeHunger54 Oct 22, 2023
fa5b374
remove reference (for PRing to Parchment instead), apply feedback
IchHabeHunger54 Oct 22, 2023
6fff887
Merge remote-tracking branch 'origin/rework/blocks' into rework/blocks
IchHabeHunger54 Oct 22, 2023
1fbcd04
remove faulty link to now-gone reference.md
IchHabeHunger54 Oct 22, 2023
32a3e05
implement feedback
IchHabeHunger54 Oct 24, 2023
17c0098
implement feedback
IchHabeHunger54 Oct 26, 2023
b6741fd
implement more feedback
IchHabeHunger54 Oct 26, 2023
55c37f5
add extra disclaimer about PlayerInteractEvent.LeftClickBlock
IchHabeHunger54 Oct 26, 2023
a3090aa
add highlights to codeblocks
IchHabeHunger54 Oct 26, 2023
27f5fd5
Update docs/blocks/states.md
IchHabeHunger54 Nov 15, 2023
f510e01
shorten interactionpipeline.md
IchHabeHunger54 Nov 15, 2023
fc9e448
Merge remote-tracking branch 'origin/rework/blocks' into rework/blocks
IchHabeHunger54 Nov 15, 2023
d7662bd
fix a grammar mistake in states.md
IchHabeHunger54 Nov 15, 2023
06b95e2
Update docs/blocks/states.md
IchHabeHunger54 Nov 15, 2023
0b9ce65
implement requested feedback
IchHabeHunger54 Nov 20, 2023
58109b5
Merge remote-tracking branch 'origin/rework/blocks' into rework/blocks
IchHabeHunger54 Nov 20, 2023
723dfe2
Update docs/blocks/states.md
IchHabeHunger54 Nov 22, 2023
03822eb
adjust to the new registry system
IchHabeHunger54 Nov 22, 2023
4c1ae30
simplify block breaking a bit
IchHabeHunger54 Nov 22, 2023
b6f860a
Merge remote-tracking branch 'origin/rework/blocks' into rework/blocks
IchHabeHunger54 Nov 22, 2023
6e6d973
Update docs/blocks/index.md
IchHabeHunger54 Nov 22, 2023
a8b4c53
Update docs/blocks/index.md
IchHabeHunger54 Nov 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 145 additions & 27 deletions docs/blocks/index.md
Original file line number Diff line number Diff line change
@@ -1,51 +1,169 @@
Blocks
======

Blocks are, obviously, essential to the Minecraft world. They make up all of the terrain, structures, and machines. Chances are if you are interested in making a mod, then you will want to add some blocks. This page will guide you through the creation of blocks, and some of the things you can do with them.
Blocks are, obviously, essential to the Minecraft world. They make up all the terrain, structures, and machines. Chances are if you are interested in making a mod, then you will want to add some blocks. This page will guide you through the creation of blocks, and some of the things you can do with them.
IchHabeHunger54 marked this conversation as resolved.
Show resolved Hide resolved

Creating a Block
----------------
One Block to Rule Them All
--------------------------

Before we get started, it is important to understand that there is only ever one of each block in the game. A world consists of thousands of references to that one block in different locations. In other words, the same block is just displayed a lot of times.

Due to this, a block should only ever be instantiated once, and that is during registration. Once the block is registered, you can then use the registered reference as needed. Consider this example (see the [Registration][registration] page if you do not know what you are looking at):

```java
public static final RegistryObject<Block> MY_BLOCK = BLOCKS.register("my_block", () -> new Block(...));
IchHabeHunger54 marked this conversation as resolved.
Show resolved Hide resolved
IchHabeHunger54 marked this conversation as resolved.
Show resolved Hide resolved
```

After registering the block, all references to the new `my_block` should use this constant. For example, if you want to check if the block at a given position is a `my_block`, the code for that would look something like this:
IchHabeHunger54 marked this conversation as resolved.
Show resolved Hide resolved

```java
level.getBlockState(position).is(MyBlockRegistrationClass.MY_BLOCK.get());
IchHabeHunger54 marked this conversation as resolved.
Show resolved Hide resolved
```

:::danger
As soon as you create a `new Block()` outside registration, things can and will break!
IchHabeHunger54 marked this conversation as resolved.
Show resolved Hide resolved
:::

Creating Blocks
---------------

### Basic Blocks

For simple blocks, which need no special functionality (think cobblestone, wooden planks, etc.), a custom class is not necessary. You can create a block by instantiating the `Block` class with a `BlockBehaviour$Properties` object. This `BlockBehaviour$Properties` object can be made using `BlockBehaviour$Properties#of`, and it can be customized by calling its methods. For instance:
For simple blocks which need no special functionality (think cobblestone, wooden planks, etc.), the `Block` class can be used directly. To do so, during registration, instantiate `Block` with a `BlockBehaviour.Properties` parameter. This `BlockBehaviour.Properties` parameter can be created using `BlockBehaviour.Properties#of`, and it can be customized by calling its methods. The most important methods for this are:

- `destroyTime` - Determines the time the block needs to be destroyed.
- `explosionResistance` - Determines the explosion resistance of the block.
- `sound` - Sets the sound the block makes when it is punched, broken, or placed.
- `lightLevel` - Sets the light emission of the block. Accepts a function with a `BlockState` parameter that returns a value between 0 and 15.
- `friction` - Sets the friction (slipperiness) of the block.
IchHabeHunger54 marked this conversation as resolved.
Show resolved Hide resolved

- `strength` - The hardness controls the time it takes to break the block. It is an arbitrary value. For reference, stone has a hardness of 1.5, and dirt 0.5. If the block should be unbreakable a hardness of -1.0 should be used, see the definition of `Blocks#BEDROCK` as an example. The resistance controls the explosion resistance of the block. For reference, stone has a resistance of 6.0, and dirt 0.5.
- `sound` - Controls the sound the block makes when it is punched, broken, or placed. Requires a `SoundType` argument, see the [sounds] page for more details.
- `lightLevel` - Controls the light emission of the block. Takes a function with a `BlockState` parameter that returns a value from zero to fifteen.
- `friction` - Controls how slippery the block is. For reference, ice has a slipperiness of 0.98.
So for example, a simple implementation would look something like this:

All these methods are *chainable* which means you can call them in series. See the `Blocks` class for examples of this.
```java
public static final RegistryObject<Block> MY_BETTER_BLOCK = BLOCKS.register("my_better_block", () -> new Block(BlockBehaviour.Properties.of()
IchHabeHunger54 marked this conversation as resolved.
Show resolved Hide resolved
.destroyTime(2.0f)
.explosionResistance(10.0f)
.sound(SoundType.GRAVEL)
.lightLevel(state -> 7)));
```

For further documentation, see the [`BlockBehaviour.Properties` reference][propertiesreference]. For more examples, or to look at the values used by Minecraft, have a look at the `Blocks` class.

:::note
Blocks have no setter for their `CreativeModeTab`. This is handled by the [`BuildCreativeModeTabContentsEvent`][creativetabs] if the block has an associated item (e.g. `BlockItem`). Furthermore, there is no setter for translation key of the block as it is generated from the registry name via `Block#getDescriptionId`.
It is important to understand that a block in the world is not the same thing as in an inventory. What looks like a block in an inventory is actually a `BlockItem`, a special type of [item] that places a block when used. This also means that things like the creative tab or the max stack size are handled by the corresponding `BlockItem`.

A `BlockItem` must be registered separately from the block. This is because a block does not necessarily need an item, for example if it is not meant to be collected (as is the case with fire, for example).
IchHabeHunger54 marked this conversation as resolved.
Show resolved Hide resolved
:::

### Advanced Blocks
### More Functionality

Of course, the above only allows for extremely basic blocks. If you want to add functionality, like player interaction, a custom class is required. However, the `Block` class has many methods and unfortunately not every single one can be documented here. See the rest of the pages in this section for things you can do with blocks.
Directly using `Block` only allows for very basic blocks. If you want to add functionality, like player interaction or a non-1x1x1 hitbox, a custom class that extends `Block` is required. The `Block` class has many methods that can be overridden to do different things; these are documented in the [`Block` reference][blockreference]. See also the Using blocks section below for some of the most common use cases for blocks.
IchHabeHunger54 marked this conversation as resolved.
Show resolved Hide resolved

Registering a Block
-------------------
If you want to make a block that has different variants (think a slab that has a bottom, top, and double variant), you should use [blockstates]. And finally, if you want a block that stores additional data (think a chest that stores its inventory), a [block entity][blockentities] should be used. The rule of thumb here is that if you have a finite amount of states, use blockstates, and if you have an infinite or near-infinite amount of states, use a block entity.
IchHabeHunger54 marked this conversation as resolved.
Show resolved Hide resolved

Blocks must be [registered][registering] to function.
### Resources

:::caution
A block in the level and a "block" in an inventory are very different things. A block in the level is represented by an `BlockState`, and its behavior defined by an instance of `Block`. Meanwhile, an item in an inventory is an `ItemStack`, controlled by an `Item`. As a bridge between the different worlds of `Block` and `Item`, there exists the class `BlockItem`. `BlockItem` is a subclass of `Item` that has a field `block` that holds a reference to the `Block` it represents. `BlockItem` defines some of the behavior of a "block" as an item, like how a right click places the block. It's possible to have a `Block` without an `BlockItem`. (E.g. `minecraft:water` exists a block, but not an item. It is therefore impossible to hold it in an inventory as one.)
If you register your block and place it in the world, you will find it to be missing things like a texture. This is because textures, among others, are handled by Minecraft's resource system.

When a block is registered, *only* a block is registered. The block does not automatically have an `BlockItem`. To create a basic `BlockItem` for a block, one should set the registry name of the `BlockItem` to that of its `Block`. Custom subclasses of `BlockItem` may be used as well. Once an `BlockItem` has been registered for a block, `Block#asItem` can be used to retrieve it. `Block#asItem` will return `Items#AIR` if there is no `BlockItem` for the `Block`, so if you are not certain that there is an `BlockItem` for the `Block` you are using, check for if `Block#asItem` returns `Items#AIR`.
:::
To apply a simple texture to a block, you must add a blockstate JSON, a model JSON, and a texture PNG. See the section on [resources] for more information.

#### Optionally Registering Blocks
Using Blocks
------------

In the past there have been several mods that have allowed users to disable blocks/items in a configuration file. However, you shouldn't do this. There is no limit on the amount of blocks that can be register, so register all blocks in your mod! If you want a block to be disabled through a configuration file, you should disable the crafting recipe. If you would like to disable the block in the creative tab, use a `FeatureFlag` when building the contents within [`BuildCreativeModeTabContentsEvent`][creativetabs].
Blocks are very rarely directly used to do things. In fact, probably two of the most common operations in all of Minecraft - getting the block at a position, and setting a block at a position - use blockstates, not blocks. The general design approach is to have the block define behavior, but have the behavior actually run through blockstates. Due to this, `BlockState`s are often passed to methods of `Block` as a parameter. For more information on how blockstates are used, and on how to get one from a block, see [Using Blockstates][usingblockstates].

Further Reading
---------------
In several situations, multiple methods of `Block` are used at different times. The following subsections list the most common block-related pipelines. Unless specified otherwise, all methods are called on both logical sides.

For information about block properties, such as those used for vanilla blocks like fences, walls, and many more, see the section on [blockstates].
### Placing a Block

[sounds]: ../gameeffects/sounds.md
[creativetabs]: ../items/index.md#creative-tabs
[registering]: ../concepts/registries.md#methods-for-registering
Block placement logic is called from `BlockItem#useOn` (or some subclass's implementation thereof, such as in `PlaceOnWaterBlockItem`, which is used for lily pads). For more information on how the game gets there, see the [Interaction Pipeline][interactionpipeline].
IchHabeHunger54 marked this conversation as resolved.
Show resolved Hide resolved

- Several prerequisites are checked, for example that you are not in spectator mode, that all required feature flags for the block are enabled or that the block in question is not outside the world border. If at least one of these checks fails, the pipeline ends.
IchHabeHunger54 marked this conversation as resolved.
Show resolved Hide resolved
- `Block#canBeReplaced` is called for the block currently at the position where the block is tried to place. If it returns `false`, the pipeline ends.
IchHabeHunger54 marked this conversation as resolved.
Show resolved Hide resolved
- `Block#getStateForPlacement` is called.
IchHabeHunger54 marked this conversation as resolved.
Show resolved Hide resolved
- `Block#canSurvive` is called with the blockstate obtained in the previous step. If it returns `false`, the pipeline ends.
- The blockstate is set into the level via a `Level#setBlock` call.
- In that `Level#setBlock` call, `Block#onPlace` is called.
- `Block#setPlacedBy` is called.

### Breaking a Block

Breaking a block is a bit more complex, as it requires time. The process can be roughly divided into four stages: "initiating", "mining", "actually breaking" and "finishing".

- When the left mouse button is clicked, the "initiating" stage is entered.
- Now, the left mouse button needs to be held down, entering the "mining" stage. **This stage's methods are called every tick.**
- If the "continuing" stage is not interrupted (by releasing the left mouse button) and the block is broken, the "actually breaking" stage is entered.
- Under all circumstances, no matter if the block was actually broken or not, the "finishing" stage is entered.

Or for those who prefer pseudocode:

```java
leftClick();
initiatingStage();
while (leftClickIsBeingHeld()) {
miningStage();
if (blockIsBroken()) {
actuallyBreakingStage();
break;
}
}
finishingStage();
```

The following subsections further break down these stages into actual method calls.

#### The "Initiating" Stage

- `InputEvent.InteractionKeyMappingTriggered` is fired with the left mouse button and the main hand. If the event is canceled, the pipeline ends.
- Several prerequisites are checked, for example that you are not in spectator mode, that all required feature flags for the `ItemStack` in your main hand are enabled or that the block in question is not outside the world border. If at least one of these checks fails, the pipeline ends.
- `PlayerInteractEvent.LeftClickBlock` is fired. If the event is canceled, the pipeline ends.
IchHabeHunger54 marked this conversation as resolved.
Show resolved Hide resolved
- `Block#attack` is called.

#### The "Mining" Stage

- `PlayerInteractEvent.LeftClickBlock` is fired. If the event is canceled, the pipeline moves to the "finishing" stage.
- `Block#getDestroyProgress` is called and added to the internal destroy progress counter.
- `Block#getDestroyProgress` returns a float value between 0 and 1, representing how much the destroy progress counter should be increased every tick.
- The progress overlay (cracking texture) is updated accordingly.
- If the destroy progress is greater than 1.0 (i.e. completed, i.e. the block should be broken), the "mining" stage is exited and the "actually breaking" stage is entered.

#### The "Actually Breaking" Stage

- `Item#onBlockStartBreak` is called. If it returns `true`, the pipeline moves to the "finishing" stage.
- Server-only: `IForgeBlock#canHarvestBlock` is called.
IchHabeHunger54 marked this conversation as resolved.
Show resolved Hide resolved
IchHabeHunger54 marked this conversation as resolved.
Show resolved Hide resolved
- `Block#playerWillDestroy` is called.
- The blockstate is removed from the level via a `Level#setBlock` call with `Blocks.AIR.defaultBlockState()` as the blockstate parameter.
- In that `Level#setBlock` call, `Block#onRemove` is called.
- `Block#destroy` is called.
- Server-only: If the previous call to `IForgeBlock#canHarvestBlock` returned `true`, `Block#playerDestroy` is called.
IchHabeHunger54 marked this conversation as resolved.
Show resolved Hide resolved
- Server-only: `Block#popExperience` is called.

#### The "Finishing" Stage

- The internal destroy progress counter is reset.

### Ticking and Random Ticking

Like many other things, blocks are ticked (updated) every tick, which is 1 / 20 of a second, or 50 milliseconds.

There are two methods for ticking: `Block#tick` and `Block#animateTick`. The difference is that `Block#tick` is called only on the server, whereas `Block#animateTick` is called only on the client. For example, redstone updates are processed in `Block#tick`, while the fire particles of torches are spawned in `Block#animateTick`. Override either of them to add your server/client ticking behavior.
IchHabeHunger54 marked this conversation as resolved.
Show resolved Hide resolved

Alongside ticking, there is a separate "weather tick", if you will. The method `Block#handlePrecipitation` is called only on the server, only when it is raining in some form, with a 1 in 16 chance. This is used for example by cauldrons that fill during rain or snowfall.

And finally, there is the random tick system. Random ticks must be enabled through the `BlockBehaviour.Properties` of the block by calling the `BlockBehaviour.Properties#randomTicks()` method. This enables the block to be part of the random ticking mechanic.

Random ticks occur every tick for a set amount of blocks in a chunk. That set amount is defined through the `randomTickSpeed` gamerule. With its default value of 3, every tick, 3 random blocks from the chunk are chosen. If these blocks have random ticking enabled, then their respective `Block#randomTick` methods are called.

Random ticking is used by a wide range of mechanics in Minecraft, such as plant growth, ice and snow melting, or copper oxidizing.

[blockentities]: ../blockentities/index.md
[blockreference]: reference.md#block
[blockstates]: states.md
[events]: ../concepts/events.md
[interactionpipeline]: ../items/interactionpipeline.md
[item]: ../items/index.md
[propertiesreference]: reference.md#blockbehaviourproperties
[registration]: ../concepts/registries.md#methods-for-registering
[resources]: ../resources/client/index.md
[sounds]: ../gameeffects/sounds.md
[usingblockstates]: states.md#using-blockstates
Loading