buildscript { repositories { jcenter() maven { name "Modmuss50 Repository" url 'https://maven.modmuss50.me' } } dependencies { classpath "net.fabricmc:weave:0.2.0.+" classpath "net.fabricmc:stitch:0.1.1.39+" classpath "commons-io:commons-io:1.4" classpath "com.google.guava:guava:19.0" classpath 'de.undercouch:gradle-download-task:3.4.3' classpath 'net.fabricmc:tiny-remapper:+' } } plugins { id 'de.undercouch.download' version '3.4.3' id 'maven' id 'maven-publish' } def minecraft_version = "19w03a" def ENV = System.getenv() // Fetch build number from Jenkins def build_number = ENV.BUILD_NUMBER ?: "local" def yarnVersion = "${minecraft_version}.$build_number" repositories { mavenCentral() maven { name "Modmuss50 Repository" url 'https://maven.modmuss50.me' } } configurations { enigmaRuntime { resolutionStrategy { cacheDynamicVersionsFor 0, "seconds" cacheChangingModulesFor 0, "seconds" } } } dependencies { enigmaRuntime "net.fabricmc:stitch:0.1.1.39+" enigmaRuntime "cuchaz:enigma:0.12.2.+:all" } def mappingsDir = file("mappings") def cacheFilesMinecraft = file(".gradle/minecraft") def tempDir = file(".gradle/temp") def mergedFile = file("${minecraft_version}-merged.jar") def intermediaryJar = file("${minecraft_version}-intermediary.jar") def namedJar = file("${minecraft_version}-named.jar") def versionFile = new File(cacheFilesMinecraft, "${minecraft_version}.json") def clientJar = new File(cacheFilesMinecraft, "${minecraft_version}-client.jar") def serverJar = new File(cacheFilesMinecraft, "${minecraft_version}-server.jar") def libraries = new File(cacheFilesMinecraft, "${minecraft_version}-libraries") def libs = new File("build/libs/") import groovy.json.JsonSlurper import org.apache.commons.io.FileUtils import com.google.common.hash.Hashing import com.google.common.io.Files import net.fabricmc.stitch.commands.CommandMergeTiny import net.fabricmc.stitch.merge.JarMerger import net.fabricmc.tinyremapper.OutputConsumerPath import net.fabricmc.tinyremapper.TinyRemapper import net.fabricmc.tinyremapper.TinyUtils import net.fabricmc.weave.CommandTinyify import net.fabricmc.weave.CommandFindMappingErrors import groovy.io.FileType import java.util.zip.GZIPOutputStream boolean validateChecksum(File file, String checksum) { if (file != null) { def hash = Files.hash(file, Hashing.sha1()) def builder = new StringBuilder() hash.asBytes().each { builder.append(Integer.toString((it & 0xFF) + 0x100, 16).substring(1)) } return builder.toString().equals(checksum) } return false } task downloadVersionsManifest { //inputs.property "mc_ver", minecraft_version inputs.property "currenttime", new Date() def manifestFile = new File(cacheFilesMinecraft, "version_manifest.json") outputs.file(manifestFile) doLast { logger.lifecycle(":downloading minecraft versions manifest") FileUtils.copyURLToFile(new URL("https://launchermeta.mojang.com/mc/game/version_manifest.json"), manifestFile) } } def getManifestVersion(File manifestFile, String minecraft_version) { def manifest = manifestFile.exists() ? new JsonSlurper().parseText(FileUtils.readFileToString(manifestFile)) : null return manifest != null ? manifest.versions.stream().filter({it.id.equals(minecraft_version)}).findFirst() : java.util.Optional.empty() } task downloadWantedVersionManifest(dependsOn: downloadVersionsManifest) { def manifestFile = downloadVersionsManifest.outputs.files.singleFile def manifestVersion = getManifestVersion(manifestFile, minecraft_version) //have to grab the release time as there's a current timestamp on each element?! inputs.property "releaseTime", manifestVersion.isPresent() ? manifestVersion.get().releaseTime : -1 outputs.file versionFile doLast { manifestVersion = getManifestVersion(manifestFile, minecraft_version) //nb need to re-read here in case it didn't exist before if (manifestVersion.isPresent() || versionFile.exists()) { if (manifestVersion.isPresent()) { FileUtils.copyURLToFile(new URL(manifestVersion.get().url), versionFile) } } else { throw new RuntimeException("No version data for Minecraft version ${minecraft_version}") } } } task downloadMcJars(dependsOn: downloadWantedVersionManifest) { inputs.files versionFile outputs.files(clientJar, serverJar) outputs.upToDateWhen { def version = new JsonSlurper().parseText(FileUtils.readFileToString(versionFile)) return clientJar.exists() && serverJar.exists() && validateChecksum(clientJar, version.downloads.client.sha1) && validateChecksum(serverJar, version.downloads.server.sha1) } doLast { if (!versionFile.exists()) { throw new RuntimeException("Can't download the jars without the ${versionFile.name} file!") } //reload in case it changed def version = new JsonSlurper().parseText(FileUtils.readFileToString(versionFile)) logger.lifecycle(":downloading minecraft jars") if (!clientJar.exists() || !validateChecksum(clientJar, version.downloads.client.sha1)) { logger.lifecycle(":downloading minecraft client") FileUtils.copyURLToFile(new URL(version.downloads.client.url), clientJar) } if (!serverJar.exists() || !validateChecksum(serverJar, version.downloads.server.sha1)) { logger.lifecycle(":downloading minecraft server") FileUtils.copyURLToFile(new URL(version.downloads.server.url), serverJar) } } } task mergeJars(dependsOn: downloadMcJars) { inputs.files downloadMcJars.outputs.files.files outputs.file(mergedFile) doLast { logger.lifecycle(":merging jars") def client = inputs.files.files.find {it.name.endsWith("-client.jar")} def server = inputs.files.files.find {it.name.endsWith("-server.jar")} def merged = mergedFile def jarMerger = new JarMerger(client, server, merged) jarMerger.merge() jarMerger.close() } } task setupYarn(dependsOn: mergeJars) { } task yarn(dependsOn: setupYarn) { doLast { ant.java( classname: 'cuchaz.enigma.Main', classpath: configurations.enigmaRuntime.asPath, fork: true, spawn: true ) { arg(value: mergedFile.getAbsolutePath()) arg(value: mappingsDir.getAbsolutePath()) } } } task buildEnigma(type: Zip) { from mappingsDir include "**/*" archiveName "yarn-enigma-${yarnVersion}.zip" destinationDir(file("build/libs")) } task checkMappings { inputs.dir mappingsDir doLast { logger.lifecycle(":checking mappings") String[] args = [ mergedFile.getAbsolutePath(), mappingsDir.getAbsolutePath() ] new CommandFindMappingErrors().run(args) } } task downloadIntermediary(type: Download){ src "https://github.com/FabricMC/intermediary/raw/master/mappings/${minecraft_version}.tiny" dest new File(cacheFilesMinecraft, "${minecraft_version}-intermediary.tiny") } task buildYarnTiny(dependsOn: "mergeJars",type: FileOutput) { inputs.dir mappingsDir if (!libs.exists()) { libs.mkdirs() } def yarnTiny = new File(tempDir, "yarn-mappings.tiny") fileOutput = yarnTiny outputs.upToDateWhen {return false} doLast { logger.lifecycle(":generating tiny mappings") String[] args = [ mergedFile.getAbsolutePath(), mappingsDir.getAbsolutePath(), yarnTiny.getAbsolutePath(), "official", "named" ] new CommandTinyify().run(args) } } task mergeTiny(dependsOn: ["buildYarnTiny", "downloadIntermediary"], type: FileOutput) { def yarnTinyInput = buildYarnTiny.fileOutput def intermediaryTinyInput = downloadIntermediary.dest def outputFile = new File(tempDir, "mappings.tiny") outputs.file(outputFile) fileOutput = outputFile outputs.upToDateWhen {return false} doLast { logger.lifecycle(":merging yarn and intermediary") String[] args = [ yarnTinyInput.getAbsolutePath(), intermediaryTinyInput.getAbsolutePath(), outputFile.getAbsolutePath(), "intermediary", "official" ] new CommandMergeTiny().run(args) } } task tinyJar(type: Jar, dependsOn: "mergeTiny") { outputs.upToDateWhen {return false} archiveName = "yarn-${yarnVersion}.jar" destinationDir(file("build/libs")) classifier = "" from (mergeTiny.fileOutput) { rename { "mappings/mappings.tiny" } } } task compressTiny(dependsOn: ["tinyJar", "mergeTiny"], type: FileOutput){ def outputFile = new File(libs, "yarn-tiny-${yarnVersion}.gz") outputs.file(outputFile) fileOutput = outputFile def inputFile = mergeTiny.fileOutput outputs.upToDateWhen {return false} doLast { logger.lifecycle(":compressing tiny mappings") def buffer = new byte[1024] def fileOutputStream = new FileOutputStream(outputFile) def outputStream = new GZIPOutputStream(fileOutputStream) def fileInputStream = new FileInputStream(inputFile) def length while ((length = fileInputStream.read(buffer)) > 0) { outputStream.write(buffer, 0, length) } fileInputStream.close() outputStream.finish() outputStream.close() inputFile.delete() } } clean.doFirst { delete tempDir, cacheFilesMinecraft } tasks.build.dependsOn "compressTiny" tasks.build.dependsOn "buildEnigma" tasks.build.dependsOn "tinyJar" task downloadMcLibs(dependsOn: downloadWantedVersionManifest) { inputs.files versionFile outputs.file(libraries) outputs.upToDateWhen { return false } doLast { if (!versionFile.exists()) { throw new RuntimeException("Can't download the jars without the ${versionFile.name} file!") } def version = new JsonSlurper().parseText(FileUtils.readFileToString(versionFile)) logger.lifecycle(":downloading minecraft libraries") if (!libraries.exists()) { libraries.mkdirs() } version.libraries.each { def downloadUrl = it.downloads.artifact.url download { src downloadUrl dest new File(libraries, downloadUrl.substring(downloadUrl.lastIndexOf("/") + 1)) overwrite false } } } } task mapIntermediaryJar(dependsOn: [downloadMcLibs, build]) { inputs.files downloadMcLibs.outputs.files.files outputs.file(intermediaryJar) //Force the task to always run outputs.upToDateWhen { return false } doLast { logger.lifecycle(":mapping minecraft to intermdiary") def tinyInput = new File("build/libs/yarn-tiny-${yarnVersion}.gz") mapJar(intermediaryJar, mergedFile, tinyInput, libraries, "official", "intermediary") } } task mapNamedJar(dependsOn: mapIntermediaryJar) { inputs.files downloadMcLibs.outputs.files.files outputs.file(namedJar) //Force the task to always run outputs.upToDateWhen { return false } doLast { logger.lifecycle(":mapping minecraft to named") def tinyInput = new File("build/libs/yarn-tiny-${yarnVersion}.gz") mapJar(namedJar, intermediaryJar, tinyInput, libraries, "intermediary", "named") } } publishing { publications { maven(MavenPublication) { groupId 'net.fabricmc' artifactId "yarn" version yarnVersion artifact (compressTiny.fileOutput) { classifier "tiny" builtBy compressTiny } artifact (buildEnigma) { classifier "enigma" } artifact (tinyJar) } } repositories { maven { url "http://mavenupload.modmuss50.me/" if (project.hasProperty('mavenPass')) { credentials { username 'buildslave' password project.getProperty('mavenPass') } } } } } void mapJar(File output, File input, File mappings, File libraries, String from, String to){ if (output.exists()) { output.delete() } def remapper = TinyRemapper.newRemapper() .withMappings(TinyUtils.createTinyMappingProvider(mappings.toPath(), from, to)) .renameInvalidLocals(true) .rebuildSourceFilenames(true) .build() try { def outputConsumer = new OutputConsumerPath(output.toPath()) outputConsumer.addNonClassFiles(input.toPath()) remapper.read(input.toPath()) libraries.eachFileRecurse(FileType.FILES) {file -> remapper.read(file.toPath()) } remapper.apply(input.toPath(), outputConsumer) remapper.finish() outputConsumer.close() } catch (Exception e) { remapper.finish(); outputConsumer.close() throw new RuntimeException("Failed to remap jar", e); } } class FileOutput extends DefaultTask { @OutputFile File fileOutput }