| .github/workflows | ||
| api | ||
| gradle | ||
| gradle-plugin | ||
| src/wasmWasiMain/kotlin/plugin | ||
| wit@1ad73fff1e | ||
| .gitignore | ||
| .gitmodules | ||
| build.gradle.kts | ||
| gradle.properties | ||
| gradlew | ||
| gradlew.bat | ||
| LICENSE-APACHE | ||
| LICENSE-MIT | ||
| plugin.wasm | ||
| README.md | ||
| settings.gradle.kts | ||
| wasi_snapshot_preview1.reactor.wasm | ||
Pumpkin Plugin API for Kotlin
Build Kotlin plugins for Pumpkin as WebAssembly components.
The api module packages generated Kotlin bindings, the WIT export bridge, and the WIT snapshot and WASI adapter needed to build a component. The gradle-plugin module integrates that package into a plugin author's build. Plugin authors do not need Rust, wit-bindgen, or a WIT checkout.
Kotlin/Wasm component support is still experimental. The planned release destinations are Maven Central for the API and the Gradle Plugin Portal for the build plugin, though that is not certain yet.
Create a plugin
Create a Gradle project with a Gradle 9.x wrapper and JDK 17 or later. You do not need to clone this repository.
In settings.gradle.kts:
pluginManagement {
repositories {
gradlePluginPortal()
mavenCentral()
}
}
rootProject.name = "my-plugin"
In build.gradle.kts:
import org.jetbrains.kotlin.gradle.ExperimentalWasmDsl
plugins {
kotlin("multiplatform") version "2.4.0"
id("io.github.pumpkin-mc.plugin") version "<plugin-version>"
}
repositories {
mavenCentral()
}
kotlin {
@OptIn(ExperimentalWasmDsl::class)
wasmWasi {
nodejs()
binaries.executable()
}
}
pumpkin {
apiVersion.set("<api-version>")
pluginClass.set("example.MyPlugin")
}
Important
Put your implementation in
src/wasmWasiMain/kotlin/example/MyPlugin.kt. ThewasmWasiMainsource set is required;src/main/kotlinis not the source directory for this target.
package example
import plugin.PluginMetadata
import plugin.PumpkinPlugin
import pumpkin.Context
import pumpkin.Logging
class MyPlugin : PumpkinPlugin() {
override fun metadata() = PluginMetadata(
name = "my-plugin",
version = "0.1.0",
authors = listOf("Your name"),
description = "My first Kotlin plugin",
dependencies = emptyList(),
permissions = emptyList(),
)
override fun onLoad(context: Context.Context): Result<Unit> {
Logging.log(Logging.Level.INFO, "Hello from Kotlin!")
return Result.success(Unit)
}
}
The configured class must extend PumpkinPlugin and be constructible without arguments. Override the callbacks your plugin uses; most optional handler defaults throw if invoked without an implementation.
Build and load
From your plugin project:
./gradlew build
The Gradle plugin downloads a pinned prebuilt wasm-tools, compiles the Kotlin code, assembles the component, and validates it. The component is written to build/<project-name>.wasm (build/my-plugin.wasm above). Copy it into the Pumpkin server's plugins directory and start the server.
To assemble only the component, run ./gradlew assemblePluginRelease. Gradle skips tasks when their inputs are unchanged. The consumer's generated sources and downloaded tools live under build/, so ./gradlew clean removes them.
How the dependency and bootstrap work
The API publishes both a Kotlin/WASI KLIB and a generated-source archive. Currently, WIT exports must be compiled as part of the final plugin to be retained by the Kotlin/Wasm linker. The Gradle plugin therefore resolves the API's source archive through pumpkinApiSources, unpacks it, and adds it to wasmWasiMain. You do not add a separate implementation(...) dependency or run binding generation yourself.
generatePumpkinPluginBootstrap generates a factory from pumpkin.pluginClass:
package plugin
internal fun createPlugin(): PumpkinPlugin = example.MyPlugin()
This replaces the API's placeholder factory during consumer compilation. On the first exported callback, the API bridge constructs and caches your plugin instance. Later callbacks use the same instance. There is no need for main(), a top-level registration initializer, or a call to registerPlugin(...). The package also supplies the generator-specific PluginRootFunctionsExportsImpl and MetadataImpl bridges.
Server compatibility
Choose an API version compatible with your Pumpkin server. Errors such as no export ... found, type-checking export func ..., or missing imports indicate a possible mismatch between the plugin's packaged WIT and the server's contract. Update pumpkin.apiVersion to a compatible release and rebuild the component.
API development
Contributors building the API itself need JDK 17 or later and a host Rust toolchain. Gradle builds the pinned Kotlin-enabled wit-bindgen from source; no Rust WebAssembly target is required.
From this repository, generate the bindings and publish both development packages locally:
git submodule update --init --recursive
./gradlew :api:publishToMavenLocal :gradle-plugin:publishToMavenLocal
To test these local packages in a consumer, add mavenLocal() before the other repositories in both settings.gradle.kts's pluginManagement.repositories and build.gradle.kts's repositories. Set both version placeholders to 0.1.0-dev. After republishing changes under that version, run ./gradlew build --refresh-dependencies in the consumer.
The repository's legacy root template still uses the earlier main() registration approach. Use the separate consumer setup above for the bootstrap integration.
Updating the WIT
API maintainers should update the wit submodule to the revision compatible with the target Pumpkin server, then regenerate and republish the API. Commit the updated submodule revision with any required callback changes so builds use the same contract.
Attributions
This project was primarily derived from @jmrtsh's work on Kotlin/sample-wasi-http-kotlin, which is licensed under Apache-2.0.