Skip to content

2. Build System ​

2.1 Root build.gradle ​

groovy
plugins {
    id 'java-library'
}

allprojects {
    group = 'me.Maussee.AppelTools'
    version = '2.0'
    description = 'A plugin for managing AppelCraft'
    ext.set("author", "Maussee")

    repositories {
        mavenCentral()
        maven { url 'https://jitpack.io' }
        maven { url 'https://repo.papermc.io/repository/maven-public/' }
        maven { url 'https://repo.william278.net/velocity/' }
        maven { url 'https://oss.sonatype.org/content/groups/public/' }
        maven { url 'https://repo.panda-lang.org/releases' }
        maven { url 'https://repo.papermc.io/repository/maven-snapshots/' }
        maven { url 'https://repo.opencollab.dev/maven-snapshots/' }
    }
}

subprojects {
    apply plugin: 'java'
    java { toolchain.languageVersion.set(JavaLanguageVersion.of(21)) }
    dependencies {
        compileOnly 'org.jetbrains:annotations:26.0.2'
        compileOnly 'org.projectlombok:lombok:1.18.40'
        annotationProcessor 'org.projectlombok:lombok:1.18.40'
        testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0'
        testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
    }
    test { useJUnitPlatform() }
}

tasks.register("deployDev") {
    dependsOn ":common:clean", ":paper:clean", ":velocity:clean"
    dependsOn ":common:build",  ":paper:build",  ":velocity:build"
    dependsOn ":paper:uploadToDev", ":velocity:uploadToDev"
    // force ordering: clean -> build -> upload
}
tasks.register("deployProd") {
    dependsOn ":common:clean", ":paper:clean", ":velocity:clean"
    dependsOn ":common:build",  ":paper:build",  ":velocity:build"
    dependsOn ":paper:uploadToDev", ":velocity:uploadToDev"
    dependsOn ":paper:uploadToProd", ":velocity:uploadToProd"
}

2.2 settings.gradle ​

groovy
rootProject.name = 'AppelTools'
include 'common'
include 'velocity'
include 'paper'

2.3 common/build.gradle ​

  • Java 25 source/target; UTF-8 encoding.
  • Gradle task replaceTokens copies src/main/java to build/filteredSrc, replacing @version@, @description@, @author@ placeholders.
  • compileJava reads from build/filteredSrc.
  • Dependencies: gson, guava, adventure, configurate, luckperms, mysql, hikari, slf4j, JDA — all compileOnly.
  • shadowJar produces AppelTools-Common.jar (no classifier).
  • build depends on shadowJar.
groovy
import org.apache.tools.ant.filters.ReplaceTokens
import java.nio.charset.StandardCharsets

plugins {
    id 'java-library'
    id 'com.gradleup.shadow' version '9.3.2'
}

java { withSourcesJar() }
group = "${rootProject.group}.common"
version = rootProject.version
base { archivesName.set("${rootProject.name}-Common") }

tasks.register('replaceTokens', Copy) {
    from("src/main/java") {
        include "**/*.java"
        filteringCharset = 'UTF-8'
        filter(ReplaceTokens, tokens: [
                'version'    : project.version,
                'description': project.description ?: "",
                'author'     : rootProject.ext.get("author").toString()
        ])
    }
    into "${layout.buildDirectory.get()}/filteredSrc"
}

compileJava {
    dependsOn replaceTokens
    source = fileTree("${layout.buildDirectory.get()}/filteredSrc")
    options.encoding = StandardCharsets.UTF_8.name()
    options.release.set(21)
}

dependencies {
    compileOnly 'com.google.code.gson:gson:2.15.0'
    implementation 'com.google.guava:guava:33.8.0-jre'
    compileOnly 'net.kyori:adventure-api:4.26.1'
    compileOnly 'net.kyori:adventure-text-minimessage:4.26.1'
    compileOnly 'de.exlll:configlib-core:4.8.1'
    compileOnly 'net.luckperms:api:5.5'
    compileOnly 'com.mysql:mysql-connector-j:26.10.0'
    compileOnly 'com.zaxxer:HikariCP:7.1.0'
    compileOnly 'org.slf4j:slf4j-api:2.0.20'
    compileOnly('net.dv8tion:JDA:6.7.0') { exclude module: 'opus-java'; exclude module: 'tink' }
    testImplementation 'org.junit.jupiter:junit-jupiter:6.1.3'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
    testImplementation 'net.kyori:adventure-api:4.26.1'
    testImplementation 'net.kyori:adventure-text-minimessage:4.26.1'
    testImplementation 'de.exlll:configlib-core:4.8.1'
}

shadowJar { archiveClassifier.set('') }
build { dependsOn shadowJar }

2.4 paper/build.gradle ​

  • Depends on :common.
  • Compile-only: paper-api 26.3.build.+, Vault, PremiumVanish, configurate, luckperms, JDA, logback.
  • Implementation: dev.rollczi:litecommands-bukkit:3.11.0.
  • processResources expands ${version}, ${description}, ${author} in paper-plugin.yml.
  • shadowJar relocates dev.rollczi.litecommands to me.Maussee.AppelTools.libs.litecommands and depends on :common shadowJar.
  • Custom task uploadToDev and uploadToProd use sshj to SFTP-upload the shadowJar to:
    • appelcraft-hub, appelcraft-survival, appelcraft-devsurvival, appelcraft-devserver.
  • Deploy properties: appelcraft.deploy.host, appelcraft.deploy.port, appelcraft.deploy.password, plus per-server *.deploy.username.
groovy
import net.schmizz.sshj.SSHClient
import net.schmizz.sshj.sftp.SFTPClient
import net.schmizz.sshj.transport.verification.PromiscuousVerifier
import java.nio.charset.StandardCharsets

buildscript {
    repositories { mavenCentral() }
    dependencies { classpath 'com.hierynomus:sshj:0.40.0' }
}

plugins {
    id 'java'
    id 'com.gradleup.shadow' version '9.3.2'
}

group = "${rootProject.group}.paper"
version = rootProject.version
base { archivesName.set("${rootProject.name}-Paper") }

dependencies {
    implementation project(':common')
    compileOnly 'ch.qos.logback:logback-classic:1.6.6'
    compileOnly 'io.papermc.paper:paper-api:1.21.11-R0.1-SNAPSHOT'
    compileOnly('com.github.MilkBowl:VaultAPI:1.7') { exclude group: 'org.bukkit', module: 'bukkit' }
    compileOnly 'com.github.LeonMangler:PremiumVanishAPI:2.9.18-2'
    compileOnly 'de.exlll:configlib-core:4.8.1'
    compileOnly 'net.luckperms:api:5.5'
    compileOnly('net.dv8tion:JDA:6.7.0') { exclude module: 'opus-java'; exclude module: 'tink' }
    implementation 'dev.rollczi:litecommands-bukkit:3.11.0'
    testImplementation 'org.junit.jupiter:junit-jupiter:6.1.3'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

tasks {
    compileJava {
        options.encoding = StandardCharsets.UTF_8.name()
        options.release.set(21)
        options.compilerArgs.add("-parameters")
    }
    processResources {
        filesMatching("paper-plugin.yml") {
            expand([
                    'version'    : project.version,
                    'description': project.description ?: "",
                    'author'     : rootProject.ext.get("author").toString()
            ])
        }
    }
    shadowJar {
        archiveClassifier.set('')
        manifest { attributes 'Main-Class': "${rootProject.group}.paper.AppelToolsPaperLoader" }
        relocate 'dev.rollczi.litecommands', "${rootProject.group}.libs.litecommands"
        dependsOn project(':common').tasks.shadowJar
    }
    build { dependsOn shadowJar }
}

String deployHost = project.hasProperty('appelcraft.deploy.host') ? project['appelcraft.deploy.host'].toString() : ''
int deployPort = project.hasProperty('appelcraft.deploy.port') ? project['appelcraft.deploy.port'].toInteger() : 22
String deployPassword = project.hasProperty('appelcraft.deploy.password') ? project['appelcraft.deploy.password'].toString() : ''

def servers = ['appelcraft-hub', 'appelcraft-survival', 'appelcraft-devsurvival', 'appelcraft-devserver']

tasks.register("uploadToProd") {
    dependsOn shadowJar
    doLast {
        servers.each { server ->
            def localFile = file("${layout.buildDirectory.get()}/libs/${shadowJar.archiveFileName.get()}")
            def remotePath = "/plugins/"
            SSHClient ssh = new SSHClient()
            ssh.addHostKeyVerifier(new PromiscuousVerifier())
            ssh.connect(deployHost, deployPort)
            ssh.authPassword(project["${server}.deploy.username"].toString(), deployPassword)
            SFTPClient sftp = ssh.newSFTPClient()
            sftp.put(localFile.absolutePath, remotePath + localFile.name)
            sftp.close()
            ssh.disconnect()
            println "Uploaded successfully to ${server}"
        }
    }
}

tasks.register('uploadToDev') {
    dependsOn shadowJar
    doLast {
        def localFile = file("${layout.buildDirectory.get()}/libs/${shadowJar.archiveFileName.get()}")
        def remotePath = "/plugins/"
        SSHClient ssh = new SSHClient()
        ssh.addHostKeyVerifier(new PromiscuousVerifier())
        ssh.connect(deployHost, deployPort)
        ssh.authPassword(project["appelcraft-devserver.deploy.username"].toString(), deployPassword)
        SFTPClient sftp = ssh.newSFTPClient()
        sftp.put(localFile.absolutePath, remotePath + localFile.name)
        sftp.close()
        ssh.disconnect()
        println "Uploaded successfully to appelcraft-devserver"
    }
}

2.5 velocity/build.gradle ​

  • Depends on :common.
  • Compile-only: velocity-api + velocity-proxy 4.2.0, luckperms, vPacketEvents, configurate.
  • Implementation: mysql, hikari, litecommands-velocity, litecommands-jda, JDA, javax.annotation-api.
  • shadowJar relocates com.mysql, com.zaxxer.hikari, dev.rollczi.litecommands, net.dv8tion.jda to internal packages (to avoid conflicts on the proxy classpath).
  • Custom task uploadToDev and uploadToProd use sshj to SFTP-upload the shadowJar to:
    • appelcraft-proxy, appelcraft-devproxy.
groovy
import net.schmizz.sshj.SSHClient
import net.schmizz.sshj.sftp.SFTPClient
import net.schmizz.sshj.transport.verification.PromiscuousVerifier
import java.nio.charset.StandardCharsets

buildscript {
    repositories { mavenCentral() }
    dependencies { classpath 'com.hierynomus:sshj:0.40.0' }
}

plugins {
    id 'java'
    id 'com.gradleup.shadow' version '9.3.2'
}

group = "${rootProject.group}.velocity"
version = rootProject.version
base { archivesName.set("${rootProject.name}-Velocity") }

dependencies {
    implementation project(':common')
    compileOnly 'com.velocitypowered:velocity-api:4.2.0'
    compileOnly 'com.velocitypowered:velocity-proxy:4.2.0'
    annotationProcessor 'com.velocitypowered:velocity-api:4.2.0'
    compileOnly 'net.luckperms:api:5.5'
    compileOnly 'io.github.4drian3d:vpacketevents-api:1.2.0'
    compileOnly 'de.exlll:configlib-core:4.8.1'
    implementation 'javax.annotation:javax.annotation-api:1.3.2'
    implementation 'com.mysql:mysql-connector-j:26.10.0'
    implementation 'com.zaxxer:HikariCP:7.1.0'
    implementation 'dev.rollczi:litecommands-velocity:3.11.0'
    implementation 'dev.rollczi:litecommands-jda:3.11.0'
    implementation('net.dv8tion:JDA:6.7.0') { exclude module: 'opus-java'; exclude module: 'tink' }
    testImplementation 'org.junit.jupiter:junit-jupiter:6.1.3'
    testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

tasks {
    compileJava {
        options.encoding = StandardCharsets.UTF_8.name()
        options.release.set(21)
        options.compilerArgs.add("-parameters")
    }
    shadowJar {
        archiveClassifier.set('')
        manifest { attributes 'Main-Class': "${rootProject.group}.velocity.AppelToolsVelocity" }
        relocate 'com.mysql', "${rootProject.group}.libs.mysql"
        relocate 'com.zaxxer.hikari', "${rootProject.group}.libs.hikari"
        relocate 'dev.rollczi.litecommands', "${rootProject.group}.libs.litecommands"
        relocate 'net.dv8tion.jda', "${rootProject.group}.libs.jda"
        dependsOn project(':common').tasks.shadowJar
    }
    build { dependsOn shadowJar }
}

String deployHost = project.hasProperty('appelcraft.deploy.host') ? project['appelcraft.deploy.host'].toString() : ''
int deployPort = project.hasProperty('appelcraft.deploy.port') ? project['appelcraft.deploy.port'].toInteger() : 22
String deployPassword = project.hasProperty('appelcraft.deploy.password') ? project['appelcraft.deploy.password'].toString() : ''

def servers = ['appelcraft-proxy', 'appelcraft-devproxy']

tasks.register("uploadToProd") { /* same shape as paper uploadToProd */ }
tasks.register('uploadToDev')  { /* same shape, dev only */ }

2.6 Build Commands ​

bash
./gradlew build                # build all modules
./gradlew build -x test        # skip tests
./gradlew test                 # run all tests
./gradlew test --tests "me.Maussee.AppelTools.common.Utils.TextUtilsTest"
./gradlew deployDev            # clean, build, SFTP to dev servers
./gradlew deployProd           # clean, build, SFTP to dev + prod

2.7 Required Gradle Properties for Deploy ​

In ~/.gradle/gradle.properties or via -P flags:

  • appelcraft.deploy.host — SSH host.
  • appelcraft.deploy.port — SSH port (default 22).
  • appelcraft.deploy.password — SSH password.
  • appelcraft-hub.deploy.username, appelcraft-survival.deploy.username, appelcraft-devsurvival.deploy.username, appelcraft-devserver.deploy.username (paper).
  • appelcraft-proxy.deploy.username, appelcraft-devproxy.deploy.username (velocity).

Built with VitePress.