From 346c3a625cab89b311b73f46b90628b918ab4b75 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 <52419336+IchHabeHunger54@users.noreply.github.com> Date: Sat, 25 Nov 2023 14:51:02 +0100 Subject: [PATCH] Explain blocks and blockstates in more depth (#21) --- docs/blocks/index.md | 205 ++++++++++++++++++++++++++---- docs/blocks/states.md | 169 ++++++++++++++++-------- docs/items/interactionpipeline.md | 71 +++++++++++ 3 files changed, 361 insertions(+), 84 deletions(-) create mode 100644 docs/items/interactionpipeline.md diff --git a/docs/blocks/index.md b/docs/blocks/index.md index f083f104d..09567c52f 100644 --- a/docs/blocks/index.md +++ b/docs/blocks/index.md @@ -1,51 +1,200 @@ 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 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. -Creating a Block ----------------- +One Block to Rule Them All +-------------------------- -### Basic Blocks +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 +//BLOCKS is a DeferredRegister.Blocks +public static final DeferredBlock MY_BLOCK = BLOCKS.register("my_block", () -> new Block(...)); +``` + +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 `my_block`, the code for that would look something like this: + +```java +level.getBlockState(position) // returns the blockstate placed in the given level (world) at the given position + //highlight-next-line + .is(MyBlockRegistrationClass.MY_BLOCK.get()); +``` + +This approach also has the convenient effect that `block1 == block2` works and can be used instead of Java's `equals` method (using `equals` still works, of course, but is pointless since it compares by reference anyway). -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: +:::danger +Do not call `new Block()` outside registration! As soon as you do that, things can and will break: -- `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. +- Blocks must be created while registries are unfrozen. NeoForge unfreezes registries for you and freezes them later, so registration is your time window to create blocks. +- If you try to create and/or register a block when registries are frozen again, the game will crash and report a `null` block, which can be very confusing. +- If you still manage to have a dangling block instance, the game will not recognize it while syncing and saving, and replace it with air. +::: + +Creating Blocks +--------------- + +### Basic Blocks -All these methods are *chainable* which means you can call them in series. See the `Blocks` class for examples of this. +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. + - Stone has a destroy time of 1.5, dirt has 0.5, obsidian has 50, and bedrock has -1 (unbreakable). +- `explosionResistance` - Determines the explosion resistance of the block. + - Stone has an explosion resistance of 6.0, dirt has 0.5, obsidian has 1,200, and bedrock has 3,600,000. +- `sound` - Sets the sound the block makes when it is punched, broken, or placed. + - The default value is `SoundType.STONE`. See the [Sounds page][sounds] for more details. +- `lightLevel` - Sets the light emission of the block. Accepts a function with a `BlockState` parameter that returns a value between 0 and 15. + - For example, glowstone uses `state -> 15`, and torches use `state -> 14`. +- `friction` - Sets the friction (slipperiness) of the block. + - Default value is 0.6. Ice uses 0.98. + +So for example, a simple implementation would look something like this: + +```java + public static final DeferredBlock MY_BETTER_BLOCK = BLOCKS.register( + "my_better_block", + () -> new Block(BlockBehaviour.Properties.of() + //highlight-start + .destroyTime(2.0f) + .explosionResistance(10.0f) + .sound(SoundType.GRAVEL) + .lightLevel(state -> 7) + //highlight-end + )); +``` + +For further documentation, see the source code of `BlockBehaviour.Properties`. 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). ::: -### 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 different hitbox, a custom class that extends `Block` is required. The `Block` class has many methods that can be overridden to do different things; see the classes `Block`, `BlockBehaviour` and `IBlockExtension` for more information. See also the [Using blocks][usingblocks] section below for some of the most common use cases for blocks. -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 and reasonably small amount of states (= a few hundred states at most), use blockstates, and if you have an infinite or near-infinite amount of states, use a block entity. -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 and should return the same result on both 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]. In practice, this means that as soon as a `BlockItem` is right-clicked (for example a cobblestone item), this behavior is called. + +- 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 target position is not outside the world border. If at least one of these checks fails, the pipeline ends. +- `Block#canBeReplaced` is called for the block currently at the position where the block is attempted to be placed. If it returns `false`, the pipeline ends. Prominent cases that return `true` here are tall grass or snow layers. +- `Block#getStateForPlacement` is called. This is where, depending on the context (which includes information like the position, the rotation and the side the block is placed on), different block states can be returned. This is useful for example for blocks that can be placed in different directions. +- `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 three stages: "initiating", "mining" and "actually breaking". + +- 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. + +Or for those who prefer pseudocode: + +```java +leftClick(); +initiatingStage(); +while (leftClickIsBeingHeld()) { + miningStage(); + if (blockIsBroken()) { + actuallyBreakingStage(); + break; + } +} +``` + +The following subsections further break down these stages into actual method calls. + +#### The "Initiating" Stage + +- Client-only: `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. + - Note that when the event is canceled on the client, no packets are sent to the server and thus no logic runs on the server. + - However, canceling this event on the server will still cause client code to run, which can lead to desyncs! +- `Block#attack` is called. + +#### The "Mining" Stage + +- `PlayerInteractEvent.LeftClickBlock` is fired. If the event is canceled, the pipeline moves to the "finishing" stage. + - Note that when the event is canceled on the client, no packets are sent to the server and thus no logic runs on the server. + - However, canceling this event on the server will still cause client code to run, which can lead to desyncs! +- `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` (determining that the block should not be broken), the pipeline moves to the "finishing" stage. +- Server-only: `IBlockExtension#canHarvestBlock` is called. This determines whether the block can be harvested, i.e. broken with drops. +- `Block#onDestroyedByPlayer` is called. If it returns `false`, the pipeline moves to the "finishing" stage. In that `Block#onDestroyedByPlayer` call: + - `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 `IBlockExtension#canHarvestBlock` returned `true`, `Block#playerDestroy` is called. +- Server-only: `IBlockExtension#getExpDrop` is called. +- Server-only: `Block#popExperience` is called with the result of the previous `IBlockExtension#getExpDrop` call, if that call returned a value greater than 0. + +### Ticking + +Ticking is a mechanism that updates (ticks) parts of the game every 1 / 20 seconds, or 50 milliseconds ("one tick"). Blocks provide different ticking methods that are called in different ways. + +#### Server Ticking and Tick Scheduling + +`Block#tick` is called in two occasions: either through default [random ticking][randomtick] (see below), or through scheduled ticks. Scheduled ticks can be created through `Level#scheduleTick(BlockPos, Block, int)`, where the `int` denotes a delay. This is used in various places by vanilla, for example, the tilting mechanism of big dripleaves heavily relies on this system. Other prominent users are various redstone components. + +#### Client Ticking + +`Block#animateTick` is called exclusively on the client, every frame. This is where client-only behavior, for example the torch particle spawning, happens. + +#### Weather Ticking + +Weather ticking is handled by `Block#handlePrecipitation` and runs independent of regular ticking. It 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. + +#### Random Ticking + +The random tick system runs independent of regular ticking. 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. + +`Block#randomTick` by default calls `Block#tick`, which is what should normally be overridden. `Block#randomTick` should only be overridden if you specifically want different behavior for random ticking and regular (scheduled) ticking. + +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 [blockstates]: states.md +[events]: ../concepts/events.md +[interactionpipeline]: ../items/interactionpipeline.md +[item]: ../items/index.md +[randomtick]: #random-ticking +[registration]: ../concepts/registries.md#methods-for-registering +[resources]: ../resources/client/index.md +[sounds]: ../gameeffects/sounds.md +[usingblocks]: #using-blocks +[usingblockstates]: states.md#using-blockstates diff --git a/docs/blocks/states.md b/docs/blocks/states.md index f18bc385e..c0829286e 100644 --- a/docs/blocks/states.md +++ b/docs/blocks/states.md @@ -1,57 +1,54 @@ -Block States -============ +Blockstates +=========== -Legacy Behavior ---------------------------------------- +Often, you will find yourself in a situation where you want different states of a block. For example, a wheat crop has eight growth stages, and making a separate block for each stage feels wrong. Or you have a slab or slab-like block - one bottom state, one top state, and one state that has both. -In Minecraft 1.7 and previous versions, blocks which need to store placement or state data that did not have BlockEntities used **metadata**. Metadata was an extra number stored with the block, allowing different rotations, facings, or even completely separate behaviors within a block. +This is where blockstates come into play. Blockstates are an easy way to represent the different states a block can have, like a growth stage or a slab placement type. -However, the metadata system was confusing and limited, since it was stored as only a number alongside the block ID, and had no meaning except what was commented in the code. For example, to implement a block that can face a direction and be on either the upper or lower half of a block space (such as a stair): +Blockstate Properties +--------------------- -```java -switch (meta) { - case 0: { ... } // south and on the lower half of the block - case 1: { ... } // south on the upper side of the block - case 2: { ... } // north and on the lower half of the block - case 3: { ... } // north and on the upper half of the block - // ... etc. ... -} -``` +Blockstates use a system of properties. A block can have multiple properties of multiple types. For example, an end portal frame has two properties: whether it has an eye (`eye`, 2 options) and which direction it is placed in (`facing`, 4 options). So in total, the end portal frame has 8 (2 * 4) different blockstates: -Because the numbers carry no meaning by themselves, no one could know what they represent unless they had access to the source code and comments. +``` +minecraft:end_portal_frame[facing=north,eye=false] +minecraft:end_portal_frame[facing=east,eye=false] +minecraft:end_portal_frame[facing=south,eye=false] +minecraft:end_portal_frame[facing=west,eye=false] +minecraft:end_portal_frame[facing=north,eye=true] +minecraft:end_portal_frame[facing=east,eye=true] +minecraft:end_portal_frame[facing=south,eye=true] +minecraft:end_portal_frame[facing=west,eye=true] +``` -Introduction of States ---------------------------------------- +The notation `blockid[property1=value1,property2=value,...]` is the standardized way of representing a blockstate in text form, and is used in some locations in vanilla, for example in commands. -In Minecraft 1.8 and above, the metadata system, along with the block ID system, was deprecated and eventually replaced with the **block state system**. The block state system abstracts out the details of the block's properties from the other behaviors of the block. +If your block does not have any blockstate properties defined, it still has exactly one blockstate - that is the one without any properties, since there are no properties to specify. This can be denoted as `minecraft:oak_planks[]` or simply `minecraft:oak_planks`. -Each *property* of a block is described by an instance of `Property`. Examples of block properties include instruments (`EnumProperty`), facing (`DirectionProperty`), poweredness (`Property`), etc. Each property has the value of the type `T` parametrized by `Property`. +As with blocks, every `BlockState` exists exactly once in memory. This means that `==` can and should be used to compare `BlockState`s. `BlockState` is also a final class, meaning it cannot be extended. **Any functionality goes in the corresponding [Block][block] class!** -A unique pair can be constructed from the `Block` and a map of the `Property` to their associated values. This unique pair is called a `BlockState`. +When to Use Blockstates +----------------------- -The previous system of meaningless metadata values were replaced by a system of block properties, which are easier to interpret and deal with. Previously, a stone button which is facing east and is powered or held down is represented by "`minecraft:stone_button` with metadata `9`. Now, this is represented by "`minecraft:stone_button[facing=east,powered=true]`". +### Blockstates vs. Separate Blocks -Proper Usage of Block States ---------------------------------------- +A good rule of thumb is: **if it has a different name, it should be a separate block**. An example is making chair blocks: the direction of the chair should be a property, while the different types of wood should be separated into different blocks. So you'd have one chair block for each wood type, and each chair block has four blockstates (one for each direction). -The `BlockState` system is a flexible and powerful system, but it also has limitations. `BlockState`s are immutable, and all combinations of their properties are generated on startup of the game. This means that having a `BlockState` with many properties and possible values will slow down the loading of the game, and befuddle anyone trying to make sense of your block logic. +### Blockstates vs. [Block Entities][blockentity] -Not all blocks and situations require the usage of `BlockState`; only the most basic properties of a block should be put into a `BlockState`, and any other situation is better off with having a `BlockEntity` or being a separate `Block`. Always consider if you actually need to use blockstates for your purposes. +Here, the rule of thumb is: **if you have a finite amount of states, use a blockstate, if you have an infinite or near-infinite amount of states, use a block entity.** Block entities can store arbitrary amounts of data, but are slower than blockstates. -:::note -A good rule of thumb is: **if it has a different name, it should be a separate block**. -::: +Blockstates and block entities can be used in conjunction with one another. For example, the chest uses blockstate properties for things like the direction, whether it is waterlogged or not, or becoming a double chest, while storing the inventory, whether it is currently open or not, or interacting with hoppers is handled by a block entity. -An example is making chair blocks: the *direction* of the chair should be a *property*, while the different *types of wood* should be separated into different blocks. -An "Oak Chair" facing east (`oak_chair[facing=east]`) is different from a "Spruce Chair" facing west (`spruce_chair[facing=west]`). +There is no definitive answer to the question "How many states are too much for a blockstate?", but we recommend that if you need more than 8-9 bits of data (i.e. more than a few hundred states), you should use a block entity instead. -Implementing Block States ---------------------------------------- +Implementing Blockstates +------------------------ -In your Block class, create or reference `static final` `Property` objects for every property that your Block has. You are free to make your own `Property` implementations, but the means to do that are not covered in this article. The vanilla code provides several convenience implementations: +To implement a blockstate property, in your block class, create or reference a `public static final Property` constant. While you are free to make your own `Property` implementations, the vanilla code provides several convenience implementations that should cover most use cases: * `IntegerProperty` - * Implements `Property`. Defines a property that holds an integer value. + * Implements `Property`. Defines a property that holds an integer value. Note that negative values are not supported. * Created by calling `IntegerProperty#create(String propertyName, int minimum, int maximum)`. * `BooleanProperty` * Implements `Property`. Defines a property that holds a `true` or `false` value. @@ -59,38 +56,98 @@ In your Block class, create or reference `static final` `Property` objects fo * `EnumProperty>` * Implements `Property`. Defines a property that can take on the values of an Enum class. * Created by calling `EnumProperty#create(String propertyName, Class enumClass)`. - * It is also possible to use only a subset of the Enum values (e.g. 4 out of 16 `DyeColor`s). See the overloads of `EnumProperty#create`. + * It is also possible to use only a subset of the Enum values (e.g. 4 out of 16 `DyeColor`s), see the overloads of `EnumProperty#create`. * `DirectionProperty` - * This is a convenience implementation of `EnumProperty` - * Several convenience predicates are also provided. For example, to get a property that represents the cardinal directions, call `DirectionProperty.create("", Direction.Plane.HORIZONTAL)`; to get the X directions, `DirectionProperty.create("", Direction.Axis.X)`. + * Extends `EnumProperty`. Defines a property that can take on a `Direction`. + * Created by calling `DirectionProperty#create(String propertyName)`. + * Several convenience predicates are provided. For example, to get a property that represents the cardinal directions, call `DirectionProperty.create("", Direction.Plane.HORIZONTAL)`; to get the X directions, `DirectionProperty.create("", Direction.Axis.X)`. The class `BlockStateProperties` contains shared vanilla properties which should be used or referenced whenever possible, in place of creating your own properties. -When you have your desired `Property<>` objects, override `Block#createBlockStateDefinition(StateDefinition$Builder)` in your Block class. In that method, call `StateDefinition$Builder#add(...);` with the parameters as every `Property` you wish the block to have. +Once you have your property constant, override `Block#createBlockStateDefinition(StateDefinition$Builder)` in your block class. In that method, call `StateDefinition.Builder#add(YOUR_PROPERTY);`. `StateDefinition.Builder#add` has a vararg parameter, so if you have multiple properties, you can add them all in one go. + +Every block will also have a default state. If nothing else is specified, the default state uses the default value of every property. You can change the default state by calling the `Block#registerDefaultState(BlockState)` method from your constructor. -Every block will also have a "default" state that is automatically chosen for you. You can change this "default" state by calling the `Block#registerDefaultState(BlockState)` method from your constructor. When your block is placed it will become this "default" state. An example from `DoorBlock`: +If you wish to change which `BlockState` is used when placing your block, override `Block#getStateForPlacement(BlockPlaceContext)`. This can be used to, for example, set the direction of your block depending on where the player is standing or looking when they place it. + +To further illustrate this, this is what the relevant bits of the `EndPortalFrameBlock` class look like: ```java -this.registerDefaultState( - this.stateDefinition.any() - .setValue(FACING, Direction.NORTH) - .setValue(OPEN, false) - .setValue(HINGE, DoorHingeSide.LEFT) - .setValue(POWERED, false) - .setValue(HALF, DoubleBlockHalf.LOWER) -); +public class EndPortalFrameBlock extends Block { + // Note: It is possible to directly use the values in BlockStateProperties instead of referencing them here again. + // However, for the sake of simplicity and readability, it is recommended to add constants like this. + public static final DirectionProperty FACING = BlockStateProperties.FACING; + public static final BooleanProperty EYE = BlockStateProperties.EYE; + + public EndPortalFrameBlock(BlockBehaviour.Properties pProperties) { + super(pProperties); + // stateDefinition.any() returns a random BlockState from an internal set, + // we don't care because we're setting all values ourselves anyway + registerDefaultState(stateDefinition.any() + .setValue(FACING, Direction.NORTH) + .setValue(EYE, false) + ); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder pBuilder) { + // this is where the properties are actually added to the state + pBuilder.add(FACING, EYE); + } + + @Override + @Nullable + public BlockState getStateForPlacement(BlockPlaceContext pContext) { + // code that determines which state will be used when + // placing down this block, depending on the BlockPlaceContext + } +} ``` -If you wish to change what `BlockState` is used when placing your block, you can overwrite `Block#getStateForPlacement(BlockPlaceContext)`. This can be used to, for example, set the direction of your block depending on where the player is standing when they place it. +Using Blockstates +----------------- -Because `BlockState`s are immutable, and all combinations of their properties are generated on startup of the game, calling `BlockState#setValue(Property, T)` will simply go to the `Block`'s `StateHolder` and request the `BlockState` with the set of values you want. +To go from `Block` to `BlockState`, call `Block#defaultBlockState()`. The default blockstate can be changed through `Block#registerDefaultState`, as described above. -Because all possible `BlockState`s are generated at startup, you are free and encouraged to use the reference equality operator (`==`) to check if two `BlockState`s are equal. +You can get the value of a property by calling `BlockState#getValue(Property)`, passing it the property you want to get the value of. Reusing our end portal frame example, this would look something like this: -Using `BlockState`'s ---------------------- +```java +// EndPortalFrameBlock.FACING is a DirectionProperty and thus can be used to obtain a Direction from the BlockState +Direction direction = endPortalFrameBlockState.getValue(EndPortalFrameBlock.FACING); +``` + +If you want to get a `BlockState` with a different set of values, simply call `BlockState#setValue(Property, T)` on an existing block state with the property and its value. With our lever, this goes something like this: + +```java +endPortalFrameBlockState = endPortalFrameBlockState.setValue(EndPortalFrameBlock.FACING, Direction.SOUTH); +``` + +:::note +`BlockState`s are immutable. This means that when you call `#setValue(Property, T)`, you are not actually modifying the blockstate. Instead, a lookup is performed internally, and you are given the blockstate object you requested, which is the one and only object that exists with these exact property values. This also means that just calling `state#setValue` without saving it into a variable (for example back into `state`) does nothing. +::: + +To get a `BlockState` from the level, use `Level#getBlockState(BlockPos)`. + +### `Level#setBlock` + +To set a `BlockState` in the level, use `Level#setBlock(BlockPos, BlockState, int)`. + +The `int` parameter deserves some extra explanation, as its meaning is not immediately obvious. It denotes what is known as update flags. + +To help setting the update flags correctly, there are a number of `int` constants in `Block`, prefixed with `UPDATE_`. These constants can be bitwise-ORed together (for example `Block.UPDATE_NEIGHBORS | Block.UPDATE_CLIENTS`) if you wish to combine them. + +- `Block.UPDATE_NEIGHBORS` sends an update to the neighboring blocks. More specifically, it calls `Block#neighborChanged`, which calls a number of methods, most of which are redstone-related in some way. +- `Block.UPDATE_CLIENTS` syncs the block update to the client. +- `Block.UPDATE_INVISIBLE` explicitly does not update on the client. This also overrules `Block.UPDATE_CLIENTS`, causing the update to not be synced. The block is always updated on the server. +- `Block.UPDATE_IMMEDIATE` forces a re-render on the client's main thread. +- `Block.UPDATE_KNOWN_SHAPE` stops neighbor update recursion. +- `Block.UPDATE_SUPPRESS_DROPS` disables block drops for the old block at that position. +- `Block.UPDATE_MOVE_BY_PISTON` is only used by piston code to signal that the block was moved by a piston. This is mainly responsible for delaying light engine updates. +- `Block.UPDATE_ALL` is an alias for `Block.UPDATE_NEIGHBORS | Block.UPDATE_CLIENTS`. +- `Block.UPDATE_ALL_IMMEDIATE` is an alias for `Block.UPDATE_NEIGHBORS | Block.UPDATE_CLIENTS | Block.UPDATE_IMMEDIATE`. +- `Block.NONE` is an alias for `Block.UPDATE_INVISIBLE`. -You can get the value of a property by calling `BlockState#getValue(Property)`, passing it the property you want to get the value of. -If you want to get a `BlockState` with a different set of values, simply call `BlockState#setValue(Property, T)` with the property and its value. +There is also a convenience method `Level#setBlockAndUpdate(BlockPos pos, BlockState state)` that calls `setBlock(pos, state, Block.UPDATE_ALL)` internally. -You can get and place `BlockState`'s in the level using `Level#setBlockAndUpdate(BlockPos, BlockState)` and `Level#getBlockState(BlockPos)`. If you are placing a `Block`, call `Block#defaultBlockState()` to get the "default" state, and use subsequent calls to `BlockState#setValue(Property, T)` as stated above to achieve the desired state. +[block]: index.md +[blockentity]: ../blockentities/index.md diff --git a/docs/items/interactionpipeline.md b/docs/items/interactionpipeline.md new file mode 100644 index 000000000..8f05f0aeb --- /dev/null +++ b/docs/items/interactionpipeline.md @@ -0,0 +1,71 @@ +The Interaction Pipeline +======================== + +This page aims to make the fairly complex and confusing process of things being right-clicked by the player more understandable, as well as clarifying what result to use where and why. + +What Happens When I Right-Click? +-------------------------------- + +When you right-click anywhere in the world, a number of things happen, depending on what you are currently looking at and what `ItemStack`s are in your hands. A number of methods returning one of two result types (see below) are called. Most of these methods cancel the pipeline if an explicit success or an explicit failure is returned. For the sake of readability, this "explicit success or explicit failure" will be called a "definitive result" from now on. + +- `InputEvent.InteractionKeyMappingTriggered` is fired with the right mouse button and the main hand. If the event is canceled, the pipeline ends. +- Several circumstances are checked, for example that you are not in spectator mode or that all required feature flags for the `ItemStack` in your main hand are enabled. If at least one of these checks fails, the pipeline ends. +- Depending on what you are looking at, different things happen: + - If you are looking at an entity that is within your reach and not outside the world border: + - `PlayerInteractEvent.EntityInteractSpecific` is fired. If the event is canceled, the pipeline ends. + - `Entity#interactAt` will be called **on the entity you are looking at**. If it returns a definitive result, the pipeline ends. + - If you want to add behavior for your own entity, override this method. If you want to add behavior for a vanilla entity, use the event. + - If the entity opens an interface (for example a villager trading GUI or a chest minecart GUI), the pipeline ends. + - `PlayerInteractEvent.EntityInteract` is fired. If the event is canceled, the pipeline ends. + - `Entity#interact` is called **on the entity you are looking at**. If it returns a definitive result, the pipeline ends. + - If you want to add behavior for your own entity, override this method. If you want to add behavior for a vanilla entity, use the event. + - For `Mob`s, the override of `Entity#interact` handles things like leashing and spawning babies when the `ItemStack` in your main hand is a spawn egg, and then defers mob-specific handling to `Mob#mobInteract`. The rules for results for `Entity#interact` apply here as well. + - If the entity you are looking at is a `LivingEntity`, `Item#interactLivingEntity` is called on the `ItemStack` in your main hand. If it returns a definitive result, the pipeline ends. + - If you are looking at a block that is within your reach and not outside the world border: + - `PlayerInteractEvent.RightClickBlock` is fired. If the event is canceled, the pipeline ends. You may also specifically deny only block or item usage in this event. + - `IItemExtension#onItemUseFirst` is called. If it returns a definitive result, the pipeline ends. + - If the player is not sneaking and the event does not deny block usage, `Block#use` is called. If it returns a definitive result, the pipeline ends. + - If the event does not deny item usage, `Item#useOn` is called. If it returns a definitive result, the pipeline ends. +- `Item#use` is called. If it returns a definitive result, the pipeline ends. +- The above process runs a second time, this time with the off hand instead of the main hand. + +Result Types +------------ + +There are two different types of results: `InteractionResult`s and `InteractionResultHolder`s. `InteractionResult` is used most of the time, only `Item#use` uses `InteractionResultHolder`. + +`InteractionResult` is an enum consisting of five values: `SUCCESS`, `CONSUME`, `CONSUME_PARTIAL`, `PASS` and `FAIL`. Additionally, the method `InteractionResult#sidedSuccess` is available, which returns `SUCCESS` on the server and `CONSUME` on the client. + +`InteractionResultHolder` is a wrapper around `InteractionResult` that adds additional context for `T`. `T` can be anything, but in 99.99 percent of cases, it is an `ItemStack`. `InteractionResultHolder` provides wrapper methods for the enum values (`#success`, `#consume`, `#pass` and `#fail`), as well as `#sidedSuccess`, which calls `#success` on the server and `#consume` on the client. + +Generally, the different values mean the following: + +- `InteractionResult#sidedSuccess` (or `InteractionResultHolder#sidedSuccess` where needed) should be used if the operation should be considered successful, and you want the arm to swing. The pipeline will end. +- `InteractionResult.SUCCESS` (or `InteractionResultHolder#success` where needed) should be used if the operation should be considered successful, and you want the arm to swing, but only on one side. Only use this if you want to return a different value on the other logical side for whatever reason. The pipeline will end. +- `InteractionResult.CONSUME` (or `InteractionResultHolder#consume` where needed) should be used if the operation should be considered successful, but you do not want the arm to swing. The pipeline will end. +- `InteractionResult.CONSUME_PARTIAL` is mostly identical to `InteractionResult.CONSUME`, the only difference is in its usage in [`Item#useOn`][itemuseon]. +- `InteractionResult.FAIL` (or `InteractionResultHolder#fail` where needed) should be used if the item functionality should be considered failed and no further interaction should be performed. The pipeline will end. This can be used everywhere, but it should be used with care outside of `Item#useOn` and `Item#use`. In many cases, using `InteractionResult.PASS` makes more sense. +- `InteractionResult.PASS` (or `InteractionResultHolder#pass` where needed) should be used if the operation should be considered neither successful nor failed. The pipeline will continue. This is the default behavior (unless otherwise specified). + +Some methods have special behavior or requirements, which are explained in the below chapters. + +`IItemExtension#onItemUseFirst` +--------------------------- + +`InteractionResult#sidedSuccess` and `InteractionResult.CONSUME` don't have an effect here. Only `InteractionResult.SUCCESS`, `InteractionResult.FAIL` or `InteractionResult.PASS` should be used here. + +`Item#useOn` +------------ + +If you want the operation to be considered successful, but you do not want the arm to swing or an `ITEM_USED` stat point to be awarded, use `InteractionResult.CONSUME_PARTIAL`. + +`Item#use` +---------- + +This is the only instance where the return type is `InteractionResultHolder`. The resulting `ItemStack` in the `InteractionResultHolder` replaces the `ItemStack` the usage was initiated with, if it has changed. + +The default implementation of `Item#use` returns `InteractionResultHolder#consume` when the item is edible and the player can eat the item (because they are hungry, or because the item is always edible), `InteractionResultHolder#fail` when the item is edible but the player cannot eat the item, and `InteractionResultHolder#pass` if the item is not edible. + +Returning `InteractionResultHolder#fail` here while considering the main hand will prevent offhand behavior from running. If you want offhand behavior to run (which you usually want), return `InteractionResultHolder#pass` instead. + +[itemuseon]: #itemuseon