From 312442d3d4cd3cb2fb7da479ce808c6028881ad0 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Mon, 29 Jan 2024 18:04:43 +0100 Subject: [PATCH 01/59] rework registries and resource location documentation --- docs/blocks/index.md | 31 ++- docs/concepts/registries.md | 304 ++++++++++++++------------ docs/concepts/resources.md | 21 -- docs/gameeffects/sounds.md | 2 +- docs/items/index.md | 47 ++++ docs/misc/resourcelocation.md | 46 ++++ docs/resources/client/index.md | 2 +- docs/resources/client/models/index.md | 2 +- docs/resources/server/glm.md | 2 +- docs/resources/server/index.md | 2 +- 10 files changed, 295 insertions(+), 164 deletions(-) delete mode 100644 docs/concepts/resources.md create mode 100644 docs/misc/resourcelocation.md diff --git a/docs/blocks/index.md b/docs/blocks/index.md index 09567c52f..bfac39ab8 100644 --- a/docs/blocks/index.md +++ b/docs/blocks/index.md @@ -8,10 +8,10 @@ One Block to Rule Them All Before we get started, it is important to understand that there is only ever one of each block in the game. A world consists of thousands of references to that one block in different locations. In other words, the same block is just displayed a lot of times. -Due to this, a block should only ever be instantiated once, and that is during registration. Once the block is registered, you can then use the registered reference as needed. Consider this example (see the [Registration][registration] page if you do not know what you are looking at): +Due to this, a block should only ever be instantiated once, and that is during [registration]. Once the block is registered, you can then use the registered reference as needed. Consider this example: ```java -//BLOCKS is a DeferredRegister.Blocks +//BLOCKS is a DeferredRegister public static final DeferredBlock MY_BLOCK = BLOCKS.register("my_block", () -> new Block(...)); ``` @@ -80,6 +80,33 @@ Directly using `Block` only allows for very basic blocks. If you want to add fun If you want to make a block that has different variants (think a slab that has a bottom, top, and double variant), you should use [blockstates]. And finally, if you want a block that stores additional data (think a chest that stores its inventory), a [block entity][blockentities] should be used. The rule of thumb here is that if you have a finite and reasonably small amount of states (= a few hundred states at most), use blockstates, and if you have an infinite or near-infinite amount of states, use a block entity. +### `DeferredRegister.Blocks` + +All registries use `DeferredRegister` to register their contents, and blocks are no exceptions. However, due to the fact that adding new blocks is such an essential feature of an overwhelming amount of mods, NeoForge provides the `DeferredRegister.Blocks` helper class that extends `DeferredRegister` and provides some block-specific helpers: + +```java +public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks(ExampleMod.MOD_ID); + +public static final Supplier EXAMPLE_BLOCK = BLOCKS.registerBlock( + "example_block", + Block::new, // The factory that the properties will be passed into. + new BlockBehaviour.Properties() // The properties to use. +); +``` + +Internally, this will simply call `BLOCKS.register("example_block", () -> new Block(new BlockBehaviour.Properties()))` by applying the properties parameter to the provided block factory (which is commonly the constructor). + +If you want to use `Block::new`, you can leave out the factory entirely: + +```java +public static final Supplier EXAMPLE_BLOCK = BLOCKS.registerSimpleBlock( + "example_block", + new BlockBehaviour.Properties() // The properties to use. +); +``` + +This does the exact same as the previous example, but is slightly shorter. Of course, if you want to use a subclass of `Block` and not `Block` itself, you will have to use the previous method instead. + ### Resources If you register your block and place it in the world, you will find it to be missing things like a texture. This is because textures, among others, are handled by Minecraft's resource system. diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index 22325c439..c1a2211f7 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -1,200 +1,232 @@ -Registries -========== +# Registries -Registration is the process of taking the objects of a mod (such as items, blocks, sounds, etc.) and making them known to the game. Registering things is important, as without registration the game will simply not know about these objects, which will cause unexplainable behaviors and crashes. +Registration is the process of taking the objects of a mod (such as [items][item], [blocks][block], entities, etc.) and making them known to the game. Registering things is important, as without registration the game will simply not know about these objects, which will cause unexplainable behaviors and crashes. -Most things that require registration in the game are handled by the Forge registries. A registry is an object similar to a map that assigns values to keys. Forge uses registries with [`ResourceLocation`][ResourceLocation] keys to register objects. This allows the `ResourceLocation` to act as the "registry name" for objects. +A registry is, simply put, a wrapper around a map that maps registry names (read on) to registered objects, often called registry entries. Registry names must be unique within the same registry, but the same registry name may be present in multiple registries. The most common example for this are blocks (in the `BLOCKS` registry) that have an item form with the same registry name (in the `ITEMS` registry). -Every type of registrable object has its own registry. To see all registries wrapped by Forge, see the `ForgeRegistries` class. All registry names within a registry must be unique. However, names in different registries will not collide. For example, there's a `Block` registry, and an `Item` registry. A `Block` and an `Item` may be registered with the same name `example:thing` without colliding; however, if two different `Block`s or `Item`s were registered with the same exact name, the second object will override the first. +Every registered object has a unique name, called its registry name. The name is represented as a [`ResourceLocation`][resloc]. For example, the registry name of the dirt block is `minecraft:dirt`, and the registry name of the zombie is `minecraft:zombie`. Modded objects will of course not use the `minecraft` namespace; their mod id will be used instead. -Methods for Registering ------------------- +## Vanilla vs. Modded -There are two proper ways to register objects: the `DeferredRegister` class, and the `RegisterEvent` lifecycle event. +To understand some of the design decisions that were made in NeoForge's registry system, we will first look at how Minecraft does this. We will use the block registry as an example, as most other registries work the same way. -### DeferredRegister +Registries generally register [singletons][singleton]. This means that all registry entries exist exactly once. For example, all stone blocks you see throughout the game are actually the same stone block, displayed many times. If you need the stone block, you can get it by referencing the registered block instance. -`DeferredRegister` is the recommended way to register objects. It allows the use and convenience of static initializers while avoiding the issues associated with it. It simply maintains a list of suppliers for entries and registers the objects from those suppliers during `RegisterEvent`. +Minecraft registers all blocks in the `Blocks` class. Through the `register` method, `Registry#register()` is called, with the block registry at `BuiltInRegistries.BLOCK` being the first parameter. After all blocks are registered, Minecraft performs various checks based on the list of blocks, for example the self check that verifies that all blocks have a model loaded. -An example of a mod registering a custom block: +The main reason all of this works is that `Blocks` is classloaded early enough by Minecraft. Mods are not automatically classloaded by Minecraft, and thus workarounds are needed. -```java -private static final DeferredRegister BLOCKS = DeferredRegister.create(ForgeRegistries.BLOCKS, MODID); +## Methods for Registering -public static final RegistryObject ROCK_BLOCK = BLOCKS.register("rock", () -> new Block(BlockBehaviour.Properties.of().mapColor(MapColor.STONE))); +NeoForge offers two ways to register objects: the `DeferredRegister` class, and the `RegisterEvent`. Note that the former is a wrapper around the latter, and is thus recommended in order to prevent mistakes. -public ExampleMod() { - BLOCKS.register(FMLJavaModLoadingContext.get().getModEventBus()); -} -``` +### `DeferredRegister` -### `RegisterEvent` - -`RegisterEvent` is the second way to register objects. This [event] is fired for each registry after the mod constructors and before the loading of configs. Objects are registered using `#register` by passing in the registry key, the name of the registry object, and the object itself. There is an additional `#register` overload which takes in a consumed helper to register an object with a given name. It is recommended to use this method to avoid unnecessary object creation. - -Here is an example: (the event handler is registered on the *mod event bus*) +We begin by creating our `DeferredRegister`: ```java -@SubscribeEvent -public void register(RegisterEvent event) { - event.register(ForgeRegistries.Keys.BLOCKS, - helper -> { - helper.register(new ResourceLocation(MODID, "example_block_1"), new Block(...)); - helper.register(new ResourceLocation(MODID, "example_block_2"), new Block(...)); - helper.register(new ResourceLocation(MODID, "example_block_3"), new Block(...)); - // ... - } - ); -} +public static final DeferredRegister BLOCKS = DeferredRegister.create( + // The registry we want to use. + // Minecraft's registries can be found in BuiltInRegistries, NeoForge's registries can be found in NeoForgeRegistries. + // Mods may also add their own registries, refer to the individual mod's documentation or source code for where to find them. + BuiltInRegistries.BLOCKS, + // Our mod id. + ExampleMod.MOD_ID +); ``` -### Registries that aren't Forge Registries - -Not all registries are wrapped by Forge. These can be static registries, like `LootItemConditionType`, which are safe to use. There are also dynamic registries, like `ConfiguredFeature` and some other worldgen registries, which are typically represented in JSON. `DeferredRegister#create` has an overload which allows modders to specify the registry key of which vanilla registry to create a `RegistryObject` for. The registry method and attaching to the mod event bus is the same as other `DeferredRegister`s. - -:::danger -Dynamic registry objects can **only** be registered through data files (e.g. JSON). They **cannot** be registered in-code. -::: +We can then add our registry entries as static final fields (see [the article on Blocks][block] for what parameters to add in `new Block()`): ```java -private static final DeferredRegister REGISTER = DeferredRegister.create(Registries.LOOT_CONDITION_TYPE, "examplemod"); - -public static final RegistryObject EXAMPLE_LOOT_ITEM_CONDITION_TYPE = REGISTER.register("example_loot_item_condition_type", () -> new LootItemConditionType(...)); +public static final DeferredHolder EXAMPLE_BLOCK = BLOCKS.register( + "example_block" // Our registry name. + () -> new Block(...) // A supplier of the object we want to register. +); ``` +The class `DeferredHolder` holds our object. The type parameter `R` is the type of the registry we are registering to (in our case `Block`). The type parameter `T` is the type of our supplier. Since we directly register a `Block` in this example, we provide `Block` as the second parameter. If we were to register an object of a subclass of `Block`, for example `SlabBlock`, we would provide `SlabBlock` here instead. + :::note -Some classes cannot by themselves be registered. Instead, `*Type` classes are registered, and used in the formers' constructors. For example, [`BlockEntity`][blockentity] has `BlockEntityType`, and `Entity` has `EntityType`. These `*Type` classes are factories that simply create the containing type on demand. +Some modders prefer to keep their `DeferredRegister`s in the same class as their registered objects. Others prefer keeping all `DeferredRegister`s in a separate class for readability. This is mostly a design decision, however if you decide to do the latter, make sure to classload the classes the objects are in, for example through an empty static method. +::: + +`DeferredHolder` is a subclass of `Supplier`. To get our registered object when we need it, we can call `DeferredHolder#get()`. The fact that `DeferredHolder` extends `Supplier` also allows us to use `Supplier` as the type of our field. That way, the above code block becomes the following: -These factories are created through the use of their `*Type$Builder` classes. An example: (`REGISTER` refers to a `DeferredRegister`) ```java -public static final RegistryObject> EXAMPLE_BLOCK_ENTITY = REGISTER.register( - "example_block_entity", () -> BlockEntityType.Builder.of(ExampleBlockEntity::new, EXAMPLE_BLOCK.get()).build(null) +public static final Supplier EXAMPLE_BLOCK = BLOCKS.register( + "example_block" // Our registry name. + () -> new Block(...) // A supplier of the object we want to register. ); ``` -::: - -Referencing Registered Objects ------------------------------- -Registered objects should not be stored in fields when they are created and registered. They are to be always newly created and registered whenever `RegisterEvent` is fired for that registry. This is to allow dynamic loading and unloading of mods in a future version of Forge. +Be aware that a few places explicitly require a `DeferredHolder` and will not just accept any `Supplier`. If you need a `DeferredHolder`, it is best to change the type of your `Supplier` back to `DeferredHolder`. -Registered objects must always be referenced through a `RegistryObject` or a field with `@ObjectHolder`. +Finally, since the entire system is a wrapper around registry events, we need to tell the `DeferredRegister` to attach itself to the registry events as needed: -### Using RegistryObjects +```java +//This is our mod constructor +public ExampleMod(IModEventBus bus) { + ExampleBlocksClass.BLOCKS.register(bus); + //Other stuff here +} +``` -`RegistryObject`s can be used to retrieve references to registered objects once they are available. These are used by `DeferredRegister` to return a reference to the registered objects. Their references are updated after `RegisterEvent` is called for their registry, along with the `@ObjectHolder` annotations. +:::info +There are specialized variants of `DeferredRegister`s for blocks and items that provide helper methods, called [`DeferredRegister.Blocks`][defregblocks] and [`DeferredRegister.Items`][defregitems], respectively. +::: -To get a `RegistryObject`, call `RegistryObject#create` with a `ResourceLocation` and the `IForgeRegistry` of the registrable object. Custom registries can also be used by supplying the registry name instead. Store the `RegistryObject` in a `public static final` field, and call `#get` whenever you need the registered object. +### `RegisterEvent` -An example of using `RegistryObject`: +`RegisterEvent` is the second way to register objects. This [event][event] is fired for each registry, after the mod constructors (since those are where `DeferredRegister`s register their internal event handlers) and before the loading of configs. `RegisterEvent` is fired on the mod event bus. ```java -public static final RegistryObject BOW = RegistryObject.create(new ResourceLocation("minecraft:bow"), ForgeRegistries.ITEMS); - -// assume that 'neomagicae:mana_type' is a valid registry, and 'neomagicae:coffeinum' is a valid object within that registry -public static final RegistryObject COFFEINUM = RegistryObject.create(new ResourceLocation("neomagicae", "coffeinum"), new ResourceLocation("neomagicae", "mana_type"), "neomagicae"); +@SubscribeEvent +public void register(RegisterEvent event) { + event.register( + // This is the registry key of the registry. + // Get these from Registries for vanilla registries, or from NeoForgeRegistries.Keys for NeoForge registries. + Registries.BLOCKS, + // Register your objects here. + registry -> { + registry.register(new ResourceLocation(MODID, "example_block_1"), new Block(...)); + registry.register(new ResourceLocation(MODID, "example_block_2"), new Block(...)); + registry.register(new ResourceLocation(MODID, "example_block_3"), new Block(...)); + } + ); +} ``` -### Using @ObjectHolder +## Custom Registries -Registered objects from registries can be injected into the `public static` fields by annotating classes or fields with `@ObjectHolder` and supplying enough information to construct a `ResourceLocation` to identify a specific object in a specific registry. +Custom registries allow you to specify additional systems that addon mods for your mod may want to plug into. For example, if your mod were to add spells, you could make the spells a registry and thus allow other mods to add spells to your mod, without you having to do anything else. It also allows you to do some things, such as syncing the entries, automatically. -The rules for `@ObjectHolder` are as follows: +Let's start by creating the [registry key][resourcekey] and the registry itself: -* If the class is annotated with `@ObjectHolder`, its value will be the default namespace for all fields within if not explicitly defined -* If the class is annotated with `@Mod`, the modid will be the default namespace for all annotated fields within if not explicitly defined -* A field is considered for injection if: - * it has at least the modifiers `public static`; - * the **field** is annotated with `@ObjectHolder`, and: - * the name value is explicitly defined; and - * the registry name value is explicitly defined - * _A compile-time exception is thrown if a field does not have a corresponding registry or name._ -* _An exception is thrown if the resulting `ResourceLocation` is incomplete or invalid (non-valid characters in path)_ -* If no other errors or exceptions occur, the field will be injected -* If all of the above rules do not apply, no action will be taken (and a message may be logged) +```java +// We use spells as an example for the registry here, without any details about what a spell actually is (as it doesn't matter). +// Of course, all mentions of spells can and should be replaced with whatever your registry actually is. +public static final ResourceKey> SPELL_REGISTRY_KEY = ResourceKey.createRegistryKey(new ResourceLocation("yourmodid", "spells")); +public static final Registry SPELL_REGISTRY = new RegistryBuilder<>(SPELL_REGISTRY_KEY) + // If you want the registry to sync its values. + .sync(true) + // The default key. Similar to minecraft:air for blocks. + .defaultKey(new ResourceLocation("yourmodid", "empty")) + // Effectively limits the max count. Generally discouraged, but may make sense in settings such as networking. + .maxId(256) + // Build the registry. + .create(); +``` -`@ObjectHolder`-annotated fields are injected with their values after `RegisterEvent` is fired for their registry, along with the `RegistryObject`s. +Then, tell the game that the registry exists by registering them to the root registry in `NewRegistryEvent`: -:::note -If the object does not exist in the registry when it is to be injected, a debug message will be logged and no value will be injected. -::: +```java +@SubscribeEvent +static void registerRegistries(NewRegistryEvent event) { + event.register(SPELL_REGISTRY); +} +``` -As these rules are rather complicated, here are some examples: +You can now register new registry contents like with any other registry, through both `DeferredRegister` and `RegisterEvent`: ```java -class Holder { - @ObjectHolder(registryName = "minecraft:enchantment", value = "minecraft:flame") - public static final Enchantment flame = null; // Annotation present. [public static] is required. [final] is optional. - // Registry name is explicitly defined: "minecraft:enchantment" - // Resource location is explicitly defined: "minecraft:flame" - // To inject: "minecraft:flame" from the [Enchantment] registry - - public static final Biome ice_flat = null; // No annotation on the field. - // Therefore, the field is ignored. - - @ObjectHolder("minecraft:creeper") - public static Entity creeper = null; // Annotation present. [public static] is required. - // The registry has not been specified on the field. - // Therefore, THIS WILL PRODUCE A COMPILE-TIME EXCEPTION. - - @ObjectHolder(registryName = "potion") - public static final Potion levitation = null; // Annotation present. [public static] is required. [final] is optional. - // Registry name is explicitly defined: "minecraft:potion" - // Resource location is not specified on the field - // Therefore, THIS WILL PRODUCE A COMPILE-TIME EXCEPTION. +public static final DeferredRegister SPELLS = DeferredRegister.create("yourmodid", SPELL_REGISTRY); +public static final Supplier EXAMPLE_SPELL = SPELLS.register("example_spell", () -> new Spell(...)); + +// Alternatively: +@SubscribeEvent +public static void register(RegisterEvent event) { + event.register(SPELL_REGISTRY, registry -> { + registry.register(new ResourceLocation("yourmodid", "example_spell"), () -> new Spell(...)); + }); } ``` -Creating Custom Forge Registries --------------------------------- +## Datapack Registries -Custom registries can usually just be a simple map of key to value. This is a common style; however, it forces a hard dependency on the registry being present. It also requires that any data that needs to be synced between sides must be done manually. Custom Forge Registries provide a simple alternative for creating soft dependents along with better management and automatic syncing between sides (unless told otherwise). Since the objects also use a Forge registry, registration becomes standardized in the same way. +A datapack registry (also known as a dynamic registry or, after its main use case, worldgen registry) is a special kind of registry that loads data from [datapack][datapack] JSONs (hence the name) at world load, instead of loading them when the game starts. Default datapack registries include most worldgen registries, as well as any custom registry (see below) that is marked as a datapack registry. -Custom Forge Registries are created with the help of a `RegistryBuilder`, through either `NewRegistryEvent` or the `DeferredRegister`. The `RegistryBuilder` class takes various parameters (such as the registry's name, id range, and various callbacks for different events happening on the registry). New registries are registered to the `RegistryManager` after `NewRegistryEvent` finishes firing. +Datapack registries allow their contents to be specified in JSON files. This means that no code (other than [datagen][datagen] if you don't want to write the JSON files yourself) is necessary. Every datapack registry has a [`Codec`][codec] associated with it, which is used for serialization, and each registry's id determines its datapack path: -Any newly created registry should use its associated [registration method][registration] to register the associated objects. +- Minecraft's datapack registries use the format `data/yourmodid/registrypath` (for example `data/yourmodid/worldgen/biomes`, where `worldgen/biomes` is the registry path). +- All other datapack registries (NeoForge or modded) use the format `data/yourmodid/registrynamespace/registrypath` (for example `data/yourmodid/neoforge/loot_modifiers`, where `neoforge` is the registry namespace and `loot_modifiers` is the registry path). -### Using NewRegistryEvent +Datapack registries can be obtained from a `RegistryAccess`. This `RegistryAccess` can be retrieved by calling `ServerLevel#registryAccess()` if on the server, by calling `Minecraft.getInstance().connection#registryAccess()` if on the client, or from a `RegistryOps`. -When using `NewRegistryEvent`, calling `#create` with a `RegistryBuilder` will return a supplier-wrapped registry. The supplied registry can be accessed after `NewRegistryEvent` has finished posting to the mod event bus. Getting the custom registry from the supplier before `NewRegistryEvent` finishes firing will result in a `null` value. +### `RegistryOps` -#### New Datapack Registries +`RegistryOps` is a special [`DynamicOps`][dynamicops] made specifically for (de)serializing datapack registries. It provides additional registry context and enables the use of special codecs that can only be used with `RegistryOps`. Data generation of datapack registry elements must always be done through `RegistryOps` to convert elements to `JsonElement`s. -New datapack registries can be added using the `DataPackRegistryEvent$NewRegistry` event on the mod event bus. The registry is created via `#dataPackRegistry` by passing in the `ResourceKey` representing the registry name and the `Codec` used to encode and decode the data from JSON. An optional `Codec` can be provided to sync the datapack registry to the client. +A `RegistryOps` can be created via `RegistryOps.create(JsonElement.INSTANCE, RegistryAccess.builtinCopy())`. `RegistryAccess.builtinCopy()` creates a set of writable datapack registries, which is necessary for datagenning unregistered objects. All data generation done in a `GatherDataEvent` handler must use the same `RegistryAccess` and `RegistryOps` instances, otherwise obscure errors will occur. -:::note -Datapack Registries cannot be created with `DeferredRegister`. They can only be created through the event. -::: +### `Holder`s -### With DeferredRegister +As mentioned before, (normal) registries rely on `DeferredHolder`s, which are a special kind of `Holder`. A `Holder` vaguely resembles a `Pair` that either starts with a key and has a value bound later, or starts with a value and may have a key bound later. Datapack registries extensively rely on (non-deferred) `Holder`s to reference registry elements of other registries. For example, `Biome`s refer to `Holder`s, and `PlacedFeature`s refer to `Holder`s. -The `DeferredRegister` method is once again another wrapper around the above event. Once a `DeferredRegister` is created in a constant field using the `#create` overload which takes in the registry name and the mod id, the registry can be constructed via `DeferredRegister#makeRegistry`. This takes in a supplied `RegistryBuilder` containing any additional configurations. The method already populates `#setName` by default. Since this method can be returned at any time, a supplied version of an `IForgeRegistry` is returned instead. Getting the custom registry from the supplier before `NewRegistryEvent` is fired will result in a `null` value. +During data generation, we can use `RegistryOps#registry` to get a registry, and `Registry#getOrCreateHolderOrThrow()` to produce key-only reference holders (we only need the key in this case, since holder codecs only encode keys when using a `RegistryOps` in order to prevent circular dependencies). -:::caution -`DeferredRegister#makeRegistry` must be called before the `DeferredRegister` is added to the mod event bus via `#register`. `#makeRegistry` also uses the `#register` method to create the registry during `NewRegistryEvent`. -::: - -Handling Missing Entries ------------------------- +### `JsonCodecProvider` -There are cases where certain registry objects will cease to exist whenever a mod is updated or, more likely, removed. It is possible to specify actions to handle the missing mapping through the third of the registry events: `MissingMappingsEvent`. Within this event, a list of missing mappings can be obtained either by `#getMappings` given a registry key and mod id or all mappings via `#getAllMappings` given a registry key. +NeoForge provides a data provider for datapack registry elements that, given a registry key and a map of objects to generate, generates all JSON files for the objects in the map. -:::caution -`MissingMappingsEvent` is fired on the **Forge** event bus. -::: +```java +@SubscribeEvent +static void onGatherData(GatherDataEvent event) { + DataGenerator generator = event.getDataGenerator(); + ExistingFileHelper existingFileHelper = event.getExistingFileHelper(); + RegistryOps registryOps = RegistryOps.create(JsonElement.INSTANCE, RegistryAccess.builtinCopy()); + + Map map = Map.of( + // Whatever entries you want. For example: + new ResourceLocation("yourmodid", "sponge_everywhere"), new PlacedFeature(...) + ); + + JsonCodecProvider provider = JsonCodecProvider.forDatapackRegistry( + generator, + existingFileHelper, + "yourmodid", + registryOps, + // The registry you want to generate in. + Registry.PLACED_FEATURE_REGISTRY, + // The elements to generate. + map + ); + + generator.addProvider(event.includeServer(), provider); +} +``` -For each `Mapping`, one of four mapping types can be selected to handle the missing entry: +### Custom Datapack Registries -| Action | Description | -| :---: | :--- | -| IGNORE | Ignores the missing entry and abandons the mapping. | -| WARN | Generates a warning in the log. | -| FAIL | Prevents the world from loading. | -| REMAP | Remaps the entry to an already registered, non-null object. | +Custom datapack registries are created through `RegistryBuilder` like all other registries, but are registered to `DataPackRegistryEvent.NewRegistry` instead of `NewRegistryEvent`. Reiterating the spells example from before, registering our spell registry as a datapack registry looks something like this: -If no action is specified, then the default action will occur by notifying the user about the missing entry and whether they still would like to load the world. All actions besides remapping will prevent any other registry object from taking the place of the existing id in case the associated entry ever gets added back into the game. +```java +@SubscribeEvent +static void registerDatapackRegistries(DataPackRegistryEvent.NewRegistry event) { + event.register( + // The registry key. + SPELL_REGISTRY_KEY, + // The codec of the registry contents. + Spell.CODEC, + // The network codec of the registry contents. Often identical to the normal codec. + // May be a reduced variant of the normal codec that omits data that is not needed on the client. + // May be null. If null, registry entries will not be synced to the client at all. + // May be omitted, which is functionally identical to passing null (a method overload + // with two parameters is called that passes null to the normal three parameter method). + Spell.CODEC + ); +} +``` -[ResourceLocation]: ./resources.md#resourcelocation -[registration]: #methods-for-registering -[event]: ./events.md +[block]: ../blocks/index.md [blockentity]: ../blockentities/index.md +[codec]: ../datastorage/codecs.md +[datagen]: ../datagen/index.md +[datapack]: ../resources/server/index.md +[defregblocks]: ../blocks/index.md#deferredregisterblocks +[defregitems]: ../items/index.md#deferredregisteritems +[dynamicops]: ../datastorage/codecs.md#dynamicops +[event]: ./events.md +[item]: ../items/index.md +[resloc]: ../misc/resourcelocation.md +[resourcekey]: ../misc/resourcelocation.md#resourcekeys +[singleton]: https://en.wikipedia.org/wiki/Singleton_pattern diff --git a/docs/concepts/resources.md b/docs/concepts/resources.md deleted file mode 100644 index d0d0b2074..000000000 --- a/docs/concepts/resources.md +++ /dev/null @@ -1,21 +0,0 @@ -Resources -========= - -A resource is extra data used by the game, and is stored in a data file, instead of being in the code. -Minecraft has two primary resource systems active: one on the logical client used for visuals such as models, textures, and localization called `assets`, and one on the logical server used for gameplay such as recipes and loot tables called `data`. -[Resource packs][respack] control the former, while [Datapacks][datapack] control the latter. - -In the default mod development kit, assets and data directories are located under the `src/main/resources` directory of the project. - -When multiple resource packs or data packs are enabled, they are merged. Generally, files from packs at the top of the stack override those below; however, for certain files, such as localization files and tags, data is actually merged contentwise. Mods define resource and data packs in their `resources` directories, but they are seen as subsets of the "Mod Resources" pack. Mod resource packs cannot be disabled, but they can be overridden by other resource packs. Mod datapacks can be disabled with the vanilla `/datapack` command. - -All resources should have snake case paths and filenames (lowercase, using "_" for word boundaries), which is enforced in 1.11 and above. - -`ResourceLocation` ------------------- - -Minecraft identifies resources using `ResourceLocation`s. A `ResourceLocation` contains two parts: a namespace and a path. It generally points to the resource at `assets///`, where `ctx` is a context-specific path fragment that depends on how the `ResourceLocation` is being used. When a `ResourceLocation` is written/read as from a string, it is seen as `:`. If the namespace and the colon are left out, then when the string is read into an `ResourceLocation` the namespace will always default to `"minecraft"`. A mod should put its resources into a namespace with the same name as its mod id (e.g. a mod with the id `examplemod` should place its resources in `assets/examplemod` and `data/examplemod` respectively, and `ResourceLocation`s pointing to those files would look like `examplemod:`.). This is not a requirement, and in some cases it can be desirable to use a different (or even more than one) namespace. `ResourceLocation`s are used outside the resource system, too, as they happen to be a great way to uniquely identify objects (e.g. [registries][]). - -[respack]: ../resources/client/index.md -[datapack]: ../resources/server/index.md -[registries]: ./registries.md diff --git a/docs/gameeffects/sounds.md b/docs/gameeffects/sounds.md index 070f108b9..c38bac81b 100644 --- a/docs/gameeffects/sounds.md +++ b/docs/gameeffects/sounds.md @@ -104,7 +104,7 @@ Note that each takes a `SoundEvent`, the ones registered above. Additionally, th - **Server Behavior**: Method is client-only. - **Usage**: Just like the ones in `Level`, these two overrides in the player classes seem to be for code that runs together on both sides. The client handles playing the sound to the user, while the server handles everyone else hearing it without re-playing to the original user. -[loc]: ../concepts/resources.md#resourcelocation +[loc]: ../misc/resourcelocation.md [wiki]: https://minecraft.wiki/w/Sounds.json [datagen]: ../datagen/client/sounds.md [registration]: ../concepts/registries.md#methods-for-registering diff --git a/docs/items/index.md b/docs/items/index.md index 12deb27ad..ef37d90e6 100644 --- a/docs/items/index.md +++ b/docs/items/index.md @@ -54,6 +54,53 @@ Directly using `Item` only allows for very basic items. If you want to add funct 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]. +### `DeferredRegister.Items` + +All registries use `DeferredRegister` to register their contents, and items are no exceptions. However, due to the fact that adding new items is such an essential feature of an overwhelming amount of mods, NeoForge provides the `DeferredRegister.Items` helper class that extends `DeferredRegister` and provides some item-specific helpers: + +```java +public static final DeferredRegister.Items ITEMS = DeferredRegister.createItems(ExampleMod.MOD_ID); + +public static final Supplier EXAMPLE_ITEM = ITEMS.registerItem( + "example_item", + Item::new, // The factory that the properties will be passed into. + new Item.Properties() // The properties to use. +); +``` + +Internally, this will simply call `ITEMS.register("example_item", () -> new Item(new Item.Properties()))` by applying the properties parameter to the provided item factory (which is commonly the constructor). + +If you want to use `Item::new`, you can leave out the factory entirely: + +```java +public static final Supplier EXAMPLE_ITEM = ITEMS.registerItem( + "example_item", + new ItemBehaviour.Properties() // The properties to use. +); +``` + +This does the exact same as the previous example, but is slightly shorter. Of course, if you want to use a subclass of `Item` and not `Item` itself, you will have to use the previous method instead. + +Both of these methods also have `simple` counterparts that omit the `new Item.Properties()` parameter: + +```java +public static final Supplier EXAMPLE_ITEM = ITEMS.registerSimpleItem("example_item", Item::new); +// Variant that also omits the Item::new parameter +public static final Supplier EXAMPLE_ITEM = ITEMS.registerSimpleItem("example_item"); +``` + +Finally, there's also shortcuts for block items: + +```java +public static final Supplier EXAMPLE_BLOCK_ITEM = ITEMS.registerSimpleBlockItem("example_block", ExampleBlocksClass.EXAMPLE_BLOCK, new Item.Properties()); +// Variant that omits the properties parameter: +public static final Supplier EXAMPLE_BLOCK_ITEM = ITEMS.registerSimpleBlockItem("example_block", ExampleBlocksClass.EXAMPLE_BLOCK); +// Variant that omits the name parameter, instead using the block's registry name: +public static final Supplier EXAMPLE_BLOCK_ITEM = ITEMS.registerSimpleBlockItem(ExampleBlocksClass.EXAMPLE_BLOCK, new Item.Properties()); +// Variant that omits both the name and the properties: +public static final Supplier EXAMPLE_BLOCK_ITEM = ITEMS.registerSimpleBlockItem(ExampleBlocksClass.EXAMPLE_BLOCK); +``` + ### Resources If you register your item and get your item (via `/give` or through a [creative tab][creativetabs]), you will find it to be missing a proper model and texture. This is because textures and models are handled by Minecraft's resource system. diff --git a/docs/misc/resourcelocation.md b/docs/misc/resourcelocation.md new file mode 100644 index 000000000..9b2d07fb6 --- /dev/null +++ b/docs/misc/resourcelocation.md @@ -0,0 +1,46 @@ +# Resource Locations + +`ResourceLocation`s are one of the most important things in Minecraft. They are used as keys in [registries][registries], as identifiers for data or resource files, as references to models in code, and in a lot of other places. A `ResourceLocation` consists of two parts: a namespace and a path, separated by a `:`. + +The namespace denotes what mod or datapack the location refers to. For example, a mod with the mod id `examplemod` will use the `examplemod` namespace. Minecraft uses the `minecraft` namespace. Extra namespaces can be defined at will simply by creating a corresponding data folder, this is usually done by datapacks to keep their logic separate from the point where they integrate with vanilla. + +The path is a reference to whatever object you want, inside your namespace. For example, `minecraft:cow` is a reference to something named `cow` in the `minecraft` namespace - usually this location would be used to get the cow entity from the entity registry. Another example would be `examplemod:example_item`, which would probably be used to get your mod's `example_item` from the item registry. + +`ResourceLocation`s may only contain lowercase letters, digits, underscores, dots and hyphens. Paths may additionally contain forward slashes. Note that due to Java module restrictions, mod ids may not contain hyphens, which by extension means that mod namespaces may not contain hyphens either (they are still permitted in paths). + +:::info +A `ResourceLocation` on its own says nothing about what kind of objects we are using it for. Objects named `minecraft:dirt` exist in multiple places, for example. It is up to whatever receives the `ResourceLocation` to associate an object with it. +::: + +A new `ResourceLocation` can be created by calling `new ResourceLocation("examplemod", "example_item")` or `new ResourceLocation("examplemod:example_item")`. If the latter is used with a string that does not contain a `:`, the entire string will be used as the path, and `minecraft` will be used as the namespace. So for example, `new ResourceLocation("example_item")` will result in `minecraft:example_item`. + +The namespace and path of a `ResourceLocation` can be retrieved using `ResourceLocation#getNamespace()` and `#getPath()`, respectively, and the combined form can be retrieved through `ResourceLocation#toString`. + +`ResourceLocation`s are immutable. All utility methods on `ResourceLocation`, such as `withPrefix` or `withSuffix`, return a new `ResourceLocation`. + +## Resolving `ResourceLocation`s + +Some places, for example registries, use `ResourceLocation`s directly. Some other places, however, will resolve the `ResourceLocation` as needed. For example: + +- `ResourceLocation`s are used as identifiers for GUI background. For example, the furnace GUI uses the resource location `minecraft:textures/gui/container/furnace.png`. This maps to the file `assets/minecraft/textures/gui/container/furnace.png` on disk. Note that the `.png` suffix is required in this resource location. +- `ResourceLocation`s are used as identifiers for block models. For example, the block model of dirt uses the resource location `minecraft:models/block/dirt`. This maps to the file `assets/minecraft/models/block/dirt.json` on disk. Note that the `.json` suffix is NOT required in this resource location. +- `ResourceLocation`s are used as identifiers for recipes. For example, the iron block crafting recipe uses the resource location `minecraft:iron_block`. This maps to the file `data/minecraft/recipes/iron_block.json` on disk. Note that the `.json` suffix is not required here. Note as well that this resource location automatically maps into the `recipes` subfolder. + +Whether the `ResourceLocation` expects a file suffix, or what exactly the resource location resolves to, depends on the use case. + +## `ModelResourceLocation`s + +`ModelResourceLocation`s are a special kind of resource location that includes a third part, called the variant. Minecraft uses these mainly to differentiate between different variants of item models, where the different variants are used in different display contexts (for example with tridents, which have different models in first person, third person and inventories). + +The variant is appended to the regular resource location, along with a `#`. For example, the full name of the diamond sword's item model is `minecraft:diamond_sword#inventory`. However, in most contexts, the `inventory` variant can be omitted. + +`ModelResourceLocation` is a [client only][sides] class. This means that servers referencing this class will crash with a `NoClassDefError`. + +## `ResourceKey`s + +`ResourceKey`s combine a registry id with a registry name. An example would be a registry key with the registry id `minecraft:item` and the registry name `minecraft:diamond_sword`. Unlike a `ResourceLocation`, `ResourceKey`s actually refer to a unique element, thus being able to clearly identify an element. They are most commonly used in contexts where many different registries come in contact with one another. A common use case are datapacks, especially worldgen. + +A new `ResourceKey` can be created through the static method `ResourceKey#create(ResourceKey>, ResourceLocation)`. The second parameter here is the registry name, while the first parameter is what is known as a registry key. Registry keys are a special kind of `ResourceKey` whose registry is the root registry (i.e. the registry of all other registries). A registry key can be created via `ResourceKey#createRegistryKey(ResourceLocation)` with the desired registry's id. + +[registries]: ../concepts/registries.md +[sides]: ../concepts/sides.md diff --git a/docs/resources/client/index.md b/docs/resources/client/index.md index f8ea1ed7c..c81a5637b 100644 --- a/docs/resources/client/index.md +++ b/docs/resources/client/index.md @@ -12,4 +12,4 @@ Additional reading: [Resource Locations][resourcelocation] [respack]: https://minecraft.wiki/w/Resource_Pack [createrespack]: https://minecraft.wiki/w/Tutorials/Creating_a_resource_pack -[resourcelocation]: ../../concepts/resources.md#ResourceLocation +[resourcelocation]: ../../misc/resourcelocation.md diff --git a/docs/resources/client/models/index.md b/docs/resources/client/models/index.md index a999c5622..d9f2df9cf 100644 --- a/docs/resources/client/models/index.md +++ b/docs/resources/client/models/index.md @@ -17,7 +17,7 @@ JSON models only support cuboid elements; there is no way to express a triangula Textures, like models, are contained within resource packs and are referred to with `ResourceLocation`s. In Minecraft, the [UV coordinates][uv] (0,0) are taken to mean the **top-left** corner. UVs are *always* from 0 to 16. If a texture is larger or smaller, the coordinates are scaled to fit. A texture should also be square, and the side length of a texture should be a power of two, as doing otherwise breaks mipmapping (e.g. 1x1, 2x2, 8x8, 16x16, and 128x128 are good. 5x5 and 30x30 are not recommended because they are not powers of 2. 5x10 and 4x8 are completely broken as they are not square.). Textures should only ever be not a square if it is [animated][animated]. [models]: https://minecraft.wiki/w/Tutorials/Models#File_path -[resloc]: ../../../concepts/resources.md#resourcelocation +[resloc]: ../../../misc/resourcelocation.md [statemodel]: https://minecraft.wiki/w/Tutorials/Models#Block_states [itemmodels]: https://minecraft.wiki/w/Tutorials/Models#Item_models [state]: ../../../blocks/states.md diff --git a/docs/resources/server/glm.md b/docs/resources/server/glm.md index 8676f1f12..b935aac05 100644 --- a/docs/resources/server/glm.md +++ b/docs/resources/server/glm.md @@ -140,7 +140,7 @@ public static final RegistryObject> = REGISTRAR.register( [Examples][examples] can be found on the Forge Git repository, including silk touch and smelting effects. [tags]: ./tags.md -[resloc]: ../../concepts/resources.md#ResourceLocation +[resloc]: ../../misc/resourcelocation.md [codec]: #the-loot-modifier-codec [registered]: ../../concepts/registries.md#methods-for-registering [codecdef]: ../../datastorage/codecs.md diff --git a/docs/resources/server/index.md b/docs/resources/server/index.md index bc5b93139..3c097338c 100644 --- a/docs/resources/server/index.md +++ b/docs/resources/server/index.md @@ -11,4 +11,4 @@ Additional reading: [Resource Locations][resourcelocation] [datapack]: https://minecraft.wiki/w/Data_pack [createdatapack]: https://minecraft.wiki/w/Tutorials/Creating_a_data_pack -[resourcelocation]: ../../concepts/resources.md#ResourceLocation +[resourcelocation]: ../../misc/resourcelocation.md From aafbf1baf838fb11fdbc927843d41c852c2d85ae Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Mon, 29 Jan 2024 20:05:38 +0100 Subject: [PATCH 02/59] rework side documentation --- docs/concepts/sides.md | 138 ++++++++++++++--------------------------- 1 file changed, 46 insertions(+), 92 deletions(-) diff --git a/docs/concepts/sides.md b/docs/concepts/sides.md index c0cd4e797..c6a122217 100644 --- a/docs/concepts/sides.md +++ b/docs/concepts/sides.md @@ -1,121 +1,75 @@ -Sides in Minecraft -=================== +# Sides -A very important concept to understand when modding Minecraft are the two sides: *client* and *server*. There are many, many common misconceptions and mistakes regarding siding, which can lead to bugs that might not crash the game, but can rather have unintended and often unpredictable effects. +Like many other programs, Minecraft follows a client-server concept, where the client is responsible for displaying the data, while the server is responsible for updating them. When using these terms, we have a fairly intuitive understanding of what we mean... right? -Different Kinds of Sides ------------------------- +Turns out, not so much. A lot of the confusion stems from Minecraft having two different concepts of sides, depending on the context: the physical and the logical side. -When we say "client" or "server", it usually follows with a fairly intuitive understanding of what part of the game we are talking about. After all, a client is what the user interacts with, and a server is where the user connects for a multiplayer game. Easy, right? +## Logical vs. Physical Side -As it turns out, there can be some ambiguity even with two such terms. Here we disambiguate the four possible meanings of "client" and "server": +### The Physical Side -* Physical client - The *physical client* is the entire program that runs whenever you launch Minecraft from the launcher. All threads, processes, and services that run during the game's graphical, interactable lifetime are part of the physical client. -* Physical server - Often known as the dedicated server, the *physical server* is the entire program that runs whenever you launch any sort of `minecraft_server.jar` that does not bring up a playable GUI. -* Logical server - The *logical server* is what runs game logic: mob spawning, weather, updating inventories, health, AI, and all other game mechanics. The logical server is present within a physical server, but it also can run inside a physical client together with a logical client, as a single player world. The logical server always runs in a thread named the `Server Thread`. -* Logical client - The *logical client* is what accepts input from the player and relays it to the logical server. In addition, it also receives information from the logical server and makes it available graphically to the player. The logical client runs in the `Render Thread`, though often several other threads are spawned to handle things like audio and chunk render batching. +When you open your Minecraft launcher, select a Minecraft installation and press play, you boot up a **physical client**. The word "physical" is used here in the sense of "this is a client program". This especially means that client-side functionality, such as all the rendering stuff, is available here and can be used as needed. The **physical server**, on the other hand, is what opens when you launch a Minecraft server JAR. While the Minecraft server comes with a rudimentary GUI, it is missing all client-only functionality. Most notably, this means that various client classes are missing from the server JAR. Calling these classes on the physical server will lead to missing class errors, i.e. crashes, so we need to safeguard against this. -In the NeoForge codebase, the physical side is represented by an enum called `Dist`, while the logical side is represented by an enum called `LogicalSide`. - -Performing Side-Specific Operations ------------------------------------ - -### `Level#isClientSide` +### The Logical Side -This boolean check will be your most used way to check sides. Querying this field on a `Level` object establishes the **logical** side the level belongs to. That is, if this field is `true`, the level is currently running on the logical client. If the field is `false`, the level is running on the logical server. It follows that the physical server will always contain `false` in this field, but we cannot assume that `false` implies a physical server, since this field can also be `false` for the logical server inside a physical client (in other words, a single player world). - -Use this check whenever you need to determine if game logic and other mechanics should be run. For example, if you want to damage the player every time they click your block, or have your machine process dirt into diamonds, you should only do so after ensuring `#isClientSide` is `false`. Applying game logic to the logical client can cause desynchronization (ghost entities, desynchronized stats, etc.) in the best case, and crashes in the worst case. +The logical side is mainly focused on the internal program structure of Minecraft. The **logical server** is where the game logic runs. Things like time and weather changing, entity ticking, entity spawning, etc. all run on the server. All kinds of data, such as inventory contents, are the server's responsibility as well. The **logical client**, on the other hand, is responsible for displaying everything there is to display. Minecraft keeps all the client code in an isolated `net.minecraft.client` package, and runs it in a separate thread called the Render Thread, while everything else is considered common (i.e. client and server) code. -This check should be used as your go-to default. Aside from `DistExecutor`, rarely will you need the other ways of determining side and adjusting behavior. +### What's the Difference? -### `DistExecutor` +The difference between physical and logical sides is best exemplified by two scenarios: -Considering the use of a single "universal" jar for client and server mods, and the separation of the physical sides into two jars, an important question comes to mind: How do we use code that is only present on one physical side? All code in `net.minecraft.client` is only present on the physical client. If any class you write references those names in any way, they will crash the game when that respective class is loaded in an environment where those names do not exist. A very common mistake in beginners is to call `Minecraft.getInstance().()` in block or block entity classes, which will crash any physical server as soon as the class is loaded. +- The player joins a **multiplayer** world. This is fairly straightforward: The player's logical and physical client connects to a logical server somewhere else - the player does not care where; so long as they can connect, that's all the client knows of, and all the client needs to know. +- The player joins a **singleplayer** world. This is where things get interesting. The player's physical client spins up a logical server and then, now in the role of the logical client, connects to that logical server on the same machine. If you are familiar with networking, you can think of it as a connection to `localhost` (only conceptually; there are no actual sockets or similar involved). -How do we resolve this? Luckily, FML has `DistExecutor`, which provides various methods to run different methods on different physical sides, or a single method only on one side. +These two scenarios also show the main problem with this: If a logical server can work with your code, that alone doesn't guarantee that a physical server will be able to work with as well. This is why you should always test with dedicated servers to check for unexpected behavior. `NoClassDefFoundError`s and `ClassNotFoundException`s due to incorrect client and server separation are among the most common errors there are in modding. Another common mistake is working with static fields and accessing them from both logical sides; this is particularly tricky because there's usually no indication that something is wrong. -:::note -It is important to understand that FML checks based on the **physical** side. A single player world (logical server + logical client within a physical client) will always use `Dist.CLIENT`! +:::tip +If you need to transfer data from one side to another, you must [send a packet][networking]. ::: -`DistExecutor` works by taking in a supplied supplier executing a method, effectively preventing classloading by taking advantage of the [`invokedynamic` JVM instruction][invokedynamic]. The executed method should be static and within a different class. Additionally, if no parameters are present for the static method, a method reference should be used instead of a supplier executing a method. - -There are two main methods within `DistExecutor`: `#runWhenOn` and `#callWhenOn`. The methods take in the physical side the executing method should run on and the supplied executing method which either runs or returns a result respectively. - -These two methods are subdivided further into `#safe*` and `#unsafe*` variants. Safe and unsafe variants are misnomers for their purposes. The main difference is that when in a development environment, the `#safe*` methods will validate that the supplied executing method is a lambda returning a method reference to another class with an error being thrown otherwise. Within the production environment, `#safe*` and `#unsafe*` are functionally the same. - -```java -// In a client class: ExampleClass -public static void unsafeRunMethodExample(Object param1, Object param2) { - // ... -} - -public static Object safeCallMethodExample() { - // ... -} - -// In some common class -DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> ExampleClass.unsafeRunMethodExample(var1, var2)); - -DistExecutor.safeCallWhenOn(Dist.CLIENT, () -> ExampleClass::safeCallMethodExample); - -``` +In the NeoForge codebase, the physical side is represented by an enum called `Dist`, while the logical side is represented by an enum called `LogicalSide`. -:::caution -Due to a change in how `invokedynamic` works in Java 9+, all `#safe*` variants of the `DistExecutor` methods throw the original exception wrapped within a `BootstrapMethodError` in the development environment. `#unsafe*` variants or a check to [`FMLEnvironment#dist`][dist] should be used instead. +:::info +Historically, server JARs have had classes the client did not. This is not the case anymore in modern versions; physical servers are a subset of physical clients, if you will. ::: -### Thread Groups +## Performing Side-Specific Operations -If `Thread.currentThread().getThreadGroup() == SidedThreadGroups.SERVER` is true, it is likely the current thread is on the logical server. Otherwise, it is likely on the logical client. This is useful to retrieve the **logical** side when you do not have access to a `Level` object to check `isClientSide`. It *guesses* which logical side you are on by looking at the group of the currently running thread. Because it is a guess, this method should only be used when other options have been exhausted. In nearly every case, you should prefer checking `Level#isClientSide`. +### `Level#isClientSide()` -### `FMLEnvironment#dist` and `@OnlyIn` +This boolean check will be your most used way to check sides. Querying this field on a `Level` object establishes the **logical** side the level belongs to: If this field is `true`, the level is running on the logical client. If the field is `false`, the level is running on the logical server. It follows that the physical server will always contain `false` in this field, but we cannot assume that `false` implies a physical server, since this field can also be `false` for the logical server inside a physical client (i.e. a singleplayer world). -`FMLEnvironment#dist` holds the **physical** side your code is running on. Since it is determined at startup, it does not rely on guessing to return its result. The number of use cases for this is limited, however. - -Annotating a method or field with the `@OnlyIn(Dist)` annotation indicates to the loader that the respective member should be completely stripped out of the definition not on the specified **physical** side. Usually, these are only seen when browsing through the decompiled Minecraft code, indicating methods that the Mojang obfuscator stripped out. There is **NO** reason for using this annotation directly. Use `DistExecutor` or a check on `FMLEnvironment#dist` instead. - -Common Mistakes ---------------- - -### Reaching Across Logical Sides - -Whenever you want to send information from one logical side to another, you must **always** use network packets. It is incredibly tempting, when in a single player scenario, to directly transfer data from the logical server to the logical client. - -This is actually very commonly inadvertently done through static fields. Since the logical client and logical server share the same JVM in a single player scenario, both threads writing to and reading from static fields will cause all sorts of race conditions and the classical issues associated with threading. - -This mistake can also be made explicitly by accessing physical client-only classes such as `Minecraft` from common code that runs or can run on the logical server. This mistake is easy to miss for beginners who debug in a physical client. The code will work there, but it will immediately crash on a physical server. - - -Writing One-Sided Mods ----------------------- - -In recent versions, Minecraft Forge has removed a "sidedness" attribute from the mods.toml. This means that your mods are expected to work whether they are loaded on the physical client or the physical server. So for one-sided mods, you would typically register your event handlers inside a `DistExecutor#safeRunWhenOn` or `DistExecutor#unsafeRunWhenOn` instead of directly calling the relevant registration methods in your mod constructor. Basically, if your mod is loaded on the wrong side, it should simply do nothing, listen to no events, and so on. A one-sided mod by nature should not register blocks, items, ... since they would need to be available on the other side, too. +Use this check whenever you need to determine if game logic and other mechanics should be run. For example, if you want to damage the player every time they click your block, or have your machine process dirt into diamonds, you should only do so after ensuring `#isClientSide` is `false`. Applying game logic to the logical client can cause desynchronization (ghost entities, desynchronized stats, etc.) in the best case, and crashes in the worst case. -Additionally, if your mod is one-sided, it typically does not forbid the user from joining a server that is lacking that mod. Therefore, you should set the `displayTest` property in your [mods.toml][structuring] to whatever value is necessary. +:::tip +This check should be used as your go-to default. Whenever you have a `Level` available, use this check. +::: -```toml -[[mods]] - # ... +### `FMLEnvironment.dist` - # MATCH_VERSION means that your mod will cause a red X if the versions on client and server differ. This is the default behaviour and should be what you choose if you have server and client elements to your mod. - # IGNORE_SERVER_VERSION means that your mod will not cause a red X if it's present on the server but not on the client. This is what you should use if you're a server only mod. - # IGNORE_ALL_VERSION means that your mod will not cause a red X if it's present on the client or the server. This is a special case and should only be used if your mod has no server component. - # NONE means that no display test is set on your mod. You need to do this yourself, see IExtensionPoint.DisplayTest for more information. You can define any scheme you wish with this value. - # IMPORTANT NOTE: this is NOT an instruction as to which environments (CLIENT or DEDICATED SERVER) your mod loads on. Your mod should load (and maybe do nothing!) whereever it finds itself. - displayTest="IGNORE_ALL_VERSION" # MATCH_VERSION is the default if nothing is specified (#optional) -``` +`FMLEnvironment.dist` is the **physical** counterpart to a `Level#isClientSide()` check. If this field is `Dist.CLIENT`, you are on a physical client. If the field is `Dist.SERVER`, you are on a physical server. -If a custom display test is to be used, then the `displayTest` option should be set to `NONE`, and an `IExtensionPoint$DisplayTest` extension should be registered: +Checking the physical environment is important when dealing with client-only classes. All calls to client-only code should always be encased in a check for `Dist.CLIENT`, and then call to a separate class to prevent accidental classloading: ```java -//Make sure the mod being absent on the other network side does not cause the client to display the server as incompatible -ModLoadingContext.get().registerExtensionPoint(IExtensionPoint.DisplayTest.class, () -> new IExtensionPoint.DisplayTest(() -> NetworkConstants.IGNORESERVERONLY, (a, b) -> true)); -``` +public class SomeCommonClass { + public void someCommonMethod() { + //SomeClientClass will be loaded if and only if you are on a physical client + if (FMLEnvironment.dist == Dist.CLIENT) { + SomeClientClass.someClientMethod(); + } + } +} -This tells the client that it should ignore the server version being absent, and the server that it should not tell the client this mod should be present. So this snippet works both for client- and server-only-sided mods. +public class SomeClientClass { + public void someClientMethod() { + Minecraft.getInstance().whatever(); + } +} +``` +:::tip +Mods are generally expected to work on either side. This especially means that if you are developing a client-only mod, you should verify that the mod actually runs on a physical client, and no-op in the event that it does not. +::: -[invokedynamic]: https://docs.oracle.com/javase/specs/jvms/se17/html/jvms-6.html#jvms-6.5.invokedynamic -[dist]: #fmlenvironmentdist-and-onlyin -[structuring]: ../gettingstarted/modfiles.md#modstoml +[networking]: ../networking/index.md From a7483045414cef99e73ad73e716b512b8740bc59 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Mon, 29 Jan 2024 22:21:36 +0100 Subject: [PATCH 03/59] rework events and lifecycle documentation --- docs/concepts/events.md | 208 +++++++++++++++++++++---------------- docs/concepts/lifecycle.md | 77 -------------- docs/concepts/sides.md | 2 +- 3 files changed, 118 insertions(+), 169 deletions(-) delete mode 100644 docs/concepts/lifecycle.md diff --git a/docs/concepts/events.md b/docs/concepts/events.md index 148eee0d0..aaf7dce99 100644 --- a/docs/concepts/events.md +++ b/docs/concepts/events.md @@ -1,148 +1,174 @@ -Events -====== +# Events -Forge uses an event bus that allows mods to intercept events from various Vanilla and mod behaviors. +One of NeoForge's main features is the event system. Events are fired for various things that happen in the game. For example, there are events for when the player right clicks, when a player or another entity jumps, when blocks are rendered, when the game is loaded, etc. A modder can subscribe event handlers to each of these events, and then perform their desired behavior inside these event handlers. -Example: An event can be used to perform an action when a Vanilla stick is right-clicked. +Events are fired on their respective event bus. The most important bus is `NeoForge.EVENT_BUS`. Besides that, during startup, a mod bus is spawned for each loaded mod and passed into the mod's constructor; mod bus event handlers can run in parallel, dramatically increasing startup speed. See [below][modbus] for more information. -The main event bus used for most events is located at `NeoForge#EVENT_BUS`. There is another event bus for mod specific events that you should only use in specific cases. More information about this bus can be [found below](#mod-event-bus). +## Registering an Event Handler -Every event is fired on one of these busses: most events are fired on the main forge event bus, but some are fired on the mod specific event buses. +There are multiple ways to register event handlers. Common for all of those ways is that every event handler is a method with a single event parameter and no result (i.e. return type `void`). -An event handler is some method that has been registered to an event bus. +### `IEventBus#addListener` -Creating an Event Handler -------------------------- - -Event handlers methods have a single parameter and do not return a result. The method could be static or instance depending on implementation. - -Event handlers can be directly registered using `IEventBus#addListener` for or `IEventBus#addGenericListener` for generic events (as denoted by subclassing `GenericEvent`). Either listener adder takes in a consumer representing the method reference. Generic event handlers need to specify the class of the generic as well. Event handlers must be registered within the constructor of the main mod class. +The simplest way to register method handlers is by registering their method reference, like so: ```java -// In the main mod class ExampleMod - -// This event is on the mod bus -private void modEventHandler(RegisterEvent event) { - // Do things here -} - -// This event is on the forge bus -private static void forgeEventHandler(ExplosionEvent.Detonate event) { - // ... +@Mod("yourmodid") +public class YourMod { + public YourMod(IEventBus modBus) { + NeoForge.EVENT_BUS.addListener(YourMod::onLivingJump); + } + + // Heals an entity by half a heart every time they jump. + private static void onLivingJump(LivingJumpEvent event) { + event.getEntity().heal(1); + } } - -// In the mod constructor -modEventBus.addListener(this::modEventHandler); -forgeEventBus.addListener(ExampleMod::forgeEventHandler); ``` -### Instance Annotated Event Handlers +### `@SubscribeEvent` -This event handler listens for the `EntityItemPickupEvent`, which is, as the name states, posted to the event bus whenever an `Entity` picks up an item. +Alternatively, event handlers can be annotation-driven by creating an event handler method and annotating it with `@SubscribeEvent`. Then, you can pass an instance of the encompassing class to the event bus, registering all `@SubscribeEvent`-annotated event handlers of that instance: ```java -public class MyForgeEventHandler { +public class EventHandler { @SubscribeEvent - public void pickupItem(EntityItemPickupEvent event) { - System.out.println("Item picked up!"); - } + public void onLivingJump(LivingJumpEvent event) { + event.getEntity().heal(1); + } } -``` - -To register this event handler, use `NeoForge.EVENT_BUS.register(...)` and pass it an instance of the class the event handler is within. If you want to register this handler to the mod specific event bus, you should use `FMLJavaModLoadingContext.get().getModEventBus().register(...)` instead. -### Static Annotated Event Handlers +@Mod("yourmodid") +public class YourMod { + public YourMod(IEventBus modBus) { + NeoForge.EVENT_BUS.addListener(new EventHandler()); + } +} +``` -An event handler may also be static. The handling method is still annotated with `@SubscribeEvent`. The only difference from an instance handler is that it is also marked `static`. In order to register a static event handler, an instance of the class won't do. The `Class` itself has to be passed in. An example: +You can also do it statically. Simply make all event handlers static, and instead of a class instance, pass in the class itself: ```java -public class MyStaticForgeEventHandler { +public class EventHandler { @SubscribeEvent - public static void arrowNocked(ArrowNockEvent event) { - System.out.println("Arrow nocked!"); - } + public static void onLivingJump(LivingJumpEvent event) { + event.getEntity().heal(1); + } } -``` -which must be registered like this: `NeoForge.EVENT_BUS.register(MyStaticForgeEventHandler.class)`. +@Mod("yourmodid") +public class YourMod { + public YourMod(IEventBus modBus) { + NeoForge.EVENT_BUS.addListener(EventHandler.class); + } +} +``` -### Automatically Registering Static Event Handlers +:::info +`@SubscribeEvent` annotated methods must not be `private`. Any out of `package-private`, `protected` and `public` will work. +::: -A class may be annotated with the `@Mod$EventBusSubscriber` annotation. Such a class is automatically registered to `NeoForge#EVENT_BUS` when the `@Mod` class itself is constructed. This is essentially equivalent to adding `NeoForge.EVENT_BUS.register(AnnotatedClass.class);` at the end of the `@Mod` class's constructor. +### `@Mod.EventBusSubscriber` -You can pass the bus you want to listen to the `@Mod$EventBusSubscriber` annotation. It is recommended you also specify the mod id, since the annotation process may not be able to figure it out, and the bus you are registering to, since it serves as a reminder to make sure you are on the correct one. You can also specify the `Dist`s or physical sides to load this event subscriber on. This can be used to not load client specific event subscribers on the dedicated server. +We can go one step further and also annotate the event handler class with `@Mod.EventBusSubscriber`. This annotation is discovered automatically by NeoForge, allowing you to remove all event-related code from the mod constructor. In essence, it is equivalent to calling `NeoForge.EVENT_BUS.register(EventHandler.class)` at the end of the mod constructor. This means that all handlers must be static, too. -An example for a static event listener listening to `RenderLevelStageEvent` which will only be called on the client: +While not required, it is highly recommended to specify the `modid` parameter in the annotation, in order to make debugging easier (especially when it comes to mod conflicts). ```java -@Mod.EventBusSubscriber(modid = "mymod", bus = Bus.FORGE, value = Dist.CLIENT) -public class MyStaticClientOnlyEventHandler { - @SubscribeEvent - public static void drawLast(RenderLevelStageEvent event) { - System.out.println("Drawing!"); - } +@Mod.EventBusSubscriber(modid = "yourmodid") +public class EventHandler { + @SubscribeEvent + public static void onLivingJump(LivingJumpEvent event) { + event.getEntity().heal(1); + } } ``` -:::note -This does not register an instance of the class; it registers the class itself (i.e. the event handling methods must be static). -::: +## Event Options -Canceling ---------- +### Fields and Methods -If an event can be canceled, it will be marked with the `@Cancelable` annotation, and the method `Event#isCancelable()` will return `true`. The cancel state of a cancelable event may be modified by calling `Event#setCanceled(boolean canceled)`, wherein passing the boolean value `true` is interpreted as canceling the event, and passing the boolean value `false` is interpreted as "un-canceling" the event. However, if the event cannot be canceled (as defined by `Event#isCancelable()`), an `UnsupportedOperationException` will be thrown regardless of the passed boolean value, since the cancel state of a non-cancelable event event is considered immutable. +Probably the most obvious part. Most events contain context for the event handler to use, such as an entity causing the event or a level the event occurs in. + +### Hierarchy + +In order to use the advantages of inheritance, some events do not directly extend `Event`, but one of its subclasses, for example `BlockEvent` (which contains block context for block-related events) or `EntityEvent` (which similarly contains entity context) and its subclasses `LivingEvent` (for `LivingEntity`-specific context) and `PlayerEvent` (for `Player`-specific context). These context-providing super events are `abstract` and cannot be listened to. :::danger -Not all events can be canceled! Attempting to cancel an event that is not cancelable will result in an unchecked `UnsupportedOperationException` being thrown, which is expected to result in the game crashing! Always check that an event can be canceled using `Event#isCancelable()` before attempting to cancel it! +If you listen to an `abstract` event, your game will crash, as this is not what you want. Listen to one of its sub events instead. ::: -Results -------- +### Cancellable Events + +Some events implement the `ICancellableEvent` interface. These events can be cancelled using `#setCanceled(boolean canceled)`, and the cancellation status can be checked using `#isCanceled()`. If an event is cancelled, other event handlers for this event will not run, and some kind of behavior that is associated with "cancelling" is enabled. For example, cancelling `LivingJumpEvent` will prevent the jump. + +Event handlers can opt to explicitly receive cancelled events. This is done by setting the `receiveCanceled` parameter in `IEventBus#addListener` (or `@SubscribeEvent`, depending on your way of attaching the event handlers) to true. -Some events have an `Event$Result`. A result can be one of three things: `DENY` which stops the event, `DEFAULT` which uses the Vanilla behavior, and `ALLOW` which forces the action to take place, regardless if it would have originally. The result of an event can be set by calling `#setResult` with an `Event$Result` on the event. Not all events have results; an event with a result will be annotated with `@HasResult`. +### Results + +Some events have a `Result`. A `Result` can be one of three things: `DENY` which stops the event, `ALLOW` which force-runs the event, and `DEFAULT` which uses the Vanilla behavior. The result of an event can be set by calling `Event#setResult`. Not all events have results; an event with a result will be annotated with `@HasResult`. :::caution -Different events may use results in different ways, refer to the event's JavaDoc before using the result. +Results are deprecated and will be replaced by more specific per-event results soon. ::: -Priority --------- +### Priority -Event handler methods (marked with `@SubscribeEvent`) have a priority. You can set the priority of an event handler method by setting the `priority` value of the annotation. The priority can be any value of the `EventPriority` enum (`HIGHEST`, `HIGH`, `NORMAL`, `LOW`, and `LOWEST`). Event handlers with priority `HIGHEST` are executed first and from there in descending order until `LOWEST` events which are executed last. +Event handlers can optionally get assigned a priority. The `EventPriority` enum contains five values: `HIGHEST`, `HIGH`, `NORMAL` (default), `LOW` and `LOWEST`. Events are executed from highest to lowest priority, with undefined order for two events of the same priority. -Sub Events ----------- +Priorities can be defined by setting the `priority` parameter in `IEventBus#addListener` or `@SubscribeEvent`, depending on how you attach event handlers. -Many events have different variations of themselves. These can be different but all based around one common factor (e.g. `PlayerEvent`) or can be an event that has multiple phases (e.g. `PotionBrewEvent`). Take note that if you listen to the parent event class, you will receive calls to your method for *all* subclasses. +### Sided Events -Mod Event Bus -------------- +Some events are only fired on one [side][side]. Common examples include the various render events, which are only fired on the client. Since client-only events generally need to access other client-only parts of the Minecraft codebase, they need to be registered accordingly. -To get access to the mod event bus for your own mod, declare a constructor parameter of type `IModEventBus` in your [mod entrypoint][ctor-injection]. +Event handlers that use `IEventBus#addListener()` should use a `FMLEnvironment.dist` check and a separate client-only class, as outlined in the article on sides. -The mod event bus is primarily used for listening to lifecycle events in which mods should initialize. Each event on the mod bus is required to implement `IModBusEvent`. Many of these events are also ran in parallel so mods can be initialized at the same time. This does mean you can't directly execute code from other mods in these events. Use the `InterModComms` system for that. +Event handlers that use `@Mod.EventBusSubscriber` can specify the side as the `value` parameter of the annotation, for example `@Mod.EventBusSubscriber(value = Dist.CLIENT, modid = "yourmodid")`. -These are the four most commonly used lifecycle events that are called during mod initialization on the mod event bus: +## Event Buses -* `FMLCommonSetupEvent` -* `FMLClientSetupEvent` & `FMLDedicatedServerSetupEvent` -* `InterModEnqueueEvent` -* `InterModProcessEvent` +While most events are posted on the `NeoForge.EVENT_BUS`, some events are posted on the mod event bus instead. These are generally called mod bus events. Mod bus events can be distinguished from regular events by their superinterface `IModBusEvent`. -:::note -The `FMLClientSetupEvent` and `FMLDedicatedServerSetupEvent` are only called on their respective distribution. -::: +The mod event bus is passed to you as a parameter in the mod constructor, and you can then subscribe mod bus events to it. If you use `@Mod.EventBusSubscriber`, you can also set the bus as an annotation parameter, like so: `@Mod.EventBusSubscriber(bus = Bus.MOD, modid = "yourmodid")`. The default bus is `Bus.FORGE`. + +### The Mod Lifecycle + +Most mod bus events are what is known as lifecycle events. Lifecycle events run once in every mod's lifecycle during startup. Many of them are fired in parallel; if you want to run code from one of these events on the main thread, enqueue them using `#enqueueWork(Runnable runnable)`. -These four lifecycle events are all ran in parallel since they all are a subclass of `ParallelDispatchEvent`. If you want to run code on the main thread during any `ParallelDispatchEvent`, you can use the `#enqueueWork` to do so. +The lifecycle generally follows the following order: -Next to the lifecycle events, there are a few miscellaneous events that are fired on the mod event bus where you can register, set up, or initialize various things. Most of these events are not ran in parallel in contrast to the lifecycle events. A few examples: +- The mod constructor is called. Register your event handlers here, or in the next step. +- All `@Mod.EventBusSubscriber`s are called. +- `FMLConstructModEvent` is fired. +- The registry events are fired, these include [`NewRegistryEvent`][newregistry], [`DataPackRegistryEvent.NewRegistry`][newdatapackregistry] and, for each registry, [`RegisterEvent`][registerevent]. +- `FMLCommonSetupEvent` is fired. This is where various miscellaneous setup happens. +- The [sided][side] setup is fired: `FMLClientSetupEvent` if on a physical client, and `FMLDedicatedServerSetupEvent` if on a physical server. +- `InterModComms` are handled (see below). +- `FMLLoadCompleteEvent` is fired. -* `RegisterColorHandlersEvent` -* `ModelEvent$BakingCompleted` -* `TextureStitchEvent` -* `RegisterEvent` +#### `InterModComms` -A good rule of thumb: events are fired on the mod event bus when they should be handled during initialization of a mod. +`InterModComms` is a system that allows modders to send messages to other mods for compatibility features. The class holds the messages for mods, all methods are thread-safe to call. The system is mainly driven by two events: `InterModEnqueueEvent` and `InterModProcessEvent`. + +During `InterModEnqueueEvent`, you can use `InterModComms#sendTo` to send messages to other mods. These methods accept the id of the mod to send the message to, the key associated with the message data (to distinguish between different messages), and a `Supplier` holding the message data. The sender can be optionally specified as well. + +Then, during `InterModProcessEvent`, you can use `InterModComms#getMessages` to get a stream of all received messages as `IMCMessage` objects. These hold the sender of the data, the intended receiver of the data, the data key, and the supplier for the actual data. + +### Other Mod Bus Events + +Next to the lifecycle events, there are a few miscellaneous events that are fired on the mod event bus, mostly for legacy reasons. These are generally events where you can register, set up, or initialize various things. Most of these events are not ran in parallel in contrast to the lifecycle events. A few examples: + +- `RegisterColorHandlersEvent` +- `ModelEvent.BakingCompleted` +- `TextureStitchEvent` + +:::warning +Most of these events will be moved to `NeoForge.EVENT_BUS` eventually. +::: -[ctor-injection]: ../gettingstarted/modfiles.md#javafml-and-mod +[modbus]: #event-buses +[newdatapackregistry]: registries.md#custom-datapack-registries +[newregistry]: registries.md#custom-registries +[registerevent]: registries.md#registerevent +[side]: sides.md diff --git a/docs/concepts/lifecycle.md b/docs/concepts/lifecycle.md deleted file mode 100644 index a5b5090ed..000000000 --- a/docs/concepts/lifecycle.md +++ /dev/null @@ -1,77 +0,0 @@ -Mod Lifecycle -============== - -During the mod loading process, the various lifecycle events are fired on the mod-specific event bus. Many actions are performed during these events, such as [registering objects][registering], preparing for [data generation][datagen], or [communicating with other mods][imc]. - -Event listeners should be registered either using `@EventBusSubscriber(bus = Bus.MOD)` or in the mod constructor: - -```java -@Mod.EventBusSubscriber(modid = "mymod", bus = Mod.EventBusSubscriber.Bus.MOD) -public class MyModEventSubscriber { - @SubscribeEvent - static void onCommonSetup(FMLCommonSetupEvent event) { ... } -} - -@Mod("mymod") -public class MyMod { - public MyMod() { - FMLModLoadingContext.get().getModEventBus().addListener(this::onCommonSetup); - } - - private void onCommonSetup(FMLCommonSetupEvent event) { ... } -} -``` - -:::caution -Most of the lifecycle events are fired in parallel: all mods will concurrently receive the same event. - -Mods *must* take care to be thread-safe, like when calling other mods' APIs or accessing vanilla systems. Defer code for later execution via `ParallelDispatchEvent#enqueueWork`. -::: - -Registry Events ---------------- - -The registry events are fired after the mod instance construction. There are three: `NewRegistryEvent`, `DataPackRegistryEvent$NewRegistry` and `RegisterEvent`. These events are fired synchronously during mod loading. - -`NewRegistryEvent` allows modders to register their own custom registries, using the `RegistryBuilder` class. - -`DataPackRegistryEvent$NewRegistry` allows modders to register custom datapack registries by providing a `Codec` to encode and decode the object from JSON. - -`RegisterEvent` is for [registering objects][registering] into the registries. The event is fired for each registry. - -Data Generation ---------------- - -If the game is setup to run [data generators][datagen], then the `GatherDataEvent` will be the last event to fire. This event is for registering mods' data providers to their associated data generator. This event is also fired synchronously. - -Common Setup ------------- - -`FMLCommonSetupEvent` is for actions that are common to both physical client and server. -This event is fired for multiple mods in parallel, so be careful. -You can use `event.enqueueWork(() -> /* do something */)` to run code that is not thread-safe. - -Sided Setup ------------ - -The sided-setup events are fired on their respective [physical sides][sides]: `FMLClientSetupEvent` on the physical client, and `FMLDedicatedServerSetupEvent` for the dedicated server. This is where physical side-specific initialization should occur, such as registering client-side key bindings. - -InterModComms -------------- - -This is where messages can be sent to mods for cross-mod compatibility. There are two events: `InterModEnqueueEvent` and `InterModProcessEvent`. - -`InterModComms` is the class responsible for holding messages for mods. The methods are safe to call during the lifecycle events, as it is backed by a `ConcurrentMap`. - -During the `InterModEnqueueEvent`, use `InterModComms#sendTo` to send messages to different mods. These methods take in the mod id that will be sent the message, the key associated with the message data, and a supplier holding the message data. Additionally, the sender of the message can also be specified, but by default it will be the mod id of the caller. - -Then during the `InterModProcessEvent`, use `InterModComms#getMessages` to get a stream of all received messages. The mod id supplied will almost always be the mod id of the mod the method is called on. Additionally, a predicate can be specified to filter out the message keys. This will return a stream of `IMCMessage`s which hold the sender of the data, the receiver of the data, the data key, and the supplied data itself. - -:::tip -There are two other lifecycle events: `FMLConstructModEvent`, fired directly after mod instance construction but before the `RegisterEvent`, and `FMLLoadCompleteEvent`, fired after the `InterModComms` events, for when the mod loading process is complete. -::: - -[registering]: ./registries.md#methods-for-registering -[datagen]: ../datagen/index.md -[imc]: ./lifecycle.md#intermodcomms -[sides]: ./sides.md diff --git a/docs/concepts/sides.md b/docs/concepts/sides.md index c6a122217..f1c612600 100644 --- a/docs/concepts/sides.md +++ b/docs/concepts/sides.md @@ -8,7 +8,7 @@ Turns out, not so much. A lot of the confusion stems from Minecraft having two d ### The Physical Side -When you open your Minecraft launcher, select a Minecraft installation and press play, you boot up a **physical client**. The word "physical" is used here in the sense of "this is a client program". This especially means that client-side functionality, such as all the rendering stuff, is available here and can be used as needed. The **physical server**, on the other hand, is what opens when you launch a Minecraft server JAR. While the Minecraft server comes with a rudimentary GUI, it is missing all client-only functionality. Most notably, this means that various client classes are missing from the server JAR. Calling these classes on the physical server will lead to missing class errors, i.e. crashes, so we need to safeguard against this. +When you open your Minecraft launcher, select a Minecraft installation and press play, you boot up a **physical client**. The word "physical" is used here in the sense of "this is a client program". This especially means that client-side functionality, such as all the rendering stuff, is available here and can be used as needed. In contrast, the **physical server**, also known as dedicated server, is what opens when you launch a Minecraft server JAR. While the Minecraft server comes with a rudimentary GUI, it is missing all client-only functionality. Most notably, this means that various client classes are missing from the server JAR. Calling these classes on the physical server will lead to missing class errors, i.e. crashes, so we need to safeguard against this. ### The Logical Side From 4b2d0213a13e7854a00a0bbe170bb2f039dee266 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Mon, 29 Jan 2024 22:34:12 +0100 Subject: [PATCH 04/59] fix broken links --- docs/datagen/server/loottables.md | 2 +- docs/datagen/server/tags.md | 2 +- docs/gettingstarted/index.md | 2 +- docs/gettingstarted/modfiles.md | 5 ++--- docs/gui/screens.md | 2 +- docs/items/index.md | 2 +- docs/items/mobeffects.md | 2 +- docs/misc/config.md | 2 +- docs/misc/gametest.mdx | 2 +- docs/misc/keymappings.md | 4 ++-- docs/resources/server/loottables.md | 2 +- docs/resources/server/recipes/incode.md | 6 +++--- 12 files changed, 16 insertions(+), 17 deletions(-) diff --git a/docs/datagen/server/loottables.md b/docs/datagen/server/loottables.md index 2436a2d2b..e7d5eb982 100644 --- a/docs/datagen/server/loottables.md +++ b/docs/datagen/server/loottables.md @@ -141,4 +141,4 @@ Scoreboard providers are a special type of number providers defined by `Scoreboa [loottable]: ../../resources/server/loottables.md [datagen]: ../index.md#data-providers -[registered]: ../../concepts/registries.md#registries-that-arent-forge-registries +[registered]: ../../concepts/registries.md diff --git a/docs/datagen/server/tags.md b/docs/datagen/server/tags.md index 5603a548e..f52824a6c 100644 --- a/docs/datagen/server/tags.md +++ b/docs/datagen/server/tags.md @@ -118,4 +118,4 @@ public AttributeTagsProvider(PackOutput output, CompletableFuture EXAMPLE_TAB = CREATIVE_MODE_TABS.r [interactionpipeline]: interactionpipeline.md [loottables]: ../resources/server/loottables.md [mobeffectinstance]: mobeffects.md#mobeffectinstances -[modbus]: ../concepts/events.md#mod-event-bus +[modbus]: ../concepts/events.md#event-buses [nbt]: ../datastorage/nbt.md [registering]: ../concepts/registries.md#methods-for-registering [resources]: ../resources/client/index.md diff --git a/docs/items/mobeffects.md b/docs/items/mobeffects.md index 1af79dfe4..a8c9e98ad 100644 --- a/docs/items/mobeffects.md +++ b/docs/items/mobeffects.md @@ -192,7 +192,7 @@ BrewingRecipeRegistry.addRecipe( This should be called some time during setup, for example during [`FMLCommonSetupEvent`][commonsetup]. Make sure to wrap this into an `event.enqueueWork()` call, as the brewing recipe registry is not thread-safe. [block]: ../blocks/index.md -[commonsetup]: ../concepts/events.md#mod-event-bus +[commonsetup]: ../concepts/events.md#event-buses [datapack]: ../resources/server/index.md [events]: ../concepts/events.md [item]: index.md diff --git a/docs/misc/config.md b/docs/misc/config.md index 4d51af03f..6ba9abdc6 100644 --- a/docs/misc/config.md +++ b/docs/misc/config.md @@ -136,4 +136,4 @@ These events are called for all configurations for the mod; the `ModConfig` obje [toml]: https://toml.io/ [nightconfig]: https://github.com/TheElectronWill/night-config [type]: https://github.com/neoforged/FancyModLoader/blob/19d6326b810233e683f1beb3d28e41372e1e89d1/core/src/main/java/net/neoforged/fml/config/ModConfig.java#L83-L111 -[events]: ../concepts/events.md#creating-an-event-handler +[events]: ../concepts/events.md#registering-an-event-handler diff --git a/docs/misc/gametest.mdx b/docs/misc/gametest.mdx index 90b1f0470..7562122c9 100644 --- a/docs/misc/gametest.mdx +++ b/docs/misc/gametest.mdx @@ -284,5 +284,5 @@ property 'forge.enableGameTest', 'true' [test]: #running-game-tests [namespaces]: #enabling-other-namespaces -[event]: ../concepts/events.md#creating-an-event-handler +[event]: ../concepts/events.md#registering-an-event-handler [buildscript]: ../gettingstarted/index.md#simple-buildgradle-customizations diff --git a/docs/misc/keymappings.md b/docs/misc/keymappings.md index 5d72a8cfe..f4fb2b8fb 100644 --- a/docs/misc/keymappings.md +++ b/docs/misc/keymappings.md @@ -150,8 +150,8 @@ public boolean mouseClicked(double x, double y, int button) { If you do not own the screen which you are trying to check a **mouse** for, you can listen to the `Pre` or `Post` events of `ScreenEvent$MouseButtonPressed` on the [**Forge event bus**][forgebus] instead. ::: -[modbus]: ../concepts/events.md#mod-event-bus +[modbus]: ../concepts/events.md#event-buses [controls]: https://minecraft.wiki/w/Options#Controls [tk]: ../concepts/internationalization.md#translatablecontents [keyinput]: https://www.glfw.org/docs/3.3/input_guide.html#input_key -[forgebus]: ../concepts/events.md#creating-an-event-handler +[forgebus]: ../concepts/events.md#registering-an-event-handler diff --git a/docs/resources/server/loottables.md b/docs/resources/server/loottables.md index 52d7eed62..2e80c2f17 100644 --- a/docs/resources/server/loottables.md +++ b/docs/resources/server/loottables.md @@ -107,5 +107,5 @@ Forge adds an additional `LootItemCondition` which checks whether the given `Loo [datapack]: https://minecraft.wiki/w/Data_pack [wiki]: https://minecraft.wiki/w/Loot_table -[event]: ../../concepts/events.md#creating-an-event-handler +[event]: ../../concepts/events.md#registering-an-event-handler [glm]: ./glm.md diff --git a/docs/resources/server/recipes/incode.md b/docs/resources/server/recipes/incode.md index 26d1f7ce4..00497a10a 100644 --- a/docs/resources/server/recipes/incode.md +++ b/docs/resources/server/recipes/incode.md @@ -60,6 +60,6 @@ public static final BannerPattern EXAMPLE_PATTERN = REGISTER.register("example_p ``` [recipe]: ./custom.md#recipe -[cancel]: ../../../concepts/events.md#canceling -[attached]: ../../../concepts/events.md#creating-an-event-handler -[registering]: ../../../concepts/registries.md#registries-that-arent-forge-registries +[cancel]: ../../../concepts/events.md#cancellable-events +[attached]: ../../../concepts/events.md#registering-an-event-handler +[registering]: ../../../concepts/registries.md From c49ff5536d08a75b7a6bd19f4e6739c4b55f32ff Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Wed, 31 Jan 2024 16:37:12 +0100 Subject: [PATCH 05/59] fix some mistakes regarding `DeferredRegister.Blocks` --- docs/blocks/index.md | 45 ++++++++++++++++++++++++------------- docs/concepts/registries.md | 2 +- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/docs/blocks/index.md b/docs/blocks/index.md index bfac39ab8..54e7a094d 100644 --- a/docs/blocks/index.md +++ b/docs/blocks/index.md @@ -1,17 +1,23 @@ -Blocks -====== +# Blocks Blocks are essential to the Minecraft world. They make up all the terrain, structures, and machines. Chances are if you are interested in making a mod, then you will want to add some blocks. This page will guide you through the creation of blocks, and some of the things you can do with them. -One Block to Rule Them All --------------------------- +## One Block to Rule Them All Before we get started, it is important to understand that there is only ever one of each block in the game. A world consists of thousands of references to that one block in different locations. In other words, the same block is just displayed a lot of times. -Due to this, a block should only ever be instantiated once, and that is during [registration]. Once the block is registered, you can then use the registered reference as needed. Consider this example: +Due to this, a block should only ever be instantiated once, and that is during [registration]. Once the block is registered, you can then use the registered reference as needed. + +Unlike most other registries, blocks use a specialized version of `DeferredRegister`, called `DeferredRegister.Blocks`. `DeferredRegister.Blocks` acts basically like a `DeferredRegister`, but with some minor differences: + +- They are created via `DeferredRegister.createBlocks("yourmodid")` instead of the regular `DeferredRegister.create(...)` method. +- `#register` returns a `DeferredBlock`, which extends `DeferredHolder`. `T` is the type of the class of the block we are registering. +- There are a few helper methods for registering block. See [below] for more details. + +So now, let's register our blocks: ```java -//BLOCKS is a DeferredRegister +//BLOCKS is a DeferredRegister.Blocks public static final DeferredBlock MY_BLOCK = BLOCKS.register("my_block", () -> new Block(...)); ``` @@ -33,8 +39,13 @@ Do not call `new Block()` outside registration! As soon as you do that, things c - If you still manage to have a dangling block instance, the game will not recognize it while syncing and saving, and replace it with air. ::: -Creating Blocks ---------------- +## Creating Blocks + +As discussed before, we start by creating our `DeferredRegister.Blocks`: + +```java +public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks("yourmodid"); +``` ### Basic Blocks @@ -54,7 +65,8 @@ For simple blocks which need no special functionality (think cobblestone, wooden So for example, a simple implementation would look something like this: ```java - public static final DeferredBlock MY_BETTER_BLOCK = BLOCKS.register( +//BLOCKS is a DeferredRegister.Blocks +public static final DeferredBlock MY_BETTER_BLOCK = BLOCKS.register( "my_better_block", () -> new Block(BlockBehaviour.Properties.of() //highlight-start @@ -80,14 +92,14 @@ Directly using `Block` only allows for very basic blocks. If you want to add fun If you want to make a block that has different variants (think a slab that has a bottom, top, and double variant), you should use [blockstates]. And finally, if you want a block that stores additional data (think a chest that stores its inventory), a [block entity][blockentities] should be used. The rule of thumb here is that if you have a finite and reasonably small amount of states (= a few hundred states at most), use blockstates, and if you have an infinite or near-infinite amount of states, use a block entity. -### `DeferredRegister.Blocks` +### `DeferredRegister.Blocks` helpers -All registries use `DeferredRegister` to register their contents, and blocks are no exceptions. However, due to the fact that adding new blocks is such an essential feature of an overwhelming amount of mods, NeoForge provides the `DeferredRegister.Blocks` helper class that extends `DeferredRegister` and provides some block-specific helpers: +We already discussed how to create a `DeferredRegister.Blocks`, and that it returns `DeferredBlock`s. Now, let's have a look at what other utilities the specialized `DeferredRegister` has to offer. Let's start with `#registerBlock`: ```java -public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks(ExampleMod.MOD_ID); +public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks("yourmodid"); -public static final Supplier EXAMPLE_BLOCK = BLOCKS.registerBlock( +public static final DeferredBlock EXAMPLE_BLOCK = BLOCKS.registerBlock( "example_block", Block::new, // The factory that the properties will be passed into. new BlockBehaviour.Properties() // The properties to use. @@ -99,7 +111,7 @@ Internally, this will simply call `BLOCKS.register("example_block", () -> new Bl If you want to use `Block::new`, you can leave out the factory entirely: ```java -public static final Supplier EXAMPLE_BLOCK = BLOCKS.registerSimpleBlock( +public static final DeferredBlock EXAMPLE_BLOCK = BLOCKS.registerSimpleBlock( "example_block", new BlockBehaviour.Properties() // The properties to use. ); @@ -113,8 +125,7 @@ If you register your block and place it in the world, you will find it to be mis To apply a simple texture to a block, you must add a blockstate JSON, a model JSON, and a texture PNG. See the section on [resources] for more information. -Using Blocks ------------- +## Using Blocks Blocks are very rarely directly used to do things. In fact, probably two of the most common operations in all of Minecraft - getting the block at a position, and setting a block at a position - use blockstates, not blocks. The general design approach is to have the block define behavior, but have the behavior actually run through blockstates. Due to this, `BlockState`s are often passed to methods of `Block` as a parameter. For more information on how blockstates are used, and on how to get one from a block, see [Using Blockstates][usingblockstates]. @@ -214,6 +225,8 @@ 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 +[below]: #deferredregisterblocks-helpers [blockentities]: ../blockentities/index.md [blockstates]: states.md [events]: ../concepts/events.md diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index c1a2211f7..29fc1ed0b 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -222,7 +222,7 @@ static void registerDatapackRegistries(DataPackRegistryEvent.NewRegistry event) [codec]: ../datastorage/codecs.md [datagen]: ../datagen/index.md [datapack]: ../resources/server/index.md -[defregblocks]: ../blocks/index.md#deferredregisterblocks +[defregblocks]: ../blocks/index.md#deferredregisterblocks-helpers [defregitems]: ../items/index.md#deferredregisteritems [dynamicops]: ../datastorage/codecs.md#dynamicops [event]: ./events.md From 242abf0d96e89f6057f20b801fdf788d4c8359e7 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Wed, 31 Jan 2024 16:39:18 +0100 Subject: [PATCH 06/59] add side checks in the event handlers --- docs/concepts/events.md | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/docs/concepts/events.md b/docs/concepts/events.md index aaf7dce99..84147be6a 100644 --- a/docs/concepts/events.md +++ b/docs/concepts/events.md @@ -21,7 +21,11 @@ public class YourMod { // Heals an entity by half a heart every time they jump. private static void onLivingJump(LivingJumpEvent event) { - event.getEntity().heal(1); + Entity entity = event.getEntity(); + // Only heal on the server side + if (!entity.level().isClientSide()) { + entity.heal(1); + } } } ``` @@ -34,7 +38,10 @@ Alternatively, event handlers can be annotation-driven by creating an event hand public class EventHandler { @SubscribeEvent public void onLivingJump(LivingJumpEvent event) { - event.getEntity().heal(1); + Entity entity = event.getEntity(); + if (!entity.level().isClientSide()) { + entity.heal(1); + } } } @@ -52,7 +59,10 @@ You can also do it statically. Simply make all event handlers static, and instea public class EventHandler { @SubscribeEvent public static void onLivingJump(LivingJumpEvent event) { - event.getEntity().heal(1); + Entity entity = event.getEntity(); + if (!entity.level().isClientSide()) { + entity.heal(1); + } } } @@ -64,10 +74,6 @@ public class YourMod { } ``` -:::info -`@SubscribeEvent` annotated methods must not be `private`. Any out of `package-private`, `protected` and `public` will work. -::: - ### `@Mod.EventBusSubscriber` We can go one step further and also annotate the event handler class with `@Mod.EventBusSubscriber`. This annotation is discovered automatically by NeoForge, allowing you to remove all event-related code from the mod constructor. In essence, it is equivalent to calling `NeoForge.EVENT_BUS.register(EventHandler.class)` at the end of the mod constructor. This means that all handlers must be static, too. @@ -79,7 +85,10 @@ While not required, it is highly recommended to specify the `modid` parameter in public class EventHandler { @SubscribeEvent public static void onLivingJump(LivingJumpEvent event) { - event.getEntity().heal(1); + Entity entity = event.getEntity(); + if (!entity.level().isClientSide()) { + entity.heal(1); + } } } ``` From a4971bd17bf8429cf1638746a3a01ca2c3dbed10 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Wed, 31 Jan 2024 16:41:37 +0100 Subject: [PATCH 07/59] fix some of the mistakes in registries.md --- docs/blocks/index.md | 2 +- docs/concepts/registries.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/blocks/index.md b/docs/blocks/index.md index 54e7a094d..911236104 100644 --- a/docs/blocks/index.md +++ b/docs/blocks/index.md @@ -94,7 +94,7 @@ If you want to make a block that has different variants (think a slab that has a ### `DeferredRegister.Blocks` helpers -We already discussed how to create a `DeferredRegister.Blocks`, and that it returns `DeferredBlock`s. Now, let's have a look at what other utilities the specialized `DeferredRegister` has to offer. Let's start with `#registerBlock`: +We already discussed how to create a `DeferredRegister.Blocks` [above], as well as that it returns `DeferredBlock`s. Now, let's have a look at what other utilities the specialized `DeferredRegister` has to offer. Let's start with `#registerBlock`: ```java public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks("yourmodid"); diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index 29fc1ed0b..448cddf3f 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -66,6 +66,7 @@ Finally, since the entire system is a wrapper around registry events, we need to ```java //This is our mod constructor public ExampleMod(IModEventBus bus) { + //highlight-next-line ExampleBlocksClass.BLOCKS.register(bus); //Other stuff here } @@ -109,7 +110,7 @@ public static final ResourceKey> SPELL_REGISTRY_KEY = ResourceKe public static final Registry SPELL_REGISTRY = new RegistryBuilder<>(SPELL_REGISTRY_KEY) // If you want the registry to sync its values. .sync(true) - // The default key. Similar to minecraft:air for blocks. + // The default key. Similar to minecraft:air for blocks. This is optional. .defaultKey(new ResourceLocation("yourmodid", "empty")) // Effectively limits the max count. Generally discouraged, but may make sense in settings such as networking. .maxId(256) From 9950d51f11066a7280d4cdd5d9608dec446cb403 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Wed, 21 Feb 2024 15:02:22 +0100 Subject: [PATCH 08/59] remove mention of registry int id syncing (it is discouraged) --- docs/concepts/registries.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index 448cddf3f..e2f1b624a 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -108,8 +108,6 @@ Let's start by creating the [registry key][resourcekey] and the registry itself: // Of course, all mentions of spells can and should be replaced with whatever your registry actually is. public static final ResourceKey> SPELL_REGISTRY_KEY = ResourceKey.createRegistryKey(new ResourceLocation("yourmodid", "spells")); public static final Registry SPELL_REGISTRY = new RegistryBuilder<>(SPELL_REGISTRY_KEY) - // If you want the registry to sync its values. - .sync(true) // The default key. Similar to minecraft:air for blocks. This is optional. .defaultKey(new ResourceLocation("yourmodid", "empty")) // Effectively limits the max count. Generally discouraged, but may make sense in settings such as networking. From a1bd87d87fd11427ab8140295f5a5c9d25ec0706 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Wed, 21 Feb 2024 15:53:28 +0100 Subject: [PATCH 09/59] update datapack registry datagen --- docs/concepts/registries.md | 134 ++++++++++++++-------- docs/datagen/index.md | 4 +- docs/datagen/server/datapackregistries.md | 128 --------------------- 3 files changed, 90 insertions(+), 176 deletions(-) delete mode 100644 docs/datagen/server/datapackregistries.md diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index e2f1b624a..c3d4ee99a 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -149,50 +149,7 @@ Datapack registries allow their contents to be specified in JSON files. This mea - Minecraft's datapack registries use the format `data/yourmodid/registrypath` (for example `data/yourmodid/worldgen/biomes`, where `worldgen/biomes` is the registry path). - All other datapack registries (NeoForge or modded) use the format `data/yourmodid/registrynamespace/registrypath` (for example `data/yourmodid/neoforge/loot_modifiers`, where `neoforge` is the registry namespace and `loot_modifiers` is the registry path). -Datapack registries can be obtained from a `RegistryAccess`. This `RegistryAccess` can be retrieved by calling `ServerLevel#registryAccess()` if on the server, by calling `Minecraft.getInstance().connection#registryAccess()` if on the client, or from a `RegistryOps`. - -### `RegistryOps` - -`RegistryOps` is a special [`DynamicOps`][dynamicops] made specifically for (de)serializing datapack registries. It provides additional registry context and enables the use of special codecs that can only be used with `RegistryOps`. Data generation of datapack registry elements must always be done through `RegistryOps` to convert elements to `JsonElement`s. - -A `RegistryOps` can be created via `RegistryOps.create(JsonElement.INSTANCE, RegistryAccess.builtinCopy())`. `RegistryAccess.builtinCopy()` creates a set of writable datapack registries, which is necessary for datagenning unregistered objects. All data generation done in a `GatherDataEvent` handler must use the same `RegistryAccess` and `RegistryOps` instances, otherwise obscure errors will occur. - -### `Holder`s - -As mentioned before, (normal) registries rely on `DeferredHolder`s, which are a special kind of `Holder`. A `Holder` vaguely resembles a `Pair` that either starts with a key and has a value bound later, or starts with a value and may have a key bound later. Datapack registries extensively rely on (non-deferred) `Holder`s to reference registry elements of other registries. For example, `Biome`s refer to `Holder`s, and `PlacedFeature`s refer to `Holder`s. - -During data generation, we can use `RegistryOps#registry` to get a registry, and `Registry#getOrCreateHolderOrThrow()` to produce key-only reference holders (we only need the key in this case, since holder codecs only encode keys when using a `RegistryOps` in order to prevent circular dependencies). - -### `JsonCodecProvider` - -NeoForge provides a data provider for datapack registry elements that, given a registry key and a map of objects to generate, generates all JSON files for the objects in the map. - -```java -@SubscribeEvent -static void onGatherData(GatherDataEvent event) { - DataGenerator generator = event.getDataGenerator(); - ExistingFileHelper existingFileHelper = event.getExistingFileHelper(); - RegistryOps registryOps = RegistryOps.create(JsonElement.INSTANCE, RegistryAccess.builtinCopy()); - - Map map = Map.of( - // Whatever entries you want. For example: - new ResourceLocation("yourmodid", "sponge_everywhere"), new PlacedFeature(...) - ); - - JsonCodecProvider provider = JsonCodecProvider.forDatapackRegistry( - generator, - existingFileHelper, - "yourmodid", - registryOps, - // The registry you want to generate in. - Registry.PLACED_FEATURE_REGISTRY, - // The elements to generate. - map - ); - - generator.addProvider(event.includeServer(), provider); -} -``` +Datapack registries can be obtained from a `RegistryAccess`. This `RegistryAccess` can be retrieved by calling `ServerLevel#registryAccess()` if on the server, or `Minecraft.getInstance().connection#registryAccess()` if on the client (the latter only works if you are actually connected to a world, as otherwise the connection will be null). The result of these calls can then be used like any other registry to get specific elements, or to iterate over the contents. ### Custom Datapack Registries @@ -216,14 +173,99 @@ static void registerDatapackRegistries(DataPackRegistryEvent.NewRegistry event) } ``` +### Data Generation for Datapack Registries + +Since writing all the JSON files by hand would be tedious and error-prone, NeoForge provides a [data provider][datagenindex] to generate the JSON files for you. This works for both built-in and your own datapack registries. + +First, we create a `RegistrySetBuilder` and add our entries to it (one `RegistrySetBuilder` can hold entries for multiple registries): + +```java +new RegistrySetBuilder() + .add(Registries.CONFIGURED_FEATURE, bootstrap -> { + // Register configured features through the bootstrap context (see below) + }) + .add(Registries.PLACED_FEATURE, bootstrap -> { + // Register placed features through the bootstrap context (see below) + }); +``` + +The `bootstrap` lambda parameter is what we actually use to register our objects. It has the type `BootstrapContext`. To register an object, we call `#register` on it, like so: + +```java +// The resource key of our object. +public static final ResourceKey> EXAMPLE_CONFIGURED_FEATURE = ResourceKey.create( + Registries.CONFIGURED_FEATURE, + new ResourceLocation(MOD_ID, "example_configured_feature") +); + +new RegistrySetBuilder() + .add(Registries.CONFIGURED_FEATURE, bootstrap -> { + bootstrap.register( + // The resource key of our configured feature. + EXAMPLE_CONFIGURED_FEATURE, + // The actual configured feature. + new ConfiguredFeature<>(Feature.ORE, new OreConfiguration(...)) + ); + }) + .add(Registries.PLACED_FEATURE, bootstrap -> { + // ... + }); +``` + +The `BootstrapContext` can also be used to lookup entries from another registry if needed: + +```java +public static final ResourceKey> EXAMPLE_CONFIGURED_FEATURE = ResourceKey.create( + Registries.CONFIGURED_FEATURE, + new ResourceLocation(MOD_ID, "example_configured_feature") +); +public static final ResourceKey EXAMPLE_PLACED_FEATURE = ResourceKey.create( + Registries.PLACED_FEATURE, + new ResourceLocation(MOD_ID, "example_placed_feature") +); + +new RegistrySetBuilder() + .add(Registries.CONFIGURED_FEATURE, bootstrap -> { + bootstrap.register(EXAMPLE_CONFIGURED_FEATURE, ...); + }) + .add(Registries.PLACED_FEATURE, bootstrap -> { + HolderGetter> otherRegistry = bootstrap.lookup(Registries.CONFIGURED_FEATURE); + bootstrap.register(EXAMPLE_PLACED_FEATURE, new PlacedFeature( + otherRegistry.getOrThrow(EXAMPLE_CONFIGURED_FEATURE), // Get the configured feature + List.of() // No-op when placement happens - replace with whatever your placement parameters are + )); + }); +``` + +Finally, we use our `RegistrySetBuilder` in an actual data provider, and register that data provider to the event: + +```java +@SubscribeEvent +static void onGatherData(GatherDataEvent event) { + event.getGenerator().addProvider( + // Only run datapack generation when server data is being generated + event.includeServer(), + // Create the provider + output -> new DatapackBuiltinEntriesProvider( + output, + event.getLookupProvider(), + // Our registry set builder to generate the data from. + new RegistrySetBuilder().add(...), + // A set of mod ids we are generating. Usually only your own mod id. + Set.of("yourmodid") + ) + ); +} +``` + [block]: ../blocks/index.md [blockentity]: ../blockentities/index.md [codec]: ../datastorage/codecs.md -[datagen]: ../datagen/index.md +[datagen]: #data-generation-for-datapack-registries +[datagenindex]: ../datagen/index.md [datapack]: ../resources/server/index.md [defregblocks]: ../blocks/index.md#deferredregisterblocks-helpers [defregitems]: ../items/index.md#deferredregisteritems -[dynamicops]: ../datastorage/codecs.md#dynamicops [event]: ./events.md [item]: ../items/index.md [resloc]: ../misc/resourcelocation.md diff --git a/docs/datagen/index.md b/docs/datagen/index.md index 983a2eb57..358ca6365 100644 --- a/docs/datagen/index.md +++ b/docs/datagen/index.md @@ -55,7 +55,7 @@ The `GatherDataEvent` is fired on the mod event bus when the data generator is b **These classes are under the `net.neoforged.neoforge.common.data` package**: * [`GlobalLootModifierProvider`][glmgen] - for [global loot modifiers][glm]; implement `#start` -* [`DatapackBuiltinEntriesProvider`][datapackregistriesgen] for datapack registry objects (i.e. world generation features, biomes, and more); pass in `RegistrySetBuilder` to the constructor +* [`DatapackBuiltinEntriesProvider`][datapackregistriesgen] - for datapack registry objects; see the linked article **These classes are under the `net.minecraft.data` package**: @@ -75,7 +75,7 @@ The `GatherDataEvent` is fired on the mod event bus when the data generator is b [blockstategen]: ./client/modelproviders.md#block-state-provider [glmgen]: ./server/glm.md [glm]: ../resources/server/glm.md -[datapackregistriesgen]: ./server/datapackregistries.md +[datapackregistriesgen]: ../concepts/registries.md#data-generation-for-datapack-registries [loottablegen]: ./server/loottables.md [loottable]: ../resources/server/loottables.md [recipegen]: ./server/recipes.md diff --git a/docs/datagen/server/datapackregistries.md b/docs/datagen/server/datapackregistries.md deleted file mode 100644 index 3f105921e..000000000 --- a/docs/datagen/server/datapackregistries.md +++ /dev/null @@ -1,128 +0,0 @@ -Datapack Registry Object Generation -================================== - -Datapack registry objects can be generated for a mod by constructing a new `DatapackBuiltinEntriesProvider` and providing a `RegistrySetBuilder` with the new objects to register. The provider must be [added][datagen] to the `DataGenerator`. - -!!! note - `DatapackBuiltinEntriesProvider` is a Forge extension on top of `RegistriesDatapackGenerator` which properly handles referencing existing datapack registry objects without exploding the entry. So, this documentation will use `DatapackBuiltinEntriesProvider`. - -```java -// On the MOD event bus -@SubscribeEvent -public void gatherData(GatherDataEvent event) { - event.getGenerator().addProvider( - // Tell generator to run only when server data are generating - event.includeServer(), - output -> new DatapackBuiltinEntriesProvider( - output, - event.getLookupProvider(), - // The builder containing the datapack registry objects to generate - new RegistrySetBuilder().add(/* ... */), - // Set of mod ids to generate the datapack registry objects of - Set.of(MOD_ID) - ) - ); -} -``` - -`RegistrySetBuilder` --------------------- - -A `RegistrySetBuilder` is responsible for building all datapack registry objects to be used within the game. The builder can add a new entry for a registry, which can then register objects to that registry. - -First, a new instance of a `RegistrySetBuilder` can be initialized by calling the constructor. Then, the `#add` method (which takes in the `ResourceKey` of the registry, a `RegistryBootstrap` consumer containing the `BootstapContext` to register the objects, and an optional `Lifecycle` argument to indicate the registry's current lifecycle status) can be called to handle a specific registry for registration. - -```java -new RegistrySetBuilder() - // Create configured features - .add(Registries.CONFIGURED_FEATURE, bootstrap -> { - // Register configured features here - }) - // Create placed features - .add(Registries.PLACED_FEATURE, bootstrap -> { - // Register placed features here - }); -``` - -!!! note - Datapack registries created through Forge can also generate their objects using this builder by also passing in the associated `ResourceKey`. - -Registering with `BootstapContext` ----------------------------------- - -The `#register` method in the `BootstapContext` provided by the builder can be used to register objects. It takes in the `ResourceKey` representing the registry name of the object, the object to register, and an optional `Lifecycle` argument to indicate the registry object's current lifecycle status. - -```java -public static final ResourceKey> EXAMPLE_CONFIGURED_FEATURE = ResourceKey.create( - Registries.CONFIGURED_FEATURE, - new ResourceLocation(MOD_ID, "example_configured_feature") -); - -// In some constant location or argument -new RegistrySetBuilder() - // Create configured features - .add(Registries.CONFIGURED_FEATURE, bootstrap -> { - // Register configured features here - bootstrap.register( - // The resource key for the configured feature - EXAMPLE_CONFIGURED_FEATURE, - new ConfiguredFeature<>( - Feature.ORE, // Create an ore feature - new OreConfiguration( - List.of(), // Does nothing - 8 // in veins of at most 8 - ) - ) - ); - }) - // Create placed features - .add(Registries.PLACED_FEATURE, bootstrap -> { - // Register placed features here - }); -``` - -### Datapack Registry Object Lookup - -Sometimes datapack registry objects may want to use other datapack registry objects or tags containing datapack registry objects. In those cases, you can look up another datapack registry using `BootstapContext#lookup` to get a `HolderGetter`. From there, you can get a `Holder$Reference` to the datapack registry object or a `HolderSet$Named` for the tag via `#getOrThrow` by passing in the associated key. - -```java -public static final ResourceKey> EXAMPLE_CONFIGURED_FEATURE = ResourceKey.create( - Registries.CONFIGURED_FEATURE, - new ResourceLocation(MOD_ID, "example_configured_feature") -); - -public static final ResourceKey EXAMPLE_PLACED_FEATURE = ResourceKey.create( - Registries.PLACED_FEATURE, - new ResourceLocation(MOD_ID, "example_placed_feature") -); - -// In some constant location or argument -new RegistrySetBuilder() - // Create configured features - .add(Registries.CONFIGURED_FEATURE, bootstrap -> { - // Register configured features here - bootstrap.register( - // The resource key for the configured feature - EXAMPLE_CONFIGURED_FEATURE, - new ConfiguredFeature(/* ... */) - ); - }) - // Create placed features - .add(Registries.PLACED_FEATURE, bootstrap -> { - // Register placed features here - - // Get configured feature registry - HolderGetter> configured = bootstrap.lookup(Registries.CONFIGURED_FEATURE); - - bootstrap.register( - // The resource key for the placed feature - EXAMPLE_PLACED_FEATURE, - new PlacedFeature( - configured.getOrThrow(EXAMPLE_CONFIGURED_FEATURE), // Get the configured feature - List.of() // and do nothing to the placement location - ) - ) - }); -``` - -[datagen]: ../index.md#data-providers \ No newline at end of file From 31b5bf26a66ef3e73a026753c51f0c3788b8ba9b Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Wed, 21 Feb 2024 16:19:20 +0100 Subject: [PATCH 10/59] registry querying --- docs/concepts/registries.md | 45 +++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index c3d4ee99a..266bd9597 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -97,6 +97,47 @@ public void register(RegisterEvent event) { } ``` +## Querying Registries + +Sometimes, you will find yourself in situations where you want to get a registered object by a given id. Or, you want to get the id of a certain registered object. Since registries are basically maps of ids (`ResourceLocation`s) to distinct objects, i.e. a reversible map, both of these operations work: + +```java +Registries.BLOCKS.get(new ResourceLocation("minecraft", "dirt")); // returns the dirt block +Registries.BLOCKS.getKey(Blocks.DIRT); // returns the resource location "minecraft:dirt" + +// Assume that ExampleBlocksClass.EXAMPLE_BLOCK.get() is a Supplier with the id "yourmodid:example_block" +Registries.BLOCKS.get(new ResourceLocation("yourmodid", "example_block")); // returns the example block +Registries.BLOCKS.getKey(ExampleBlocksClass.EXAMPLE_BLOCK.get()); // returns the resource location "yourmodid:example_block" +``` + +If you just want to check for the presence of an object, this is also possible, though only with keys: + +```java +Registries.BLOCKS.containsKey(new ResourceLocation("minecraft", "dirt")); // true +Registries.BLOCKS.containsKey(new ResourceLocation("create", "brass_ingot")); // true only if Create is installed +``` + +As the last example shows, this is possible with any mod id, and thus a perfect way to check if a certain item from another mod exists. + +Finally, we can also iterate over all entries in a registry, either over the keys or over the entries (entries use the Java `Map.Entry` type): + +```java +for (ResourceLocation id : Registries.BLOCKS.keySet()) { + // ... +} +for (Map.Entry entry : Registries.BLOCKS.entrySet()) { + // ... +} +``` + +:::note +Query operations should always use vanilla `Registry`s, not `DeferredRegister`s. This is because `DeferredRegister`s are merely registration utilities and effectively do not exist after registration has finished. +::: + +:::danger +Query operations are only safe to use after registration has finished. **DO NOT QUERY REGISTRIES WHILE REGISTRATION IS STILL ONGOING!** +::: + ## Custom Registries Custom registries allow you to specify additional systems that addon mods for your mod may want to plug into. For example, if your mod were to add spells, you could make the spells a registry and thus allow other mods to add spells to your mod, without you having to do anything else. It also allows you to do some things, such as syncing the entries, automatically. @@ -142,7 +183,7 @@ public static void register(RegisterEvent event) { ## Datapack Registries -A datapack registry (also known as a dynamic registry or, after its main use case, worldgen registry) is a special kind of registry that loads data from [datapack][datapack] JSONs (hence the name) at world load, instead of loading them when the game starts. Default datapack registries include most worldgen registries, as well as any custom registry (see below) that is marked as a datapack registry. +A datapack registry (also known as a dynamic registry or, after its main use case, worldgen registry) is a special kind of registry that loads data from [datapack][datapack] JSONs (hence the name) at world load, instead of loading them when the game starts. Default datapack registries most notably include most worldgen registries, among a few others. Datapack registries allow their contents to be specified in JSON files. This means that no code (other than [datagen][datagen] if you don't want to write the JSON files yourself) is necessary. Every datapack registry has a [`Codec`][codec] associated with it, which is used for serialization, and each registry's id determines its datapack path: @@ -175,7 +216,7 @@ static void registerDatapackRegistries(DataPackRegistryEvent.NewRegistry event) ### Data Generation for Datapack Registries -Since writing all the JSON files by hand would be tedious and error-prone, NeoForge provides a [data provider][datagenindex] to generate the JSON files for you. This works for both built-in and your own datapack registries. +Since writing all the JSON files by hand is both tedious and error-prone, NeoForge provides a [data provider][datagenindex] to generate the JSON files for you. This works for both built-in and your own datapack registries. First, we create a `RegistrySetBuilder` and add our entries to it (one `RegistrySetBuilder` can hold entries for multiple registries): From ff5d5b12eb61997f84fbd250188cfcdfeaa055ee Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Wed, 21 Feb 2024 16:32:56 +0100 Subject: [PATCH 11/59] add back int ID syncing, with a big disclaimer --- docs/concepts/registries.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index 266bd9597..24c143be3 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -149,6 +149,9 @@ Let's start by creating the [registry key][resourcekey] and the registry itself: // Of course, all mentions of spells can and should be replaced with whatever your registry actually is. public static final ResourceKey> SPELL_REGISTRY_KEY = ResourceKey.createRegistryKey(new ResourceLocation("yourmodid", "spells")); public static final Registry SPELL_REGISTRY = new RegistryBuilder<>(SPELL_REGISTRY_KEY) + // If you want to enable integer id syncing, for networking. + // Never use integer ids yourself, even if this is enabled! This is purely intended to reduce bandwidth. + .sync(true) // The default key. Similar to minecraft:air for blocks. This is optional. .defaultKey(new ResourceLocation("yourmodid", "empty")) // Effectively limits the max count. Generally discouraged, but may make sense in settings such as networking. From 965be163cd5dea5fa1c765b29a295f2330015347 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Wed, 21 Feb 2024 16:43:11 +0100 Subject: [PATCH 12/59] update sync disclaimer --- docs/concepts/registries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index 24c143be3..3c1237126 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -150,7 +150,7 @@ Let's start by creating the [registry key][resourcekey] and the registry itself: public static final ResourceKey> SPELL_REGISTRY_KEY = ResourceKey.createRegistryKey(new ResourceLocation("yourmodid", "spells")); public static final Registry SPELL_REGISTRY = new RegistryBuilder<>(SPELL_REGISTRY_KEY) // If you want to enable integer id syncing, for networking. - // Never use integer ids yourself, even if this is enabled! This is purely intended to reduce bandwidth. + // These should only be used in networking contexts, for example in packets or purely networking-related NBT data. .sync(true) // The default key. Similar to minecraft:air for blocks. This is optional. .defaultKey(new ResourceLocation("yourmodid", "empty")) From 2e65131562c8f9bf76cbcab96e38e50d4092ca02 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Thu, 22 Feb 2024 22:42:48 +0100 Subject: [PATCH 13/59] address most feedback by ChampionAsh --- docs/blocks/index.md | 4 ++-- docs/concepts/events.md | 12 ++++++------ docs/items/index.md | 6 +++++- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/docs/blocks/index.md b/docs/blocks/index.md index 911236104..ebc67c57c 100644 --- a/docs/blocks/index.md +++ b/docs/blocks/index.md @@ -102,7 +102,7 @@ public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBloc public static final DeferredBlock EXAMPLE_BLOCK = BLOCKS.registerBlock( "example_block", Block::new, // The factory that the properties will be passed into. - new BlockBehaviour.Properties() // The properties to use. + BlockBehaviour.Properties.of() // The properties to use. ); ``` @@ -113,7 +113,7 @@ 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", - new BlockBehaviour.Properties() // The properties to use. + BlockBehaviour.Properties.of() // The properties to use. ); ``` diff --git a/docs/concepts/events.md b/docs/concepts/events.md index 84147be6a..877dbafa4 100644 --- a/docs/concepts/events.md +++ b/docs/concepts/events.md @@ -2,7 +2,7 @@ One of NeoForge's main features is the event system. Events are fired for various things that happen in the game. For example, there are events for when the player right clicks, when a player or another entity jumps, when blocks are rendered, when the game is loaded, etc. A modder can subscribe event handlers to each of these events, and then perform their desired behavior inside these event handlers. -Events are fired on their respective event bus. The most important bus is `NeoForge.EVENT_BUS`. Besides that, during startup, a mod bus is spawned for each loaded mod and passed into the mod's constructor; mod bus event handlers can run in parallel, dramatically increasing startup speed. See [below][modbus] for more information. +Events are fired on their respective event bus. The most important bus is `NeoForge.EVENT_BUS`. Besides that, during startup, a mod bus is spawned for each loaded mod and passed into the mod's constructor. Many mod bus events are fired in parallel (as opposed to main bus events that always run on the same thread), dramatically increasing startup speed. See [below][modbus] for more information. ## Registering an Event Handler @@ -36,7 +36,7 @@ Alternatively, event handlers can be annotation-driven by creating an event hand ```java public class EventHandler { - @SubscribeEvent + @SubscribeEvent public void onLivingJump(LivingJumpEvent event) { Entity entity = event.getEntity(); if (!entity.level().isClientSide()) { @@ -97,14 +97,14 @@ public class EventHandler { ### Fields and Methods -Probably the most obvious part. Most events contain context for the event handler to use, such as an entity causing the event or a level the event occurs in. +Fields and methods are probably the most obvious part of an event. Most events contain context for the event handler to use, such as an entity causing the event or a level the event occurs in. ### Hierarchy In order to use the advantages of inheritance, some events do not directly extend `Event`, but one of its subclasses, for example `BlockEvent` (which contains block context for block-related events) or `EntityEvent` (which similarly contains entity context) and its subclasses `LivingEvent` (for `LivingEntity`-specific context) and `PlayerEvent` (for `Player`-specific context). These context-providing super events are `abstract` and cannot be listened to. :::danger -If you listen to an `abstract` event, your game will crash, as this is not what you want. Listen to one of its sub events instead. +If you listen to an `abstract` event, your game will crash, as this is never what you want. You always want to listen to one of the subevents instead. ::: ### Cancellable Events @@ -125,7 +125,7 @@ Results are deprecated and will be replaced by more specific per-event results s Event handlers can optionally get assigned a priority. The `EventPriority` enum contains five values: `HIGHEST`, `HIGH`, `NORMAL` (default), `LOW` and `LOWEST`. Events are executed from highest to lowest priority, with undefined order for two events of the same priority. -Priorities can be defined by setting the `priority` parameter in `IEventBus#addListener` or `@SubscribeEvent`, depending on how you attach event handlers. +Priorities can be defined by setting the `priority` parameter in `IEventBus#addListener` or `@SubscribeEvent`, depending on how you attach event handlers. Note that priorities are ignored for events that are fired in parallel. ### Sided Events @@ -173,7 +173,7 @@ Next to the lifecycle events, there are a few miscellaneous events that are fire - `TextureStitchEvent` :::warning -Most of these events will be moved to `NeoForge.EVENT_BUS` eventually. +Most of these events are planned to be moved to the main event bus in a future version. ::: [modbus]: #event-buses diff --git a/docs/items/index.md b/docs/items/index.md index ba74b916c..1c45deb72 100644 --- a/docs/items/index.md +++ b/docs/items/index.md @@ -75,7 +75,7 @@ If you want to use `Item::new`, you can leave out the factory entirely: ```java public static final Supplier EXAMPLE_ITEM = ITEMS.registerItem( "example_item", - new ItemBehaviour.Properties() // The properties to use. + new Item.Properties() // The properties to use. ); ``` @@ -101,6 +101,10 @@ public static final Supplier EXAMPLE_BLOCK_ITEM = ITEMS.registerSimpl public static final Supplier EXAMPLE_BLOCK_ITEM = ITEMS.registerSimpleBlockItem(ExampleBlocksClass.EXAMPLE_BLOCK); ``` +:::note +If you keep your blocks in a separate class, you should classload your blocks class before your items class. +::: + ### Resources If you register your item and get your item (via `/give` or through a [creative tab][creativetabs]), you will find it to be missing a proper model and texture. This is because textures and models are handled by Minecraft's resource system. From 1ed292c0f7cc49e8594c54ed8cfc6940c719424b Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 <52419336+IchHabeHunger54@users.noreply.github.com> Date: Fri, 23 Feb 2024 00:42:07 +0100 Subject: [PATCH 14/59] Update docs/blocks/index.md Co-authored-by: Dennis C --- docs/blocks/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/blocks/index.md b/docs/blocks/index.md index ebc67c57c..3622a17dd 100644 --- a/docs/blocks/index.md +++ b/docs/blocks/index.md @@ -8,7 +8,7 @@ Before we get started, it is important to understand that there is only ever one Due to this, a block should only ever be instantiated once, and that is during [registration]. Once the block is registered, you can then use the registered reference as needed. -Unlike most other registries, blocks use a specialized version of `DeferredRegister`, called `DeferredRegister.Blocks`. `DeferredRegister.Blocks` acts basically like a `DeferredRegister`, but with some minor differences: +Unlike most other registries, blocks can use a specialized version of `DeferredRegister`, called `DeferredRegister.Blocks`. `DeferredRegister.Blocks` acts basically like a `DeferredRegister`, but with some minor differences: - They are created via `DeferredRegister.createBlocks("yourmodid")` instead of the regular `DeferredRegister.create(...)` method. - `#register` returns a `DeferredBlock`, which extends `DeferredHolder`. `T` is the type of the class of the block we are registering. From e2fbc3190cf2792bdfad5bde46dd3d70450dc705 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 <52419336+IchHabeHunger54@users.noreply.github.com> Date: Fri, 23 Feb 2024 00:42:20 +0100 Subject: [PATCH 15/59] Update docs/concepts/registries.md Co-authored-by: Dennis C --- docs/concepts/registries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index 3c1237126..c1cc30a08 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -59,7 +59,7 @@ public static final Supplier EXAMPLE_BLOCK = BLOCKS.register( ); ``` -Be aware that a few places explicitly require a `DeferredHolder` and will not just accept any `Supplier`. If you need a `DeferredHolder`, it is best to change the type of your `Supplier` back to `DeferredHolder`. +Be aware that a few places explicitly require a `Holder` or `DeferredHolder` and will not just accept any `Supplier`. If you need either of those two, it is best to change the type of your `Supplier` back to `Holder` or `DeferredHolder` as necessary. Finally, since the entire system is a wrapper around registry events, we need to tell the `DeferredRegister` to attach itself to the registry events as needed: From 40c17a949a53cbcf89b23c543310023ebaa4d5f5 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Fri, 23 Feb 2024 01:00:23 +0100 Subject: [PATCH 16/59] implement most of XFact's feedback --- docs/concepts/events.md | 2 +- docs/concepts/registries.md | 17 +++++++++-------- docs/concepts/sides.md | 2 +- docs/items/index.md | 8 ++++---- docs/misc/resourcelocation.md | 8 +++++--- 5 files changed, 20 insertions(+), 17 deletions(-) diff --git a/docs/concepts/events.md b/docs/concepts/events.md index 877dbafa4..64b0b6f21 100644 --- a/docs/concepts/events.md +++ b/docs/concepts/events.md @@ -123,7 +123,7 @@ Results are deprecated and will be replaced by more specific per-event results s ### Priority -Event handlers can optionally get assigned a priority. The `EventPriority` enum contains five values: `HIGHEST`, `HIGH`, `NORMAL` (default), `LOW` and `LOWEST`. Events are executed from highest to lowest priority, with undefined order for two events of the same priority. +Event handlers can optionally get assigned a priority. The `EventPriority` enum contains five values: `HIGHEST`, `HIGH`, `NORMAL` (default), `LOW` and `LOWEST`. Event handlers are executed from highest to lowest priority. If they have the same priority, they fire in registration order on the main bus, which is roughly related to mod load order, and in exact mod load order on the mod bus (see below). Priorities can be defined by setting the `priority` parameter in `IEventBus#addListener` or `@SubscribeEvent`, depending on how you attach event handlers. Note that priorities are ignored for events that are fired in parallel. diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index c1cc30a08..27c5959f5 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -18,7 +18,7 @@ The main reason all of this works is that `Blocks` is classloaded early enough b ## Methods for Registering -NeoForge offers two ways to register objects: the `DeferredRegister` class, and the `RegisterEvent`. Note that the former is a wrapper around the latter, and is thus recommended in order to prevent mistakes. +NeoForge offers two ways to register objects: the `DeferredRegister` class, and the `RegisterEvent`. Note that the former is a wrapper around the latter, and is recommended in order to prevent mistakes. ### `DeferredRegister` @@ -85,8 +85,9 @@ There are specialized variants of `DeferredRegister`s for blocks and items that public void register(RegisterEvent event) { event.register( // This is the registry key of the registry. - // Get these from Registries for vanilla registries, or from NeoForgeRegistries.Keys for NeoForge registries. - Registries.BLOCKS, + // Get these from BuiltInRegistries for vanilla registries, + // or from NeoForgeRegistries.Keys for NeoForge registries. + BuiltInRegistries.BLOCKS, // Register your objects here. registry -> { registry.register(new ResourceLocation(MODID, "example_block_1"), new Block(...)); @@ -102,12 +103,12 @@ public void register(RegisterEvent event) { Sometimes, you will find yourself in situations where you want to get a registered object by a given id. Or, you want to get the id of a certain registered object. Since registries are basically maps of ids (`ResourceLocation`s) to distinct objects, i.e. a reversible map, both of these operations work: ```java -Registries.BLOCKS.get(new ResourceLocation("minecraft", "dirt")); // returns the dirt block -Registries.BLOCKS.getKey(Blocks.DIRT); // returns the resource location "minecraft:dirt" +BuiltInRegistries.BLOCKS.get(new ResourceLocation("minecraft", "dirt")); // returns the dirt block +BuiltInRegistries.BLOCKS.getKey(Blocks.DIRT); // returns the resource location "minecraft:dirt" // Assume that ExampleBlocksClass.EXAMPLE_BLOCK.get() is a Supplier with the id "yourmodid:example_block" -Registries.BLOCKS.get(new ResourceLocation("yourmodid", "example_block")); // returns the example block -Registries.BLOCKS.getKey(ExampleBlocksClass.EXAMPLE_BLOCK.get()); // returns the resource location "yourmodid:example_block" +BuiltInRegistries.BLOCKS.get(new ResourceLocation("yourmodid", "example_block")); // returns the example block +BuiltInRegistries.BLOCKS.getKey(ExampleBlocksClass.EXAMPLE_BLOCK.get()); // returns the resource location "yourmodid:example_block" ``` If you just want to check for the presence of an object, this is also possible, though only with keys: @@ -131,7 +132,7 @@ for (Map.Entry entry : Registries.BLOCKS.entrySet()) { ``` :::note -Query operations should always use vanilla `Registry`s, not `DeferredRegister`s. This is because `DeferredRegister`s are merely registration utilities and effectively do not exist after registration has finished. +Query operations always use vanilla `Registry`s, not `DeferredRegister`s. This is because `DeferredRegister`s are merely registration utilities. ::: :::danger diff --git a/docs/concepts/sides.md b/docs/concepts/sides.md index f1c612600..48712d430 100644 --- a/docs/concepts/sides.md +++ b/docs/concepts/sides.md @@ -18,7 +18,7 @@ The logical side is mainly focused on the internal program structure of Minecraf The difference between physical and logical sides is best exemplified by two scenarios: -- The player joins a **multiplayer** world. This is fairly straightforward: The player's logical and physical client connects to a logical server somewhere else - the player does not care where; so long as they can connect, that's all the client knows of, and all the client needs to know. +- The player joins a **multiplayer** world. This is fairly straightforward: The player's physical (and logical) client connects to a physical (and logical) server somewhere else - the player does not care where; so long as they can connect, that's all the client knows of, and all the client needs to know. - The player joins a **singleplayer** world. This is where things get interesting. The player's physical client spins up a logical server and then, now in the role of the logical client, connects to that logical server on the same machine. If you are familiar with networking, you can think of it as a connection to `localhost` (only conceptually; there are no actual sockets or similar involved). These two scenarios also show the main problem with this: If a logical server can work with your code, that alone doesn't guarantee that a physical server will be able to work with as well. This is why you should always test with dedicated servers to check for unexpected behavior. `NoClassDefFoundError`s and `ClassNotFoundException`s due to incorrect client and server separation are among the most common errors there are in modding. Another common mistake is working with static fields and accessing them from both logical sides; this is particularly tricky because there's usually no indication that something is wrong. diff --git a/docs/items/index.md b/docs/items/index.md index 1c45deb72..dab3959d0 100644 --- a/docs/items/index.md +++ b/docs/items/index.md @@ -70,10 +70,10 @@ public static final Supplier EXAMPLE_ITEM = ITEMS.registerItem( Internally, this will simply call `ITEMS.register("example_item", () -> new Item(new Item.Properties()))` by applying the properties parameter to the provided item factory (which is commonly the constructor). -If you want to use `Item::new`, you can leave out the factory entirely: +If you want to use `Item::new`, you can leave out the factory entirely and use the `simple` method variant: ```java -public static final Supplier EXAMPLE_ITEM = ITEMS.registerItem( +public static final Supplier EXAMPLE_ITEM = ITEMS.registerSimpleItem( "example_item", new Item.Properties() // The properties to use. ); @@ -81,10 +81,10 @@ public static final Supplier EXAMPLE_ITEM = ITEMS.registerItem( This does the exact same as the previous example, but is slightly shorter. Of course, if you want to use a subclass of `Item` and not `Item` itself, you will have to use the previous method instead. -Both of these methods also have `simple` counterparts that omit the `new Item.Properties()` parameter: +Both of these methods also have overloads that omit the `new Item.Properties()` parameter: ```java -public static final Supplier EXAMPLE_ITEM = ITEMS.registerSimpleItem("example_item", Item::new); +public static final Supplier EXAMPLE_ITEM = ITEMS.registerItem("example_item", Item::new); // Variant that also omits the Item::new parameter public static final Supplier EXAMPLE_ITEM = ITEMS.registerSimpleItem("example_item"); ``` diff --git a/docs/misc/resourcelocation.md b/docs/misc/resourcelocation.md index 9b2d07fb6..f317a7341 100644 --- a/docs/misc/resourcelocation.md +++ b/docs/misc/resourcelocation.md @@ -2,7 +2,7 @@ `ResourceLocation`s are one of the most important things in Minecraft. They are used as keys in [registries][registries], as identifiers for data or resource files, as references to models in code, and in a lot of other places. A `ResourceLocation` consists of two parts: a namespace and a path, separated by a `:`. -The namespace denotes what mod or datapack the location refers to. For example, a mod with the mod id `examplemod` will use the `examplemod` namespace. Minecraft uses the `minecraft` namespace. Extra namespaces can be defined at will simply by creating a corresponding data folder, this is usually done by datapacks to keep their logic separate from the point where they integrate with vanilla. +The namespace denotes what mod, resource pack or datapack the location refers to. For example, a mod with the mod id `examplemod` will use the `examplemod` namespace. Minecraft uses the `minecraft` namespace. Extra namespaces can be defined at will simply by creating a corresponding data folder, this is usually done by datapacks to keep their logic separate from the point where they integrate with vanilla. The path is a reference to whatever object you want, inside your namespace. For example, `minecraft:cow` is a reference to something named `cow` in the `minecraft` namespace - usually this location would be used to get the cow entity from the entity registry. Another example would be `examplemod:example_item`, which would probably be used to get your mod's `example_item` from the item registry. @@ -23,14 +23,14 @@ The namespace and path of a `ResourceLocation` can be retrieved using `ResourceL Some places, for example registries, use `ResourceLocation`s directly. Some other places, however, will resolve the `ResourceLocation` as needed. For example: - `ResourceLocation`s are used as identifiers for GUI background. For example, the furnace GUI uses the resource location `minecraft:textures/gui/container/furnace.png`. This maps to the file `assets/minecraft/textures/gui/container/furnace.png` on disk. Note that the `.png` suffix is required in this resource location. -- `ResourceLocation`s are used as identifiers for block models. For example, the block model of dirt uses the resource location `minecraft:models/block/dirt`. This maps to the file `assets/minecraft/models/block/dirt.json` on disk. Note that the `.json` suffix is NOT required in this resource location. +- `ResourceLocation`s are used as identifiers for block models. For example, the block model of dirt uses the resource location `minecraft:block/dirt`. This maps to the file `assets/minecraft/models/block/dirt.json` on disk. Note that the `.json` suffix is not required here. Note as well that this resource location automatically maps into the `models` subfolder. - `ResourceLocation`s are used as identifiers for recipes. For example, the iron block crafting recipe uses the resource location `minecraft:iron_block`. This maps to the file `data/minecraft/recipes/iron_block.json` on disk. Note that the `.json` suffix is not required here. Note as well that this resource location automatically maps into the `recipes` subfolder. Whether the `ResourceLocation` expects a file suffix, or what exactly the resource location resolves to, depends on the use case. ## `ModelResourceLocation`s -`ModelResourceLocation`s are a special kind of resource location that includes a third part, called the variant. Minecraft uses these mainly to differentiate between different variants of item models, where the different variants are used in different display contexts (for example with tridents, which have different models in first person, third person and inventories). +`ModelResourceLocation`s are a special kind of resource location that includes a third part, called the variant. Minecraft uses these mainly to differentiate between different variants of models, where the different variants are used in different display contexts (for example with tridents, which have different models in first person, third person and inventories). The variant is always `inventory` for items, and the comma-delimited string of property-value pairs for blockstates (for example `facing=north,waterlogged=false`, empty for blocks with no blockstate properties). The variant is appended to the regular resource location, along with a `#`. For example, the full name of the diamond sword's item model is `minecraft:diamond_sword#inventory`. However, in most contexts, the `inventory` variant can be omitted. @@ -42,5 +42,7 @@ The variant is appended to the regular resource location, along with a `#`. For A new `ResourceKey` can be created through the static method `ResourceKey#create(ResourceKey>, ResourceLocation)`. The second parameter here is the registry name, while the first parameter is what is known as a registry key. Registry keys are a special kind of `ResourceKey` whose registry is the root registry (i.e. the registry of all other registries). A registry key can be created via `ResourceKey#createRegistryKey(ResourceLocation)` with the desired registry's id. +`ResourceKey`s are interned at creation. This means that comparing by reference equality (`==`) is possible and encouraged, but their creation is comparatively expensive. + [registries]: ../concepts/registries.md [sides]: ../concepts/sides.md From 7a24414dad609180c3f6cabab101abdd6490c8a3 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Fri, 23 Feb 2024 01:13:58 +0100 Subject: [PATCH 17/59] fix datapack registry creation --- docs/concepts/registries.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index 27c5959f5..4c9939ac3 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -198,12 +198,14 @@ Datapack registries can be obtained from a `RegistryAccess`. This `RegistryAcces ### Custom Datapack Registries -Custom datapack registries are created through `RegistryBuilder` like all other registries, but are registered to `DataPackRegistryEvent.NewRegistry` instead of `NewRegistryEvent`. Reiterating the spells example from before, registering our spell registry as a datapack registry looks something like this: +Custom datapack registries do not require a `Registry` to be constructed. Instead, they just need a registry key and at least one [`Codec`][codec] to (de-)serialize its contents. Reiterating on the spells example from before, registering our spell registry as a datapack registry looks something like this: ```java +public static final ResourceKey> SPELL_REGISTRY_KEY = ResourceKey.createRegistryKey(new ResourceLocation("yourmodid", "spells")); + @SubscribeEvent -static void registerDatapackRegistries(DataPackRegistryEvent.NewRegistry event) { - event.register( +public static void registerDatapackRegistries(DataPackRegistryEvent.NewRegistry event) { + event.dataPackRegistry( // The registry key. SPELL_REGISTRY_KEY, // The codec of the registry contents. From 55873a11245fe70f12daa07fd9c4520100b57e29 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Fri, 23 Feb 2024 16:10:40 +0100 Subject: [PATCH 18/59] remove recommendation of separate DR and DH classes --- docs/concepts/registries.md | 4 ---- docs/items/index.md | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index 4c9939ac3..83381c3ad 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -46,10 +46,6 @@ public static final DeferredHolder EXAMPLE_BLOCK = BLOCKS.register The class `DeferredHolder` holds our object. The type parameter `R` is the type of the registry we are registering to (in our case `Block`). The type parameter `T` is the type of our supplier. Since we directly register a `Block` in this example, we provide `Block` as the second parameter. If we were to register an object of a subclass of `Block`, for example `SlabBlock`, we would provide `SlabBlock` here instead. -:::note -Some modders prefer to keep their `DeferredRegister`s in the same class as their registered objects. Others prefer keeping all `DeferredRegister`s in a separate class for readability. This is mostly a design decision, however if you decide to do the latter, make sure to classload the classes the objects are in, for example through an empty static method. -::: - `DeferredHolder` is a subclass of `Supplier`. To get our registered object when we need it, we can call `DeferredHolder#get()`. The fact that `DeferredHolder` extends `Supplier` also allows us to use `Supplier` as the type of our field. That way, the above code block becomes the following: ```java diff --git a/docs/items/index.md b/docs/items/index.md index dab3959d0..73ff7a79a 100644 --- a/docs/items/index.md +++ b/docs/items/index.md @@ -102,7 +102,7 @@ public static final Supplier EXAMPLE_BLOCK_ITEM = ITEMS.registerSimpl ``` :::note -If you keep your blocks in a separate class, you should classload your blocks class before your items class. +If you keep your registered blocks in a separate class, you should classload your blocks class before your items class. ::: ### Resources From cdf58f2f23423f8341c5c00d37fc08415b502d2c Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Fri, 23 Feb 2024 16:41:34 +0100 Subject: [PATCH 19/59] fix an oversight --- docs/concepts/registries.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index 83381c3ad..5e901f779 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -110,8 +110,8 @@ BuiltInRegistries.BLOCKS.getKey(ExampleBlocksClass.EXAMPLE_BLOCK.get()); // retu If you just want to check for the presence of an object, this is also possible, though only with keys: ```java -Registries.BLOCKS.containsKey(new ResourceLocation("minecraft", "dirt")); // true -Registries.BLOCKS.containsKey(new ResourceLocation("create", "brass_ingot")); // true only if Create is installed +BuiltInRegistries.BLOCKS.containsKey(new ResourceLocation("minecraft", "dirt")); // true +BuiltInRegistries.BLOCKS.containsKey(new ResourceLocation("create", "brass_ingot")); // true only if Create is installed ``` As the last example shows, this is possible with any mod id, and thus a perfect way to check if a certain item from another mod exists. From 380db4041bb063948beef79cfee86289b2e138e0 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 <52419336+IchHabeHunger54@users.noreply.github.com> Date: Fri, 23 Feb 2024 17:17:23 +0100 Subject: [PATCH 20/59] Update docs/blocks/index.md Co-authored-by: ChampionAsh5357 --- docs/blocks/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/blocks/index.md b/docs/blocks/index.md index 3622a17dd..5a0c07605 100644 --- a/docs/blocks/index.md +++ b/docs/blocks/index.md @@ -106,7 +106,7 @@ public static final DeferredBlock EXAMPLE_BLOCK = BLOCKS.registerBlock( ); ``` -Internally, this will simply call `BLOCKS.register("example_block", () -> new Block(new BlockBehaviour.Properties()))` by applying the properties parameter to the provided block factory (which is commonly the constructor). +Internally, this will simply call `BLOCKS.register("example_block", () -> new Block(BlockBehaviour.Properties.of()))` by applying the properties parameter to the provided block factory (which is commonly the constructor). If you want to use `Block::new`, you can leave out the factory entirely: From 2a369a74d9e5b42f3a687bedeb33b55a2a44de37 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Sun, 25 Feb 2024 19:46:35 +0100 Subject: [PATCH 21/59] BootstapContext :screm: --- docs/concepts/registries.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index 5e901f779..ebf044619 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -255,7 +255,7 @@ new RegistrySetBuilder() }); ``` -The `BootstrapContext` can also be used to lookup entries from another registry if needed: +The `BootstrapContext` (name is typoed as `BootstapContext` in 1.20.4 and below) can also be used to lookup entries from another registry if needed: ```java public static final ResourceKey> EXAMPLE_CONFIGURED_FEATURE = ResourceKey.create( From b5a78bb1efce03065b86866ad2f8f3df22e1e0c7 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Mon, 26 Feb 2024 15:43:02 +0100 Subject: [PATCH 22/59] fix two wrong mentions of Registries --- docs/concepts/registries.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/concepts/registries.md b/docs/concepts/registries.md index ebf044619..d4d97e13a 100644 --- a/docs/concepts/registries.md +++ b/docs/concepts/registries.md @@ -119,10 +119,10 @@ As the last example shows, this is possible with any mod id, and thus a perfect Finally, we can also iterate over all entries in a registry, either over the keys or over the entries (entries use the Java `Map.Entry` type): ```java -for (ResourceLocation id : Registries.BLOCKS.keySet()) { +for (ResourceLocation id : BuiltInRegistries.BLOCKS.keySet()) { // ... } -for (Map.Entry entry : Registries.BLOCKS.entrySet()) { +for (Map.Entry entry : BuiltInRegistries.BLOCKS.entrySet()) { // ... } ``` From b6dcbb9f583e206f1ff6f0b94e0b94e28808de49 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Mon, 26 Feb 2024 16:28:03 +0100 Subject: [PATCH 23/59] fix exception name --- docs/misc/resourcelocation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/misc/resourcelocation.md b/docs/misc/resourcelocation.md index f317a7341..89cc8e4fb 100644 --- a/docs/misc/resourcelocation.md +++ b/docs/misc/resourcelocation.md @@ -34,7 +34,7 @@ Whether the `ResourceLocation` expects a file suffix, or what exactly the resour The variant is appended to the regular resource location, along with a `#`. For example, the full name of the diamond sword's item model is `minecraft:diamond_sword#inventory`. However, in most contexts, the `inventory` variant can be omitted. -`ModelResourceLocation` is a [client only][sides] class. This means that servers referencing this class will crash with a `NoClassDefError`. +`ModelResourceLocation` is a [client only][sides] class. This means that servers referencing this class will crash with a `NoClassDefFoundError`. ## `ResourceKey`s From 99dd0e6babf60bdc110fe0800b8fb870fe18d5b2 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Wed, 2 Oct 2024 20:14:04 +0200 Subject: [PATCH 24/59] add Champ's previous work on entities --- docs/entities/_category_.json | 4 + docs/entities/index.md | 134 ++++++++++++++++++++++++++++++++++ docs/entities/renderer.md | 3 + 3 files changed, 141 insertions(+) create mode 100644 docs/entities/_category_.json create mode 100644 docs/entities/index.md create mode 100644 docs/entities/renderer.md diff --git a/docs/entities/_category_.json b/docs/entities/_category_.json new file mode 100644 index 000000000..8afd8a39e --- /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/index.md b/docs/entities/index.md new file mode 100644 index 000000000..18b2b0c41 --- /dev/null +++ b/docs/entities/index.md @@ -0,0 +1,134 @@ +# Entities + +Entities are actors that can interact with the world in a variety of ways. Entities comprise mobs, projectiles, rideable objects, and even players. Each entity is comprised of multiple systems that may not seem understandable at first glance. This section will breakdown some of the key components related to constructing an entity and making it behave as the modder intends. + +## A Quick Summary + +Entities, in their simplest and most trivial form, are made up of four parts: + +- The [`EntityType` registry object][entitytype] + - The default properties that all entities have +- An [`Entity` subclass][entity] + - The logic to execute when this entity ticks or is interacted with +- An [`EntityRenderer` subclass][entityrenderer] + - How the entity is rendered in-game +- A [summoning method][summon] + - How the entity is spanwed into a level + +An entity may require more parts depending on the subclass of `Entity` and `EntityRenderer` used (e.g., [`LivingEntity` or `Mob`][living]). + +:::warning +The simplest and most trivial entity does absolutely nothing. Most users will only need to extend a subclass of `Entity` and `EntityRenderer`. However, understanding the underlying principles is necessary to more effectively create entities and avoid hard to diagnose bugs. +::: + +## `EntityType`: The Grounding Principle + +An `EntityType` is the singleton that defines what an entity is. Multiple entities within the world can be associated with one `EntityType`. The `EntityType` also links an [`EntityRenderer`][entityrenderer] to its corresponding `Entity` class. It also defines default properties that all entities with this type has. The `EntityType` must be [registered]. + +An `EntityType` can be constructed via `EntityType.Builder#of`. This method takes in two parameters: an `EntityType.EntityFactory` that [constructs a default `Entity` instance][entity], and a [`MobCategory`][mobcategory] indicating the entity type. The `EntityType` can be built via `EntityType.Builder#build` by passing in `null` as the argument. + +:::info +The `String` in the `build` method represents the registry name of the `EntityType` and is used when dealing with data fixers. The reason we pass in `null` is that data fixers go unused in mod development. If a value is passed in, then Vanilla's data fixer will continually throw a warning when loading the game as there is no definition within an applicable schema. +::: + +```java +// For some entity +public class ExampleEntity extends Entity { + + // This is the constructor definition for use in 'EntityType.EntityFactory' + public ExampleEntity(EntityType type, Level level) { + super(type, level); + // ... + } + + // ... +} + +// In some class that holds registry objects +public static final DeferredRegister> REGISTRAR = DeferredRegister.create(Registries.ENTITY_TYPE, MOD_ID); + +public static final DeferredHolder, EntityType> EXAMPLE_ENITTY = REGISTRAR.register( + "example_entity", + () -> EntityType.Builder.of( + ExampleEntity::new // The constructor definition, + MobCategory.MISC // Category for entities that do not generally extend 'Mob' + ).build(null) // String value goes unused +); +``` + +:::note +The builder contains many methods that will be further discussed in other sections to help with understanding. An example for applying each to an `EntityType` will be provided there. +::: + +## Everything Revolves Around `Entity` + +The `Entity` class is the base containing the logic that the entity executes when ticked or interacted with. The `Entity` constructor takes in two parameters: the `EntityType` of the entity, and the `Level` the entity is spawned in. This constructor is passed into the `EntityType` builder to spawn entities on both the server and the client. + +```java +public class ExampleEntity extends Entity { + + // This is the constructor definition for use in 'EntityType.EntityFactory' + public ExampleEntity(EntityType type, Level level) { + super(type, level); + // ... + } +} +``` + +If the entity will be spawned manually rather than by the game, then additional constructors can be added to spawn the entity. These constructors usually hardcode the entity type to whatever is attempting to be spawned. + +```java +public class ExampleEntity extends Entity { + + public ExampleEntity(Level level, double x, double y, double z) { + // Delegates to the factory constructor + this(EXAMPLE_ENITTY.value(), level); + this.setPos(x, y, z); + } +} +``` + +### To Pick or Not to Pick + +TODO + +### Entity Dimensions + +TODO + +### Entity Attachments + +TODO + +### Reading and Writing Data + +TODO + +### Synchronizing to the Client + +TODO (Move to its own page) + +## Summoning an Entity + +TODO + +### `LevelWriter#addFreshEntity` + +TODO + +### `SpawnerData` + +TODO + +#### `MobSpawnCost` + +TODO + +[entitytype]: #entitytype-the-grounding-principle +[entity]: #everything-revolves-around-entity +[entityrenderer]: ./renderer.md +[summon]: #summoning-an-entity +[registered]: ../concepts/registries.md#methods-for-registering + +[living]: # +[mobcategory]: # diff --git a/docs/entities/renderer.md b/docs/entities/renderer.md new file mode 100644 index 000000000..539f37336 --- /dev/null +++ b/docs/entities/renderer.md @@ -0,0 +1,3 @@ +# Entity Renderers + +TODO \ No newline at end of file From a9937e3a011fad02eb4023710ecc37deb0cfdf5d Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Thu, 3 Oct 2024 01:55:15 +0200 Subject: [PATCH 25/59] basic structure setup and entitytype documentation --- docs/entities/attributes.md | 6 + docs/entities/data.md | 12 ++ docs/entities/index.md | 205 +++++++++++++++++++------------- docs/entities/livingentity.md | 6 + docs/entities/projectile.md | 6 + docs/entities/renderer.md | 3 + docs/entities/spawning.md | 6 + docs/networking/entities.md | 30 ----- docs/networking/streamcodecs.md | 6 +- 9 files changed, 164 insertions(+), 116 deletions(-) create mode 100644 docs/entities/attributes.md create mode 100644 docs/entities/data.md create mode 100644 docs/entities/livingentity.md create mode 100644 docs/entities/projectile.md create mode 100644 docs/entities/spawning.md delete mode 100644 docs/networking/entities.md diff --git a/docs/entities/attributes.md b/docs/entities/attributes.md new file mode 100644 index 000000000..f2ff4649b --- /dev/null +++ b/docs/entities/attributes.md @@ -0,0 +1,6 @@ +--- +sidebar_position: 5 +--- +# Attributes + +TODO \ No newline at end of file diff --git a/docs/entities/data.md b/docs/entities/data.md new file mode 100644 index 000000000..694439963 --- /dev/null +++ b/docs/entities/data.md @@ -0,0 +1,12 @@ +--- +sidebar_position: 2 +--- +# Data and Networking + +TODO + +## Custom Network Messages + +Of course, you can also always opt to use a custom packet to send information. Please refer to the [Networking articles][networking] for more information. + +[networking]: ../networking/index.md diff --git a/docs/entities/index.md b/docs/entities/index.md index 18b2b0c41..9c1a5d382 100644 --- a/docs/entities/index.md +++ b/docs/entities/index.md @@ -1,134 +1,173 @@ +--- +sidebar_position: 1 +--- # Entities -Entities are actors that can interact with the world in a variety of ways. Entities comprise mobs, projectiles, rideable objects, and even players. Each entity is comprised of multiple systems that may not seem understandable at first glance. This section will breakdown some of the key components related to constructing an entity and making it behave as the modder intends. +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. -## A Quick Summary +## Terminology -Entities, in their simplest and most trivial form, are made up of four parts: +A simple entity is made up of three parts: -- The [`EntityType` registry object][entitytype] - - The default properties that all entities have -- An [`Entity` subclass][entity] - - The logic to execute when this entity ticks or is interacted with -- An [`EntityRenderer` subclass][entityrenderer] - - How the entity is rendered in-game -- A [summoning method][summon] - - How the entity is spanwed into a level +- 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 -An entity may require more parts depending on the subclass of `Entity` and `EntityRenderer` used (e.g., [`LivingEntity` or `Mob`][living]). +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]. -:::warning -The simplest and most trivial entity does absolutely nothing. Most users will only need to extend a subclass of `Entity` and `EntityRenderer`. However, understanding the underlying principles is necessary to more effectively create entities and avoid hard to diagnose bugs. -::: - -## `EntityType`: The Grounding Principle +## `EntityType` -An `EntityType` is the singleton that defines what an entity is. Multiple entities within the world can be associated with one `EntityType`. The `EntityType` also links an [`EntityRenderer`][entityrenderer] to its corresponding `Entity` class. It also defines default properties that all entities with this type has. The `EntityType` must be [registered]. +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. -An `EntityType` can be constructed via `EntityType.Builder#of`. This method takes in two parameters: an `EntityType.EntityFactory` that [constructs a default `Entity` instance][entity], and a [`MobCategory`][mobcategory] indicating the entity type. The `EntityType` can be built via `EntityType.Builder#build` by passing in `null` as the argument. - -:::info -The `String` in the `build` method represents the registry name of the `EntityType` and is used when dealing with data fixers. The reason we pass in `null` is that data fixers go unused in mod development. If a value is passed in, then Vanilla's data fixer will continually throw a warning when loading the game as there is no definition within an applicable schema. -::: +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 -// For some entity -public class ExampleEntity extends Entity { - - // This is the constructor definition for use in 'EntityType.EntityFactory' - public ExampleEntity(EntityType type, Level level) { - super(type, level); - // ... - } - - // ... -} - -// In some class that holds registry objects -public static final DeferredRegister> REGISTRAR = DeferredRegister.create(Registries.ENTITY_TYPE, MOD_ID); - -public static final DeferredHolder, EntityType> EXAMPLE_ENITTY = REGISTRAR.register( - "example_entity", - () -> EntityType.Builder.of( - ExampleEntity::new // The constructor definition, - MobCategory.MISC // Category for entities that do not generally extend 'Mob' - ).build(null) // String value goes unused +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. + .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 is a string used for datafixing; mods generally + // do not utilize this and can safely pass null here instead. + .build(null) ); ``` -:::note -The builder contains many methods that will be further discussed in other sections to help with understanding. An example for applying each to an `EntityType` will be provided there. +:::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(...) +``` ::: -## Everything Revolves Around `Entity` +### `MobCategory` -The `Entity` class is the base containing the logic that the entity executes when ticked or interacted with. The `Entity` constructor takes in two parameters: the `EntityType` of the entity, and the `Level` the entity is spawned in. This constructor is passed into the `EntityType` builder to spawn entities on both the server and the client. +TODO -```java -public class ExampleEntity extends Entity { +## The Entity Class - // This is the constructor definition for use in 'EntityType.EntityFactory' - public ExampleEntity(EntityType type, Level level) { +To begin, we create an `Entity` subclass. Alongside a constructor, `Entity` (which is an abstract class) defines three required methods that we are required to implement. These will be explained in the [Data and Networking article][data], in order to not further bloat this article. + +```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) {} } ``` -If the entity will be spawned manually rather than by the game, then additional constructors can be added to spawn the entity. These constructors usually hardcode the entity type to whatever is attempting to be spawned. +:::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. +::: -```java -public class ExampleEntity extends Entity { +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: - public ExampleEntity(Level level, double x, double y, double z) { - // Delegates to the factory constructor - this(EXAMPLE_ENITTY.value(), level); - this.setPos(x, y, z); - } +```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); } ``` -### To Pick or Not to Pick +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. -TODO +### Data Storage on Entities -### Entity Dimensions +_See [Entities/Data and Networking][data]._ -TODO +### Rendering -### Entity Attachments +_See [Entities/Entity Renderers][renderer]._ -TODO +### Spawning -### Reading and Writing Data +If we now boot up the game now and enter a world, we have exactly one way of spawning: through the `/summon` command (assuming `EntityType.Builder#noSummon` was not called). -TODO +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: -### Synchronizing to the Client +```java +// In some method that has a level available +MyEntity entity = new MyEntity(level, 100.0, 200.0, 300.0); +level.addFreshEntity(entity); +``` -TODO (Move to its own page) +For more complex spawn behavior, please refer to the [Spawning article][spawning]. -## Summoning an Entity +### Ticking Entities TODO -### `LevelWriter#addFreshEntity` +### Picking Entities TODO -### `SpawnerData` +### Entity Attachments TODO -#### `MobSpawnCost` +## Entity Class Hierarchy TODO -[entitytype]: #entitytype-the-grounding-principle -[entity]: #everything-revolves-around-entity -[entityrenderer]: ./renderer.md -[summon]: #summoning-an-entity -[registered]: ../concepts/registries.md#methods-for-registering - -[living]: # -[mobcategory]: # +[data]: data.md +[entity]: #the-entity-class +[hierarchy]: #entity-class-hierarchy +[item]: ../items/index.md +[itemstack]: ../items/index.md#itemstacks +[registration]: ../concepts/registries.md#methods-for-registering +[renderer]: renderer.md +[spawning]: spawning.md +[type]: #entitytype diff --git a/docs/entities/livingentity.md b/docs/entities/livingentity.md new file mode 100644 index 000000000..7f94d3bf9 --- /dev/null +++ b/docs/entities/livingentity.md @@ -0,0 +1,6 @@ +--- +sidebar_position: 4 +--- +# Living Entities, Mobs & Players + +TODO \ No newline at end of file diff --git a/docs/entities/projectile.md b/docs/entities/projectile.md new file mode 100644 index 000000000..87017b1c1 --- /dev/null +++ b/docs/entities/projectile.md @@ -0,0 +1,6 @@ +--- +sidebar_position: 3 +--- +# Projectiles + +TODO \ No newline at end of file diff --git a/docs/entities/renderer.md b/docs/entities/renderer.md index 539f37336..71b581e7e 100644 --- a/docs/entities/renderer.md +++ b/docs/entities/renderer.md @@ -1,3 +1,6 @@ +--- +sidebar_position: 6 +--- # Entity Renderers TODO \ No newline at end of file diff --git a/docs/entities/spawning.md b/docs/entities/spawning.md new file mode 100644 index 000000000..9d7c02a03 --- /dev/null +++ b/docs/entities/spawning.md @@ -0,0 +1,6 @@ +--- +sidebar_position: 7 +--- +# Spawning Entities + +TODO \ No newline at end of file diff --git a/docs/networking/entities.md b/docs/networking/entities.md deleted file mode 100644 index 80be9b6dc..000000000 --- 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 c2a54c0fc..6c862382d 100644 --- a/docs/networking/streamcodecs.md +++ b/docs/networking/streamcodecs.md @@ -71,7 +71,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`. @@ -381,6 +381,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 From 4dd1142c5be1c834b078ca052b6d692dcc95575d Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Thu, 3 Oct 2024 02:23:52 +0200 Subject: [PATCH 26/59] entity class hierarchy --- docs/entities/index.md | 15 ++++++++++++++- docs/items/index.md | 3 ++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/entities/index.md b/docs/entities/index.md index 9c1a5d382..2f9a62d7a 100644 --- a/docs/entities/index.md +++ b/docs/entities/index.md @@ -160,13 +160,26 @@ TODO ## Entity Class Hierarchy -TODO +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. + +Direct subclasses of `Entity` include: + +- `Projectile`: The base class for various projectiles, including arrows, fireballs, snowballs, fireworks and similar entities. Read more about them in the [Projectiles article][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]. +- `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. +- `BlockAttachedEntity`: The base class for entities that are immobile and attached to blocks. Includes leash knots, item frames and paintings. +- `Display`: The base class for the various map-maker display entities. + +Several entities are also direct subclasses of `Entity`, simply because there was no other fitting superclass. Prominent examples include `ItemEntity` (dropped items), `LightningBolt`, `ExperienceOrb` and `PrimedTnt`. [data]: data.md [entity]: #the-entity-class [hierarchy]: #entity-class-hierarchy [item]: ../items/index.md [itemstack]: ../items/index.md#itemstacks +[livingentity]: livingentity.md +[mobeffect]: ../items/mobeffects.md +[projectile]: projectile.md [registration]: ../concepts/registries.md#methods-for-registering [renderer]: renderer.md [spawning]: spawning.md diff --git a/docs/items/index.md b/docs/items/index.md index c0fa0ab51..df295d0a3 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. @@ -216,6 +216,7 @@ It is also possible to implement `ItemLike` on your custom objects. Simply overr [creativetabs]: #creative-tabs [datacomponents]: ./datacomponents.mdx [datagen]: ../resources/index.md#data-generation +[entity]: ../entities/index.md [food]: #food [hunger]: https://minecraft.wiki/w/Hunger#Mechanics [interactionpipeline]: interactionpipeline.md From f615ade7c8a5a2d254c61d3fb4552eb17e65613d Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Thu, 3 Oct 2024 15:51:05 +0200 Subject: [PATCH 27/59] damaging and ticking entities --- docs/entities/index.md | 52 ++++++++++++++++++++++++++-- docs/entities/livingentity.md | 10 +++++- docs/resources/server/damagetypes.md | 5 +-- 3 files changed, 61 insertions(+), 6 deletions(-) diff --git a/docs/entities/index.md b/docs/entities/index.md index 2f9a62d7a..a2a853c87 100644 --- a/docs/entities/index.md +++ b/docs/entities/index.md @@ -128,11 +128,11 @@ And now, we are free to do basically whatever we want with our entity. The follo _See [Entities/Data and Networking][data]._ -### Rendering +### Rendering Entities _See [Entities/Entity Renderers][renderer]._ -### Spawning +### Spawning Entities If we now boot up the game now and enter a world, we have exactly one way of spawning: through the `/summon` command (assuming `EntityType.Builder#noSummon` was not called). @@ -146,9 +146,52 @@ level.addFreshEntity(entity); For more complex spawn behavior, please refer to the [Spawning article][spawning]. +### Damaging Entities + +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 `Entity#hurt`. `Entity#hurt` takes 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, it is also possible for entities to modify the behavior in `#hurt` by overriding it. For example, we could make our entity take double damage from fire, and no damage from any other source, like so: + +```java +@Override +// The boolean return value determines whether the entity was actually damaged or not. +public boolean hurt(DamageSource damageSource, float amount) { + if (damageSource.is(DamageTypeTags.IS_FIRE)) { + return super.hurt(damageSource, amount * 2); + } else { + return false; + } +} +``` + +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. Please see [Damage Events][damageevents] for more information. + ### Ticking Entities -TODO +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. + - 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 @@ -172,6 +215,8 @@ Direct subclasses of `Entity` include: Several entities are also direct subclasses of `Entity`, simply because there was no other fitting superclass. Prominent examples include `ItemEntity` (dropped items), `LightningBolt`, `ExperienceOrb` and `PrimedTnt`. +[damageevents]: livingentity.md#damage-events +[damagesource]: ../resources/server/damagetypes.md#creating-and-using-damage-sources [data]: data.md [entity]: #the-entity-class [hierarchy]: #entity-class-hierarchy @@ -179,6 +224,7 @@ Several entities are also direct subclasses of `Entity`, simply because there wa [itemstack]: ../items/index.md#itemstacks [livingentity]: livingentity.md [mobeffect]: ../items/mobeffects.md +[particle]: ../resources/client/particles.md [projectile]: projectile.md [registration]: ../concepts/registries.md#methods-for-registering [renderer]: renderer.md diff --git a/docs/entities/livingentity.md b/docs/entities/livingentity.md index 7f94d3bf9..d0dad1d88 100644 --- a/docs/entities/livingentity.md +++ b/docs/entities/livingentity.md @@ -3,4 +3,12 @@ sidebar_position: 4 --- # Living Entities, Mobs & Players -TODO \ No newline at end of file +TODO + +## Health, Damage and Healing + +TODO + +### Damage Events + +TODO diff --git a/docs/resources/server/damagetypes.md b/docs/resources/server/damagetypes.md index af40c2e25..034306029 100644 --- a/docs/resources/server/damagetypes.md +++ b/docs/resources/server/damagetypes.md @@ -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: @@ -128,6 +128,7 @@ 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 +[entityhurt]: ../../entities/index.md#damaging-entities [extenum]: ../../advanced/extensibleenums.md [rk]: ../../misc/resourcelocation.md#resourcekeys [tags]: tags.md From 773ce6aac77e173d8c9678ad2b0f8f69229bd781 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Thu, 3 Oct 2024 16:33:19 +0200 Subject: [PATCH 28/59] picking entities --- docs/entities/index.md | 30 ++++++++++++++++++++++++++++-- 1 file changed, 28 insertions(+), 2 deletions(-) diff --git a/docs/entities/index.md b/docs/entities/index.md index a2a853c87..9798ba6cf 100644 --- a/docs/entities/index.md +++ b/docs/entities/index.md @@ -166,7 +166,7 @@ public boolean hurt(DamageSource damageSource, float amount) { } ``` -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. Please see [Damage Events][damageevents] for more information. +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 @@ -195,7 +195,32 @@ public void tick() { ### Picking Entities -TODO +Entity picking is an ambiguous term because Mojang uses it for two things: the action of middle-clicking an entity to get a spawn egg or similar item, and for selecting the entity (or [block]) to begin with. + +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. + return new ItemStack(MY_CUSTOM_ITEM.get()); +} +``` + +Selecting the entity or block to begin with is done through what is known as a ray cast in basically any other engine. This is mainly used by things like the F3 debug overlay, game-relevant things such as players attacking or breaking things have their own checks in place here. + +Our own entity can be disabled from picking 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` that you can further check for what exactly has been hit by the ray cast. ### Entity Attachments @@ -215,6 +240,7 @@ Direct subclasses of `Entity` include: Several entities are also direct subclasses of `Entity`, simply because there was no other fitting superclass. Prominent examples include `ItemEntity` (dropped items), `LightningBolt`, `ExperienceOrb` and `PrimedTnt`. +[block]: ../blocks/index.md [damageevents]: livingentity.md#damage-events [damagesource]: ../resources/server/damagetypes.md#creating-and-using-damage-sources [data]: data.md From ec869d5da01af67c21fd1e6155116f7f811f4afc Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Thu, 3 Oct 2024 20:16:05 +0200 Subject: [PATCH 29/59] add docs for the left click and middle click pipelines --- docs/blocks/index.md | 9 +- docs/entities/index.md | 15 ++-- docs/gui/menus.md | 2 +- docs/items/index.md | 4 +- docs/items/interactionpipeline.md | 73 ---------------- docs/items/interactions.md | 138 ++++++++++++++++++++++++++++++ 6 files changed, 154 insertions(+), 87 deletions(-) delete mode 100644 docs/items/interactionpipeline.md create mode 100644 docs/items/interactions.md diff --git a/docs/blocks/index.md b/docs/blocks/index.md index ea54c393d..7e766d0c4 100644 --- a/docs/blocks/index.md +++ b/docs/blocks/index.md @@ -198,7 +198,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. @@ -230,11 +230,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. @@ -300,13 +299,13 @@ Random ticking is used by a wide range of mechanics in Minecraft, such as plant [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 [usingblocks]: #using-blocks diff --git a/docs/entities/index.md b/docs/entities/index.md index 9798ba6cf..ecf2f9ebd 100644 --- a/docs/entities/index.md +++ b/docs/entities/index.md @@ -148,6 +148,8 @@ For more complex spawn behavior, please refer to the [Spawning article][spawning ### 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 `Entity#hurt`. `Entity#hurt` takes 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. @@ -195,9 +197,9 @@ public void tick() { ### Picking Entities -Entity picking is an ambiguous term because Mojang uses it for two things: the action of middle-clicking an entity to get a spawn egg or similar item, and for selecting the entity (or [block]) to begin with. +_See also [Middle-Clicking][middleclick]._ -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): +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 @@ -208,9 +210,7 @@ public ItemStack getPickResult() { } ``` -Selecting the entity or block to begin with is done through what is known as a ray cast in basically any other engine. This is mainly used by things like the F3 debug overlay, game-relevant things such as players attacking or breaking things have their own checks in place here. - -Our own entity can be disabled from picking like so: +Your entity can also be disabled from picking entirely like so: ```java @Override @@ -220,7 +220,7 @@ public boolean isPickable() { } ``` -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` that you can further check for what exactly has been hit by the ray cast. +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 @@ -246,9 +246,12 @@ Several entities are also direct subclasses of `Entity`, simply because there wa [data]: data.md [entity]: #the-entity-class [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 [particle]: ../resources/client/particles.md [projectile]: projectile.md diff --git a/docs/gui/menus.md b/docs/gui/menus.md index 05ee87914..7990ca8cf 100644 --- a/docs/gui/menus.md +++ b/docs/gui/menus.md @@ -345,4 +345,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/index.md b/docs/items/index.md index df295d0a3..5ed019129 100644 --- a/docs/items/index.md +++ b/docs/items/index.md @@ -52,7 +52,7 @@ To get the `FoodProperties` for an item, call `Item#getFoodProperties(ItemStack, 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` @@ -219,7 +219,7 @@ It is also possible to implement `ItemLike` on your custom objects. Simply overr [entity]: ../entities/index.md [food]: #food [hunger]: https://minecraft.wiki/w/Hunger#Mechanics -[interactionpipeline]: interactionpipeline.md +[interactions]: interactions.md [loottables]: ../resources/server/loottables/index.md [mobeffectinstance]: mobeffects.md#mobeffectinstances [modbus]: ../concepts/events.md#event-buses diff --git a/docs/items/interactionpipeline.md b/docs/items/interactionpipeline.md deleted file mode 100644 index ff9f56ddc..000000000 --- a/docs/items/interactionpipeline.md +++ /dev/null @@ -1,73 +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 `ItemInteractionResult` is `PASS_TO_DEFAULT_BLOCK_INTERACTION` 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. - -## Result Types - -There are three different types of results: `InteractionResult`s, `ItemInteractionResult`s, and `InteractionResultHolder`s. `InteractionResult` is used most of the time, only `Item#use` uses `InteractionResultHolder`, and only `BlockBehaviour#useItemOn` and `CauldronInteraction#interact` use `ItemInteractionResult`. - -`InteractionResult` is an enum consisting of five values: `SUCCESS`, `CONSUME`, `CONSUME_PARTIAL`, `PASS` and `FAIL`. Additionally, the method `InteractionResult#sidedSuccess` is available, which returns `SUCCESS` on the server and `CONSUME` on the client. - -`InteractionResultHolder` is a wrapper around `InteractionResult` that adds additional context for `T`. `T` can be anything, but in 99.99 percent of cases, it is an `ItemStack`. `InteractionResultHolder` provides wrapper methods for the enum values (`#success`, `#consume`, `#pass` and `#fail`), as well as `#sidedSuccess`, which calls `#success` on the server and `#consume` on the client. - -`ItemInteractionResult` is a parallel to `InteractionResult` specifically for when an item is used on a block. It is an enum of six values: `SUCCESS`, `CONSUME`, `CONSUME_PARTIAL`, `PASS_TO_DEFAULT_BLOCK_INTERACTION`, `SKIP_DEFAULT_BLOCK_INTERACTION`, and `FAIL`. Each `ItemInteractionResult` can be mapped to a `InteractionResult` via `#result`; `PASS_TO_DEFAULT_BLOCK_INTERACTION`, `SKIP_DEFAULT_BLOCK_INTERACTION` both represent `InteractionResult#PASS`. Similarly, `#sidedSucess` also exists for `ItemInteractionResult`. - -Generally, the different values mean the following: - -- `InteractionResult#sidedSuccess` (or `InteractionResultHolder#sidedSuccess` / `ItemInteractionResult#sidedSucess` where needed) should be used if the operation should be considered successful, and you want the arm to swing. The pipeline will end. -- `InteractionResult#SUCCESS` (or `InteractionResultHolder#success` / `ItemInteractionResult#SUCCESS` where needed) should be used if the operation should be considered successful, and you want the arm to swing, but only on one side. Only use this if you want to return a different value on the other logical side for whatever reason. The pipeline will end. -- `InteractionResult#CONSUME` (or `InteractionResultHolder#consume` / `ItemInteractionResult#CONSUME` where needed) should be used if the operation should be considered successful, but you do not want the arm to swing. The pipeline will end. -- `InteractionResult#CONSUME_PARTIAL` is mostly identical to `InteractionResult#CONSUME`, the only difference is in its usage in [`Item#useOn`][itemuseon]. - - `ItemInteractionResult#CONSUME_PARTIAL` is similar within its usage in `BlockBehaviour#useItemOn`. -- `InteractionResult.FAIL` (or `InteractionResultHolder#fail` / `ItemInteractionResult#FAIL` where needed) should be used if the item functionality should be considered failed and no further interaction should be performed. The pipeline will end. This can be used everywhere, but it should be used with care outside of `Item#useOn` and `Item#use`. In many cases, using `InteractionResult.PASS` makes more sense. -- `InteractionResult.PASS` (or `InteractionResultHolder#pass` where needed) should be used if the operation should be considered neither successful nor failed. The pipeline will continue. This is the default behavior (unless otherwise specified). - - `ItemInteractionResult#PASS_TO_DEFAULT_BLOCK_INTERACTION` allows `BlockBehaviour#useWithoutItem` to be called for the mainhand while `#SKIP_DEFAULT_BLOCK_INTERACTION` prevents the method from executing altogether. `#PASS_TO_DEFAULT_BLOCK_INTERACTION` is the default behavior (unless otherwise specified). - -Some methods have special behavior or requirements, which are explained in the below chapters. - -## `IItemExtension#onItemUseFirst` - -`InteractionResult#sidedSuccess` and `InteractionResult.CONSUME` don't have an effect here. Only `InteractionResult.SUCCESS`, `InteractionResult.FAIL` or `InteractionResult.PASS` should be used here. - -## `Item#useOn` - -If you want the operation to be considered successful, but you do not want the arm to swing or an `ITEM_USED` stat point to be awarded, use `InteractionResult.CONSUME_PARTIAL`. - -## `Item#use` - -This is the only instance where the return type is `InteractionResultHolder`. The resulting `ItemStack` in the `InteractionResultHolder` replaces the `ItemStack` the usage was initiated with, if it has changed. - -The default implementation of `Item#use` returns `InteractionResultHolder#consume` when the item is edible and the player can eat the item (because they are hungry, or because the item is always edible), `InteractionResultHolder#fail` when the item is edible but the player cannot eat the item, and `InteractionResultHolder#pass` if the item is not edible. - -Returning `InteractionResultHolder#fail` here while considering the main hand will prevent offhand behavior from running. If you want offhand behavior to run (which you usually want), return `InteractionResultHolder#pass` instead. - -[itemuseon]: #itemuseon diff --git a/docs/items/interactions.md b/docs/items/interactions.md new file mode 100644 index 000000000..9b34ce7e7 --- /dev/null +++ b/docs/items/interactions.md @@ -0,0 +1,138 @@ +--- +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 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 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 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 `ItemInteractionResult` is `PASS_TO_DEFAULT_BLOCK_INTERACTION` 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. + +### Result Types + +There are three different types of results: `InteractionResult`s, `ItemInteractionResult`s, and `InteractionResultHolder`s. `InteractionResult` is used most of the time, only `Item#use` uses `InteractionResultHolder`, and only `BlockBehaviour#useItemOn` and `CauldronInteraction#interact` use `ItemInteractionResult`. + +`InteractionResult` is an enum consisting of five values: `SUCCESS`, `CONSUME`, `CONSUME_PARTIAL`, `PASS` and `FAIL`. Additionally, the method `InteractionResult#sidedSuccess` is available, which returns `SUCCESS` on the server and `CONSUME` on the client. + +`InteractionResultHolder` is a wrapper around `InteractionResult` that adds additional context for `T`. `T` can be anything, but in 99.99 percent of cases, it is an `ItemStack`. `InteractionResultHolder` provides wrapper methods for the enum values (`#success`, `#consume`, `#pass` and `#fail`), as well as `#sidedSuccess`, which calls `#success` on the server and `#consume` on the client. + +`ItemInteractionResult` is a parallel to `InteractionResult` specifically for when an item is used on a block. It is an enum of six values: `SUCCESS`, `CONSUME`, `CONSUME_PARTIAL`, `PASS_TO_DEFAULT_BLOCK_INTERACTION`, `SKIP_DEFAULT_BLOCK_INTERACTION`, and `FAIL`. Each `ItemInteractionResult` can be mapped to a `InteractionResult` via `#result`; `PASS_TO_DEFAULT_BLOCK_INTERACTION`, `SKIP_DEFAULT_BLOCK_INTERACTION` both represent `InteractionResult#PASS`. Similarly, `#sidedSucess` also exists for `ItemInteractionResult`. + +Generally, the different values mean the following: + +- `InteractionResult#sidedSuccess` (or `InteractionResultHolder#sidedSuccess` / `ItemInteractionResult#sidedSucess` where needed) should be used if the operation should be considered successful, and you want the arm to swing. The pipeline will end. +- `InteractionResult#SUCCESS` (or `InteractionResultHolder#success` / `ItemInteractionResult#SUCCESS` where needed) should be used if the operation should be considered successful, and you want the arm to swing, but only on one side. Only use this if you want to return a different value on the other logical side for whatever reason. The pipeline will end. +- `InteractionResult#CONSUME` (or `InteractionResultHolder#consume` / `ItemInteractionResult#CONSUME` where needed) should be used if the operation should be considered successful, but you do not want the arm to swing. The pipeline will end. +- `InteractionResult#CONSUME_PARTIAL` is mostly identical to `InteractionResult#CONSUME`, the only difference is in its usage in [`Item#useOn`][itemuseon]. + - `ItemInteractionResult#CONSUME_PARTIAL` is similar within its usage in `BlockBehaviour#useItemOn`. +- `InteractionResult.FAIL` (or `InteractionResultHolder#fail` / `ItemInteractionResult#FAIL` where needed) should be used if the item functionality should be considered failed and no further interaction should be performed. The pipeline will end. This can be used everywhere, but it should be used with care outside of `Item#useOn` and `Item#use`. In many cases, using `InteractionResult.PASS` makes more sense. +- `InteractionResult.PASS` (or `InteractionResultHolder#pass` where needed) should be used if the operation should be considered neither successful nor failed. The pipeline will continue. This is the default behavior (unless otherwise specified). + - `ItemInteractionResult#PASS_TO_DEFAULT_BLOCK_INTERACTION` allows `BlockBehaviour#useWithoutItem` to be called for the mainhand while `#SKIP_DEFAULT_BLOCK_INTERACTION` prevents the method from executing altogether. `#PASS_TO_DEFAULT_BLOCK_INTERACTION` is the default behavior (unless otherwise specified). + +Some methods have special behavior or requirements, which are explained in the below sections. + +#### `IItemExtension#onItemUseFirst` + +`InteractionResult#sidedSuccess` and `InteractionResult.CONSUME` don't have an effect here. Only `InteractionResult.SUCCESS`, `InteractionResult.FAIL` or `InteractionResult.PASS` should be used here. + +#### `Item#useOn` + +If you want the operation to be considered successful, but you do not want the arm to swing or an `ITEM_USED` stat point to be awarded, use `InteractionResult.CONSUME_PARTIAL`. + +#### `Item#use` + +This is the only instance where the return type is `InteractionResultHolder`. The resulting `ItemStack` in the `InteractionResultHolder` replaces the `ItemStack` the usage was initiated with, if it has changed. + +The default implementation of `Item#use` returns `InteractionResultHolder#consume` when the item is edible and the player can eat the item (because they are hungry, or because the item is always edible), `InteractionResultHolder#fail` when the item is edible but the player cannot eat the item, and `InteractionResultHolder#pass` if the item is not edible. + +Returning `InteractionResultHolder#fail` here while considering the main hand will prevent offhand behavior from running. If you want offhand behavior to run (which you usually want), return `InteractionResultHolder#pass` instead. + +## 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 the player is 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 +[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 +[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 From 461206c7ee9c50527df0b59c9983908f2021460c Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Mon, 28 Oct 2024 14:06:56 +0100 Subject: [PATCH 30/59] living entity docs --- docs/entities/index.md | 2 +- docs/entities/livingentity.md | 128 +++++++++++++++++++++++++++++++++- 2 files changed, 126 insertions(+), 4 deletions(-) diff --git a/docs/entities/index.md b/docs/entities/index.md index ecf2f9ebd..2107ec634 100644 --- a/docs/entities/index.md +++ b/docs/entities/index.md @@ -154,7 +154,7 @@ While not all entities have the concept of hit points, they can still all receiv Damaging an entity is possible by calling `Entity#hurt`. `Entity#hurt` takes 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, it is also possible for entities to modify the behavior in `#hurt` by overriding it. For example, we could make our entity take double damage from fire, and no damage from any other source, like so: +In turn, entities can also modify the behavior in `#hurt` by overriding it. For example, we could make our entity take double damage from fire, and no damage from any other source, like so: ```java @Override diff --git a/docs/entities/livingentity.md b/docs/entities/livingentity.md index d0dad1d88..7485ac1ad 100644 --- a/docs/entities/livingentity.md +++ b/docs/entities/livingentity.md @@ -3,12 +3,134 @@ sidebar_position: 4 --- # Living Entities, Mobs & Players -TODO +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 -TODO +_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 -TODO +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. + +#### `LivingArmorHurtEvent` + +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. + +#### `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. + +## Mob Effects + +_See [Mob Effects & Potions][mobeffects]._ + +## Equipment + +_See [Containers on Entities][containers]._ + +## 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. +::: + +[attributes]: attributes.md +[containers]: ../blockentities/container.md +[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 +[mobeffects]: ../items/mobeffects.md +[priority]: ../concepts/events.md#priority +[spawning]: spawning.md +[tags]: ../resources/server/tags.md From 859d0314598277a1ed46e4aaf393b14986bcc5cf Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Mon, 28 Oct 2024 15:18:17 +0100 Subject: [PATCH 31/59] data and networking article --- docs/entities/data.md | 74 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 2 deletions(-) diff --git a/docs/entities/data.md b/docs/entities/data.md index 694439963..89c0d01c8 100644 --- a/docs/entities/data.md +++ b/docs/entities/data.md @@ -3,10 +3,80 @@ sidebar_position: 2 --- # Data and Networking -TODO +One of the most important use cases of entities is to store data of some sort. 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. + +As such, vanilla introduces a few systems to help with that. These systems generally exist in parallel and can be replaced with one another, this is due to legacy reasons. + +## `SynchedEntityData` + +`SynchedEntityData` is a system used for both storing and syncing values over the network. It is split into three classes: + +- `EntityDataSerializer`s are basically wrappers around a [`StreamCodec`][streamcodec]. + - They are 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 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 + ); +} +``` + +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` + +This method works 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 Network Messages -Of course, you can also always opt to use a custom packet to send information. Please refer to the [Networking articles][networking] for more information. +Of course, 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. +[nbt]: ../datastorage/nbt.md [networking]: ../networking/index.md +[registration]: ../concepts/registries.md#methods-for-registering +[streamcodec]: ../networking/streamcodecs.md From 8963971ffd1d920640da7cd534652c55346d7842 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Tue, 29 Oct 2024 11:30:57 +0100 Subject: [PATCH 32/59] use better TODO markers --- docs/entities/attributes.md | 4 +++- docs/entities/index.md | 8 ++++++-- docs/entities/projectile.md | 4 +++- docs/entities/renderer.md | 4 +++- docs/entities/spawning.md | 4 +++- 5 files changed, 18 insertions(+), 6 deletions(-) diff --git a/docs/entities/attributes.md b/docs/entities/attributes.md index f2ff4649b..af86a3cd5 100644 --- a/docs/entities/attributes.md +++ b/docs/entities/attributes.md @@ -3,4 +3,6 @@ sidebar_position: 5 --- # Attributes -TODO \ No newline at end of file +:::info +This section is a work in progress. +::: diff --git a/docs/entities/index.md b/docs/entities/index.md index 2107ec634..a23bf2569 100644 --- a/docs/entities/index.md +++ b/docs/entities/index.md @@ -82,7 +82,9 @@ Sometimes, there may be generic bounds errors with the entity type and the entit ### `MobCategory` -TODO +:::info +This section is a work in progress. +::: ## The Entity Class @@ -224,7 +226,9 @@ If you want to do the picking (i.e. ray casting) yourself, you can call `Entity# ### Entity Attachments -TODO +:::info +This section is a work in progress. +::: ## Entity Class Hierarchy diff --git a/docs/entities/projectile.md b/docs/entities/projectile.md index 87017b1c1..13429f8fd 100644 --- a/docs/entities/projectile.md +++ b/docs/entities/projectile.md @@ -3,4 +3,6 @@ sidebar_position: 3 --- # Projectiles -TODO \ No newline at end of file +:::info +This section is a work in progress. +::: diff --git a/docs/entities/renderer.md b/docs/entities/renderer.md index 71b581e7e..093e7cf36 100644 --- a/docs/entities/renderer.md +++ b/docs/entities/renderer.md @@ -3,4 +3,6 @@ sidebar_position: 6 --- # Entity Renderers -TODO \ No newline at end of file +:::info +This section is a work in progress. +::: diff --git a/docs/entities/spawning.md b/docs/entities/spawning.md index 9d7c02a03..12483d341 100644 --- a/docs/entities/spawning.md +++ b/docs/entities/spawning.md @@ -3,4 +3,6 @@ sidebar_position: 7 --- # Spawning Entities -TODO \ No newline at end of file +:::info +This section is a work in progress. +::: From d4466af0c9f47ea278282a05103ba4c6d4d954b1 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Wed, 30 Oct 2024 15:19:24 +0100 Subject: [PATCH 33/59] built-in attribute list --- docs/blocks/index.md | 34 +++++++++++++++ docs/entities/attributes.md | 84 +++++++++++++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/docs/blocks/index.md b/docs/blocks/index.md index 7e766d0c4..6ebcad763 100644 --- a/docs/blocks/index.md +++ b/docs/blocks/index.md @@ -269,6 +269,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 as f 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 f = item.getDestroySpeed(); +// If we have an applicable tool, add the minecraft:mining_efficiency attribute as an additive modifier. +if (f > 1) { + f += player.getAttributeValue(Attributes.MINING_EFFICIENCY); +} +// Apply effects from haste, conduit power, and slowness multiplicatively. +if (player.hasEffect(MobEffects.DIG_SPEED)) { f *= ...; } +if (player.hasEffect(MobEffects.CONDUIT_POWER)) { f *= ...; } +if (player.hasEffect(MobEffects.DIG_SLOWDOWN)) { f *= ...; } +// Add the minecraft:block_break_speed attribute as a multiplicative modifier. +f *= player.getAttributeValue(Attributes.BLOCK_BREAK_SPEED); +// If the player is underwater, apply the underwater mining speed penalty multiplicatively. +if (player.isEyeInFluid(FluidTags.WATER)) { + f *= 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()) { + f /= 5; +} +f = /* The PlayerEvent.BreakSpeed event is fired here, allowing modders to further modify this value. */; +return f; +``` + +The exact code for this can be found in `Player#getDigSpeed` 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. @@ -294,6 +326,7 @@ 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 +[attribute]: ../entities/attributes.md [below]: #deferredregisterblocks-helpers [blockentities]: ../blockentities/index.md [blockstates]: states.md @@ -308,5 +341,6 @@ Random ticking is used by a wide range of mechanics in Minecraft, such as plant [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/entities/attributes.md b/docs/entities/attributes.md index af86a3cd5..1fb52fb62 100644 --- a/docs/entities/attributes.md +++ b/docs/entities/attributes.md @@ -3,6 +3,90 @@ sidebar_position: 5 --- # Attributes +Attributes are special properties 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.6 | 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. | +| `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. This is mitigated by some mods that change these values. +::: + +### 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,64]` | 64 | 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`. | + +## Using Attributes + +:::info +This section is a work in progress. +::: + +## Custom Attributes + +:::info +This section is a work in progress. +::: + +## Default Attributes + :::info This section is a work in progress. ::: + +## Attribute Modifiers + +:::info +This section is a work in progress. +::: + +[livingentity]: livingentity.md +[loottables]: ../resources/server/loottables/index.md +[miningspeed]: ../blocks/index.md#mining-speed +[toughness]: https://minecraft.wiki +[wiki]: https://minecraft.wiki From 25cc2c1b574aa1ee6e4f547e8d89e9e8f26832c7 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Wed, 30 Oct 2024 16:57:13 +0100 Subject: [PATCH 34/59] default attributes --- docs/entities/attributes.md | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/docs/entities/attributes.md b/docs/entities/attributes.md index 1fb52fb62..83415f8d8 100644 --- a/docs/entities/attributes.md +++ b/docs/entities/attributes.md @@ -61,32 +61,56 @@ The following attributes are in the `neoforge` namespace, and their in-code valu | `nametag_distance` | `NAMETAG_DISTANCE` | `[0,64]` | 64 | 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`. | -## Using Attributes +## Default Attributes -:::info -This section is a work in progress. +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. ::: -## Custom Attributes +## Querying Attributes :::info This section is a work in progress. ::: -## Default Attributes +## Attribute Modifiers :::info This section is a work in progress. ::: -## Attribute Modifiers +## Custom Attributes :::info This section is a work in progress. ::: +[event]: ../concepts/events.md [livingentity]: livingentity.md [loottables]: ../resources/server/loottables/index.md [miningspeed]: ../blocks/index.md#mining-speed +[spawning]: spawning.md [toughness]: https://minecraft.wiki [wiki]: https://minecraft.wiki From 0bcad78bc6867f7b335c104d077cf3652a6cd757 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Wed, 30 Oct 2024 19:06:12 +0100 Subject: [PATCH 35/59] attribute modifiers --- docs/entities/attributes.md | 88 +++++++++++++++++-- docs/items/interactions.md | 3 +- docs/items/mobeffects.md | 3 +- docs/items/tools.md | 3 +- docs/resources/server/enchantments/builtin.md | 2 +- .../server/loottables/lootfunctions.md | 3 +- 6 files changed, 90 insertions(+), 12 deletions(-) diff --git a/docs/entities/attributes.md b/docs/entities/attributes.md index 83415f8d8..31be707c9 100644 --- a/docs/entities/attributes.md +++ b/docs/entities/attributes.md @@ -91,15 +91,87 @@ Some classes have specialized versions of `LivingEntity#createLivingAttributes`. ## Querying Attributes -:::info -This section is a work in progress. -::: +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.get(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)) { ... } +``` ## Attribute Modifiers -:::info -This section is a work in progress. -::: +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 @@ -107,10 +179,12 @@ This section is a work in progress. This section is a work in progress. ::: +[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 [spawning]: spawning.md -[toughness]: https://minecraft.wiki +[toughness]: https://minecraft.wiki/w/Armor#Armor_toughness [wiki]: https://minecraft.wiki diff --git a/docs/items/interactions.md b/docs/items/interactions.md index 9b34ce7e7..ac8478130 100644 --- a/docs/items/interactions.md +++ b/docs/items/interactions.md @@ -25,7 +25,7 @@ Every frame on the [physical client][physicalside], the `Minecraft` class update - `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 from the main hand item, these are added after the check. + - 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. @@ -122,6 +122,7 @@ Returning `InteractionResultHolder#fail` here while considering the main hand wi - 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 diff --git a/docs/items/mobeffects.md b/docs/items/mobeffects.md index 87cf3cf69..d79d9b390 100644 --- a/docs/items/mobeffects.md +++ b/docs/items/mobeffects.md @@ -65,7 +65,7 @@ public static final Supplier MY_MOB_EFFECT = MOB_EFFECTS.register(" )); ``` -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 Supplier MY_MOB_EFFECT = MOB_EFFECTS.register("my_mob_effect", () -> new MyMobEffect(...) @@ -189,6 +189,7 @@ 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 diff --git a/docs/items/tools.md b/docs/items/tools.md index 97cef82f0..f7135fb8f 100644 --- a/docs/items/tools.md +++ b/docs/items/tools.md @@ -134,7 +134,7 @@ A `HolderSet` can be created from a `TagKey` via `Registry#getOrCreateTag`. Creating a 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) or any tool-like does not need to extend any of the existing `TieredItem`s. 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`. - Overriding `IItemExtension#canPerformAction` to determine what [`ItemAbility`s][itemability] the item can perform. - Calling `IBlockExtension#getToolModifiedState` if you want your item to modify the block state on right click based on the `ItemAbility`s. - Adding your tool to some of the `minecraft:enchantable/*` tags so that your item can have certain enchantments applied to it. @@ -232,6 +232,7 @@ public static final Supplier COPPER_BOOTS = ITEMS.register("copper_bo When creating your armor texture, it is a good idea to work on top of the vanilla armor texture to see which part goes where. +[attributemodifier]: ../entities/attributes.md#attribute-modifiers [block]: ../blocks/index.md [datacomponents]: ./datacomponents.mdx [item]: index.md diff --git a/docs/resources/server/enchantments/builtin.md b/docs/resources/server/enchantments/builtin.md index dcd15a573..b81efefa4 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]: /docs/items/mobeffects/ -[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/lootfunctions.md b/docs/resources/server/loottables/lootfunctions.md index 8b5190564..c516846a6 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 { @@ -845,6 +845,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 From 3f53b818a1488eb6e21462eb2d2de057e04da7e3 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Wed, 30 Oct 2024 19:13:11 +0100 Subject: [PATCH 36/59] custom spawn data --- docs/entities/data.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/entities/data.md b/docs/entities/data.md index 89c0d01c8..d21bf4c55 100644 --- a/docs/entities/data.md +++ b/docs/entities/data.md @@ -72,6 +72,12 @@ protected void addAdditionalSaveData(CompoundTag tag) { } ``` +## 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. + ## Custom Network Messages Of course, 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. From 87938c6a8e8530f6422e2e2155761948b90e205d Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Wed, 30 Oct 2024 19:29:46 +0100 Subject: [PATCH 37/59] custom attributes --- docs/entities/attributes.md | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/docs/entities/attributes.md b/docs/entities/attributes.md index 31be707c9..2a07ab52c 100644 --- a/docs/entities/attributes.md +++ b/docs/entities/attributes.md @@ -3,7 +3,7 @@ sidebar_position: 5 --- # Attributes -Attributes are special properties 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. +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. @@ -175,9 +175,34 @@ double modifierValue = attributes.getModifierValue(Attributes.ARMOR, id); ## Custom Attributes -:::info -This section is a work in progress. -::: +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 @@ -185,6 +210,7 @@ This section is a work in progress. [loottables]: ../resources/server/loottables/index.md [miningspeed]: ../blocks/index.md#mining-speed [mobeffect]: ../items/mobeffects.md +[registry]: ../concepts/registries.md [spawning]: spawning.md [toughness]: https://minecraft.wiki/w/Armor#Armor_toughness [wiki]: https://minecraft.wiki From 54600a41ab11bb155b1e0242b5d73b6a948ccd83 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Wed, 30 Oct 2024 19:29:55 +0100 Subject: [PATCH 38/59] fix broken link to attributes page --- docs/blocks/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/blocks/index.md b/docs/blocks/index.md index 6ebcad763..5009bb42f 100644 --- a/docs/blocks/index.md +++ b/docs/blocks/index.md @@ -326,7 +326,7 @@ 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 -[attribute]: ../entities/attributes.md +[attributes]: ../entities/attributes.md [below]: #deferredregisterblocks-helpers [blockentities]: ../blockentities/index.md [blockstates]: states.md From 930fca62fa31c465a7d3d89020af2199da275b0e Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Wed, 30 Oct 2024 19:54:33 +0100 Subject: [PATCH 39/59] living entity hierarchy --- docs/entities/livingentity.md | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/docs/entities/livingentity.md b/docs/entities/livingentity.md index 7485ac1ad..fbea9e167 100644 --- a/docs/entities/livingentity.md +++ b/docs/entities/livingentity.md @@ -90,6 +90,32 @@ This event is called immediately before the damage is done. The `DamageContainer 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. +## Hierarchy + +Living entities have a complex class hierarchy. As mentioned before, the three direct subclasses are `ArmorStand`, `Mob` and `Player`. Of these, `ArmorStand` has no subclasses, so we will focus on the class hierarchy of `Mob` and `Player`. + +### Hierarchy of `Mob` + +`Mob`'s most important subclass is `PathfinderMob`, which contains (surprise!) logic for pathfinding. `PathfinderMob`'s subclasses are as follows: + +- `AbstractGolem`: The superclass for iron golems, snow golems and (for some reason) shulkers. +- `AgeableMob`: This class has two direct subclasses `AbstractVillager` and `Animal`, both of which should be self-explanatory. These contain most of the aging logic. `Animal` additionally has the abstract `TamableAnimal` subclass that is used for tamable animals such as wolves, cats and parrots; as well as the `AbstractHorse` subclass, which is the superclass of horses, donkeys and mules. +- `Allay`: Allays directly extend `PathfinderMob`. +- `Monster`: The abstract class for everything the game considers monsters. Like `Animal`, this has various abstract subclasses, such as `AbstractPiglin`, `AbstractSkeleton`, `Raider`, and `Zombie`. +- `WaterAnimal`: The superclass for water-based animals, such as fish, squids and dolphins. These are kept separate from the other animals due to significantly different pathfinding. + +Some other classes also extend `Mob` directly. These include `AmbientCreature` with its only subclass `Bat`, `EnderDragon`, `FlyingMob` with its two subclasses `Ghast` and `Phantom` (no, there is no consistency here whatsoever), and `Slime` and its `MagmaCube` subclass. + +### Hierarchy of `Player` + +Depending on which side the player is on, a different player class is used: + +- `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. +- `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. `RemotePlayer`s do not exist in singleplayer contexts. + ## Mob Effects _See [Mob Effects & Potions][mobeffects]._ @@ -130,6 +156,7 @@ This section is a work in progress. [enchantments]: ../resources/server/enchantments/index.md [entities]: index.md [hurt]: index.md#damaging-entities +[logicalsides]: ../concepts/sides.md#the-logical-side [mobeffects]: ../items/mobeffects.md [priority]: ../concepts/events.md#priority [spawning]: spawning.md From e0e802087bc3830b7d9ac74e9becd77816f459c3 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Tue, 5 Nov 2024 18:01:05 +0100 Subject: [PATCH 40/59] spawning --- docs/entities/attributes.md | 2 +- docs/entities/index.md | 8 +++-- docs/entities/livingentity.md | 55 +++++++++++++++++++++++++++++----- docs/entities/spawning.md | 8 ----- docs/worldgen/biomemodifier.md | 2 +- 5 files changed, 54 insertions(+), 21 deletions(-) delete mode 100644 docs/entities/spawning.md diff --git a/docs/entities/attributes.md b/docs/entities/attributes.md index 2a07ab52c..50da3c10c 100644 --- a/docs/entities/attributes.md +++ b/docs/entities/attributes.md @@ -211,6 +211,6 @@ And that's it! Just don't forget to register your `DeferredRegister` to the mod [miningspeed]: ../blocks/index.md#mining-speed [mobeffect]: ../items/mobeffects.md [registry]: ../concepts/registries.md -[spawning]: spawning.md +[spawning]: index.md#spawning-entities [toughness]: https://minecraft.wiki/w/Armor#Armor_toughness [wiki]: https://minecraft.wiki diff --git a/docs/entities/index.md b/docs/entities/index.md index a23bf2569..157dc25cd 100644 --- a/docs/entities/index.md +++ b/docs/entities/index.md @@ -136,7 +136,7 @@ _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` command (assuming `EntityType.Builder#noSummon` was not called). +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: @@ -146,7 +146,7 @@ MyEntity entity = new MyEntity(level, 100.0, 200.0, 300.0); level.addFreshEntity(entity); ``` -For more complex spawn behavior, please refer to the [Spawning article][spawning]. +This will be used for pretty much all non-living entities. Players should obviously not be spawned yourself, and `Mob`s have [their own ways of spawning][mobspawn] (though they can also be added via `#addFreshEntity`). ### Damaging Entities @@ -257,9 +257,11 @@ Several entities are also direct subclasses of `Entity`, simply because there wa [livingentity]: livingentity.md [middleclick]: ../items/interactions.md#middle-clicking [mobeffect]: ../items/mobeffects.md +[mobspawn]: livingentity.md#spawning [particle]: ../resources/client/particles.md [projectile]: projectile.md [registration]: ../concepts/registries.md#methods-for-registering [renderer]: renderer.md -[spawning]: spawning.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 index fbea9e167..3f347ce78 100644 --- a/docs/entities/livingentity.md +++ b/docs/entities/livingentity.md @@ -90,6 +90,14 @@ This event is called immediately before the damage is done. The `DamageContainer 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. +## Mob Effects + +_See [Mob Effects & Potions][mobeffects]._ + +## Equipment + +_See [Containers on Entities][containers]._ + ## Hierarchy Living entities have a complex class hierarchy. As mentioned before, the three direct subclasses are `ArmorStand`, `Mob` and `Player`. Of these, `ArmorStand` has no subclasses, so we will focus on the class hierarchy of `Mob` and `Player`. @@ -111,18 +119,43 @@ Some other classes also extend `Mob` directly. These include `AmbientCreature` w Depending on which side the player is on, a different player class is used: - `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. + - `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. - `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. `RemotePlayer`s do not exist in singleplayer contexts. + - `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. `RemotePlayer`s do not exist in singleplayer contexts. -## Mob Effects +## Spawning -_See [Mob Effects & Potions][mobeffects]._ +In addition to the [regular ways of spawning][spawning], `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. -## Equipment +## Spawn Eggs -_See [Containers on Entities][containers]._ +It is common (though not required) to [register] a spawn egg for mobs. Vanilla uses the `SpawnEggItem` class here, which does some additional setup in the constructor. + +Due to modded registration order, a crash will occur when mods use this class. As a workaround, NeoForge introduces the `DeferredSpawnEggItem` class, which postpones that additional setup to a point where it can be run safely. + +Using `DeferredSpawnEggItem`, our implementation is as simple as this: + +```java +// Assume we have a DeferredRegister.Items called ITEMS +DeferredItem MY_ENTITY_SPAWN_EGG = ITEMS.register("my_entity_spawn_egg", + properties -> new DeferredSpawnEggItem( + // The entity type to spawn, as a Supplier>. + MY_ENTITY_TYPE, + // The colors to use for the spawn egg. The first one is the base/background color, + // the second one is the spots/highlight color. + 0xff0000, + 0x00ff00, + // 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 [model] and [translation] should be added. + +## Natural Spawning + +_See [Worldgen/Biome Modifers/Add Spawns][addspawns] and [Worldgen/Biome Modifers/Add Spawn Costs][addspawncosts]._ ## AI and Navigation @@ -159,5 +192,11 @@ This section is a work in progress. [logicalsides]: ../concepts/sides.md#the-logical-side [mobeffects]: ../items/mobeffects.md [priority]: ../concepts/events.md#priority -[spawning]: spawning.md +[spawning]: index.md#spawning-entities [tags]: ../resources/server/tags.md +[creative]: ../items/index.md#creative-tabs +[model]: ../resources/client/models/index.md +[register]: ../concepts/registries.md +[translation]: ../resources/client/i18n.md +[addspawns]: ../worldgen/biomemodifier.md#add-spawns +[addspawncosts]: ../worldgen/biomemodifier.md#add-spawn-costs diff --git a/docs/entities/spawning.md b/docs/entities/spawning.md deleted file mode 100644 index 12483d341..000000000 --- a/docs/entities/spawning.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -sidebar_position: 7 ---- -# Spawning Entities - -:::info -This section is a work in progress. -::: diff --git a/docs/worldgen/biomemodifier.md b/docs/worldgen/biomemodifier.md index ba9102227..873b45a8e 100644 --- a/docs/worldgen/biomemodifier.md +++ b/docs/worldgen/biomemodifier.md @@ -200,7 +200,7 @@ BUILDER.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, bootstrap -> { -### Add Spawns +### Add Spawns 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. From 631676785aeaf0dd7ea2563b39f39c76e88e7832 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Fri, 15 Nov 2024 14:42:56 +0100 Subject: [PATCH 41/59] projectiles --- docs/entities/attributes.md | 2 +- docs/entities/index.md | 18 ++++++++++++++++-- docs/entities/livingentity.md | 4 ++-- docs/entities/projectile.md | 8 -------- docs/entities/renderer.md | 2 +- 5 files changed, 20 insertions(+), 14 deletions(-) delete mode 100644 docs/entities/projectile.md diff --git a/docs/entities/attributes.md b/docs/entities/attributes.md index 50da3c10c..484873f53 100644 --- a/docs/entities/attributes.md +++ b/docs/entities/attributes.md @@ -1,5 +1,5 @@ --- -sidebar_position: 5 +sidebar_position: 4 --- # Attributes diff --git a/docs/entities/index.md b/docs/entities/index.md index 157dc25cd..1fd38be7c 100644 --- a/docs/entities/index.md +++ b/docs/entities/index.md @@ -236,7 +236,7 @@ Due to the many different types of entities, there is a complex hierarchy of sub Direct subclasses of `Entity` include: -- `Projectile`: The base class for various projectiles, including arrows, fireballs, snowballs, fireworks and similar entities. Read more about them in the [Projectiles article][projectile]. +- `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]. - `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. - `BlockAttachedEntity`: The base class for entities that are immobile and attached to blocks. Includes leash knots, item frames and paintings. @@ -244,6 +244,20 @@ Direct subclasses of `Entity` include: Several entities are also direct subclasses of `Entity`, simply because there was no other fitting superclass. Prominent examples include `ItemEntity` (dropped items), `LightningBolt`, `ExperienceOrb` and `PrimedTnt`. +### 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). + +There are three big subgroups of projectiles: + +- Arrows: Represented by the `AbstractArrow` superclass, this group 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. +- Throwables: Represented by the `ThrowableProjectile` superclass, this group 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. +- Hurting Projectiles: Represented by the `AbstractHurtingProjectile` superclass, this group covers wind charges, fireballs and wither skulls. These are damaging projectiles unaffected by gravity. + +Other projectiles that directly extend `Projectile` include fireworks, fishing bobbers and shulker bullets. + +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 would be `#shoot`, which calculates and sets the correct velocity on the projectile; `#onHit`, `#onHitEntity` and `#onHitBlock`, which do exactly what you'd expect; and `#getOwner` and `#setOwner`, which get and set the owning entity, respectively. + [block]: ../blocks/index.md [damageevents]: livingentity.md#damage-events [damagesource]: ../resources/server/damagetypes.md#creating-and-using-damage-sources @@ -259,7 +273,7 @@ Several entities are also direct subclasses of `Entity`, simply because there wa [mobeffect]: ../items/mobeffects.md [mobspawn]: livingentity.md#spawning [particle]: ../resources/client/particles.md -[projectile]: projectile.md +[projectile]: #projectiles [registration]: ../concepts/registries.md#methods-for-registering [renderer]: renderer.md [spawning]: #spawning-entities diff --git a/docs/entities/livingentity.md b/docs/entities/livingentity.md index 3f347ce78..10f81c0ef 100644 --- a/docs/entities/livingentity.md +++ b/docs/entities/livingentity.md @@ -1,5 +1,5 @@ --- -sidebar_position: 4 +sidebar_position: 3 --- # Living Entities, Mobs & Players @@ -128,7 +128,7 @@ Depending on which side the player is on, a different player class is used: In addition to the [regular ways of spawning][spawning], `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. -## Spawn Eggs +### Spawn Eggs It is common (though not required) to [register] a spawn egg for mobs. Vanilla uses the `SpawnEggItem` class here, which does some additional setup in the constructor. diff --git a/docs/entities/projectile.md b/docs/entities/projectile.md deleted file mode 100644 index 13429f8fd..000000000 --- a/docs/entities/projectile.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -sidebar_position: 3 ---- -# Projectiles - -:::info -This section is a work in progress. -::: diff --git a/docs/entities/renderer.md b/docs/entities/renderer.md index 093e7cf36..6e8f04920 100644 --- a/docs/entities/renderer.md +++ b/docs/entities/renderer.md @@ -1,5 +1,5 @@ --- -sidebar_position: 6 +sidebar_position: 5 --- # Entity Renderers From 74eef1c543fb9e18eb318149748476562025b001 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Sat, 16 Nov 2024 00:15:21 +0100 Subject: [PATCH 42/59] apply 1.21.3 changes to interactions.md, and fix outdated mention of DeferredSpawnEggItem --- docs/entities/livingentity.md | 26 +++++------ docs/items/interactionpipeline.md | 75 ------------------------------- docs/items/interactions.md | 53 +++++++++++----------- 3 files changed, 39 insertions(+), 115 deletions(-) delete mode 100644 docs/items/interactionpipeline.md diff --git a/docs/entities/livingentity.md b/docs/entities/livingentity.md index 10f81c0ef..3c11827aa 100644 --- a/docs/entities/livingentity.md +++ b/docs/entities/livingentity.md @@ -130,18 +130,14 @@ In addition to the [regular ways of spawning][spawning], `Mob`s can also be spaw ### Spawn Eggs -It is common (though not required) to [register] a spawn egg for mobs. Vanilla uses the `SpawnEggItem` class here, which does some additional setup in the constructor. - -Due to modded registration order, a crash will occur when mods use this class. As a workaround, NeoForge introduces the `DeferredSpawnEggItem` class, which postpones that additional setup to a point where it can be run safely. - -Using `DeferredSpawnEggItem`, our implementation is as simple as this: +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.register("my_entity_spawn_egg", - properties -> new DeferredSpawnEggItem( - // The entity type to spawn, as a Supplier>. - MY_ENTITY_TYPE, +DeferredItem MY_ENTITY_SPAWN_EGG = ITEMS.register("my_entity_spawn_egg", + properties -> new SpawnEggItem( + // The entity type to spawn. + MY_ENTITY_TYPE.get(), // The colors to use for the spawn egg. The first one is the base/background color, // the second one is the spots/highlight color. 0xff0000, @@ -153,7 +149,7 @@ DeferredItem MY_ENTITY_SPAWN_EGG = ITEMS.register("my_enti As an item like any other, the item should be added to a [creative tab][creative], and a [model] and [translation] should be added. -## Natural Spawning +### Natural Spawning _See [Worldgen/Biome Modifers/Add Spawns][addspawns] and [Worldgen/Biome Modifers/Add Spawn Costs][addspawncosts]._ @@ -181,8 +177,11 @@ This section is a work in progress. This section is a work in progress. ::: +[addspawncosts]: ../worldgen/biomemodifier.md#add-spawn-costs +[addspawns]: ../worldgen/biomemodifier.md#add-spawns [attributes]: attributes.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 @@ -191,12 +190,9 @@ This section is a work in progress. [hurt]: index.md#damaging-entities [logicalsides]: ../concepts/sides.md#the-logical-side [mobeffects]: ../items/mobeffects.md +[model]: ../resources/client/models/index.md [priority]: ../concepts/events.md#priority +[register]: ../concepts/registries.md [spawning]: index.md#spawning-entities [tags]: ../resources/server/tags.md -[creative]: ../items/index.md#creative-tabs -[model]: ../resources/client/models/index.md -[register]: ../concepts/registries.md [translation]: ../resources/client/i18n.md -[addspawns]: ../worldgen/biomemodifier.md#add-spawns -[addspawncosts]: ../worldgen/biomemodifier.md#add-spawn-costs diff --git a/docs/items/interactionpipeline.md b/docs/items/interactionpipeline.md deleted file mode 100644 index 1c3a31122..000000000 --- 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 index ac8478130..f674d634a 100644 --- a/docs/items/interactions.md +++ b/docs/items/interactions.md @@ -15,7 +15,7 @@ Every frame on the [physical client][physicalside], the `Minecraft` class update ## Left-Clicking an Item -- It is checked that all required feature flags for the [`ItemStack`][itemstack] in your main hand are enabled. If this check fails, the pipeline ends. +- 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: @@ -46,7 +46,7 @@ Every frame on the [physical client][physicalside], the `Minecraft` class update 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 for the [`ItemStack`][itemstack] in your main hand are enabled. If at least one of these checks fails, 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. @@ -62,49 +62,50 @@ During the right-clicking pipeline, a number of methods returning one of two res - `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 `ItemInteractionResult` is `PASS_TO_DEFAULT_BLOCK_INTERACTION` and the executing hand is the main hand, then `Block#useWithoutItem` 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. -### Result Types +### `InteractionResult` -There are three different types of results: `InteractionResult`s, `ItemInteractionResult`s, and `InteractionResultHolder`s. `InteractionResult` is used most of the time, only `Item#use` uses `InteractionResultHolder`, and only `BlockBehaviour#useItemOn` and `CauldronInteraction#interact` use `ItemInteractionResult`. +`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. -`InteractionResult` is an enum consisting of five values: `SUCCESS`, `CONSUME`, `CONSUME_PARTIAL`, `PASS` and `FAIL`. Additionally, the method `InteractionResult#sidedSuccess` is available, which returns `SUCCESS` on the server and `CONSUME` on the client. +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. -`InteractionResultHolder` is a wrapper around `InteractionResult` that adds additional context for `T`. `T` can be anything, but in 99.99 percent of cases, it is an `ItemStack`. `InteractionResultHolder` provides wrapper methods for the enum values (`#success`, `#consume`, `#pass` and `#fail`), as well as `#sidedSuccess`, which calls `#success` on the server and `#consume` on the client. +```java +// In some method that returns an interaction result -`ItemInteractionResult` is a parallel to `InteractionResult` specifically for when an item is used on a block. It is an enum of six values: `SUCCESS`, `CONSUME`, `CONSUME_PARTIAL`, `PASS_TO_DEFAULT_BLOCK_INTERACTION`, `SKIP_DEFAULT_BLOCK_INTERACTION`, and `FAIL`. Each `ItemInteractionResult` can be mapped to a `InteractionResult` via `#result`; `PASS_TO_DEFAULT_BLOCK_INTERACTION`, `SKIP_DEFAULT_BLOCK_INTERACTION` both represent `InteractionResult#PASS`. Similarly, `#sidedSucess` also exists for `ItemInteractionResult`. +// Item in hand will turn into an apple +return InteractionResult.SUCCESS.heldItemTransformedTo(new ItemStack(Items.APPLE)); +``` -Generally, the different values mean the following: +:::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. +::: -- `InteractionResult#sidedSuccess` (or `InteractionResultHolder#sidedSuccess` / `ItemInteractionResult#sidedSucess` where needed) should be used if the operation should be considered successful, and you want the arm to swing. The pipeline will end. -- `InteractionResult#SUCCESS` (or `InteractionResultHolder#success` / `ItemInteractionResult#SUCCESS` where needed) should be used if the operation should be considered successful, and you want the arm to swing, but only on one side. Only use this if you want to return a different value on the other logical side for whatever reason. The pipeline will end. -- `InteractionResult#CONSUME` (or `InteractionResultHolder#consume` / `ItemInteractionResult#CONSUME` where needed) should be used if the operation should be considered successful, but you do not want the arm to swing. The pipeline will end. -- `InteractionResult#CONSUME_PARTIAL` is mostly identical to `InteractionResult#CONSUME`, the only difference is in its usage in [`Item#useOn`][itemuseon]. - - `ItemInteractionResult#CONSUME_PARTIAL` is similar within its usage in `BlockBehaviour#useItemOn`. -- `InteractionResult.FAIL` (or `InteractionResultHolder#fail` / `ItemInteractionResult#FAIL` where needed) should be used if the item functionality should be considered failed and no further interaction should be performed. The pipeline will end. This can be used everywhere, but it should be used with care outside of `Item#useOn` and `Item#use`. In many cases, using `InteractionResult.PASS` makes more sense. -- `InteractionResult.PASS` (or `InteractionResultHolder#pass` where needed) should be used if the operation should be considered neither successful nor failed. The pipeline will continue. This is the default behavior (unless otherwise specified). - - `ItemInteractionResult#PASS_TO_DEFAULT_BLOCK_INTERACTION` allows `BlockBehaviour#useWithoutItem` to be called for the mainhand while `#SKIP_DEFAULT_BLOCK_INTERACTION` prevents the method from executing altogether. `#PASS_TO_DEFAULT_BLOCK_INTERACTION` is the default behavior (unless otherwise specified). +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. -Some methods have special behavior or requirements, which are explained in the below sections. +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. -#### `IItemExtension#onItemUseFirst` - -`InteractionResult#sidedSuccess` and `InteractionResult.CONSUME` don't have an effect here. Only `InteractionResult.SUCCESS`, `InteractionResult.FAIL` or `InteractionResult.PASS` should be used here. +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_PARTIAL`. +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 return type is `InteractionResultHolder`. The resulting `ItemStack` in the `InteractionResultHolder` replaces the `ItemStack` the usage was initiated with, if it has changed. +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 `InteractionResultHolder#consume` when the item is edible and the player can eat the item (because they are hungry, or because the item is always edible), `InteractionResultHolder#fail` when the item is edible but the player cannot eat the item, and `InteractionResultHolder#pass` if the item is not edible. +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 `InteractionResultHolder#fail` here while considering the main hand will prevent offhand behavior from running. If you want offhand behavior to run (which you usually want), return `InteractionResultHolder#pass` instead. +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 @@ -131,9 +132,11 @@ Returning `InteractionResultHolder#fail` here while considering the main hand wi [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 From 7c12eff934af9ac2de96ea7749abffa14df6b879 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Tue, 19 Nov 2024 15:48:33 +0100 Subject: [PATCH 43/59] consistently apply code and list formatting --- docs/entities/attributes.md | 2 +- docs/entities/data.md | 12 ++--- docs/entities/index.md | 94 +++++++++++++++++------------------ docs/entities/livingentity.md | 16 +++--- 4 files changed, 62 insertions(+), 62 deletions(-) diff --git a/docs/entities/attributes.md b/docs/entities/attributes.md index 484873f53..16ecfdeb5 100644 --- a/docs/entities/attributes.md +++ b/docs/entities/attributes.md @@ -179,7 +179,7 @@ If needed, you can also add your own attributes. Like many other systems, attrib ```java public static final DeferredRegister ATTRIBUTES = DeferredRegister.create( - BuiltInRegistries.ATTRIBUTE, "yourmodid"); + BuiltInRegistries.ATTRIBUTE, "yourmodid"); ``` For the attributes themselves, there are three classes you can choose from: diff --git a/docs/entities/data.md b/docs/entities/data.md index d21bf4c55..dc6c6b082 100644 --- a/docs/entities/data.md +++ b/docs/entities/data.md @@ -25,12 +25,12 @@ To get started, create an `EntityDataAccessor` in your entity class: 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 - ); + SynchedEntityData.defineId( + // The class of the entity. + MyEntity.class, + // The entity data accessor type. + EntityDataSerializers.INT + ); } ``` diff --git a/docs/entities/index.md b/docs/entities/index.md index 1fd38be7c..8bf364681 100644 --- a/docs/entities/index.md +++ b/docs/entities/index.md @@ -23,52 +23,52 @@ Let's create our `EntityType` registry and register an `EntityType` for it, assu ```java public static final DeferredRegister> ENTITY_TYPES = - DeferredRegister.create(Registries.ENTITY_TYPE, ExampleMod.MOD_ID); + 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. - .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 is a string used for datafixing; mods generally - // do not utilize this and can safely pass null here instead. - .build(null) + "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. + .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 is a string used for datafixing; mods generally + // do not utilize this and can safely pass null here instead. + .build(null) ); ``` @@ -177,11 +177,11 @@ It is also possible to modify damage done to entities that do not belong to you, 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. + - 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. - - By default, `Entity#tick` will forward to this method. + - 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. + - 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: diff --git a/docs/entities/livingentity.md b/docs/entities/livingentity.md index 3c11827aa..043b92566 100644 --- a/docs/entities/livingentity.md +++ b/docs/entities/livingentity.md @@ -60,11 +60,11 @@ public static void decreaseArmor(LivingIncomingDamageEvent event) { 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 + // 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 ); } } @@ -119,10 +119,10 @@ Some other classes also extend `Mob` directly. These include `AmbientCreature` w Depending on which side the player is on, a different player class is used: - `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. + - `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. - `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. `RemotePlayer`s do not exist in singleplayer contexts. + - `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. `RemotePlayer`s do not exist in singleplayer contexts. ## Spawning From 84d7574427df07ef960f16a6d439de016dd285a9 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Wed, 20 Nov 2024 10:30:28 +0100 Subject: [PATCH 44/59] mobcategory --- docs/entities/index.md | 24 +++++++++++++++++++++++- docs/entities/livingentity.md | 11 ++++++++++- docs/worldgen/biomemodifier.md | 9 ++++++--- 3 files changed, 39 insertions(+), 5 deletions(-) diff --git a/docs/entities/index.md b/docs/entities/index.md index 8bf364681..e5f7cc9bb 100644 --- a/docs/entities/index.md +++ b/docs/entities/index.md @@ -82,8 +82,29 @@ Sometimes, there may be generic bounds errors with the entity type and the entit ### `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 -This section is a work in progress. +`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 @@ -263,6 +284,7 @@ A new projectile can be created by extending `Projectile` or a fitting subclass, [damagesource]: ../resources/server/damagetypes.md#creating-and-using-damage-sources [data]: data.md [entity]: #the-entity-class +[extenum]: ../advanced/extensibleenums.md [hierarchy]: #entity-class-hierarchy [hitresult]: ../items/interactions.md#hitresults [item]: ../items/index.md diff --git a/docs/entities/livingentity.md b/docs/entities/livingentity.md index 043b92566..2db7c333b 100644 --- a/docs/entities/livingentity.md +++ b/docs/entities/livingentity.md @@ -151,7 +151,13 @@ As an item like any other, the item should be added to a [creative tab][creative ### Natural Spawning -_See [Worldgen/Biome Modifers/Add Spawns][addspawns] and [Worldgen/Biome Modifers/Add Spawn Costs][addspawncosts]._ +_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 @@ -189,10 +195,13 @@ This section is a work in progress. [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/worldgen/biomemodifier.md b/docs/worldgen/biomemodifier.md index 45ad1f257..870fbd210 100644 --- a/docs/worldgen/biomemodifier.md +++ b/docs/worldgen/biomemodifier.md @@ -204,6 +204,8 @@ BUILDER.add(NeoForgeRegistries.Keys.BIOME_MODIFIERS, bootstrap -> { ### 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. :::note @@ -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 From 551bac0527da88c88585929e1a5eca99ffa82269 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Wed, 20 Nov 2024 10:33:48 +0100 Subject: [PATCH 45/59] link to and from the data attachments article --- docs/datastorage/attachments.md | 5 +++-- docs/entities/data.md | 5 +++++ docs/entities/index.md | 3 +++ 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/docs/datastorage/attachments.md b/docs/datastorage/attachments.md index 13562c28f..db8d5916e 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/entities/data.md b/docs/entities/data.md index dc6c6b082..74d4f03a0 100644 --- a/docs/entities/data.md +++ b/docs/entities/data.md @@ -78,10 +78,15 @@ In some cases, there is custom data needed for your entity on the client when it 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]. Please see the linked article for more information. + ## Custom Network Messages Of course, 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 [nbt]: ../datastorage/nbt.md [networking]: ../networking/index.md [registration]: ../concepts/registries.md#methods-for-registering diff --git a/docs/entities/index.md b/docs/entities/index.md index e5f7cc9bb..ff5a936da 100644 --- a/docs/entities/index.md +++ b/docs/entities/index.md @@ -247,6 +247,8 @@ If you want to do the picking (i.e. ray casting) yourself, you can call `Entity# ### Entity Attachments +_Not to be confused with [Data Attachments][dataattachments]._ + :::info This section is a work in progress. ::: @@ -283,6 +285,7 @@ A new projectile can be created by extending `Projectile` or a fitting subclass, [damageevents]: livingentity.md#damage-events [damagesource]: ../resources/server/damagetypes.md#creating-and-using-damage-sources [data]: data.md +[dataattachments]: ../datastorage/attachments.md [entity]: #the-entity-class [extenum]: ../advanced/extensibleenums.md [hierarchy]: #entity-class-hierarchy From ca9bc3303a0babe98e8405fe1ade93a5c9ab70a6 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Wed, 20 Nov 2024 10:44:43 +0100 Subject: [PATCH 46/59] add links to the entity articles --- docs/blockentities/container.md | 3 ++- docs/blockentities/index.md | 3 ++- docs/datastorage/nbt.md | 3 ++- docs/items/armor.md | 5 +++-- docs/items/consumables.md | 3 ++- docs/items/mobeffects.md | 3 ++- docs/resources/client/i18n.md | 3 ++- docs/resources/client/models/bakedmodel.md | 6 ++++-- docs/resources/server/advancements.md | 3 ++- docs/resources/server/conditions.md | 3 ++- docs/resources/server/damagetypes.md | 3 ++- docs/resources/server/loottables/custom.md | 3 ++- docs/resources/server/loottables/index.md | 4 +++- 13 files changed, 30 insertions(+), 15 deletions(-) diff --git a/docs/blockentities/container.md b/docs/blockentities/container.md index 4b07dda74..4caf439d0 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 d8c0c2ca2..e30f07a0c 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/datastorage/nbt.md b/docs/datastorage/nbt.md index 4532cf06f..cd863bceb 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/items/armor.md b/docs/items/armor.md index 86204da70..bbc0761a9 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 @@ -416,8 +416,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 7c22b1eac..0b79c352b 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/mobeffects.md b/docs/items/mobeffects.md index b53536bba..6d4bc575f 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 @@ -201,5 +201,6 @@ public static void registerBrewingRecipes(RegisterBrewingRecipesEvent event) { [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/resources/client/i18n.md b/docs/resources/client/i18n.md index c66292df8..fac6a8a09 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 99a9089be..f3f3d3223 100644 --- a/docs/resources/client/models/bakedmodel.md +++ b/docs/resources/client/models/bakedmodel.md @@ -16,7 +16,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` @@ -69,7 +69,7 @@ Minecraft's render engine recognizes a total of 8 perspective types (9 if you in - An `ItemStack`: The item stack being rendered. - A `ClientLevel`: The level the model is being rendered in. This should only be used for querying the level, not mutating it in any way. May be null. -- A `LivingEntity`: The entity the model is rendered on. May be null, e.g. when rendering from a [block entity renderer][ber]. +- A `LivingEntity`: The [living entity][livingentity] the model is rendered on. May be null, e.g. when rendering from a [block entity renderer][ber]. - An `int`: A seed for randomizing. `BakedOverrides` also hold the model's override options as `BakedOverride`s. An object of `BakedOverride` is an in-code representation of a model's [`overrides`][overrides] block. It can be used by baked models to return different models depending on its contents. A list of all `BakedOverride`s of an `BakedOverrides` instance can be retrieved through `BakedOverrides#getOverrides()`. @@ -124,9 +124,11 @@ It is generally encouraged to use a [custom model loader][modelloader] over wrap [ber]: ../../../blockentities/ber.md [bewlr]: ../../../blockentities/ber.md#blockentitywithoutlevelrenderer [blockstate]: ../../../blocks/states.md +[entityrenderer]: ../../../entities/renderer.md [event]: ../../../concepts/events.md [bakedoverrides]: #bakedoverrides [itemstack]: ../../../items/index.md#itemstacks +[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 3ca5143fe..fe1bcc892 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. @@ -282,6 +282,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/conditions.md b/docs/resources/server/conditions.md index bc00f36be..9cd5f6ac0 100644 --- a/docs/resources/server/conditions.md +++ b/docs/resources/server/conditions.md @@ -136,7 +136,7 @@ This condition returns true if the given item [tag] is empty, and false otherwis Custom conditions can be created by implementing `ICondition` and its `#test(IContext)` method, as well as creating a [map codec][codec] for it. The `IContext` parameter in `#test` has access to some parts of the game state. Currently, this only allows you to query tags from registries. Some objects with conditions may be loaded earlier than tags, in which case the context will be `IContext.EMPTY` and not contain any tag information at all. -For example, let's assume we want to reimplement the `tag_empty` condition, but for entity type tags instead of item tags, then our condition would look something like this: +For example, let's assume we want to reimplement the `tag_empty` condition, but for [entity type][entitytype] tags instead of item tags, then our condition would look something like this: ```java // This class is basically a boiled-down copy of TagEmptyCondition, adjusted for entity types instead of items. @@ -195,6 +195,7 @@ For the conditions themselves, the `IConditionBuilder` interface provides static [codec]: ../../datastorage/codecs [datagen]: ../index.md#data-generation [datamapprovider]: datamaps/index.md#data-generation +[entitytype]: ../../entities/index.md#entitytype [glmprovider]: loottables/glm.md#datagen [loottable]: loottables/index.md [recipeprovider]: recipes/index.md#data-generation diff --git a/docs/resources/server/damagetypes.md b/docs/resources/server/damagetypes.md index 19c6d8c7c..4b00de662 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. @@ -129,6 +129,7 @@ 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 diff --git a/docs/resources/server/loottables/custom.md b/docs/resources/server/loottables/custom.md index 3a42417c1..fd904331c 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 1419b2c78..fe123766c 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 From 79bb3132b4d2f3bb31d4d86977b656cf838bd67b Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Wed, 20 Nov 2024 16:00:37 +0100 Subject: [PATCH 47/59] escape characters --- docs/entities/livingentity.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/entities/livingentity.md b/docs/entities/livingentity.md index 2db7c333b..5eea2f7de 100644 --- a/docs/entities/livingentity.md +++ b/docs/entities/livingentity.md @@ -153,9 +153,9 @@ As an item like any other, the item should be added to a [creative tab][creative _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. +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. +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. From 44951b90330d841672310503dde70c402632f285 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Wed, 20 Nov 2024 16:45:24 +0100 Subject: [PATCH 48/59] entity attachments --- docs/entities/index.md | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/docs/entities/index.md b/docs/entities/index.md index ff5a936da..8d5a29716 100644 --- a/docs/entities/index.md +++ b/docs/entities/index.md @@ -249,10 +249,42 @@ If you want to do the picking (i.e. ray casting) yourself, you can call `Entity# _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. + +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 -This section is a work in progress. +`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. ::: +`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 EntityAttachments will make name tags float half a block above the top end of the 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. From 29865146d99c3f76d7a9c3c7be30b658d97e0d49 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Thu, 21 Nov 2024 13:44:16 +0100 Subject: [PATCH 49/59] apply feedback, part 1 --- docs/blocks/index.md | 70 +++++++++++++++++------------------ docs/entities/attributes.md | 12 +++++- docs/entities/index.md | 24 +++++++++--- docs/entities/livingentity.md | 6 ++- docs/items/interactions.md | 3 +- 5 files changed, 70 insertions(+), 45 deletions(-) diff --git a/docs/blocks/index.md b/docs/blocks/index.md index 6c86e23e0..c3750b3e3 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. ); ``` @@ -282,32 +280,32 @@ The following subsections further break down these stages into actual method cal #### Mining Speed -The mining speed is calculated as f from the block's hardness, the used [tool]'s speed, and several entity [attributes] according to the following rules. +The mining speed is calculated as f 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 f = item.getDestroySpeed(); +float destroySpeed = item.getDestroySpeed(); // If we have an applicable tool, add the minecraft:mining_efficiency attribute as an additive modifier. -if (f > 1) { - f += player.getAttributeValue(Attributes.MINING_EFFICIENCY); +if (destroySpeed > 1) { + destroySpeed += player.getAttributeValue(Attributes.MINING_EFFICIENCY); } // Apply effects from haste, conduit power, and slowness multiplicatively. -if (player.hasEffect(MobEffects.DIG_SPEED)) { f *= ...; } -if (player.hasEffect(MobEffects.CONDUIT_POWER)) { f *= ...; } -if (player.hasEffect(MobEffects.DIG_SLOWDOWN)) { f *= ...; } +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. -f *= player.getAttributeValue(Attributes.BLOCK_BREAK_SPEED); +destroySpeed *= player.getAttributeValue(Attributes.BLOCK_BREAK_SPEED); // If the player is underwater, apply the underwater mining speed penalty multiplicatively. if (player.isEyeInFluid(FluidTags.WATER)) { - f *= player.getAttributeValue(Attributes.SUBMERGED_MINING_SPEED); + 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()) { - f /= 5; + destroySpeed /= 5; } -f = /* The PlayerEvent.BreakSpeed event is fired here, allowing modders to further modify this value. */; -return f; +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#getDigSpeed` for reference. diff --git a/docs/entities/attributes.md b/docs/entities/attributes.md index 16ecfdeb5..b2b282e17 100644 --- a/docs/entities/attributes.md +++ b/docs/entities/attributes.md @@ -99,13 +99,21 @@ The `AttributeMap` of an entity can be retrieved by calling `LivingEntity#getAtt // 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.get(Attributes.ARMOR); +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. @@ -191,7 +199,7 @@ For the attributes themselves, there are three classes you can choose from: 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( +public static final Holder MY_ATTRIBUTE = ATTRIBUTES.register("my_attribute", () -> new RangedAttribute( // The translation key to use. "attributes.yourmodid.my_attribute", // The default value. diff --git a/docs/entities/index.md b/docs/entities/index.md index 8d5a29716..8f86a8af5 100644 --- a/docs/entities/index.md +++ b/docs/entities/index.md @@ -62,6 +62,8 @@ public static final Supplier> MY_ENTITY = ENTITY_TYPES.regi .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. @@ -162,9 +164,11 @@ If we now boot up the game now and enter a world, we have exactly one way of spa 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 -MyEntity entity = new MyEntity(level, 100.0, 200.0, 300.0); -level.addFreshEntity(entity); +// 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, and `Mob`s have [their own ways of spawning][mobspawn] (though they can also be added via `#addFreshEntity`). @@ -233,7 +237,7 @@ public ItemStack getPickResult() { } ``` -Your entity can also be disabled from picking entirely like so: +Your entity can also be disabled from picking entirely. The primary use case for this would be multipart entities, such as the ender dragon, where the parent entity has picking disabled, but the parts have it enabled again, for finer hitbox tuning. Picking can be disabled like so: ```java @Override @@ -266,6 +270,8 @@ Vanilla defines the following four `EntityAttachment`s: `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. @@ -311,7 +317,15 @@ There are three big subgroups of projectiles: Other projectiles that directly extend `Projectile` include fireworks, fishing bobbers and shulker bullets. -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 would be `#shoot`, which calculates and sets the correct velocity on the projectile; `#onHit`, `#onHitEntity` and `#onHitBlock`, which do exactly what you'd expect; and `#getOwner` and `#setOwner`, which get and set the owning entity, respectively. +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 diff --git a/docs/entities/livingentity.md b/docs/entities/livingentity.md index 5eea2f7de..21977ed88 100644 --- a/docs/entities/livingentity.md +++ b/docs/entities/livingentity.md @@ -86,10 +86,14 @@ This event should be pretty self-explanatory. It is fired when armor damage from 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]._ @@ -134,7 +138,7 @@ It is common (though not required) to [register] a spawn egg for mobs. This is d ```java // Assume we have a DeferredRegister.Items called ITEMS -DeferredItem MY_ENTITY_SPAWN_EGG = ITEMS.register("my_entity_spawn_egg", +DeferredItem MY_ENTITY_SPAWN_EGG = ITEMS.registerItem("my_entity_spawn_egg", properties -> new SpawnEggItem( // The entity type to spawn. MY_ENTITY_TYPE.get(), diff --git a/docs/items/interactions.md b/docs/items/interactions.md index f674d634a..bca115386 100644 --- a/docs/items/interactions.md +++ b/docs/items/interactions.md @@ -113,7 +113,8 @@ Returning `InteractionResult#FAIL` here while considering the main hand will pre - `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 the player is not in creative, the pipeline ends. + - 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: From e7ca864cffed7d66e47e69537c08dd09239cd359 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Thu, 21 Nov 2024 13:45:56 +0100 Subject: [PATCH 50/59] fix wrong name for ArmorHurtEvent --- docs/entities/livingentity.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/entities/livingentity.md b/docs/entities/livingentity.md index 21977ed88..89ba886b5 100644 --- a/docs/entities/livingentity.md +++ b/docs/entities/livingentity.md @@ -78,7 +78,7 @@ This event can be used to fully customize shield blocking. This includes introdu Note that this event is not designed for immunities or attack cancellations that are outside the scope of "shield-like" items. -#### `LivingArmorHurtEvent` +#### `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. From 79078a2382dc95763d60e7b8866b0ae618a2488d Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Tue, 26 Nov 2024 13:09:52 +0100 Subject: [PATCH 51/59] entity renderers, pt 1 --- docs/entities/renderer.md | 61 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/docs/entities/renderer.md b/docs/entities/renderer.md index 6e8f04920..424011991 100644 --- a/docs/entities/renderer.md +++ b/docs/entities/renderer.md @@ -3,6 +3,67 @@ 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]. + +## 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. +public class MyEntityRenderer extends EntityRenderer { + // Define a constant field for our texture, to use below. The ResourceLocation will be interpreted + // as relative to your assets namespace, i.e. assets/, and must end with .png. + private static final ResourceLocation TEXTURE = + ResourceLocation.fromNamespaceAndPath("examplemod", "textures/entity/my_entity.png"); + + // In our constructor, we just forward to super. + public MyEntityRenderer(EntityRendererProvider.Context context) { + super(context); + } + + // Return the texture resource location here. The parameter type matches the generic type we pass to + // the superclass. More elaborate checks may be performed here, but should probably be cached. + public ResourceLocation getTextureLocation(Entity entity) { + return TEXTURE; + } + + // Actually render the entity. The first parameter matches the generic type we pass to the superclass. + // Calling super will handle leash and name tag rendering for you, if applicable. + public void render(Entity entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) { + super.render(entity, 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); +} +``` + +## Hierarchy + +:::info +This section is a work in progress. +::: + +## Entity Models + +:::info +This section is a work in progress. +::: + +## Animations + :::info This section is a work in progress. ::: + +[events]: ../concepts/events.md +[sides]: ../concepts/sides.md From 7b35abed7ed97e94666548074d7615879885f04a Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Tue, 26 Nov 2024 16:03:02 +0100 Subject: [PATCH 52/59] renderer hierarchy and 1.21.3 update --- docs/entities/renderer.md | 57 ++++++++++++++++++++++++++++----------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/docs/entities/renderer.md b/docs/entities/renderer.md index 424011991..69da5fd49 100644 --- a/docs/entities/renderer.md +++ b/docs/entities/renderer.md @@ -5,6 +5,8 @@ sidebar_position: 5 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 to avoid common issues when the entity renderer accidentally mutates the entity's fields. + ## Creating an Entity Renderer The simplest entity renderer is one that directly extends `EntityRenderer`: @@ -12,27 +14,30 @@ 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. -public class MyEntityRenderer extends EntityRenderer { - // Define a constant field for our texture, to use below. The ResourceLocation will be interpreted - // as relative to your assets namespace, i.e. assets/, and must end with .png. - private static final ResourceLocation TEXTURE = - ResourceLocation.fromNamespaceAndPath("examplemod", "textures/entity/my_entity.png"); - +// 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); } - // Return the texture resource location here. The parameter type matches the generic type we pass to - // the superclass. More elaborate checks may be performed here, but should probably be cached. - public ResourceLocation getTextureLocation(Entity entity) { - return TEXTURE; + // Tell the render engine how to create a new entity render state. + public EntityRenderState createRenderState() { + return new EntityRenderState(); } - // Actually render the entity. The first parameter matches the generic type we pass to the superclass. + // 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(Entity entity, float entityYaw, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) { - super.render(entity, entityYaw, partialTick, poseStack, bufferSource, packedLight); + public void render(EntityRenderState state, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight) { + super.render(state, entityYaw, partialTick, poseStack, bufferSource, packedLight); // do your own rendering here } } @@ -47,11 +52,30 @@ public static void registerEntityRenderers(EntityRenderersEvent.RegisterRenderer } ``` +## 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 -:::info -This section is a work in progress. -::: +Like with entities themselves, entity renderers also have a class hierarchy, though not as layered. It basically boils down to: + +- `EntityRenderer`: The abstract base class. Many entities, notably almost all non-living ones, extend this class directly. + - `ArrowRenderer`, `AbstractBoatRenderer`, `AbstractMinecartRenderer`: These exist mainly for convenience, and are used as parents for more specific renderers. `ArrowRenderer` is also used directly by the regular arrow entity. + - `LivingRenderer`: The abstract base class for renderers for [living entities][livingentity]. Direct subclasses include `ArmorStandRenderer` and `PlayerRenderer`. + - `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, `LivingRenderer` has type bounds for `LivingEntity` and `LivingEntityRenderState`. ## Entity Models @@ -66,4 +90,5 @@ This section is a work in progress. ::: [events]: ../concepts/events.md +[livingentity]: livingentity.md [sides]: ../concepts/sides.md From 110be93458e309097f2e306a80d07cf661dee63d Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Fri, 29 Nov 2024 11:38:25 +0100 Subject: [PATCH 53/59] entity layers, part 1 --- docs/entities/renderer.md | 85 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 84 insertions(+), 1 deletion(-) diff --git a/docs/entities/renderer.md b/docs/entities/renderer.md index 69da5fd49..440c6d9ba 100644 --- a/docs/entities/renderer.md +++ b/docs/entities/renderer.md @@ -77,7 +77,89 @@ Like with entities themselves, entity renderers also have a class hierarchy, tho 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, `LivingRenderer` has type bounds for `LivingEntity` and `LivingEntityRenderState`. -## Entity Models +## Entity Models and Layer Definitions + +Many renderers, especially the `LivingRenderer` 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. + "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 :::info This section is a work in progress. @@ -89,6 +171,7 @@ This section is a work in progress. This section is a work in progress. ::: +[blockbench]: https://www.blockbench.net/ [events]: ../concepts/events.md [livingentity]: livingentity.md [sides]: ../concepts/sides.md From 56eef02b2731b2b8cf6fb186f24a909215515904 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Sun, 1 Dec 2024 22:38:10 +0100 Subject: [PATCH 54/59] entity layers, part 2 --- docs/entities/renderer.md | 37 ++++++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/docs/entities/renderer.md b/docs/entities/renderer.md index 440c6d9ba..a0868d82a 100644 --- a/docs/entities/renderer.md +++ b/docs/entities/renderer.md @@ -161,9 +161,40 @@ public static void registerLayerDefinitions(EntityRenderersEvent.RegisterLayerDe ### Adding a Layer Definition to an Entity -:::info -This section is a work in progress. -::: +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 From 362280b04299feff28a2391aa3af3c7fc67e478a Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Mon, 2 Dec 2024 00:47:08 +0100 Subject: [PATCH 55/59] use mermaid diagrams --- docs/entities/index.md | 99 +++++++++++++++++++++++++++++++---- docs/entities/livingentity.md | 89 ++++++++++++++++++++++++++----- docs/entities/renderer.md | 36 +++++++++---- 3 files changed, 191 insertions(+), 33 deletions(-) diff --git a/docs/entities/index.md b/docs/entities/index.md index 8f86a8af5..d5779b057 100644 --- a/docs/entities/index.md +++ b/docs/entities/index.md @@ -295,27 +295,106 @@ EntityType.Builder.of(...) 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. -Direct subclasses of `Entity` include: +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]. -- `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. -- `BlockAttachedEntity`: The base class for entities that are immobile and attached to blocks. Includes leash knots, item frames and paintings. -- `Display`: The base class for the various map-maker display entities. +- `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: -Several entities are also direct subclasses of `Entity`, simply because there was no other fitting superclass. Prominent examples include `ItemEntity` (dropped items), `LightningBolt`, `ExperienceOrb` and `PrimedTnt`. +- `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). -There are three big subgroups of projectiles: +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; +``` -- Arrows: Represented by the `AbstractArrow` superclass, this group 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. -- Throwables: Represented by the `ThrowableProjectile` superclass, this group 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. -- Hurting Projectiles: Represented by the `AbstractHurtingProjectile` superclass, this group covers wind charges, fireballs and wither skulls. These are damaging projectiles unaffected by gravity. +Of note are the three direct abstract subclasses of `Projectile`: -Other projectiles that directly extend `Projectile` include fireworks, fishing bobbers and shulker bullets. +- `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: diff --git a/docs/entities/livingentity.md b/docs/entities/livingentity.md index 89ba886b5..8e53976ff 100644 --- a/docs/entities/livingentity.md +++ b/docs/entities/livingentity.md @@ -104,33 +104,94 @@ _See [Containers on Entities][containers]._ ## Hierarchy -Living entities have a complex class hierarchy. As mentioned before, the three direct subclasses are `ArmorStand`, `Mob` and `Player`. Of these, `ArmorStand` has no subclasses, so we will focus on the class hierarchy of `Mob` and `Player`. +Living entities have a complex class hierarchy. As mentioned before, there are three direct subclasses: + +```mermaid +graph LR; + LivingEntity-->ArmorStand; + LivingEntity-->Mob; + LivingEntity-->Player; +``` + +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` -`Mob`'s most important subclass is `PathfinderMob`, which contains (surprise!) logic for pathfinding. `PathfinderMob`'s subclasses are as follows: +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`. -- `AbstractGolem`: The superclass for iron golems, snow golems and (for some reason) shulkers. -- `AgeableMob`: This class has two direct subclasses `AbstractVillager` and `Animal`, both of which should be self-explanatory. These contain most of the aging logic. `Animal` additionally has the abstract `TamableAnimal` subclass that is used for tamable animals such as wolves, cats and parrots; as well as the `AbstractHorse` subclass, which is the superclass of horses, donkeys and mules. -- `Allay`: Allays directly extend `PathfinderMob`. -- `Monster`: The abstract class for everything the game considers monsters. Like `Animal`, this has various abstract subclasses, such as `AbstractPiglin`, `AbstractSkeleton`, `Raider`, and `Zombie`. -- `WaterAnimal`: The superclass for water-based animals, such as fish, squids and dolphins. These are kept separate from the other animals due to significantly different pathfinding. +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. -Some other classes also extend `Mob` directly. These include `AmbientCreature` with its only subclass `Bat`, `EnderDragon`, `FlyingMob` with its two subclasses `Ghast` and `Phantom` (no, there is no consistency here whatsoever), and `Slime` and its `MagmaCube` subclass. +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: +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; +``` -- `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. - `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. `RemotePlayer`s do not exist in singleplayer contexts. +- `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], `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. +In addition to the [regular ways of spawning][spawning], `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 diff --git a/docs/entities/renderer.md b/docs/entities/renderer.md index a0868d82a..04717378d 100644 --- a/docs/entities/renderer.md +++ b/docs/entities/renderer.md @@ -66,20 +66,38 @@ That's literally it. Extend the class, add your field, and off you go. The only ## Hierarchy -Like with entities themselves, entity renderers also have a class hierarchy, though not as layered. It basically boils down to: +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 entities, notably almost all non-living ones, extend this class directly. - - `ArrowRenderer`, `AbstractBoatRenderer`, `AbstractMinecartRenderer`: These exist mainly for convenience, and are used as parents for more specific renderers. `ArrowRenderer` is also used directly by the regular arrow entity. - - `LivingRenderer`: The abstract base class for renderers for [living entities][livingentity]. Direct subclasses include `ArmorStandRenderer` and `PlayerRenderer`. - - `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. +- `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, `LivingRenderer` has type bounds for `LivingEntity` and `LivingEntityRenderState`. +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 `LivingRenderer` 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. +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. From a1746fef90e60a1969ca5aec4f42bcf3b2c8cd09 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Mon, 2 Dec 2024 01:01:41 +0100 Subject: [PATCH 56/59] address the leftover comments --- docs/entities/data.md | 3 ++- docs/entities/index.md | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/entities/data.md b/docs/entities/data.md index 74d4f03a0..3e66822b9 100644 --- a/docs/entities/data.md +++ b/docs/entities/data.md @@ -5,7 +5,7 @@ sidebar_position: 2 One of the most important use cases of entities is to store data of some sort. 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. +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. These systems generally exist in parallel and can be replaced with one another, this is due to legacy reasons. @@ -87,6 +87,7 @@ Entities have been patched to extend `AttachmentHolder` and as such support data Of course, 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 diff --git a/docs/entities/index.md b/docs/entities/index.md index d5779b057..f9ddc36e9 100644 --- a/docs/entities/index.md +++ b/docs/entities/index.md @@ -181,14 +181,14 @@ While not all entities have the concept of hit points, they can still all receiv Damaging an entity is possible by calling `Entity#hurt`. `Entity#hurt` takes 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 the behavior in `#hurt` by overriding it. For example, we could make our entity take double damage from fire, and no damage from any other source, like so: +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 hurt(DamageSource damageSource, float amount) { +public boolean hurtServer(ServerLevel level, DamageSource damageSource, float amount) { if (damageSource.is(DamageTypeTags.IS_FIRE)) { - return super.hurt(damageSource, amount * 2); + return super.hurt(level, damageSource, amount * 2); } else { return false; } @@ -203,7 +203,7 @@ Quite often, you will want your entity to do something (e.g. move) every tick. T - `#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. +- `#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. From 887ed98fe3916c8539323d7c9ff5199f1783b83b Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Mon, 2 Dec 2024 17:12:24 +0100 Subject: [PATCH 57/59] address luke's comment --- docs/concepts/events.md | 1 + docs/entities/livingentity.md | 8 +++++++- src/pages/contributing.md | 18 ++++++++++++++++++ 3 files changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/concepts/events.md b/docs/concepts/events.md index 75dfb0380..8fd043fe1 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/entities/livingentity.md b/docs/entities/livingentity.md index 8e53976ff..0b7410e86 100644 --- a/docs/entities/livingentity.md +++ b/docs/entities/livingentity.md @@ -104,13 +104,16 @@ _See [Containers on Entities][containers]._ ## Hierarchy -Living entities have a complex class hierarchy. As mentioned before, there are three direct subclasses: +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`. @@ -181,6 +184,9 @@ graph LR; 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]. diff --git a/src/pages/contributing.md b/src/pages/contributing.md index 878cc8814..b992ebfc2 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 From 8e8774dade8a2ff2bce050578c3e4ee775162edf Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Sat, 14 Dec 2024 18:06:43 +0100 Subject: [PATCH 58/59] address xfact's comments --- docs/blocks/index.md | 2 +- docs/entities/data.md | 20 ++++++++++++-------- docs/entities/renderer.md | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/docs/blocks/index.md b/docs/blocks/index.md index c3750b3e3..6496fbbd4 100644 --- a/docs/blocks/index.md +++ b/docs/blocks/index.md @@ -280,7 +280,7 @@ The following subsections further break down these stages into actual method cal #### Mining Speed -The mining speed is calculated as f from the block's hardness, the used [tool]'s speed, and several entity [attributes] according to the following rules: +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, diff --git a/docs/entities/data.md b/docs/entities/data.md index 3e66822b9..4c29254df 100644 --- a/docs/entities/data.md +++ b/docs/entities/data.md @@ -3,20 +3,20 @@ sidebar_position: 2 --- # Data and Networking -One of the most important use cases of entities is to store data of some sort. 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. +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. These systems generally exist in parallel and can be replaced with one another, this is due to legacy reasons. +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 both storing and syncing values over the network. It is split into three classes: +`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]. - - They are a registry, meaning that if you want to add new `EntityDataSerializer`s, they must be added by [registration]. + - 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 used to get and set the data values. +- `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: @@ -34,6 +34,10 @@ public class MyEntity extends Entity { } ``` +:::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 @@ -57,7 +61,7 @@ this.getEntityData().set(MY_DATA, 1); ## `readAdditionalSaveData` and `addAdditionalSaveData` -This method works by loading/saving your values from/to an [NBT tag][nbt], like so: +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. @@ -80,11 +84,11 @@ Additionally, you can send your own packets upon spawning. To do so, override `I ## Data Attachments -Entities have been patched to extend `AttachmentHolder` and as such support data storage via [data attachments][attachment]. Please see the linked article for more information. +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 -Of course, 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. +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 diff --git a/docs/entities/renderer.md b/docs/entities/renderer.md index 04717378d..be0704d9f 100644 --- a/docs/entities/renderer.md +++ b/docs/entities/renderer.md @@ -5,7 +5,7 @@ sidebar_position: 5 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 to avoid common issues when the entity renderer accidentally mutates the entity's fields. +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 From 77247baca945a656a3719426427616ceed91fce7 Mon Sep 17 00:00:00 2001 From: IchHabeHunger54 Date: Sat, 11 Jan 2025 00:19:33 +0100 Subject: [PATCH 59/59] address champ's comments, pt 1 --- docs/blocks/index.md | 2 +- docs/entities/attributes.md | 7 ++++--- docs/entities/index.md | 33 ++++++++++++++++++++++++--------- docs/entities/livingentity.md | 9 +++------ docs/entities/renderer.md | 3 ++- 5 files changed, 34 insertions(+), 20 deletions(-) diff --git a/docs/blocks/index.md b/docs/blocks/index.md index 6496fbbd4..a83f4b438 100644 --- a/docs/blocks/index.md +++ b/docs/blocks/index.md @@ -308,7 +308,7 @@ destroySpeed = /* The PlayerEvent.BreakSpeed event is fired here, allowing modde return destroySpeed; ``` -The exact code for this can be found in `Player#getDigSpeed` for reference. +The exact code for this can be found in `Player#getDestroySpeed` for reference. ### Ticking diff --git a/docs/entities/attributes.md b/docs/entities/attributes.md index b2b282e17..5be9492fe 100644 --- a/docs/entities/attributes.md +++ b/docs/entities/attributes.md @@ -44,11 +44,12 @@ The following attributes are in the `minecraft` namespace, and their in-code val | `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.6 | 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. | +| `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. This is mitigated by some mods that change these values. +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 @@ -58,7 +59,7 @@ The following attributes are in the `neoforge` namespace, and their in-code valu | 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,64]` | 64 | How far the nametag of the entity will be visible, in blocks. | +| `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 diff --git a/docs/entities/index.md b/docs/entities/index.md index f9ddc36e9..e5dba451d 100644 --- a/docs/entities/index.md +++ b/docs/entities/index.md @@ -68,9 +68,8 @@ public static final Supplier> MY_ENTITY = ENTITY_TYPES.regi // 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 is a string used for datafixing; mods generally - // do not utilize this and can safely pass null here instead. - .build(null) + // Build the entity type. The parameter should be the same as the entity id. + .build("my_entity") ); ``` @@ -111,7 +110,7 @@ There are also some other properties that are only set on one or two `MobCategor ## The Entity Class -To begin, we create an `Entity` subclass. Alongside a constructor, `Entity` (which is an abstract class) defines three required methods that we are required to implement. These will be explained in the [Data and Networking article][data], in order to not further bloat this article. +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 { @@ -130,6 +129,11 @@ public class MyEntity extends Entity { @Override protected void defineSynchedData(SynchedEntityData.Builder builder) {} + + @Override + public boolean hurtServer(ServerLevel level, DamageSource damageSource, float amount) { + return true; + } } ``` @@ -171,7 +175,7 @@ if (!level.isClientSide()) { } ``` -This will be used for pretty much all non-living entities. Players should obviously not be spawned yourself, and `Mob`s have [their own ways of spawning][mobspawn] (though they can also be added via `#addFreshEntity`). +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 @@ -179,7 +183,7 @@ _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 `Entity#hurt`. `Entity#hurt` takes 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. +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: @@ -188,6 +192,9 @@ In turn, entities can also modify that behavior. This isn't done by overriding ` // 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; @@ -195,6 +202,8 @@ public boolean hurtServer(ServerLevel level, DamageSource damageSource, float am } ``` +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 @@ -233,11 +242,14 @@ Picking is the process of selecting the thing that the player is currently looki @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()); } ``` -Your entity can also be disabled from picking entirely. The primary use case for this would be multipart entities, such as the ender dragon, where the parent entity has picking disabled, but the parts have it enabled again, for finer hitbox tuning. Picking can be disabled like so: +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 @@ -253,7 +265,7 @@ If you want to do the picking (i.e. ray casting) yourself, you can call `Entity# _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. +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. @@ -286,7 +298,9 @@ Alternatively, attachments can be defined yourself by calling `EntityAttachments ```java // In some EntityType creation EntityType.Builder.of(...) - // This EntityAttachments will make name tags float half a block above the top end of the entity's hitbox. + // 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(); ``` @@ -409,6 +423,7 @@ A new projectile can be created by extending `Projectile` or a fitting subclass, [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 diff --git a/docs/entities/livingentity.md b/docs/entities/livingentity.md index 0b7410e86..34b216598 100644 --- a/docs/entities/livingentity.md +++ b/docs/entities/livingentity.md @@ -197,7 +197,7 @@ graph LR; ## Spawning -In addition to the [regular ways of spawning][spawning], `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. +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 @@ -209,16 +209,12 @@ DeferredItem MY_ENTITY_SPAWN_EGG = ITEMS.registerItem("my_entity_s properties -> new SpawnEggItem( // The entity type to spawn. MY_ENTITY_TYPE.get(), - // The colors to use for the spawn egg. The first one is the base/background color, - // the second one is the spots/highlight color. - 0xff0000, - 0x00ff00, // 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 [model] and [translation] should be added. +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 @@ -257,6 +253,7 @@ 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 diff --git a/docs/entities/renderer.md b/docs/entities/renderer.md index be0704d9f..631772b3f 100644 --- a/docs/entities/renderer.md +++ b/docs/entities/renderer.md @@ -166,7 +166,8 @@ 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. + // 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" );