feat(api): package Kotlin bindings for Gradle consumers

This commit is contained in:
Cayden Tisdale 2026-09-05 20:47:08 -04:00
parent 268f2f15d9
commit a4dba0be3d
No known key found for this signature in database
9 changed files with 520 additions and 142 deletions

3
.gitignore vendored
View File

@ -22,6 +22,9 @@
.idea/**/dbnavigator.xml
/build/
/api/build/
/api/tools/
/gradle-plugin/build/
.idea
.kotlin

View File

@ -1,20 +1,22 @@
# Pumpkin Plugin Template for Kotlin
# Pumpkin Plugin API for Kotlin
This repository provides a template to get started making [Pumpkin](https://github.com/Pumpkin-MC/Pumpkin) plugins using WebAssembly (Wasm) components.
This repository builds the Kotlin bindings for [Pumpkin](https://github.com/Pumpkin-MC/Pumpkin) plugins using WebAssembly (Wasm) components.
Unlike many of the other available language bindings, this repo is a template, not a package. You should clone this repository as a starting point.
The `api` Gradle module is a publishable Kotlin/WASI library and generated-source snapshot. It generates the WIT bindings only while API maintainers build or publish the library; the consumer Gradle plugin will unpack that snapshot before compiling a plugin, so plugin authors do not install or run `wit-bindgen`.
## Requirements
The root component template remains available while the consumer Gradle plugin is being moved into `gradle-plugin`.
## API maintainer requirements
Kotlin/Wasm + components it's still in it's early stages, and not as straightforward as could be. You will need to have several things installed for everything to work.
- JDK 17 or later
- To run Gradle 9.4
- Rust
- This is required as a key component (wit-bindgen) is written and Rust must be built from a particular Kotlin-enabled fork.
- This is required only to build or publish the API, because `wit-bindgen` is built from a particular Kotlin-enabled fork.
- You only need a default Rust install for your host platform. NOT for any WebAssembly targets
## Initial setup
## Building the API
Initialize the WIT submodule after cloning:
@ -22,15 +24,28 @@ Initialize the WIT submodule after cloning:
git submodule update --init --recursive
```
Then adjust the project name in `settings.gradle.kts`. The generated component is named after that project.
Publish a local development artifact with:
Run `./gradlew build` to install the pinned build tools, generate the `pumpkin` package bindings, compile the Kotlin/Wasm module, and produce a validated component at `build/<project-name>.wasm`, ready to be installed into Pumpkin. Generated bindings live under `build/generated/`.
```sh
./gradlew :api:publishToMavenLocal
```
Then you can tweak the plugin source in `src/wasmWasiMain/kotlin/plugin/Plugin.kt`.
This generates the `pumpkin` package bindings under `api/build/generated/`, compiles them with the package-owned WIT export bridge, and publishes the KLIB plus a generated-source archive. The source archive includes the pinned WIT snapshot and Preview 1 reactor adapter needed to assemble a component.
## Rebuilding
```kotlin
repositories {
mavenLocal()
mavenCentral()
}
Rebuild with `./gradlew build`. Gradle skips tool installation, binding generation, and component assembly when their declared inputs are unchanged. Use `./gradlew clean` to remove generated build output; it intentionally retains the installed tool cache under `tools/`.
// The consumer Gradle plugin resolves the matching generated-source archive.
```
The API artifact exposes `PumpkinPlugin` and `registerPlugin(...)`; plugin authors should implement and register that class rather than defining the generator-specific `PluginRootFunctionsExportsImpl` or `MetadataImpl` types.
## Root template build
The original root template still builds a complete component with `./gradlew build`. Gradle skips tool installation, binding generation, and component assembly when their declared inputs are unchanged. Use `./gradlew clean` to remove generated build output; it intentionally retains the installed tool cache under `tools/`.
## Updating the WIT

110
api/build.gradle.kts Normal file
View File

@ -0,0 +1,110 @@
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.jvm.tasks.Jar
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
plugins {
alias(libs.plugins.kotlinMultiplatform)
`maven-publish`
}
group = "io.github.pumpkin-mc"
version = providers.gradleProperty("pumpkinApiVersion").getOrElse("0.1.0-dev")
base {
archivesName.set("pumpkin-api-kt")
}
repositories {
mavenCentral()
}
kotlin {
@OptIn(ExperimentalWasmDsl::class)
wasmWasi {
nodejs()
}
}
publishing {
publications.named<MavenPublication>("kotlinMultiplatform") {
artifactId = "pumpkin-api-kt"
}
publications.named<MavenPublication>("wasmWasi") {
artifactId = "pumpkin-api-kt-wasm-wasi"
}
}
val cargoHome = providers.environmentVariable("CARGO_HOME")
.orElse(providers.systemProperty("user.home").map { "$it/.cargo" })
val executableSuffix = if (System.getProperty("os.name").startsWith("Windows", ignoreCase = true)) ".exe" else ""
val cargoExecutable = cargoHome.map { "$it/bin/cargo$executableSuffix" }
val witDirectory = rootProject.layout.projectDirectory.dir("wit/v0.1")
val witBindgenRevision = "700f2db5e1d01f7bee8d756750c6f631171f520e"
val witBindgenDirectory = layout.projectDirectory.dir("tools/wit-bindgen/$witBindgenRevision")
val witBindgen = witBindgenDirectory.file("bin/wit-bindgen$executableSuffix")
val generatedBindings = layout.buildDirectory.dir("generated/wit/wasmWasiMain/kotlin")
val installWitBindgen by tasks.registering(Exec::class) {
group = "build setup"
description = "Installs Pumpkin's pinned Kotlin binding generator."
inputs.property("revision", witBindgenRevision)
outputs.dir(witBindgenDirectory)
commandLine(
cargoExecutable.get(),
"install",
"wit-bindgen-cli",
"--git", "https://github.com/Kotlin/wit-bindgen",
"--rev", witBindgenRevision,
"--locked",
"--root", witBindgenDirectory.asFile.absolutePath,
)
}
val generateWitBindings by tasks.registering(Exec::class) {
group = "build"
description = "Generates Kotlin bindings for the published Pumpkin API."
dependsOn(installWitBindgen)
inputs.dir(witDirectory)
inputs.file(witBindgen)
inputs.property("kotlinPackage", "pumpkin")
inputs.property("kotlinImports", "plugin.*")
outputs.dir(generatedBindings)
doFirst {
val outputDirectory = generatedBindings.get().asFile
check(outputDirectory.deleteRecursively() || !outputDirectory.exists()) {
"Could not remove stale bindings from $outputDirectory"
}
check(outputDirectory.mkdirs() || outputDirectory.isDirectory) {
"Could not create bindings directory $outputDirectory"
}
}
commandLine(
witBindgen.asFile.absolutePath,
"kotlin",
"--kotlin-imports", "plugin.*",
"--kotlin-package-name", "pumpkin",
witDirectory.asFile.absolutePath,
"--out-dir", generatedBindings.get().asFile.absolutePath,
)
}
kotlin {
sourceSets.named("wasmWasiMain") {
kotlin.srcDir(generateWitBindings)
}
}
tasks.named<Jar>("wasmWasiSourcesJar") {
from(rootProject.layout.projectDirectory.dir("wit/v0.1")) {
into("wit/v0.1")
}
from(rootProject.layout.projectDirectory.file("wasi_snapshot_preview1.reactor.wasm")) {
into("wasi")
}
}

View File

@ -0,0 +1,124 @@
package plugin
import pumpkin.Command
import pumpkin.Context
import pumpkin.Event
import pumpkin.Metadata
import pumpkin.PluginRootFunctions
import pumpkin.Server
import pumpkin.World
import pumpkin.runtime.ComponentException
/** The implementation supplied by a Kotlin Pumpkin plugin. */
abstract class PumpkinPlugin {
abstract fun metadata(): PluginMetadata
open fun initPlugin() = Unit
open fun onLoad(context: Context.Context): Result<Unit> = Result.success(Unit)
open fun onUnload(context: Context.Context): Result<Unit> = Result.success(Unit)
open fun handleCommand(
commandId: UInt,
sender: Command.CommandSender,
server: Server.Server,
args: Command.ConsumedArgs,
): Result<Int> = unsupportedCallback("handleCommand")
open fun handleEvent(
eventId: UInt,
server: Server.Server,
event: Event.Event,
): Event.Event = unsupportedCallback("handleEvent")
open fun handleTask(handlerId: UInt, server: Server.Server) =
unsupportedCallback<Unit>("handleTask")
open fun handleIpcMessage(sender: String, message: List<UByte>): Result<List<UByte>> =
Result.failure(ComponentException("This plugin cannot receive messages."))
open fun handleAiGoalCanStart(goalId: UInt, server: Server.Server, entity: World.Entity): Boolean =
unsupportedCallback("handleAiGoalCanStart")
open fun handleAiGoalShouldContinue(goalId: UInt, server: Server.Server, entity: World.Entity): Boolean =
unsupportedCallback("handleAiGoalShouldContinue")
open fun handleAiGoalStart(goalId: UInt, server: Server.Server, entity: World.Entity) =
unsupportedCallback<Unit>("handleAiGoalStart")
open fun handleAiGoalTick(goalId: UInt, server: Server.Server, entity: World.Entity) =
unsupportedCallback<Unit>("handleAiGoalTick")
open fun handleAiGoalStop(goalId: UInt, server: Server.Server, entity: World.Entity) =
unsupportedCallback<Unit>("handleAiGoalStop")
}
typealias PluginMetadata = Metadata.PluginMetadata
private var registeredPlugin: PumpkinPlugin? = null
/** Registers the plugin instance exported by this WebAssembly component. */
fun registerPlugin(plugin: PumpkinPlugin) {
check(registeredPlugin == null) { "A Pumpkin plugin has already been registered." }
registeredPlugin = plugin
}
private fun requirePlugin(): PumpkinPlugin = checkNotNull(registeredPlugin) {
"No Pumpkin plugin has been registered. Call registerPlugin(...) during initialization."
}
private fun <T> unsupportedCallback(name: String): T =
error("PumpkinPlugin.$name has not been implemented.")
/**
* WIT export bridge used by the generated bindings. Plugin authors use [PumpkinPlugin] and
* [registerPlugin] instead of implementing this generated interface directly.
*/
internal class PluginRootFunctionsExportsImpl {
companion object : PluginRootFunctions.Exports {
override fun initPlugin() = requirePlugin().initPlugin()
override fun onLoad(context: Context.Context) = requirePlugin().onLoad(context)
override fun onUnload(context: Context.Context) = requirePlugin().onUnload(context)
override fun handleCommand(
commandId: UInt,
sender: Command.CommandSender,
server: Server.Server,
args: Command.ConsumedArgs,
) = requirePlugin().handleCommand(commandId, sender, server, args)
override fun handleEvent(eventId: UInt, server: Server.Server, event: Event.Event) =
requirePlugin().handleEvent(eventId, server, event)
override fun handleTask(handlerId: UInt, server: Server.Server) =
requirePlugin().handleTask(handlerId, server)
override fun handleIpcMessage(sender: String, message: List<UByte>) =
requirePlugin().handleIpcMessage(sender, message)
override fun handleAiGoalCanStart(goalId: UInt, server: Server.Server, entity: World.Entity) =
requirePlugin().handleAiGoalCanStart(goalId, server, entity)
override fun handleAiGoalShouldContinue(goalId: UInt, server: Server.Server, entity: World.Entity) =
requirePlugin().handleAiGoalShouldContinue(goalId, server, entity)
override fun handleAiGoalStart(goalId: UInt, server: Server.Server, entity: World.Entity) =
requirePlugin().handleAiGoalStart(goalId, server, entity)
override fun handleAiGoalTick(goalId: UInt, server: Server.Server, entity: World.Entity) =
requirePlugin().handleAiGoalTick(goalId, server, entity)
override fun handleAiGoalStop(goalId: UInt, server: Server.Server, entity: World.Entity) =
requirePlugin().handleAiGoalStop(goalId, server, entity)
}
}
/** WIT metadata export bridge used by the generated bindings. */
internal class MetadataImpl {
companion object : Metadata {
override fun getMetadata() = requirePlugin().metadata()
}
}

View File

@ -1,12 +1,16 @@
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
import org.gradle.process.CommandLineArgumentProvider
import org.gradle.api.tasks.Sync
import org.gradle.jvm.tasks.Jar
import org.jetbrains.kotlin.gradle.targets.wasm.binaryen.BinaryenExec
plugins {
alias(libs.plugins.kotlinMultiplatform)
}
evaluationDependsOn(":api")
repositories {
mavenCentral()
}
@ -19,47 +23,18 @@ kotlin {
}
}
val cargoHome = providers.environmentVariable("CARGO_HOME")
.orElse(providers.systemProperty("user.home").map { "$it/.cargo" })
val executableSuffix = if (System.getProperty("os.name").startsWith("Windows", ignoreCase = true)) ".exe" else ""
val cargoExecutable = cargoHome.map { "$it/bin/cargo$executableSuffix" }
val witDirectory = layout.projectDirectory.dir("wit/v0.1")
val reactorAdapter = layout.projectDirectory.file("wasi_snapshot_preview1.reactor.wasm")
val witBindgenRevision = "700f2db5e1d01f7bee8d756750c6f631171f520e"
val witBindgenDirectory = layout.projectDirectory.dir("tools/wit-bindgen/$witBindgenRevision")
val witBindgen = witBindgenDirectory.file("bin/wit-bindgen")
val wasmToolsVersion = "1.258.0"
val wasmToolsDirectory = layout.projectDirectory.dir("tools/wasm-tools/$wasmToolsVersion")
val wasmTools = wasmToolsDirectory.file("bin/wasm-tools$executableSuffix")
val generatedBindings = layout.buildDirectory.dir("generated/wit/wasmWasiMain/kotlin")
val projectWasmName = rootProject.name
val embeddedReleaseComponent = layout.buildDirectory.file(
"intermediates/pumpkin-components/release/$projectWasmName-embedded.wasm"
)
val releaseComponent = layout.buildDirectory.file("$projectWasmName.wasm")
val installWitBindgen by tasks.registering(Exec::class) {
group = "build setup"
description = "Installs Pumpkin's pinned Kotlin binding generator."
inputs.property("revision", witBindgenRevision)
outputs.dir(witBindgenDirectory)
commandLine(
cargoExecutable.get(),
"install",
"wit-bindgen-cli",
"--git", "https://github.com/Kotlin/wit-bindgen",
"--rev", witBindgenRevision,
"--locked",
"--root", witBindgenDirectory.asFile.absolutePath,
)
}
val installWasmTools by tasks.registering(Exec::class) {
group = "build setup"
description = "Installs the pinned wasm-tools executable."
@ -68,7 +43,10 @@ val installWasmTools by tasks.registering(Exec::class) {
outputs.dir(wasmToolsDirectory)
commandLine(
cargoExecutable.get(),
providers.environmentVariable("CARGO_HOME")
.orElse(providers.systemProperty("user.home").map { "$it/.cargo" })
.map { "$it/bin/cargo$executableSuffix" }
.get(),
"install",
"wasm-tools",
"--version", wasmToolsVersion,
@ -77,40 +55,21 @@ val installWasmTools by tasks.registering(Exec::class) {
)
}
val generateWitBindings by tasks.registering(Exec::class) {
group = "build"
description = "Generates Kotlin bindings from Pumpkin's WIT definitions."
val apiSourcesJar = project(":api").tasks.named<Jar>("wasmWasiSourcesJar")
val unpackApiSources by tasks.registering(Sync::class) {
group = "build setup"
description = "Unpacks the published Pumpkin API source snapshot for component compilation."
dependsOn(installWitBindgen)
inputs.dir(witDirectory)
inputs.file(witBindgen)
inputs.property("kotlinPackage", "pumpkin")
inputs.property("kotlinImports", "plugin.*")
outputs.dir(generatedBindings)
doFirst {
val outputDirectory = generatedBindings.get().asFile
check(outputDirectory.deleteRecursively() || !outputDirectory.exists()) {
"Could not remove stale bindings from $outputDirectory"
}
check(outputDirectory.mkdirs() || outputDirectory.isDirectory) {
"Could not create bindings directory $outputDirectory"
}
}
commandLine(
witBindgen.asFile.absolutePath,
"kotlin",
"--kotlin-imports", "plugin.*",
"--kotlin-package-name", "pumpkin",
witDirectory.asFile.absolutePath,
"--out-dir", generatedBindings.get().asFile.absolutePath,
)
dependsOn(apiSourcesJar)
from(apiSourcesJar.flatMap { it.archiveFile }.map { zipTree(it.asFile) })
into(layout.buildDirectory.dir("generated/pumpkin-api"))
}
val witDirectory = unpackApiSources.map { it.destinationDir.resolve("wit/v0.1") }
val reactorAdapter = unpackApiSources.map { it.destinationDir.resolve("wasi/wasi_snapshot_preview1.reactor.wasm") }
kotlin {
sourceSets.named("wasmWasiMain") {
kotlin.srcDir(generateWitBindings)
kotlin.srcDir(unpackApiSources)
}
}
@ -135,7 +94,7 @@ val embedComponentWitRelease by tasks.registering(Exec::class) {
override fun asArguments() = listOf(
"component",
"embed",
witDirectory.asFile.absolutePath,
witDirectory.get().absolutePath,
releaseCoreWasm.get().asFile.absolutePath,
"-o", embeddedReleaseComponent.get().asFile.absolutePath,
)
@ -165,7 +124,7 @@ val assemblePluginRelease by tasks.registering(Exec::class) {
"component",
"new",
embeddedReleaseComponent.get().asFile.absolutePath,
"--adapt", "wasi_snapshot_preview1=${reactorAdapter.asFile.absolutePath}",
"--adapt", "wasi_snapshot_preview1=${reactorAdapter.get().absolutePath}",
"-o", releaseComponent.get().asFile.absolutePath,
)
}

View File

@ -0,0 +1,36 @@
plugins {
`kotlin-dsl`
`java-gradle-plugin`
`maven-publish`
}
group = "io.github.pumpkin-mc"
version = providers.gradleProperty("pumpkinApiVersion").getOrElse("0.1.0-dev")
base {
archivesName.set("pumpkin-api-kt-gradle-plugin")
}
repositories {
gradlePluginPortal()
mavenCentral()
}
dependencies {
compileOnly("org.jetbrains.kotlin:kotlin-gradle-plugin:2.4.0")
}
kotlin {
jvmToolchain(17)
}
gradlePlugin {
plugins {
create("pumpkinPlugin") {
id = "io.github.pumpkin-mc.plugin"
implementationClass = "io.github.pumpkinmc.gradle.PumpkinPlugin"
displayName = "Pumpkin Kotlin plugin build"
description = "Builds Kotlin/Wasm Pumpkin plugins from the published API source snapshot."
}
}
}

View File

@ -0,0 +1,174 @@
package io.github.pumpkinmc.gradle
import javax.inject.Inject
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.artifacts.Configuration
import org.gradle.api.file.Directory
import org.gradle.api.file.RegularFile
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.api.tasks.Sync
import org.gradle.api.tasks.TaskProvider
import org.gradle.kotlin.dsl.create
import org.gradle.kotlin.dsl.named
import org.gradle.kotlin.dsl.register
import org.gradle.process.CommandLineArgumentProvider
import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension
import org.jetbrains.kotlin.gradle.targets.wasm.binaryen.BinaryenExec
abstract class PumpkinExtension @Inject constructor(objects: ObjectFactory) {
val apiGroup: Property<String> = objects.property(String::class.java).convention("io.github.pumpkin-mc")
val apiArtifact: Property<String> = objects.property(String::class.java).convention("pumpkin-api-kt")
val apiVersion: Property<String> = objects.property(String::class.java).convention("0.1.0-dev") // Replace
val wasmToolsVersion: Property<String> = objects.property(String::class.java).convention("1.258.0")
}
class PumpkinPlugin : Plugin<Project> {
override fun apply(project: Project) {
val extension = project.extensions.create<PumpkinExtension>("pumpkin")
val apiSources = project.configurations.create("pumpkinApiSources") {
isCanBeConsumed = false
isCanBeResolved = true
isTransitive = false
description = "The generated Pumpkin Kotlin API source snapshot."
}
project.afterEvaluate {
configurePlugin(project, extension, apiSources)
}
}
private fun configurePlugin(
project: Project,
extension: PumpkinExtension,
apiSources: Configuration,
) {
require(project.plugins.hasPlugin("org.jetbrains.kotlin.multiplatform")) {
"io.github.pumpkin-mc.plugin requires org.jetbrains.kotlin.multiplatform."
}
project.dependencies.add(
apiSources.name,
"${extension.apiGroup.get()}:${extension.apiArtifact.get()}-wasm-wasi:${extension.apiVersion.get()}:sources@jar",
)
val unpackApiSources = project.tasks.register<Sync>("unpackPumpkinApiSources") {
group = "build setup"
description = "Unpacks the published Pumpkin API source snapshot for component compilation."
from(project.provider { project.zipTree(apiSources.singleFile) })
into(project.layout.buildDirectory.dir("generated/pumpkin-api"))
}
project.extensions.getByType(KotlinMultiplatformExtension::class.java)
.sourceSets.named("wasmWasiMain") {
kotlin.srcDir(unpackApiSources)
}
val unpackedApiDirectory = unpackApiSources.map { it.destinationDir }
val witDirectory = unpackedApiDirectory.map { it.resolve("wit/v0.1") }
val reactorAdapter = unpackedApiDirectory.map { it.resolve("wasi/wasi_snapshot_preview1.reactor.wasm") }
val executableSuffix = if (System.getProperty("os.name").startsWith("Windows", ignoreCase = true)) ".exe" else ""
val wasmToolsDirectory = project.layout.buildDirectory.dir(
"tools/wasm-tools/${extension.wasmToolsVersion.get()}",
)
val wasmTools = wasmToolsDirectory.map { it.file("bin/wasm-tools$executableSuffix") }
val installWasmTools = project.tasks.register("installWasmTools", org.gradle.api.tasks.Exec::class.java) {
group = "build setup"
description = "Installs the pinned wasm-tools executable."
inputs.property("version", extension.wasmToolsVersion)
outputs.dir(wasmToolsDirectory)
val cargoHome = project.providers.environmentVariable("CARGO_HOME")
.orElse(project.providers.systemProperty("user.home").map { "$it/.cargo" })
commandLine(
cargoHome.map { "$it/bin/cargo$executableSuffix" }.get(),
"install",
"wasm-tools",
"--version", extension.wasmToolsVersion.get(),
"--locked",
"--root", wasmToolsDirectory.get().asFile.absolutePath,
)
}
val projectWasmName = project.name
val compileReleaseWasm = project.tasks.named<BinaryenExec>(
"compileProductionExecutableKotlinWasmWasiOptimize",
)
val releaseCoreWasm = compileReleaseWasm.flatMap {
it.outputDirectory.file("$projectWasmName.wasm")
}
val embeddedReleaseComponent = project.layout.buildDirectory.file(
"intermediates/pumpkin-components/release/$projectWasmName-embedded.wasm",
)
val releaseComponent = project.layout.buildDirectory.file("$projectWasmName.wasm")
val embedComponentWitRelease = project.tasks.register("embedComponentWitRelease", org.gradle.api.tasks.Exec::class.java) {
group = "build"
description = "Embeds Pumpkin's WIT into the release Kotlin/Wasm module."
dependsOn(compileReleaseWasm, installWasmTools)
inputs.dir(witDirectory)
inputs.file(releaseCoreWasm)
inputs.file(wasmTools)
outputs.file(embeddedReleaseComponent)
executable = wasmTools.get().asFile.absolutePath
argumentProviders.add(componentEmbedArguments(witDirectory, releaseCoreWasm, embeddedReleaseComponent))
doFirst {
embeddedReleaseComponent.get().asFile.parentFile.mkdirs()
}
}
val assemblePluginRelease = project.tasks.register("assemblePluginRelease", org.gradle.api.tasks.Exec::class.java) {
group = "build"
description = "Creates the release WebAssembly component that Pumpkin can load."
dependsOn(embedComponentWitRelease)
inputs.file(embeddedReleaseComponent)
inputs.file(reactorAdapter)
inputs.file(wasmTools)
outputs.file(releaseComponent)
executable = wasmTools.get().asFile.absolutePath
argumentProviders.add(componentNewArguments(embeddedReleaseComponent, reactorAdapter, releaseComponent))
}
val validatePluginRelease = project.tasks.register("validatePluginRelease", org.gradle.api.tasks.Exec::class.java) {
group = "verification"
description = "Validates the release WebAssembly component."
dependsOn(assemblePluginRelease)
inputs.file(releaseComponent)
inputs.file(wasmTools)
commandLine(wasmTools.get().asFile.absolutePath, "validate", releaseComponent.get().asFile.absolutePath)
}
project.tasks.named("assemble") { dependsOn(assemblePluginRelease) }
project.tasks.named("check") { dependsOn(validatePluginRelease) }
}
private fun componentEmbedArguments(
witDirectory: org.gradle.api.provider.Provider<java.io.File>,
releaseCoreWasm: org.gradle.api.provider.Provider<RegularFile>,
embeddedReleaseComponent: org.gradle.api.provider.Provider<RegularFile>,
) = CommandLineArgumentProvider {
listOf(
"component", "embed",
witDirectory.get().absolutePath,
releaseCoreWasm.get().asFile.absolutePath,
"-o", embeddedReleaseComponent.get().asFile.absolutePath,
)
}
private fun componentNewArguments(
embeddedReleaseComponent: org.gradle.api.provider.Provider<RegularFile>,
reactorAdapter: org.gradle.api.provider.Provider<java.io.File>,
releaseComponent: org.gradle.api.provider.Provider<RegularFile>,
) = CommandLineArgumentProvider {
listOf(
"component", "new",
embeddedReleaseComponent.get().asFile.absolutePath,
"--adapt", "wasi_snapshot_preview1=${reactorAdapter.get().absolutePath}",
"-o", releaseComponent.get().asFile.absolutePath,
)
}
}

View File

@ -7,3 +7,6 @@ pluginManagement {
}
}
}
include("api")
include("gradle-plugin")

View File

@ -1,79 +1,33 @@
package plugin;
package plugin
import pumpkin.*
import pumpkin.Context
import pumpkin.Logging
class PluginRootFunctionsExportsImpl {
companion object : PluginRootFunctions.Exports {
override fun initPlugin() {
Logging.log(Logging.Level.INFO, "Init Kotlin plugin!")
}
class ExamplePlugin : PumpkinPlugin() {
override fun metadata() = PluginMetadata(
name = "Example Kotlin Plugin",
version = "0.1.0",
authors = listOf("You"),
description = "An example plugin written in Kotlin",
dependencies = listOf(),
permissions = listOf(),
)
override fun onLoad(context: Context.Context): Result<Unit> {
Logging.log(Logging.Level.INFO, "Load Kotlin plugin!")
return Result.success(Unit)
}
override fun initPlugin() {
Logging.log(Logging.Level.INFO, "Init Kotlin plugin!")
}
override fun onUnload(context: Context.Context): Result<Unit> {
Logging.log(Logging.Level.INFO, "Unload Kotlin plugin!")
return Result.success(Unit)
}
override fun onLoad(context: Context.Context): Result<Unit> {
Logging.log(Logging.Level.INFO, "Load Kotlin plugin!")
return Result.success(Unit)
}
override fun handleCommand(
commandId: UInt,
sender: Command.CommandSender,
server: Server.Server,
args: Command.ConsumedArgs
): Result<Int> {
TODO("Not yet implemented")
}
override fun handleEvent(eventId: UInt, server: Server.Server, event: Event.Event): Event.Event {
TODO("Not yet implemented")
}
override fun handleTask(handlerId: UInt, server: Server.Server) {
TODO("Not yet implemented")
}
override fun handleIpcMessage(sender: String, message: List<UByte>): Result<List<UByte>> {
return Result.failure(
pumpkin.runtime.ComponentException("This plugin cannot recieve messages.")
);
}
override fun handleAiGoalCanStart(goalId: UInt, server: Server.Server, entity: World.Entity): Boolean {
TODO("Not yet implemented");
}
override fun handleAiGoalShouldContinue(goalId: UInt, server: Server.Server, entity: World.Entity): Boolean {
TODO("Not yet implemented");
}
override fun handleAiGoalStart(goalId: UInt, server: Server.Server, entity: World.Entity): Unit {
TODO("Not yet implemented");
}
override fun handleAiGoalTick(goalId: UInt, server: Server.Server, entity: World.Entity): Unit {
TODO("Not yet implemented");
}
override fun handleAiGoalStop(goalId: UInt, server: Server.Server, entity: World.Entity): Unit {
TODO("Not yet implemented");
}
override fun onUnload(context: Context.Context): Result<Unit> {
Logging.log(Logging.Level.INFO, "Unload Kotlin plugin!")
return Result.success(Unit)
}
}
class MetadataImpl {
companion object : Metadata {
override fun getMetadata(): Metadata.PluginMetadata {
return Metadata.PluginMetadata(
name = "Example Kotlin Plugin",
version = "0.1.0",
authors = listOf("You"),
description = "An example plugin written in Kotlin",
dependencies = listOf(),
permissions = listOf()
);
}
}
fun main() {
registerPlugin(ExamplePlugin())
}