Files
Iris/probe/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

454 lines
22 KiB
Groovy

plugins {
id 'application'
}
group = 'art.arcane'
version = rootProject.version
application {
mainClass = 'art.arcane.iris.probe.ClassloadProbe'
}
configurations {
studioProbeApi
}
dependencies {
implementation(project(':core'))
compileOnly(libs.spigot)
testImplementation('junit:junit:4.13.2')
testImplementation(libs.mockito.core)
testImplementation(libs.gson)
testImplementation(testFixtures(project(':core')))
testImplementation('com.github.VolmitSoftware.VolmLib:volmlib-shared:master-SNAPSHOT') {
transitive = false
}
compileOnly('com.github.VolmitSoftware.VolmLib:volmlib-shared:master-SNAPSHOT') {
transitive = false
}
runtimeOnly(libs.paralithic)
runtimeOnly(libs.gson)
runtimeOnly(libs.lru)
runtimeOnly(libs.commons.io)
runtimeOnly(libs.commons.lang3)
runtimeOnly(libs.fastutil)
runtimeOnly(libs.caffeine)
runtimeOnly(libs.lz4)
runtimeOnly('com.fulcrumgenomics:jlibdeflate:0.2.0')
runtimeOnly(libs.zip)
runtimeOnly(libs.guava)
runtimeOnly(libs.oshi)
runtimeOnly(libs.byteBuddy.core)
runtimeOnly(libs.byteBuddy.agent)
studioProbeApi(libs.spigot) {
transitive = false
}
}
tasks.named('run', JavaExec).configure {
args(project(':core').layout.buildDirectory.dir('classes/java/main').get().asFile.absolutePath)
jvmArgs('--add-modules', 'jdk.incubator.vector')
dependsOn(':core:compileJava')
}
tasks.named('test').configure {
dependsOn(':core:compileJava')
}
Provider<String> probePack = providers.gradleProperty('probePack')
Provider<String> probeDimension = providers.gradleProperty('probeDimension')
Provider<String> probeWarmupChunks = providers.gradleProperty('probeWarmupChunks').orElse('256')
Provider<String> probeMeasuredChunks = providers.gradleProperty('probeMeasuredChunks').orElse('1024')
Provider<String> probeCenterChunkX = providers.gradleProperty('probeCenterChunkX').orElse('2048')
Provider<String> probeCenterChunkZ = providers.gradleProperty('probeCenterChunkZ').orElse('2048')
Provider<String> probeMulticore = providers.gradleProperty('probeMulticore').orElse('false')
Provider<String> probeStudio = providers.gradleProperty('probeStudio').orElse('false')
Provider<String> explicitProbeMulticore = providers.gradleProperty('probeMulticore')
Provider<String> explicitProbeStudio = providers.gradleProperty('probeStudio')
Provider<String> probeRequiredCoverage = providers.gradleProperty('probeRequiredCoverage')
Provider<String> probeSeed = providers.gradleProperty('probeSeed')
Provider<String> probeMinimumChunkX = providers.gradleProperty('probeMinimumChunkX')
Provider<String> probeMaximumChunkX = providers.gradleProperty('probeMaximumChunkX')
Provider<String> probeMinimumChunkZ = providers.gradleProperty('probeMinimumChunkZ')
Provider<String> probeMaximumChunkZ = providers.gradleProperty('probeMaximumChunkZ')
Provider<String> probeParallelism = providers.gradleProperty('probeParallelism')
Provider<String> probeShuffleSeed = providers.gradleProperty('probeShuffleSeed')
Provider<String> probeJfr = providers.gradleProperty('probeJfr')
Provider<String> probeOutput = providers.gradleProperty('probeOutput')
Provider<String> probeStrictGeometry = providers.gradleProperty('probeStrictGeometry').orElse('false')
Provider<String> probeWindows = providers.gradleProperty('probeWindows')
tasks.register('hydrologyProbe', JavaExec) {
group = 'verification'
description = 'Plans hydrology over a window range of a real pack and reports cold first-chunk latency, plan cost, yield, containment, rising water, seams and fluid-tick checks per window.'
mainClass = 'art.arcane.iris.probe.HydrologyProbe'
classpath = sourceSets.main.runtimeClasspath
jvmArgs('--add-modules', 'jdk.incubator.vector')
minHeapSize = '2g'
maxHeapSize = '6g'
dependsOn(':core:compileJava')
doFirst {
Map<String, Provider<String>> required = [
probePack: probePack,
probeSeed: probeSeed,
probeWindows: probeWindows
]
required.each { String name, Provider<String> value ->
if (!value.isPresent() || value.get().isBlank()) {
throw new GradleException("hydrologyProbe requires -P${name}=<value>")
}
}
args(probePack.get(), probeDimension.getOrElse('-'), probeSeed.get(), probeWindows.get(),
providers.gradleProperty('probeThreads').getOrElse('8'), probeStudio.get())
}
}
tasks.register('riverTransectProbe', JavaExec) {
group = 'verification'
description = 'Plans hydrology over a window range of a real pack and renders each window as a plan, river cross-sections and pond outlines.'
mainClass = 'art.arcane.iris.probe.RiverTransectProbe'
classpath = sourceSets.main.runtimeClasspath
jvmArgs('--add-modules', 'jdk.incubator.vector', '-Djava.awt.headless=true')
minHeapSize = '2g'
maxHeapSize = '6g'
dependsOn(':core:compileJava')
doFirst {
Map<String, Provider<String>> required = [
probePack: probePack,
probeSeed: probeSeed,
probeWindows: probeWindows,
probeOutput: probeOutput
]
required.each { String name, Provider<String> value ->
if (!value.isPresent() || value.get().isBlank()) {
throw new GradleException("riverTransectProbe requires -P${name}=<value>")
}
}
java.nio.file.Path output = new File(probeOutput.get()).canonicalFile.toPath()
if (output.startsWith(rootProject.projectDir.canonicalFile.toPath())) {
throw new GradleException('probeOutput must point outside the repository')
}
args(probePack.get(), probeDimension.getOrElse('-'), probeSeed.get(), probeWindows.get(), output.toString(), probeStudio.get())
}
}
tasks.register('hydrologyBlockProbe', JavaExec) {
group = 'verification'
description = 'Generates up to 64 real pack chunks and checks the planned water surface, containment, fluid-tick stability and cane support, with block images.'
mainClass = 'art.arcane.iris.probe.HydrologyBlockProbe'
classpath = sourceSets.main.runtimeClasspath
jvmArgs('--add-modules', 'jdk.incubator.vector', '-Djava.awt.headless=true')
maxHeapSize = '4g'
dependsOn(':core:compileJava')
doFirst {
Map<String, Provider<String>> required = [
probePack: probePack,
probeDimension: probeDimension,
probeSeed: probeSeed,
probeMinimumChunkX: probeMinimumChunkX,
probeMaximumChunkX: probeMaximumChunkX,
probeMinimumChunkZ: probeMinimumChunkZ,
probeMaximumChunkZ: probeMaximumChunkZ,
probeOutput: probeOutput
]
required.each { String name, Provider<String> value ->
if (!value.isPresent() || value.get().isBlank()) {
throw new GradleException("hydrologyBlockProbe requires -P${name}=<value>")
}
}
java.nio.file.Path output = new File(probeOutput.get()).canonicalFile.toPath()
if (output.startsWith(rootProject.projectDir.canonicalFile.toPath())) {
throw new GradleException('probeOutput must point outside the repository')
}
args(probePack.get(), probeDimension.get(), probeSeed.get(), probeMinimumChunkX.get(), probeMaximumChunkX.get(),
probeMinimumChunkZ.get(), probeMaximumChunkZ.get(), probeRequiredCoverage.getOrElse('-'), output.toString(), probeStudio.get())
}
}
tasks.register('terrain3DProbe', JavaExec) {
group = 'verification'
description = 'Generates real pack chunks, verifies volumetric terrain spans and required biome overhang coverage, and exports actual block cross-sections.'
mainClass = 'art.arcane.iris.probe.Terrain3DProbe'
classpath = sourceSets.main.runtimeClasspath
jvmArgs('--add-modules', 'jdk.incubator.vector', '-Djava.awt.headless=true')
dependsOn(':core:compileJava')
doFirst {
Map<String, Provider<String>> required = [
probePack: probePack,
probeDimension: probeDimension,
probeSeed: probeSeed,
probeMinimumChunkX: probeMinimumChunkX,
probeMaximumChunkX: probeMaximumChunkX,
probeMinimumChunkZ: probeMinimumChunkZ,
probeMaximumChunkZ: probeMaximumChunkZ,
probeRequiredCoverage: probeRequiredCoverage,
probeOutput: probeOutput
]
required.each { String name, Provider<String> value ->
if (!value.isPresent() || value.get().isBlank()) {
throw new GradleException("terrain3DProbe requires -P${name}=<value>")
}
}
args(probePack.get(), probeDimension.get(), probeSeed.get(), probeMinimumChunkX.get(),
probeMaximumChunkX.get(), probeMinimumChunkZ.get(), probeMaximumChunkZ.get(),
probeRequiredCoverage.get(), probeOutput.get(), probeStrictGeometry.get(), probeStudio.get())
}
}
tasks.register('genProbe', JavaExec) {
group = 'verification'
description = 'Constructs a real Iris engine over a real pack and verifies generation hashes and optional native backend usage.'
mainClass = 'art.arcane.iris.probe.GenerationProbe'
classpath = sourceSets.main.runtimeClasspath
jvmArgs('--add-modules', 'jdk.incubator.vector')
minHeapSize = '2g'
maxHeapSize = '6g'
if (providers.gradleProperty('probeNativeAccess').orElse('false').get().toBoolean()) {
jvmArgs('--enable-native-access=ALL-UNNAMED')
}
if (providers.gradleProperty('probeNativeBillow').isPresent()) {
systemProperty('volmlib.noise.nativeBillow', providers.gradleProperty('probeNativeBillow').get())
}
systemProperty('volmlib.noise.nativeBillowDiagnostics', providers.gradleProperty('probeNativeDiagnostics').orElse('false').get())
systemProperty('iris.probe.expectedBackend', providers.gradleProperty('probeExpectedBackend').orElse('').get())
systemProperty('iris.probe.requireNativeSamples', providers.gradleProperty('probeRequireNativeSamples').orElse('false').get())
if (providers.gradleProperty('probeNativeLibrary').isPresent()) {
systemProperty('volmlib.native.library', providers.gradleProperty('probeNativeLibrary').get())
}
if (providers.gradleProperty('probeNativePolicy').isPresent()) {
systemProperty('volmlib.native', providers.gradleProperty('probeNativePolicy').get())
}
systemProperty('iris.probe.threads', providers.gradleProperty('probeThreads').orElse('1').get())
systemProperty('iris.probe.pregenProfile', providers.gradleProperty('probePregenProfile').orElse('false').get())
systemProperty('iris.probe.hydrologyPreplan', providers.gradleProperty('probeHydrologyPreplan').orElse('false').get())
systemProperty('iris.probe.historyRouter', providers.gradleProperty('probeHistoryRouter').orElse('false').get())
systemProperty('iris.probe.historyDigest', providers.gradleProperty('probeHistoryDigest').orElse('false').get())
systemProperty('iris.probe.productionBuffers', providers.gradleProperty('probeProductionBuffers').orElse('false').get())
String probeCounters = providers.gradleProperty('probeCounters').orElse('false').get()
systemProperty('iris.generation.counters', probeCounters)
systemProperty('volmlib.cache.countContention', probeCounters)
dependsOn(':core:compileJava')
doFirst {
if (!probePack.isPresent() || probePack.get().isBlank()) {
throw new GradleException('genProbe requires -PprobePack=/absolute/path/to/pack')
}
Provider<String> probeDumpFile = providers.gradleProperty('probeDumpFile')
if (probeDumpFile.isPresent()) {
File dump = new File(probeDumpFile.get())
if (!dump.isAbsolute() || dump.isDirectory()) {
throw new GradleException('probeDumpFile must be an absolute file path')
}
java.nio.file.Path normalizedDump = dump.canonicalFile.toPath()
if (normalizedDump.startsWith(rootProject.projectDir.canonicalFile.toPath())) {
throw new GradleException('probeDumpFile must point outside the repository')
}
if (normalizedDump.parent == null || !normalizedDump.parent.toFile().isDirectory()) {
throw new GradleException('probeDumpFile parent directory must already exist')
}
systemProperty('iris.probe.dumpFile', normalizedDump.toString())
}
if (!probeDimension.isPresent() || probeDimension.get().isBlank()) {
throw new GradleException('genProbe requires -PprobeDimension=<dimension-key>')
}
args(
probePack.get(),
probeDimension.get(),
probeWarmupChunks.get(),
probeMeasuredChunks.get(),
probeCenterChunkX.get(),
probeCenterChunkZ.get(),
probeMulticore.get(),
probeStudio.get(),
probeSeed.orElse('1337').get())
}
}
tasks.register('genDiff', JavaExec) {
group = 'verification'
description = 'Compares two genProbe chunk dumps and reports block, surface, biome and per-category parity.'
mainClass = 'art.arcane.iris.probe.GenerationDiff'
classpath = sourceSets.main.runtimeClasspath
maxHeapSize = '4g'
doFirst {
Provider<String> baseline = providers.gradleProperty('diffBaseline')
Provider<String> candidate = providers.gradleProperty('diffCandidate')
if (!baseline.isPresent() || !candidate.isPresent()) {
throw new GradleException('genDiff requires -PdiffBaseline=<dump> and -PdiffCandidate=<dump>')
}
args(baseline.get(), candidate.get(), providers.gradleProperty('diffWorstChunks').orElse('10').get())
}
}
tasks.register('generationOrderProbe', JavaExec) {
group = 'verification'
description = 'Compares complete real-pack chunk block and biome hashes across forward, reverse, shuffled and bounded-parallel generation orders.'
mainClass = 'art.arcane.iris.probe.GenerationOrderProbe'
classpath = sourceSets.main.runtimeClasspath
jvmArgs('--add-modules', 'jdk.incubator.vector')
dependsOn(':core:compileJava')
doFirst {
Map<String, Provider<String>> required = [
probePack: probePack,
probeDimension: probeDimension,
probeSeed: probeSeed,
probeMinimumChunkX: probeMinimumChunkX,
probeMaximumChunkX: probeMaximumChunkX,
probeMinimumChunkZ: probeMinimumChunkZ,
probeMaximumChunkZ: probeMaximumChunkZ,
probeParallelism: probeParallelism,
probeShuffleSeed: probeShuffleSeed,
probeMulticore: explicitProbeMulticore,
probeStudio: explicitProbeStudio
]
required.each { String name, Provider<String> value ->
if (!value.isPresent() || value.get().isBlank()) {
throw new GradleException("generationOrderProbe requires -P${name}=<value>")
}
}
args(
probePack.get(),
probeDimension.get(),
probeSeed.get(),
probeMinimumChunkX.get(),
probeMaximumChunkX.get(),
probeMinimumChunkZ.get(),
probeMaximumChunkZ.get(),
probeParallelism.get(),
probeShuffleSeed.get(),
explicitProbeMulticore.get(),
explicitProbeStudio.get())
}
}
tasks.register('cavePoolProbe', JavaExec) {
group = 'verification'
description = 'Generates real pack chunks with cave pools and reports containment, pool sizes and generation-order hashes.'
mainClass = 'art.arcane.iris.probe.CavePoolProbe'
classpath = sourceSets.main.runtimeClasspath
jvmArgs('--add-modules', 'jdk.incubator.vector')
minHeapSize = '2g'
maxHeapSize = '8g'
dependsOn(':core:compileJava')
doFirst {
Provider<String> threads = providers.gradleProperty('probeThreads').orElse('8')
Provider<String> work = providers.gradleProperty('probeWork').orElse(System.getProperty('java.io.tmpdir'))
Map<String, Provider<String>> required = [
probePack: probePack,
probeDimension: probeDimension,
probeSeed: probeSeed,
probeMinimumChunkX: probeMinimumChunkX,
probeMaximumChunkX: probeMaximumChunkX,
probeMinimumChunkZ: probeMinimumChunkZ,
probeMaximumChunkZ: probeMaximumChunkZ
]
required.each { String name, Provider<String> value ->
if (!value.isPresent() || value.get().isBlank()) {
throw new GradleException("cavePoolProbe requires -P${name}=<value>")
}
}
args(probePack.get(), probeDimension.get(), probeSeed.get(), probeMinimumChunkX.get(), probeMaximumChunkX.get(),
probeMinimumChunkZ.get(), probeMaximumChunkZ.get(), threads.get(), work.get())
}
}
tasks.register('seaInletProbe', JavaExec) {
group = 'verification'
description = 'Plans and generates real pack chunks with and without sea inlets and reports containment, seal, roof, sea level, ocean connection and order determinism.'
mainClass = 'art.arcane.iris.probe.SeaInletProbe'
classpath = sourceSets.main.runtimeClasspath
jvmArgs('--add-modules', 'jdk.incubator.vector', '-Djava.awt.headless=true')
minHeapSize = '2g'
maxHeapSize = '8g'
dependsOn(':core:compileJava')
doFirst {
Provider<String> packOff = providers.gradleProperty('probePackOff')
Provider<String> windows = providers.gradleProperty('probeWindows')
Provider<String> mode = providers.gradleProperty('probeMode').orElse('gate')
if (!probePack.isPresent() || !packOff.isPresent() || !probeDimension.isPresent() || !windows.isPresent() || !probeOutput.isPresent()) {
throw new GradleException('seaInletProbe requires probePack, probePackOff, probeDimension, probeWindows=x0..x1,z0..z1 and probeOutput')
}
String[] axes = windows.get().split(',')
String[] xs = axes[0].split('\\.\\.')
String[] zs = axes[1].split('\\.\\.')
args(probePack.get(), packOff.get(), probeDimension.get(), probeSeed.orElse('1337').get(), xs[0], xs[1], zs[0], zs[1], mode.get(),
probeOutput.get(), providers.gradleProperty('probeThreads').orElse('6').get())
}
}
tasks.register('transitionProbe', JavaExec) {
group = 'verification'
description = 'Exercises a bounded two-pack Studio cutover, transition routing, receipts and semantics with a vanilla-biome pack.'
mainClass = 'art.arcane.iris.probe.TransitionProbe'
workingDir = rootProject.projectDir
classpath = sourceSets.main.runtimeClasspath
jvmArgs('--add-modules', 'jdk.incubator.vector')
timeout = java.time.Duration.ofMinutes(12)
doFirst {
Provider<String> replacement = providers.gradleProperty('probeReplacementPack')
if (!probePack.isPresent() || !replacement.isPresent() || !probeDimension.isPresent() || !probeOutput.isPresent()) {
throw new GradleException('transitionProbe requires probePack, probeReplacementPack, probeDimension and probeOutput')
}
java.nio.file.Path output = new File(probeOutput.get()).canonicalFile.toPath()
if (output.startsWith(rootProject.projectDir.canonicalFile.toPath())) {
throw new GradleException('probeOutput must point outside the repository')
}
args(probePack.get(), replacement.get(), probeDimension.get(), probeSeed.getOrElse('1337'),
providers.gradleProperty('probeTransitionWidth').getOrElse('32'),
providers.gradleProperty('probeTransitionChunks').getOrElse('2'), output.toString(),
providers.gradleProperty('probeTransitionReverse').getOrElse('false'))
}
}
['transitionProbe', 'hydrologyProbe', 'genProbe', 'generationOrderProbe', 'riverTransectProbe', 'terrain3DProbe',
'hydrologyBlockProbe', 'cavePoolProbe', 'seaInletProbe'].each { String taskName ->
tasks.named(taskName, JavaExec).configure { JavaExec task ->
classpath += configurations.studioProbeApi
doFirst {
if (probeJfr.isPresent()) {
String requestedPath = probeJfr.get()
if (requestedPath.isBlank()) {
throw new GradleException('probeJfr must be a nonblank absolute path')
}
File recording = new File(requestedPath)
if (!recording.isAbsolute()) {
throw new GradleException('probeJfr must be an absolute path')
}
if (recording.isDirectory()) {
throw new GradleException('probeJfr must name a recording file, not a directory')
}
java.nio.file.Path normalized = recording.canonicalFile.toPath()
java.nio.file.Path repository = rootProject.projectDir.canonicalFile.toPath()
if (normalized.startsWith(repository)) {
throw new GradleException('probeJfr must point outside the repository')
}
File parent = normalized.toFile().parentFile
if (parent == null || !parent.isDirectory()) {
throw new GradleException('probeJfr parent directory must already exist')
}
String settings = providers.gradleProperty('probeJfrSettings').orElse('profile').get()
if (settings.isBlank() || settings.contains(',')) {
throw new GradleException('probeJfrSettings must name a JFR configuration or an absolute .jfc path')
}
task.jvmArgs(
'-XX:FlightRecorderOptions=stackdepth=256',
"-XX:StartFlightRecording=filename=${normalized},settings=${settings},dumponexit=true"
)
}
}
}
}
String deserializationPack = providers.gradleProperty('deserializationPack')
.orElse(project.file('src/main/resources/deserialization-pack').absolutePath)
.get()
tasks.register('deserializationProbe', JavaExec) {
group = 'verification'
description = 'Deserializes entity, spawner and loot pack content through the real Iris Gson loaders on a Bukkit-free JVM to keep Bukkit-typed pack-model fields dead.'
mainClass = 'art.arcane.iris.probe.DeserializationProbe'
classpath = sourceSets.main.runtimeClasspath
jvmArgs('--add-modules', 'jdk.incubator.vector')
args(deserializationPack)
dependsOn(':core:compileJava')
}