Files
Iris/adapters/neoforge/build.gradle
T
Brian Neumann-Fopiano db0f98d103 Basin hydrology
- The route-and-reject hydrology system and its probes are removed
- Hydrology is configured under dimension hydrology and per biome, region and dimension hydrologyPolicy
- riverPolicy and the old hydrology config types are gone
- Grid kernels cover priority flood, D8 routing, flow accumulation, basin extraction, breaching and stream burning
- A basin spills at its pour point and an interior bump can never be the spill
- Region plans cover 4096-block regions on a 64-block grid for major rivers and lakes
- Window plans cover 512-block windows on an 8-block grid for reaches and ponds
- Plans are cached in memory, persisted per world under iris/hydrology and keyed by pack, seed, world height and settings
- Only the main runtime of an engine persists plans and stores swap on hotload and default runtime change
- Demand rasters outrank prefetch and stopping pregen withdraws its queued prefetch
- Ponds come from natural basins, seeded cups and chained cups with strictly falling levels
- A pond level never exceeds its rim plus maxFill or the end grade of any inflow
- Pond outlet loops are opened at their lowest pond
- Reaches split at junctions, pond outlets and 256-block grid lines
- Reaches meander, fit the valley, grade to a running minimum and smooth with Chaikin passes
- Drops of two blocks or more get a lip and one-block corner steps become dry rim ledges
- Cascades and waterfalls write falling fluid from the mantle at level eight
- Inflows into region lakes end at the lake level
- River mouths and water beside the sea sit at the sea surface
- The per-chunk raster gives terrain height, water head, role, tier, fluid, cave ceiling and shore distance
- WaterRole codes are stable and exposed to packs through the WATER_ROLE engine stream
- WATER_HEAD replaces the old river engine streams and an unknown stream name fails the pack load
- Banks take SMOOTH, LINEAR, CONCAVE, TERRACED or CLIFF profiles
- Containment berms grow as tall as the water needs
- Columns within two blocks of water stay solid and keep no Terrain3D overhang
- Seam dams hold water where new chunks meet chunks generated earlier
- The transition blend skips hydrology-owned columns
- Caves stay at least two blocks below river and pond beds
- The painter is the only writer of still water
- Objects cannot open water, falls or bank ground and solid objects may stand in water
- On-water objects need fluid at their anchor column
- Floating child islands stay out of channels
- Sea inlets run inland from cliff coasts at sea level and end in a chamber pool
- Inlet passages are sealed against carvers, subterrain features and the post pass
- Cave pools settle into carved lows, keep one fluid and stay out of inlet space
- Pool and inlet cells use their own cave actions in mantle data
- Water vegetation lists sit on tiers, ponds and hydrologyPolicy with SEA_FLOOR, SEA_SURFACE and SHORE_LINE parts
- Pack validation compiles hydrology settings and rejects empty or water-holding palettes
- Chunk generation fails closed when its 3x3 water rasters are not planned
- /iris find covers stream, river, major river, cascade, waterfall, mouth, pond, lake, dam, sea inlet and cave pool
- Generation history records the new feature types and drops old hydrology entries
- The studio map draws water from rasters up close and from plans when zoomed out
- Pregen prepares the current region and one region ahead
- hydrologyProbe, riverTransectProbe, hydrologyBlockProbe, seaInletProbe and cavePoolProbe report containment, rises, seams, tick conversion and missing surface
- Test forks run with a 2 GB heap, a 60-minute timeout and the Mockito agent loaded explicitly
- Generation with hydrology off is unchanged
2026-10-25 15:28:26 -04:00

370 lines
13 KiB
Groovy

/*
* Iris is a World Generator for Minecraft Servers
* Copyright (c) 2026 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 <https://www.gnu.org/licenses/>.
*/
import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
import org.gradle.api.file.DeleteSpec
import org.gradle.api.file.FileCopyDetails
import org.gradle.api.file.FileSystemOperations
import org.gradle.api.file.FileTree
import org.gradle.api.tasks.compile.JavaCompile
import org.gradle.api.attributes.Bundling
import org.gradle.jvm.toolchain.JavaLanguageVersion
import javax.inject.Inject
buildscript {
repositories {
mavenCentral()
maven { url = uri('https://jitpack.io') }
}
dependencies {
classpath('com.github.VolmitSoftware.VolmLib:packaging:master-SNAPSHOT')
}
}
plugins {
id 'java'
id 'net.neoforged.moddev' version '2.0.147'
alias(libs.plugins.shadow)
}
apply from: file('../../gradle/mod-target.gradle')
apply plugin: 'art.arcane.volmit-packaging'
interface IrisFileOperations {
@Inject
FileSystemOperations getFileSystemOperations()
}
IrisFileOperations irisFileOperations = objects.newInstance(IrisFileOperations)
apply from: file('../../gradle/modded-generation-build-revision.gradle')
apply from: file('../../gradle/volmlib-stamp.gradle')
apply from: file('../../gradle/java21-runtime-exclusions.gradle')
Properties rootProperties = new Properties()
file('../../gradle.properties').withInputStream { InputStream stream -> rootProperties.load(stream) }
String irisVersion = providers.gradleProperty('irisVersion').getOrElse(rootProperties.getProperty('irisVersion', '4.3.5'))
String minecraftVersion = providers.gradleProperty('minecraftVersion').getOrElse(rootProperties.getProperty('minecraftVersion', '26.3'))
String neoForgeVersion = providers.gradleProperty('neoForgeVersion').getOrElse(irisModTarget.neoForge.toString())
Closure<String> irisArtifactName = { String platform, String targetVersion ->
return "Iris v${project.version} [${platform}] ${targetVersion}.jar"
}
group = 'art.arcane'
version = irisVersion
java {
toolchain {
languageVersion = JavaLanguageVersion.of(25)
}
}
ext.nativeSourceLoader = 'neoforge'
apply from: file('../../gradle/native-minecraft-sources.gradle')
sourceSets {
modLibraries
main {
java {
srcDir '../modded-common/src/main/java'
srcDir '../client-common/src/main/java'
}
resources {
srcDir '../modded-common/src/main/resources'
}
}
test {
java {
srcDir '../modded-common/src/test/java'
}
}
}
repositories {
mavenCentral()
maven {
name = 'NeoForged'
url = uri('https://maven.neoforged.net/releases/')
}
maven { url = uri('https://repo.codemc.org/repository/maven-public/') }
maven { url = uri('https://jitpack.io') }
maven { url = uri('https://hub.spigotmc.org/nexus/content/repositories/snapshots/') }
}
configurations {
bundle {
canBeConsumed = false
canBeResolved = true
}
devBundle {
canBeConsumed = false
canBeResolved = true
}
}
configurations.named('bundle').configure {
exclude(group: 'com.google.code.gson')
exclude(group: 'com.google.guava')
exclude(group: 'commons-io')
exclude(group: 'org.apache.commons', module: 'commons-lang3')
exclude(group: 'it.unimi.dsi', module: 'fastutil')
exclude(group: 'org.slf4j')
exclude(group: 'org.apache.logging.log4j')
exclude(group: 'de.crazydev22.slimjar.helper')
exclude(group: 'de.crazydev22.slimjar')
exclude(group: 'io.github.slimjar')
}
configurations.compileClasspath.extendsFrom(configurations.devBundle)
configurations.runtimeClasspath.extendsFrom(configurations.devBundle)
configurations.testCompileClasspath.extendsFrom(configurations.compileClasspath, configurations.devBundle)
configurations.testRuntimeClasspath.extendsFrom(configurations.runtimeClasspath, configurations.devBundle)
dependencies {
testImplementation('junit:junit:4.13.2')
testImplementation(libs.mockito.core)
testImplementation(testFixtures('art.arcane:core'))
testImplementation(libs.spigot) {
transitive = false
}
testRuntimeOnly('org.junit.platform:junit-platform-launcher:6.1.2')
testRuntimeOnly('org.junit.vintage:junit-vintage-engine:6.1.2')
compileOnly("org.slf4j:slf4j-api:${irisModTarget.slf4j}")
compileOnly(libs.spigot) {
transitive = false
}
add('bundle', 'com.github.VolmitSoftware.VolmLib:volmlib-shared:master-SNAPSHOT') {
transitive = false
}
add('devBundle', 'com.github.VolmitSoftware.VolmLib:volmlib-shared:master-SNAPSHOT') {
transitive = false
}
add('bundle', libs.zip) {
transitive = false
}
add('devBundle', libs.zip) {
transitive = false
}
List<Object> shared = [
"art.arcane:core:${irisVersion}".toString(),
"art.arcane:spi:${irisVersion}".toString(),
libs.paralithic,
libs.lru,
libs.caffeine,
'com.fulcrumgenomics:jlibdeflate:0.2.0',
libs.dom4j,
libs.jaxen
]
add('bundle', libs.asm)
add('devBundle', 'org.ow2.asm:asm') {
version {
prefer(libs.versions.asm.get())
}
}
shared.each { Object notation ->
['bundle', 'devBundle'].each { String configuration ->
add(configuration, notation) {
if (group == 'art.arcane' && name == 'core') {
attributes {
attribute(Bundling.BUNDLING_ATTRIBUTE, objects.named(Bundling, Bundling.EXTERNAL))
}
}
}
}
}
}
def developmentModLibraries = configurations.devBundle.incoming.artifactView {
componentFilter { identifier ->
identifier instanceof org.gradle.api.artifacts.component.ProjectComponentIdentifier
|| (identifier instanceof org.gradle.api.artifacts.component.ModuleComponentIdentifier
&& (identifier.group == 'art.arcane' || identifier.group.startsWith('com.github.VolmitSoftware')))
}
}.files
def stageDevelopmentModLibraries = tasks.register('stageDevelopmentModLibraries', Sync) {
dependsOn(developmentModLibraries)
from({ developmentModLibraries.collect { File artifact -> zipTree(artifact) } })
into(layout.buildDirectory.dir('mod-libraries'))
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
exclude('META-INF/MANIFEST.MF', 'module-info.class', 'META-INF/versions/**')
}
sourceSets.modLibraries.output.classesDirs.setFrom(stageDevelopmentModLibraries)
neoForge {
version = neoForgeVersion
accessTransformers.from(art.arcane.volmit.packaging.NativeAccessRules.prepare(irisModTarget.nativeAccess.toString(), "neoforge", layout.buildDirectory.dir("native-access").get().asFile))
runs {
client {
client()
jvmArgument('-Xmx8G')
}
server {
server()
String parity = providers.gradleProperty('irisParity').getOrNull()
if (parity != null) {
systemProperty('iris.parity', parity)
}
String parityGolden = providers.gradleProperty('irisParityGolden').getOrNull()
if (parityGolden != null) {
systemProperty('iris.parity.golden', parityGolden)
}
String parityDeep = providers.gradleProperty('irisParityDeep').getOrNull()
if (parityDeep != null) {
systemProperty('iris.parity.deep', parityDeep)
}
String worldCheck = providers.gradleProperty('irisWorldCheck').getOrNull()
if (worldCheck != null) {
systemProperty('iris.worldcheck', worldCheck)
}
jvmArgument('-Xmx8G')
programArgument('nogui')
}
}
mods {
irisworldgen {
sourceSet(sourceSets.main)
sourceSet(sourceSets.modLibraries)
}
}
unitTest {
enable()
testedMod = mods.irisworldgen
}
}
tasks.named('compileJava', JavaCompile).configure {
options.encoding = 'UTF-8'
options.release.set((int) irisModTarget.java)
}
apply from: file('../../gradle/mockito-agent.gradle')
tasks.named('test').configure {
useJUnitPlatform()
classpath = classpath.minus(developmentModLibraries).plus(sourceSets.modLibraries.output)
exclude('art/arcane/volmlib/nativelib/minecraft26_2/modded/mixin/**')
}
processResources {
inputs.property('version', irisVersion)
inputs.property('minecraftVersion', minecraftVersion)
inputs.property('loaderRange', irisModTarget.neoForgeRange.toString())
filesMatching('META-INF/neoforge.mods.toml') {
expand('version': irisVersion, 'minecraftVersion': minecraftVersion, 'loaderRange': irisModTarget.neoForgeRange.toString())
}
}
FileTree staleLoaderJars = fileTree(layout.buildDirectory.dir('libs')) {
include('Iris v* [NeoForge] *.jar')
include('iris-neoforge-*.jar')
}
File legacyLoaderJar = layout.buildDirectory.file("libs/Iris-${project.version}+mc${minecraftVersion}-neoforge.jar").get().asFile
tasks.named('shadowJar', ShadowJar).configure {
doFirst {
irisFileOperations.fileSystemOperations.delete { DeleteSpec spec ->
spec.delete(staleLoaderJars, legacyLoaderJar)
}
}
from(tasks.named('generateVolmLibStamp')) {
into('META-INF/volmit')
}
archiveFileName.set(irisArtifactName('NeoForge', "${minecraftVersion}+${neoForgeVersion}"))
configurations = [project.configurations.named('bundle').get()]
from(sourceSets.main.output)
mergeServiceFiles()
if ((int) irisModTarget.java == 21) {
exclude(java21RuntimeExclusions)
}
exclude('META-INF/maven/**')
exclude('META-INF/proguard/**')
exclude('META-INF/*.SF')
exclude('META-INF/*.DSA')
exclude('META-INF/*.RSA')
// The only multi-release entry any bundled dependency contributes is an OSGi manifest. Keeping
// it forces `Multi-Release: true` onto the mod jar, which makes the loaders treat the whole
// archive as versioned and re-scan it per release directory for nothing.
exclude('META-INF/versions/**')
addMultiReleaseAttribute.set(false)
exclude('art/arcane/volmlib/util/localization/BukkitLanguageSwitcher*.class')
exclude('art/arcane/volmlib/util/localization/BukkitLanguageEditor*.class')
exclude('art/arcane/volmlib/util/localization/BukkitVolmitCommand*.class')
// Invalid package name on Java 9+ ('enum' is a keyword); a module-path scan chokes on it.
exclude('org/apache/commons/lang/enum/**')
// Compile-only annotation carriers. Shipping them duplicates packages other mods also ship.
exclude('org/jspecify/**')
exclude('com/google/errorprone/**')
exclude('com/google/j2objc/**')
exclude('org/checkerframework/**')
exclude('org/jetbrains/annotations/**')
exclude('org/intellij/lang/**')
// The Bukkit platform binding cannot load without org.bukkit on the classpath, which no mod
// loader has. Shipping it only adds dead classes that reference a missing package.
exclude('art/arcane/iris/platform/bukkit/**')
exclude('art/arcane/iris/platform/bootstrap/SlimJar*.class')
exclude('art/arcane/iris/platform/bootstrap/NativeRuntimeLibraries*.class')
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
filesMatching('META-INF/services/**') { FileCopyDetails entry ->
entry.duplicatesStrategy = DuplicatesStrategy.INCLUDE
}
relocate('org.objectweb.asm', 'art.arcane.iris.shadow.asm')
// Split-package guard. A co-installed mod that ships any of these at their original coordinates
// puts two copies of the same package on one module layer, which the loaders reject at boot
// (Terra ships paralithic; caffeine and dom4j are common in mod dependency trees). First-party
// art.arcane.volmlib is deliberately NOT relocated.
relocate('com.github.benmanes.caffeine', 'art.arcane.iris.shadow.caffeine')
relocate('com.googlecode.concurrentlinkedhashmap', 'art.arcane.iris.shadow.clhm')
relocate('com.dfsek.paralithic', 'art.arcane.iris.shadow.paralithic')
relocate('org.dom4j', 'art.arcane.iris.shadow.dom4j')
relocate('org.jaxen', 'art.arcane.iris.shadow.jaxen')
relocate('org.zeroturnaround.zip', 'art.arcane.iris.shadow.ztzip')
}
// The thin `jar` output carries valid neoforge.mods.toml metadata but bundles zero dependencies.
// Installing it is an instant NoClassDefFoundError, and it sits in build/libs right next to the real
// artifact. shadowJar is the only shippable NeoForge jar.
tasks.named('jar').configure {
enabled = false
}
tasks.named('assemble').configure {
dependsOn(tasks.named('shadowJar'))
}
pluginPackaging {
artifacts {
create('distribution') {
taskName = 'shadowJar'
policyName = 'Iris NeoForge'
prunePrefixes = ['art/arcane/volmlib/']
keepPrefixes = ['art/arcane/volmlib/util/matter/slices/']
}
}
}
apply from: file('../../gradle/modded-version-sources.gradle')