diff --git a/docs/blockentities/container.md b/docs/blockentities/container.md index 4b07dda7..4caf439d 100644 --- a/docs/blockentities/container.md +++ b/docs/blockentities/container.md @@ -264,7 +264,7 @@ Be aware that menus that directly interface with `Container`s must `#copy()` the ## `Container`s on `Entity`s -`Container`s on `Entity`s are finicky: whether an entity has a container or not cannot be universally determined. It all depends on what entity you are handling, and as such can require a lot of special-casing. +`Container`s on [`Entity`s][entity] are finicky: whether an entity has a container or not cannot be universally determined. It all depends on what entity you are handling, and as such can require a lot of special-casing. If you are creating an entity yourself, there is nothing stopping you from implementing `Container` on it directly, though be aware that you will not be able to use superclasses such as `SimpleContainer` (since `Entity` is the superclass). @@ -307,6 +307,7 @@ When iterating over the inventory contents, it is recommended to iterate over `i [blockentity]: index.md [component]: ../resources/client/i18n.md#components [datacomponent]: ../items/datacomponents.md +[entity]: ../entities/index.md [item]: ../items/index.md [itemstack]: ../items/index.md#itemstacks [menu]: ../gui/menus.md diff --git a/docs/blockentities/index.md b/docs/blockentities/index.md index d8c0c2ca..e30f07a0 100644 --- a/docs/blockentities/index.md +++ b/docs/blockentities/index.md @@ -1,6 +1,6 @@ # Block Entities -Block entities allow the storage of data on [blocks][block] in cases where [block states][blockstate] are not suitable. This is especially the case for data with a non-finite amount of options, such as inventories. Block entities are stationary and bound to a block, but otherwise share many similarities with entities, hence the name. +Block entities allow the storage of data on [blocks][block] in cases where [block states][blockstate] are not suitable. This is especially the case for data with a non-finite amount of options, such as inventories. Block entities are stationary and bound to a block, but otherwise share many similarities with [entities], hence the name. :::note If you have a finite and reasonably small amount (= a few hundred at most) of possible states for your block, you might want to consider using [block states][blockstate] instead. @@ -237,6 +237,7 @@ It is important that you do safety checks, as the `BlockEntity` might already be [blockreg]: ../blocks/index.md#basic-blocks [blockstate]: ../blocks/states.md [dataattachments]: ../datastorage/attachments.md +[entities]: ../entities/index.md [modbus]: ../concepts/events.md#event-buses [nbt]: ../datastorage/nbt.md [networking]: ../networking/index.md diff --git a/docs/blocks/index.md b/docs/blocks/index.md index 21e3cb44..cfe8efd2 100644 --- a/docs/blocks/index.md +++ b/docs/blocks/index.md @@ -25,8 +25,8 @@ After registering the block, all references to the new `my_block` should use thi ```java level.getBlockState(position) // returns the blockstate placed in the given level (world) at the given position - //highlight-next-line - .is(MyBlockRegistrationClass.MY_BLOCK); + //highlight-next-line + .is(MyBlockRegistrationClass.MY_BLOCK); ``` 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). @@ -69,16 +69,16 @@ So for example, a simple implementation would look something like this: ```java //BLOCKS is a DeferredRegister.Blocks public static final DeferredBlock MY_BETTER_BLOCK = BLOCKS.register( - "my_better_block", - registryName -> new Block(BlockBehaviour.Properties.of() - //highlight-start - .setId(ResourceKey.create(Registries.BLOCK, registryName)) - .destroyTime(2.0f) - .explosionResistance(10.0f) - .sound(SoundType.GRAVEL) - .lightLevel(state -> 7) - //highlight-end - )); + "my_better_block", + registryName -> new Block(BlockBehaviour.Properties.of() + //highlight-start + .setId(ResourceKey.create(Registries.BLOCK, registryName)) + .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. @@ -104,7 +104,6 @@ If the block subclass only takes in the `BlockBehaviour.Properties`, then `Block ```java // For some block subclass public class SimpleBlock extends Block { - public SimpleBlock(BlockBehavior.Properties properties) { // ... } @@ -129,7 +128,6 @@ If the block subclass contains more parameters, then [`RecordCodecBuilder#mapCod ```java // For some block subclass public class ComplexBlock extends Block { - public ComplexBlock(int value, BlockBehavior.Properties properties) { // ... } @@ -170,19 +168,19 @@ We already discussed how to create a `DeferredRegister.Blocks` [above], as well public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks("yourmodid"); public static final DeferredBlock EXAMPLE_BLOCK = BLOCKS.register( - "example_block", registryName -> new Block( - BlockBehaviour.Properties.of() - // The ID must be set on the block - .setId(ResourceKey.create(Registries.BLOCK, registryName)) - ) + "example_block", registryName -> new Block( + BlockBehaviour.Properties.of() + // The ID must be set on the block + .setId(ResourceKey.create(Registries.BLOCK, registryName)) + ) ); // Same as above, except that the block properties are constructed eagerly. // setId is also called internally on the properties object. public static final DeferredBlock EXAMPLE_BLOCK = BLOCKS.registerBlock( - "example_block", - Block::new, // The factory that the properties will be passed into. - BlockBehaviour.Properties.of() // The properties to use. + "example_block", + Block::new, // The factory that the properties will be passed into. + BlockBehaviour.Properties.of() // The properties to use. ); ``` @@ -190,8 +188,8 @@ If you want to use `Block::new`, you can leave out the factory entirely: ```java public static final DeferredBlock EXAMPLE_BLOCK = BLOCKS.registerSimpleBlock( - "example_block", - BlockBehaviour.Properties.of() // The properties to use. + "example_block", + BlockBehaviour.Properties.of() // The properties to use. ); ``` @@ -209,7 +207,7 @@ In several situations, multiple methods of `Block` are used at different times. ### Placing a Block -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. +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 [Right-Clicking Items][rightclick]. 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. - `BlockBehaviour#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. @@ -241,11 +239,10 @@ while (leftClickIsBeingHeld()) { } ``` -The following subsections further break down these stages into actual method calls. +The following subsections further break down these stages into actual method calls. For information about how the game gets from left-clicking to this pipeline, see [Left-Clicking an Item][leftclick]. #### 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. @@ -281,6 +278,38 @@ The following subsections further break down these stages into actual method cal - Server-only: `BlockDropsEvent` is fired. If the event is canceled, then nothing is dropped when the block breaks. Otherwise, every `ItemEntity` in `BlockDropsEvent#getDrops` is added to the current level. - Server-only: `Block#popExperience` is called with the result of the previous `IBlockExtension#getExpDrop` call, if that call returned a value greater than 0. +#### Mining Speed + +The mining speed is calculated from the block's hardness, the used [tool]'s speed, and several entity [attributes] according to the following rules: + +```java +// This will return the tool's mining speed, or 1 if the held item is either empty, not a tool, +// or not applicable for the block being broken. +float destroySpeed = item.getDestroySpeed(); +// If we have an applicable tool, add the minecraft:mining_efficiency attribute as an additive modifier. +if (destroySpeed > 1) { + destroySpeed += player.getAttributeValue(Attributes.MINING_EFFICIENCY); +} +// Apply effects from haste, conduit power, and slowness multiplicatively. +if (player.hasEffect(MobEffects.DIG_SPEED)) { destroySpeed *= ...; } +if (player.hasEffect(MobEffects.CONDUIT_POWER)) { destroySpeed *= ...; } +if (player.hasEffect(MobEffects.DIG_SLOWDOWN)) { destroySpeed *= ...; } +// Add the minecraft:block_break_speed attribute as a multiplicative modifier. +destroySpeed *= player.getAttributeValue(Attributes.BLOCK_BREAK_SPEED); +// If the player is underwater, apply the underwater mining speed penalty multiplicatively. +if (player.isEyeInFluid(FluidTags.WATER)) { + destroySpeed *= player.getAttributeValue(Attributes.SUBMERGED_MINING_SPEED); +} +// If the player is trying to break a block in mid-air, make the player mine 5 times slower. +if (!player.onGround()) { + destroySpeed /= 5; +} +destroySpeed = /* The PlayerEvent.BreakSpeed event is fired here, allowing modders to further modify this value. */; +return destroySpeed; +``` + +The exact code for this can be found in `Player#getDestroySpeed` for reference. + ### 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. @@ -306,19 +335,21 @@ Random ticks occur every tick for a set amount of blocks in a chunk. That set am Random ticking is used by a wide range of mechanics in Minecraft, such as plant growth, ice and snow melting, or copper oxidizing. [above]: #one-block-to-rule-them-all +[attributes]: ../entities/attributes.md [below]: #deferredregisterblocks-helpers [blockentities]: ../blockentities/index.md [blockstates]: states.md [bsfile]: ../resources/client/models/index.md#blockstate-files [codec]: ../datastorage/codecs.md#records -[events]: ../concepts/events.md -[interactionpipeline]: ../items/interactionpipeline.md [item]: ../items/index.md +[leftclick]: ../items/interactions.md#left-clicking-an-item [model]: ../resources/client/models/index.md [randomtick]: #random-ticking [registration]: ../concepts/registries.md#methods-for-registering [resources]: ../resources/index.md#assets +[rightclick]: ../items/interactions.md#right-clicking-an-item [sounds]: ../resources/client/sounds.md [textures]: ../resources/client/textures.md +[tool]: ../items/tools.md [usingblocks]: #using-blocks [usingblockstates]: states.md#using-blockstates diff --git a/docs/concepts/events.md b/docs/concepts/events.md index 3231949c..60899157 100644 --- a/docs/concepts/events.md +++ b/docs/concepts/events.md @@ -120,6 +120,7 @@ graph TD; PlayerEvent-->CanPlayerSleepEvent; class Event,BlockEvent,EntityEvent,LivingEvent,PlayerEvent red; + class BlockDropsEvent,CanPlayerSleepEvent blue; ``` ### Cancellable Events diff --git a/docs/datastorage/attachments.md b/docs/datastorage/attachments.md index d7102551..63efa1d1 100644 --- a/docs/datastorage/attachments.md +++ b/docs/datastorage/attachments.md @@ -99,7 +99,7 @@ To sync block entity, chunk, or entity attachments to a client, you need to [sen ## Copying data on player death -By default, entity data attachments are not copied on player death. To automatically copy an attachment on player death, set `copyOnDeath` in the attachment builder. +By default, [entity] data attachments are not copied on player death. To automatically copy an attachment on player death, set `copyOnDeath` in the attachment builder. More complex handling can be implemented via `PlayerEvent.Clone` by reading the data from the original entity and assigning it to the new entity. In this event, the `#isWasDeath` method can be used to distinguish between respawning after death and returning from the End. This is important because the data will already exist when returning from the End, so care has to be taken to not duplicate values in this case. @@ -113,6 +113,7 @@ NeoForge.EVENT_BUS.register(PlayerEvent.Clone.class, event -> { }); ``` -[saveddata]: saveddata.md [datacomponents]: ../items/datacomponents.md +[entity]: ../entities/index.md [network]: ../networking/index.md +[saveddata]: saveddata.md diff --git a/docs/datastorage/nbt.md b/docs/datastorage/nbt.md index 4532cf06..cd863bce 100644 --- a/docs/datastorage/nbt.md +++ b/docs/datastorage/nbt.md @@ -89,7 +89,7 @@ tag.get("Tag"); ## Usages of NBT -NBT is used in a lot of places in Minecraft. Some of the most common examples include [`BlockEntity`][blockentity]s and `Entity`s. +NBT is used in a lot of places in Minecraft. Some of the most common examples include [`BlockEntity`][blockentity]s and [`Entity`s][entity]. :::note `ItemStack`s abstract away the usage of NBT into [data components][datacomponents]. @@ -102,4 +102,5 @@ NBT is used in a lot of places in Minecraft. Some of the most common examples in [blockentity]: ../blockentities/index.md [datapack]: ../resources/index.md#data [datacomponents]: ../items/datacomponents.md +[entity]: ../entities/index.md [nbtwiki]: https://minecraft.wiki/w/NBT_format diff --git a/docs/entities/_category_.json b/docs/entities/_category_.json new file mode 100644 index 00000000..8afd8a39 --- /dev/null +++ b/docs/entities/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Entities", + "position": 5 +} \ No newline at end of file diff --git a/docs/entities/attributes.md b/docs/entities/attributes.md new file mode 100644 index 00000000..5be9492f --- /dev/null +++ b/docs/entities/attributes.md @@ -0,0 +1,225 @@ +--- +sidebar_position: 4 +--- +# Attributes + +Attributes are special fields of [living entities][livingentity] that determine basic properties such as max health, speed or armor. All attributes are stored as double values and synced automatically. Vanilla offers a wide range of default attributes, and you can also add your own. + +Due to legacy implementations, not all attributes work with all entities. For example, flying speed is ignored by ghasts, and jump strength only affects horses, not players. + +## Built-In Attributes + +### Minecraft + +The following attributes are in the `minecraft` namespace, and their in-code values can be found in the `Attributes` class. + +| Name | In Code | Range | Default Value | Usage | +|----------------------------------|----------------------------------|----------------|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `armor` | `ARMOR` | `[0,30]` | 0 | The armor value of the entity. A value of 1 means half a chestplate icon above the hotbar. | +| `armor_toughness` | `ARMOR_TOUGHNESS` | `[0,20]` | 0 | The armor toughness value of the entity. See [Armor Toughness][toughness] on the [Minecraft Wiki][wiki] for more information. | +| `attack_damage` | `ATTACK_DAMAGE` | `[0,2048]` | 2 | The base attack damage done by the entity, without any weapon or similar item. | +| `attack_knockback` | `ATTACK_KNOCKBACK` | `[0,5]` | 0 | The extra knockback dealt by the entity. Knockback additionally has a base strength not represented by this attribute. | +| `attack_speed` | `ATTACK_SPEED` | `[0,1024]` | 4 | The attack cooldown of the entity. Higher numbers mean more cooldown, setting this to 0 effectively re-enables pre-1.9 combat. | +| `block_break_speed` | `BLOCK_BREAK_SPEED` | `[0,1024]` | 1 | How fast the entity can mine blocks, as a multiplicative modifier. See [Mining Speed][miningspeed] for more information. | +| `block_interaction_range` | `BLOCK_INTERACTION_RANGE` | `[0,64]` | 4.5 | The interaction range in which the entity can interact with blocks, in blocks. | +| `burning_time` | `BURNING_TIME` | `[0,1024]` | 1 | A multiplier for how long the entity will burn when ignited. | +| `explosion_knockback_resistance` | `EXPLOSION_KNOCKBACK_RESISTANCE` | `[0,1]` | 0 | The explosion knockback resistance of the entity. This is a value in percent, i.e. 0 is no resistance, 0.5 is half resistance, and 1 is full resistance. | +| `entity_interaction_range` | `ENTITY_INTERACTION_RANGE` | `[0,64]` | 3 | The interaction range in which the entity can interact with other entities, in blocks. | +| `fall_damage_multiplier` | `FALL_DAMAGE_MULTIPLIER` | `[0,100]` | 1 | A multiplier for fall damage taken by the entity. | +| `flying_speed` | `FLYING_SPEED` | `[0,1024]` | 0.4 | A multiplier for flying speed. This is not actually used by all flying entities, and ignored by e.g. ghasts. | +| `follow_range` | `FOLLOW_RANGE` | `[0,2048]` | 32 | The distance in blocks that the entity will target/follow the player. | +| `gravity` | `GRAVITY` | `[1,1]` | 0.08 | The gravity the entity is influenced by, in blocks per tick squared. | +| `jump_strength` | `JUMP_STRENGTH` | `[0,32]` | 0.42 | The jump strength of the entity. Higher value means higher jumping. | +| `knockback_resistance` | `KNOCKBACK_RESISTANCE` | `[0,1]` | 0 | The knockback resistance of the entity. This is a value in percent, i.e. 0 is no resistance, 0.5 is half resistance, and 1 is full resistance. | +| `luck` | `LUCK` | `[-1024,1024]` | 0 | The luck value of the entity. This is used when rolling [loot tables][loottables] to give bonus rolls or otherwise modify the resulting items' quality. | +| `max_absorption` | `MAX_ABSORPTION` | `[0,2048]` | 0 | The max absorption (yellow hearts) of the entity. A value of 1 means half a heart. | +| `max_health` | `MAX_HEALTH` | `[1,1024]` | 20 | The max health of the entity. A value of 1 means half a heart. | +| `mining_efficiency` | `MINING_EFFICIENCY` | `[0,1024]` | 0 | How fast the entity can mine blocks, as an additive modifier, only if the used tool is correct. See [Mining Speed][miningspeed] for more information. | +| `movement_efficiency` | `MOVEMENT_EFFICIENCY` | `[0,1]` | 0 | A linearly-interpolated movement speed bonus applied to the entity when it is walking on blocks that have a slowdown, such as soul sand. | +| `movement_speed` | `MOVEMENT_SPEED` | `[0,1024]` | 0.7 | The movement speed of the entity. Higher value means faster. | +| `oxygen_bonus` | `OXYGEN_BONUS` | `[0,1024]` | 0 | An oxygen bonus for the entity. The higher this is, the longer it takes for the entity to start drowning. | +| `safe_fall_distance` | `SAFE_FALL_DISTANCE` | `[-1024,1024]` | 3 | The fall distance for the entity that is safe, i.e. the distance in which no fall damage is taken. | +| `scale` | `SCALE` | `[0.0625,16]` | 1 | The scale at which the entity is rendered. | +| `sneaking_speed` | `SNEAKING_SPEED` | `[0,1]` | 0.3 | A movement speed multiplier applied to the entity when it is sneaking. | +| `spawn_reinforcements` | `SPAWN_REINFORCEMENTS_CHANCE` | `[0,1]` | 0 | The chance for zombies to spawn other zombies. This is only relevant on hard difficulty, as zombie reinforcements do not occur on normal difficulty or lower. | +| `step_height` | `STEP_HEIGHT` | `[0,10]` | 0.6 | The step height of the entity, in blocks. If this is 1, the player can walk up 1-block ledges like they were slabs. | +| `submerged_mining_speed` | `SUBMERGED_MINING_SPEED` | `[0,20]` | 0.2 | How fast the entity can mine blocks, as a multiplicative modifier, only if the entity is underwater. See [Mining Speed][miningspeed] for more information. | +| `sweeping_damage_ratio` | `SWEEPING_DAMAGE_RATIO` | `[0,1]` | 0 | The amount of damage done by sweep attacks, in percent of the main attack. This is a value in percent, i.e. 0 is no damage, 0.5 is half damage, and 1 is full damage. | +| `tempt_range` | `TEMPT_RANGE` | `[0,2048]` | 10 | The range at which the entity can be tempted using items. Mainly for passive animals, e.g. cows or pigs. | +| `water_movement_efficiency` | `WATER_MOVEMENT_EFFICIENCY` | `[0,1]` | 0 | A movement speed multiplier that is applied when the entity is underwater. | + +:::warning +Some attribute caps are set relatively arbitrarily by Mojang. This is especially notable for armor, which is capped at 30. NeoForge doesn't touch those caps, however there are mods to change them. +::: + +### NeoForge + +The following attributes are in the `neoforge` namespace, and their in-code values can be found in the `NeoForgeMod` class. + +| Name | In Code | Range | Default Value | Usage | +|--------------------|--------------------|------------|---------------|------------------------------------------------------------------------------------------------------------------------------------------------------| +| `creative_flight` | `CREATIVE_FLIGHT` | `[0,1]` | 0 | Determines whether creative flight for the entity is enabled (\> 0) or disabled (\<\= 0). | +| `nametag_distance` | `NAMETAG_DISTANCE` | `[0,32]` | 32 | How far the nametag of the entity will be visible, in blocks. | +| `swim_speed` | `SWIM_SPEED` | `[0,1024]` | 1 | A movement speed multiplier that is applied when the entity is underwater. This is applied independently from `minecraft:water_movement_efficiency`. | + +## Default Attributes + +When creating a `LivingEntity`, it is required to register a set of default attributes for them. When an entity is [spawned][spawning] in, its default attributes are set on it. Default attributes are registered in the [`EntityAttributeCreationEvent`][event] like so: + +```java +@SubscribeEvent +public static void createDefaultAttributes(EntityAttributeCreationEvent event) { + event.put( + // Your entity type. + MY_ENTITY.get(), + // An AttributeSupplier. This is typically created by calling LivingEntity#createLivingAttributes, + // setting your values on it, and calling #build. You can also create the AttributeSupplier from scratch + // if you want, see the source of LivingEntity#createLivingAttributes for an example. + LivingEntity.createLivingAttributes() + // Add an attribute with its default value. + .add(Attributes.MAX_HEALTH) + // Add an attribute with a non-default value. + .add(Attributes.MAX_HEALTH, 50) + // Build the AttributeSupplier. + .build() + ); +} +``` + +:::tip +Some classes have specialized versions of `LivingEntity#createLivingAttributes`. For example, the `Monster` class has a method named `Monster#createMonsterAttributes` that can be used instead. +::: + +## Querying Attributes + +Attribute values are stored on entities in an `AttributeMap`, which is basically a `Map`. Attribute instances are basically what item stacks are to items, i.e. whereas an attribute is a registered singleton, attribute instances are concrete attribute objects bound to a concrete entity. + +The `AttributeMap` of an entity can be retrieved by calling `LivingEntity#getAttributes`. You can then query the map like so: + +```java +// Get the attribute map. +AttributeMap attributes = livingEntity.getAttributes(); +// Get an attribute instance. This may be null if the entity does not have the attribute. +AttributeInstance instance = attributes.getInstance(Attributes.ARMOR); +// Get the value for an attribute. Will fallback to the default for the entity if needed. +double value = attributes.getValue(Attributes.ARMOR); +// Of course, we can also check if an attribute is present to begin with. +if (attributes.hasAttribute(Attributes.ARMOR)) { ... } + +// Alternatively, LivingEntity also offers shortcuts: +AttributeInstance instance = livingEntity.getAttribute(Attributes.ARMOR); +double value = livingEntity.getAttributeValue(Attributes.ARMOR); +``` + +:::info +When handling attributes, you will almost exclusively use `Holder`s instead of `Attribute`s. This is also why with custom attributes (see below), we explicitly store the `Holder`. +::: + +## Attribute Modifiers + +In contrast to querying, changing the attribute values is not as easy. This is mainly because there may be multiple changes required to an attribute at the same time. + +Consider this: You are a player, who has an attack damage attribute of 1. You wield a diamond sword, which does 6 extra attack damage, so you have 7 total attack damage. Then you drink a strength potion, adding a damage multiplier. You then also have some sort of trinket equipped that adds yet another multiplier. + +To avoid miscalculations and to better communicate how the attribute values are modified, Minecraft introduces the attribute modifier system. In this system, every attribute has a **base value**, which is typically sourced from the default attributes we discussed earlier. We can then add any amount of **attribute modifiers** that can be individually removed again, without us having to worry about correctly applying operations. + +To get started, let's create an attribute modifier: + +```java +// The name of the modifier. This is later used to query the modifier from the attribute map +// and as such must be (semantically) unique. +ResourceLocation id = ResourceLocation.fromNamespaceAndPath("yourmodid", "my_modifier"); +// The modifier itself. +AttributeModifier modifier = new AttributeModifier( + // The name we defined earlier. + id, + // The amount by which we modify the attribute value. + 2.0, + // The operation used to apply the modifier. Possible values are: + // - AttributeModifier.Operation.ADD_VALUE: Adds the value to the total attribute value. + // - AttributeModifier.Operation.ADD_MULTIPLIED_BASE: Multiplies the value with the attribute base value + // and adds it to the total attribute value. + // - AttributeModifier.Operation.ADD_MULTIPLIED_TOTAL: Multiplies the value with the total attribute value, + // i.e. the attribute base value with all previous modifications already performed, + // and adds it to the total attribute value. + AttributeModifier.Operation.ADD_VALUE +); +``` + +Now, to apply the modifier, we have two options: add it as a transient modifier, or as a permanent modifier. Permanent modifiers are saved to disk, while transient modifiers are not. The use case for permanent modifiers is things like permanent stat bonuses (e.g. some sort of armor or health skill), while transient modifiers are mainly for [equipment], [mob effects][mobeffect] and other modifiers that depend on the player's current state. + +```java +AttributeMap attributes = livingEntity.getAttributes(); +// Add a transient modifier. If a modifier with the same id is already present, this will throw an exception. +attributes.getInstance(Attributes.ARMOR).addTransientModifier(modifier); +// Add a transient modifier. If a modifier with the same id is already present, it is removed first. +attributes.getInstance(Attributes.ARMOR).addOrUpdateTransientModifier(modifier); +// Add a permanent modifier. If a modifier with the same id is already present, this will throw an exception. +attributes.getInstance(Attributes.ARMOR).addPermanentModifier(modifier); +// Add a permanent modifier. If a modifier with the same id is already present, it is removed first. +attributes.getInstance(Attributes.ARMOR).addOrReplacePermanentModifier(modifier); +``` + +These modifiers can also be removed again: + +```java +// Remove by modifier object. +attributes.getInstance(Attributes.ARMOR).removeModifier(modifier); +// Remove by modifier id. +attributes.getInstance(Attributes.ARMOR).removeModifier(id); +// Remove all modifiers for an attribute. +attributes.getInstance(Attributes.ARMOR).removeModifiers(); +``` + +Finally, we can also query the attribute map for whether it has a modifier with a certain ID, as well as query base values and modifier values separately, like so: + +```java +// Check for the modifier being present. +if (attributes.getInstance(Attributes.ARMOR).hasModifier(id)) { ... } +// Get the base armor attribute value. +double baseValue = attributes.getBaseValue(Attributes.ARMOR); +// Get the value of a certain modifier. +double modifierValue = attributes.getModifierValue(Attributes.ARMOR, id); +``` + +## Custom Attributes + +If needed, you can also add your own attributes. Like many other systems, attributes are a [registry], and you can register your own objects to it. To get started, create a `DeferredRegister` like so: + +```java +public static final DeferredRegister ATTRIBUTES = DeferredRegister.create( + BuiltInRegistries.ATTRIBUTE, "yourmodid"); +``` + +For the attributes themselves, there are three classes you can choose from: + +- `RangedAttribute`: Used by most attributes, this class defines lower and upper bounds for the attribute, along with a default value. +- `PercentageAttribute`: Like `RangedAttribute`, but is displayed in percent instead of float values. NeoForge-added. +- `BooleanAttribute`: An attribute that only has semantic true (\> 0) and false (\<\= 0). This still uses doubles internally. NeoForge-added. + +Using `RangedAttribute` as an example (the other two work similarly), registering an attribute would look like this: + +```java +public static final Holder MY_ATTRIBUTE = ATTRIBUTES.register("my_attribute", () -> new RangedAttribute( + // The translation key to use. + "attributes.yourmodid.my_attribute", + // The default value. + 0, + // Min and max values. + -10000, + 10000 +)); +``` + +And that's it! Just don't forget to register your `DeferredRegister` to the mod bus, and off you go. + +[equipment]: ../blockentities/container.md#containers-on-entitys +[event]: ../concepts/events.md +[livingentity]: livingentity.md +[loottables]: ../resources/server/loottables/index.md +[miningspeed]: ../blocks/index.md#mining-speed +[mobeffect]: ../items/mobeffects.md +[registry]: ../concepts/registries.md +[spawning]: index.md#spawning-entities +[toughness]: https://minecraft.wiki/w/Armor#Armor_toughness +[wiki]: https://minecraft.wiki diff --git a/docs/entities/data.md b/docs/entities/data.md new file mode 100644 index 00000000..4c29254d --- /dev/null +++ b/docs/entities/data.md @@ -0,0 +1,98 @@ +--- +sidebar_position: 2 +--- +# Data and Networking + +An entity without data is quite useless, as such storing data on an entity is essential. All entities store some default data, such as their type and their position. This article will explain how to add your own data, as well as how to synchronize that data. + +The most simple way to add data is as a field in your `Entity` class. You can then interact with this data in any way you wish. However, this quickly becomes very annoying as soon as you have to synchronize that data. This is because most entity logic is run on the server only, and it is only occasionally (depending on the [`EntityType`][entitytype]'s `clientUpdateInterval` value) that an update is sent to the client; this is also the cause for easily noticeable entity "lags" when the server's tick speed is too slow. + +As such, vanilla introduces a few systems to help with that, each of which serves a specific purpose. + +## `SynchedEntityData` + +`SynchedEntityData` is a system used for storing values at runtime and syncing them over the network. It is split into three classes: + +- `EntityDataSerializer`s are basically wrappers around a [`StreamCodec`][streamcodec]. + - Minecraft uses a hard-coded map of serializers. NeoForge transforms this map into a registry, meaning that if you want to add new `EntityDataSerializer`s, they must be added by [registration]. + - Minecraft defines various default `EntityDataSerializer`s in the `EntityDataSerializers` class. +- `EntityDataAccessor`s are held by the entity and are used to get and set the data values. +- `SynchedEntityData` itself holds all `EntityDataAccessor`s for an entity, and automatically calls on the `EntityDataSerializer`s to sync values as needed. + +To get started, create an `EntityDataAccessor` in your entity class: + +```java +public class MyEntity extends Entity { + // The generic type must match the one of the second parameter below. + public static final EntityDataAccessor MY_DATA = + SynchedEntityData.defineId( + // The class of the entity. + MyEntity.class, + // The entity data accessor type. + EntityDataSerializers.INT + ); +} +``` + +:::danger +While the compiler will allow you to use a class other than the owning class as the first parameter in `SynchedEntityData#defineId()`, this can and will lead to hard-to-debug issues and as such should be avoided. +::: + +We must then define default values in the `defineSynchedData` method, like so: + +```java +public class MyEntity extends Entity { + public static final EntityDataAccessor MY_DATA = SynchedEntityData.defineId(MyEntity.class, EntityDataSerializers.INT); + + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) { + // Our default value is zero. + builder.define(MY_DATA, 0); + } +} +``` + +Finally, we can get and set entity data like so (assuming we're in a method within `MyEntity`): + +```java +int data = this.getEntityData().get(MY_DATA); +this.getEntityData().set(MY_DATA, 1); +``` + +## `readAdditionalSaveData` and `addAdditionalSaveData` + +These two methods are used to read and write data to disk. They work by loading/saving your values from/to an [NBT tag][nbt], like so: + +```java +// Assume that an `int data` exists in the class. +@Override +protected void readAdditionalSaveData(CompoundTag tag) { + this.data = tag.getInt("my_data"); +} + +@Override +protected void addAdditionalSaveData(CompoundTag tag) { + tag.putInt("my_data", this.data); +} +``` + +## Custom Spawn Data + +In some cases, there is custom data needed for your entity on the client when it is spawned, but that same data doesn't change over time. When this is the case, you can implement the `IEntityWithComplexSpawn` interface on your entity and use its two methods `#writeSpawnData` and `#readSpawnData` to write/read data to/from the network buffer. + +Additionally, you can send your own packets upon spawning. To do so, override `IEntityExtension#sendPairingData` and send your packets there like any other packet. Please refer to the [Networking articles][networking] for more information. + +## Data Attachments + +Entities have been patched to extend `AttachmentHolder` and as such support data storage via [data attachments][attachment]. Its main use is to define custom data on entities that are not your own, i.e., entities added by Minecraft or other mods. Please see the linked article for more information. + +## Custom Network Messages + +For syncing, you can also always opt to use a custom packet to send additional information when needed. Please refer to the [Networking articles][networking] for more information. + +[attachment]: ../datastorage/attachments.md +[entitytype]: index.md#entitytype +[nbt]: ../datastorage/nbt.md +[networking]: ../networking/index.md +[registration]: ../concepts/registries.md#methods-for-registering +[streamcodec]: ../networking/streamcodecs.md diff --git a/docs/entities/index.md b/docs/entities/index.md new file mode 100644 index 00000000..e5dba451 --- /dev/null +++ b/docs/entities/index.md @@ -0,0 +1,446 @@ +--- +sidebar_position: 1 +--- +# Entities + +Entities are in-world objects that can interact with the world in a variety of ways. Common example include mobs, projectiles, rideable objects, and even players. Each entity consists of multiple systems that may not seem understandable at first glance. This section will break down some of the key components related to constructing an entity and making it behave as the modder intends. + +## Terminology + +A simple entity is made up of three parts: + +- The [`Entity`][entity] subclass, which holds most of our entity's logic +- The [`EntityType`][type], which is [registered][registration] and holds some common properties, and +- The [`EntityRenderer`][renderer], which is responsible for displaying the entity in-game + +More complex entities may require more parts. For example, many of the more complex `EntityRenderer`s use an underlying `EntityModel` instance. Or, a naturally spawning entity will need some sort of [spawn mechanism][spawning]. + +## `EntityType` + +The relationship between `EntityType`s and `Entity`s is similar to that of [`Item`s][item] and [`ItemStack`s][itemstack]. Like `Item`s, `EntityType`s are singletons that are registered to their corresponding registry (the entity type registry) and hold some values common to all entities of that type, while `Entity`s, like `ItemStack`s, are "instances" of that singleton type that hold data specific to that one entity instance. However, the key difference here is that most of the behavior is not defined in the singleton `EntityType`, but rather in the instantiated `Entity` class itself. + +Let's create our `EntityType` registry and register an `EntityType` for it, assuming we have a class `MyEntity` that extends `Entity` (see [below][entity] for more information). All methods on `EntityType.Builder`, except for the `#build` call at the end, are optional. + +```java +public static final DeferredRegister> ENTITY_TYPES = + DeferredRegister.create(Registries.ENTITY_TYPE, ExampleMod.MOD_ID); + +public static final Supplier> MY_ENTITY = ENTITY_TYPES.register( + "my_entity", + // The entity type, created using a builder. + () -> EntityType.Builder.of( + // An EntityType.EntityFactory, where T is the entity class used - MyEntity in this case. + // You can think of it as a BiFunction, Level, T>. + // This is commonly a reference to the entity constructor. + MyEntity::new, + // The MobCategory our entity uses. This is mainly relevant for spawning. + // See below for more information. + MobCategory.MISC + ) + // The width and height, in blocks. The width is used in both horizontal directions. + // This also means that non-square footprints are not supported. Default is 0.6f and 1.8f. + .sized(1.0f, 1.0f) + // The spawn dimensions. This is used by mobs that spawn in varying sizes. + // In vanilla, these are only slimes and magma cubes, both of which use 4.0f. + .spawnDimensionsScale(4.0f) + // The eye height, in blocks from the bottom of the size. Defaults to height * 0.85. + // This must be called after #sized to have an effect. + .eyeHeight(0.5f) + // Disables the entity being summonable via /summon. + .noSummon() + // Prevents the entity from being saved to disk. + .noSave() + // Makes the entity fire immune. + .fireImmune() + // Makes the entity immune to damage from a certain block. Vanilla uses this to make + // foxes immune to sweet berry bushes, withers and wither skeletons immune to wither roses, + // and polar bears, snow golems and strays immune to powder snow. + .immuneTo(Blocks.POWDER_SNOW) + // Disables a rule in the spawn handler that limits the distance at which entities can spawn. + // This means that no matter the distance to the player, this entity can spawn. + // Vanilla enables this for pillagers and shulkers. + .canSpawnFarFromPlayer() + // The range in which the entity is kept loaded by the client, in chunks. + // Vanilla values for this vary, but it's often something around 8 or 10. Defaults to 5. + // Be aware that if this is greater than the client's chunk view distance, + // then that chunk view distance is effectively used here instead. + .clientTrackingRange(8) + // How often update packets are sent for this entity, in once every x ticks. This is set to higher values + // for entities that have predictable movement patterns, for example projectiles. Defaults to 3. + .updateInterval(10) + // Build the entity type. The parameter should be the same as the entity id. + .build("my_entity") +); +``` + +:::warning +Sometimes, there may be generic bounds errors with the entity type and the entity constructor. If this happens, the easiest solution is often to use an explicit generic type for `EntityType.Builder#of`, like so: + +```java +() -> EntityType.Builder.of(...) +``` +::: + +### `MobCategory` + +_See also [Natural Spawning][mobspawn]._ + +An entity's `MobCategory` determines some properties for the entity, which are related to [spawning and despawning][mobspawn]. Vanilla adds a total of eight `MobCategory`s by default: + +| Name | Spawn Cap | Examples | +|------------------------------|-----------|--------------------------------------------------------------------------------------------------------------------------------| +| `MONSTER` | 70 | Various monsters | +| `CREATURE` | 10 | Various animals | +| `AMBIENT` | 15 | Bats | +| `AXOLOTS` | 5 | Axolotls | +| `UNDERGROUND_WATER_CREATURE` | 5 | Glow Squids | +| `WATER_CREATURE` | 5 | Squids, Dolphins | +| `WATER_AMBIENT` | 20 | Fish | +| `MISC` | N/A | All non-living entities, e.g. projectiles; using this `MobCategory` will make the entity unable to be spawned naturally at all | + +There are also some other properties that are only set on one or two `MobCategory`s each: + +- `isFriendly`: Set to false for `MONSTER`, and true for all others. +- `isPersistent`: Set to true for `CREATURE` and `MISC`, and false for all others. +- `despawnDistance`: Set to 64 for `WATER_AMBIENT`, and 128 for all others. + +:::info +`MobCategory` is an [extensible enum][extenum], meaning that you can add custom entries to it. If you do so, you will also have to add some spawning mechanism for entities of this custom `MobCategory`. +::: + +## The Entity Class + +To begin, we create an `Entity` subclass. Alongside a constructor, `Entity` (which is an abstract class) defines four required methods that we are required to implement. The first three will be explained in the [Data and Networking article][data], in order to not further bloat this article, and `#hurtServer` is explained in the [Damaging Entities section][damaging]. + +```java +public class MyEntity extends Entity { + // We inherit this constructor without the bound on the generic wildcard. + // The bound is needed for registration below, so we add it here. + public MyEntity(EntityType type, Level level) { + super(type, level); + } + + // See the Data and Networking article for information about these methods. + @Override + protected void readAdditionalSaveData(CompoundTag compoundTag) {} + + @Override + protected void addAdditionalSaveData(CompoundTag compoundTag) {} + + @Override + protected void defineSynchedData(SynchedEntityData.Builder builder) {} + + @Override + public boolean hurtServer(ServerLevel level, DamageSource damageSource, float amount) { + return true; + } +} +``` + +:::info +While `Entity` can be extended directly, it often makes sense to use one of its many subclasses as a base instead. See the [entity class hierarchy][hierarchy] for more information. +::: + +If required (e.g. because you're spawning entities from code), you can also add custom constructors. These generally hardcode the entity type as a reference to the registered object, like so: + +```java +public MyEntity(Level level, double x, double y, double z) { + // Delegates to the factory constructor, using the EntityType we registered before. + this(MY_ENTITY.get(), level); + this.setPos(x, y, z); +} +``` + +And now, we are free to do basically whatever we want with our entity. The following subsections will display a variety of common entity use cases. + +### Data Storage on Entities + +_See [Entities/Data and Networking][data]._ + +### Rendering Entities + +_See [Entities/Entity Renderers][renderer]._ + +### Spawning Entities + +If we now boot up the game now and enter a world, we have exactly one way of spawning: through the [`/summon`][summon] command (assuming `EntityType.Builder#noSummon` was not called). + +Obviously, we want to add our entities some other way. The easiest way to do so is through the `LevelWriter#addFreshEntity` method. This method simply accepts an `Entity` instance and adds it to the world, like so: + +```java +// In some method that has a level available, only on the server +if (!level.isClientSide()) { + MyEntity entity = new MyEntity(level, 100.0, 200.0, 300.0); + level.addFreshEntity(entity); +} +``` + +This will be used for pretty much all non-living entities. Players should obviously not be spawned yourself, `Mob`s have [their own ways of spawning][mobspawn] (though they can also be added via `#addFreshEntity`), and vanilla [projectiles][projectile] also have static helpers for spawning in the `Projectile` class. + +### Damaging Entities + +_See also [Left-Clicking an Item][leftclick]._ + +While not all entities have the concept of hit points, they can still all receive damage. This is not only used by things like mobs and players: If you cast your mind to item entities (dropped items), they too can take damage from sources like fire or cacti, in which case they are usually deleted immediately. + +Damaging an entity is possible by calling either `Entity#hurt` or `Entity#hurtOrSimulate`, the difference between those two is explained below. Both methods take two arguments: the [`DamageSource`][damagesource] and the damage amount, as a float in half hearts. For example, calling `entity.hurt(entity.damageSources().wither(), 4.25)` will cause a little over two hearts of wither damage. + +In turn, entities can also modify that behavior. This isn't done by overriding `#hurt`, as it is a final method. Rather, there are two methods `#hurtServer` and `#hurtClient` that each handle damage logic for the corresponding side. `#hurtClient` is commonly used to tell the client that an attack has succeeded, even though that may not always be true, mainly for playing attack sounds and other effects regardless. For changing damage behavior, we mainly care about `#hurtServer`, which we can override like so: + +```java +@Override +// The boolean return value determines whether the entity was actually damaged or not. +public boolean hurtServer(ServerLevel level, DamageSource damageSource, float amount) { + if (damageSource.is(DamageTypeTags.IS_FIRE)) { + // This assumes that super#hurt() is implemented. Common other ways to do this + // are to set some field yourself. Vanilla implementations vary greatly across different entities. + // Notably, living entities usually call #actuallyHurt, which in turn calls #setHealth. + return super.hurt(level, damageSource, amount * 2); + } else { + return false; + } +} +``` + +This server/client separation is also the difference between `Entity#hurt` and `Entity#hurtOrSimulate`: `Entity#hurt` only runs on the server (and calls `Entity#hurtServer`), whereas `Entity#hurtOrSimulate` runs on both sides, calling `Entity#hurtServer` or `Entity#hurtClient` depending on the side. + +It is also possible to modify damage done to entities that do not belong to you, i.e. those added by Minecraft or other mods, through events. These events contain a lot of code specific to `LivingEntity`s; as such, their documentation resides in the [Damage Events section][damageevents] within the [Living Entities article][livingentity]. + +### Ticking Entities + +Quite often, you will want your entity to do something (e.g. move) every tick. This logic is split across several methods: + +- `#tick`: This is the central tick method, and the one you will want to override in 99% of cases. + - By default, this forwards to `#baseTick`, however this is overridden by almost every subclass. +- `#baseTick`: This method handles updating some values common to all entities, including the "on fire" state, freezing from powder snow, the swimming state, and passing through portals. `LivingEntity` additionally handles drowning, in-block damage, and updates to the damage tracker here. Override this method if you want to change or add to that logic. + - By default, `Entity#tick` will forward to this method. +- `#rideTick`: This method is called for passengers of other entities, for example for players riding horses, or any entity that rides another entity due to use of the `/ride` command. + - By default, this does some checks and then calls `#tick`. Skeletons and players override this method for special handling of riding entities. + +Additionally, the entity has a field called `tickCount`, which is the time, in ticks, that the entity has existed in the level, and a boolean field named `firstTick`, which should be self-explanatory. For example, if you wanted to [spawn a particle][particle] every 5 ticks, you could use the following code: + +```java +@Override +public void tick() { + // Always call super unless you have a good reason not to. + super.tick(); + // Run this code once every 5 ticks, and make sure we spawn the particle on the server. + if (this.tickCount % 5 == 0 && !level().isClientSide()) { + level().addParticle(...); + } +} +``` + +### Picking Entities + +_See also [Middle-Clicking][middleclick]._ + +Picking is the process of selecting the thing that the player is currently looking at, as well as subsequently picking the associated item. The result of middle-clicking, known as the "pick result", can be modified by your entity (be aware that the `Mob` class will select the correct spawn egg for you): + +```java +@Override +@Nullable +public ItemStack getPickResult() { + // Assumes that MY_CUSTOM_ITEM is a DeferredItem, see the Items article for more information. + // If the entity should not be pickable, it is advised to return null here. + return new ItemStack(MY_CUSTOM_ITEM.get()); +} +``` + +While entities should generally be pickable, there are some niche cases where this isn't desirable. A vanilla use case for this is the ender dragon, which consists of multiple parts. The parent entity has picking disabled, but the parts have it enabled again, for finer hitbox tuning. + +If you have a similarly niche use case, your entity can also be disabled from picking entirely like so: + +```java +@Override +public boolean isPickable() { + // Additional checks may be performed here if needed. + return false; +} +``` + +If you want to do the picking (i.e. ray casting) yourself, you can call `Entity#pick` on the entity that you want to start the ray cast from. This will return a [`HitResult`][hitresult] that you can further check for what exactly has been hit by the ray cast. + +### Entity Attachments + +_Not to be confused with [Data Attachments][dataattachments]._ + +Entity attachments are used to define visual attachment points for the entity. Using this system, it can be defined where things like passengers or name tags will be displayed relative to the entity itself. The entity itself controls only the default position of the attachment, and the attachment can then define an offset from that default. + +When building the `EntityType`, any amount of attachment points can be set by calling `EntityType.Builder#attach`. This method accepts an `EntityAttachment`, which defines the attachment to consider, and three floats to define the position (x/y/z). The position should be defined relative to where the default value of the attachment would be. + +Vanilla defines the following four `EntityAttachment`s: + +| Name | Default | Usages | +|----------------|------------------------------------------|----------------------------------------------------------------------| +| `PASSENGER` | Center X/top Y/center Z of the hitbox | Rideable entities, e.g. horses, to define where passengers appear | +| `VEHICLE` | Center X/bottom Y/center Z of the hitbox | All entities, to define where they appear when riding another entity | +| `NAME_TAG` | Center X/top Y/center Z of the hitbox | Define where the name tag of the entity appears, if applicable | +| `WARDEN_CHEST` | Center X/center Y/center Z of the hitbox | By wardens, to define where the sonic boom attack originates from | + +:::info +`PASSENGER` and `VEHICLE` are related in that they are used in the same context. First, `PASSENGER` is applied to position the rider. Then, `VEHICLE` is applied on the rider. +::: + +Every attachment can be thought of as a mapping from `EntityAttachment` to `List`. The amount of points actually used depends on the consuming system. For example, boats and camels will use two `PASSENGER` points, while entities like horses or minecarts will only use one `PASSENGER` point. + +`EntityType.Builder` also has some helpers related to `EntityAttachment`s: + +- `#passengerAttachment()`: Used to define `PASSENGER` attachments. Comes in two variants. + - One variant accepts a `Vec3...` of attachment points. + - The other accepts a `float...`, which forwards to the `Vec3...` variant by transforming each float to a `Vec3` that uses the given float as the y value, and sets x and z to 0. +- `#vehicleAttachment()`: Used to define a `VEHICLE` attachment. Accepts a `Vec3`. +- `#ridingOffset()`: Used to define a `VEHICLE` attachment. Accepts a float and forwards to `#vehicleAttachment()` with a `Vec3` that has its x and z values set to 0, and the y value set to the negated value of the passed-in float. +- `#nameTagOffset()`: Used to define a `NAME_TAG` attachment. Accepts a float, which is used for the y value, with 0 being used for the x and z values. + +Alternatively, attachments can be defined yourself by calling `EntityAttachments#builder()` and then calling `#attach()` on that builder, like so: + +```java +// In some EntityType creation +EntityType.Builder.of(...) + // This EntityAttachment will make name tags float half a block above the ground. + // However, most living entities will have the default for this to be at the top of their hitbox instead. + // Therefore, the name tag will actually appear half a block above the top of the living entity's hitbox. + .attach(EntityAttachment.NAME_TAG, 0, 0.5f, 0) + .build(); +``` + +## Entity Class Hierarchy + +Due to the many different types of entities, there is a complex hierarchy of subclasses of `Entity`. These are important to know about when choosing what class to extend when making your own entity, as you will be able to save a lot of work by reusing their code. + +The vanilla entity hierarchy looks like this (red classes are `abstract`, blue classes are not): + +```mermaid +graph LR; + Entity-->Projectile; + Entity-->LivingEntity; + Entity-->BlockAttachedEntity; + BlockAttachedEntity-->LeashFenceKnotEntity; + BlockAttachedEntity-->HangingEntity; + HangingEntity-->ItemFrame; + ItemFrame-->GlowItemFrame; + HangingEntity-->Painting; + Entity-->PartEntity; + PartEntity-->EnderDragonPart; + Entity-->VehicleEntity; + VehicleEntity-->AbstractBoat; + AbstractBoat-->AbstractChestBoat; + AbstractChestBoat-->ChestBoat; + AbstractChestBoat-->ChestRaft; + AbstractBoat-->Boat; + AbstractBoat-->Raft; + VehicleEntity-->AbstractMinecart; + AbstractMinecart-->AbstractMinecartContainer; + AbstractMinecartContainer-->MinecartChest; + AbstractMinecartContainer-->MinecartHopper; + AbstractMinecart-->Minecart; + AbstractMinecart-->MinecartCommandBlock; + AbstractMinecart-->MinecartFurnace; + AbstractMinecart-->MinecartSpawner; + AbstractMinecart-->MinecartTNT; + + class Entity,Projectile,LivingEntity,BlockAttachedEntity,HangingEntity,PartEntity,VehicleEntity,AbstractBoat,AbstractChestBoat,AbstractMinecart,AbstractMinecartContainer red; + class LeashFenceKnotEntity,ItemFrame,GlowItemFrame,Painting,EnderDragonPart,ChestBoat,ChestRaft,Boat,Raft,MinecartChest,MinecartHopper,Minecart,MinecartCommandBlock,MinecartCommandBlock,MinecartFurnace,MinecartSpawner,MinecartTNT blue; +``` + +Let's break these down: + +- `Projectile`: The base class for various projectiles, including arrows, fireballs, snowballs, fireworks and similar entities. Read more about them [below][projectile]. +- `LivingEntity`: The base class for anything "living", in the sense of it having things like hit points, equipment, [mob effects][mobeffect] and some other properties. Includes things such as monsters, animals, villagers, and players. Read more about them in the [Living Entities article][livingentity]. +- `BlockAttachedEntity`: The base class for entities that are immobile and attached to blocks. Includes leash knots, item frames and paintings. The subclasses mainly serve the purpose of reusing common code. +- `PartEntity`: The base class for part entities, i.e. entities made up of multiple smaller entities. Vanilla currently only uses this for the Ender Dragon. +- `VehicleEntity`: The base class for boats and minecarts. While these entities loosely share the concept of hit points with `LivingEntity`s, they do not share many other properties with them and are as such kept separated. The subclasses mainly serve the purpose of reusing common code. + +There are also several entities that are direct subclasses of `Entity`, simply because there was no other fitting superclass. Most of these should be self-explanatory: + +- `AreaEffectCloud` (lingering potion clouds) +- `EndCrystal` +- `EvokerFangs` +- `ExperienceOrb` +- `EyeOfEnder` +- `FallingBlockEntity` (falling sand, gravel etc.) +- `ItemEntity` (dropped items) +- `LightningBolt` +- `OminousItemSpawner` (for continuously spawning the loot of trial spawners) +- `PrimedTnt` + +Not included in this diagram and list are the mapmaker entities (displays, interactions and markers). + +### Projectiles + +Projectiles are a subgroup of entities. Common to them is that they fly in one direction until they hit something, and that they have an owner assigned to them (e.g. a player or a skeleton would be the owner of an arrow, or a ghast would be the owner of a fireball). + +The class hierarchy of projectiles looks as follows (red classes are `abstract`, blue classes are not): + +```mermaid +graph LR; + Projectile-->AbstractArrow; + AbstractArrow-->Arrow; + AbstractArrow-->SpectralArrow; + AbstractArrow-->ThrownTrident; + Projectile-->AbstractHurtingProjectile; + AbstractHurtingProjectile-->AbstractWindCharge; + AbstractWindCharge-->BreezeWindCharge; + AbstractWindCharge-->WindCharge; + AbstractHurtingProjectile-->DragonFireball; + AbstractHurtingProjectile-->Fireball; + Fireball-->LargeFireball; + Fireball-->SmallFireball; + AbstractHurtingProjectile-->WitherSkull; + Projectile-->FireworkRocketEntity; + Projectile-->FishingHook; + Projectile-->LlamaSpit; + Projectile-->ShulkerBullet; + Projectile-->ThrowableProjectile; + ThrowableProjectile-->ThrowableItemProjectile; + ThrowableItemProjectile-->Snowball; + ThrowableItemProjectile-->ThrownEgg; + ThrowableItemProjectile-->ThrownEnderpearl; + ThrowableItemProjectile-->ThrownExperienceBottle; + ThrowableItemProjectile-->ThrownPotion; + + class Projectile,AbstractArrow,AbstractHurtingProjectile,AbstractWindCharge,Fireball,ThrowableProjectile,ThrowableItemProjectile red; + class Arrow,SpectralArrow,ThrownTrident,BreezeWindCharge,WindCharge,DragonFireball,LargeFireball,SmallFireball,WitherSkull,FireworkRocketEntity,FishingHook,LlamaSpit,ShulkerBullet,Snowball,ThrownEgg,ThrownEnderpearl,ThrownExperienceBottle,ThrownPotion blue; +``` + +Of note are the three direct abstract subclasses of `Projectile`: + +- `AbstractArrow`: This class covers the different kinds of arrows, as well as the trident. An important common property is that they will not fly straight, but are affected by gravity. +- `AbstractHurtingProjectile`: This class covers wind charges, various fireballs, and wither skulls. These are damaging projectiles unaffected by gravity. +- `ThrowableProjectile`: This class covers things like eggs, snowballs and ender pearls. Like arrows, they are affected by gravity, but unlike arrows, they will not inflict damage upon hitting the target. They are also all spawned by using the corresponding [item]. + +A new projectile can be created by extending `Projectile` or a fitting subclass, and then overriding the methods required for adding your functionality. Common methods to override include: + +- `#shoot`: Calculates and sets the correct velocity on the projectile. +- `#onHit`: Called when something is hit. + - `#onHitEntity`: Called when that something is an [entity]. + - `#onHitBlock`: Called when that something is a [block]. +- `#getOwner` and `#setOwner`, which get and set the owning entity, respectively. +- `#deflect`, which deflects the projectile based on the passed `ProjectileDeflection` enum value. +- `#onDeflection`, which is called from `#deflect` for any post-deflection behavior. + +[block]: ../blocks/index.md +[damageevents]: livingentity.md#damage-events +[damagesource]: ../resources/server/damagetypes.md#creating-and-using-damage-sources +[damaging]: #damaging-entities +[data]: data.md +[dataattachments]: ../datastorage/attachments.md +[entity]: #the-entity-class +[extenum]: ../advanced/extensibleenums.md +[hierarchy]: #entity-class-hierarchy +[hitresult]: ../items/interactions.md#hitresults +[item]: ../items/index.md +[itemstack]: ../items/index.md#itemstacks +[leftclick]: ../items/interactions.md#left-clicking-an-item +[livingentity]: livingentity.md +[middleclick]: ../items/interactions.md#middle-clicking +[mobeffect]: ../items/mobeffects.md +[mobspawn]: livingentity.md#spawning +[particle]: ../resources/client/particles.md +[projectile]: #projectiles +[registration]: ../concepts/registries.md#methods-for-registering +[renderer]: renderer.md +[spawning]: #spawning-entities +[summon]: https://minecraft.wiki/w/Commands/summon +[type]: #entitytype diff --git a/docs/entities/livingentity.md b/docs/entities/livingentity.md new file mode 100644 index 00000000..34b21659 --- /dev/null +++ b/docs/entities/livingentity.md @@ -0,0 +1,275 @@ +--- +sidebar_position: 3 +--- +# Living Entities, Mobs & Players + +Living entities are a big subgroup of [entities] that all inherit from the common `LivingEntity` superclass. These include mobs (through the `Mob` subclass), players (through the `Player` subclass) and armor stands (through the `ArmorStand` subclass). + +Living entities have a number of additional properties that regular entities do not have. These include [attributes], [mob effects][mobeffects], damage tracking and more. + +## Health, Damage and Healing + +_See also: [Attributes][attributes]._ + +One of the most notable features that sets living entities apart from others is the fully-fleshed health system. Living entities generally have a max health, a current health and sometimes things such as armor or natural regeneration. + +By default, max health is determined by the `minecraft:generic.max_health` [attribute][attributes], and the current health is set to the same value when [spawning]. When the entity is damaged by calling [`Entity#hurt`][hurt] on it, the current health is decreased according to the damage calculations. Many entities, such as zombies, will by default then remain at that reduced health value, while some, such as players, can heal these lost hit points again. + +To get or set the max health value, the attribute is read or written directly, like so: + +```java +// Get the attribute map of our entity. +AttributeMap attributes = entity.getAttributes(); + +// Get the max health of our entity. +float maxHealth = attributes.getValue(Attributes.MAX_HEALTH); +// Shortcut for the above. +maxHealth = entity.getMaxHealth(); + +// Setting the max health must either be done by getting the AttributeInstance and calling #setBaseValue, or by +// adding an attribute modifier. We will do the former here. Please refer to the Attributes article for more details. +attributes.getInstance(Attributes.MAX_HEALTH).setBaseValue(50); +``` + +When [taking damage][damage], living entities will apply some additional calculations, such as considering the `minecraft:generic.armor` attribute (except for [damage types][damagetypes] that are in the `minecraft:bypasses_armor` [tag][tags]) as well as the `minecraft:generic.absorption` attribute. Living entities can also override `#onDamageTaken` to perform post-attack behavior; it is only called if the final damage value is greater than zero. + +### Damage Events + +Due to the complexity of the damage pipeline, there are multiple events for you to hook into, which are fired in the order they are listed in. This is generally intended for damage modifications you want to do to entities that are not (or not necessarily) your own, i.e. if you want to modify damage done to entities from Minecraft or other mods, or if you want to modify damage done to any entity, which may or may not be your own. + +Common to all these events is the `DamageContainer`. A new `DamageContainer` is instantiated at the start of the attack, and discarded after the attack has finished. It contains the original [`DamageSource`][damagesources], the original damage amount, and a list of all individual modifications - armor, absorption, [enchantments], [mob effects][mobeffects], etc. The `DamageContainer` is passed to all events listed below, and you can check what modifications have already been done to make your own changes as necessary. + +#### `EntityInvulnerabilityCheckEvent` + +This event allows mods to both bypass and add invulnerabilities for an entity. This event is also fired for non-living entities. You would use this event to make an entity immune to an attack, or strip away an existing immunity it may have. + +For technical reasons, hooks to this event should be deterministic and only depend on the damage type. This means that random chances for invulnerabilities, or invulnerabilities that only apply up to a certain damage amount, should instead be added in `LivingIncomingDamageEvent` (see below). + +#### `LivingIncomingDamageEvent` + +This event is called only on the server side and should be used for two main use cases: dynamically cancelling the attack, and adding reduction modifier callbacks. + +Dynamically cancelling attacks is basically adding a non-deterministic invulnerability, for example a random chance to cancel damage, an invulnerability depending on the time of day or the amount of damage taken, etc. Consistent invulnerabilities should be performed via `EntityInvulnerabilityCheckEvent` (see above). + +Reduction modifier callbacks allow you to modify a part of the performed damage reduction. For example, it would allow you to reduce the effect of armor damage reduction by 50%. This would then also propagate correctly to mob effects, which then have a different damage amount to work with, etc. A reduction modifier callback can be added like so: + +```java +@SubscribeEvent +public static void decreaseArmor(LivingIncomingDamageEvent event) { + // We only apply this decrease to players and leave zombies etc. unchanged + if (event.getEntity() instanceof Player) { + // Add our reduction modifier callback. + event.getDamageContainer().addModifier( + // The reduction to target. See the DamageContainer.Reduction enum for possible values. + DamageContainer.Reduction.ARMOR, + // The modification to perform. Gets the damage container and the base reduction as inputs, + // and outputs the new reduction. Both input and output reductions are floats. + (container, baseReduction) -> baseReduction * 0.5f + ); + } +} +``` + +Callbacks are applied in the order they are added. This means that callbacks added in an event handler with higher [priority] will be run first. + +#### `LivingShieldBlockEvent` + +This event can be used to fully customize shield blocking. This includes introducing additional shield blocking, preventing shield blocks, modifying the vanilla shield block check, changing the damage done to the shield or the attacking item, changing the view arc of the shield, allowing projectiles but blocking melee attacks (or vice versa), block attacks passively (i.e. without using the shield), block only a percentage of damage, etc. + +Note that this event is not designed for immunities or attack cancellations that are outside the scope of "shield-like" items. + +#### `ArmorHurtEvent` + +This event should be pretty self-explanatory. It is fired when armor damage from an attack is calculated, and can be used to modify how much durability damage (if any at all) is done to which armor piece. + +#### `LivingDamageEvent.Pre` + +This event is called immediately before the damage is done. The `DamageContainer` is fully populated, the final damage amount is available, and the event can no longer be canceled as the attack is considered successful by this point. + +At this point, all kinds of modifiers are available, allowing you to finely modify the damage amount. Be aware that things like armor damage are already done by this point. + +#### `LivingDamageEvent.Post` + +This event is called after the damage has been done, absorption has been reduced, the combat tracker has been updated, and stats and game events have been handled. It is not cancellable, as the attack has already happened. This event would commonly be used for post-attack effects. Note that the event is fired even if the damage amount is zero, so check that value accordingly if needed. + +If you are calling this on your own entity, you should consider overriding `ILivingEntityExtension#onDamageTaken()` instead. Unlike `LivingDamageEvent.Post`, this is only called if the damage is greater than zero. + +## Mob Effects + +_See [Mob Effects & Potions][mobeffects]._ + +## Equipment + +_See [Containers on Entities][containers]._ + +## Hierarchy + +Living entities have a complex class hierarchy. As mentioned before, there are three direct subclasses (red classes are `abstract`, blue classes are not): + +```mermaid +graph LR; + LivingEntity-->ArmorStand; + LivingEntity-->Mob; + LivingEntity-->Player; + + class LivingEntity,Mob,Player red; + class ArmorStand blue; +``` + +Of these, `ArmorStand` has no subclasses (and is also the only non-abstract class), so we will focus on the class hierarchy of `Mob` and `Player`. + +### Hierarchy of `Mob` + +The class hierarchy of `Mob` looks as follows (red classes are `abstract`, blue classes are not): + +```mermaid +graph LR; + Mob-->AmbientCreature; + AmbientCreature-->Bat; + Mob-->EnderDragon; + Mob-->FlyingMob; + FlyingMob-->Ghast; + FlyingMob-->Phantom; + Mob-->PathfinderMob; + PathfinderMob-->AbstractGolem; + AbstractGolem-->IronGolem; + AbstractGolem-->Shulker; + AbstractGolem-->SnowGolem; + PathfinderMob-->AgeableMob; + AgeableMob-->AbstractVillager; + AbstractVillager-->Villager; + AbstractVillager-->WanderingTrader; + AgeableMob-->AgeableWaterCreature; + AgeableWaterCreature-->Dolphin; + AgeableWaterCreature-->Squid; + Squid-->GlowSquid; + AgeableMob-->Animal; + PathfinderMob-->Allay; + PathfinderMob-->Monster; + PathfinderMob-->WaterAnimal; + WaterAnimal-->AbstractFish; + AbstractFish-->AbstractSchoolingFish; + AbstractSchoolingFish-->Cod; + AbstractSchoolingFish-->Salmon; + AbstractSchoolingFish-->TropicalFish; + AbstractFish-->Pufferfish; + AbstractFish-->Tadpole; + Mob-->Slime; + Slime-->MagmaCube; + + class Mob,AmbientCreature,FlyingMob,PathfinderMob,AbstractGolem,AgeableMob,AbstractVillager,AgeableWaterCreature,Animal,Monster,WaterAnimal,AbstractFish,AbstractSchoolingFish red; + class Bat,EnderDragon,Ghast,Phantom,IronGolem,Shulker,SnowGolem,Villager,WanderingTrader,Dolphin,Squid,GlowSquid,Allay,Cod,Salmon,TropicalFish,Pufferfish,Tadpole,Slime,MagmaCube blue; +``` + +All other living entities missing from the diagram are subclasses of either `Animal` or `Monster`. + +As you may have noticed, this is very messy. For example, why aren't bees, parrots etc. also flying mobs? This problem becomes even worse when looking into the subclass hierarchy of `Animal` and `Monster`, which will not be discussed here in detail (look them up using your IDE's Show Hierarchy feature if you're interested). It is best to acknowledge it, but not worry about it. + +Let's go over the most important classes: + +- `PathfinderMob`: Contains (surprise!) logic for pathfinding. +- `AgeableMob`: Contains the logic for aging and baby entities. Zombies and other monsters with baby variants do not extend this class, they instead are children of `Monster`. +- `Animal`: What most animals extend. Has further abstract subclasses, such as `AbstractHorse` or `TamableAnimal`. +- `Monster`: The abstract class for most entities the game considers monsters. Like `Animal`, this has further abstract subclasses, such as `AbstractPiglin`, `AbstractSkeleton`, `Raider`, and `Zombie`. +- `WaterAnimal`: The abstract class for water-based animals, such as fish, squids and dolphins. These are kept separate from the other animals due to significantly different pathfinding. + +### Hierarchy of `Player` + +Depending on which side the player is on, a different player class is used. You should never need to construct a player yourself, except for `FakePlayer`s. + +```mermaid +graph LR; + Player-->AbstractClientPlayer; + AbstractClientPlayer-->LocalPlayer; + AbstractClientPlayer-->RemotePlayer; + Player-->ServerPlayer; + ServerPlayer-->FakePlayer; + + class Player,AbstractClientPlayer red; + class LocalPlayer,RemotePlayer,ServerPlayer,FakePlayer blue; +``` + +- `AbstractClientPlayer`: This class is used as a base for the two client players, both used to represent players on the [logical client][logicalsides]. +- `LocalPlayer`: This class is used to represent the player currently running the game. +- `RemotePlayer`: This class is used to represent other players that the `LocalPlayer` may encounter during multiplayer. As such, `RemotePlayer`s do not exist in singleplayer contexts. +- `ServerPlayer`: This class is used to represent players on the [logical server][logicalsides]. +- `FakePlayer`: This is a special subclass of `ServerPlayer` designed to be used as a mock for a player, for non-player mechanisms that need a player context. + +## Spawning + +In addition to the [regular ways of spawning][spawning] - that is, the `/summon` command and the in-code way via `Level#addFreshEntity` -, `Mob`s can also be spawned through some other means. `ArmorStand`s can be spawned through regular means, and `Player`s should not be instantiated yourself, except for `FakePlayer`s. + +### Spawn Eggs + +It is common (though not required) to [register] a spawn egg for mobs. This is done through the `SpawnEggItem` class, which has been patched by NeoForge to do some extra setup, such as registering the color handler and adding the spawn egg to the internal `SpawnEggItem` -> `EntityType` map. + +```java +// Assume we have a DeferredRegister.Items called ITEMS +DeferredItem MY_ENTITY_SPAWN_EGG = ITEMS.registerItem("my_entity_spawn_egg", + properties -> new SpawnEggItem( + // The entity type to spawn. + MY_ENTITY_TYPE.get(), + // The properties passed into the lambda, with any additional setup. + properties + )); +``` + +As an item like any other, the item should be added to a [creative tab][creative], and a [client item][clientitem], [model] and [translation] should be added. + +### Natural Spawning + +_See also [Entities/`MobCategory`][mobcategory], [Worldgen/Biome Modifers/Add Spawns][addspawns], [Worldgen/Biome Modifers/Add Spawn Costs][addspawncosts]; and [Spawn Cycle][spawncycle] on the [Minecraft Wiki][mcwiki]._ + +Natural spawning is performed every tick for entities where `MobCategory#isFriendly()` is true, and every 400 ticks (\= 20 seconds) for entities where `MobCategory#isFriendly()` is false. If `MobCategory#isPersistent()` returns true, this process additionally also happens on chunk generation. + +For each chunk and mob category, it is checked whether there are less than `MobCategory#getMaxInstancesPerChunk() * loadedChunks / 289` in the world. Additionally, for each chunk, it is required that there are less than `MobCategory#getMaxInstancesPerChunk()` entities of that `MobCategory` near at least one player (near means that the distance between mob and player \<\= 128) for spawning of that `MobCategory` to occur. + +If the conditions are met, an entry is randomly chosen from the relevant biome's spawn data, and spawning occurs if a suitable position can be found. There are at most three attempts to find a random position; if no position can be found, no spawning will occur. + +## AI and Navigation + +One of the `Mob` subclass's main features is the AI system, as this is of course not applicable to players or armor stands. It tells the mob where to go, what to do and whom to attack. + +There are two implementations of this system: the goal system and the brain system. Both of these systems then use a subclass of `PathNavigation` to calculate the path to their desired position. + +### Goals + +:::info +This section is a work in progress. +::: + +### Brains + +:::info +This section is a work in progress. +::: + +### Navigation + +:::info +This section is a work in progress. +::: + +[addspawncosts]: ../worldgen/biomemodifier.md#add-spawn-costs +[addspawns]: ../worldgen/biomemodifier.md#add-spawns +[attributes]: attributes.md +[clientitem]: ../resources/client/models/items.md +[containers]: ../blockentities/container.md +[creative]: ../items/index.md#creative-tabs +[damage]: index.md#damaging-entities +[damagesources]: ../resources/server/damagetypes.md#creating-and-using-damage-sources +[damagetypes]: ../resources/server/damagetypes.md +[enchantments]: ../resources/server/enchantments/index.md +[entities]: index.md +[hurt]: index.md#damaging-entities +[logicalsides]: ../concepts/sides.md#the-logical-side +[mcwiki]: https://minecraft.wiki +[mobcategory]: index.md#mobcategory +[mobeffects]: ../items/mobeffects.md +[model]: ../resources/client/models/index.md +[priority]: ../concepts/events.md#priority +[register]: ../concepts/registries.md +[spawncycle]: https://minecraft.wiki/w/Mob_spawning#Spawn_cycle +[spawning]: index.md#spawning-entities +[tags]: ../resources/server/tags.md +[translation]: ../resources/client/i18n.md diff --git a/docs/entities/renderer.md b/docs/entities/renderer.md new file mode 100644 index 00000000..631772b3 --- /dev/null +++ b/docs/entities/renderer.md @@ -0,0 +1,227 @@ +--- +sidebar_position: 5 +--- +# Entity Renderers + +Entity renderers are used to define rendering behavior for an entity. They only exist on the [logical and physical client][sides]. + +Entity rendering uses what is known as entity render states. Simply put, this is an object that holds all values that the renderer needs. Every time the entity is rendered, the render state is updated, and then the `#render` method uses that render state to render the entity. This is probably intended to be adapted into a deferred rendering system at some point, where the render information is collected beforehand (which could potentially be multithreaded) and rendering then happens at a later point in time. + +## Creating an Entity Renderer + +The simplest entity renderer is one that directly extends `EntityRenderer`: + +```java +// The generic type in the superclass should be set to what entity you want to render. +// If you wanted to enable rendering for any entity, you'd use Entity, like we do here. +// You'd also use an EntityRenderState that fits your use case. More on this below. +public class MyEntityRenderer extends EntityRenderer { + // In our constructor, we just forward to super. + public MyEntityRenderer(EntityRendererProvider.Context context) { + super(context); + } + + // Tell the render engine how to create a new entity render state. + public EntityRenderState createRenderState() { + return new EntityRenderState(); + } + + // Update the render state by copying the needed values from the passed entity to the passed state. + // Both Entity and EntityRenderState may be replaced with more concrete types, + // based on the generic types that have been passed to the supertype. + public void extractRenderState(Entity entity, EntityRenderState state, float partialTick) { + super.extractRenderState(entity, state, partialTick); + // Extract and store any additional values in the state here. + } + + // Actually render the entity. The first parameter matches the render state's generic type. + // Calling super will handle leash and name tag rendering for you, if applicable. + public void render(EntityRenderState state, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) { + super.render(state, entityYaw, partialTick, poseStack, bufferSource, packedLight); + // do your own rendering here + } +} +``` + +Now that we have our entity renderer, we also need to register it and connect it to its owning entity. This is done in [`EntityRenderersEvent.RegisterRenderers`][events] like so: + +```java +@SubscribeEvent +public static void registerEntityRenderers(EntityRenderersEvent.RegisterRenderers event) { + event.registerEntityRenderer(MY_ENTITY_TYPE.get(), MyEntityRenderer::new); +} +``` + +## Entity Render States + +As mentioned before, entity render states are used to separate values used for rendering from the actual entity's values. There's nothing more to them, they are really just mutable data storage objects. As such, extending is really easy: + +```java +public class MyEntityRenderState extends EntityRenderState { + public ItemStack stackInHand; +} +``` + +That's literally it. Extend the class, add your field, and off you go. The only thing left to do now is to update that `stackInHand` field in `EntityRenderer#extractRenderState`, as explained above. + +## Hierarchy + +Like entities themselves, entity renderers have a class hierarchy, though not as layered. The most important classes of the hierarchy are related like this (red classes are `abstract`, blue classes are not): + +```mermaid +graph LR; + EntityRenderer-->AbstractBoatRenderer; + EntityRenderer-->AbstractMinecartRenderer; + EntityRenderer-->ArrowRenderer; + EntityRenderer-->LivingEntityRenderer; + LivingEntityRenderer-->ArmorStandRenderer; + LivingEntityRenderer-->MobRenderer; + MobRenderer-->AgeableRenderer; + AgeableRenderer-->HumanoidMobRenderer; + LivingEntityRenderer-->PlayerRenderer; + + class EntityRenderer,AbstractBoatRenderer,AbstractMinecartRenderer,ArrowRenderer,LivingEntityRenderer,MobRenderer,AgeableRenderer,HumanoidMobRenderer red; + class ArmorStandRenderer,PlayerRenderer blue; +``` + +- `EntityRenderer`: The abstract base class. Many renderers, notably almost all renderers for non-living entities, extend this class directly. +- `ArrowRenderer`, `AbstractBoatRenderer`, `AbstractMinecartRenderer`: These exist mainly for convenience, and are used as parents for more specific renderers. +- `LivingEntityRenderer`: The abstract base class for renderers for [living entities][livingentity]. Direct subclasses include `ArmorStandRenderer` and `PlayerRenderer`. +- `ArmorStandRenderer`: Self-explanatory. +- `PlayerRenderer`: Used to render players. Note that unlike most other renderers, multiple instances of this class used for different contexts may exist at the same time. +- `MobRenderer`: The abstract base class for renderers for `Mob`s. Many renderers extend this directly. +- `AgeableRenderer`: The abstract base class for renderers for `Mob`s that have child variants. This includes monsters with child variants, such as hoglins. +- `HumanoidMobRenderer`: The abstract base class for humanoid entity renderers. Used by e.g. zombies and skeletons. + +As with the various entity classes, use what fits your use case most. Be aware that many of these classes have corresponding type bounds in their generics; for example, `LivingEntityRenderer` has type bounds for `LivingEntity` and `LivingEntityRenderState`. + +## Entity Models and Layer Definitions + +Many renderers, especially the `LivingEntityRenderer` and its subclasses, make use of `EntityModel`s. `EntityModel`s are basically a list of cubes and associated textures for the renderer to use. They are commonly created statically when the entity renderer's constructor is first created. + +Entity models use a layer system, where each layer is represented as a `LayerDefinition`. A renderer can use multiple layers, and the renderer can decide what layer(s) to render at what time. For example, the elytra uses a separate layer that is rendered independently of the `LivingEntity` wearing it. Similarly, player capes are also a separate layer. + +### Creating a Layer Definition + +With all that out of the way, let's create an entity model ourselves: + +```java +// You may use a more specific subtype of EntityRenderState in the generic. +// If you do, all uses of EntityRenderState within the class will change to that more specific subtype. +public class MyEntityModel extends EntityModel { + // A static method in which we create our layer definition. createBodyLayer() is the name + // most vanilla models use. If you have multiple layers, you will have multiple of these static methods. + public static LayerDefinition createBodyLayer() { + // Create our mesh. + MeshDefinition mesh = new MeshDefinition(); + // The mesh initially contains no object other than the root, which is invisible (has a size of 0x0x0). + PartDefinition root = mesh.getRoot(); + // We add a head part. + PartDefinition head = root.addOrReplaceChild( + // The name of the part. + "head", + // The CubeListBuilder we want to add. While it is possible to add multiple cubes into one part, + // it is generally discouraged and only one cube per PartDefinition should be used. + CubeListBuilder.create() + // The UV coordinates to use within the texture. Texture binding itself is explained below. + // In this example, we start at U=10, V=20. + .texOffs(10, 20) + // Add our cube. Again, while multiple can be added, it is recommended to only add one. + .addBox( + // The origin of the cube, relative to the parent object's position. + -5, -5, -5, + // The size of the cube. + 10, 10, 10 + ), + // An additional offset to apply to all elements of the CubeListBuilder. Besides PartPose#offset, + // PartPose#offsetAndRotation is also available. This can be reused across multiple PartDefinitions. + PartPose.offset(0, 8, 0) + ); + // We can now add children to any PartDefinition, thus creating a hierarchy. + PartDefinition part1 = root.addOrReplaceChild(...); + PartDefinition part2 = head.addOrReplaceChild(...); + PartDefinition part3 = part1.addOrReplaceChild(...); + // At the end, we create a LayerDefinition from the MeshDefinition. + // The two integers are the expected dimensions of the texture; 64x32 in our example. + return LayerDefinition.create(mesh, 64, 32); + } +} +``` + +Note that in the above example, we directly extend `EntityModel`; depending on your use case, it might be more appropriate to use one of the subclasses instead. When creating a new model, it is recommended you have a look at whatever existing model is closest to your use case, and then work from there. + +:::tip +The [Blockbench][blockbench] modeling program is a great help in creating entity models. To do so, choose the Modded Entity option when creating your model in Blockbench. + +Blockbench also has an option to export models as a `LayerDefinition` creation method, which can be found under `File -> Export -> Export Java Entity`. +::: + +### Registering a Layer Definition + +Once we have our entity layer definition, we also need to register it in `EntityRenderersEvent.RegisterLayerDefinitions`. To do so, we need a `ModelLayerLocation`, which essentially acts as an identifier for our layer (remember, one entity can have multiple layers). + +```java +// Our ModelLayerLocation. +public static final ModelLayerLocation MY_LAYER = new ModelLayerLocation( + // Should be the name of the entity this layer belongs to. + // May be more generic if this layer can be used on multiple entities. + ResourceLocation.fromNamespaceAndPath("examplemod", "example_entity"), + // The name of the layer itself. Should be main for the entity's base model, + // and a more descriptive name (e.g. "wings") for more specific layers. + "main" +); + +@SubscribeEvent +public static void registerLayerDefinitions(EntityRenderersEvent.RegisterLayerDefinitions event) { + // Add our layer here. + event.add(MY_LAYER, MyEntityModel::createBodyLayer); +} +``` + +### Adding a Layer Definition to an Entity + +In some contexts, you might also want to add a new layer to an existing entity. For example, you might want to render some extra equipment on an entity when worn. This can be done like so: + +```java +@SubscribeEvent +public static void addLayers(EntityRenderersEvent.AddLayers event) { + // Add a layer to every single entity type. + for (EntityType entityType : event.getEntityTypes()) { + // Get our renderer. + EntityRenderer renderer = event.getRenderer(entityType); + // Null-check the renderer. You could add more checks here. For example, a common check is + // `instanceof LivingEntityRenderer` to only target living entity renderers. + if (renderer != null) { + // Add the layer to the renderer. Reuses the ModelLayerLocation from above. + renderer.addLayer(MY_LAYER); + } + } +} +``` + +For players, a bit of special-casing is required because there can actually be multiple player renderers. These are managed separately by the event. We can interact with them like so: + +```java +@SubscribeEvent +public static void addPlayerLayers(EntityRenderersEvent.AddLayers event) { + // Iterate over all possible player models. + for (PlayerSkin.Model skin : event.getSkins()) { + // Get the associated PlayerRenderer. + if (event.getSkin(skin) instanceof PlayerRenderer playerRenderer) { + // Add the layer to the renderer. + playerRenderer.addLayer(MY_LAYER); + } + } +} +``` + +## Animations + +:::info +This section is a work in progress. +::: + +[blockbench]: https://www.blockbench.net/ +[events]: ../concepts/events.md +[livingentity]: livingentity.md +[sides]: ../concepts/sides.md diff --git a/docs/gui/menus.md b/docs/gui/menus.md index 417b0553..e46ae2fd 100644 --- a/docs/gui/menus.md +++ b/docs/gui/menus.md @@ -351,4 +351,4 @@ Once again, this is the simplest way to implement the logic, not the only way. [screen]: screens.md [icf]: #icontainerfactory [side]: ../concepts/sides.md#the-logical-side -[interaction]: ../items/interactionpipeline.md +[interaction]: ../items/interactions.md#right-clicking-an-item diff --git a/docs/items/armor.md b/docs/items/armor.md index dafe865f..e4b24d86 100644 --- a/docs/items/armor.md +++ b/docs/items/armor.md @@ -7,7 +7,7 @@ import TabItem from '@theme/TabItem'; # Armor -Armors are [items][item] whose primary use is to protect an entity from damage using a variety of resistances and effects. Many mods add new armor sets (for example copper armor). +Armors are [items][item] whose primary use is to protect a [`LivingEntity`][livingentity] from damage using a variety of resistances and effects. Many mods add new armor sets (for example copper armor). ## Custom Armor Sets @@ -408,8 +408,9 @@ this.equipmentLayerRenderer.renderLayers( [item]: index.md [datacomponents]: datacomponents.md [enchantment]: ../resources/server/enchantments/index.md#enchantment-costs-and-levels +[livingentity]: ../entities/livingentity.md [registering]: ../concepts/registries.md#methods-for-registering -[rendering]: #equipement-rendering +[rendering]: #equipment-rendering [respack]: ../resources/index.md#assets [tag]: ../resources/server/tags.md [tinting]: ../resources/client/models/index.md#tinting diff --git a/docs/items/consumables.md b/docs/items/consumables.md index 7c22b1ea..0b79c352 100644 --- a/docs/items/consumables.md +++ b/docs/items/consumables.md @@ -279,7 +279,7 @@ public class ConsumableClientItemExtensions implements IClientItemExtensions { ### Overriding Sounds on Entity -Sometimes, an entity may want to play a different sound while consuming an item. In those instances, the `LivingEntity` instance can implement `Consumable.OverrideConsumeSound` and have `getConsumeSound` return the `SoundEvent` they want their entity to play. +Sometimes, an entity may want to play a different sound while consuming an item. In those instances, the [`LivingEntity`][livingentity] instance can implement `Consumable.OverrideConsumeSound` and have `getConsumeSound` return the `SoundEvent` they want their entity to play. ```java public class MyEntity extends LivingEntity implements Consumable.OverrideConsumeSound { @@ -360,6 +360,7 @@ The contents of a [potion][potions] via `PotionContents` is another `ConsumableL [food]: #food [hunger]: https://minecraft.wiki/w/Hunger#Mechanics [item]: index.md +[livingentity]: ../entities/livingentity.md [modbus]: ../concepts/events.md#event-buses [mobeffectinstance]: mobeffects.md#mobeffectinstances [particles]: ../resources/client/particles.md diff --git a/docs/items/index.md b/docs/items/index.md index 6f2928c7..a435ca7f 100644 --- a/docs/items/index.md +++ b/docs/items/index.md @@ -8,7 +8,7 @@ Before we get further into creating items, it is important to understand what an - In the world, you encounter a dirt block and want to mine it. This is a **block**, because it is placed in the world. (Actually, it is not a block, but a blockstate. See the [Blockstates article][blockstates] for more detailed information.) - Not all blocks drop themselves when breaking (e.g. leaves), see the article on [loot tables][loottables] for more information. -- Once you have [mined the block][breaking], it is removed (= replaced with an air block) and the dirt drops. The dropped dirt is an item **entity**. This means that like other entities (pigs, zombies, arrows, etc.), it can inherently be moved by things like water pushing on it, or burned by fire and lava. +- Once you have [mined the block][breaking], it is removed (= replaced with an air block) and the dirt drops. The dropped dirt is an item **[entity][entity]**. This means that like other entities (pigs, zombies, arrows, etc.), it can inherently be moved by things like water pushing on it, or burned by fire and lava. - Once you pick up the dirt item entity, it becomes an **item stack** in your inventory. An item stack is, simply put, an instance of an item with some extra information, such as the stack size. - Item stacks are backed by their corresponding **item** (which is what we're creating). Items hold [data components][datacomponents] that contains the default information all items stacks are initialized to (for example, every iron sword has a max durability of 250), while item stacks can modify those data components, allowing two different stacks for the same item to have different information (for example, one iron sword has 100 uses left, while another iron sword has 200 uses left). For more information on what is done through items and what is done through item stacks, read on. - The relationship between items and item stacks is roughly the same as between [blocks][block] and [blockstates][blockstates], in that a blockstate is always backed by a block. It's not a really accurate comparison (item stacks aren't singletons, for example), but it gives a good basic idea about what the concept is here. @@ -57,7 +57,7 @@ More information can be found on their relevant pages. Directly using `Item` only allows for very basic items. If you want to add functionality, for example right-click interactions, a custom class that extends `Item` is required. The `Item` class has many methods that can be overridden to do different things; see the classes `Item` and `IItemExtension` for more information. -The two most common use cases for items are left-clicking and right-clicking. For left-clicking, see [Breaking a Block][breaking] and Attacking an Entity (WIP). For right-clicking, see [The Interaction Pipeline][interactionpipeline]. +The two most common use cases for items are left-clicking and right-clicking. Due to their complexity and their reaching into other systems, they are explained in a separate [Interaction article][interactions]. ### `DeferredRegister.Items` @@ -244,8 +244,10 @@ It is also possible to implement `ItemLike` on your custom objects. Simply overr [datacomponents]: datacomponents.md [datagen]: ../resources/index.md#data-generation [enchantment]: ../resources/server/enchantments/index.md#enchantment-costs-and-levels +[entity]: ../entities/index.md [food]: consumables.md#food -[interactionpipeline]: interactionpipeline.md +[hunger]: https://minecraft.wiki/w/Hunger#Mechanics +[interactions]: interactions.md [loottables]: ../resources/server/loottables/index.md [modbus]: ../concepts/events.md#event-buses [recipes]: ../resources/server/recipes/index.md diff --git a/docs/items/interactionpipeline.md b/docs/items/interactionpipeline.md deleted file mode 100644 index 1c3a3112..00000000 --- a/docs/items/interactionpipeline.md +++ /dev/null @@ -1,75 +0,0 @@ ---- -sidebar_position: 1 ---- -# 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, `UseItemOnBlockEvent` is fired. If the event is canceled, the cancellation result is used. Otherwise, `Block#useItemOn` is called. If it returns a definitive result, the pipeline ends. - - If the `InteractionResult` is `TRY_WITH_EMPTY_HAND` and the executing hand is the main hand, then `Block#useWithoutItem` 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. - -## `InteractionResult` - -`InteractionResult` is a sealed interface that respresents the result of some interaction between an item or an empty hand and some object (e.g. entities, blocks, etc.). The interface is broken into four records, where there are six potential default states. - -First there is `InteractionResult.Success`, which indicates that the operation should be considered sucessful, ending the pipeline. A successful state has two parameters: the `SwingSource`, which indicates whether the entity should swing on the respective [logical side][side]; and the `InteractionResult.ItemContext`, which holds whether the interaction was caused by a held item, and what the held item transformed into after use. The swing source is determined by one of the default states: `InteractionResult#SUCCESS` for client swing, `InteractionResult#SUCCESS_SERVER` for server swing, and `InteractionResult#CONSUME` for no swing. The item context is set via `Success#heldItemTransformedTo` if the `ItemStack` changed, or `withoutItem` if there wasn't an interaction between the held item and the object. The default sets there was an item interaction but no transformation. - -```java -// In some method that returns an interaction result - -// Item in hand will turn into an apple -return InteractionResult.SUCCESS.heldItemTransformedTo(new ItemStack(Items.APPLE)); -``` - -:::note -`SUCCESS` and `SUCCESS_SERVER` should generally never be used in the same method. If the client has enough information to determine when to swing, then `SUCCESS` should always be used. Otherwise, if it relies on server information not present on the client, `SUCCESS_SERVER` should be used. -::: - -Then there is `InteractionResult.Fail`, implemented by `InteractionResult#FAIL`, which indicates that the operation should be considered failed, allowing no further interaction to occur. The pipeline will end. This can be used anywhere, but it should be used with care outside of `Item#useOn` and `Item#use`. In many cases, using `InteractionResult#PASS` makes more sense. - -Finally, there is `InteractionResult.Pass` and `InteractionResult.TryWithEmptyHandInteraction`, implemented by `InteractionResult#PASS` and `InteractionResult#TRY_WITH_EMPTY_HAND` respectively. These records indicate when an operation should be considered neither successful or failed, and the pipeline should continue. `PASS` is the default behavior for all `InteractionResult` methods except `BlockBehaviour#useItemOn`, which returns `TRY_WITH_EMPTY_HAND`. More specifically, if `BlockBehaviour#useItemOn` returns anything but `TRY_WITH_EMPTY_HAND`, `BlockBehaviour#useWithoutItem` will not be called regardless of if the item is in the main hand. - -Some methods have special behavior or requirements, which are explained in the below chapters. - -## `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` and calling `#withoutItem`. - -```java -// In Item#useOn -return InteractionResult.CONSUME.withoutItem(); -``` - -## `Item#use` - -This is the only instance where the transformed `ItemStack` is used from a `Success` variant (`SUCCESS`, `SUCCESS_SERVER`, `CONSUME`). The resulting `ItemStack` set by `Success#heldItemTransformedTo` replaces the `ItemStack` the usage was initiated with, if it has changed. - -The default implementation of `Item#use` returns `InteractionResult#CONSUME` when the item is edible (has `DataComponents#CONSUMABLE`) and the player can eat the item (because they are hungry, or because the item is always edible) and `InteractionResult#FAIL` when the item is edible (has `DataComponents#CONSUMABLE`) but the player cannot eat the item. If the item is equippable (has `DataComponents#EQUIPPABLE`), then it returns `InteractionResult#SUCCESS` on swap with the held item replaced by the swaped item (via `heldItemTransformedTo`), or `InteractionResult#FAIL` if the enchantment on the armor has the `EnchantmentEffectComponents#PREVENT_ARMOR_CHANGE` component. Otherwise `InteractionResult#PASS` is returned. - -Returning `InteractionResult#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 `InteractionResult#PASS` instead. - -[itemuseon]: #itemuseon -[side]: ../concepts/sides.md#the-logical-side diff --git a/docs/items/interactions.md b/docs/items/interactions.md new file mode 100644 index 00000000..bca11538 --- /dev/null +++ b/docs/items/interactions.md @@ -0,0 +1,143 @@ +--- +sidebar_position: 1 +--- +# Interactions + +This page aims to make the fairly complex and confusing process of things being left-clicked, right-clicked or middle-clicked by the player more understandable, as well as clarifying what result to use where and why. + +## `HitResult`s + +For the purpose of determining what the player is currently looking at, Minecraft uses a `HitResult`. A `HitResult` is somewhat equivalent to a ray cast result in other game engines, and most notably contains a method `#getLocation`. + +A hit result can be of one of three types, represented through the `HitResult.Type` enum: `BLOCK`, `ENTITY`, or `MISS`. A `HitResult` of type `BLOCK` can be cast to `BlockHitResult`, while a `HitResult` of type `ENTITY` can be cast to `EntityHitResult`; both types provide additional context about what [block] or [entity] was hit. If the type is `MISS`, this indicates that neither a block nor an entity was hit, and should not be cast to either subclass. + +Every frame on the [physical client][physicalside], the `Minecraft` class updates and stores the currently looked-at `HitResult` in the `hitResult` field. This field can then be accessed through `Minecraft.getInstance().hitResult`. + +## Left-Clicking an Item + +- It is checked that all required [feature flags][featureflag] for the [`ItemStack`][itemstack] in your main hand are enabled. If this check fails, the pipeline ends. +- `InputEvent.InteractionKeyMappingTriggered` is fired with the left mouse button and the main hand. If the [event][event] is [canceled][cancel], the pipeline ends. +- Depending on what you are looking at (using the [`HitResult`][hitresult] in `Minecraft`), different things happen: + - If you are looking at an [entity] that is within your reach: + - `AttackEntityEvent` is fired. If the event is canceled, the pipeline ends. + - `IItemExtension#onLeftClickEntity` is called. If it returns true, the pipeline ends. + - `Entity#isAttackable` is called on the target. If it returns false, the pipeline ends. + - `Entity#skipAttackInteraction` is called on the target. If it returns true, the pipeline ends. + - If the target is in the `minecraft:redirectable_projectile` tag (by default this is fireballs and wind charges) and an instance of `Projectile`, the target is deflected and the pipeline ends. + - Entity base damage (the value of the `minecraft:generic.attack_damage` [attribute]) and enchantment bonus damage are calculated as two separate floats. If both are 0, the pipeline ends. + - Note that this excludes [attribute modifiers][attributemodifier] from the main hand item, these are added after the check. + - `minecraft:generic.attack_damage` attribute modifiers from the main hand item are added to the base damage. + - `CriticalHitEvent` is fired. If the event's `#isCriticalHit` method returns true, the base damage is multiplied with the value returned from the event's `#getDamageMultiplier` method, which defaults to 1.5 if [a number of conditions][critical] pass and 1.0 otherwise, but may be modified by the event. + - Enchantment bonus damage is added to the base damage, resulting in the final damage value. + - [`Entity#hurt`][hurt] is called. If it returns false, the pipeline ends. + - If the target is an instance of `LivingEntity`, `LivingEntity#knockback` is called. + - Within that method, `LivingKnockBackEvent` is fired. + - If the attack cooldown is > 90%, the attack is not a critical hit, the player is on the ground and not moving faster than their `minecraft:generic.movement_speed` attribute value, a sweep attack is performed on nearby `LivingEntity`s. + - Within that method, `LivingEntity#knockback` is called again, which in turn fires `LivingKnockBackEvent` a second time. + - `Item#hurtEnemy` is called. This can be used for post-attack effects. For example, the mace launches the player back in the air here, if applicable. + - `Item#postHurtEnemy` is called. Durability damage is applied here. + - If you are looking at a [block] that is within your reach: + - The [block breaking sub-pipeline][blockbreak] is initiated. + - Otherwise: + - `PlayerInteractEvent.LeftClickEmpty` is fired. + +## Right-Clicking an Item + +During the right-clicking pipeline, 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][event] is [canceled][cancel], the pipeline ends. +- Several circumstances are checked, for example that you are not in spectator mode or that all required [feature flags][featureflag] for the [`ItemStack`][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 (using the [`HitResult`][hitresult] in `Minecraft`), 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][livingentity], 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, `UseItemOnBlockEvent` is fired. If the event is canceled, the cancellation result is used. Otherwise, `Block#useItemOn` is called. If it returns a definitive result, the pipeline ends. + - If the `InteractionResult` is `TRY_WITH_EMPTY_HAND` and the executing hand is the main hand, then `Block#useWithoutItem` 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. + +### `InteractionResult` + +`InteractionResult` is a sealed interface that respresents the result of some interaction between an item or an empty hand and some object (e.g. entities, blocks, etc.). The interface is broken into four records, where there are six potential default states. + +First there is `InteractionResult.Success`, which indicates that the operation should be considered sucessful, ending the pipeline. A successful state has two parameters: the `SwingSource`, which indicates whether the entity should swing on the respective [logical side][side]; and the `InteractionResult.ItemContext`, which holds whether the interaction was caused by a held item, and what the held item transformed into after use. The swing source is determined by one of the default states: `InteractionResult#SUCCESS` for client swing, `InteractionResult#SUCCESS_SERVER` for server swing, and `InteractionResult#CONSUME` for no swing. The item context is set via `Success#heldItemTransformedTo` if the `ItemStack` changed, or `withoutItem` if there wasn't an interaction between the held item and the object. The default sets there was an item interaction but no transformation. + +```java +// In some method that returns an interaction result + +// Item in hand will turn into an apple +return InteractionResult.SUCCESS.heldItemTransformedTo(new ItemStack(Items.APPLE)); +``` + +:::note +`SUCCESS` and `SUCCESS_SERVER` should generally never be used in the same method. If the client has enough information to determine when to swing, then `SUCCESS` should always be used. Otherwise, if it relies on server information not present on the client, `SUCCESS_SERVER` should be used. +::: + +Then there is `InteractionResult.Fail`, implemented by `InteractionResult#FAIL`, which indicates that the operation should be considered failed, allowing no further interaction to occur. The pipeline will end. This can be used anywhere, but it should be used with care outside of `Item#useOn` and `Item#use`. In many cases, using `InteractionResult#PASS` makes more sense. + +Finally, there is `InteractionResult.Pass` and `InteractionResult.TryWithEmptyHandInteraction`, implemented by `InteractionResult#PASS` and `InteractionResult#TRY_WITH_EMPTY_HAND` respectively. These records indicate when an operation should be considered neither successful or failed, and the pipeline should continue. `PASS` is the default behavior for all `InteractionResult` methods except `BlockBehaviour#useItemOn`, which returns `TRY_WITH_EMPTY_HAND`. More specifically, if `BlockBehaviour#useItemOn` returns anything but `TRY_WITH_EMPTY_HAND`, `BlockBehaviour#useWithoutItem` will not be called regardless of if the item is in the main hand. + +Some methods have special behavior or requirements, which are explained in the below chapters. + +#### `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` and calling `#withoutItem`. + +```java +// In Item#useOn +return InteractionResult.CONSUME.withoutItem(); +``` + +#### `Item#use` + +This is the only instance where the transformed `ItemStack` is used from a `Success` variant (`SUCCESS`, `SUCCESS_SERVER`, `CONSUME`). The resulting `ItemStack` set by `Success#heldItemTransformedTo` replaces the `ItemStack` the usage was initiated with, if it has changed. + +The default implementation of `Item#use` returns `InteractionResult#CONSUME` when the item is edible (has `DataComponents#CONSUMABLE`) and the player can eat the item (because they are hungry, or because the item is always edible) and `InteractionResult#FAIL` when the item is edible (has `DataComponents#CONSUMABLE`) but the player cannot eat the item. If the item is equippable (has `DataComponents#EQUIPPABLE`), then it returns `InteractionResult#SUCCESS` on swap with the held item replaced by the swaped item (via `heldItemTransformedTo`), or `InteractionResult#FAIL` if the enchantment on the armor has the `EnchantmentEffectComponents#PREVENT_ARMOR_CHANGE` component. Otherwise `InteractionResult#PASS` is returned. + +Returning `InteractionResult#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 `InteractionResult#PASS` instead. + +## Middle-Clicking + +- If the [`HitResult`][hitresult] in `Minecraft.getInstance().hitResult` is null or of type `MISS`, the pipeline ends. +- `InputEvent.InteractionKeyMappingTriggered` is fired with the left mouse button and the main hand. If the [event][event] is [canceled][cancel], the pipeline ends. +- Depending on what you are looking at (using the `HitResult` in `Minecraft.getInstance().hitResult`), different things happen: + - If you are looking at an [entity] that is within your reach: + - If `Entity#isPickable` returns false, the pipeline ends. + - If you are not in creative, the pipeline ends. + - `IEntityExtension#getPickedResult` is called. The resulting `ItemStack` is added to the player's inventory. + - By default, this method forwards to `Entity#getPickResult`, which can be overridden by modders. + - If you are looking at a [block] that is within your reach: + - `Block#getCloneItemStack` is called and becomes the "selected" `ItemStack`. + - By default, this returns the `Item` representation of the `Block`. + - If the Control key is held down, the player is in creative and the targeted block has a [`BlockEntity`][blockentity], the `BlockEntity`'s data is added to the "selected" `ItemStack`. + - If the player is in creative, the "selected" `ItemStack` is added to the player's inventory. Otherwise, a hotbar slot that matches the "selected" item is set active, if such a hotbar slot exists. + +[attribute]: ../entities/attributes.md +[attributemodifier]: ../entities/attributes.md#attribute-modifiers +[block]: ../blocks/index.md +[blockbreak]: ../blocks/index.md#breaking-a-block +[blockentity]: ../blockentities/index.md +[cancel]: ../concepts/events.md#cancellable-events +[critical]: https://minecraft.wiki/w/Damage#Critical_hit +[effect]: mobeffects.md +[entity]: ../entities/index.md +[event]: ../concepts/events.md +[featureflag]: ../advanced/featureflags.md +[hitresult]: #hitresults +[hurt]: ../entities/index.md#damaging-entities +[itemstack]: index.md#itemstacks +[itemuseon]: #itemuseon +[livingentity]: ../entities/livingentity.md +[physicalside]: ../concepts/sides.md#the-physical-side +[side]: ../concepts/sides.md#the-logical-side diff --git a/docs/items/mobeffects.md b/docs/items/mobeffects.md index 7a6e7634..c26f5b32 100644 --- a/docs/items/mobeffects.md +++ b/docs/items/mobeffects.md @@ -3,7 +3,7 @@ sidebar_position: 6 --- # Mob Effects & Potions -Status effects, sometimes known as potion effects and referred to in-code as `MobEffect`s, are effects that influence an entity every tick. This article explains how to use them, what the difference between an effect and a potion is, and how to add your own. +Status effects, sometimes known as potion effects and referred to in-code as `MobEffect`s, are effects that influence a [`LivingEntity`][livingentity] every tick. This article explains how to use them, what the difference between an effect and a potion is, and how to add your own. ## Terminology @@ -65,7 +65,7 @@ public static final Holder MY_MOB_EFFECT = MOB_EFFECTS.register("my_m )); ``` -The `MobEffect` class also provides default functionality for adding attribute modifiers to affected entities. For example, the speed effect adds an attribute modifier for movement speed. Effect attribute modifiers are added like so: +The `MobEffect` class also provides default functionality for adding [attribute modifiers][attributemodifier] to affected entities, and also removing them when the effect expires or is removed through other means. For example, the speed effect adds an attribute modifier for movement speed. Effect attribute modifiers are added like so: ```java public static final Holder MY_MOB_EFFECT = MOB_EFFECTS.register("my_mob_effect", () -> new MyMobEffect(...) @@ -194,11 +194,13 @@ public static void registerBrewingRecipes(RegisterBrewingRecipesEvent event) { } ``` +[attributemodifier]: ../entities/attributes.md#attribute-modifiers [block]: ../blocks/index.md [commonsetup]: ../concepts/events.md#event-buses [datapack]: ../resources/index.md#data [events]: ../concepts/events.md [item]: index.md [itemstack]: index.md#itemstacks +[livingentity]: ../entities/livingentity.md [registration]: ../concepts/registries.md#methods-for-registering [uuidgen]: https://www.uuidgenerator.net/version4 diff --git a/docs/items/tools.md b/docs/items/tools.md index 2b074f00..1b62b02a 100644 --- a/docs/items/tools.md +++ b/docs/items/tools.md @@ -140,7 +140,7 @@ A `HolderSet` can be created from a `TagKey` via `Registry#getOrThrow`. Creating any tool or multitool-like item (i.e. an item that combines two or more tools into one, e.g. an axe and a pickaxe as one item) does not need to extend any of the existing `DiggerItem`s or `SwordItem`. It simply can be implemented using a combination of the following parts: - Adding a `Tool` with your own rules by setting `DataComponents#TOOL` via `Item.Properties#component`. -- Adding attributes to the item (e.g. attack damage, attack speed) via `Item.Properties#attributes`. +- Adding [attribute modifiers][attributemodifier] to the item (e.g. attack damage, attack speed) via `Item.Properties#attributes`. - Adding item durability via `Item.Properties#durability`. - Allowing the item to be repaired via `Item.Properties#repariable`. - Allowing the item to be enchanted via `Item.Properties#enchantable`. diff --git a/docs/networking/entities.md b/docs/networking/entities.md deleted file mode 100644 index 80be9b6d..00000000 --- a/docs/networking/entities.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -sidebar_position: 4 ---- -# Entities - -In addition to regular network messages, there are various other systems provided to handle synchronizing entity data. - -## Spawn Data - -Since 1.20.2 Mojang introduced the concept of Bundle packets, which are used to send entity spawn packets together. This allows for more data to be sent with the spawn packet, and for that data to be sent more efficiently. - -You can add extra data to the spawn packet NeoForge sends by implementing the following interface. - -### IEntityWithComplexSpawn - -If your entity has data that is needed on the client, but does not change over time, then it can be added to the entity spawn packet using this interface. `#writeSpawnData` and `#readSpawnData` control how the data should be encoded to/decoded from the network buffer. Alternatively you can override the method `IEntityExtension#sendPairingData` which is called when the entity's initial data is sent to the client. This method is called on the server, and can be used to send additional payloads to the client within the same bundle as the spawn packet. - -## Dynamic Data Parameters - -This is the main vanilla system for synchronizing entity data from the server to the client. As such, a number of vanilla examples are available to refer to. - -Firstly, you need a `EntityDataAccessor` for the data you wish to keep synchronized. This should be stored as a `static final` field in your entity class, obtained by calling `SynchedEntityData#defineId` and passing the entity class and a serializer for that type of data. The available serializer implementations can be found as static constants within the `EntityDataSerializers` class. - -:::caution -You should __only__ create data parameters for your own entities, _within that entity's class_. Adding parameters to entities you do not control can cause the IDs used to send that data over the network to become desynchronized, causing difficult to debug crashes. -::: - -Then, override `Entity#defineSynchedData` and call `SynchedEntityData.Builder#define` for each of your data parameters, passing the parameter and an initial value to use. Remember to always call the `super` method first! - -You can then get and set these values via your entity's `entityData` instance. Changes made will be synchronized to the client automatically. diff --git a/docs/networking/streamcodecs.md b/docs/networking/streamcodecs.md index 028bf785..59529eae 100644 --- a/docs/networking/streamcodecs.md +++ b/docs/networking/streamcodecs.md @@ -72,7 +72,7 @@ Additionally, there are some static instances that encode and decode primivites #### Trusted Tags -`TRUSTED_TAG` and `TRUSTED_COMPOUND_TAG` are variants of `TAG` and `COMPOUND_TAG`, respectively, that have an unlimited heap to decode the tag to, compared to the 2MiB limit of `TAG` and `COMPOUND_TAG`. Trusted tag stream codecs should ideally only be used in clientbound packets, such as what Vanilla does for [block entity data packet][blockentity] and [entity data serializers][entityserializer]. +`TRUSTED_TAG` and `TRUSTED_COMPOUND_TAG` are variants of `TAG` and `COMPOUND_TAG`, respectively, that have an unlimited heap to decode the tag to, compared to the 2MiB limit of `TAG` and `COMPOUND_TAG`. Trusted tag stream codecs should ideally only be used in clientbound packets, such as what Vanilla does for [block entity data packet][blockentity] and [entity data serializers][entity]. If a different limit should be used, then a `NbtAccounter` can be supplied with the given size using `ByteBufCodecs#tagCodec` or `#compoundTagCodec`. @@ -378,6 +378,6 @@ public static final StreamCodec DISPATCH [networking]: payload.md [codecs]: ../datastorage/codecs.md -[blockentity]: ../blockentities/index.md#synchronizing-on-block-update -[entityserializer]: ../networking/entities.md#dynamic-data-parameters +[blockentity]: ../blockentities/index.md#syncing-on-block-update +[entity]: ../entities/data.md [transformers]: ../datastorage/codecs.md#transformers diff --git a/docs/resources/client/i18n.md b/docs/resources/client/i18n.md index c66292df..fac6a8a0 100644 --- a/docs/resources/client/i18n.md +++ b/docs/resources/client/i18n.md @@ -15,7 +15,7 @@ A `Component` is a piece of text with metadata, with the metadata including thin | `keybind` | Creates a component containing the (translated) display name of the given keybind. | | `nbt` | Creates a component representing the [NBT][nbt] at the given path. | | `score` | Creates a component containing a scoreboard objective value. | -| `selector` | Creates a component containing a list of entity names for a given [entity selector][selector]. | +| `selector` | Creates a component containing a list of [entity] names for a given [entity selector][selector]. | `Component.translatable()` additionally has a vararg parameter that accepts string interpolation elements. This works similar to Java's `String#format`, but always uses `%s` instead of `%i`, `%d`, `%f` and any other format specifier, calling `#toString()` where needed. @@ -181,6 +181,7 @@ Then, register the provider like any other provider in the `GatherDataEvent`. [datacomponent]: ../../items/datacomponents.md [datagen]: ../index.md#data-generation +[entity]: ../../entities/index.md [itemstack]: ../../items/index.md#itemstacks [mcwikilang]: https://minecraft.wiki/w/Language [modstoml]: ../../gettingstarted/modfiles.md#modstoml diff --git a/docs/resources/client/models/bakedmodel.md b/docs/resources/client/models/bakedmodel.md index d030bfdf..87b7f062 100644 --- a/docs/resources/client/models/bakedmodel.md +++ b/docs/resources/client/models/bakedmodel.md @@ -20,7 +20,7 @@ The most important method of a baked model is `getQuads`. This method is respons - A `ModelData`: The extra model data to use. This may contain additional data from the block entity needed for rendering. Supplied by `BakedModel#getModelData`. - A `RenderType`: The [render type][rendertype] to use for rendering the block. May be null, indicating that the quads for all render types used by this model should be returned. Otherwise, it is one of the render types returned by `BakedModel#getRenderTypes` (see below). -Models should heavily cache. This is because even though chunks are only rebuilt when a block in them changes, the computations done in this method still need to be as fast as possible and should ideally be cached heavily due to the amount of times this method will be called per chunk section (up to seven times per RenderType used by a given model * amount of RenderTypes used by the respective model * 4096 blocks per chunk section). In addition, [BERs][ber] or entity renderers may actually call this method several times per frame. +Models should heavily cache. This is because even though chunks are only rebuilt when a block in them changes, the computations done in this method still need to be as fast as possible and should ideally be cached heavily due to the amount of times this method will be called per chunk section (up to seven times per RenderType used by a given model * amount of RenderTypes used by the respective model * 4096 blocks per chunk section). In addition, [BERs][ber] or [entity renderers][entityrenderer] may actually call this method several times per frame. ### `applyTransform` and `getTransforms` @@ -102,8 +102,10 @@ It is generally encouraged to use a [custom model loader][modelloader] over wrap [ao]: https://en.wikipedia.org/wiki/Ambient_occlusion [ber]: ../../../blockentities/ber.md [blockstate]: ../../../blocks/states.md +[entityrenderer]: ../../../entities/renderer.md [event]: ../../../concepts/events.md [itemmodels]: items.md#manually-rendering-an-item +[livingentity]: ../../../entities/livingentity.md [modbus]: ../../../concepts/events.md#event-buses [modelloader]: modelloaders.md [mrl]: ../../../misc/resourcelocation.md#modelresourcelocations diff --git a/docs/resources/server/advancements.md b/docs/resources/server/advancements.md index c2ea736b..b74f7b94 100644 --- a/docs/resources/server/advancements.md +++ b/docs/resources/server/advancements.md @@ -38,7 +38,7 @@ Minecraft only ever has one root advancement per tab, and always calls the root ## Criteria Triggers -To unlock an advancement, the specified criteria must be met. Criteria are tracked through triggers, which are executed from code when the associated action happens (e.g. the `player_killed_entity` trigger executes when the player kills the specified entity). Any time an advancement is loaded into the game, the criteria defined are read and added as listeners to the trigger. When a trigger is executed, all advancements that have a listener for the corresponding criterion are rechecked for completion. If the advancement is completed, the listeners are removed. +To unlock an advancement, the specified criteria must be met. Criteria are tracked through triggers, which are executed from code when the associated action happens (e.g. the `player_killed_entity` trigger executes when the player kills the specified [entity]). Any time an advancement is loaded into the game, the criteria defined are read and added as listeners to the trigger. When a trigger is executed, all advancements that have a listener for the corresponding criterion are rechecked for completion. If the advancement is completed, the listeners are removed. Custom criteria triggers are made up of two parts: the trigger, which is activated in code by calling `#trigger`, and the instance which defines the conditions under which the trigger should award the criterion. The trigger extends `SimpleCriterionTrigger` while the instance implements `SimpleCriterionTrigger.SimpleInstance`. The generic value `T` represents the trigger instance type. @@ -277,6 +277,7 @@ builder.save(saver, ResourceLocation.fromNamespaceAndPath("examplemod", "example [codec]: ../../datastorage/codecs.md [conditions]: conditions.md [datagen]: ../index.md#data-generation +[entity]: ../../entities/index.md [function]: https://minecraft.wiki/w/Function_(Java_Edition) [itemstackjson]: ../../items/index.md#json-representation [loottable]: loottables/index.md diff --git a/docs/resources/server/damagetypes.md b/docs/resources/server/damagetypes.md index 6515f070..4b00de66 100644 --- a/docs/resources/server/damagetypes.md +++ b/docs/resources/server/damagetypes.md @@ -1,6 +1,6 @@ # Damage Types & Damage Sources -A damage type denotes what kind of damage is being applied to an entity - physical damage, fire damage, drowning damage, magic damage, void damage, etc. The distinction into damage types is used for various immunities (e.g. blazes won't take fire damage), enchantments (e.g. blast protection will only protect against explosion damage), and many more use cases. +A damage type denotes what kind of damage is being applied to an [entity] - physical damage, fire damage, drowning damage, magic damage, void damage, etc. The distinction into damage types is used for various immunities (e.g. blazes won't take fire damage), enchantments (e.g. blast protection will only protect against explosion damage), and many more use cases. A damage type is a template for a damage source, so to speak. Or in other words, a damage source can be viewed as a damage type instance. Damage types exist as [`ResourceKey`s][rk] in code, but have all of their properties defined in data packs. Damage sources, on the other hand, are created as needed by the game, based off the values in the data pack files. They can hold additional context, for example the attacking entity. @@ -45,7 +45,7 @@ The same format is also used for vanilla's damage types, and pack developers can ## Creating and Using Damage Sources -`DamageSource`s are usually created on the fly when `Entity#hurt` is called. Be aware that since damage types are a [datapack registry][dr], you will need a `RegistryAccess` to query them, which can be obtained via `Level#registryAccess`. To create a `DamageSource`, call the `DamageSource` constructor with up to four parameters: +`DamageSource`s are usually created on the fly when [`Entity#hurt`][entityhurt] is called. Be aware that since damage types are a [datapack registry][dr], you will need a `RegistryAccess` to query them, which can be obtained via `Level#registryAccess`. To create a `DamageSource`, call the `DamageSource` constructor with up to four parameters: ```java DamageSource damageSource = new DamageSource( @@ -79,7 +79,7 @@ public static DamageSource exampleDamage(Entity causer) { ``` :::tip -Vanilla's `DamageSource` factories can be found in `DamageSources`, and vanilla's `DamageType` resource keys can be found in `DamageTypes`. +Vanilla's `DamageSource` factories can be found in `DamageSources`, and vanilla's `DamageType` resource keys can be found in `DamageTypes`. Entities also have the method `Entity#damageSources`, which is a convenience getter for the `DamageSources` instance. ::: The first and foremost use case for damage sources is `Entity#hurt`. This method is called whenever an entity is receiving damage. To hurt an entity with our own damage type, we simply call `Entity#hurt` ourselves: @@ -129,6 +129,8 @@ public static void onGatherData(GatherDataEvent event) { [datagen]: ../index.md#data-generation [dr]: ../../concepts/registries.md#datapack-registries [drdatagen]: ../../concepts/registries.md#data-generation-for-datapack-registries +[entity]: ../../entities/index.md +[entityhurt]: ../../entities/index.md#damaging-entities [extenum]: ../../advanced/extensibleenums.md [rk]: ../../misc/resourcelocation.md#resourcekeys [tags]: tags.md diff --git a/docs/resources/server/enchantments/builtin.md b/docs/resources/server/enchantments/builtin.md index 625b525d..481ed744 100644 --- a/docs/resources/server/enchantments/builtin.md +++ b/docs/resources/server/enchantments/builtin.md @@ -337,5 +337,5 @@ For more detail on each of these, please look at the [relevant minecraft wiki pa [datapack function]: https://minecraft.wiki/w/Function_(Java_Edition) [luck]: https://minecraft.wiki/w/Luck [mob effect]: ../../../items/mobeffects.md -[attribute modifier]: https://minecraft.wiki/w/Attribute#Modifiers +[attribute modifier]: ../../../entities/attributes.md#attribute-modifiers [relevant minecraft wiki page]: https://minecraft.wiki/w/Enchantment_definition#Components_with_entity_effects \ No newline at end of file diff --git a/docs/resources/server/loottables/custom.md b/docs/resources/server/loottables/custom.md index ceba663b..a1c33a40 100644 --- a/docs/resources/server/loottables/custom.md +++ b/docs/resources/server/loottables/custom.md @@ -6,7 +6,7 @@ All loot table related registries follow a similar pattern. To add a new registr ## Custom Loot Entry Types -To create a custom loot entry type, extend `LootPoolEntryContainer` or one of its two direct subclasses, `LootPoolSingletonContainer` or `CompositeEntryBase`. For the sake of example, we want to create a loot entry type that returns the drops of a entity - this is purely for example purposes, in practice it would be more ideal to directly reference the other loot table. Let's start by creating our loot entry type class: +To create a custom loot entry type, extend `LootPoolEntryContainer` or one of its two direct subclasses, `LootPoolSingletonContainer` or `CompositeEntryBase`. For the sake of example, we want to create a loot entry type that returns the drops of a [entity] - this is purely for example purposes, in practice it would be more ideal to directly reference the other loot table. Let's start by creating our loot entry type class: ```java // We extend LootPoolSingletonContainer since we have a "finite" set of drops. @@ -295,4 +295,5 @@ public class RandomEnchantmentWithLevelFunction extends LootItemConditionalFunct ``` [codec]: ../../../datastorage/codecs.md +[entity]: ../../../entities/index.md [registries]: ../../../concepts/registries.md#methods-for-registering diff --git a/docs/resources/server/loottables/index.md b/docs/resources/server/loottables/index.md index 1419b2c7..fe123766 100644 --- a/docs/resources/server/loottables/index.md +++ b/docs/resources/server/loottables/index.md @@ -2,7 +2,7 @@ Loot tables are data files that are used to define randomized loot drops. A loot table can be rolled, returning a (potentially empty) list of item stacks. The output of this process depends on (pseudo-)randomness. Loot tables are located at `data//loot_table/.json`. For example, the loot table `minecraft:blocks/dirt`, used by the dirt block, is located at `data/minecraft/loot_table/blocks/dirt.json`. -Minecraft uses loot tables at various points in the game, including block drops, entity drops, chest loot, fishing loot, and many others. How a loot table is referenced depends on the context: +Minecraft uses loot tables at various points in the game, including [block] drops, [entity] drops, chest loot, fishing loot, and many others. How a loot table is referenced depends on the context: - Every block will, by default, receive an associated loot table, located at `:blocks/`. This can be disabled by calling `#noLootTable` on the block's `Properties`, resulting in no loot table being created and the block dropping nothing; this is mainly done by air-like or technical blocks. - Every entity that does not call `EntityType.Builder#noLootTable` (which is typically entities in `MobCategory#MISC`) will, by default, receive an associated loot table, located at `:entities/`. This can be changed by overriding `#getLootTable`. For example, sheep use this to roll different loot tables depending on their wool color. @@ -416,6 +416,7 @@ new LootTableProvider(output, Set.of(), List.of(new SubProviderEntry( [advancement]: ../advancements.md [binomial]: https://en.wikipedia.org/wiki/Binomial_distribution +[block]: ../../../blocks/index.md [conditions]: ../conditions.md [context]: #loot-context [customentry]: custom.md#custom-loot-entry-types @@ -423,6 +424,7 @@ new LootTableProvider(output, Set.of(), List.of(new SubProviderEntry( [customnumber]: custom.md#custom-number-providers [damagesource]: ../damagetypes.md#creating-and-using-damage-sources [datagen]: ../../index.md#data-generation +[entity]: ../../../entities/index.md [entry]: #loot-entry [glm]: glm.md [lootcondition]: lootconditions diff --git a/docs/resources/server/loottables/lootfunctions.md b/docs/resources/server/loottables/lootfunctions.md index 30d35aa9..29960c0f 100644 --- a/docs/resources/server/loottables/lootfunctions.md +++ b/docs/resources/server/loottables/lootfunctions.md @@ -466,7 +466,7 @@ During datagen, call `SetItemDamageFunction#setDamage` with the desired number p ## `minecraft:set_attributes` -Adds a list of attribute modifiers to the result item stack. +Adds a list of [attribute modifiers][attributemodifier] to the result item stack. ```json5 { @@ -876,6 +876,7 @@ During datagen, call `SequenceFunction#of` with the other functions to construct - [Item Modifiers][itemmodifiers] on the [Minecraft Wiki][mcwiki] +[attributemodifier]: ../../../entities/attributes.md#attribute-modifiers [component]: ../../client/i18n.md#components [conditions]: lootconditions [custom]: custom.md#custom-loot-functions diff --git a/docs/worldgen/biomemodifier.md b/docs/worldgen/biomemodifier.md index 9034534c..383662be 100644 --- a/docs/worldgen/biomemodifier.md +++ b/docs/worldgen/biomemodifier.md @@ -202,7 +202,9 @@ BUILDER.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, bootstrap -> { -### Add Spawns +### Add Spawns + +_See also [Living Entities/Natural Spawning][spawning]._ This biome modifier type adds entity spawns to biomes. The modifier takes in the biome id or tag of the biomes the entity spawns are added to, and the `SpawnerData` of the entities to add. Each `SpawnerData` contains the entity id, the spawn weight, and the minimum/maximum number of entities to spawn at a given time. @@ -710,8 +712,9 @@ protected void addTags(HolderLookup.Provider registries) { } ``` +[datagen]: ../resources/index.md#data-generation +[datapackdatagen]: ../concepts/registries#data-generation-for-datapack-registries +[datapacks]: ../resources/index.md#data [datareg]: ../concepts/registries.md#datapack-registries +[spawning]: ../entities/livingentity.md#natural-spawning [staticreg]: ../concepts/registries.md#methods-for-registering -[datapacks]: ../resources/index.md#data -[datagen]: ../resources/index.md#data-generation -[datapackdatagen]: ../concepts/registries#data-generation-for-datapack-registries \ No newline at end of file diff --git a/src/pages/contributing.md b/src/pages/contributing.md index 878cc881..b992ebfc 100644 --- a/src/pages/contributing.md +++ b/src/pages/contributing.md @@ -297,6 +297,23 @@ I'm within an admonition! ::: ``` +### Diagrams + +Diagrams can be created using the [Mermaid library][mermaid]. + +When creating a diagram, a convention is used to make `abstract` classes red and non-`abstract` classes blue. + +````md + +```mermaid +graph LR; + Class1-->Class2; + + class Class1 red; + class Class2 blue; +``` +```` + [docs]: https://github.com/neoforged/Documentation [npm]: https://www.npmjs.com/ @@ -313,6 +330,7 @@ I'm within an admonition! [eck]: http://math.hws.edu/javanotes/ [docusaurus]: https://docusaurus.io/docs/markdown-features +[mermaid]: https://mermaid.js.org/intro/ [mdx]: https://mdxjs.com/guides/ [admonition]: https://docusaurus.io/docs/markdown-features/admonitions