diff --git a/docs/resources/server/advancements.md b/docs/resources/server/advancements.md index 38e6de13..e9699b82 100644 --- a/docs/resources/server/advancements.md +++ b/docs/resources/server/advancements.md @@ -158,35 +158,60 @@ Advancements can be [datagenned][datagen] using an `AdvancementProvider`. An `Ad Both Minecraft and NeoForge provide a class named `AdvancementProvider`, located at `net.minecraft.data.advancements.AdvancementProvider` and `net.neoforged.neoforge.common.data.AdvancementProvider`, respectively. The NeoForge class is an improvement on the one Minecraft provides, and should always be used in favor of the Minecraft one. The following documentation always assumes usage of the NeoForge `AdvancementProvider` class. ::: -To start, create a subclass of `AdvancementProvider`: +To start, create an instance of `AdvancementProvider` within `GatherDataEvent`: ```java -public class MyAdvancementProvider extends AdvancementProvider { - // Parameters can be obtained from GatherDataEvent. - public MyAdvancementProvider(PackOutput output, - CompletableFuture lookupProvider, ExistingFileHelper existingFileHelper) { - super(output, lookupProvider, existingFileHelper, List.of()); - } +@SubscribeEvent +public static void gatherData(GatherDataEvent event) { + DataGenerator generator = event.getGenerator(); + PackOutput output = generator.getPackOutput(); + CompletableFuture lookupProvider = event.getLookupProvider(); + ExistingFileHelper existingFileHelper = event.getExistingFileHelper(); + + generator.addProvider( + event.includeServer(), + new AdvancementProvider( + output, lookupProvider, existingFileHelper, + // Add generators here + List.of(...) + ) + ); + + // Other providers } ``` -Now, the next step is to fill the list with our generators. To do so, we add one or more generators as static classes and then add an instance of each of them to the currently empty list in the constructor parameter. +Now, the next step is to fill the list with our generators. To do so, we can either add generators as classes or lambdas, and then add an instance of each of them to the currently empty list in the constructor parameter. ```java -public class MyAdvancementProvider extends AdvancementProvider { - public MyAdvancementProvider(PackOutput output, CompletableFuture lookupProvider, ExistingFileHelper existingFileHelper) { - // Add an instance of our generator to the list parameter. This can be done as many times as you want. - // Having multiple generators is purely for organization, all functionality can be achieved with a single generator. - super(output, lookupProvider, existingFileHelper, List.of(new MyAdvancementGenerator())); - } +// Class example +public class MyAdvancementGenerator extends AdvancementProvider.AdvancementGenerator { - private static final class MyAdvancementGenerator implements AdvancementProvider.AdvancementGenerator { - @Override - public void generate(HolderLookup.Provider registries, Consumer saver, ExistingFileHelper existingFileHelper) { - // Generate your advancements here. - } + @Override + public void generate(HolderLookup.Provider registries, Consumer saver, ExistingFileHelper existingFileHelper) { + // Generate your advancements here. } } + +// Method Example, assume in ExampleClass +public static void generateExampleAdvancements(HolderLookup.Provider registries, Consumer saver, ExistingFileHelper existingFileHelper) { + // Generate your advancements here. +} + +// In GatherDataEvent +generator.addProvider( + event.includeServer(), + new AdvancementProvider( + output, lookupProvider, existingFileHelper, + // Add generators here + List.of( + // Add an instance of our generator to the list parameter. This can be done as many times as you want. + // Having multiple generators is purely for organization, all functionality can be achieved with a single generator. + new MyAdvancementGenerator(), + ExampleClass::generateExampleAdvancements + ) + ) +); ``` To generate an advancement, you want to use an `Advancement.Builder`: @@ -233,7 +258,7 @@ builder.rewards( // Alternatively, use loot() to create a new builder. .addLootTable(ResourceKey.create(Registries.LOOT_TABLE, ResourceLocation.fromNamespaceAndPath("minecraft", "chests/igloo"))) // Alternatively, use recipe() to create a new builder. - .addRecipe(ResourceLocation.fromNamespaceAndPath("minecraft", "iron_ingot")) + .addRecipe(ResourceKey.create(Registries.RECIPE, ResourceLocation.fromNamespaceAndPath("minecraft", "iron_ingot"))) // Alternatively, use function() to create a new builder. .runs(ResourceLocation.fromNamespaceAndPath("examplemod", "example_function")) ); @@ -250,24 +275,6 @@ builder.requirements(AdvancementRequirements.allOf(List.of("pickup_dirt"))); builder.save(saver, ResourceLocation.fromNamespaceAndPath("examplemod", "example_advancement"), existingFileHelper); ``` -Of course, don't forget to add your provider to the `GatherDataEvent`: - -```java -@SubscribeEvent -public static void gatherData(GatherDataEvent event) { - DataGenerator generator = event.getGenerator(); - PackOutput output = generator.getPackOutput(); - CompletableFuture lookupProvider = event.getLookupProvider(); - ExistingFileHelper existingFileHelper = event.getExistingFileHelper(); - - // other providers here - generator.addProvider( - event.includeServer(), - new MyAdvancementProvider(output, lookupProvider, existingFileHelper) - ); -} -``` - [codec]: ../../datastorage/codecs.md [conditions]: conditions.md [datagen]: ../index.md#data-generation diff --git a/docs/resources/server/conditions.md b/docs/resources/server/conditions.md index 139b4354..bc00f36b 100644 --- a/docs/resources/server/conditions.md +++ b/docs/resources/server/conditions.md @@ -142,12 +142,12 @@ For example, let's assume we want to reimplement the `tag_empty` condition, but // This class is basically a boiled-down copy of TagEmptyCondition, adjusted for entity types instead of items. public record EntityTagEmptyCondition(TagKey> tag) implements ICondition { public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(inst -> inst.group( - ResourceLocation.CODEC.xmap(rl -> TagKey.create(Registries.ENTITY_TYPES, rl), TagKey::location).fieldOf("tag").forGetter(EntityTagEmptyCondition::tag) + TagKey.codec(Registries.ENTITY_TYPE).fieldOf("tag").forGetter(EntityTagEmptyCondition::tag) ).apply(inst, EntityTagEmptyCondition::new)); @Override public boolean test(ICondition.IContext context) { - return context.getTag(this.tag()).isEmpty(); + return !context.isTagLoaded(this.tag()); } @Override diff --git a/docs/resources/server/damagetypes.md b/docs/resources/server/damagetypes.md index 28bef462..19b4fd13 100644 --- a/docs/resources/server/damagetypes.md +++ b/docs/resources/server/damagetypes.md @@ -50,7 +50,7 @@ The same format is also used for vanilla's damage types, and pack developers can ```java DamageSource damageSource = new DamageSource( // The damage type holder to use. Query from the registry. This is the only required parameter. - registryAccess.registryOrThrow(Registries.DAMAGE_TYPE).getHolderOrThrow(EXAMPLE_DAMAGE), + registryAccess.lookupOrThrow(Registries.DAMAGE_TYPE).getOrThrow(EXAMPLE_DAMAGE), // The direct entity. For example, if a skeleton shot you, the skeleton would be the causing entity // (= the parameter above), and the arrow would be the direct entity (= this parameter). Similar to // the causing entity, this isn't always applicable and therefore nullable. Optional, defaults to null. @@ -73,7 +73,7 @@ If `DamageSource`s have no entity or position context whatsoever, it makes sense ```java public static DamageSource exampleDamage(Entity causer) { return new DamageSource( - causer.level().registryAccess().registryOrThrow(Registries.DAMAGE_TYPE).getHolderOrThrow(EXAMPLE_DAMAGE), + causer.level().registryAccess().lookupOrThrow(Registries.DAMAGE_TYPE).getOrThrow(EXAMPLE_DAMAGE), causer); } ``` @@ -101,10 +101,9 @@ Damage type JSON files can be [datagenned][datagen]. Since damage types are a da // In your datagen class @SubscribeEvent public static void onGatherData(GatherDataEvent event) { - CompletableFuture lookupProvider = event.getLookupProvider(); - event.getGenerator().addProvider( + CompletableFuture lookupProvider = event.getGenerator().addProvider( event.includeServer(), - output -> new DatapackBuiltinEntriesProvider(output, lookupProvider, new RegistrySetBuilder() + output -> new DatapackBuiltinEntriesProvider(output, event.getLookupProvider(), new RegistrySetBuilder() // Add a datapack builtin entry provider for damage types. If this lambda becomes longer, // this should probably be extracted into a separate method for the sake of readability. .add(Registries.DAMAGE_TYPE, bootstrap -> { @@ -121,7 +120,9 @@ public static void onGatherData(GatherDataEvent event) { .add(...), Set.of(ExampleMod.MOD_ID) ) - ); + ).getRegistryProvider(); + + // ... } ``` diff --git a/docs/resources/server/tags.md b/docs/resources/server/tags.md index ff6f2391..1671445d 100644 --- a/docs/resources/server/tags.md +++ b/docs/resources/server/tags.md @@ -87,7 +87,7 @@ We can then use our tag to perform various operations on it. Let's start with th ```java // Check whether dirt is in our tag. -boolean isInTag = BuiltInRegistries.BLOCK.getOrCreateTag(MY_TAG).stream().anyMatch(e -> e == Items.DIRT); +boolean isInTag = BuiltInRegistries.BLOCK.getOrThrow(MY_TAG).stream().anyMatch(holder -> holder.is(Items.DIRT)); ``` Since this is a very verbose statement, especially when used often, `BlockState` and `ItemStack` - the two most common users of the tag system - each define a `#is` helper method, used like so: @@ -99,36 +99,10 @@ boolean isInBlockTag = blockState.is(MY_TAG); boolean isInItemTag = itemStack.is(MY_ITEM_TAG); ``` -If needed, we can also get ourselves a set of tag entries, like so: +If needed, we can also get ourselves a set of tag entries to stream, like so: ```java -Set blocksInTag = BuiltInRegistries.BLOCK.getOrCreateTag(MY_TAG).stream().toSet(); -``` - -For performance reasons, it is recommended to cache these sets in a field, invalidating them when tags are reloaded (which can be listened for using `TagsUpdatedEvent`). This can be done like so: - -```java -public class MyTagsCacheClass { - private static Set blocksInTag = null; - - public static Set getBlockTagContents() { - if (blocksInTag == null) { - // Wrap as an unmodifiable set, as we're not supposed to modify this anyway - blocksInTag = Collections.unmodifiableSet(BuiltInRegistries.BLOCK.getOrCreateTag(MY_TAG).stream().toSet()); - } - return blocksInTag; - } - - public static void invalidateCache() { - blocksInTag = null; - } -} - -// In an event handler class -@SubscribeEvent -public static void onTagsUpdated(TagsUpdatedEvent event) { - MyTagsCacheClass.invalidateCache(); -} +Stream> blocksInTag = BuiltInRegistries.BLOCK.getOrThrow(MY_TAG).stream(); ``` ## Datagen @@ -169,7 +143,7 @@ public class MyBlockTagsProvider extends BlockTagsProvider { @Override protected void addTags(HolderLookup.Provider lookupProvider) { // Create a tag builder for our tag. This could also be e.g. a vanilla or NeoForge tag. - tag(MY_TAG) + this.tag(MY_TAG) // Add entries. This is a vararg parameter. // Non-intrinsic providers must provide ResourceKeys here instead of the actual objects. .add(Blocks.DIRT, Blocks.COBBLESTONE) @@ -253,7 +227,7 @@ public static void gatherData(GatherDataEvent event) { ```java // In an ItemTagsProvider's #addTags method, assuming types TagKey and TagKey for the two parameters. -copy(EXAMPLE_BLOCK_TAG, EXAMPLE_ITEM_TAG); +this.copy(EXAMPLE_BLOCK_TAG, EXAMPLE_ITEM_TAG); ``` ### Custom Tag Providers