Skip to content

Commit

Permalink
Merge remote-tracking branch 'refs/remotes/upstream/main' into featur…
Browse files Browse the repository at this point in the history
…e/blockentities
  • Loading branch information
IchHabeHunger54 committed Sep 25, 2024
2 parents 1557449 + 56cb721 commit 7a8c624
Show file tree
Hide file tree
Showing 17 changed files with 917 additions and 37 deletions.
2 changes: 1 addition & 1 deletion docs/advanced/_category_.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"label": "Advanced Topics",
"position": 11
"position": 12
}
2 changes: 1 addition & 1 deletion docs/datastorage/attachments.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,5 +114,5 @@ NeoForge.EVENT_BUS.register(PlayerEvent.Clone.class, event -> {
```

[saveddata]: ./saveddata.md
[datacomponents]: ../items/datacomponents.md
[datacomponents]: ../items/datacomponents.mdx
[network]: ../networking/index.md
2 changes: 1 addition & 1 deletion docs/datastorage/nbt.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,5 +101,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
[datacomponents]: ../items/datacomponents.mdx
[nbtwiki]: https://minecraft.wiki/w/NBT_format
62 changes: 62 additions & 0 deletions docs/items/datacomponents.md → docs/items/datacomponents.mdx
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
---
sidebar_position: 2
---
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Data Components

Data components are key-value pairs within a map used to store data on an `ItemStack`. Each piece of data, such as firework explosions or tools, are stored as actual objects on the stack, making the values visible and operable without having to dynamically transform a general encoded instance (e.g., `CompoundTag`, `JsonElement`).
Expand Down Expand Up @@ -65,6 +68,63 @@ Either `persistent` or `networkSynchronized` must be provided in the builder; ot

`DataComponentType` are registry objects and must be [registered].

<Tabs defaultValue="latest">
<TabItem value="latest" label="Latest">

```java
// Using ExampleRecord(int, boolean)
// Only one Codec and/or StreamCodec should be used below
// Multiple are provided for an example

// Basic codec
public static final Codec<ExampleRecord> BASIC_CODEC = RecordCodecBuilder.create(instance ->
instance.group(
Codec.INT.fieldOf("value1").forGetter(ExampleRecord::value1),
Codec.BOOL.fieldOf("value2").forGetter(ExampleRecord::value2)
).apply(instance, ExampleRecord::new)
);
public static final StreamCodec<ByteBuf, ExampleRecord> BASIC_STREAM_CODEC = StreamCodec.composite(
ByteBufCodecs.INT, ExampleRecord::value1,
ByteBufCodecs.BOOL, ExampleRecord::value2,
ExampleRecord::new
);

// Unit stream codec if nothing should be sent across the network
public static final StreamCodec<ByteBuf, ExampleRecord> UNIT_STREAM_CODEC = StreamCodec.unit(new ExampleRecord(0, false));


// In another class
// The specialized DeferredRegister.DataComponents simplifies data component registration and avoids some generic inference issues with the `DataComponentType.Builder` within a `Supplier`
public static final DeferredRegister.DataComponents REGISTRAR = DeferredRegister.createDataComponents(Registries.DATA_COMPONENT_TYPE, "examplemod");

public static final DeferredHolder<DataComponentType<?>, DataComponentType<ExampleRecord>> BASIC_EXAMPLE = REGISTRAR.registerComponentType(
"basic",
builder -> builder
// The codec to read/write the data to disk
.persistent(BASIC_CODEC)
// The codec to read/write the data across the network
.networkSynchronized(BASIC_STREAM_CODEC)
);

/// Component will not be saved to disk
public static final DeferredHolder<DataComponentType<?>, DataComponentType<ExampleRecord>> TRANSIENT_EXAMPLE = REGISTRAR.registerComponentType(
"transient",
builder -> builder.networkSynchronized(BASIC_STREAM_CODEC)
);

// No data will be synced across the network
public static final DeferredHolder<DataComponentType<?>, DataComponentType<ExampleRecord>> NO_NETWORK_EXAMPLE = REGISTRAR.registerComponentType(
"no_network",
builder -> builder
.persistent(BASIC_CODEC)
// Note we use a unit stream codec here
.networkSynchronized(UNIT_STREAM_CODEC)
);
```

</TabItem>
<TabItem value="21.1.48" label="[21.0.0, 21.1.48]">

```java
// Using ExampleRecord(int, boolean)
// Only one Codec and/or StreamCodec should be used below
Expand Down Expand Up @@ -115,6 +175,8 @@ public static final DeferredHolder<DataComponentType<?>, DataComponentType<Examp
.networkSynchronized(UNIT_STREAM_CODEC)
);
```
</TabItem>
</Tabs>

## The Component Map

Expand Down
2 changes: 1 addition & 1 deletion docs/items/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ It is also possible to implement `ItemLike` on your custom objects. Simply overr
[blockstates]: ../blocks/states.md
[breaking]: ../blocks/index.md#breaking-a-block
[creativetabs]: #creative-tabs
[datacomponents]: ./datacomponents.md
[datacomponents]: ./datacomponents.mdx
[datagen]: ../resources/index.md#data-generation
[food]: #food
[hunger]: https://minecraft.wiki/w/Hunger#Mechanics
Expand Down
2 changes: 1 addition & 1 deletion docs/items/mobeffects.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ The `MobEffect` class also provides default functionality for adding attribute m

```java
public static final Supplier<MyMobEffect> MY_MOB_EFFECT = MOB_EFFECTS.register("my_mob_effect", () -> new MyMobEffect(...)
.addAttributeModifier(Attribute.ATTACK_DAMAGE, ResourceLocation.fromNamespaceAndPath("examplemod", "effect.strength"), 2.0, AttributeModifier.Operation.ADD_VALUE)
.addAttributeModifier(Attributes.ATTACK_DAMAGE, ResourceLocation.fromNamespaceAndPath("examplemod", "effect.strength"), 2.0, AttributeModifier.Operation.ADD_VALUE)
);
```

Expand Down
2 changes: 1 addition & 1 deletion docs/items/tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ public static final Supplier<ArmorItem> 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.

[block]: ../blocks/index.md
[datacomponents]: ./datacomponents.md
[datacomponents]: ./datacomponents.mdx
[item]: index.md
[itemability]: #itemabilitys
[tags]: ../resources/server/tags.md
Expand Down
2 changes: 1 addition & 1 deletion docs/legacy/_category_.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"label": "Legacy",
"position": 13
"position": 14
}
2 changes: 1 addition & 1 deletion docs/misc/_category_.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"label": "Miscellaneous",
"position": 12
"position": 13
}
2 changes: 1 addition & 1 deletion docs/networking/_category_.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
{
"label": "Networking",
"position": 10
"position": 11
}
80 changes: 68 additions & 12 deletions docs/networking/payload.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ public static void register(final RegisterPayloadHandlersEvent event) {
MyData.TYPE,
MyData.STREAM_CODEC,
new DirectionalPayloadHandler<>(
ClientPayloadHandler::handleData,
ServerPayloadHandler::handleData
ClientPayloadHandler::handleDataOnMain,
ServerPayloadHandler::handleDataOnMain
)
);
}
Expand All @@ -79,7 +79,72 @@ Now that we have registered the payload we need to implement a handler. For this
```java
public class ClientPayloadHandler {

public static void handleData(final MyData data, final IPayloadContext context) {
public static void handleDataOnMain(final MyData data, final IPayloadContext context) {
// Do something with the data, on the main thread
blah(data.age());
}
}
```

Here a couple of things are of note:

- The handling method here gets the payload, and a contextual object.
- The handling method of the payload is, by default, invoked on the main thread.


If you need to do some computation that is resource intensive, then the work should be done on the network thread, instead of blocking the main thread. This is done by setting the `HandlerThread` of the `PayloadRegistrar` to `HandlerThread#NETWORK` via `PayloadRegistrar#executesOn` before registering the payload.

```java
@SubscribeEvent
public static void register(final RegisterPayloadHandlersEvent event) {
final PayloadRegistrar registrar = event.registrar("1")
.executesOn(HandlerThread.NETWORK); // All subsequent payloads will register on the network thread
registrar.playBidirectional(
MyData.TYPE,
MyData.STREAM_CODEC,
new DirectionalPayloadHandler<>(
ClientPayloadHandler::handleDataOnNetwork,
ServerPayloadHandler::handleDataOnNetwork
)
);
}
```

:::note
All payloads registered after an `executesOn` call will retain the same thread execution location until `executesOn` is called again.

```java
PayloadRegistrar registrar = event.registrar("1");

registrar.playBidirectional(...); // On the main thread
registrar.playBidirectional(...); // On the main thread

// Configuration methods modify the state of the registrar
// by creating a new instance, so the change needs to be
/// updated by storing the result
registrar = registrar.executesOn(HandlerThread.NETWORK);

registrar.playBidirectional(...); // On the network thread
registrar.playBidirectional(...); // On the network thread

registrar = registrar.executesOn(HandlerThread.MAIN);

registrar.playBidirectional(...); // On the main thread
registrar.playBidirectional(...); // On the main thread
```
:::

Here a couple of things are of note:

- If you want to run code on the main game thread you can use `enqueueWork` to submit a task to the main thread.
- The method will return a `CompletableFuture` that will be completed on the main thread.
- Notice: A `CompletableFuture` is returned, this means that you can chain multiple tasks together, and handle exceptions in a single place.
- If you do not handle the exception in the `CompletableFuture` then it will be swallowed, **and you will not be notified of it**.

```java
public class ClientPayloadHandler {

public static void handleDataOnNetwork(final MyData data, final IPayloadContext context) {
// Do something with the data, on the network thread
blah(data.name());

Expand All @@ -96,15 +161,6 @@ public class ClientPayloadHandler {
}
```

Here a couple of things are of note:

- The handling method here gets the payload, and a contextual object.
- The handler of the payload method is invoked on the networking thread, so it is important to do all the heavy work here, instead of blocking the main game thread.
- If you want to run code on the main game thread you can use `enqueueWork` to submit a task to the main thread.
- The method will return a `CompletableFuture` that will be completed on the main thread.
- Notice: A `CompletableFuture` is returned, this means that you can chain multiple tasks together, and handle exceptions in a single place.
- If you do not handle the exception in the `CompletableFuture` then it will be swallowed, **and you will not be notified of it**.

With your own payloads you can then use those to configure the client and server using [Configuration Tasks][configuration].

## Sending Payloads
Expand Down
60 changes: 57 additions & 3 deletions docs/resources/server/datamaps/builtin.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ Allows configuring composter values, as a replacement for `ComposterBlock.COMPOS
```json5
{
// A 0 to 1 (inclusive) float representing the chance that the item will update the level of the composter
"chance": 1
"chance": 1,
// Optional, defaults to false - whether farmer villagers can compost this item
"can_villager_compost": false
}
```

Expand Down Expand Up @@ -98,6 +100,34 @@ Example:
}
```

## `neoforge:oxidizables`

Allows configuring oxidation stages, as a replacement for `WeatheringCopper#NEXT_BY_BLOCK` (which will be ignored in 1.21.2). This data map is also used to build a reverse deoxidation map (for scraping with an axe). It is located at `neoforge/data_maps/block/oxidizables.json` and its objects have the following structure:

```json5
{
// The block this block will turn into once oxidized
"next_oxidized_stage": "examplemod:oxidized_block"
}
```

:::note
Custom blocks must implement `WeatheringCopperFullBlock` or `WeatheringCopper` and call `changeOverTime` in `randomTick` to oxidize naturally.
:::

Example:

```json5
{
"values": {
"mymod:custom_copper": {
// Make a custom copper block oxidize into custom oxidized copper
"next_oxidized_stage": "mymod:custom_oxidized_copper"
}
}
}
```

## `neoforge:parrot_imitations`

Allows configuring the sounds produced by parrots when they want to imitate a mob, as a replacement for `Parrot#MOB_SOUND_MAP` (which is now ignored). This data map is located at `neoforge/data_maps/entity_type/parrot_imitations.json` and its objects have the following structure:
Expand Down Expand Up @@ -141,7 +171,7 @@ Example:
"minecraft:armorer": {
// Make armorers give the raid hero the armorer gift loot table
"loot_table": "minecraft:gameplay/hero_of_the_village/armorer_gift"
},
}
}
}
```
Expand Down Expand Up @@ -170,5 +200,29 @@ Example:
}
```

[datacomponent]: ../../../items/datacomponents.md
## `neoforge:waxables`

Allows configuring the block a block will turn into when waxed (right clicked with a honeycomb), as a replacement for `HoneycombItem#WAXABLES` (which will be ignored in 1.21.2). This data map is also used to build a reverse dewaxing map (for scraping with an axe). It is located at `neoforge/data_maps/block/waxables.json` and its objects have the following structure:

```json5
{
// The waxed variant of this block
"waxed": "minecraft:iron_block"
}
```

Example:

```json5
{
"values": {
// Make gold blocks turn into iron blocks once waxed
"minecraft:gold_block": {
"waxed": "minecraft:iron_block"
}
}
}
```

[datacomponent]: ../../../items/datacomponents.mdx
[datamap]: index.md
2 changes: 1 addition & 1 deletion docs/resources/server/datamaps/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ Like many other things, data maps are serialized and deserialized using [codecs]

```java
public record ExampleData(float amount, float chance) {
public static final Codec<ExampleData> CODEC = RecordCodecBuilder(instance -> instance.group(
public static final Codec<ExampleData> CODEC = RecordCodecBuilder.create(instance -> instance.group(
Codec.FLOAT.fieldOf("amount").forGetter(ExampleData::amount),
Codec.floatRange(0, 1).fieldOf("chance").forGetter(ExampleData::chance)
).apply(instance, ExampleData::new));
Expand Down
2 changes: 1 addition & 1 deletion docs/resources/server/loottables/lootfunctions.md
Original file line number Diff line number Diff line change
Expand Up @@ -848,7 +848,7 @@ During datagen, call `SequenceFunction#of` with the other functions to construct
[component]: ../../client/i18n.md#components
[conditions]: lootconditions
[custom]: custom.md#custom-loot-functions
[datacomponent]: ../../../items/datacomponents.md
[datacomponent]: ../../../items/datacomponents.mdx
[entitytarget]: index.md#entity-targets
[entry]: index.md#loot-entry
[itemmodifiers]: https://minecraft.wiki/w/Item_modifier#JSON_format
Expand Down
Loading

0 comments on commit 7a8c624

Please sign in to comment.