Skip to content

Commit

Permalink
feat(docs): Update server section
Browse files Browse the repository at this point in the history
  • Loading branch information
ChampionAsh5357 committed Oct 18, 2024
1 parent 45ee798 commit 365d215
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 77 deletions.
83 changes: 45 additions & 38 deletions docs/resources/server/advancements.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<HolderLookup.Provider> 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<HolderLookup.Provider> 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<HolderLookup.Provider> 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<AdvancementHolder> saver, ExistingFileHelper existingFileHelper) {
// Generate your advancements here.
}
@Override
public void generate(HolderLookup.Provider registries, Consumer<AdvancementHolder> saver, ExistingFileHelper existingFileHelper) {
// Generate your advancements here.
}
}

// Method Example, assume in ExampleClass
public static void generateExampleAdvancements(HolderLookup.Provider registries, Consumer<AdvancementHolder> 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`:
Expand Down Expand Up @@ -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"))
);
Expand All @@ -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<HolderLookup.Provider> 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
Expand Down
4 changes: 2 additions & 2 deletions docs/resources/server/conditions.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<EntityType<?>> tag) implements ICondition {
public static final MapCodec<EntityTagEmptyCondition> 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
Expand Down
13 changes: 7 additions & 6 deletions docs/resources/server/damagetypes.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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);
}
```
Expand Down Expand Up @@ -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<HolderLookup.Provider> lookupProvider = event.getLookupProvider();
event.getGenerator().addProvider(
CompletableFuture<HolderLookup.Provider> 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 -> {
Expand All @@ -121,7 +120,9 @@ public static void onGatherData(GatherDataEvent event) {
.add(...),
Set.of(ExampleMod.MOD_ID)
)
);
).getRegistryProvider();

// ...
}
```

Expand Down
36 changes: 5 additions & 31 deletions docs/resources/server/tags.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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<Block> 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<Block> blocksInTag = null;

public static Set<Block> 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<Holder<Block>> blocksInTag = BuiltInRegistries.BLOCK.getOrThrow(MY_TAG).stream();
```

## Datagen
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -253,7 +227,7 @@ public static void gatherData(GatherDataEvent event) {

```java
// In an ItemTagsProvider's #addTags method, assuming types TagKey<Block> and TagKey<Item> for the two parameters.
copy(EXAMPLE_BLOCK_TAG, EXAMPLE_ITEM_TAG);
this.copy(EXAMPLE_BLOCK_TAG, EXAMPLE_ITEM_TAG);
```

### Custom Tag Providers
Expand Down

1 comment on commit 365d215

@neoforged-pages-deployments
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploying with Cloudflare Pages

Name Result
Last commit: 365d2157c236353a130fb2b229bf7fbf49760da0
Status: ✅ Deploy successful!
Preview URL: https://cd28be7f.neoforged-docs-previews.pages.dev
PR Preview URL: https://pr-178.neoforged-docs-previews.pages.dev

Please sign in to comment.