plugins { id 'java-library' id 'net.neoforged.moddev' version '2.0.148' } group = 'net.dnorberg.rivencraft' version = mod_version base { archivesName = "rivencraft-${minecraft_version}" } java.toolchain.languageVersion = JavaLanguageVersion.of(25) neoForge { version = project.neo_version runs { client { client() if (project.hasProperty('connectAddress')) { programArgument '--quickPlayMultiplayer' programArgument project.property('connectAddress').toString() } if (project.hasProperty('clientName')) { programArgument '--username' programArgument project.property('clientName').toString() gameDirectory = project.layout.buildDirectory.dir('run-client-' + project.property('clientName')).get().asFile } } server { server() gameDirectory = project.hasProperty('serverRunDir') ? file(project.property('serverRunDir').toString()) : project.layout.buildDirectory.dir('run-server').get().asFile programArgument '--nogui' } } mods { rivencraft { sourceSet(sourceSets.main) } } } def generateModMetadata = tasks.register('generateModMetadata', ProcessResources) { def replaceProperties = [ mod_id: mod_id, mod_name: mod_name, mod_license: mod_license, mod_version: mod_version, neo_version: neo_version, minecraft_version_range: minecraft_version_range, ] inputs.properties replaceProperties expand replaceProperties from 'src/main/templates' into 'build/generated/sources/modMetadata' } sourceSets.main.resources.srcDir generateModMetadata neoForge.ideSyncTask generateModMetadata // Keep the bundled page list in step with the 1.21.1 symbol definitions. abstract class GeneratePortSymbolIndex extends DefaultTask { @InputDirectory abstract DirectoryProperty getSymbolsDir() @InputDirectory abstract DirectoryProperty getPortSymbolsDir() @OutputDirectory abstract DirectoryProperty getIndexDir() @TaskAction void generate() { File root = symbolsDir.get().asFile def definitions = new LinkedHashMap() java.nio.file.Files.walk(root.toPath()).withCloseable { paths -> paths.filter { java.nio.file.Files.isRegularFile(it) && it.toString().endsWith('.json') } .sorted().forEach { source -> String path = root.toPath().relativize(source).toString() .replace('\\', '/').replaceAll(/\.json$/, '') definitions["rivencraft:${path}"] = new groovy.json.JsonSlurper().parse(source.toFile()) } } File portRoot = portSymbolsDir.get().asFile java.nio.file.Files.walk(portRoot.toPath()).withCloseable { paths -> paths.filter { java.nio.file.Files.isRegularFile(it) && it.toString().endsWith('.json') } .sorted().forEach { source -> String path = portRoot.toPath().relativize(source).toString() .replace('\\', '/').replaceAll(/\.json$/, '') definitions["rivencraft:${path}"] = new groovy.json.JsonSlurper().parse(source.toFile()) } } File index = new File(indexDir.get().asFile, 'assets/rivencraft/creative_symbol_pages.json') index.parentFile.mkdirs() index.text = groovy.json.JsonOutput.prettyPrint(groovy.json.JsonOutput.toJson(definitions)) + '\n' } } def generatePortSymbolIndex = tasks.register('generatePortSymbolIndex', GeneratePortSymbolIndex) { symbolsDir.set(layout.projectDirectory.dir('../../src/main/resources/data/rivencraft/rivencraft/symbols')) portSymbolsDir.set(layout.projectDirectory.dir('src/main/resources/data/rivencraft/rivencraft/symbols')) indexDir.set(layout.buildDirectory.dir('generated/resources/creativeSymbols')) } sourceSets.main.resources.srcDir generatePortSymbolIndex // Use the established symbol names/descriptions without overwriting 26.3-only strings. abstract class GeneratePortLanguage extends DefaultTask { @InputFile abstract RegularFileProperty getBaseLanguage() @InputFile abstract RegularFileProperty getPortLanguage() @OutputDirectory abstract DirectoryProperty getOutputDir() @TaskAction void generate() { def base = new groovy.json.JsonSlurper().parse(baseLanguage.get().asFile) as Map def port = new groovy.json.JsonSlurper().parse(portLanguage.get().asFile) as Map base.putAll(port) File result = new File(outputDir.get().asFile, 'assets/rivencraft/lang/en_us.json') result.parentFile.mkdirs() result.text = groovy.json.JsonOutput.prettyPrint(groovy.json.JsonOutput.toJson(base)) + '\n' } } def generatePortLanguage = tasks.register('generatePortLanguage', GeneratePortLanguage) { baseLanguage.set(layout.projectDirectory.file('../../src/main/resources/assets/rivencraft/lang/en_us.json')) portLanguage.set(layout.projectDirectory.file('port_en_us.json')) outputDir.set(layout.buildDirectory.dir('generated/resources/portLanguage')) } sourceSets.main.resources.srcDir generatePortLanguage // Derive registered presentation variants without duplicating vanilla gameplay settings. abstract class GeneratePortDimensionTypes extends DefaultTask { @InputFile abstract RegularFileProperty getMinecraftJar() @OutputDirectory abstract DirectoryProperty getOutputDir() @TaskAction void generate() { new java.util.zip.ZipFile(minecraftJar.get().asFile).withCloseable { archive -> def entry = archive.getEntry('data/minecraft/dimension_type/overworld.json') if (entry == null) throw new GradleException('Missing vanilla Overworld dimension type') String template = archive.getInputStream(entry).withCloseable { it.getText('UTF-8') } def dayEntry = archive.getEntry('data/minecraft/timeline/day.json') if (dayEntry == null) throw new GradleException('Missing vanilla day timeline') def day = archive.getInputStream(dayEntry).withCloseable { new groovy.json.JsonSlurper().parseText(it.getText('UTF-8')) } // Keep gameplay schedules and illumination, but do not tint these // native non-Overworld sky backgrounds blue/black with the day cycle. ['fog_color', 'sky_color', 'cloud_color'].each { day.tracks.remove("minecraft:visual/${it}".toString()) } // Markers are global per clock and already owned by minecraft:day. day.remove('time_markers') File dayFile = new File(outputDir.get().asFile, 'data/rivencraft/timeline/age_sky_day.json') dayFile.parentFile.mkdirs() dayFile.text = groovy.json.JsonOutput.prettyPrint(groovy.json.JsonOutput.toJson(day)) + '\n' File timelineTag = new File(outputDir.get().asFile, 'data/rivencraft/tags/timeline/age_sky.json') timelineTag.parentFile.mkdirs() timelineTag.text = groovy.json.JsonOutput.prettyPrint(groovy.json.JsonOutput.toJson([ values: ['#minecraft:universal', 'rivencraft:age_sky_day', 'minecraft:moon', 'minecraft:early_game']])) + '\n' ['overworld', 'nether', 'end', 'static'].each { sky -> ['age_normal', 'age_dark', 'age_bright', 'age_cave_bright'].each { lighting -> if (sky == 'overworld' && lighting == 'age_normal') return String name = lighting + (sky == 'overworld' ? '' : "_sky_${sky}") def definition = new groovy.json.JsonSlurper().parseText(template) definition.has_skylight = lighting in ['age_normal', 'age_bright'] definition.ambient_light = lighting in ['age_bright', 'age_cave_bright'] ? 1.0 : 0.0 // 26.3's lightmap reads this attribute, not ambient_light. if (definition.ambient_light == 1.0) definition.attributes['minecraft:visual/ambient_light_color'] = '#ffffff' if (sky != 'overworld') { definition.skybox = sky == 'end' ? 'end' : 'none' definition.attributes['minecraft:visual/fog_color'] = sky == 'static' ? '#929292' : sky == 'end' ? '#181318' : '#6b2725' definition.attributes['minecraft:visual/sky_color'] = sky == 'static' ? '#929292' : sky == 'end' ? '#000000' : '#6b2725' definition.attributes['minecraft:visual/cloud_color'] = '#00000000' if (sky == 'nether') { definition.attributes['minecraft:visual/fog_start_distance'] = 10.0 definition.attributes['minecraft:visual/fog_end_distance'] = 96.0 } definition.timelines = '#rivencraft:age_sky' } File result = new File(outputDir.get().asFile, "data/rivencraft/dimension_type/${name}.json") result.parentFile.mkdirs() result.text = groovy.json.JsonOutput.prettyPrint(groovy.json.JsonOutput.toJson(definition)) + '\n' } } } } } def generatePortDimensionTypes = tasks.register('generatePortDimensionTypes', GeneratePortDimensionTypes) { dependsOn 'createMinecraftArtifacts' minecraftJar.set(layout.buildDirectory.file("moddev/artifacts/minecraft-patched-${neo_version}.jar")) outputDir.set(layout.buildDirectory.dir('generated/resources/dimensionTypes')) } sourceSets.main.resources.srcDir generatePortDimensionTypes tasks.named('processResources', ProcessResources).configure { // Reuse the existing sprite and base model without copying binary assets. from('../../src/main/resources') { include 'data/rivencraft/worldgen/structure_set/fortresses.json' include 'data/rivencraft/worldgen/structure_set/bastions.json' include 'assets/rivencraft/models/block/star_fissure.json' include 'assets/rivencraft/blockstates/star_fissure.json' include 'assets/rivencraft/sounds.json' include 'assets/rivencraft/sounds/**/*.ogg' include 'assets/rivencraft/textures/item/crystal_shard.png' include 'assets/rivencraft/textures/item/page.png' include 'assets/rivencraft/textures/item/page_link_panel.png' include 'assets/rivencraft/textures/item/page_symbol.png' include 'assets/rivencraft/models/item/page_symbol.json' include 'assets/rivencraft/textures/gui/symbolcomponents.png' include 'assets/rivencraft/models/item/page_link_panel.json' include 'assets/rivencraft/textures/item/ink_vial.png' include 'assets/rivencraft/textures/item/ink_vial.png.mcmeta' include 'assets/rivencraft/models/item/ink_vial.json' include 'assets/rivencraft/models/item/crystal_shard.json' include 'assets/rivencraft/textures/item/descriptive_book.png' include 'assets/rivencraft/models/item/descriptive_book.json' include 'assets/rivencraft/textures/item/linking_book.png' include 'assets/rivencraft/textures/item/linking_book_unwritten.png' include 'assets/rivencraft/models/item/linking_book_bound.json' include 'assets/rivencraft/textures/gui/bookui_cover.png' include 'assets/rivencraft/textures/gui/notebook_pages.png' include 'assets/rivencraft/textures/gui/bookui_pager.png' include 'assets/rivencraft/textures/block/crystal_block.png' include 'assets/rivencraft/textures/block/book_binder.png' include 'assets/rivencraft/textures/block/book_binder_press.png' include 'assets/rivencraft/textures/gui/book_binder_legacy.png' include 'assets/rivencraft/textures/gui/ink_mixer_legacy.png' include 'assets/rivencraft/textures/block/inkwell_base.png' include 'assets/rivencraft/textures/block/inkwell_cup.png' include 'assets/rivencraft/textures/block/ink.png' include 'assets/rivencraft/textures/block/ink_glass.png' include 'assets/rivencraft/models/block/ink_mixer.json' include 'assets/rivencraft/models/item/ink_mixer.json' include 'assets/rivencraft/blockstates/ink_mixer.json' include 'data/rivencraft/loot_table/blocks/ink_mixer.json' include 'assets/rivencraft/models/block/book_binder.json' include 'assets/rivencraft/models/item/book_binder.json' include 'assets/rivencraft/blockstates/book_binder.json' include 'data/rivencraft/loot_table/blocks/book_binder.json' include 'assets/rivencraft/models/block/crystal_block.json' include 'assets/rivencraft/blockstates/crystal_block.json' include 'assets/rivencraft/blockstates/budding_crystal.json' include 'assets/rivencraft/blockstates/small_crystal_bud.json' include 'assets/rivencraft/blockstates/medium_crystal_bud.json' include 'assets/rivencraft/blockstates/large_crystal_bud.json' include 'assets/rivencraft/blockstates/crystal_cluster.json' include 'assets/rivencraft/models/block/budding_crystal.json' include 'assets/rivencraft/models/block/small_crystal_bud.json' include 'assets/rivencraft/models/block/medium_crystal_bud.json' include 'assets/rivencraft/models/block/large_crystal_bud.json' include 'assets/rivencraft/models/block/crystal_cluster.json' include 'assets/rivencraft/models/item/budding_crystal.json' include 'assets/rivencraft/models/item/small_crystal_bud.json' include 'assets/rivencraft/models/item/medium_crystal_bud.json' include 'assets/rivencraft/models/item/large_crystal_bud.json' include 'assets/rivencraft/models/item/crystal_cluster.json' include 'assets/rivencraft/textures/block/small_crystal_bud.png' include 'assets/rivencraft/textures/block/medium_crystal_bud.png' include 'assets/rivencraft/textures/block/large_crystal_bud.png' include 'assets/rivencraft/textures/block/crystal_cluster.png' include 'data/rivencraft/loot_table/blocks/crystal_block.json' include 'data/minecraft/tags/block/mineable/pickaxe.json' include 'data/minecraft/tags/item/cluster_max_harvestables.json' } } tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' } tasks.named('runServer').configure { standardInput = System.in }