Skip to content

Instantly share code, notes, and snippets.

@Commoble
Created November 15, 2019 23:08
Show Gist options
  • Save Commoble/a821e8fe62238c913bd189aaffe7e425 to your computer and use it in GitHub Desktop.
Save Commoble/a821e8fe62238c913bd189aaffe7e425 to your computer and use it in GitHub Desktop.
Autogenerating data jsons in minecraft forge
// generating data jsons from code can take advantage of the java compiler to enforce correctness,
// as well as potentially taking advantage of helper functions and classes to deal with repetitive bits of json (not demonstrated here)
// this is done by subscribing to the GatherDataEvent MOD event
// this refers to a few things from this gist:
// https://gist.github.com/Commoble/407716790fc1d3717b1a3eaac29f7423
// let's say we want to generate this loot table from code:
{
"type": "minecraft:chest",
"pools":
[
{
"rolls":
{
"min": 3,
"max": 10,
"type": "minecraft:uniform"
},
"entries":
[
{
"type": "minecraft:loot_table",
"name": "dungeonfist:subtables/enchanted_weapons",
"weight": 1,
"functions":
[
{
"function": "dungeonfist:apply_functions_if_item_has_tag",
"tag": "dungeonfist:wooden_tools",
"functions":
[
{
"function": "minecraft:set_name",
"name": "Yep, it's wood"
}
]
}
]
}
]
}
]
}
// this event only fires when you launch runData from your IDE or wherever
// the intended use case is that you generate the data ahead-of-time and ship the mod with the generated data
// the launch config generated by the default build.gradle generates them in yourmod/src/generated/resources,
// this folder isn't read by minecraft so you'll need to move them manually or just change the launch config
// if your file path has any spaces in it, you'll need to put quotes around the path in the args as well,
// or it'll ignore everything in the path after the first space and generate them somewhere weird
// if your IDE doesn't auto-refresh when files are added externally, make sure to refresh it after you do this
@SubscribeEvent
public static void onGatherData(GatherDataEvent event)
{
// so anything that is needed by the data gatherer must go here, such as registering custom loot functions/conditions
// in particular, FMLCommonSetupEvent does not fire
// if you registered any loot functions/conditions there, you'll need them here as well
// register custom loot table functions/conditions
LootFunctionManager.registerFunction(new ApplyFunctionsIfItemHasTag.Serializer());
// add loot table generators
event.getGenerator().addProvider(new DungeonLootTableProvider(event.getGenerator()));
}
////////////////
// that event needs a LootTableProvider, so we'll make one of those
////////////////
public class DungeonLootTableProvider extends LootTableProvider
{
// if you're familiar with the top-level property "type" in loot tables
// e.g. "type": "minecraft:chest" or "type": "minecraft:generic"
// this determines what sorts of parameters are available to the loot table for conditions, etc
// and when we generate loot tables, we define them with their parameter sets in this list here
// technically, loot table builders have a method to set it in the builder
// but this seems to be ALWAYS overridden but what we define here
private final List<Pair<Supplier<Consumer<BiConsumer<ResourceLocation, LootTable.Builder>>>, LootParameterSet>> loot_tables =
ImmutableList.of(Pair.of(DungeonChestLootTables::new, LootParameterSets.CHEST));
public DungeonLootTableProvider(DataGenerator dataGenerator)
{
super(dataGenerator);
}
@Override
protected List<Pair<Supplier<Consumer<BiConsumer<ResourceLocation, LootTable.Builder>>>, LootParameterSet>> getTables()
{
return this.loot_tables;
}
@Override
public String getName()
{
// TODO Auto-generated method stub
return DungeonFist.MODID + ":loot_tables";
}
@Override
protected void validate(Map<ResourceLocation, LootTable> map, ValidationResults validationresults)
{
map.forEach((resourceLocation, lootTable) -> {
LootTableManager.func_215302_a(validationresults, resourceLocation, lootTable, map::get);
});
}
}
////////////////
// We need one more thing: that DungeonChestLootTables class we referred to above
// this is where the actual loot table objects are built
// Now, when we autogenerate a loot table that pulls from other loot tables,
// we'll need those other loot tables to have been generated first
// so we'll create a few of them here
////////////////
public class DungeonChestLootTables implements Consumer<BiConsumer<ResourceLocation, LootTable.Builder>>
{
public ResourceLocation TEST_CHEST = new ResourceLocation(DungeonFist.MODID, "chests/test");
// subtable locations
public ResourceLocation ENCHANTED_WEAPONS = new ResourceLocation(DungeonFist.MODID, "subtables/enchanted_weapons");
public ResourceLocation WEAPONS = new ResourceLocation(DungeonFist.MODID, "subtables/weapons");
public ResourceLocation WOODEN_TOOLS_TAG = new ResourceLocation(DungeonFist.MODID, "wooden_tools");
@Override
public void accept(BiConsumer<ResourceLocation, Builder> builderConsumer)
{
builderConsumer.accept(
this.WEAPONS,
LootTable.builder().addLootPool(
LootPool.builder().rolls(ConstantRange.of(1))
.addEntry(ItemLootEntry.builder(Items.IRON_SWORD).weight(40))
.addEntry(ItemLootEntry.builder(Items.IRON_AXE).weight(40))
.addEntry(ItemLootEntry.builder(Items.BOW).weight(11))
.addEntry(ItemLootEntry.builder(Items.GOLDEN_SWORD).weight(1))
.addEntry(ItemLootEntry.builder(Items.GOLDEN_AXE).weight(1))
.addEntry(ItemLootEntry.builder(Items.WOODEN_SWORD).weight(40))
.addEntry(ItemLootEntry.builder(Items.WOODEN_AXE).weight(1))
.addEntry(ItemLootEntry.builder(Items.DIAMOND_SWORD).weight(1))
.addEntry(ItemLootEntry.builder(Items.DIAMOND_AXE).weight(1))
.addEntry(ItemLootEntry.builder(Items.TRIDENT).weight(1))
)
);
builderConsumer.accept(
this.ENCHANTED_WEAPONS,
LootTable.builder().addLootPool(
LootPool.builder().rolls(ConstantRange.of(1)).addEntry(
TableLootEntry.builder(this.WEAPONS).weight(1).acceptFunction(
// unmapped function names: This gets the builder for EnchantWithLevels,
// sets the random level range to between 1 and 5, and enables treasure enchantments
EnchantWithLevels.func_215895_a(RandomValueRange.func_215837_a(1F, 5F)).func_216059_e()
)
).addEntry(
TableLootEntry.builder(this.WEAPONS).weight(1).acceptFunction(
EnchantWithLevels.func_215895_a(RandomValueRange.func_215837_a(10F, 20F)).func_216059_e()
)
)
)
);
builderConsumer.accept(
this.TEST_CHEST,
LootTable.builder().addLootPool(
LootPool.builder().rolls(RandomValueRange.func_215837_a(3F, 10F)).addEntry(
TableLootEntry.builder(this.ENCHANTED_WEAPONS).acceptFunction(
ApplyFunctionsIfItemHasTag.getBuilder(
this.WOODEN_TOOLS_TAG,
ApplyFunctionsIfItemHasTag.getSetNameBuilder(new StringTextComponent("Yep, it's wood"), null).build()
)
)
)
)
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment