Line 1: |
Line 1: |
− | {{Under construction}}
| + | ==The LootTableProvider class== |
− | Loot Tables are not so polished like the other Providers. You need need to do some work to get them to a good state.
| + | First you would need a new class that extends <code>LootTableProvider</code>. In this class you will override the <code>getTables</code> and <code>validate</code> methods. |
| + | You can optionally override <code>getName</code> to return a name for the logs, if you have multiple providers and don't want to simply show up as "LootTables". |
| | | |
− | ==Preperation==
| + | None of the methods you will be overriding (either here or, where applicable, on the subproviders) should call the superclass methods - these superclass methods implement the vanilla data generation. |
− | First you would need a new Class that extend the <code>LootTableProvider</code>. In this class you would override the <code>run</code> and optionally <code>getName</code> Method.
| |
− | In the <code>getName</code> you just return the Name shown in the Logs.
| |
− | Also you should create a abstract Method which you would override in subclasses but this is not strictly needed.
| |
− | Also you need a Gson constant. You would create it like this <code> private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); </code>
| |
− | You should also save the <code>DataGenerator</code> for later use since you can't access from the <code>LootTableProvider</code>.
| |
− | Also you would need two Maps, one with the the Class which is used to get the Lootable Resource Location in this example the Block and one the Loot Table Builder.
| |
− | The second Map consist of a <code>ResourceLocation</code> and the actual <code>LootTable</code>. Look at the Code for more info.
| |
| | | |
| + | The <code>getTables</code> method will return a list of subproviders (explained further in the next section), the function signature looks a bit overwhelming but the actual method body is simple. Each subprovider will typically be a class, and you can simply use their constructors here. For more advanced scenarios (e.g. if you want to organize subproviders for multiple parameter sets into a single class), you can use a lambda that returns another lambda or a method reference. |
| <syntaxhighlight lang="java"> | | <syntaxhighlight lang="java"> |
− | private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
| + | @Override |
| + | protected List<Pair<Supplier<Consumer<BiConsumer<ResourceLocation, LootTable.Builder>>>, LootContextParamSet>> getTables() { |
| + | return List.of( |
| + | Pair.of(MyBlockLoot::new, LootContextParamSets.BLOCK), |
| + | Pair.of(MyEntityLoot::new, LootContextParamSets.ENTITY), |
| + | Pair.of(MyChestLoot::new, LootContextParamSets.CHEST)); |
| + | } |
| + | </syntaxhighlight> |
| | | |
− | protected final Map<Block, LootTable.Builder> lootTables = new HashMap<>();
| + | The <code>validate</code> method needs to be overriden to eliminate the part of the vanilla code that verifies that the vanilla special loot tables have all been defined. If you have special loot tables (i.e. any loot table other than the main loot table for a block or entity), you may wish to add similar verification code here. The remaining code ensures that each table is consistent with the associated parameter set (e.g. that you're not trying to access a block state in an entity loot table). |
− | public static Map<ResourceLocation, LootTable> tables = new HashMap<>();
| + | <syntaxhighlight lang="java"> |
− | protected final DataGenerator generator;
| + | @Override |
| + | protected void validate(Map<ResourceLocation, LootTable> map, ValidationContext validationtracker) { |
| + | map.forEach((location, lootTable) -> LootTables.validate(validationtracker, location, lootTable); |
| + | } |
| </syntaxhighlight> | | </syntaxhighlight> |
| | | |
− | Also you would need a Function to save the Tables
| + | ==Subproviders== |
| + | Each subprovider is a <code>Consumer<BiConsumer<ResourceLocation, LootTable.Builder>></code>. This is a fancy name that means that it has an accept function that is called with an argument containing another function that it can call with each loot table it wants to create. |
| + | |
| + | Each vanilla subprovider is a class. BlockLoot and EntityLoot contain helper methods to ensure that every block or entity has a primary loot table and to help generate certain types of loot tables. BlockLoot in particular has a large collection of helper functions that are extremely useful for implementing standard forms of loot tables for block types such as ores, doors, slabs, crops, or any block that simply always drops itself as an item. |
| + | |
| + | ===Extending BlockLoot or EntityLoot=== |
| + | If you are extending BlockLoot or EntityLoot, you will need to override the <code>addTables</code> and <code>getKnownBlocks</code> or <code>getKnownEntities</code> methods. From the <code>addTables</code> methods you will call the <code>add</code> method for each of the tables you wish to create (a handful of the helper functions in BlockLoot including <code>dropSelf</code> automatically add the tables they create). |
| + | |
| <syntaxhighlight lang="java"> | | <syntaxhighlight lang="java"> |
− | private void writeTables(DirectoryCache cache, Map<ResourceLocation, LootTable> tables) {
| + | @Override |
− | Path outputFolder = this.generator.getOutputFolder(); | + | protected void addTables() { |
− | tables.forEach((key, lootTable) -> { | + | dropSelf(ModBlocks.BLOCK1.get()); |
− | Path path = outputFolder.resolve("data/" + key.getNamespace() + "/loot_tables/" + key.getPath() + ".json");
| + | add(ModBlocks.BLOCK2.get(), ...); |
− | try {
| + | } |
− | IDataProvider.save(GSON, cache, LootTableManager.serialize(lootTable), path); | |
− | } catch (IOException e) {
| |
− | LOGGER.error("Couldn't write loot table {}", path, (Object) e);
| |
− | }
| |
− | }); | |
− | }
| |
| </syntaxhighlight> | | </syntaxhighlight> |
| | | |
− | For the writeTables method you would need to convert the First Map to the second map, for this we need to iterate over the first map. | + | The second parameter to <code>BlockLoot.add</code> may be a function that accepts the Block and returns a LootTable.Builder, or the LootTable.Builder itself. Many of the helper functions in BlockLoot work this way. For <code>EntityLoot.add</code>, it must simply be the LootTable.Builder, but the first argument may be a ResourceLocation to create a special loot table rather than the primary loot table for the entity (this is used by vanilla for sheep colors). |
| + | |
| + | You must override <code>getKnownBlocks</code> or <code>getKnownEntities</code>, respectively, to return only the blocks or entity types registered by your mod. |
| <syntaxhighlight lang="java"> | | <syntaxhighlight lang="java"> |
− | lootTables.forEach(blockBuilderMap -> { | + | @Override |
− | for (Map.Entry<Block, LootTable.Builder> entry : blockBuilderMap.entrySet()) {
| + | protected Iterable<Block> getKnownBlocks() { |
− | tables.put(entry.getKey().getLootTable(), entry.getValue().build()); | + | return ModBlocks.BLOCKS.getEntries().stream().map(RegistryObject::get).toList(); |
− | }
| + | } |
− | }); | |
| </syntaxhighlight> | | </syntaxhighlight> |
| | | |
− | in the <code>run</code> method you would then first call the method where you create the tables or just create them in there (if you do you can ignore the next section), then you would convert the Tables and at last you would save the loottables.
| + | ===Implementing a simple subprovider=== |
| + | The other vanilla subproviders don't have much worth extending for, so you can simply directly implement <code>Consumer<BiConsumer<ResourceLocation, LootTable.Builder>></code>. |
| + | <syntaxhighlight lang="java"> |
| + | public void accept(BiConsumer<ResourceLocation, LootTable.Builder> consumer) { |
| + | consumer.accept({ResourceLocation}, {LootTable.Builder}); |
| + | } |
| + | </syntaxhighlight> |
| | | |
− | == The actual Class for Lootables ==
| + | ===The LootTable Builder=== |
− | === Another class (Optional) ===
| + | This is where you actually make a loot table. You start with an empty <code>LootPool.Builder</code> and add the necessary attributes to it. |
− | Create a new class that extends from the Class you created in the Section above and override the abstract function in there you can begin to create your Lootables.
| |
− | | |
− | ===The LootPool Builder=== | |
− | This is where you actually make a loottable. You start with an empty<code>LootPool</code>and add the necessary attributes to it. | |
| * <code>.name</code> is used for the pool name. | | * <code>.name</code> is used for the pool name. |
| * <code>.setRolls</code> is used for the amount. | | * <code>.setRolls</code> is used for the amount. |
− | * <code>.add</code> is used to add an <code>ItemLootEntry</code>. | + | * <code>.add</code> is used to add an <code>LootPoolEntryContainer$Builder</code>. You can have multiple entries. |
| + | |
| + | A <code>LootPoolEntryContainer$Builder</code> defines what entry container is returned, and which functions and/or conditions are applied (see [https://minecraft.fandom.com/wiki/Loot_table Loot table] for the vanilla functions and conditions). |
| + | * <code>.when</code> is used to apply a condition. These themselves can have multiple operations. |
| | | |
− | An <code>ItemLootEntry</code> defines what is returned, and which functions and/or conditions are applied (see [https://minecraft.fandom.com/wiki/Loot_table Loot table] for the vanilla functions and conditions).
| + | After all attributes have been added the <code>LootPool.Builder</code> can be used to make a <code>LootTable.Builder</code>. This builder can then be used in the subproviders as discussed above. |
− | * <code>.lootTableItem</code> is used to define which item is returned.
| |
− | * <code>.apply</code> is used to apply a function or condition
| |
| | | |
− | After all attributes have been added the <code>LootPool.Builder</code> can be used to make a <code>LootTable.Builder</code> of the proper <code>LootParameterSets</code>. This builder can then be added to the <code>lootTables</code> map made in the previous section (in the overwritten abstract method).
| + | The <code>LootContextParamSet</code> is automatically applied by the vanilla <code>LootTableProvider.run</code> method. |
| <syntaxhighlight lang="java"> | | <syntaxhighlight lang="java"> |
− | LootPool.Builder builder = LootPool.lootPool() | + | LootPool.Builder poolBuilder = LootPool.lootPool() |
− | .name(...)
| + | .name(...) |
− | .setRolls(...)
| + | .setRolls(...) |
− | .add(ItemLootEntry.lootTableItem(...)
| + | .add(LootItem.lootTableItem(...) |
− | .apply(Function1)
| + | .apply(function1) |
− | .apply(Function2
| + | .apply(function2) |
− | .options(option1)
| + | .when(condition1) |
− | .options(option2))
| + | .when(condition2)) |
− | );
| + | ); |
− | LootTable.lootTable().withPool(builder).setParamSet(...);
| + | LootTable.Builder tableBuilder = LootTable.lootTable().withPool(poolBuilder); |
| </syntaxhighlight>[[Category:Data Generation]] | | </syntaxhighlight>[[Category:Data Generation]] |