import art.arcane.volmit.packaging.PackedJar import art.arcane.volmit.packaging.PackagingMode import art.arcane.volmit.packaging.NativeDevelopmentTargets import io.papermc.paperweight.userdev.PaperweightUser import io.papermc.paperweight.userdev.PaperweightUserDependenciesExtension import io.papermc.paperweight.userdev.PaperweightUserExtension import org.gradle.api.artifacts.component.ComponentIdentifier import org.gradle.api.plugins.JavaPluginExtension import org.gradle.api.provider.Provider import org.gradle.jvm.toolchain.JavaLauncher import org.gradle.jvm.toolchain.JavaToolchainService 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.initialization.IncludedBuild 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 { mavenCentral() maven { url = uri('https://jitpack.io') } } dependencies { classpath('com.github.VolmitSoftware.VolmLib:packaging:master-SNAPSHOT') } } plugins { id 'java' id 'java-library' } apply plugin: 'art.arcane.volmit-packaging' apply from: file('gradle/runtime-relocations.gradle') apply from: file('gradle/volmlib-stamp.gradle') interface IrisFileOperations { @Inject FileSystemOperations getFileSystemOperations() } IrisFileOperations irisFileOperations = objects.newInstance(IrisFileOperations) group = 'art.arcane' version = providers.gradleProperty('irisVersion').getOrElse('4.3.3') project(':core') { group = 'art.arcane' version = rootProject.version } apply from: file('gradle/mod-target.gradle') String minecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse('26.3') // The Bukkit jar retains 26.1.2 and 26.2 alongside 26.3. String bukkitMinecraftRange = providers.gradleProperty('bukkitMinecraftRange').getOrElse('26.1.2-26.3') String fabricLoaderVersion = providers.gradleProperty('fabricLoaderVersion').getOrElse(irisModTarget.fabricLoader.toString()) String forgeVersion = providers.gradleProperty('forgeVersion').getOrElse(irisModTarget.forge.toString()) String neoForgeVersion = providers.gradleProperty('neoForgeVersion').getOrElse(irisModTarget.neoForge.toString()) String volmLibShared = 'com.github.VolmitSoftware.VolmLib:volmlib-shared:master-SNAPSHOT' File localVolmLibCheckout = (File) gradle.extensions.extraProperties.get('volmLibDirectory') IncludedBuild localVolmLibBuild = localVolmLibCheckout == null ? null : gradle.includedBuilds.find { IncludedBuild includedBuild -> includedBuild.projectDir.canonicalFile == localVolmLibCheckout.canonicalFile } Map volmLibStamp = (Map) gradle.extensions.extraProperties.get('volmLibStamp') 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 = file(providers.gradleProperty('pluginOutDirectory').getOrElse('../PluginOuts')) 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/${minecraftVersion}/libs/${fabricArtifactName}").asFile File forgeArtifact = layout.projectDirectory.file("adapters/forge/build/${minecraftVersion}/libs/${forgeArtifactName}").asFile File neoForgeArtifact = layout.projectDirectory.file("adapters/neoforge/build/${minecraftVersion}/libs/${neoForgeArtifactName}").asFile File consumerDirectory = layout.buildDirectory.dir('consumer').get().asFile FileTree staleConsumerBukkitJars = fileTree(consumerDirectory) { include('Iris v* [CraftBukkit] *.jar') } FileTree staleBukkitOutJars = fileTree(pluginOutDirectory) { include('Iris v* [CraftBukkit] *.jar') } String selectedModdedSuffix = minecraftVersion.replace('.', '_') 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" List nmsBindings = ['v26_2_R1', 'v26_3_R1'] ext.nativeDevelopmentAdapters = nmsBindings nmsBindings.each { String key -> project(":adapters:bukkit:nms:${key}") { apply plugin: JavaPlugin apply plugin: PaperweightUser if (localVolmLibBuild != null) { tasks.named('paperweightUserdevSetup').configure { dependsOn(localVolmLibBuild.task(":native:${key}:paperweightUserdevSetup")) if (key == 'v26_2_R1') { dependsOn(localVolmLibBuild.task(':native-common:paperweightUserdevSetup')) } } } NativeDevelopmentTargets.Target nativeTarget = NativeDevelopmentTargets.require(key) dependencies.extensions.getByType(PaperweightUserDependenciesExtension) .paperDevBundle(nativeTarget.paperBundle()) JavaPluginExtension javaExtension = extensions.getByType(JavaPluginExtension) javaExtension.toolchain.languageVersion.set(JavaLanguageVersion.of(nativeTarget.javaVersion())) JavaToolchainService nativeToolchains = extensions.getByType(JavaToolchainService) extensions.configure(PaperweightUserExtension) { PaperweightUserExtension extension -> extension.javaLauncher.set(nativeToolchains.launcherFor(javaExtension.toolchain)) } dependencies { implementation("com.github.VolmitSoftware.VolmLib:native-${key}:master-SNAPSHOT") { transitive = false } compileOnly(project(':core')) compileOnly(volmLibShared) { 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') if (key == 'v26_3_R1') { testImplementation(project(':probe')) } testImplementation(project(':core')) testImplementation(testFixtures(project(':core'))) testImplementation('com.github.VolmitSoftware.VolmLib:native-common:master-SNAPSHOT') { transitive = false } testCompileOnly(volmLibShared) { transitive = false } testRuntimeOnly(project(':core')) testRuntimeOnly(rootProject.libs.caffeine) } } } def included = configurations.create('included') def nativeRuntime = configurations.create('nativeRuntime') def nativeRuntimeOutput = layout.buildDirectory.dir('generated/native-runtime') def nativeRuntimeRules = layout.buildDirectory.file('generated/native-runtime-keep.pro') List nativeRuntimeModules = ['native-common'] + nmsBindings.collect { String adapter -> 'native-' + adapter } dependencies { nativeRuntimeModules.each { String module -> add('nativeRuntime', "com.github.VolmitSoftware.VolmLib:${module}:master-SNAPSHOT") { transitive = false } } } // The natives PackedJar embeds are the single source for the runtime manifest, the reachability roots and // the ProGuard keep rules, so the shrinker can never prune against a different provider build. Closure> embeddedNatives = { -> Map resolved = new LinkedHashMap<>() nativeRuntime.incoming.artifacts.artifacts.each { artifact -> resolved.put(artifact.id.componentIdentifier, artifact.file) } return PackedJar.resolveNativeArtifacts(resolved) } def generateNativeRuntime = tasks.register('generateNativeRuntime') { inputs.files(nativeRuntime) inputs.property('modules', nativeRuntimeModules) outputs.dir(nativeRuntimeOutput) outputs.file(nativeRuntimeRules) doLast { NativeRuntimeArtifacts.generate(embeddedNatives(), nativeRuntimeModules, nativeRuntimeOutput.get().asFile, nativeRuntimeRules.get().asFile) } } def jarJar = configurations.create('jarJar') dependencies { 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) dependsOn(generateNativeRuntime) from(nativeRuntimeOutput) from(tasks.named('generateVolmLibStamp')) { into('META-INF/volmit') } duplicatesStrategy = DuplicatesStrategy.EXCLUDE includeEmptyDirs = false from(jarJar) from(provider { included.resolve().collect { zipTree(it) } }) { exclude('languages/**') exclude('com/fren_gor/**') } doFirst { irisFileOperations.fileSystemOperations.delete { DeleteSpec spec -> spec.delete(legacyBukkitJar) } } archiveFileName.set(bukkitArtifactName) } tasks.register('iris', Copy) { group = 'iris' dependsOn('packedJar') from({ tasks.named('packedJar').get().outputJar }) rename { String ignored -> 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', "verifyFabric${selectedModdedSuffix}", "verifyForge${selectedModdedSuffix}", "verifyNeoForge${selectedModdedSuffix}") 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('packedJar') from({ tasks.named('packedJar').get().outputJar }) rename { String ignored -> bukkitArtifactName } into(pluginOutDirectory) File legacyOutJar = new File(pluginOutDirectory, "Iris-${project.version}+mc${minecraftVersion}.jar") doFirst { irisFileOperations.fileSystemOperations.delete { DeleteSpec spec -> spec.delete(staleBukkitOutJars, 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. List moddedSharedInputs = [] [ 'adapters/modded-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/volmlib-stamp.gradle', 'gradle/generation-build-revision.gradle', 'gradle/modded-generation-build-revision.gradle', 'gradle/native-minecraft-sources.gradle', 'gradle/mod-target.gradle', 'gradle/mod-targets.json', 'gradle/modded-version-sources.gradle' ].each { String path -> moddedSharedInputs.add(layout.projectDirectory.file(path).asFile) } if (localVolmLibCheckout != null) { moddedSharedInputs.add(fileTree(localVolmLibCheckout) { include('shared/src/main/**', 'native-api/src/main/**', 'native-terrain-api/src/main/**', 'native/**/src/main/**', 'native/minecraft-common/src/**', 'packaging/src/main/**', '**/build.gradle', 'settings.gradle', 'gradle.properties', 'gradle/libs.versions.toml') exclude('**/build/**', '**/.gradle/**') }) } // The published master-SNAPSHOT is resolved inside the nested build, so no outer input changes when it // moves. Without a local checkout the Exec tasks always run and the nested build decides what is stale. Closure declareVolmLibInputs = { Exec task -> task.inputs.property('volmLibStamp', volmLibStamp) String nativeBillow = providers.gradleProperty('includeNativeBillow').getOrElse('false') task.inputs.property('includeNativeBillow', nativeBillow) task.environment('ORG_GRADLE_PROJECT_includeNativeBillow', nativeBillow) if (localVolmLibCheckout == null) { task.outputs.upToDateWhen { false } } return null } List commonModdedArtifactEntries = [ 'agent.jar', 'pack.mcmeta', 'com/fulcrumgenomics/jlibdeflate/LibdeflateDecompressor.class', 'com/fulcrumgenomics/jlibdeflate/LibdeflateException.class', 'native/linux-aarch64/libjlibdeflate.so', 'native/linux-x86_64/libjlibdeflate.so', 'native/osx-aarch64/libjlibdeflate.dylib', 'native/osx-x86_64/libjlibdeflate.dylib', 'native/windows-x86_64/jlibdeflate.dll', 'META-INF/iris/generation-kernels/catalog.tsv', 'META-INF/iris/generation-kernels/abi-1.revision', 'META-INF/volmit/volmlib.properties', 'META-INF/services/art.arcane.iris.localization.IrisMessageContributor', 'art/arcane/iris/generation/runtime/IrisEngine.class', 'art/arcane/iris/pack/datapack/DataVersion.class', 'art/arcane/iris/pack/datapack/IDataFixer.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. 'volmlib.entity.mixins.json', 'volmlib.client.mixins.json', 'art/arcane/volmlib/nativelib/minecraft26_2/client/mixin/NativeWorldOpenFlowsMixin.class', 'art/arcane/volmlib/nativelib/minecraft26_2/client/mixin/NativeWorldTypeEntryMixin.class', '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() Map moddedLoaderLabels = [fabric: 'Fabric', forge: 'Forge', neoforge: 'NeoForge'] Map moddedLoaderPins = [fabric: 'fabricLoader', forge: 'forge', neoforge: 'neoForge'] Map moddedLoaderProperties = [fabric: 'fabricLoaderVersion', forge: 'forgeVersion', neoforge: 'neoForgeVersion'] Map selectedLoaderVersions = [fabric: fabricLoaderVersion, forge: forgeVersion, neoforge: neoForgeVersion] Map> moddedLoaderEntries = [ fabric : ['fabric.mod.json', 'volmlib.fabric.mixins.json', 'volmlib.accesswidener', 'art/arcane/iris/fabric/IrisFabricBootstrap.class'], forge : ['META-INF/mods.toml', 'META-INF/accesstransformer.cfg', 'art/arcane/iris/forge/IrisForgeBootstrap.class'], neoforge: ['META-INF/neoforge.mods.toml', 'META-INF/accesstransformer.cfg', 'art/arcane/iris/neoforge/IrisNeoForgeBootstrap.class'] ] String moddedWrapperScript = System.getProperty('os.name').toLowerCase().contains('windows') ? 'gradlew.bat' : 'gradlew' TaskProvider verifyModdedMatrix = tasks.register('verifyModdedMatrix') { group = 'verification' description = 'Builds, tests and verifies the Fabric, Forge and NeoForge jars of every supported Minecraft mod target.' } List> moddedExports = [] Map> selectedModdedBuilds = [:] Map> selectedModdedExports = [:] TaskProvider previousMatrixBuild = null irisModTargets.each { String targetMinecraft, Map target -> String suffix = targetMinecraft.replace('.', '_') boolean selected = targetMinecraft == minecraftVersion moddedLoaderLabels.each { String loader, String label -> String loaderPin = selected ? selectedLoaderVersions[loader] : target.get(moddedLoaderPins[loader]).toString() String displayedLoader = loaderPin.startsWith(targetMinecraft + '-') ? loaderPin.substring(targetMinecraft.length() + 1) : loaderPin String artifactName = irisArtifactName(label, "${targetMinecraft}+${displayedLoader}") File artifact = file("adapters/${loader}/build/${targetMinecraft}/libs/${artifactName}") boolean unitTests = ((List) (target.testLoaders ?: moddedLoaderLabels.keySet().toList())).contains(loader) TaskProvider buildTarget = tasks.register("verify${label}${suffix}", Exec) { group = 'verification' description = "Builds, tests and verifies the ${label} ${targetMinecraft} jar in its own Gradle build." workingDir = file("adapters/${loader}") List command = [file(moddedWrapperScript).absolutePath, 'assemble', 'compileTestJava', 'verifyLoggingPolicy', 'verifyNativeClassBoundary', '--console=plain', "-PirisVersion=${project.version}", "-PminecraftVersion=${targetMinecraft}", "-P${moddedLoaderProperties[loader]}=${loaderPin}", "-PuseLocalVolmLib=${localVolmLibCheckout != null}"] if (unitTests) { command.add('test') } commandLine(command) if (localVolmLibCheckout != null) { environment('ORG_GRADLE_PROJECT_localVolmLibDirectory', localVolmLibCheckout.absolutePath) } inputs.files(moddedSharedInputs).withPropertyName('moddedSharedSources') .withPathSensitivity(PathSensitivity.RELATIVE) inputs.files(file("adapters/${loader}/src"), file("adapters/${loader}/build.gradle"), file("adapters/${loader}/settings.gradle")).withPropertyName('adapterSources') inputs.properties(target) inputs.property('loaderVersion', loaderPin) inputs.property('irisVersion', project.version.toString()) declareVolmLibInputs(it) outputs.file(artifact) if (unitTests) { outputs.dir(file("adapters/${loader}/build/${targetMinecraft}/test-results/test")) } doLast { ModdedArtifactVerifier.verify(artifact, commonModdedArtifactEntries + moddedLoaderEntries[loader], bukkitCoupledCoreClasses) ModdedArtifactVerifier.verifyJavaRuntime(artifact, (int) target.java) logger.lifecycle("Verified ${artifact.name}") } } if (previousMatrixBuild != null) { TaskProvider preceding = previousMatrixBuild buildTarget.configure { mustRunAfter(preceding) } } previousMatrixBuild = buildTarget verifyModdedMatrix.configure { dependsOn(buildTarget) } File legacyOutJar = new File(pluginOutDirectory, "Iris-${project.version}+mc${targetMinecraft}-${loader}.jar") FileTree staleTargetJars = fileTree(pluginOutDirectory) { include("Iris v* [${label}] ${targetMinecraft}+*.jar") } TaskProvider export = tasks.register("build${label}${suffix}", Copy) { group = 'iris' description = "Exports the verified ${label} ${targetMinecraft} jar to the plugin output directory." dependsOn(buildTarget) from(artifact) into(pluginOutDirectory) doFirst { irisFileOperations.fileSystemOperations.delete { DeleteSpec spec -> spec.delete(staleTargetJars, legacyOutJar) } } } moddedExports.add(export) if (selected) { selectedModdedBuilds[loader] = buildTarget selectedModdedExports[loader] = export } } } if (selectedModdedBuilds.isEmpty()) { throw new GradleException("Minecraft ${minecraftVersion} is not a mod target in gradle/mod-targets.json") } tasks.register('buildFabric') { group = 'iris' description = "Exports the verified Fabric ${minecraftVersion} jar." dependsOn(selectedModdedExports.fabric) } tasks.register('buildForge') { group = 'iris' description = "Exports the verified Forge ${minecraftVersion} jar." dependsOn(selectedModdedExports.forge) } tasks.register('buildNeoforge') { group = 'iris' description = "Exports the verified NeoForge ${minecraftVersion} jar." dependsOn(selectedModdedExports.neoforge) } gradle.projectsEvaluated { List sharedOutputTasks = allprojects.collectMany { Project candidate -> candidate.tasks.findAll { Task task -> task instanceof JavaCompile || task instanceof Jar || task instanceof Test } } tasks.withType(Exec).matching { Task task -> task.name.startsWith('verify') }.configureEach { mustRunAfter(sharedOutputTasks) mustRunAfter(tasks.named('buildBukkit')) } } tasks.named('check').configure { dependsOn(selectedModdedBuilds.fabric) } tasks.register('checkAll') { group = 'verification' description = 'Runs every root, core, Bukkit and SPI check plus the full Fabric, Forge and NeoForge matrix for every Minecraft mod target.' dependsOn(allprojects.collect { Project candidate -> candidate.tasks.matching { Task task -> task.name == 'check' } }) dependsOn(verifyModdedMatrix) } // 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', 'META-INF/iris/native-runtime.properties', 'META-INF/volmit/volmlib.properties', '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/BukkitBinding.class', 'art/arcane/volmlib/nativelib/NativeAdapters.class', 'art/arcane/volmlib/nativelib/terrain/NativeWorldGeneration.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(nativeRuntime) inputs.files(commandHandlerSources) inputs.files(mantleComponentSources) doLast { BukkitArtifactVerifier.verify( artifact, requiredBukkitArtifactEntries, 16, runtimeDownloadedPrefixes) NativeRuntimeArtifacts.verify(artifact, nativeRuntimeModules, embeddedNatives()) logger.lifecycle("Verified ${artifact.name} packaging and class reference graph") } } tasks.named('buildBukkit').configure { dependsOn('verifyBukkitArtifact') } tasks.named('iris').configure { dependsOn('verifyBukkitArtifact') } tasks.register('buildAllToOut') { group = 'iris' description = 'Builds and verifies the Bukkit jar and every mod target jar into the plugin output directory.' dependsOn('buildBukkit', ':spi:jar') dependsOn(moddedExports) 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', '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('buildPsychoLT') { group = 'development' description = 'Builds all verified Iris platform jars and stages them in the local test-server dropins and PluginOuts.' dependsOn('buildAll') } 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') } int defaultTestForks = Math.min(8, Math.max(1, Runtime.runtime.availableProcessors().intdiv(2))) int testForks = Math.max(1, Integer.parseInt(providers.gradleProperty('testForks') .getOrElse(String.valueOf(defaultTestForks)))) int irisRuntimeJava = providers.gradleProperty('irisRuntimeJava').getOrElse(irisModTarget.java.toString()).toInteger() if (!(irisRuntimeJava in [21, 25])) { throw new GradleException('irisRuntimeJava must be 21 or 25') } Set sharedRuntimeProjects = [':core', ':core:agent', ':spi'] as Set allprojects { apply plugin: 'java' boolean java21Runtime = irisRuntimeJava == 21 && sharedRuntimeProjects.contains(project.path) if (java21Runtime) { layout.buildDirectory.set(layout.projectDirectory.dir('build/java21')) } tasks.withType(Test).configureEach { int moduleForks = project.path.startsWith(':adapters:bukkit:') ? Math.min(2, testForks) : project.path == ':probe' ? Math.min(4, testForks) : testForks maxParallelForks = Math.min(gradle.startParameter.maxWorkerCount, moduleForks) jvmArgs('-XX:ParallelGCThreads=2', '-XX:ConcGCThreads=1', '-XX:CICompilerCount=2') } java { toolchain { languageVersion = JavaLanguageVersion.of(25) } if (java21Runtime) { sourceCompatibility = JavaVersion.VERSION_21 targetCompatibility = JavaVersion.VERSION_21 } } if (java21Runtime) { Provider runtimeLauncher = javaToolchains.launcherFor { languageVersion = JavaLanguageVersion.of(21) } tasks.withType(JavaCompile).configureEach { sourceCompatibility = '21' targetCompatibility = '21' if (project.path == ':core') { options.compilerArgumentProviders.add({ ['--system', runtimeLauncher.get().metadata.installationPath.asFile.absolutePath] } as org.gradle.process.CommandLineArgumentProvider) } else { options.release.set(21) } } if (project.path != ':core') { tasks.withType(Test).configureEach { javaLauncher.set(runtimeLauncher) } } } repositories { 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' if (!java21Runtime) { 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' packed = true packedJarAccessors = ['art/arcane/iris/platform/bukkit/plugin/VolmitPlugin#getJarFile'] 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/', 'art/arcane/volmlib/nativelib/'] shrinkRules = [file('packaging/shrink.pro'), nativeRuntimeRules.get().asFile] shrinkDontwarn = [ 'java.lang.invoke.MethodHandle', 'java.lang.invoke.VarHandle', 'net.minecraft.server.MinecraftServer', 'net.minecraft.server.level.ServerLevel', 'net.minecraft.world.attribute.EnvironmentAttributes', 'net.minecraft.world.level.NaturalSpawner', 'net.minecraft.world.level.StructureManager', 'net.minecraft.world.level.biome.BiomeManager', 'net.minecraft.world.level.biome.BiomeResolver', 'net.minecraft.world.level.biome.BiomeSource', 'net.minecraft.world.level.biome.MobSpawnSettings', 'net.minecraft.world.level.biome.MobSpawnSettings$SpawnerData', 'net.minecraft.world.level.chunk.ChunkAccess', 'net.minecraft.world.level.chunk.ChunkGenerator', 'net.minecraft.world.level.chunk.status.ChunkStatus', 'net.minecraft.world.level.levelgen.RandomState', 'net.minecraft.world.level.levelgen.feature.Feature', 'net.minecraft.world.level.levelgen.structure.Structure', 'net.minecraft.world.level.levelgen.structure.placement.ConcentricRingsStructurePlacement', 'net.minecraft.world.level.levelgen.structure.placement.RandomSpreadStructurePlacement', 'org.bukkit.craftbukkit.inventory.CraftItemStack', ] shrinkLibraries.from(provider { project(':core').configurations.compileClasspath }) shrinkLibraries.from(provider { project(':adapters:bukkit:plugin').configurations.compileClasspath }) nmsBindings.each { binding -> shrinkLibraries.from(provider { project(":adapters:bukkit:nms:${binding}").configurations.compileClasspath.incoming.artifactView { Set nativeComponents = nativeRuntime.incoming.artifacts.artifacts.collect { artifact -> artifact.id.componentIdentifier }.toSet() componentFilter { component -> !nativeComponents.contains(component) } }.files }) } shrinkRelocations = runtimeDownloadRelocations } } } project(':adapters:bukkit:nms:v26_3_R1') { Configuration headlessNativeSources = configurations.create('headlessNativeSources') { canBeConsumed = false canBeResolved = true } dependencies.add(headlessNativeSources.name, 'com.github.VolmitSoftware.VolmLib:native-minecraft26_2-sources:master-SNAPSHOT') List headlessNativeTypes = ['ModdedBlockState', 'ModdedBiome', 'NativeBlockProperties', 'NativeBlockResolver', 'NativeBlockFaces', 'NativeStateRotator', 'NativeStateMerger', 'NativeRegistryAccess', 'ModdedItem', 'ModdedEntityType', 'NativeTileReader', 'NativeTileData'] TaskProvider unpackHeadlessNativeSources = tasks.register('unpackHeadlessNativeSources', Sync) { dependsOn(headlessNativeSources) inputs.property('nativeTypes', headlessNativeTypes) from(provider { headlessNativeSources.collect { File artifact -> zipTree(artifact) } }) { headlessNativeTypes.each { String type -> include("art/arcane/volmlib/nativelib/minecraft26_2/modded/${type}.java") } } from(rootProject.file('adapters/modded-common/src/main/java')) { include('art/arcane/iris/modded/ModdedDecoratorHooks.java', 'art/arcane/iris/modded/ModdedStateRotator.java', 'art/arcane/iris/modded/ModdedTileData.java') } into(layout.buildDirectory.dir('generated/sources/headless-native')) } sourceSets { offline { java.srcDir(unpackHeadlessNativeSources) compileClasspath += sourceSets.main.compileClasspath runtimeClasspath += sourceSets.main.compileClasspath + sourceSets.main.output } } dependencies { offlineImplementation(project(':probe')) offlineImplementation(project(':core')) offlineImplementation('com.github.VolmitSoftware.VolmLib:native-common:master-SNAPSHOT') { transitive = false } offlineImplementation(volmLibShared) { transitive = false } } sourceSets.test.compileClasspath += sourceSets.offline.output sourceSets.test.runtimeClasspath += sourceSets.offline.output TaskProvider offlineJar = tasks.register('offlineJar', Jar) { dependsOn(offlineClasses) archiveClassifier = 'offline' from(sourceSets.offline.runtimeClasspath.filter { File file -> file.isDirectory() }) } FileCollection offlineLibraries = sourceSets.offline.runtimeClasspath.filter { File file -> file.isFile() } TaskProvider offlineStartScripts = tasks.register('offlineStartScripts', CreateStartScripts) { dependsOn(offlineJar) mainClass = 'art.arcane.iris.probe.OfflineRegionGenerator' applicationName = 'iris-offline' outputDir = layout.buildDirectory.dir('offline-scripts').get().asFile classpath = files(offlineJar) + offlineLibraries defaultJvmOpts = ['-Xmx8G', '--add-modules=jdk.incubator.vector', '-Djava.awt.headless=true'] } tasks.register('installOffline', Sync) { group = 'distribution' description = 'Installs the standalone native region generator and its runtime libraries.' dependsOn(offlineJar, offlineStartScripts) into(layout.buildDirectory.dir('install/iris-offline')) into('lib') { from(offlineJar, offlineLibraries) } into('bin') { from(offlineStartScripts) filePermissions { unix('rwxr-xr-x') } } } tasks.register('regionMantleInspection', JavaExec) { group = 'verification' dependsOn(testClasses) mainClass = 'art.arcane.iris.probe.HeadlessMantleInspection' classpath = sourceSets.test.runtimeClasspath maxHeapSize = '4G' jvmArgs('--add-modules', 'jdk.incubator.vector', '-Djava.awt.headless=true') doFirst { args(['probePack', 'probeDimension', 'probeSeed', 'probeWorld', 'probeRegionX', 'probeRegionZ'] .collect { String name -> providers.gradleProperty(name).get() }) } } tasks.register('generateOfflineRegions', JavaExec) { group = 'verification' description = 'Writes fresh native TERRAIN checkpoints with exact prerequisites for subsequent native FULL completion.' dependsOn(offlineClasses) mainClass = 'art.arcane.iris.probe.OfflineRegionGenerator' classpath = sourceSets.offline.runtimeClasspath maxHeapSize = '8G' jvmArgs('--add-modules', 'jdk.incubator.vector', '-Djava.awt.headless=true') doFirst { List names = ['probePack', 'probeDimension', 'probeSeed', 'probeChunkX', 'probeChunkZ', 'probeWidth', 'probeRegionsX', 'probeRegionsZ', 'probeParallelism', 'probeSpigot', 'probeWorldName', 'probeLevelKey', 'probeNoiseSettings', 'probeOutput'] args(names.collect { String name -> providers.gradleProperty(name).get() }) } } } project(':adapters:bukkit:nms:v26_3_R1') { Provider fixtureResources = layout.buildDirectory.dir('generated/region-native-fixture') TaskProvider fixtureDescriptor = tasks.register('regionNativeFixtureDescriptor') { outputs.dir(fixtureResources) doLast { File descriptor = fixtureResources.get().file('plugin.yml').asFile descriptor.parentFile.mkdirs() descriptor.text = '''name: RegionNativeFixture version: 1.0 main: art.arcane.iris.probe.RegionNativeFixture api-version: '26.3' depend: [Iris] commands: mcaterrain: description: Export native terrain checkpoints on an isolated test server mcaload: description: Verify native terrain checkpoints finish generation on reload ''' } } TaskProvider compileRegionNativeFixture = tasks.register('compileRegionNativeFixture', JavaCompile) { source(fileTree('src/test/java') { include('art/arcane/iris/probe/RegionNativeFixture.java', 'art/arcane/iris/probe/DetachedNativeTerrain.java') }) source(fileTree('src/offline/java') { include('art/arcane/iris/probe/RegionGenerationWindow.java', 'art/arcane/iris/probe/NativeRegionTerrainWriter.java', 'art/arcane/iris/probe/NativeRegionPlan.java') }) classpath = sourceSets.test.compileClasspath destinationDirectory = layout.buildDirectory.dir('classes/region-native-fixture') javaCompiler = javaToolchains.compilerFor { languageVersion = JavaLanguageVersion.of(25) } options.release = 25 options.compilerArgs.add('-parameters') } tasks.register('regionNativeFixtureJar', Jar) { dependsOn(compileRegionNativeFixture, fixtureDescriptor) archiveFileName = 'mca-native-fixture.jar' from(compileRegionNativeFixture.flatMap { JavaCompile task -> task.destinationDirectory }) from(fixtureResources) } } tasks.withType(PackedJar).configureEach { nativeManifestPath.set(NativeRuntimeArtifacts.MANIFEST) nativeResourceDirectory.set(NativeRuntimeArtifacts.RESOURCES) dependsOn(tasks.named('verifyBukkitArtifact'), nativeRuntime) nativeArtifacts.set(providers.provider { embeddedNatives() }) doLast { PackedJar task -> NativeRuntimeArtifacts.verifyPacked(task.outputJar.get().asFile, nativeRuntimeModules, embeddedNatives()) } }