plugins { id 'net.fabricmc.fabric-loom' version '1.17.12' id 'maven-publish' id 'pmd' id 'checkstyle' id 'com.github.spotbugs' version '6.5.11' } repositories { mavenCentral() maven { url = 'https://maven.fabricmc.net/' } exclusiveContent { forRepository { maven { name = "Modrinth" url = "https://api.modrinth.com/maven" } } filter { includeGroup "maven.modrinth" } } maven { // ViaFabricPlus / ViaVersion url = 'https://repo.viaversion.com/everything' } maven { // ViaBedrock snapshots (dependency of ViaFabricPlus) url = 'https://repo.opencollab.dev/maven-snapshots/' } maven { // ViaBedrock snapshots (alt repo used by Via* stack) url = 'https://maven.lenni0451.net/snapshots' } maven { // Simple Voice Chat API (optional compat) url = 'https://maven.maxhenkel.de/repository/public' } maven { // JitPack (for com.github.* artifacts like CubeConverter) url = 'https://jitpack.io' } maven { url = uri("$rootDir/local-maven") } } version = project.mod_version group = project.maven_group base { archivesName = project.archives_base_name } def lovisualVulkanDebugEnabled = providers.gradleProperty("lovisualVulkanDebug") .map { it.toBoolean() } .orElse(false) def lovisualDevTaskRequested = gradle.startParameter.taskNames.any { it == "buildDev" || it.endsWith(":buildDev") } def lovisualDevEnabled = providers.gradleProperty("lovisualDev") .orElse(providers.gradleProperty("dev")) .orElse(providers.gradleProperty("devBuild")) .orElse(lovisualDevTaskRequested ? "true" : "false") .map { it.toBoolean() } .orElse(false) def svgMsdfGeneratedResources = layout.buildDirectory.dir("generated/resources/svgMsdf") def javetNativeGeneratedResources = layout.buildDirectory.dir("generated/resources/javetNatives") def javetVersion = "5.0.9" def targetJavaVersion = 25 sourceSets { main { resources.srcDir(svgMsdfGeneratedResources) } svgMsdfTool { java.srcDir("src/svgMsdfTool/java") compileClasspath += sourceSets.main.output.classesDirs + sourceSets.main.compileClasspath runtimeClasspath += output + compileClasspath } dev { java.srcDir("src/dev/java") resources.srcDir("src/dev/resources") compileClasspath += sourceSets.main.output + sourceSets.main.compileClasspath runtimeClasspath += output + compileClasspath } vulkanDebug { java.srcDir("src/vulkanDebug/java") resources.srcDir("src/vulkanDebug/resources") compileClasspath += sourceSets.main.output + sourceSets.main.compileClasspath runtimeClasspath += output + compileClasspath } } configurations { bundledLibrary javetNativeBundle compileOnly.extendsFrom bundledLibrary runtimeOnly.extendsFrom bundledLibrary devImplementation.extendsFrom implementation devCompileOnly.extendsFrom compileOnly devAnnotationProcessor.extendsFrom annotationProcessor vulkanDebugImplementation.extendsFrom implementation vulkanDebugCompileOnly.extendsFrom compileOnly vulkanDebugAnnotationProcessor.extendsFrom annotationProcessor } loom { mods { "lovisual" { sourceSet sourceSets.main if (lovisualDevEnabled.get()) { sourceSet sourceSets.dev } if (lovisualVulkanDebugEnabled.get()) { sourceSet sourceSets.vulkanDebug } } } } dependencies { compileOnly 'org.projectlombok:lombok:1.18.46' annotationProcessor 'org.projectlombok:lombok:1.18.46' testCompileOnly 'org.projectlombok:lombok:1.18.46' testAnnotationProcessor 'org.projectlombok:lombok:1.18.46' testImplementation platform('org.junit:junit-bom:5.14.4') testImplementation 'org.junit.jupiter:junit-jupiter' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' devCompileOnly 'org.projectlombok:lombok:1.18.46' devAnnotationProcessor 'org.projectlombok:lombok:1.18.46' vulkanDebugCompileOnly 'org.projectlombok:lombok:1.18.46' vulkanDebugAnnotationProcessor 'org.projectlombok:lombok:1.18.46' minecraft "com.mojang:minecraft:${project.minecraft_version}" implementation "net.fabricmc:fabric-loader:${project.loader_version}" // Loader 0.19.4 ships MixinExtras 0.5.4, which crashes on @Redirect(at) arrays with this Mixin; 0.5.5 handles them. implementation "io.github.llamalad7:mixinextras-fabric:${project.mixinextras_version}" include "io.github.llamalad7:mixinextras-fabric:${project.mixinextras_version}" implementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}" // Renderer targets. Sodium is required; Iris stays optional but available in dev runtime. compileOnly "maven.modrinth:sodium:${project.sodium_version}" compileOnly "maven.modrinth:iris:${project.iris_version}" compileOnly "maven.modrinth:immediatelyfast:${project.immediatelyfast_version}" if (providers.gradleProperty("withOptionalModRuntime").map { it.toBoolean() }.orElse(false).get()) { runtimeOnly "maven.modrinth:sodium:${project.sodium_version}" runtimeOnly "maven.modrinth:iris:${project.iris_version}" runtimeOnly "maven.modrinth:immediatelyfast:${project.immediatelyfast_version}" } // Dev-only Xaero API for optional triangulator waypoint compat. // Pulled from Modrinth Maven so IDE sources/classes follow the selected MC version. compileOnly "maven.modrinth:xaeros-minimap:${project.xaero_minimap_version}" compileOnly "maven.modrinth:xaeros-world-map:${project.xaero_worldmap_version}" // ViaFabricPlus (optional) compileOnly "com.viaversion:viafabricplus-api:${project.viafabricplus_version}" if (providers.gradleProperty("withViaFabricPlusRuntime").map { it.toBoolean() }.orElse(false).get()) { // Dev/runtime only (not bundled) to test locally. runtimeOnly "com.viaversion:viafabricplus:${project.viafabricplus_version}" } // Simple Voice Chat (optional): client plugin API for speaking indicators; the mod provides it at runtime. compileOnly "de.maxhenkel.voicechat:voicechat-api:${project.voicechat_api_version}" // Entity Culling (optional): typed compile-time API for shader ESP visibility compat. compileOnly "maven.modrinth:entityculling:${project.entityculling_version}" bundledLibrary "de.marhali:json5-java:3.0.0" // Linux MPRIS backend for the built-in media session service. bundledLibrary "com.github.hypfvieh:dbus-java-core:4.3.1" bundledLibrary "com.github.hypfvieh:dbus-java-transport-native-unixsocket:4.3.1" // Provided by Minecraft runtime; keep it out of the bundled jar. compileOnly 'com.google.code.gson:gson:2.14.0' // Provided by Minecraft/Fabric runtime; do not shade it into the mod jar. compileOnly 'org.slf4j:slf4j-api:2.0.17' bundledLibrary 'io.github.classgraph:classgraph:4.8.192' // Javet core only. Native V8 binaries are copied into resources/javet/natives. bundledLibrary "com.caoccao.javet:javet:${javetVersion}" // LuaJ: scripted hand/item poses for HoldMyItems (MIT, pure Java interpreter). bundledLibrary "org.luaj:luaj-jse:3.0.1" testImplementation "org.luaj:luaj-jse:3.0.1" // Javet in minimal V8 mode only: no Node runtime and no ARM native payloads. // ARM builds can be added later behind a separate opt-in distribution profile. javetNativeBundle "com.caoccao.javet:javet-v8-windows-x86_64:${javetVersion}" javetNativeBundle "com.caoccao.javet:javet-v8-linux-x86_64:${javetVersion}" javetNativeBundle "com.caoccao.javet:javet-v8-macos-x86_64:${javetVersion}" } tasks.named('test') { useJUnitPlatform() } tasks.register("validateIrisPatches", JavaExec) { group = "verification" description = "Compiles bundled Iris patch manifests against a shaderpack zip without modifying it." dependsOn classes classpath = sourceSets.main.runtimeClasspath mainClass = "dev.loki.lovisual.render.iris.patch.ShaderPatchCompilerCli" doFirst { if (!project.hasProperty("shaderpack")) { throw new GradleException("Pass -Pshaderpack=") } args project.property("shaderpack") } } tasks.register("generateSvgMsdf", Exec) { group = "render assets" description = "Generates MTSDF texture assets for tintable SVG icons." dependsOn tasks.named("compileSvgMsdfToolJava") def inputDir = file("src/main/resources/assets/lovisual/svg") def outputDir = svgMsdfGeneratedResources.map { it.dir("assets/lovisual/svg/msdf") } def script = file("tools/msdf/GenerateSvgMsdf.ps1") def preprocessorClasspath = sourceSets.svgMsdfTool.output.classesDirs.asPath + File.pathSeparator + sourceSets.main.output.classesDirs.asPath + File.pathSeparator + sourceSets.main.compileClasspath.asPath def generatorJava = javaToolchains.launcherFor { languageVersion = JavaLanguageVersion.of(targetJavaVersion) } inputs.files(fileTree(inputDir) { include "**/*.svg" exclude "msdf/**" }) inputs.file(script) outputs.dir(outputDir) onlyIf { if (!script.isFile()) { logger.warn("SVG MSDF generator missing: ${script}") return false } if (!org.gradle.internal.os.OperatingSystem.current().isWindows()) { logger.warn("SVG MSDF generation requires Windows (PowerShell); skipping on ${org.gradle.internal.os.OperatingSystem.current().getFamilyName()}") return false } return true } doFirst { outputDir.get().asFile.mkdirs() } commandLine "powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", script.absolutePath, "-InputRoot", inputDir.path, "-OutRoot", outputDir.get().asFile.path, "-PreprocessorClasspath", preprocessorClasspath, "-JavaExe", generatorJava.get().executablePath.asFile.absolutePath } tasks.register("unpackJavetNatives", Copy) { group = "runtime assets" description = "Copies bundled Javet V8 native binaries into generated resources." into javetNativeGeneratedResources.map { it.dir("javet/natives") } from({ configurations.javetNativeBundle.collect { zipTree(it) } }) { include "**/*.dll", "**/*.so", "**/*.dylib" eachFile { details -> details.path = details.name } includeEmptyDirs = false } duplicatesStrategy = DuplicatesStrategy.EXCLUDE } tasks.register("syncJavetNatives", Copy) { group = "runtime assets" description = "Copies Javet V8 native binaries into src/main/resources/javet/natives for committing/debugging." into file("src/main/resources/javet/natives") from({ configurations.javetNativeBundle.collect { zipTree(it) } }) { include "**/*.dll", "**/*.so", "**/*.dylib" eachFile { details -> details.path = details.name } includeEmptyDirs = false } duplicatesStrategy = DuplicatesStrategy.EXCLUDE } processResources { duplicatesStrategy = DuplicatesStrategy.EXCLUDE dependsOn tasks.named("generateSvgMsdf") dependsOn tasks.named("unpackJavetNatives") from(javetNativeGeneratedResources) inputs.property "version", project.version inputs.property "minecraft_version", project.minecraft_version inputs.property "lovisual_dev", lovisualDevEnabled.get() inputs.property "lovisual_vulkan_debug", lovisualVulkanDebugEnabled.get() filteringCharset "UTF-8" filesMatching("fabric.mod.json") { expand "version": project.version, "minecraft_version": project.minecraft_version, "loader_version": project.loader_version } doLast { if (!lovisualDevEnabled.get() && !lovisualVulkanDebugEnabled.get()) return def metadataFile = new File(destinationDir, "fabric.mod.json") def metadata = new groovy.json.JsonSlurper().parse(metadataFile) def mixins = metadata.mixins instanceof java.util.Collection ? new ArrayList(metadata.mixins) : new ArrayList() if (lovisualDevEnabled.get() && !mixins.contains("lovisual-dev.mixins.json")) { mixins.add("lovisual-dev.mixins.json") } if (!mixins.contains("lovisual-vulkan-debug.mixins.json")) { if (lovisualVulkanDebugEnabled.get()) { mixins.add("lovisual-vulkan-debug.mixins.json") } } metadata.mixins = mixins metadataFile.setText( groovy.json.JsonOutput.prettyPrint(groovy.json.JsonOutput.toJson(metadata)) + System.lineSeparator(), "UTF-8" ) } } tasks.named("processDevResources") { duplicatesStrategy = DuplicatesStrategy.EXCLUDE } tasks.withType(JavaCompile).configureEach { // ensure that the encoding is set to UTF-8, no matter what the system default is // this fixes some edge cases with special characters not displaying correctly // see http://yodaconditions.net/blog/fix-for-java-file-encoding-problems-with-gradle.html // If Javadoc is generated, this must be specified in that task too. it.options.encoding = "UTF-8" if (targetJavaVersion >= 10 || JavaVersion.current().isJava10Compatible()) { it.options.release.set(targetJavaVersion) } } tasks.named("compileDevJava").configure { enabled = lovisualDevEnabled.get() } tasks.named("processDevResources").configure { enabled = lovisualDevEnabled.get() } tasks.named("compileVulkanDebugJava").configure { enabled = lovisualVulkanDebugEnabled.get() } tasks.named("processVulkanDebugResources").configure { enabled = lovisualVulkanDebugEnabled.get() } java { def javaVersion = JavaVersion.toVersion(targetJavaVersion) if (JavaVersion.current() < javaVersion) { toolchain.languageVersion = JavaLanguageVersion.of(targetJavaVersion) } // Loom will automatically attach sourcesJar to a RemapSourcesJar task and to the "build" task // if it is present. // If you remove this line, sources will not be generated. withSourcesJar() } pmd { toolVersion = '7.27.0' ruleSetFiles = files("config/pmd/ruleset.xml") ruleSets = [] ignoreFailures = true consoleOutput = true sourceSets = [sourceSets.main] } checkstyle { toolVersion = '14.1.0' configFile = file("config/checkstyle/checkstyle.xml") ignoreFailures = true showViolations = true sourceSets = [sourceSets.main] } spotbugs { toolVersion = '4.10.3' ignoreFailures = true showProgress = true } spotbugsMain { reports { html.required = true xml.required = false } } tasks.withType(com.github.spotbugs.snom.SpotBugsTask).configureEach { if (it.name != "spotbugsMain") { it.enabled = false } } tasks.register("checkFolderLimit") { group = "verification" description = "Проверяет правило ≤4 файла на папку (проектное)" doLast { def base = file("src/main/java") def bad = [] base.eachDirRecurse { dir -> def files = dir.listFiles({ f -> f.name.endsWith(".java") } as FileFilter) def count = files != null ? files.length : 0 if (count > 4) bad << "${dir} : ${count}" } if (!bad.isEmpty()) { logger.warn("Папок >4 файлов:\n" + bad.join("\n")) if (!project.hasProperty("ignoreFolderLimit")) { throw new GradleException("Правило ≤4 файла/папку нарушено:\n" + bad.join("\n") + "\nИсправь разбивкой на подпапки") } } else { logger.lifecycle("checkFolderLimit: OK (0 папок >4)") } } } check.dependsOn("checkFolderLimit") tasks.matching { it.name == "sourcesJar" }.configureEach { dependsOn tasks.named("generateSvgMsdf") exclude("javet/natives/**") } jar { manifest { attributes("Multi-Release": "true") } from("LICENSE") { rename { "${it}_${project.archives_base_name}" } } from { configurations.bundledLibrary.collect { it.isDirectory() ? it : zipTree(it) } } exclude "com/google/gson/**" exclude "org/slf4j/**" exclude "META-INF/*.RSA", "META-INF/*.SF", "META-INF/*.DSA" duplicatesStrategy = DuplicatesStrategy.EXCLUDE if (lovisualDevEnabled.get()) { dependsOn tasks.named("compileDevJava"), tasks.named("processDevResources") from(sourceSets.dev.output) } if (lovisualVulkanDebugEnabled.get()) { dependsOn tasks.named("compileVulkanDebugJava"), tasks.named("processVulkanDebugResources") from(sourceSets.vulkanDebug.output) } } // Gradle cannot accept an arbitrary global `--dev` flag for the built-in `build` task // from build.gradle itself. Use this alias or `-PlovisualDev=true`. tasks.register("buildDev") { group = "build" description = "Builds LoVisual with development source set and dev metadata enabled." dependsOn "build" } // configure the maven publication publishing { publications { create("mavenJava", MavenPublication) { artifactId = project.archives_base_name from components.java } } // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. repositories { maven { name = "GitHubPackages" url = uri("https://maven.pkg.github.com/loki5512344/lovisual-client") credentials { username = findProperty("gpr.user") ?: System.getenv("GITHUB_ACTOR") ?: "" password = findProperty("gpr.key") ?: System.getenv("GITHUB_TOKEN") ?: System.getenv("GH_TOKEN") ?: "" } } } }