User Tools

Site Tools


tutorial:datagen_advancements

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
Last revisionBoth sides next revision
tutorial:datagen_advancements [2022/12/22 11:51] jmanc3tutorial:datagen_advancements [2023/09/29 18:08] – Updated pre-custom criterion section for fabric 1.20.2 jmanc3
Line 5: Line 5:
 ==== Before continuing ==== ==== Before continuing ====
  
-Make sure you've to read the first section of the [[datagen_setup|Getting started with Data Generation]] page, have a class that implements ''DataGenerationEntrypoint'', and know about the gradle task that needs to be called after any change in our data generators.+Make sure you've to read the first section of the [[datagen_setup|Getting started with Data Generation]] page, have a class that implements ''**DataGeneratorEntrypoint**'', and know about the gradle task that needs to be called after any change in our data generators.
  
 ===== Hooking Up the Provider ===== ===== Hooking Up the Provider =====
  
-To begin making custom advancements, we need to hook up an advancement generator to the general data generator. +To begin making custom advancements, we need to hook up an advancement generator to the class which ''**implements DataGeneratorEntrypoint**'' as follows:
  
-Unfortunately there's like three layers of indirection before we can actually start adding some advancements, but we'll go one layer at a time. First things first, in the ''DataGeneration'' file we created earlier, add the following: +<code java>
- +
-<code java [highlight_lines_extra="11"]>+
 import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint; import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint;
 import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator; import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
 +import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
 +import net.fabricmc.fabric.api.datagen.v1.provider.FabricAdvancementProvider;
 +import net.minecraft.advancement.*;
 +import java.util.function.Consumer;
  
-public class DataGeneration implements DataGeneratorEntrypoint {+public class ExampleModDataGenerator implements DataGeneratorEntrypoint {
  
     @Override     @Override
     public void onInitializeDataGenerator(FabricDataGenerator generator) {     public void onInitializeDataGenerator(FabricDataGenerator generator) {
-        /**  +        FabricDataGenerator.Pack pack = generator.createPack();
-        /* Add our advancements generator +
-         **/ +
-        generator.createPack().addProvider(AdvancementsProvider::new);+
  
-        // .. (Your other generators)+        pack.addProvider(AdvancementsProvider::new);
     }     }
-} 
-</code> 
  
-  * It should be noted that the ''DataGeneration'' class we're making is actually not specific to advancements. It is a more general class that is the starting point for [[datagen_loot|loot table generation]], [[datagen_model|model generation]], [[datagen_tags|tag generation]], [[datagen_recipe|recipe generation]], and [[datagen_language|language generation]].+    static class AdvancementsProvider extends FabricAdvancementProvider { 
 +        protected AdvancementsProvider(FabricDataOutput dataGenerator) { 
 +            super(dataGenerator); 
 +        }
  
-We passed the ''addProvider'' function a class (''AdvancementsProvider'') that we haven't made yet, so let's make it. Add a new class ''AdvancementsProvider'' which extends ''FabricAdvancementProvider'' as follows: +        @Override 
- +        public void generateAdvancement(Consumer<AdvancementEntry> consumer) { 
-<code java [highlight_lines_extra="14"]> +            //  
-import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; +            // We will create our custom advancements here... 
-import net.fabricmc.fabric.api.datagen.v1.provider.FabricAdvancementProvider; +            // 
-import net.minecraft.advancement.Advancement; +        }
- +
-import java.util.function.Consumer; +
- +
-public class AdvancementsProvider extends FabricAdvancementProvider { +
-    protected AdvancementsProvider(FabricDataOutput dataGenerator) { +
-        super(dataGenerator); +
-    } +
- +
-    @Override +
-    public void generateAdvancement(Consumer<Advancement> consumer) { +
-        new Advancements().accept(consumer);+
     }     }
 } }
 </code> </code>
  
-You'll once again notice on the highlighted line that we have another class we haven't created yet. +  * It should be noted that the ''**ExampleModDataGenerator**'' class is not specific to advancements. It is also the starting point for [[datagen_loot|loot table generation]], [[datagen_model|model generation]], [[datagen_tags|tag generation]], [[datagen_recipe|recipe generation]], and [[datagen_language|language generation]].
- +
-Here's how the ''Advancements'' class should look, and where we will finally begin writing some custom advancements. +
- +
-<code java> +
-import net.minecraft.advancement.Advancement; +
- +
-import java.util.function.Consumer; +
- +
-public class Advancements implements Consumer<Consumer<Advancement>>+
- +
-    @Override +
-    public void accept(Consumer<Advancement> consumer) { +
-        //  +
-        // We will create our custom advancements here... +
-        // +
-    } +
-+
-</code>+
  
 ===== Simple Advancement ===== ===== Simple Advancement =====
  
-Let's start simple and work our way up to custom criterions. We'll start with an advancement that activates after you pick up your first dirt block, and we're going to add it to our ''Advancements'' class.+Let's start simple and work our way up to custom criterions. We'll start with an advancement that activates after you pick up your first dirt block, and we're going to add it to the function ''**generateAdvancement**'' inside the ''**AdvancementsProvider**'' class we just wrote.
  
 <code java> <code java>
-import net.minecraft.advancement.Advancement; +// ... (Previous imports)
-import net.minecraft.advancement.AdvancementFrame;+
 import net.minecraft.advancement.criterion.InventoryChangedCriterion; import net.minecraft.advancement.criterion.InventoryChangedCriterion;
 import net.minecraft.item.Items; import net.minecraft.item.Items;
Line 86: Line 56:
 import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
  
-import java.util.function.Consumer;+public class ExampleModDataGenerator implements DataGeneratorEntrypoint {
  
-public class Advancements implements Consumer<Consumer<Advancement>> {+    // ... (Rest of the code)
  
-    @Override +    static class AdvancementsProvider extends FabricAdvancementProvider { 
-    public void accept(Consumer<Advancement> consumer) { + 
-        Advancement rootAdvancement = Advancement.Builder.create() +        // ... (Rest of the code) 
-                .display( + 
-                        Items.DIRT, // The display icon +        @Override 
-                        Text.literal("Your First Dirt Block"), // The title +        public void generateAdvancement(Consumer<AdvancementEntry> consumer) { 
-                        Text.literal("Now make a three by three"), // The description +            AdvancementEntry rootAdvancement = Advancement.Builder.create() 
-                        new Identifier("textures/gui/advancements/backgrounds/adventure.png"), // Background image used +                    .display( 
-                        AdvancementFrame.TASK, // Options: TASK, CHALLENGE, GOAL +                            Items.DIRT, // The display icon 
-                        true, // Show toast top right +                            Text.literal("Your First Dirt Block"), // The title 
-                        true, // Announce to chat +                            Text.literal("Now make a three by three"), // The description 
-                        false // Hidden in the advancement tab +                            new Identifier("textures/gui/advancements/backgrounds/adventure.png"), // Background image used 
-                +                            AdvancementFrame.TASK, // Options: TASK, CHALLENGE, GOAL 
-                // The first string used in criterion is the name referenced by other advancements when they want to have 'requirements' +                            true, // Show toast top right 
-                .criterion("got_dirt", InventoryChangedCriterion.Conditions.items(Items.DIRT)) +                            true, // Announce to chat 
-                .build(consumer, "your_mod_id_please_change_me" + "/root");+                            false // Hidden in the advancement tab 
 +                    
 +                    // The first string used in criterion is the name referenced by other advancements when they want to have 'requirements' 
 +                    .criterion("got_dirt", InventoryChangedCriterion.Conditions.items(Items.DIRT)) 
 +                    .build(consumer, "your_mod_id_please_change_me" + "/root"); 
 +        }
     }     }
 } }
 </code> </code>
  
-  * Make sure you change the ''your_mod_id_please_change_me'' string to the name of your mod. (Also leave the "/root" part as is).+  * Make sure you change the ''**your_mod_id_please_change_me**'' string to the name of your mod. (Also leave the "/root" part as is).
  
-I'll explain in more detail what everything means, but if you compile your program now, and jump into a world in minecraft, you'll notice nothing happens. That's because we haven't generated the data. We haven't ran the ''runDatagenClient'' gradle task we created earlier and we need to do that every time we add, modify, or remove one of our custom made advancements, otherwise the change won't be reflected in the game.+I'll explain in more detail what everything means, but if you compile your program now, and jump into a world in minecraft, you'll notice nothing happens. That's because we haven't generated the data. We haven't ran the ''**runDatagen**'' gradle task we created earlier and we need to do that every time we add, modify, or remove one of our custom made advancements, otherwise the change won't be reflected in the game.
  
-So open up your projects root directory on the terminal and run:+If you have a configuration on ''**IntelliJ IDEA**'' that runs the gradle task you can use that, 
 +or you can open your projects root folder on the terminal and run:
  
-<code bash> +<code bash Windows> 
-./gradlew runDatagenClient+gradlew runDatagen 
 +</code> 
 + 
 +<code bash Linux
 +./gradlew runDatagen
 </code> </code>
  
-In the ''generated'' folder we talked about before, you should now see a file ''root.json'' which holds our advancements data. Something like this:+In the ''**src/main/generated/data/minecraft/advancements/yourmodid/**'' folder we talked about before, you should now see a file ''**root.json**'' which holds our advancements data. Something like this:
  
 <code javascript> <code javascript>
Line 162: Line 142:
 </code> </code>
      
-Go ahead and run the game now and see if the advancement works by collecting a dirt block. You should even be able to leave the world, come back, collect another dirt block and notice that there is no re-trigger. If you press ''Escape'' and open up the Advancements tab, you should see our advancement with it's title and description, on it's own tab, separate from the vanilla advancements.+Go ahead and run the game now and see if the advancement works by collecting a dirt block. You should even be able to leave the world, come back, collect another dirt block and notice that there is no re-trigger. If you press ''**Escape**'' and open up the Advancements tab, you should see our advancement with it's title and description, on it's own tab, separate from the vanilla advancements.
  
   * **NOTE:** You have to complete one advancement in the tab group to open it up, otherwise the tab wont show (just in case you were wondering were the vanilla advancements were).   * **NOTE:** You have to complete one advancement in the tab group to open it up, otherwise the tab wont show (just in case you were wondering were the vanilla advancements were).
Line 168: Line 148:
 ===== Advancements Explained ===== ===== Advancements Explained =====
  
-All advancements in minecraft look like that ''root.json'' file we generated. In fact, it is not at all required to write any code to make advancements, as long as your mods blocks, items, weapons, and so on are registered on its given registry ([[blocks#registering_your_block|how that's done for blocks for example]]), you could reference any custom items your mod adds, be it food, or whatever and make advancements with them like if they were vanilla using datapacks. We still recommend you follow this method instead as it's far more durable than writing out advancements by hand.+All advancements in minecraft look like that ''**root.json**'' file we generated. In fact, it is not at all required to write any code to make advancements, as long as your mods blocks, items, weapons, and so on are registered on its given registry ([[blocks#registering_your_block|how that's done for blocks for example]]), you could reference any custom items your mod adds, be it food, or whatever and make advancements with them like if they were vanilla using datapacks. We still recommend you follow this method instead as it's far more durable than writing out advancements by hand.
  
-Let's go through the advancement we created step by step and see the options we have. We start by calling the ''Advancement.Builder.create()'' and assigning it to the variable ''rootAdvancement''. (We'll be making use of this later).+Let's go through the advancement we created step by step and see the options we have. We start by calling the ''**Advancement.Builder.create()**'' and assigning it to the variable ''**rootAdvancement**''. (We'll be making use of this later).
  
 <code java> <code java>
-Advancement rootAdvancement = Advancement.Builder.create()+AdvancementEntry rootAdvancement = Advancement.Builder.create()
 </code> </code>
  
Line 204: Line 184:
 </code> </code>
  
-Then we tell Minecraft when this advancement should be triggered (like after eating an item, or in our case, after a block enters our inventory) calling the ''criterion'' function.+Then we tell Minecraft when this advancement should be triggered (like after eating an item, or in our case, after a block enters our inventory) calling the ''**criterion**'' function.
  
 <code java> <code java>
Line 210: Line 190:
 </code> </code>
  
-The first argument is a name of type ''String''.+The first argument is a name of type ''**String**''.
  
-  * This name is only ever used by ''requirements'' (another property we can add to advancements) which make it so that before an advancement activates, the ''requirements'' (other advancements) need to be fulfilled first. In other words, it mostly doesn't matter what name you give the criterion.+  * This name is only ever used by ''**requirements**'' (another property we can add to advancements) which make it so that before an advancement activates, the ''**requirements**'' (other advancements) need to be fulfilled first. In other words, it mostly doesn't matter what name you give the criterion.
  
-The second argument is the criterion. In our example we use the ''InventoryChangedCriterion'' and we pass it the item we want it to trigger for ''Items.DIRT''. But there are many criterions. The Minecraft Wiki has them listed as "[[https://minecraft.fandom.com/wiki/Advancement/JSON_format|List of triggers]]". But the better reference to use is the Minecraft source itself. (If you haven't generated the source yet, read [[tutorial:setup#generating_minecraft_sources|here]].) You can take a look at the ''net.minecraft.advancement.criterion'' folder where they are all located and see what's already available.+The second argument is the criterion. In our example we use the ''**InventoryChangedCriterion**'' and we pass it the item we want it to trigger for ''**Items.DIRT**''. But there are many criterions. The Minecraft Wiki has them listed as "[[https://minecraft.wiki/w/Advancement/JSON_format#List_of_triggers|List of triggers]]". But the better reference to use is the Minecraft source itself. (If you haven't generated the source yet, read [[tutorial:setup#generating_minecraft_sources|here]].) You can take a look at the ''**net.minecraft.advancement.criterion**'' folder where they are all located and see what's already available.
  
 | PlayerHurtEntityCriterion.class | ImpossibleCriterion.class | Criterion.class | AbstractCriterion.class | VillagerTradeCriterion.class | PlayerHurtEntityCriterion.class | ImpossibleCriterion.class | Criterion.class | AbstractCriterion.class | VillagerTradeCriterion.class
Line 234: Line 214:
 </code> </code>
  
-We pass it the ''consumer'', and set the id of the advancement. +We pass it the ''**consumer**'', and set the id of the advancement. 
  
-  * Make sure you change the ''your_mod_id_please_change_me'' string to the name of your mod. (Also leave the "/root" part as is).+  * Make sure you change the ''**your_mod_id_please_change_me**'' string to the name of your mod. (Also leave the "/root" part as is).
  
-===== One More Simple Example =====+===== One More Example =====
  
 Just to get the hang of it, lets add two more advancements to our example. Just to get the hang of it, lets add two more advancements to our example.
Line 244: Line 224:
  
 <code java> <code java>
-import net.minecraft.advancement.Advancement+package com.example; 
-import net.minecraft.advancement.AdvancementFrame;+ 
 +import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint; 
 +import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator; 
 +import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; 
 +import net.fabricmc.fabric.api.datagen.v1.provider.FabricAdvancementProvider; 
 +import net.minecraft.advancement.*; 
 +import java.util.function.Consumer
 +import net.minecraft.advancement.criterion.ConsumeItemCriterion;
 import net.minecraft.advancement.criterion.InventoryChangedCriterion; import net.minecraft.advancement.criterion.InventoryChangedCriterion;
 import net.minecraft.item.Items; import net.minecraft.item.Items;
Line 251: Line 238:
 import net.minecraft.util.Identifier; import net.minecraft.util.Identifier;
  
-import java.util.function.Consumer;+public class ExampleModDataGenerator implements DataGeneratorEntrypoint {
  
-public class Advancements implements Consumer<Consumer<Advancement>> {+    // ... (Rest of the code)
  
-    @Override +    static class AdvancementsProvider extends FabricAdvancementProvider {
-    public void accept(Consumer<Advancement> consumer) { +
-        Advancement rootAdvancement = Advancement.Builder.create() +
-                .display( +
-                        Items.DIRT, // The display icon +
-                        Text.literal("Your First Dirt Block"), // The title +
-                        Text.literal("Now make a three by three"), // The description +
-                        new Identifier("textures/gui/advancements/backgrounds/adventure.png"), // Background image used +
-                        AdvancementFrame.TASK, // Options: TASK, CHALLENGE, GOAL +
-                        true, // Show toast top right +
-                        true, // Announce to chat +
-                        false // Hidden in the advancement tab +
-                ) +
-                // The first string used in criterion is the name referenced by other advancements when they want to have 'requirements' +
-                .criterion("got_dirt", InventoryChangedCriterion.Conditions.items(Items.DIRT)) +
-                .build(consumer, "your_mod_id_please_change_me" + "/root");+
  
-        Advancement gotOakAdvancement = Advancement.Builder.create().parent(rootAdvancement) +        // ... (Rest of the code)
-                .display( +
-                        Items.OAK_LOG, +
-                        Text.literal("Your First Oak Block"), +
-                        Text.literal("Bare fisted"), +
-                        null, // children to parent advancements don't need a background set +
-                        AdvancementFrame.TASK, +
-                        true, +
-                        true, +
-                        false +
-                ) +
-                .rewards(AdvancementRewards.Builder.experience(1000)) +
-                .criterion("got_wood", InventoryChangedCriterion.Conditions.items(Items.OAK_LOG)) +
-                .build(consumer, "your_mod_id_please_change_me" + "/got_wood");+
  
-        Advancement eatAppleAdvancement = Advancement.Builder.create().parent(rootAdvancement) +        @Override 
-                .display( +        public void generateAdvancement(Consumer<AdvancementEntry> consumer) { 
-                        Items.APPLE, +            AdvancementEntry rootAdvancement = Advancement.Builder.create() 
-                        Text.literal("Apple and Beef"), +                    .display( 
-                        Text.literal("Ate an apple and beef"), +                            Items.DIRT, // The display icon 
-                        null, // children to parent advancements don't need a background set +                            Text.literal("Your First Dirt Block"), // The title 
-                        AdvancementFrame.CHALLENGE, +                            Text.literal("Now make a three by three"), // The description 
-                        true, +                            new Identifier("textures/gui/advancements/backgrounds/adventure.png"), // Background image used 
-                        true, +                            AdvancementFrame.TASK, // Options: TASK, CHALLENGE, GOAL 
-                        false +                            true, // Show toast top right 
-                +                            true, // Announce to chat 
-                .criterion("ate_apple", ConsumeItemCriterion.Conditions.item(Items.APPLE)) +                            false // Hidden in the advancement tab 
-                .criterion("ate_cooked_beef", ConsumeItemCriterion.Conditions.item(Items.COOKED_BEEF)) +                    ) 
-                .build(consumer, "your_mod_id_please_change_me" + "/ate_apple_and_beef");+                    // The first string used in criterion is the name referenced by other advancements when they want to have 'requirements' 
 +                    .criterion("got_dirt", InventoryChangedCriterion.Conditions.items(Items.DIRT)) 
 +                    .build(consumer, "your_mod_id_please_change_me" + "/root"); 
 + 
 +            AdvancementEntry gotOakAdvancement = Advancement.Builder.create().parent(rootAdvancement) 
 +                    .display( 
 +                            Items.OAK_LOG, 
 +                            Text.literal("Your First Log"), 
 +                            Text.literal("Bare fisted"), 
 +                            null, // children to parent advancements don't need a background set 
 +                            AdvancementFrame.TASK, 
 +                            true, 
 +                            true, 
 +                            false 
 +                    ) 
 +                    .rewards(AdvancementRewards.Builder.experience(1000)) 
 +                    .criterion("got_wood", InventoryChangedCriterion.Conditions.items(Items.OAK_LOG)) 
 +                    .build(consumer, "your_mod_id_please_change_me" + "/got_wood"); 
 + 
 +            AdvancementEntry eatAppleAdvancement = Advancement.Builder.create().parent(rootAdvancement) 
 +                    .display( 
 +                            Items.APPLE, 
 +                            Text.literal("Apple and Beef"), 
 +                            Text.literal("Ate an apple and beef"), 
 +                            null, // children to parent advancements don't need a background set 
 +                            AdvancementFrame.CHALLENGE, 
 +                            true, 
 +                            true, 
 +                            false 
 +                    
 +                    .criterion("ate_apple", ConsumeItemCriterion.Conditions.item(Items.APPLE)) 
 +                    .criterion("ate_cooked_beef", ConsumeItemCriterion.Conditions.item(Items.COOKED_BEEF)) 
 +                    .build(consumer, "your_mod_id_please_change_me" + "/ate_apple_and_beef"); 
 +        }
     }     }
 } }
 </code> </code>
  
-Don't forget to generate the data.+Don't forget to generate the data (Run the gradle task).
  
-<code bash> +<code bash Windows> 
-./gradlew runDatagenClient+gradlew runDatagen 
 +</code> 
 + 
 +<code bash Linux
 +./gradlew runDatagen
 </code> </code>
  
-We added another advancement that activates when you get an oak log this time, and which awards one-thousand experience when completed. And another advancements which the player must actually complete two criterions for, before being awarded the advancement. One criterion to eat an apple, and one criterion to eat cooked beef. This was done by just chain linking some method calls. We've also, importantly, put reasonable values for the fields, like calling the criterion that triggers when an apple is eaten 'ate_apple'. Also notice that we kept the "your_mod_id_please_change_me" part the same but changed the second part with the dash:+We added an advancement that activates when you get an oak log, and which awards one-thousand experience when completed. And we added another advancement which the player must actually complete two criterions for, before being awarded the advancement. One criterion to eat an apple, and one criterion to eat cooked beef. This was done by just chain linking some method calls. We've also, importantly, put reasonable values for the fields, like calling the criterion that triggers when an apple is eaten 'ate_apple'. Also notice that we kept the "your_mod_id_please_change_me" part the same but changed the second part with the dash:
  
 <code java> <code java>
Line 324: Line 320:
  
 <code java> <code java>
-Advancement gotOakAdvancement = Advancement.Builder.create().parent(rootAdvancement)+AdvancementEntry gotOakAdvancement = Advancement.Builder.create().parent(rootAdvancement)
  
 // .... // ....
  
-Advancement eatAppleAdvancement = Advancement.Builder.create().parent(rootAdvancement)+AdvancementEntry eatAppleAdvancement = Advancement.Builder.create().parent(rootAdvancement)
 </code> </code>
  
   * If an advancement doesn't have a parent, it creates a new page, and becomes its root.   * If an advancement doesn't have a parent, it creates a new page, and becomes its root.
  
-We also, of course, changed the titles and descriptions, and even the frame for the ''ate_apple_and_beef'' advancement into a challenge type. (Those advancements which print out in purple and make crazy sound effects). One thing to keep in mind is the current root advancement for our mod is not very good. You want it to be something that is almost guaranteed to happen in your mod. For instance some mods make the root advancement triggered by detecting a custom book in the players inventory (a tutorial book basically), and then put the book in the players inventory when they spawn. The root advancement should be basically free, the children should be challenges.+We also, of course, changed the titles and descriptions, and even the frame for the ''**ate_apple_and_beef**'' advancement into a challenge type. (Those advancements which print out in purple and make crazy sound effects). One thing to keep in mind is the current root advancement for our mod is not very good. You want it to be something that is almost guaranteed to happen in your mod. For instance some mods make the root advancement triggered by detecting a custom book in the players inventory (a tutorial book basically), and then put the book in the players inventory when they spawn. The root advancement should be basically free, the children should be challenges.
  
 ===== When To Make a Custom Criterion? ===== ===== When To Make a Custom Criterion? =====
Line 343: Line 339:
 ===== How To Make a Custom Criterion? ===== ===== How To Make a Custom Criterion? =====
  
-Our mod is keeping track of how many jumping jacks a player has done, and we want to make an advancement when they complete one hundred. First thing we got to do is make the ''JumpingJacks'' class which will extend the ''AbstractCriterion<JumpingJacks.Condition>'' class like so:+Our mod is keeping track of how many jumping jacks a player has done, and we want to make an advancement when they complete one hundred. First thing we got to do is make the ''**JumpingJacks**'' class which will extend the ''**AbstractCriterion<JumpingJacks.Condition>**'' class like so:
  
 <code java> <code java>
Line 386: Line 382:
 </code> </code>
  
-You'll notice inside the class, there is another class called ''Condition'' which implements ''AbstractCriterionConditions''. It just calls the super function for now and nothing else. In fact this whole class is basically doing nothing, (other than making an ID). The only function that does anything is the ''trigger'' function which calls the ''trigger'' function which exists in the ''AbstractCriterion'' class we extended, and which we, with no checking against any data, return true always. That means that **any** time this JumpingJacks criterion is triggered, it'll award the player the advancement.+You'll notice inside the class, there is another class called ''**Condition**'' which implements ''**AbstractCriterionConditions**''. It just calls the super function for now and nothing else. In fact this whole class is basically doing nothing, (other than making an ID). The only function that does anything is the ''**trigger**'' function which calls the ''**trigger**'' function which exists in the ''**AbstractCriterion**'' class we extended, and which we, with no checking against any data, return true always. That means that **any** time this JumpingJacks criterion is triggered, it'll award the player the advancement.
  
 Let's create an advancement with it now. Let's create an advancement with it now.
Line 423: Line 419:
  
 <code bash> <code bash>
-./gradlew runDatagenClient+./gradlew runDatagen
 </code> </code>
  
Line 456: Line 452:
 </code> </code>
  
-  * Because we aren't implementing a full blown mod we pretend a function ''checkedPlayerStateAndHesJumpedOneHundredTimes'' exists and returns true when the player has done more than one-hundred jumping jacks.+  * Because we aren't implementing a full blown mod we pretend a function ''**checkedPlayerStateAndHesJumpedOneHundredTimes**'' exists and returns true when the player has done more than one-hundred jumping jacks
 + 
 +  * **NOTE:** You must call ''**Criteria.register**'' with your custom made criterion, or your game won't award the advancements. (And it MUST be done server side, which is why we do this in the ''**ModInitializer**'' class).
  
-If you run your game now (changing that fake function to a simple ''true'' so it compiles), when you log into a world, you should be granted the jumping jack advancement, but because we are using the server join event here to do this, it gives it to you before you load in, which is why you don't get a toast message. If you open up the advancements page in the escape menu, you'll see it was in fact granted.+If you run your game now (changing that fake function to a simple ''**true**'' so it compiles), when you log into a world, you should be granted the jumping jack advancement, but because we are using the server join event here to do this, it gives it to you before you load in, which is why you don't get a toast message. If you open up the advancements page in the escape menu, you'll see it was in fact granted.
  
-The last thing to do is to make a criterion that takes in some data when created and uses it during when the trigger function is called. (Like how the consume item criterion takes an ''Item'', and then only triggers when that specific ''Item'' is consumed).+The last thing to do is to make a criterion that takes in some data when created and uses it during when the trigger function is called. (Like how the consume item criterion takes an ''**Item**'', and then only triggers when that specific ''**Item**'' is consumed).
  
 ===== Criterion with State ===== ===== Criterion with State =====
Line 519: Line 517:
 </code> </code>
  
-Our ''Condition'' class now takes in a string in it's constructor and saves it for later use. The ''Condition'' class also has a new function ''test'' which takes in a string and returns true if it equals it's own ''wandElment'' string. In the ''toJson'' function we convert the ''wandElement'' to json so it can be saved to disk.+Our ''**Condition**'' class now takes in a string in it's constructor and saves it for later use. The ''**Condition**'' class also has a new function ''**test**'' which takes in a string and returns true if it equals it's own ''**wandElment**'' string. In the ''**toJson**'' function we convert the ''**wandElement**'' to json so it can be saved to disk.
  
-You'll also notice that the ''trigger'' function doesn't just return true now, it actually uses the new ''test'' function in the ''Condition'' class to see if the passed in data matches. And in the ''conditionsFromJson'' we convert the saved out ''wandElement'' json back to string.+You'll also notice that the ''**trigger**'' function doesn't just return true now, it actually uses the new ''**test**'' function in the ''**Condition**'' class to see if the passed in data matches. And in the ''**conditionsFromJson**'' we convert the saved out ''**wandElement**'' json back to string.
  
 Now we could write our advancement like so: Now we could write our advancement like so:
tutorial/datagen_advancements.txt · Last modified: 2023/10/02 23:11 by jmanc3