From baa8bba842ea4b9ab93d54dd6aead35674b2d94d Mon Sep 17 00:00:00 2001 From: Matyrobbrt Date: Fri, 26 Jan 2024 15:01:24 +0200 Subject: [PATCH 01/16] Guide on data maps --- docs/datamaps/_category_.json | 3 + docs/datamaps/index.md | 164 ++++++++++++++++++++++++++++++++++ docs/datamaps/neo_maps.md | 29 ++++++ docs/datamaps/structure.md | 99 ++++++++++++++++++++ 4 files changed, 295 insertions(+) create mode 100644 docs/datamaps/_category_.json create mode 100644 docs/datamaps/index.md create mode 100644 docs/datamaps/neo_maps.md create mode 100644 docs/datamaps/structure.md diff --git a/docs/datamaps/_category_.json b/docs/datamaps/_category_.json new file mode 100644 index 000000000..43a13824e --- /dev/null +++ b/docs/datamaps/_category_.json @@ -0,0 +1,3 @@ +{ + "label": "Data Maps" +} \ No newline at end of file diff --git a/docs/datamaps/index.md b/docs/datamaps/index.md new file mode 100644 index 000000000..cdf813009 --- /dev/null +++ b/docs/datamaps/index.md @@ -0,0 +1,164 @@ +# Data Maps + +A registry data map contains data-driven, reloadable objects that can be attached to a registry object. +This system allows more easily data-driving game behaviour, as they provide functionality such as syncing or conflict resolution, leading to a better and more configurable user experience. + +You can think of tags as entry->boolean maps, while data maps are more flexible entry->object maps. + +A data map can be attached to both static, built-in, registries and dynamic data-driven datapack registries. + +## Registration +A data map type should be statically created and then registered to the `RegisterDataMapTypesEvent` (which is fired on the mod event bus). The `DataMapType` can be created using a `DataMapType$Builder`, through `DataMapType#builder`. + +A simple `DataMapType` has two generic arguments: `T` (the values that are being attached) and `R` (the type of the registry the data map is for). A data map of `SomeObject`s that are attached to `Item`s can, as such, be represented as `DataMapType`. + +Data maps are serialized and deserialized using [Codecs](../datastorage/codecs.md). + +Let's take the following record representing the data map value as an example: +```java +public record DropHealing( + float amount, float chance +) { + public static final Codec CODEC = RecordCodecBuilder.create(in -> in.group( + Codec.FLOAT.fieldOf("amount").forGetter(DropHealing::amount), + Codec.floatRange(0, 1).fieldOf("chance").forGetter(DropHealing::chance) + ).apply(in, DropHealing::new)); +} +``` + +:::warning +The value should be an *immutable* object, as otherwise weird behaviour can be caused if the object is attached to all values within a tag (since no copy is created). +::: + +For the purposes of this example, we will use this data map to heal players when they drop an item. +The `DataMapType` can be created as such: +```java +public static final DataMapType DROP_HEALING = DataMapType.builder( + new ResourceLocation("mymod:drop_healing"), Registries.ITEM, DropHealing.CODEC +).build(); +``` +and then registered to the `RegisterDataMapTypesEvent` using `RegisterDataMapTypesEvent#register`. + +## Syncing +A synced data map will have its values synced to clients. A data map can be marked as synced using `DataMapType$Builder#synced(Codec networkCodec, boolean mandatory)`. +The values of the data map will then be synced using the `networkCodec`. +If the `mandatory` flag is set to `true`, clients that do not support the data map (including Vanilla clients) will not be able to connect to the server, nor vice-versa. A non-mandatory data map is, on the other side, optional, so it will not prevent any clients from joining. + +## JSON Structure and location +Data maps are loaded from a JSON file located at `:mapNamespace/data_maps/:registryNamespace/:registryPath/:mapPath.json`. +For more information, please [check out the dedicated page](./structure.md). + +## Usage +As data maps can be used on any registry, they can be queried through `Holder`s, and not through the actual registry objects. +You can query a data map value using `Holder#getData(DataMapType)`. If that object doesn't have a value attached, the method will return `null`. + +:::note +Only reference holders will return a value in that method. `Named` holders will **not**. Generally, you will only encounter reference holders (which are returned by methods such as `Registry#wrapAsHolder`, `Registry#getHolder` or the different `builtInRegistryHolder` methods). +::: + +To continue the example above, we can implement our intended behaviour as follows: +```java +public static void onItemDrop(final ItemTossEvent event) { + final ItemStack stack = event.getEntity().getItem(); + // ItemStack has a getItemHolder method that will return a Holder which points to the item the stack is of + //highlight-next-line + final DropHealing value = stack.getItemHolder().getData(DROP_HEALING); + // Since getData returns null if the item will not have a drop healing value attached, we guard against it being null + if (value != null) { + // And here we simply use the values + if (event.getPlayer().level().getRandom().nextFloat() > value.chance()) { + event.getPlayer().heal(value.amount()); + } + } +} +``` + +## Advanced data maps +Advanced data maps are data maps which have added functionality. Namely, the ability of merging values and selectively removing them, through a remover. Implementing some form of merging and removers is highly recommended for data maps whose values are collection-likes (like `Map`s or `List`s). + +`AdvancedDataMapType` have one more generic besides `T` and `R`: `VR extends DataMapValueRemover`. This additional generic allows you to datagen remove objects with increased type safety, but it can otherwise be ignored and treated as wilcard (`?`) for most use cases. + +### Creation +You create an `AdvancedDataMapType` using `AdvancedDataMapType#builder`. Unlike the normal builder, the builder returned by that method will have two more methods (`merger` and `remover`), and it will return an `AdvancedDataMapType`. + +Registration methods remain the same. + +### Mergers +An advanced data map can provide a `DataMapValueMerger` through `AdvancedDataMapType#merger`. This merger will be used to handle conflicts between data packs that attempt to attach a value to the same object. +The merger will be given the two conflicting values, and their sources (as an `Either, ResourceKey>` since values can be attached to all entries within a tag, not just individual entries), and is expected to return the value that will actually be attached. +Generally, mergers should simply merge the values, and should not perform "hard" overwrites unless necessary (i.e. if merging isn't possible). A value can be overwritten by specifying the object-level `replace` field, which will bypass the merger. + +We provide some default mergers for merging lists, sets and maps in `DataMapValueMerger`. + +The default merger (`DataMapValueMerger#defaultMerger`) has the typical first come last serverd priority-based overwriting behaviour that you'd expect from normal data packs, where the newest value always wins. + +### Removers +An advanced data map can provide a `DataMapValueRemover` through `AdvancedDataMapType#remover`. The remover will allow selective removals of data map values, effectively decomposition. +While by default a datapack can only remove the whole object attached to a registry entry, with a remover it can remove just speciffic values from the attached object (i.e. just the entry with a given key in the case of a map, or the entry with a specific property in the case of a list). + +The codec that is passed to the builder will decode removers that will then be given the value currently attached and its source, and is expected to create a new object that will have the properties requested by the `remove` object removed. Alternatively, an empty `Optional` will lead to the value being completely removed. + +An example of a remover that will remove a value with a specific key from a `Map`-based data map: +```java +public record MapRemover(String key) implements DataMapValueRemover, Item> { + public static final Codec CODEC = Codec.STRING.xmap(MapRemover::new, MapRemover::key); + + @Override + public Optional> remove(Map value, Registry registry, Either, ResourceKey> source, Item object) { + final Map newMap = new HashMap<>(value); + newMap.remove(key); + return Optional.of(newMap); + } +} +``` + +With the remover above in mind, we're attaching maps of string to string to items. Take the following data map JSON file: +```js +{ + "values": { + //highlight-start + "minecraft:carrot": { + "somekey1": "value1", + "somekey2": "value2" + } + //highlight-end + } +} +``` +That file will attach the map `[somekey1=value1, somekey2=value2]` to the `minecraft:carrot` item. Now, another datapack can come on top of it and remove just the value with the `somekey1` key, as such: +```js +{ + "remove": { + // As the remover is decoded as a string, we can use a string as the value here. If it were decoded as an object, we would have needed to use an object. + //highlight-next-line + "minecraft:carrot": "somekey1" + } +} +``` +After the second datapack is read and applied, the new value attached to the `minecraft:carrot` item will be `[somekey2=value2]`. + +## Datagen +Data maps can be generated through `DataMapProvider`. +You should extend that class, and then override the `generate` method to create your entries, similar to tag generation. + +Considering the drop healing example from the start, we could generate some values as follows: +```java +public class DropHealingGen extends DataMapProvider { + + public DropHealingGen(PackOutput packOutput, CompletableFuture lookupProvider) { + super(packOutput, lookupProvider); + } + + @Override + protected void gather() { + // In the examples below, we do not need to forcibly replace any value as that's the default behaviour since a merger isn't provided, so the third parameter can be false. + + // If you were to provide a merger for your data map, then the third parameter will cause the old value to be overwritten if set to true, without invoking the merger + builder(DROP_HEALING) + // Always give entities that drop any item in the minecraft:fox_food tag 12 hearts + .add(ItemTags.FOX_FOOD, new DropHealing(12, 1f), false) + // Have a 10% chance of healing entities that drop an acacia boat by one point + .add(Items.ACACIA_BOAT.builtInRegistryHolder(), new DropHealing(1, 0.1f), false); + } +} +``` \ No newline at end of file diff --git a/docs/datamaps/neo_maps.md b/docs/datamaps/neo_maps.md new file mode 100644 index 000000000..b12541fb3 --- /dev/null +++ b/docs/datamaps/neo_maps.md @@ -0,0 +1,29 @@ +# Built-in Data Maps +NeoForge provides a few data maps that mostly replace hardcoded in-code vanilla maps. +These dats maps can be found in `NeoForgeDataMaps`. + +## `neoforge:compostables` +NeoForge provides a data map that allows configuring composter values, as a replacement for `ComposterBlock#COMPOSTABLES` (which is now ignored). +This data map is located at `neoforged/data_maps/item/compostables.json` and its objects have the following structure: +```js +{ + // A 0 to 1 (inclusive) float representing the chance that the item will update the level of the composter + "chance": 1, + + // A 1 to 7 integer that indicates how many levels should be added to the composter when the item is successfully composted. Defaults to 1 level. + "amount": 1 // int +} +``` + +Example: +```js +{ + "values": { + // Give acacia logs a 50% chance that they will fill a composter with 2 levels + "minecraft:acacia_log": { + "chance": 0.5, + "amount": 2 + } + } +} +``` \ No newline at end of file diff --git a/docs/datamaps/structure.md b/docs/datamaps/structure.md new file mode 100644 index 000000000..27a6e79a9 --- /dev/null +++ b/docs/datamaps/structure.md @@ -0,0 +1,99 @@ +# JSON Structure +For the purposes of this page, we will use a data map which is an object with two float keys: `amount` and `chance` as an example. The codec for that object can be found [here](./index.md#registration). + +## Location +Data maps are loaded from a JSON file located at `:mapNamespace/data_maps/:registryNamespace/:registryPath/:mapPath.json`, where: +- `mapNamespace` is the namespace of the ID of the data map +- `mapPath` is the path of the ID of the data map +- `registryNamespace` is the namespace of the ID of the registry +- `registryPath` is the path of the ID of the registry + +:::note +The registry namespace is ommited if it is `minecraft`. +::: + +Examples: +- For a data map named `mymod:drop_healing` for the `minecraft:item` registry (as in the example), the path will be `mymod/data_maps/item/drop_healing.json`. +- For a data map named `somemod:somemap` for the `minecraft:block` registry, the path will be `somemod/data_maps/block/somemap.json`. +- For a data map named `example:stuff` for the `somemod:custom` registry, the path will be `example/data_maps/somemod/custom/stuff.json`. + +## Global `replace` field +The JSON file has an optional, global `replace` field, which is similar to tags, and when `true` will remove all previously attached values of that data map. This is useful for datapacks that want to completely change the entire data map. + +## Adding values +Values can be attached to objects using the `values` map. Each key will represent either the ID of an individual registry entry to attach the value to, or a tag key, preceeded by `#`. If it is a tag, the same value will be attached to all entries in that tag. +The key will be the object to attach. + +```js +{ + "values": { + // Attach a value to the carrot item + "minecraft:carrot": { + "amount": 12, + "chance": 1 + }, + // Attach a value to all items in the logs tag + "#minecraft:logs": { + "amount": 1, + "chance": 0.1 + } + } +} +``` + +:::info +The above structure will invoke mergers in the case of [advanced data maps](./index.md#advanced-data-maps). If you do not want to invoke the merger for a specific object, then you will have to use a structure similar to this one: +```js +{ + "values": { + // Overwrite the value of the carrot item + "minecraft:carrot": { + "replace": true, + // The new value will be under a value sub-object + "value": { + "amount": 12, + "chance": 1 + } + } + } +} +``` +::: + +## Removing values + +A JSON file can also remove values previously attached to objects, through the use of the `remove` array: +```js +{ + // Remove the value attached to apples and potatoes + "remove": ["minecraft:apple", "minecraft:potato"] +} +``` +The array contains a list of registry entry IDs or tags to remove the value from. + +:::warning +Removals happen after the values in the current JSON file have been attached, so you can use the removal feature to remove a value attached to an object through a tag: +```js +{ + "values": { + "#minecraft:logs": 12 + }, + // Remove the value from the acacia log, so that all logs but acacia have the value 12 attached to them + "remove": ["minecraft:acacia_log"] +} +``` +::: + +:::info +In the case of [advanced data maps](./index.md#advanced-data-maps) that provide a custom remover, the arguments of the remover can be provided by transforming the `remove` array into a map. +Let's assume that the remover object is serialized as a string and removes the value with a given key for a `Map`-based data map: +```js +{ + "remove": { + // The remover will be deserialized from the value (`somekey1` in this case) + // and applied to the value attached to the carrot item + "minecraft:carrot": "somekey1" + } +} +``` +::: \ No newline at end of file From 95d24fbf8ca65071a2262e4919793b130a9694d7 Mon Sep 17 00:00:00 2001 From: Matyrobbrt Date: Fri, 26 Jan 2024 18:43:52 +0200 Subject: [PATCH 02/16] Update index.md --- docs/datamaps/index.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/datamaps/index.md b/docs/datamaps/index.md index cdf813009..1e1862083 100644 --- a/docs/datamaps/index.md +++ b/docs/datamaps/index.md @@ -5,11 +5,15 @@ This system allows more easily data-driving game behaviour, as they provide func You can think of tags as entry->boolean maps, while data maps are more flexible entry->object maps. -A data map can be attached to both static, built-in, registries and dynamic data-driven datapack registries. +A data map can be attached to both static, built-in, registries and dynamic data-driven datapack registries. + +Data maps support reloading through the use of the `/reload` command or any other means that reload server resources. ## Registration A data map type should be statically created and then registered to the `RegisterDataMapTypesEvent` (which is fired on the mod event bus). The `DataMapType` can be created using a `DataMapType$Builder`, through `DataMapType#builder`. +The builder provides a `syced` method which can be used to mark a data map as synced and have it sent to clients. + A simple `DataMapType` has two generic arguments: `T` (the values that are being attached) and `R` (the type of the registry the data map is for). A data map of `SomeObject`s that are attached to `Item`s can, as such, be represented as `DataMapType`. Data maps are serialized and deserialized using [Codecs](../datastorage/codecs.md). @@ -27,7 +31,7 @@ public record DropHealing( ``` :::warning -The value should be an *immutable* object, as otherwise weird behaviour can be caused if the object is attached to all values within a tag (since no copy is created). +The value (`T`) should be an *immutable* object, as otherwise weird behaviour can be caused if the object is attached to all entries within a tag (since no copy is created). ::: For the purposes of this example, we will use this data map to heal players when they drop an item. @@ -53,7 +57,7 @@ As data maps can be used on any registry, they can be queried through `Holder`s, You can query a data map value using `Holder#getData(DataMapType)`. If that object doesn't have a value attached, the method will return `null`. :::note -Only reference holders will return a value in that method. `Named` holders will **not**. Generally, you will only encounter reference holders (which are returned by methods such as `Registry#wrapAsHolder`, `Registry#getHolder` or the different `builtInRegistryHolder` methods). +Only reference holders will return a value in that method. `Direct` holders will **not**. Generally, you will only encounter reference holders (which are returned by methods such as `Registry#wrapAsHolder`, `Registry#getHolder` or the different `builtInRegistryHolder` methods). ::: To continue the example above, we can implement our intended behaviour as follows: @@ -86,7 +90,7 @@ Registration methods remain the same. ### Mergers An advanced data map can provide a `DataMapValueMerger` through `AdvancedDataMapType#merger`. This merger will be used to handle conflicts between data packs that attempt to attach a value to the same object. The merger will be given the two conflicting values, and their sources (as an `Either, ResourceKey>` since values can be attached to all entries within a tag, not just individual entries), and is expected to return the value that will actually be attached. -Generally, mergers should simply merge the values, and should not perform "hard" overwrites unless necessary (i.e. if merging isn't possible). A value can be overwritten by specifying the object-level `replace` field, which will bypass the merger. +Generally, mergers should simply merge the values, and should not perform "hard" overwrites unless necessary (i.e. if merging isn't possible). If a pack wants to bypass the merger, it can do so by specifying the object-level `replace` field. We provide some default mergers for merging lists, sets and maps in `DataMapValueMerger`. From 5eff8523cb6896533ff956439dbeb322ad216ded Mon Sep 17 00:00:00 2001 From: Matyrobbrt Date: Fri, 26 Jan 2024 18:47:23 +0200 Subject: [PATCH 03/16] Update index.md --- docs/datamaps/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/datamaps/index.md b/docs/datamaps/index.md index 1e1862083..fa8afd8a0 100644 --- a/docs/datamaps/index.md +++ b/docs/datamaps/index.md @@ -80,7 +80,7 @@ public static void onItemDrop(final ItemTossEvent event) { ## Advanced data maps Advanced data maps are data maps which have added functionality. Namely, the ability of merging values and selectively removing them, through a remover. Implementing some form of merging and removers is highly recommended for data maps whose values are collection-likes (like `Map`s or `List`s). -`AdvancedDataMapType` have one more generic besides `T` and `R`: `VR extends DataMapValueRemover`. This additional generic allows you to datagen remove objects with increased type safety, but it can otherwise be ignored and treated as wilcard (`?`) for most use cases. +`AdvancedDataMapType` have one more generic besides `T` and `R`: `VR extends DataMapValueRemover`. This additional generic allows you to datagen remove objects with increased type safety. ### Creation You create an `AdvancedDataMapType` using `AdvancedDataMapType#builder`. Unlike the normal builder, the builder returned by that method will have two more methods (`merger` and `remover`), and it will return an `AdvancedDataMapType`. From c005ede7073465bfd5311fbcc7c6a0c331dd3e10 Mon Sep 17 00:00:00 2001 From: Matyrobbrt Date: Fri, 26 Jan 2024 18:51:08 +0200 Subject: [PATCH 04/16] Update index.md --- docs/datamaps/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/datamaps/index.md b/docs/datamaps/index.md index fa8afd8a0..f0b8a43bf 100644 --- a/docs/datamaps/index.md +++ b/docs/datamaps/index.md @@ -94,7 +94,7 @@ Generally, mergers should simply merge the values, and should not perform "hard" We provide some default mergers for merging lists, sets and maps in `DataMapValueMerger`. -The default merger (`DataMapValueMerger#defaultMerger`) has the typical first come last serverd priority-based overwriting behaviour that you'd expect from normal data packs, where the newest value always wins. +The default merger (`DataMapValueMerger#defaultMerger`) has the typical behaviour you'd expect from normal data packs, where the newest value (which comes from the highest datapack) always wins. ### Removers An advanced data map can provide a `DataMapValueRemover` through `AdvancedDataMapType#remover`. The remover will allow selective removals of data map values, effectively decomposition. From 69c7b16d6959e435ec5d20d6079b89717f494692 Mon Sep 17 00:00:00 2001 From: Matyrobbrt Date: Fri, 26 Jan 2024 18:52:21 +0200 Subject: [PATCH 05/16] Update index.md --- docs/datamaps/index.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/datamaps/index.md b/docs/datamaps/index.md index f0b8a43bf..b6f1b412d 100644 --- a/docs/datamaps/index.md +++ b/docs/datamaps/index.md @@ -100,7 +100,8 @@ The default merger (`DataMapValueMerger#defaultMerger`) has the typical behaviou An advanced data map can provide a `DataMapValueRemover` through `AdvancedDataMapType#remover`. The remover will allow selective removals of data map values, effectively decomposition. While by default a datapack can only remove the whole object attached to a registry entry, with a remover it can remove just speciffic values from the attached object (i.e. just the entry with a given key in the case of a map, or the entry with a specific property in the case of a list). -The codec that is passed to the builder will decode removers that will then be given the value currently attached and its source, and is expected to create a new object that will have the properties requested by the `remove` object removed. Alternatively, an empty `Optional` will lead to the value being completely removed. +The codec that is passed to the builder will decode remover instances. These removers will then be given the value currently attached and its source, and are expected to create a new object to replace the old value. +Alternatively, an empty `Optional` will lead to the value being completely removed. An example of a remover that will remove a value with a specific key from a `Map`-based data map: ```java From d1c0e39161bafd6bb26272efc2ef1360298730a8 Mon Sep 17 00:00:00 2001 From: Matyrobbrt Date: Fri, 26 Jan 2024 21:29:38 +0200 Subject: [PATCH 06/16] Update index.md --- docs/datamaps/index.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/datamaps/index.md b/docs/datamaps/index.md index b6f1b412d..696bdaca1 100644 --- a/docs/datamaps/index.md +++ b/docs/datamaps/index.md @@ -14,7 +14,7 @@ A data map type should be statically created and then registered to the `Registe The builder provides a `syced` method which can be used to mark a data map as synced and have it sent to clients. -A simple `DataMapType` has two generic arguments: `T` (the values that are being attached) and `R` (the type of the registry the data map is for). A data map of `SomeObject`s that are attached to `Item`s can, as such, be represented as `DataMapType`. +A simple `DataMapType` has two generic arguments: `R` (the type of the registry the data map is for) and `T` (the values that are being attached). A data map of `SomeObject`s that are attached to `Item`s can, as such, be represented as `DataMapType`. Data maps are serialized and deserialized using [Codecs](../datastorage/codecs.md). @@ -37,7 +37,7 @@ The value (`T`) should be an *immutable* object, as otherwise weird behaviour ca For the purposes of this example, we will use this data map to heal players when they drop an item. The `DataMapType` can be created as such: ```java -public static final DataMapType DROP_HEALING = DataMapType.builder( +public static final DataMapType DROP_HEALING = DataMapType.builder( new ResourceLocation("mymod:drop_healing"), Registries.ITEM, DropHealing.CODEC ).build(); ``` @@ -80,7 +80,7 @@ public static void onItemDrop(final ItemTossEvent event) { ## Advanced data maps Advanced data maps are data maps which have added functionality. Namely, the ability of merging values and selectively removing them, through a remover. Implementing some form of merging and removers is highly recommended for data maps whose values are collection-likes (like `Map`s or `List`s). -`AdvancedDataMapType` have one more generic besides `T` and `R`: `VR extends DataMapValueRemover`. This additional generic allows you to datagen remove objects with increased type safety. +`AdvancedDataMapType` have one more generic besides `T` and `R`: `VR extends DataMapValueRemover`. This additional generic allows you to datagen remove objects with increased type safety. ### Creation You create an `AdvancedDataMapType` using `AdvancedDataMapType#builder`. Unlike the normal builder, the builder returned by that method will have two more methods (`merger` and `remover`), and it will return an `AdvancedDataMapType`. @@ -105,7 +105,7 @@ Alternatively, an empty `Optional` will lead to the value being completely remov An example of a remover that will remove a value with a specific key from a `Map`-based data map: ```java -public record MapRemover(String key) implements DataMapValueRemover, Item> { +public record MapRemover(String key) implements DataMapValueRemover> { public static final Codec CODEC = Codec.STRING.xmap(MapRemover::new, MapRemover::key); @Override From b3725425fd4163c1cf91a835510b69d4cf2d1479 Mon Sep 17 00:00:00 2001 From: Matyrobbrt Date: Fri, 26 Jan 2024 22:29:35 +0200 Subject: [PATCH 07/16] Update structure.md --- docs/datamaps/structure.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/datamaps/structure.md b/docs/datamaps/structure.md index 27a6e79a9..51562cdcc 100644 --- a/docs/datamaps/structure.md +++ b/docs/datamaps/structure.md @@ -48,6 +48,7 @@ The above structure will invoke mergers in the case of [advanced data maps](./in "values": { // Overwrite the value of the carrot item "minecraft:carrot": { + // highlight-next-line "replace": true, // The new value will be under a value sub-object "value": { From 455ff660b8440d7c5df3e0a77d27d6883c912eb8 Mon Sep 17 00:00:00 2001 From: Matyrobbrt Date: Sun, 28 Jan 2024 18:22:29 +0200 Subject: [PATCH 08/16] Some review comments --- docs/datamaps/index.md | 39 ++++++++++++++++++++++++++++++-------- docs/datamaps/structure.md | 3 +++ 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/docs/datamaps/index.md b/docs/datamaps/index.md index 696bdaca1..a8276afd3 100644 --- a/docs/datamaps/index.md +++ b/docs/datamaps/index.md @@ -3,7 +3,7 @@ A registry data map contains data-driven, reloadable objects that can be attached to a registry object. This system allows more easily data-driving game behaviour, as they provide functionality such as syncing or conflict resolution, leading to a better and more configurable user experience. -You can think of tags as entry->boolean maps, while data maps are more flexible entry->object maps. +You can think of tags as registry object ➜ boolean maps, while data maps are more flexible registry object ➜ object maps. A data map can be attached to both static, built-in, registries and dynamic data-driven datapack registries. @@ -12,7 +12,7 @@ Data maps support reloading through the use of the `/reload` command or any othe ## Registration A data map type should be statically created and then registered to the `RegisterDataMapTypesEvent` (which is fired on the mod event bus). The `DataMapType` can be created using a `DataMapType$Builder`, through `DataMapType#builder`. -The builder provides a `syced` method which can be used to mark a data map as synced and have it sent to clients. +The builder provides a `synced` method which can be used to mark a data map as synced and have it sent to clients. A simple `DataMapType` has two generic arguments: `R` (the type of the registry the data map is for) and `T` (the values that are being attached). A data map of `SomeObject`s that are attached to `Item`s can, as such, be represented as `DataMapType`. @@ -48,8 +48,17 @@ A synced data map will have its values synced to clients. A data map can be mark The values of the data map will then be synced using the `networkCodec`. If the `mandatory` flag is set to `true`, clients that do not support the data map (including Vanilla clients) will not be able to connect to the server, nor vice-versa. A non-mandatory data map is, on the other side, optional, so it will not prevent any clients from joining. +:::tip +A separate network codec allows for packet sizes to be smaller, as you can choose what data to send, and in what format. Otherwise the default codec can be used. +::: + ## JSON Structure and location -Data maps are loaded from a JSON file located at `:mapNamespace/data_maps/:registryNamespace/:registryPath/:mapPath.json`. +Data maps are loaded from a JSON file located at `:mapNamespace/data_maps/:registryNamespace/:registryPath/:mapPath.json`, where: +- `mapNamespace` is the namespace of the ID of the data map +- `mapPath` is the path of the ID of the data map +- `registryNamespace` is the namespace of the ID of the registry +- `registryPath` is the path of the ID of the registry + For more information, please [check out the dedicated page](./structure.md). ## Usage @@ -83,14 +92,24 @@ Advanced data maps are data maps which have added functionality. Namely, the abi `AdvancedDataMapType` have one more generic besides `T` and `R`: `VR extends DataMapValueRemover`. This additional generic allows you to datagen remove objects with increased type safety. ### Creation -You create an `AdvancedDataMapType` using `AdvancedDataMapType#builder`. Unlike the normal builder, the builder returned by that method will have two more methods (`merger` and `remover`), and it will return an `AdvancedDataMapType`. - -Registration methods remain the same. +You create an `AdvancedDataMapType` using `AdvancedDataMapType#builder`. Unlike the normal builder, the builder returned by that method will have two more methods (`merger` and `remover`), and it will return an `AdvancedDataMapType`. Registration methods remain the same. ### Mergers An advanced data map can provide a `DataMapValueMerger` through `AdvancedDataMapType#merger`. This merger will be used to handle conflicts between data packs that attempt to attach a value to the same object. The merger will be given the two conflicting values, and their sources (as an `Either, ResourceKey>` since values can be attached to all entries within a tag, not just individual entries), and is expected to return the value that will actually be attached. -Generally, mergers should simply merge the values, and should not perform "hard" overwrites unless necessary (i.e. if merging isn't possible). If a pack wants to bypass the merger, it can do so by specifying the object-level `replace` field. +Generally, mergers should simply merge the values, and should not perform "hard" overwrites unless necessary (i.e. if merging isn't possible). If a pack wants to bypass the merger, it can do so by specifying the object-level `replace` field. + +Let's imagine a scenario where we have a data map that attaches integers to items: +```java +public class IntMerger implements DataMapValueMerger { + @Override + public Integer merge(Registry registry, Either, ResourceKey> first, Integer firstValue, Either, ResourceKey> second, Integer secondValue) { + //highlight-next-line + return firstValue + secondValue; + } +} +``` +The above merger will merge the values if two datapacks attach to the same object. So if the first pack attaches the value `12` to `minecraft:carrot`, and the second pack attaches the value `15` to `minecraft:carrot`, the final value will be `27`. However, if the second pack specifies the object-level `replace` field, the final value will be `15` as the merger won't be invoked. We provide some default mergers for merging lists, sets and maps in `DataMapValueMerger`. @@ -166,4 +185,8 @@ public class DropHealingGen extends DataMapProvider { .add(Items.ACACIA_BOAT.builtInRegistryHolder(), new DropHealing(1, 0.1f), false); } } -``` \ No newline at end of file +``` + +:::tip +There are `add` overloads that accept raw `ResourceLocation`s if you want to attach values to objects added by optional dependencies. In that case you should also provide [a loading condition](../resources/server/conditional) through the var-args parameter to avoid crashes. +::: \ No newline at end of file diff --git a/docs/datamaps/structure.md b/docs/datamaps/structure.md index 51562cdcc..0cda7370d 100644 --- a/docs/datamaps/structure.md +++ b/docs/datamaps/structure.md @@ -20,6 +20,9 @@ Examples: ## Global `replace` field The JSON file has an optional, global `replace` field, which is similar to tags, and when `true` will remove all previously attached values of that data map. This is useful for datapacks that want to completely change the entire data map. +## Loading conditions +Data map files support [loading conditions](../resources/server/conditional) both at root-level and at entry-level through a `neoforge:conditions` array. + ## Adding values Values can be attached to objects using the `values` map. Each key will represent either the ID of an individual registry entry to attach the value to, or a tag key, preceeded by `#`. If it is a tag, the same value will be attached to all entries in that tag. The key will be the object to attach. From 1e015acc0d996fbad5e18c719fe0e9cb70cc4027 Mon Sep 17 00:00:00 2001 From: Matyrobbrt Date: Sun, 28 Jan 2024 18:43:56 +0200 Subject: [PATCH 09/16] Update docusaurus.config.js --- docusaurus.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docusaurus.config.js b/docusaurus.config.js index 6ae368ad3..02bbdef53 100644 --- a/docusaurus.config.js +++ b/docusaurus.config.js @@ -170,7 +170,7 @@ const config = { prism: { theme: lightTheme, darkTheme: darkTheme, - additionalLanguages: ["java", "gradle", "toml", "groovy", "kotlin"], + additionalLanguages: ["java", "gradle", "toml", "groovy", "kotlin", "javascript", "json", "json5"], }, algolia: { // The application ID provided by Algolia From 1e52938a3028957b77a0ce979cafcccb5af0cab9 Mon Sep 17 00:00:00 2001 From: Matyrobbrt Date: Sun, 28 Jan 2024 21:42:41 +0200 Subject: [PATCH 10/16] Update neo_maps.md --- docs/datamaps/neo_maps.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/docs/datamaps/neo_maps.md b/docs/datamaps/neo_maps.md index b12541fb3..a97b04810 100644 --- a/docs/datamaps/neo_maps.md +++ b/docs/datamaps/neo_maps.md @@ -8,10 +8,7 @@ This data map is located at `neoforged/data_maps/item/compostables.json` and its ```js { // A 0 to 1 (inclusive) float representing the chance that the item will update the level of the composter - "chance": 1, - - // A 1 to 7 integer that indicates how many levels should be added to the composter when the item is successfully composted. Defaults to 1 level. - "amount": 1 // int + "chance": 1 } ``` @@ -19,10 +16,9 @@ Example: ```js { "values": { - // Give acacia logs a 50% chance that they will fill a composter with 2 levels + // Give acacia logs a 50% chance that they will fill a composter "minecraft:acacia_log": { - "chance": 0.5, - "amount": 2 + "chance": 0.5 } } } From f22ce0309f34964056abf6a3d869788b9ae79a05 Mon Sep 17 00:00:00 2001 From: Matyrobbrt Date: Sun, 28 Jan 2024 21:48:40 +0200 Subject: [PATCH 11/16] Stuff --- docs/datamaps/index.md | 8 ++++---- docs/datamaps/neo_maps.md | 2 +- docs/datamaps/structure.md | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/datamaps/index.md b/docs/datamaps/index.md index a8276afd3..9e9b6a800 100644 --- a/docs/datamaps/index.md +++ b/docs/datamaps/index.md @@ -10,7 +10,7 @@ A data map can be attached to both static, built-in, registries and dynamic data Data maps support reloading through the use of the `/reload` command or any other means that reload server resources. ## Registration -A data map type should be statically created and then registered to the `RegisterDataMapTypesEvent` (which is fired on the mod event bus). The `DataMapType` can be created using a `DataMapType$Builder`, through `DataMapType#builder`. +A data map type should be statically created and then registered to the `RegisterDataMapTypesEvent` (which is fired on the [mod event bus](../concepts/events)). The `DataMapType` can be created using a `DataMapType$Builder`, through `DataMapType#builder`. The builder provides a `synced` method which can be used to mark a data map as synced and have it sent to clients. @@ -53,7 +53,7 @@ A separate network codec allows for packet sizes to be smaller, as you can choos ::: ## JSON Structure and location -Data maps are loaded from a JSON file located at `:mapNamespace/data_maps/:registryNamespace/:registryPath/:mapPath.json`, where: +Data maps are loaded from a JSON file located at `mapNamespace/data_maps/registryNamespace/registryPath/mapPath.json`, where: - `mapNamespace` is the namespace of the ID of the data map - `mapPath` is the path of the ID of the data map - `registryNamespace` is the namespace of the ID of the registry @@ -113,7 +113,7 @@ The above merger will merge the values if two datapacks attach to the same objec We provide some default mergers for merging lists, sets and maps in `DataMapValueMerger`. -The default merger (`DataMapValueMerger#defaultMerger`) has the typical behaviour you'd expect from normal data packs, where the newest value (which comes from the highest datapack) always wins. +The default merger (`DataMapValueMerger#defaultMerger`) has the typical behaviour you'd expect from normal data packs, where the newest value (which comes from the highest datapack) overwrites the previous value. ### Removers An advanced data map can provide a `DataMapValueRemover` through `AdvancedDataMapType#remover`. The remover will allow selective removals of data map values, effectively decomposition. @@ -162,7 +162,7 @@ That file will attach the map `[somekey1=value1, somekey2=value2]` to the `minec After the second datapack is read and applied, the new value attached to the `minecraft:carrot` item will be `[somekey2=value2]`. ## Datagen -Data maps can be generated through `DataMapProvider`. +Data maps can be [generated](../datagen) through `DataMapProvider`. You should extend that class, and then override the `generate` method to create your entries, similar to tag generation. Considering the drop healing example from the start, we could generate some values as follows: diff --git a/docs/datamaps/neo_maps.md b/docs/datamaps/neo_maps.md index a97b04810..21f15ee39 100644 --- a/docs/datamaps/neo_maps.md +++ b/docs/datamaps/neo_maps.md @@ -1,6 +1,6 @@ # Built-in Data Maps NeoForge provides a few data maps that mostly replace hardcoded in-code vanilla maps. -These dats maps can be found in `NeoForgeDataMaps`. +These dats maps can be found in `NeoForgeDataMaps`, and are always *optional* to ensure compatibility with vanilla clients. ## `neoforge:compostables` NeoForge provides a data map that allows configuring composter values, as a replacement for `ComposterBlock#COMPOSTABLES` (which is now ignored). diff --git a/docs/datamaps/structure.md b/docs/datamaps/structure.md index 0cda7370d..431b6a4c0 100644 --- a/docs/datamaps/structure.md +++ b/docs/datamaps/structure.md @@ -2,7 +2,7 @@ For the purposes of this page, we will use a data map which is an object with two float keys: `amount` and `chance` as an example. The codec for that object can be found [here](./index.md#registration). ## Location -Data maps are loaded from a JSON file located at `:mapNamespace/data_maps/:registryNamespace/:registryPath/:mapPath.json`, where: +Data maps are loaded from a JSON file located at `mapNamespace/data_maps/registryNamespace/registryPath/mapPath.json`, where: - `mapNamespace` is the namespace of the ID of the data map - `mapPath` is the path of the ID of the data map - `registryNamespace` is the namespace of the ID of the registry From 6c040b9cefd6aa9c0d95b32e5ec339a33641995c Mon Sep 17 00:00:00 2001 From: Matyrobbrt <65940752+Matyrobbrt@users.noreply.github.com> Date: Mon, 29 Jan 2024 09:44:22 +0200 Subject: [PATCH 12/16] Update docs/datamaps/index.md Co-authored-by: Dennis C --- docs/datamaps/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/datamaps/index.md b/docs/datamaps/index.md index 9e9b6a800..27e18a736 100644 --- a/docs/datamaps/index.md +++ b/docs/datamaps/index.md @@ -46,7 +46,7 @@ and then registered to the `RegisterDataMapTypesEvent` using `RegisterDataMapTyp ## Syncing A synced data map will have its values synced to clients. A data map can be marked as synced using `DataMapType$Builder#synced(Codec networkCodec, boolean mandatory)`. The values of the data map will then be synced using the `networkCodec`. -If the `mandatory` flag is set to `true`, clients that do not support the data map (including Vanilla clients) will not be able to connect to the server, nor vice-versa. A non-mandatory data map is, on the other side, optional, so it will not prevent any clients from joining. +If the `mandatory` flag is set to `true`, clients that do not support the data map (including Vanilla clients) will not be able to connect to the server, nor vice-versa. A non-mandatory data map on the other hand is optional, so it will not prevent any clients from joining. :::tip A separate network codec allows for packet sizes to be smaller, as you can choose what data to send, and in what format. Otherwise the default codec can be used. From b40d8f8fbdb75d5c4bf1f5c78d30a899b4223d50 Mon Sep 17 00:00:00 2001 From: Matyrobbrt <65940752+Matyrobbrt@users.noreply.github.com> Date: Mon, 29 Jan 2024 09:44:56 +0200 Subject: [PATCH 13/16] Update docs/datamaps/index.md Co-authored-by: Dennis C --- docs/datamaps/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/datamaps/index.md b/docs/datamaps/index.md index 27e18a736..37d92c410 100644 --- a/docs/datamaps/index.md +++ b/docs/datamaps/index.md @@ -87,7 +87,7 @@ public static void onItemDrop(final ItemTossEvent event) { ``` ## Advanced data maps -Advanced data maps are data maps which have added functionality. Namely, the ability of merging values and selectively removing them, through a remover. Implementing some form of merging and removers is highly recommended for data maps whose values are collection-likes (like `Map`s or `List`s). +Advanced data maps are data maps which have additional functionality. Namely, the ability of merging values and selectively removing them, through a remover. Implementing some form of merging and removers is highly recommended for data maps whose values are collection-likes (like `Map`s or `List`s). `AdvancedDataMapType` have one more generic besides `T` and `R`: `VR extends DataMapValueRemover`. This additional generic allows you to datagen remove objects with increased type safety. From 7674799d1238aefe08b2f706ac304cd6c5377df7 Mon Sep 17 00:00:00 2001 From: Matyrobbrt <65940752+Matyrobbrt@users.noreply.github.com> Date: Mon, 29 Jan 2024 09:45:16 +0200 Subject: [PATCH 14/16] Update docs/datamaps/index.md Co-authored-by: Dennis C --- docs/datamaps/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/datamaps/index.md b/docs/datamaps/index.md index 37d92c410..209ca9b25 100644 --- a/docs/datamaps/index.md +++ b/docs/datamaps/index.md @@ -111,7 +111,7 @@ public class IntMerger implements DataMapValueMerger { ``` The above merger will merge the values if two datapacks attach to the same object. So if the first pack attaches the value `12` to `minecraft:carrot`, and the second pack attaches the value `15` to `minecraft:carrot`, the final value will be `27`. However, if the second pack specifies the object-level `replace` field, the final value will be `15` as the merger won't be invoked. -We provide some default mergers for merging lists, sets and maps in `DataMapValueMerger`. +NeoForge provides some default mergers for merging lists, sets and maps in `DataMapValueMerger`. The default merger (`DataMapValueMerger#defaultMerger`) has the typical behaviour you'd expect from normal data packs, where the newest value (which comes from the highest datapack) overwrites the previous value. From ab01b2394c73e9771932beacdab7a2f03645b2e4 Mon Sep 17 00:00:00 2001 From: Matyrobbrt <65940752+Matyrobbrt@users.noreply.github.com> Date: Mon, 29 Jan 2024 09:45:49 +0200 Subject: [PATCH 15/16] Update docs/datamaps/neo_maps.md Co-authored-by: Dennis C --- docs/datamaps/neo_maps.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/datamaps/neo_maps.md b/docs/datamaps/neo_maps.md index 21f15ee39..6ce2d6a9d 100644 --- a/docs/datamaps/neo_maps.md +++ b/docs/datamaps/neo_maps.md @@ -1,6 +1,6 @@ # Built-in Data Maps NeoForge provides a few data maps that mostly replace hardcoded in-code vanilla maps. -These dats maps can be found in `NeoForgeDataMaps`, and are always *optional* to ensure compatibility with vanilla clients. +These data maps can be found in `NeoForgeDataMaps`, and are always *optional* to ensure compatibility with vanilla clients. ## `neoforge:compostables` NeoForge provides a data map that allows configuring composter values, as a replacement for `ComposterBlock#COMPOSTABLES` (which is now ignored). From 7657202ee331ba9b4fe5f16e91dcc41c9afc07ce Mon Sep 17 00:00:00 2001 From: Matyrobbrt <65940752+Matyrobbrt@users.noreply.github.com> Date: Mon, 29 Jan 2024 09:47:58 +0200 Subject: [PATCH 16/16] Update index.md --- docs/datamaps/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/datamaps/index.md b/docs/datamaps/index.md index 209ca9b25..3886d4ff6 100644 --- a/docs/datamaps/index.md +++ b/docs/datamaps/index.md @@ -56,7 +56,7 @@ A separate network codec allows for packet sizes to be smaller, as you can choos Data maps are loaded from a JSON file located at `mapNamespace/data_maps/registryNamespace/registryPath/mapPath.json`, where: - `mapNamespace` is the namespace of the ID of the data map - `mapPath` is the path of the ID of the data map -- `registryNamespace` is the namespace of the ID of the registry +- `registryNamespace` is the namespace of the ID of the registry; if the namespace is `minecraft`, this value will be omitted - `registryPath` is the path of the ID of the registry For more information, please [check out the dedicated page](./structure.md).