import art.arcane.volmit.packaging.PackagingMode import org.gradle.api.file.CopySpec import org.gradle.api.file.DeleteSpec import org.gradle.api.file.FileSystemOperations import org.gradle.api.file.FileTree import org.gradle.api.plugins.JavaPlugin import org.gradle.api.tasks.Copy import org.gradle.api.tasks.compile.JavaCompile import org.gradle.jvm.tasks.Jar import org.gradle.jvm.toolchain.JavaLanguageVersion import javax.inject.Inject /* * Iris is a World Generator for Minecraft Bukkit Servers * Copyright (c) 2021 Arcane Arts (Volmit Software) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ buildscript { repositories { maven { url = uri('https://jitpack.io') } } dependencies { classpath('com.github.VolmitSoftware:NMSTools:c88961416f') } } buildscript { repositories { mavenCentral() maven { url = uri('https://jitpack.io') } } dependencies { String volmLibRevision = providers.gradleProperty('volmLibCoordinate').get().tokenize(':').last() classpath("com.github.VolmitSoftware.VolmLib:packaging:${volmLibRevision}") } } plugins { id 'java' id 'java-library' } apply plugin: 'art.arcane.volmit-packaging' apply from: file('gradle/runtime-relocations.gradle') interface IrisFileOperations { @Inject FileSystemOperations getFileSystemOperations() } IrisFileOperations irisFileOperations = objects.newInstance(IrisFileOperations) group = 'art.arcane' version = providers.gradleProperty('irisVersion').getOrElse('4.1.1-26.2') project(':core') { group = 'art.arcane' version = rootProject.version } String minecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse('26.2') // The Bukkit jar runs Paper 26.1.2 through 26.2 (compile-low pins); the loader jars stay 26.2-only. String bukkitMinecraftRange = providers.gradleProperty('bukkitMinecraftRange').getOrElse('26.1.2-26.2') String fabricLoaderVersion = providers.gradleProperty('fabricLoaderVersion').getOrElse('0.19.3') String forgeVersion = providers.gradleProperty('forgeVersion').getOrElse('26.2-65.1.1') String neoForgeVersion = providers.gradleProperty('neoForgeVersion').getOrElse('26.2.0.59') String volmLibCoordinate = providers.gradleProperty('volmLibCoordinate') .orElse('com.github.VolmitSoftware:VolmLib:master-SNAPSHOT') .get() String useLocalVolmLib = providers.gradleProperty('useLocalVolmLib').getOrElse('true') String localVolmLibDirectory = providers.gradleProperty('localVolmLibDirectory').getOrNull() boolean useMavenLocal = providers.gradleProperty('useMavenLocal') .orElse(useLocalVolmLib.equalsIgnoreCase('true') ? 'false' : 'true') .map { String value -> value.equalsIgnoreCase('true') } .get() Closure loaderDisplayVersion = { String loaderVersion -> String coordinatePrefix = "${minecraftVersion}-" if (loaderVersion.startsWith(coordinatePrefix)) { return loaderVersion.substring(coordinatePrefix.length()) } return loaderVersion } Closure irisArtifactName = { String platform, String targetVersion -> return "Iris v${project.version} [${platform}] ${targetVersion}.jar" } String bukkitArtifactName = irisArtifactName('CraftBukkit', bukkitMinecraftRange) String fabricArtifactName = irisArtifactName('Fabric', "${minecraftVersion}+${loaderDisplayVersion(fabricLoaderVersion)}") String forgeArtifactName = irisArtifactName('Forge', "${minecraftVersion}+${loaderDisplayVersion(forgeVersion)}") String neoForgeArtifactName = irisArtifactName('NeoForge', "${minecraftVersion}+${loaderDisplayVersion(neoForgeVersion)}") Map runtimeDownloadRelocations = (Map) ext.runtimeDownloadRelocations List runtimeDownloadedPrefixes = (List) ext.runtimeDownloadedPrefixes File pluginOutDirectory = layout.projectDirectory.dir('../PluginOuts').asFile File legacyBukkitJar = layout.buildDirectory.file("libs/Iris-${project.version}.jar").get().asFile File bukkitArtifact = layout.buildDirectory.file("libs/${bukkitArtifactName}").get().asFile File consumerBukkitArtifact = layout.buildDirectory.file("consumer/${bukkitArtifactName}").get().asFile File fabricArtifact = layout.projectDirectory.file("adapters/fabric/build/libs/${fabricArtifactName}").asFile File forgeArtifact = layout.projectDirectory.file("adapters/forge/build/libs/${forgeArtifactName}").asFile File neoForgeArtifact = layout.projectDirectory.file("adapters/neoforge/build/libs/${neoForgeArtifactName}").asFile File consumerDirectory = layout.buildDirectory.dir('consumer').get().asFile FileTree staleConsumerBukkitJars = fileTree(consumerDirectory) { include('Iris v* [CraftBukkit] *.jar') } Map staleOutJars = [ CraftBukkit: 'Iris v* [CraftBukkit] *.jar', Fabric : 'Iris v* [Fabric] *.jar', Forge : 'Iris v* [Forge] *.jar', NeoForge : 'Iris v* [NeoForge] *.jar' ].collectEntries { String platform, String pattern -> [(platform): fileTree(pluginOutDirectory) { include(pattern) }] } apply plugin: ApiGenerator // Where `buildAll` drops the per-platform jars for a local test server. Use the approved sibling // Multiplexor workspace when it exists, with a repo-local fallback for fresh clones. File multiplexorConsumerLocation = new File(rootDir, '../../[Minecraft Server]/consumers').canonicalFile String defaultConsumerLocation = multiplexorConsumerLocation.isDirectory() ? multiplexorConsumerLocation.absolutePath : layout.buildDirectory.dir('consumers').get().asFile.absolutePath String consumerLocation = providers.gradleProperty('location') .getOrElse(defaultConsumerLocation) String bukkitConsumerPath = "${consumerLocation}/plugin-consumers/dropins/plugins" String fabricConsumerPath = "${consumerLocation}/fabric-mod-consumers/dropins/mods" String forgeConsumerPath = "${consumerLocation}/forge-mod-consumers/dropins/mods" String neoForgeConsumerPath = "${consumerLocation}/neoforge-mod-consumers/dropins/mods" def nmsBindings = [ v26_2_R1: '26.1.2.build.74-stable', ] Class nmsTypeClass = Class.forName('NMSBinding$Type') nmsBindings.each { key, value -> project(":adapters:bukkit:nms:${key}") { apply plugin: JavaPlugin def nmsConfig = new Config() nmsConfig.jvm = 25 nmsConfig.version = value nmsConfig.type = Enum.valueOf(nmsTypeClass, 'USER_DEV') extensions.extraProperties.set('nms', nmsConfig) plugins.apply(NMSBinding) sourceSets.main.java.srcDir(rootProject.file('adapters/minecraft-common/src/main/java')) dependencies { compileOnly(project(':core')) compileOnly(volmLibCoordinate) { transitive = false } compileOnly(rootProject.libs.annotations) compileOnly(rootProject.libs.byteBuddy.core) testImplementation('junit:junit:4.13.2') testImplementation('org.mockito:mockito-core:5.23.0') testImplementation(project(':core')) testImplementation(testFixtures(project(':core'))) testCompileOnly(volmLibCoordinate) { transitive = false } testRuntimeOnly(project(':core')) testRuntimeOnly(rootProject.libs.caffeine) } } } def included = configurations.create('included') def jarJar = configurations.create('jarJar') dependencies { nmsBindings.keySet().each { key -> add('included', project(path: ":adapters:bukkit:nms:${key}", configuration: 'runtimeElements')) } add('included', project(path: ':core', configuration: 'shadow')) add('included', project(path: ':adapters:bukkit:plugin', configuration: 'runtimeElements')) add('jarJar', project(':core:agent')) } tasks.named('jar', Jar).configure { inputs.files(included) duplicatesStrategy = DuplicatesStrategy.EXCLUDE includeEmptyDirs = false from(jarJar) from(provider { included.resolve().collect { zipTree(it) } }) { exclude('languages/**') } doFirst { irisFileOperations.fileSystemOperations.delete { DeleteSpec spec -> spec.delete(legacyBukkitJar) } } archiveFileName.set(bukkitArtifactName) } tasks.register('iris', Copy) { group = 'iris' dependsOn('jar') from(layout.buildDirectory.file("libs/${bukkitArtifactName}")) into(layout.buildDirectory.dir('consumer')) doFirst { irisFileOperations.fileSystemOperations.delete { DeleteSpec spec -> spec.delete(staleConsumerBukkitJars) } } } tasks.register('buildSwiftSwamp') { group = 'iris' description = 'Builds every platform jar and drops them in the local BUILDS output directory.' outputs.upToDateWhen { false } dependsOn('iris', 'verifyModdedArtifacts') File swiftSwampOut = file('C://VolmitSoftware/BUILDS') Map swiftSwampSources = [ 'Iris.jar' : consumerBukkitArtifact, 'Iris-Fabric.jar' : fabricArtifact, 'Iris-Forge.jar' : forgeArtifact, 'Iris-Neoforge.jar': neoForgeArtifact ] doLast { swiftSwampSources.each { String target, File source -> irisFileOperations.fileSystemOperations.copy { CopySpec spec -> spec.from(source) spec.into(swiftSwampOut) spec.rename { String ignored -> target } } } println('') println('=== Iris buildSwiftSwamp -> ' + swiftSwampOut + ' ===') ['Iris.jar', 'Iris-Fabric.jar', 'Iris-Forge.jar', 'Iris-Neoforge.jar'].each { String jar -> File produced = new File(swiftSwampOut, jar) println(String.format(' %-24s %8d KB', jar, (long) (produced.length() / 1024))) } } } tasks.register('buildBukkit', Copy) { group = 'iris' dependsOn('jar') from(layout.buildDirectory.file("libs/${bukkitArtifactName}")) into(pluginOutDirectory) File legacyOutJar = new File(pluginOutDirectory, "Iris-${project.version}+mc${minecraftVersion}.jar") doFirst { irisFileOperations.fileSystemOperations.delete { DeleteSpec spec -> spec.delete(staleOutJars['CraftBukkit'], legacyOutJar) } } } // The three loader jars come out of nested standalone Gradle builds driven through Exec. The outer // task therefore has to carry the full inventory of what those builds read: every shared source tree, // every build script and version file they load, and the local VolmLib checkout when the composite // resolves one. Anything missing from that list publishes a stale jar. Closure resolveLocalVolmLibDirectory = { -> String configured = localVolmLibDirectory != null && !localVolmLibDirectory.isBlank() ? localVolmLibDirectory : System.getenv('VOLMLIB_DIR') if (configured != null && !configured.isBlank()) { File configuredDirectory = new File(configured) if (!configuredDirectory.isAbsolute()) { configuredDirectory = new File(rootDir, configured) } return configuredDirectory.isDirectory() ? configuredDirectory : null } File current = rootDir while (current != null) { File candidate = new File(current, 'VolmLib') if (new File(candidate, 'settings.gradle').isFile() || new File(candidate, 'settings.gradle.kts').isFile()) { return candidate } current = current.parentFile } return null } List moddedSharedInputs = [] [ 'adapters/modded-common/src', 'adapters/minecraft-common/src', 'adapters/client-common/src', 'core/src/main', 'core/build.gradle', 'core/agent/src', 'core/agent/build.gradle', 'spi/src/main', 'spi/build.gradle', 'build.gradle', 'settings.gradle', 'gradle.properties', 'lombok.config', 'gradle/libs.versions.toml', 'gradle/volmlib-resolution.settings.gradle', 'gradle/generation-build-revision.gradle', 'gradle/modded-generation-build-revision.gradle' ].each { String path -> moddedSharedInputs.add(layout.projectDirectory.file(path).asFile) } File localVolmLibCheckout = useLocalVolmLib.equalsIgnoreCase('true') ? resolveLocalVolmLibDirectory() : null if (localVolmLibCheckout != null) { moddedSharedInputs.add(new File(localVolmLibCheckout, 'shared/src/main')) } Closure declareModdedJarIo = { Exec task, String adapter, String artifactName, Map versions -> task.inputs.files(moddedSharedInputs).withPropertyName('moddedSharedSources') task.inputs.files( layout.projectDirectory.file("adapters/${adapter}/src").asFile, layout.projectDirectory.file("adapters/${adapter}/build.gradle").asFile, layout.projectDirectory.file("adapters/${adapter}/settings.gradle").asFile ).withPropertyName('adapterSources') versions.each { String key, String value -> task.inputs.property(key, value) } task.inputs.property('useLocalVolmLib', useLocalVolmLib) task.inputs.property('volmLibCoordinate', volmLibCoordinate) task.inputs.property('localVolmLibCheckout', localVolmLibCheckout == null ? '' : localVolmLibCheckout.absolutePath) task.outputs.file(layout.projectDirectory.file("adapters/${adapter}/build/libs/${artifactName}")) return null } tasks.register('fabricJar', Exec) { group = 'iris' workingDir = layout.projectDirectory.dir('adapters/fabric').asFile String wrapperScript = System.getProperty('os.name').toLowerCase().contains('windows') ? 'gradlew.bat' : 'gradlew' List command = [ layout.projectDirectory.file(wrapperScript).asFile.absolutePath, 'shadowJar', '--console=plain', "-PirisVersion=${project.version}", "-PminecraftVersion=${minecraftVersion}", "-PfabricLoaderVersion=${fabricLoaderVersion}", "-PuseLocalVolmLib=${useLocalVolmLib}", "-PvolmLibCoordinate=${volmLibCoordinate}" ] if (localVolmLibDirectory != null && !localVolmLibDirectory.isBlank()) { command.add("-PlocalVolmLibDirectory=${localVolmLibDirectory}") } commandLine(command) declareModdedJarIo(it, 'fabric', fabricArtifactName, [ irisVersion: project.version.toString(), minecraftVersion: minecraftVersion, fabricLoaderVersion: fabricLoaderVersion ]) } tasks.register('buildFabric', Copy) { group = 'iris' dependsOn('fabricJar') from(layout.projectDirectory.file("adapters/fabric/build/libs/${fabricArtifactName}")) into(pluginOutDirectory) File legacyOutJar = new File(pluginOutDirectory, "Iris-${project.version}+mc${minecraftVersion}-fabric.jar") doFirst { irisFileOperations.fileSystemOperations.delete { DeleteSpec spec -> spec.delete(staleOutJars['Fabric'], legacyOutJar) } } } tasks.register('forgeJar', Exec) { group = 'iris' workingDir = layout.projectDirectory.dir('adapters/forge').asFile String wrapperScript = System.getProperty('os.name').toLowerCase().contains('windows') ? 'gradlew.bat' : 'gradlew' List command = [ layout.projectDirectory.file(wrapperScript).asFile.absolutePath, 'shadowJar', '--console=plain', "-PirisVersion=${project.version}", "-PminecraftVersion=${minecraftVersion}", "-PforgeVersion=${forgeVersion}", "-PuseLocalVolmLib=${useLocalVolmLib}", "-PvolmLibCoordinate=${volmLibCoordinate}" ] if (localVolmLibDirectory != null && !localVolmLibDirectory.isBlank()) { command.add("-PlocalVolmLibDirectory=${localVolmLibDirectory}") } commandLine(command) declareModdedJarIo(it, 'forge', forgeArtifactName, [ irisVersion: project.version.toString(), minecraftVersion: minecraftVersion, forgeVersion: forgeVersion ]) } tasks.register('buildForge', Copy) { group = 'iris' dependsOn('forgeJar') from(layout.projectDirectory.file("adapters/forge/build/libs/${forgeArtifactName}")) into(pluginOutDirectory) File legacyOutJar = new File(pluginOutDirectory, "Iris-${project.version}+mc${minecraftVersion}-forge.jar") doFirst { irisFileOperations.fileSystemOperations.delete { DeleteSpec spec -> spec.delete(staleOutJars['Forge'], legacyOutJar) } } } tasks.register('neoforgeJar', Exec) { group = 'iris' workingDir = layout.projectDirectory.dir('adapters/neoforge').asFile String wrapperScript = System.getProperty('os.name').toLowerCase().contains('windows') ? 'gradlew.bat' : 'gradlew' List command = [ layout.projectDirectory.file(wrapperScript).asFile.absolutePath, 'shadowJar', '--console=plain', "-PirisVersion=${project.version}", "-PminecraftVersion=${minecraftVersion}", "-PneoForgeVersion=${neoForgeVersion}", "-PuseLocalVolmLib=${useLocalVolmLib}", "-PvolmLibCoordinate=${volmLibCoordinate}" ] if (localVolmLibDirectory != null && !localVolmLibDirectory.isBlank()) { command.add("-PlocalVolmLibDirectory=${localVolmLibDirectory}") } commandLine(command) declareModdedJarIo(it, 'neoforge', neoForgeArtifactName, [ irisVersion: project.version.toString(), minecraftVersion: minecraftVersion, neoForgeVersion: neoForgeVersion ]) } tasks.register('buildNeoforge', Copy) { group = 'iris' dependsOn('neoforgeJar') from(layout.projectDirectory.file("adapters/neoforge/build/libs/${neoForgeArtifactName}")) into(pluginOutDirectory) File legacyOutJar = new File(pluginOutDirectory, "Iris-${project.version}+mc${minecraftVersion}-neoforge.jar") doFirst { irisFileOperations.fileSystemOperations.delete { DeleteSpec spec -> spec.delete(staleOutJars['NeoForge'], legacyOutJar) } } } tasks.named('forgeJar').configure { mustRunAfter(tasks.named('fabricJar')) } tasks.named('fabricJar').configure { mustRunAfter(tasks.named('jar')) mustRunAfter(tasks.named('buildBukkit')) mustRunAfter(project(':spi').tasks.named('jar')) } tasks.named('neoforgeJar').configure { mustRunAfter(tasks.named('forgeJar')) } // adapters/modded-common has no build of its own: its sources are compiled by each loader wrapper, so // its tests only run when one of those wrappers is asked for `test`. The Fabric wrapper is the one // that runs them as plain JUnit - NeoForge's moddev unit-test harness boots the mod through FML's // transforming class loader and dies on a split-package ServiceConfigurationError before any test // starts. One loader is enough: the sources are identical across all three. tasks.register('moddedTest', Exec) { group = 'verification' description = 'Runs the adapters/modded-common test suite through the Fabric adapter build.' workingDir = layout.projectDirectory.dir('adapters/fabric').asFile String wrapperScript = System.getProperty('os.name').toLowerCase().contains('windows') ? 'gradlew.bat' : 'gradlew' List command = [ layout.projectDirectory.file(wrapperScript).asFile.absolutePath, 'test', '--console=plain', "-PirisVersion=${project.version}", "-PminecraftVersion=${minecraftVersion}", "-PfabricLoaderVersion=${fabricLoaderVersion}", "-PuseLocalVolmLib=${useLocalVolmLib}", "-PvolmLibCoordinate=${volmLibCoordinate}" ] if (localVolmLibDirectory != null && !localVolmLibDirectory.isBlank()) { command.add("-PlocalVolmLibDirectory=${localVolmLibDirectory}") } commandLine(command) inputs.files(moddedSharedInputs).withPropertyName('moddedSharedSources') inputs.files( layout.projectDirectory.file('adapters/fabric/src').asFile, layout.projectDirectory.file('adapters/fabric/build.gradle').asFile, layout.projectDirectory.file('adapters/fabric/settings.gradle').asFile ).withPropertyName('adapterSources') inputs.property('irisVersion', project.version.toString()) inputs.property('minecraftVersion', minecraftVersion) inputs.property('fabricLoaderVersion', fabricLoaderVersion) inputs.property('useLocalVolmLib', useLocalVolmLib) inputs.property('volmLibCoordinate', volmLibCoordinate) inputs.property('localVolmLibCheckout', localVolmLibCheckout == null ? '' : localVolmLibCheckout.absolutePath) outputs.dir(layout.projectDirectory.dir('adapters/fabric/build/test-results/test')) mustRunAfter(tasks.named('jar')) mustRunAfter(project(':core').tasks.named('check')) mustRunAfter(project(':spi').tasks.named('jar')) } tasks.named('fabricJar').configure { mustRunAfter(tasks.named('moddedTest')) } tasks.named('check').configure { dependsOn('moddedTest') } List commonModdedArtifactEntries = [ 'META-INF/iris/generation-kernels/catalog.tsv', 'META-INF/iris/generation-kernels/abi-1.revision', 'META-INF/services/art.arcane.iris.localization.IrisMessageContributor', 'art/arcane/iris/generation/runtime/IrisEngine.class', 'art/arcane/iris/spi/IrisPlatform.class', 'art/arcane/volmlib/util/mantle/io/Lz4IOWorkerCodecSupport.class', 'art/arcane/iris/platform/bootstrap/getHardware.class', // Both shared mixin configs plus the lang file and mod icon every loader's metadata points at. // A shadowJar exclude or a renamed resource that drops any of these is silent at build time and // shows up as a non-applied mixin or a missing-translation report from a user. 'irisworldgen.entity.mixins.json', 'irisworldgen.client.mixins.json', 'assets/irisworldgen/lang/en_us.json', 'assets/irisworldgen/icon.png' ] // The modded jars ship the Bukkit-only code paths inert - excluding them means chasing the whole core // reference graph. What must not slip in is new Bukkit coupling in core, which core already declares // in purity-allowlist.txt, so the baseline is read from there instead of frozen by hand. File bukkitPurityAllowlist = layout.projectDirectory.file('core/purity-allowlist.txt').asFile Set bukkitCoupledCoreClasses = bukkitPurityAllowlist.readLines() .findAll { String line -> !line.isBlank() } .collect { String line -> line.trim() - ~/\.java$/ } .toSet() tasks.register('verifyFabricArtifact') { group = 'verification' dependsOn('fabricJar') File artifact = fabricArtifact inputs.file(artifact) inputs.file(bukkitPurityAllowlist) doLast { List requiredEntries = commonModdedArtifactEntries + [ 'fabric.mod.json', 'irisworldgen.mixins.json', 'irisworldgen.accesswidener', 'art/arcane/iris/fabric/IrisFabricBootstrap.class' ] ModdedArtifactVerifier.verify(artifact, requiredEntries, bukkitCoupledCoreClasses) logger.lifecycle("Verified ${artifact.name} packaging, mixin compat, and nested jars") } } tasks.register('verifyForgeArtifact') { group = 'verification' dependsOn('forgeJar') File artifact = forgeArtifact inputs.file(artifact) inputs.file(bukkitPurityAllowlist) doLast { List requiredEntries = commonModdedArtifactEntries + [ 'META-INF/mods.toml', 'META-INF/accesstransformer.cfg', 'art/arcane/iris/forge/IrisForgeBootstrap.class' ] ModdedArtifactVerifier.verify(artifact, requiredEntries, bukkitCoupledCoreClasses) logger.lifecycle("Verified ${artifact.name} packaging, mixin compat, and nested jars") } } tasks.register('verifyNeoforgeArtifact') { group = 'verification' dependsOn('neoforgeJar') File artifact = neoForgeArtifact inputs.file(artifact) inputs.file(bukkitPurityAllowlist) doLast { List requiredEntries = commonModdedArtifactEntries + [ 'META-INF/neoforge.mods.toml', 'META-INF/accesstransformer.cfg', 'art/arcane/iris/neoforge/IrisNeoForgeBootstrap.class' ] ModdedArtifactVerifier.verify(artifact, requiredEntries, bukkitCoupledCoreClasses) logger.lifecycle("Verified ${artifact.name} packaging, mixin compat, and nested jars") } } tasks.register('verifyModdedArtifacts') { group = 'verification' dependsOn('verifyFabricArtifact', 'verifyForgeArtifact', 'verifyNeoforgeArtifact') } // Entries the Bukkit artifact only ever reaches by name - NMS binding lookup, the SIMD kernels, the // instrumentation agent, the JarScanner-driven command and service discovery, the Matter codec - plus // the two plugin descriptors and the slimjar manifests. Nothing in the reference graph points at // them, so a shadowJar exclude that drops one is silent until a server hits that path. File commandSourceRoot = layout.projectDirectory.dir('core/src/main/java').asFile FileTree commandHandlerSources = fileTree(commandSourceRoot) { include('art/arcane/iris/command/handlers/**/*.java') include('art/arcane/iris/command/context/**/*.java') include('art/arcane/iris/command/specialhandlers/**/*.java') } List requiredCommandHandlerEntries = commandHandlerSources.files.collect { File source -> commandSourceRoot.toPath().relativize(source.toPath()).toString() .replace(File.separator, '/').replaceAll(/\.java$/, '.class') }.sort() FileTree mantleComponentSources = fileTree(commandSourceRoot) { include('art/arcane/iris/generation/mantle/**/*.java') } List requiredMantleComponentEntries = mantleComponentSources.files.findAll { File source -> source.getText('UTF-8') =~ /(?m)^\s*@ComponentFlag\b/ }.collect { File source -> commandSourceRoot.toPath().relativize(source.toPath()).toString() .replace(File.separator, '/').replaceAll(/\.java$/, '.class') }.sort() List requiredBukkitArtifactEntries = [ 'META-INF/iris/generation-kernels/catalog.tsv', 'META-INF/iris/generation-kernels/abi-1.revision', 'plugin.yml', 'paper-plugin.yml', 'slimjar.dat', 'slimjar-resolutions.dat', 'iris-language-source.properties', 'agent.jar', 'art/arcane/iris/Iris.class', 'art/arcane/iris/IrisBootstrap.class', 'art/arcane/iris/platform/bukkit/nms/INMS.class', 'art/arcane/iris/platform/bukkit/nms/v26_2_R1/NMSBinding.class', 'art/arcane/iris/platform/bukkit/nms/v1X/NMSBinding1X.class', 'art/arcane/iris/platform/generation/PlatformChunkGenerator.class', 'art/arcane/iris/world/lifecycle/WorldLifecycleStaging.class', 'art/arcane/iris/generation/simd/VectorSimdKernels.class', 'art/arcane/iris/platform/agent/Agent.class', 'art/arcane/iris/platform/bootstrap/getHardware.class', 'art/arcane/volmlib/util/io/JarScanner.class', 'art/arcane/volmlib/util/director/runtime/DirectorRuntimeEngine.class', 'art/arcane/volmlib/util/mantle/io/Lz4IOWorkerCodecSupport.class', 'art/arcane/volmlib/util/matter/Matter.class' ] + requiredCommandHandlerEntries + requiredMantleComponentEntries tasks.register('verifyBukkitArtifact') { group = 'verification' dependsOn('jar') File artifact = bukkitArtifact inputs.file(artifact) inputs.files(commandHandlerSources) inputs.files(mantleComponentSources) doLast { BukkitArtifactVerifier.verify( artifact, requiredBukkitArtifactEntries, 16, runtimeDownloadedPrefixes) logger.lifecycle("Verified ${artifact.name} packaging and class reference graph") } } tasks.named('buildBukkit').configure { dependsOn('verifyBukkitArtifact') } tasks.named('iris').configure { dependsOn('verifyBukkitArtifact') } tasks.named('buildFabric').configure { dependsOn('verifyFabricArtifact') } tasks.named('buildForge').configure { dependsOn('verifyForgeArtifact') } tasks.named('buildNeoforge').configure { dependsOn('verifyNeoforgeArtifact') } tasks.register('buildAllToOut') { group = 'iris' dependsOn('buildBukkit', 'buildFabric', 'buildForge', 'buildNeoforge', ':spi:jar') doLast { File[] jars = pluginOutDirectory.listFiles((java.io.FileFilter) { File f -> f.name.startsWith('Iris v') && f.name.endsWith('.jar') }) println('') println('=== Iris buildAllToOut -> ' + pluginOutDirectory + ' ===') if (jars != null) { jars.sort { it.name }.each { File f -> println(String.format(' %-60s %8d KB', f.name, (long) (f.length() / 1024))) } } } } tasks.register('buildAll') { group = 'iris' outputs.upToDateWhen { false } dependsOn('iris', 'verifyModdedArtifacts', 'buildAllToOut') Map> consumerDrops = [ (bukkitConsumerPath) : ['Iris.jar', consumerBukkitArtifact], (fabricConsumerPath) : ['Iris-Fabric.jar', fabricArtifact], (forgeConsumerPath) : ['Iris-Forge.jar', forgeArtifact], (neoForgeConsumerPath): ['Iris-Neoforge.jar', neoForgeArtifact] ] doLast { consumerDrops.each { String consumerPath, List drop -> String keep = (String) drop.get(0) File source = (File) drop.get(1) irisFileOperations.fileSystemOperations.delete { DeleteSpec spec -> consumerDrops.values().each { List other -> String name = (String) other.get(0) if (name != keep) { spec.delete(new File(consumerPath, name)) } } } irisFileOperations.fileSystemOperations.copy { CopySpec spec -> spec.from(source) spec.into(consumerPath) spec.rename { String ignored -> keep } } } println('') println("=== Iris buildAll -> ${consumerLocation} ===") println(" ${bukkitConsumerPath}/Iris.jar") println(" ${fabricConsumerPath}/Iris-Fabric.jar") println(" ${forgeConsumerPath}/Iris-Forge.jar") println(" ${neoForgeConsumerPath}/Iris-Neoforge.jar") } } tasks.register('irisDev', Copy) { group = 'iris' from(project(':core').layout.buildDirectory.files('libs/core-javadoc.jar', 'libs/core-sources.jar')) rename { String fileName -> fileName.replace('core', "Iris-${project.version}") } into(layout.buildDirectory) dependsOn(':core:sourcesJar') dependsOn(':core:javadocJar') } configurations.configureEach { resolutionStrategy.cacheChangingModulesFor(0, 'seconds') resolutionStrategy.cacheDynamicVersionsFor(0, 'seconds') } allprojects { apply plugin: 'java' java { toolchain { languageVersion = JavaLanguageVersion.of(25) } } repositories { if (useMavenLocal) { mavenLocal() } mavenCentral() maven { url = uri('https://repo.papermc.io/repository/maven-public/') } maven { url = uri('https://repo.codemc.org/repository/maven-public/') } maven { url = uri('https://jitpack.io') content { includeGroupByRegex('com\\.github\\..*') } } // score maven { url = uri('https://repo.auxilor.io/repository/maven-releases/') content { includeGroupByRegex('com\\.willfp(\\..*)?') } } // eco maven { url = uri('https://repo.nexomc.com/releases/') content { includeGroup('com.nexomc') includeGroup('team.unnamed') } } // nexo maven { url = uri('https://repo.oraxen.com/releases/') content { includeGroup('io.th0rgal') } } maven { url = uri('https://repo.extendedclip.com/releases/') content { includeGroup('me.clip') } } // placeholderapi maven { url = uri('https://mvn.lumine.io/repository/maven-public/') content { includeModule('io.lumine', 'Mythic-Dist') includeModule('io.lumine', 'MythicCrucible-Dist') } } // mythic maven { url = uri('https://nexus.phoenixdevt.fr/repository/maven-public/') content { includeGroup('net.Indyuce') includeModule('io.lumine', 'MythicLib-dist') } } //MMOItems maven { url = uri('https://repo.onarandombox.com/content/groups/public/') content { includeGroupByRegex('org\\.mvplugins\\.multiverse(\\..*)?') } } //Multiverse Core maven { url = uri('https://repo.momirealms.net/releases/') content { includeGroup('net.momirealms') } } // CraftEngine } /** * We need parameter meta for the decree command system */ tasks.named('compileJava', JavaCompile).configure { options.compilerArgs.add('-parameters') options.encoding = 'UTF-8' options.debugOptions.debugLevel = 'none' options.release.set(25) } tasks.named('javadoc').configure { options.encoding = 'UTF-8' options.quiet() //options.addStringOption("Xdoclint:none") // TODO: Re-enable this } tasks.register('sourcesJar', Jar) { archiveClassifier.set('sources') from(sourceSets.main.allSource) } tasks.register('javadocJar', Jar) { archiveClassifier.set('javadoc') from(tasks.named('javadoc').map { it.destinationDir }) } } if (!JavaVersion.current().isCompatibleWith(JavaVersion.VERSION_25)) { System.err.println() System.err.println('=========================================================================================================') System.err.println('You must run gradle on Java 25 or newer. You are using ' + JavaVersion.current()) System.err.println() System.err.println('=== For IDEs ===') System.err.println('1. Configure the project for Java 25 toolchain') System.err.println('2. Configure the bundled gradle to use Java 25+ in settings') System.err.println() System.err.println('=== For Command Line (gradlew) ===') System.err.println('1. Install JDK 25 from https://adoptium.net/temurin/releases/?version=25') System.err.println('2. Set JAVA_HOME environment variable to the new jdk installation folder such as C:\\Program Files\\Java\\jdk-25') System.err.println('3. Open a new command prompt window to get the new environment variables if need be.') System.err.println('=========================================================================================================') System.err.println() System.exit(69) } pluginPackaging { artifacts { create('distribution') { taskName = 'jar' policyName = 'Iris Bukkit' stripDirectories = true stripLocalVariables = true releaseCompression = providers.gradleProperty('compactRelease') .map { String value -> value.toBoolean() } .getOrElse(!PackagingMode.resolve(providers).development()) prunePrefixes = ['art/arcane/volmlib/'] keepPrefixes = ['art/arcane/volmlib/util/matter/slices/'] shrinkRules = [file('packaging/shrink.pro')] shrinkLibraries.from(provider { project(':core').configurations.compileClasspath }) shrinkLibraries.from(provider { project(':adapters:bukkit:plugin').configurations.compileClasspath }) shrinkLibraries.from(provider { project(':adapters:bukkit:nms:v26_2_R1').configurations.compileClasspath }) shrinkRelocations = runtimeDownloadRelocations } } }