import java.io.File import java.net.URI import java.util.zip.ZipFile // Remap tooling: rebuilds the Mojang-mapped 1.20.4 NMS classes at build time. buildscript { repositories { mavenCentral() maven("https://maven.fabricmc.net/") } dependencies { classpath("net.fabricmc:mapping-io:0.6.1") classpath("net.fabricmc:tiny-remapper:0.9.0") } } plugins { id("java") id("com.gradleup.shadow") version("9.6.1") id("xyz.jpenilla.run-paper") version("3.0.2") checkstyle pmd } group = "dev.loki" version = "1.3.3" description = "LoParkour - Advanced parkour plugin for Minecraft" // Multi-version build: pass -PmcVersion / -PapiCoordinate / -PjavaRelease / -PapiVersion // to build the same source against a different Paper API (e.g. GitHub Actions matrix). val mcVersion: String = providers.gradleProperty("mcVersion").getOrElse("1.20.4") val apiCoordinate: String = providers.gradleProperty("apiCoordinate") .getOrElse("org.spigotmc:spigot-api:$mcVersion-R0.1-SNAPSHOT") val javaRelease: Int = providers.gradleProperty("javaRelease").map { it.toInt() }.getOrElse(21) val apiVersion: String = providers.gradleProperty("apiVersion").getOrElse("1.20") repositories { mavenCentral() maven { name = "papermc" url = uri("https://repo.papermc.io/repository/maven-public/") } maven { name = "spigot-repo" url = uri("https://hub.spigotmc.org/nexus/content/repositories/snapshots/") } maven { name = "jitpack" url = uri("https://jitpack.io") } maven { name = "maven-central" url = uri("https://oss.sonatype.org/content/groups/public") } maven { name = "placeholderapi" url = uri("https://repo.extendedclip.com/content/repositories/placeholderapi/") } maven { name = "mv-repo" url = uri("https://repo.onarandombox.com/content/groups/public/") } maven { name = "dmulloy2-repo" url = uri("https://repo.dmulloy2.net/repository/public/") } maven { name = "codemc-repo" url = uri("https://repo.codemc.io/repository/maven-public/") } maven { name = "opencollab-snapshot-repo" url = uri("https://repo.opencollab.dev/maven-snapshots/") } // For ConfigUpdater dependency maven { name = "codemc-snapshots" url = uri("https://repo.codemc.io/repository/maven-snapshots/") } maven { name = "enginehub" url = uri("https://maven.enginehub.org/repo/") } } // --- Mojang-mapped NMS (net.minecraft.*) for the packet NPC renderer --------- // repo.papermc.io purged the old io.papermc.paper:paper:1.20.4-R0.1-SNAPSHOT // dev-bundle, so the 1.20.4 vanilla server jar (bundler/obfuscated) is remapped // with Mojang's official mappings via tiny-remapper into the same Mojang-mapped // classes the dev-bundle shipped. Result is cached under the Gradle user home. val nmsVersion = "1.20.4" val nmsCacheDir = gradle.gradleUserHomeDir.resolve("caches/loparkour-nms/$nmsVersion") val nmsServerJar = nmsCacheDir.resolve("server.jar") val nmsNestedJar = nmsCacheDir.resolve("server-nested.jar") val nmsMappings = nmsCacheDir.resolve("server-mappings.txt") val nmsTiny = nmsCacheDir.resolve("server-mappings.tiny") val nmsMappedJar = nmsCacheDir.resolve("server-mojang-mapped.jar") val nmsAuthlibJar = nmsCacheDir.resolve("authlib-6.0.52.jar") val remapNms by tasks.registering { outputs.files(nmsServerJar, nmsMappings, nmsMappedJar, nmsAuthlibJar) doLast { nmsCacheDir.mkdirs() fun download(url: String, target: File) { if (target.isFile) { return } URI(url).toURL().openStream().use { input -> target.outputStream().use { input.copyTo(it) } } } download("https://piston-data.mojang.com/v1/objects/8dd1a28015f51b1803213892b50b7b4fc76e594d/server.jar", nmsServerJar) download("https://piston-data.mojang.com/v1/objects/c1cafe916dd8b58ed1fe0564fc8f786885224e62/server.txt", nmsMappings) download("https://libraries.minecraft.net/com/mojang/authlib/6.0.52/authlib-6.0.52.jar", nmsAuthlibJar) if (!nmsNestedJar.isFile) { ZipFile(nmsServerJar).use { zip -> zip.getInputStream(zip.getEntry("META-INF/versions/$nmsVersion/server-$nmsVersion.jar")).use { input -> nmsNestedJar.outputStream().use { input.copyTo(it) } } } } val tree = net.fabricmc.mappingio.tree.MemoryMappingTree(false) net.fabricmc.mappingio.MappingReader.read(nmsMappings.toPath(), net.fabricmc.mappingio.format.MappingFormat.PROGUARD_FILE, tree) net.fabricmc.mappingio.MappingWriter.create(nmsTiny.toPath(), net.fabricmc.mappingio.format.MappingFormat.TINY_2_FILE).use { writer -> tree.accept(net.fabricmc.mappingio.adapter.MappingSourceNsSwitch(writer, "target")) } val remapper = net.fabricmc.tinyremapper.TinyRemapper.newRemapper() .withMappings(net.fabricmc.tinyremapper.TinyUtils.createTinyMappingProvider(nmsTiny.toPath(), "target", "source")) .ignoreConflicts(true) .rebuildSourceFilenames(true) .build() net.fabricmc.tinyremapper.OutputConsumerPath.Builder(nmsMappedJar.toPath()).build().use { output -> output.addNonClassFiles(nmsNestedJar.toPath()) remapper.readInputs(nmsNestedJar.toPath()) remapper.apply(output) } remapper.finish() } } tasks.withType().configureEach { dependsOn(remapNms) } // Resolve the org.bukkit:bukkit capability conflict that newer paper-api artifacts // introduce (they both bundle the bukkit classes and declare the capability). configurations.configureEach { resolutionStrategy.capabilitiesResolution.withCapability("org.bukkit:bukkit") { selectHighestVersion() } } dependencies { // Spigot/Paper API (overridable per MC version via -PapiCoordinate). // Newer paper-api artifacts declare a conflicting org.bukkit:bukkit capability // (they bundle the classes themselves) — drop the duplicate transitive. if (apiCoordinate.startsWith("io.papermc.paper:paper-api")) { val (apiGroup, apiName, apiVersion) = apiCoordinate.split(":") compileOnly(mapOf( "group" to apiGroup, "name" to apiName, "version" to apiVersion, "exclude" to mapOf("group" to "org.bukkit", "module" to "bukkit") )) } else { compileOnly(apiCoordinate) } // NMS (net.minecraft.*) for packet NPC rendering — compileOnly, Mojang-mapped. // Built once by the remapNms task above (vanilla 1.20.4 server + official // mappings) because the io.papermc.paper:paper:1.20.4 dev-bundle was purged. compileOnly(files(nmsMappedJar, nmsAuthlibJar)) // fastutil types are referenced by the NMS packet signatures (IntList). compileOnly("it.unimi.dsi:fastutil:8.5.9") // Adventure API (for Component) compileOnly("net.kyori:adventure-api:4.14.0") compileOnly("net.kyori:adventure-text-minimessage:4.14.0") // LoLib Core implementation(files("libs/lolib-3.0.0.jar")) // Shaded dependencies implementation("io.papermc:paperlib:1.0.7") implementation("com.google.code.gson:gson:2.10.1") implementation("org.bstats:bstats-bukkit:3.2.1") implementation("com.zaxxer:HikariCP:5.1.0") implementation("com.mysql:mysql-connector-j:8.3.0") implementation("org.apache.commons:commons-math3:3.6.1") compileOnly("com.sk89q.worldedit:worldedit-core:7.3.6") compileOnly("com.sk89q.worldedit:worldedit-bukkit:7.3.6") // Provided dependencies (plugins) compileOnly("me.clip:placeholderapi:2.11.6") compileOnly("com.github.MilkBowl:VaultAPI:1.7") compileOnly("me.filoghost.holographicdisplays:holographicdisplays-api:3.0.0-SNAPSHOT") compileOnly("org.geysermc.floodgate:api:2.0-SNAPSHOT") // Annotations compileOnly("org.jetbrains:annotations:24.1.0") // Testing testImplementation("org.junit.jupiter:junit-jupiter:5.11.0-M1") testImplementation("org.mockito:mockito-core:5.10.0") testImplementation("org.mockito:mockito-junit-jupiter:5.10.0") testImplementation("org.spigotmc:spigot-api:1.20.4-R0.1-SNAPSHOT") testRuntimeOnly("org.junit.platform:junit-platform-launcher") // Test runtime classpath for loading PlayerGhost (references NMS + authlib types). testImplementation(files(nmsMappedJar, nmsAuthlibJar)) } tasks.shadowJar { archiveClassifier.set("") archiveFileName.set("LoParkour-${project.version}-mc${mcVersion}.jar") // Relocate shaded dependencies relocate("dev.lolib", "dev.loki.loparkour.lib.lolib") relocate("io.papermc.lib", "dev.loki.loparkour.lib.paperlib") relocate("com.google.gson", "dev.loki.loparkour.lib.gson") relocate("org.bstats", "dev.loki.loparkour.lib.bstats") relocate("org.apache.commons.math3", "dev.loki.loparkour.lib.commonsmath3") // Don't minimize - causes issues with Caffeine's dynamically generated classes // minimize { // exclude(dependency("com.zaxxer:HikariCP:.*")) // exclude(dependency("com.mysql:mysql-connector-j:.*")) // exclude(dependency(files("libs/lolib-3.0.0.jar"))) // } } tasks.named("build") { dependsOn(tasks.named("shadowJar")) } tasks.withType { filesMatching(listOf("plugin.yml", "config.yml")) { expand( "version" to project.version, "description" to (project.description ?: ""), "apiVersion" to apiVersion ) } } tasks.withType { useJUnitPlatform() } tasks.withType { options.encoding = "UTF-8" options.release = javaRelease } tasks.withType { options.encoding = "UTF-8" } // Run paper server via gradlew runServer tasks { runServer { minecraftVersion("1.21.4") jvmArgs("-Xms2G", "-Xmx2G") runDirectory.set(layout.projectDirectory.dir("run")) downloadPlugins { modrinth("placeholderapi", "2.11.6") } } } checkstyle { toolVersion = "10.21.4" configFile = file("config/checkstyle/checkstyle.xml") isIgnoreFailures = false maxErrors = 0 // Warnings are NOT blocking: noisy/opinionated rules (MagicNumber, // FinalLocalVariable, EmptyLineSeparator, ClassFanOutComplexity, ...) run at // severity="warning" in checkstyle.xml and surface in build/reports/checkstyle/. } pmd { toolVersion = "7.0.0" ruleSetFiles = files(rootProject.file("config/pmd/ruleset.xml")) // PMD is now a HARD GATE (0 violations). Any new violation fails the build. isIgnoreFailures = false maxFailures = 0 } tasks.withType().configureEach { reports { xml.required.set(true) html.required.set(true) } } tasks.withType().configureEach { reports { xml.required.set(true) html.required.set(true) } // Ignore generated/shaded sources and tests for now (test sources are not analyzed). exclude("**/build/generated/**") } // Default task defaultTasks("clean", "build")