Skip to content

Build, Release & Versioning ​

A Gradle multi-module setup shares engine while building and releasing Paper / Velocity as independent artifacts.

Build configuration ​

  • Root build.gradle.kts — manages Kotlin 2.4.0 + serialization / Shadow / ktlint / dokka. The JVM target is JVM_25. Tests use JUnit Platform + jacoco, with common test dependencies injected into all modules
  • engine — exposes core libraries (serialization / coroutines / ktor) via api() to propagate them to the platforms. Adventure is compileOnly. A pure library with no Shadow
  • platform-paper — version = paperVersion. api(project(":engine")). paper-api as compileOnly, KAML + kotlin-reflect as implementation. Output is LunaticChat-<ver>.jar (no classifier; jar disabled)
  • platform-velocity — version = velocityVersion. api(project(":engine")). velocity-api as compileOnly. Output is LunaticChat-<ver>-velocity.jar (distinguished by classifier)
  • dokka — aggregates engine/paper/velocity and includes the README in the HTML

Both platforms derive their version from isNightly: with -PisNightly=true the base version gains a -nightly.<git short hash> suffix, so a development build is never mistakable for the release of the same base version (LunaticChat-1.3.0-nightly.44132f3.jar). Without the flag the version stays the bare paperVersion / velocityVersion, which is what the release workflows build.

processResources then token-expands version / gitCommitHash / channel into paper-plugin.yml / velocity-plugin.json and build-info.properties.

Independent versioning ​

properties
# gradle.properties
paperVersion=1.3.0
velocityVersion=1.2.0

Paper and Velocity carry separate version numbers and can be released independently. That's because compatibility is guaranteed by the engine-shared ProtocolVersion rather than the numeric version, so the two platforms — which change at different rates — can be bumped and published at their own pace. The wire format is forward-compatible via JSON + ignoreUnknownKeys, and backward compatibility is controlled by matching MAJOR + a MINOR-range check on the protocol.

Release workflows ​

One tag releases exactly one platform. The tag pattern selects which.

WorkflowTrigger tagBuild targetVersion validation
release-paper.yamlvX.Y.ZPaper onlyrequires the tag to match paperVersion
release-velocity.yamlvelocity/vX.Y.ZVelocity onlyrequires the tag to match velocityVersion
  • Releasing both at once means pushing both tags together (git push origin v1.5.0 velocity/v1.4.0). Each produces its own GitHub Release
  • Paper keeps the bare vX.Y.Z tag because the update checker in already-deployed Paper builds only understands that form
  • Up to v1.4.0, a vX.Y.Z tag released both platforms (v1.0.0, v1.3.0, v1.4.0), and some Paper-only releases used paper/vX.Y.Z. Those tags are kept as they are
  • Both workflows call the shared _release.yaml: validate (tag format, match with gradle.properties, duplicate release check) → build (mise + Gradle setup, shadowJar) → release (gh release create --draft + publish to Modrinth)
  • The GitHub Release is created as a draft. Publish a Velocity release with --latest=false, so that the repository's latest release stays the Paper one
  • Modrinth game-versions are Paper=26.3.x (loaders: paper, folia) and Velocity=1.21.x + 26.1.x + 26.2.x (loader: velocity), set in each caller workflow

CI ​

ci.yaml runs on push to main / PR / manual dispatch.

  • build_plugin — ktlintCheck → test + jacocoTestReport → upload to Codecov → nightly shadowJar (-PisNightly=true) → retain artifacts as LunaticChat-paper-<sha> / LunaticChat-velocity-<sha> (one per platform, each holding the JAR at the archive root)
  • build_dokka / deploy_dokka — generate Dokka → deploy to GitHub Pages (main push only)
  • build_docs — format/lint/build website/ with bun → deploy to Cloudflare Workers (wrangler) (main push only)

Development environment ​

  • mise.toml — bun / java zulu-25 (consistent with JVM_25)
  • x — a bash debug-server script. ./x <action> <platform> [--stable] for start/stop/log/clean/rcon/help. Without --stable it builds nightly. With velocity it brings up 1 Velocity + 2 Paper so you can test cross-server chat relay for real
  • docker/ — compose.yaml for three environments: paper / velocity / folia (itzg/minecraft-server:java25, etc.). velocity.toml enables plugin messaging with bungee-plugin-message-channel=true
  • Server versions are derived by x from the paper-api / velocity-api coordinates in build.gradle.kts and passed to Compose as PAPER_MC_VERSION / PAPER_BUILD / VELOCITY_VERSION, so the debug environments always run the build the plugin is compiled against. Invoking docker compose directly fails with a message telling you to go through ./x. Only Folia's build number is pinned by hand, since it is numbered separately from Paper