Skip to main content

Building from Source

XDiscordUltimate is built with Gradle and the com.gradleup.shadow plugin. The output is a single shaded JAR ready to drop into a server’s plugins/ directory.

Prerequisites

Build Steps

1

Clone the repository

git clone https://github.com/xreatlabs/XDiscordUltimate.git
cd XDiscordUltimate
2

Verify your JDK

java -version
You need Java 17 or newer at runtime. The build itself compiles against Java 16 source/target so the JAR loads on Minecraft 1.16.5+.
3

Build the shaded JAR

./gradlew shadowJar
shadowJar produces the distributable artifact. The Gradle wrapper script (gradlew) downloads the correct Gradle version automatically, so you do not need Gradle installed system-wide.
4

Locate the artifact

ls build/libs/
# XDiscordUltimate-1.2.0.jar
Copy build/libs/XDiscordUltimate-1.2.0.jar into your server’s plugins/ directory.
To build without running tests, use ./gradlew shadowJar -x test. For a clean rebuild, run ./gradlew clean shadowJar.

Relevant build.gradle

The build file declares the project coordinates, Java toolchain, repositories, and dependencies. The parts that matter for contributors:
plugins {
    id 'java'
    id 'com.gradleup.shadow' version '8.3.0'
}

group = 'com.xreatlabs.xdiscordultimate'
version = '1.2.0'

java {
    sourceCompatibility = JavaVersion.VERSION_16
    targetCompatibility = JavaVersion.VERSION_16
}

repositories {
    mavenCentral()
    maven { url 'https://hub.spigotmc.org/nexus/content/repositories/snapshots/' }
    maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
    maven { url 'https://papermc.io/repo/repository/maven-public/' }
    maven { url 'https://jitpack.io' }
    maven { url 'https://repo.extendedclip.com/content/repositories/placeholderapi/' }
    maven { url 'https://nexus.scarsz.me/content/groups/public/' }
    maven { url 'https://repo.alessiodp.com/releases/' }
}

dependencies {
    compileOnly 'org.spigotmc:spigot-api:1.16.5-R0.1-SNAPSHOT'
    compileOnly 'net.dv8tion:JDA:5.0.0-beta.18'
    compileOnly 'com.zaxxer:HikariCP:5.0.1'
    compileOnly 'org.xerial:sqlite-jdbc:3.42.0.0'
    compileOnly 'com.mysql:mysql-connector-j:8.0.33'
    compileOnly 'org.postgresql:postgresql:42.6.0'
    implementation 'net.byteflux:libby-bukkit:1.3.1'

    // Optional integrations
    compileOnly 'net.luckperms:api:5.4'
    compileOnly 'me.clip:placeholderapi:2.11.3'
    compileOnly 'com.github.MilkBowl:VaultAPI:1.7'

    testImplementation 'org.junit.jupiter:junit-jupiter:5.10.0'
    testImplementation 'org.mockito:mockito-core:5.5.0'
}

shadowJar {
    archiveBaseName.set('XDiscordUltimate')
    archiveClassifier.set('')
    archiveVersion.set(version)
}
JDA, HikariCP, and every JDBC driver are declared compileOnly. The libby-bukkit runtime loader (LibraryManager) downloads them at server startup, which keeps the published JAR small and avoids classpath conflicts with other plugins. Only Libby itself is implementation and actually shaded into the JAR.

The uploadToDiscord Task

The build defines a convenience task that posts the freshly built JAR to a Discord webhook:
task uploadToDiscord(type: Exec) {
    dependsOn shadowJar
    onlyIf {
        // Reads DISCORD_WEBHOOK_URL from .env; skips if unset
    }
    commandLine 'curl', '-s', '-F', "content=✅ **Build Complete!** ...",
                '-F', "file=@${jarFile.absolutePath}", url
}

build.finalizedBy uploadToDiscord
Because build.finalizedBy uploadToDiscord, running ./gradlew build always attempts the upload as its final step. The task is guarded by onlyIf: if DISCORD_WEBHOOK_URL is not present in a .env file at the project root, it prints a skip notice and does nothing. It never fails the build and never sends anything unless you opt in by setting the variable.
If you want the JAR without the webhook step, use ./gradlew shadowJar directly — that task has no dependency on uploadToDiscord.

Running Tests

./gradlew test
The test suite uses JUnit 5 (junit-jupiter) and Mockito (mockito-core). SQLite’s JDBC driver is on the test classpath so persistence code can be exercised against an in-memory database. Notable coverage includes DiscordRateLimiterTest, which validates the bot’s request-throttling logic. Test reports are written to build/reports/tests/test/index.html.

Next Steps

Contributing

Turn your local build into a pull request.