LoVisual/mod/build.gradle
loki5512344 e00ea8eb11
feat: frontend perf and a11y pass, plus mod visuals, hero main menu, Simple Voice Chat and vanilla chat autocomplete
Frontend:
- replace the i18next stack with a small typed i18n module
- hero clouds removed, llms.txt, HUD widget ARIA fixes and tests

Mod:
- TargetESP constellation mode; tracker and colors extracted
- JumpCircles: 7 new shapes, shared animation and color pipeline
- KillEffect blood mode with Burst/Fountain/Ring/Spray styles
- main menu: menuLayout setting (orbs/hero) and hero screen
- Simple Voice Chat soft integration: VoiceChat module and HUD panel
- renderer/quick: static Draw facade with glass outline
- vanilla chat: Baritone-style autocomplete popup for the % command prefix
2026-10-01 21:29:54 +02:00

480 lines
18 KiB
Groovy
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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=<path-to-shaderpack.zip>")
}
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") ?: ""
}
}
}
}