1
0
Fork
You've already forked dapper
0
A data-generation library for NeoForge.
  • Kotlin 100%
2025年07月14日 14:01:26 -05:00
gradle/wrapper Initial commit 2024年11月10日 19:42:23 -06:00
repo/martian/dapper Publish 1.4-beta 2025年07月14日 14:01:26 -05:00
src Add more smelting helpers, clean up code, and document recipe utils 2025年07月14日 14:00:40 -05:00
.gitignore We kinda need that jar 2024年11月10日 20:03:21 -06:00
build.gradle.kts Add more smelting helpers, clean up code, and document recipe utils 2025年07月14日 14:00:40 -05:00
gradle.properties Publish 1.4-beta 2025年07月14日 14:01:26 -05:00
license-mit.txt Initial commit 2024年11月10日 19:42:23 -06:00
license-unlicense.txt Initial commit 2024年11月10日 19:42:23 -06:00
readme.md Add more smelting helpers, clean up code, and document recipe utils 2025年07月14日 14:00:40 -05:00
settings.gradle.kts Initial commit 2024年11月10日 19:42:23 -06:00
todo.md Initial commit 2024年11月10日 19:42:23 -06:00

Dapper 🎩

Dapper is a library intending to abstract a large majority of the NeoForge data generation API.

Instead of going on about what Dapper does, I'll just show some examples of what it looks like:

// Recipe data generation in Java without Dapper// This will look basically the same in Kotlin without Dapperpublicclass MFRecipeProviderextendsRecipeProvider{publicMFRecipeProvider(PackOutputoutput,CompletableFuture<HolderLookup.Provider>registries){super(output,registries);}protectedvoidbuildRecipes(RecipeOutputrecipeOutput,HolderLookup.ProviderholderLookup){SimpleCookingRecipeBuilder.smelting(Ingredient.of(MFItems.RAW_RUBBER.get()),RecipeCategory.MISC,MFItems.RUBBER_INGOT.get(),0.1f,200).unlockedBy("has",has(MFItems.RAW_RUBBER)).save(recipeOutput,id("smelting/raw_rubber_to_rubber_ingot"));SimpleCookingRecipeBuilder.smelting(Ingredient.of(MFTags.Items.RUBBER_INGOT),RecipeCategory.MISC,MFItems.RAW_PLASTIC,0.1f,200).unlockedBy("has",has(MFItems.RUBBER_INGOT)).save(recipeOutput,id("smelting/rubber_ingot_to_raw_plastic"));ShapedRecipeBuilder.shaped(RecipeCategory.MISC,MFItems.PLASTIC_SHEETS,4).pattern("pp").pattern("pp").define('p',MFItems.RAW_PLASTIC).unlockedBy("has",has(MFItems.RAW_PLASTIC)).save(recipeOutput,id("shaped/plastic_sheets"));}}
// With Dapper, this can be changed into this:
class MFRecipeProvider(event: GatherDataEvent) : DapperRecipeProvider(event) {
	override fun buildRecipes() {
		// These parentheses are here to allow us to use infix notation on another line
		(MFItems.RAW_RUBBER smeltsTo MFItems.RUBBER_INGOT
			unlockWith MFItems.RAW_RUBBER
			save "smelting/rubber_ingot_from_raw_rubber".id)
		(MFItems.RUBBER_INGOT smeltsTo MFItems.RAW_PLASTIC
			unlockWith MFItems.RUBBER_INGOT
			save "smelting/raw_plastic_from_rubber_ingot".id)
		(MFItems.PLASTIC_SHEETS.shapedRecipeBuilder(count = 4)
			pattern2x2 MFItems.RAW_PLASTIC
			unlockWith MFItems.RAW_PLASTIC
			save "shaped/plastic_sheets".id)
 
 // Alternatively, you can use Kotlin's .apply:
 MFItems.RAW_RUBBER.smeltsTo(MFItems.RUBBER_INGOT).apply {
 unlockWith(MFItems.RAW_RUBBER)
 save("smelting/rubber_ingot_from_raw_rubber".id)
 }
 MFItems.RUBBER_INGOT.smeltsTo(MFItems.RAW_PLASTIC).apply {
 unlockWith(MFItems.RUBBER_INGOT)
 save("smelting/raw_plastic_from_rubber_ingot".id)
 }
 MFItems.PLASTIC_SHEETS.shapedRecipeBuilder(count = 4).apply {
 pattern2x2(MFItems.RAW_PLASTIC)
 unlockWith(MFItems.RAW_PLASTIC)
 save("shaped/plastic_sheets".id)
 }
	}
}

Usage

Buildscript time :D

Add the following to your buildscript:

repositories {
	// Groovy
	maven {
		url = "https://codeberg.org/EmmaTheMartian/dapper/raw/branch/main/repo/"
	}
	// Kotlin
	maven("https://codeberg.org/EmmaTheMartian/dapper/raw/branch/main/repo/")
}
dependencies {
	// Architectury Loom and ModDevGradle
	implementation("martian:dapper:1.0.0")
	// NeoGradle
	implementation(fg.deobf("martian:dapper:1.0.0"))
}

If your codebase is written in Java, but you want to use Dapper as a Kotlin library, you can do that! Although it takes a good bit more buildscript effort.

To do that in Architectury Loom, expand the details/summary block below

I am unfamiliar with ModDevGradle and NeoGradle, so I do not have a guide for it quite yet.

(this is the details/summary block)

To do this, we will be adding the Kotlin JVM plugin and making a new source-set specifically for data generation. You can also make this source set just be the same one used for generated assets (src/generated/) if you prefer.

// This buildscript is written in Kotlin, but most of it works in Groovy
// without too many changes.

// Add the Kotlin JVM plugin to allow us to compile Kotlin code
plugins {
	id("org.jetbrains.kotlin.jvm") version "2.0.0"
}
repositories {
	// You will see why this is needed once we get to the `dependencies` block.
	maven("https://thedarkcolour.github.io/KotlinForForge/")
}
// Set the Kotlin compiler to target Java 21
kotlin.compilerOptions.jvmTarget = JvmTarget.JVM_21
sourceSets {
	// Make the `main` source set also use the generated resources
	// You may already have this if you have used data generation already
	main {
		resources {
			srcDir("src/generated/resources")
			exclude("*.cache/")
		}
	}
	// Make the `data` source set to contain our new data generation code
	create("data") {
		kotlin {
			srcDirs("src/data/kotlin")
		}
		// This allows us to reference code in `src/main/` from `src/data/`
		compileClasspath += main.get().compileClasspath + main.get().output
		runtimeClasspath += main.get().runtimeClasspath + main.get().output
	}
}
// Tell Kotlin that `src/data/kotlin/` is source that it should compile
tasks.compileKotlin {
	source(sourceSets["data"].kotlin)
}
loom {
	runs {
		// You may already have a run task for data generation, but you will
		// still need to modify it a little bit.
		register("data") {
			data()
			// This is the important line, it tells the data generator to use
			// the `src/data/` source set for its source.
			source(sourceSets["data"])
			programArgs("--all", "--mod", prop("mod_id"),
				"--output", file("src/generated/resources/").absolutePath,
				"--existing", file("src/main/resources/").absolutePath)
		}
	}
}
// Next, we need Kotlin to use Dapper. We could just depend on all the Kotlin
// stdlib manually, but another way it just to simply use KotlinForForge as a
// dependency. This will only be used for data generation, so KFF will not be
// needed for users downloading your mod.
dependencies {
	implementation("thedarkcolour:kotlinforforge-neoforge:${prop("kff_version")}") {
		// https://github.com/thedarkcolour/KotlinForForge/issues/103
		exclude(group = "net.neoforged.fancymodloader", module = "loader")
	}
}
// This isn't needed, but I highly recommend it. This line excludes our `data`
// source set from any jars we build.
tasks.withType<Jar>().configureEach {
 // This should be the package containing your datagen code but with slashes
 // instead of dots, and with a wildcard (asterisk) at the end.
 exclude("com/example/datagen/*")
}

And we are done with the buildscript changes!


Now for the actual code. This is all going to be in Kotlin however you can translate to Java if you would like. However, Dapper is designed with Kotlin in mind so the API may not feel too comfortable to work with in Java.

Dapper makes a lot of helper methods for assorted things, so I will include the imports for those whenever I use them. Unfortunately Kotlin is not able to do wildcard imports of an object ;-;

First we need to make an event bus subscriber for the GatherDataEvent:

@EventBusSubscriber(modid = YourModClass.MODID, bus = EventBusSubscriber.Bus.MOD)
object ModDataGenerator {
	@JvmStatic
	@SubscribeEvent
	fun onGatherData(event: GatherDataEvent) {
		// We will start adding to this once we make our first data provider.
	}
}

Now we can make a provider. We will use a recipe provider as the example.

// The infix functions seen here are all from martian.dapper.api.server.recipe.DapperShapedRecipeUtil

class MFRecipeProvider(event: GatherDataEvent) : DapperRecipeProvider(event) {
	override fun buildRecipes() {
		(Items.COAL smeltsTo Items.DIAMOND
			unlockWith Items.COAL
			save ResourceLocation.fromNamespaceAndPath("modid", "smelting/coal_to_diamond"))
		// ofStack is an infix function at martian.dapper.api.ofStack
		(Items.DIAMOND smeltsTo (Items.EMERALD ofStack 4)
			unlockWith Items.DIAMOND
			save ResourceLocation.fromNamespaceAndPath("modid", "smelting/diamond_to_emeralds"))
		// This is an example straight from one of my own mods
		(MFItems.PLASTIC_SHEETS.shapedRecipeBuilder(count = 4)
			pattern2x2 MFItems.RAW_PLASTIC
			unlockWith MFItems.RAW_PLASTIC
			save "shaped/plastic_sheets".id)
		// An alternate method of writing it is:
		// MFItems.PLASTIC_SHEETS.shapedRecipeBuilder(count = 4).apply {
		// 	pattern2x2(MFItems.RAW_PLASTIC)
		// 	unlockWith(MFItems.RAW_PLASTIC)
		// 	save("shaped/plastic_sheets".id)
		// }
	}
}

For this to actually do anything, we need to add it to our ModDataGenerator object that we made above:

// In `#onGatherData`

// `GatherDataEvent#addProviders` and `DataGenerator#add` are extension
// methods, which can each be found here respectively:
// import martian.minefactorial.datagen.api.add
// import martian.minefactorial.datagen.api.addProviders

event.addProviders(
	server = { it: DataGenerator ->
		it.add(ModRecipeProvider(event))
	}
)

Now we can run the gradle runData to run our brand-new data generator.

License

Dapper is dual-licensed under MIT and the Unlicense.

Some people have a vendetta against crayon licenses regardless of it being just a mod for a block-game. Along with that, I have heard that some countries apparently don't let developers put their code into the public domain, meaning the Unlicense is unusable to them. For these two reasons, Dapper is dual-licensed. Pick whichever one works better for you.

How Dapper got its Name

Data API -> DAPI ("dappy") -> Dapper