import com.replaymod.gradle.preprocess.PreprocessTask buildscript { def mcVersion def (major, minor, patch) = project.name.tokenize('-')[0].tokenize('.') mcVersion = "${major}${minor.padLeft(2, '0')}${(patch ?: '').padLeft(2, '0')}" as int def fabric = mcVersion >= 11400 && !project.name.endsWith("-forge") project.ext.mcVersion = mcVersion project.ext.fabric = fabric repositories { mavenLocal() jcenter() mavenCentral() maven { name = "fabric" url = "https://maven.fabricmc.net/" } if (!fabric) { maven { name = "forge" url = "https://files.minecraftforge.net/maven" } } maven { name = "sonatype" url = "https://oss.sonatype.org/content/repositories/snapshots/" } maven { url 'https://jitpack.io' } } dependencies { classpath 'com.github.jengelman.gradle.plugins:shadow:4.0.2' if (fabric) { classpath 'fabric-loom:fabric-loom.gradle.plugin:0.2.2-SNAPSHOT' } else if (mcVersion >= 11400) { classpath('net.minecraftforge.gradle:ForgeGradle:3.+'){ exclude group: 'trove', module: 'trove' // preprocessor/idea requires more recent one } } else if (mcVersion >= 10800) { classpath('com.github.ReplayMod:ForgeGradle:' + ( mcVersion >= 11200 ? 'b64de9c' : // FG 2.3 mcVersion >= 10904 ? '5bb7a53' : // FG 2.2 'fc1eabc' // FG 2.1 ) + ':all') } else { classpath 'com.github.ReplayMod:ForgeGradle:e93f77b4' // FG 1.2 } } } def FG3 = !fabric && mcVersion >= 11400 def FABRIC = fabric def jGuiVersion = project.name if (['1.10.2', '1.11', '1.11.2'].contains(jGuiVersion)) jGuiVersion = '1.9.4' if (['1.12.1', '1.12.2'].contains(jGuiVersion)) jGuiVersion = '1.12' apply plugin: 'com.github.johnrengelman.shadow' if (mcVersion >= 10800) { if (FABRIC) { apply plugin: 'fabric-loom' } else if (FG3) { apply plugin: 'net.minecraftforge.gradle' } else { apply plugin: 'net.minecraftforge.gradle.forge' } } else { apply plugin: 'forge' } if (!FABRIC) { ext { mixinSrg = new File(project.buildDir, 'tmp/mixins/mixins.srg') mixinRefMap = new File(project.buildDir, 'tmp/mixins/mixins.replaymod.refmap.json') } compileJava { options.compilerArgs += [ "-AoutSrgFile=${project.mixinSrg.canonicalPath}", "-AoutRefMapFile=${project.mixinRefMap.canonicalPath}", "-AreobfSrgFile=${project.file('build/mcp-srg.srg').canonicalPath}" ] } } apply plugin: 'com.replaymod.preprocess' preprocess { vars.put("MC", project.mcVersion) vars.put("FABRIC", project.fabric ? 1 : 0) keywords.set([ ".java": PreprocessTask.DEFAULT_KEYWORDS, ".json": PreprocessTask.DEFAULT_KEYWORDS, ".mcmeta": PreprocessTask.DEFAULT_KEYWORDS, ".cfg": PreprocessTask.CFG_KEYWORDS, ".vert": PreprocessTask.DEFAULT_KEYWORDS, ".frag": PreprocessTask.DEFAULT_KEYWORDS, ]) } def mcVersionStr = "${(int)(mcVersion/10000)}.${(int)(mcVersion/100)%100}" + (mcVersion%100==0 ? '' : ".${mcVersion%100}") sourceCompatibility = 1.8 targetCompatibility = 1.8 if (mcVersion >= 11400) { sourceSets { api } } version = mcVersionStr + '-' + rootProject.version group= "com.replaymod" archivesBaseName = "replaymod" if (FABRIC) { minecraft { refmapName = 'mixins.replaymod.refmap.json' } } else { minecraft { if (FG3) { runs { client { workingDirectory rootProject.file('eclipse') property 'forge.logging.console.level', 'info' mods { replaymod { source sourceSets.main } } } } } else { if (mcVersion >= 10800) { coreMod = 'com.replaymod.core.LoadingPlugin' } runDir = "../../eclipse" } if (!FG3) { version = [ 11202: '1.12.2-14.23.0.2486', 11201: '1.12.1-14.22.0.2444', 11200: '1.12-14.21.1.2387', 11102: '1.11.2-13.20.0.2216', 11100: '1.11-13.19.1.2188', 11002: '1.10.2-12.18.2.2099', 10904: '1.9.4-12.17.0.1976', 10809: '1.8.9-11.15.1.1722', 10800: '1.8-11.14.4.1563', 10710: '1.7.10-10.13.4.1558-1.7.10', ][mcVersion] } mappings = [ 11404: "snapshot_20190719-1.14.3", 11202: "snapshot_20170615", 11201: "snapshot_20170615", 11200: "snapshot_20170615", 11102: "snapshot_20161220", 11100: "snapshot_20161111", 11002: "snapshot_20160518", 10904: "snapshot_20160518", 10809: "stable_22", 10800: "snapshot_nodoc_20141130", 10710: "stable_12", ][mcVersion] } } afterEvaluate { if (mcVersion >= 11400) { // No longer required in 1.13+ because all version info is in the toml file } else { // Note cannot use minecraft.replace because that has already been forwarded to the task by FG by now tasks.sourceMainJava.replace '@MOD_VERSION@', project.version // Includes intentional whitespace to stop Forge from declaring the mod to be compatible with // a newer srg-compatible MC version (that may be using a different protocol version) tasks.sourceMainJava.replace '@MC_VERSION@', "[ $mcVersionStr ]" } } repositories { mavenLocal() maven { name = "SpongePowered Repo" url = "http://repo.spongepowered.org/maven/" } maven { name = "fabric" url = "https://maven.fabricmc.net/" } maven { url 'https://jitpack.io' } } configurations { // Include dep in fat jar without relocation and, when forge supports it, without exploding (TODO) shade compile.extendsFrom shade // Include dep in fat jar with relocation and minimization shadow compile.extendsFrom shadow // Like shadow but run the dep through reobfJar (jGui needs this because it references MC) shadowReobf compile.extendsFrom shadowReobf // Combination of shadow and shadowReobf relocate { extendsFrom shadow extendsFrom shadowReobf } } def shadeExclusions = { // Cannot just add these to the shade configuration because they'd be inherited by the compile configuration then exclude group: 'com.google.guava', module: 'guava-jdk5' exclude group: 'com.google.guava', module: 'guava' // provided by MC exclude group: 'com.google.code.gson', module: 'gson' // provided by MC exclude group: 'org.projectlombok', module: 'lombok' // runtime only for @SneakyThrows which isn't used } dependencies { if (FABRIC) { minecraft 'com.mojang:minecraft:' + [ 11404: '1.14.4', 11502: '1.15.2', ][mcVersion] mappings 'net.fabricmc:yarn:' + [ 11404: '1.14.4+build.16', 11502: '1.15.2+build.14', ][mcVersion] modCompile 'net.fabricmc:fabric-loader:' + [ 11404: '0.4.8+build.155', 11502: '0.7.8+build.189', ][mcVersion] modCompile 'net.fabricmc.fabric-api:fabric-api:' + [ 11404: '0.3.0+build.200', 11502: '0.5.1+build.294-1.15', ][mcVersion] } if (FG3) { minecraft 'net.minecraftforge:forge:' + [ 11404: '1.14.4-28.1.113', ][mcVersion] } if (!FABRIC) { // compile 'com.github.SpongePowered:Mixin:404f5da' // 0.7.5-SNAPSHOT // ^ mixin doesn't compile on jitpack, so we'll have to depend on the SNAPSHOT and build it manually for reprod annotationProcessor 'org.spongepowered:mixin:0.7.10-SNAPSHOT' compileOnly 'org.spongepowered:mixin:0.7.10-SNAPSHOT' shade('org.spongepowered:mixin:0.7.10-SNAPSHOT') { transitive = false // deps should all be bundled with MC } } shadow 'com.googlecode.mp4parser:isoparser:1.1.7' shadow 'org.apache.commons:commons-exec:1.3' shadow 'com.google.apis:google-api-services-youtube:v3-rev178-1.22.0', shadeExclusions shadow 'com.google.api-client:google-api-client-gson:1.20.0', shadeExclusions shadow 'com.google.api-client:google-api-client-java6:1.20.0', shadeExclusions shadow 'com.google.oauth-client:google-oauth-client-jetty:1.20.0' if (FABRIC) { shadow 'org.apache.maven:maven-artifact:3.6.1' } shadow 'org.aspectj:aspectjrt:1.8.2' shadow 'com.github.ReplayMod.JavaBlend:2.79.0:a0696f8' shadow "com.github.ReplayMod:ReplayStudio:93cc258", shadeExclusions project.evaluationDependsOn(":jGui:$jGuiVersion") shadowReobf(project(":jGui:$jGuiVersion")){ transitive = false // FG 1.2 puts all MC deps into the compile configuration and we don't want to shade those exclude group: 'org.projectlombok', module: 'lombok' // runtime only for @SneakyThrows which isn't used } project(":jGui:$jGuiVersion").afterEvaluate { shadow files(project(":jGui:$jGuiVersion").tasks.preshadowJar.archivePath) // include pre-shadowed lwjgl_util } if (FABRIC) { modCompile 'io.github.prospector.modmenu:ModMenu:1.6.2-92' } annotationProcessor 'org.projectlombok:lombok:1.16.6' compile 'org.projectlombok:lombok:1.16.6' // runtime only for @SneakyThrows which isn't used testCompile 'junit:junit:4.11' } if (mcVersion <= 10710) { // FG 1.2 adds all MC deps to the compile configuration which we don't want afterEvaluate { // Remove them from the compile and runtime configurations configurations.compile.extendsFrom -= [configurations.minecraft, configurations.minecraftDeps] configurations.runtime.extendsFrom -= [configurations.forgeGradleStartClass] // And add them to the source sets instead sourceSets.main.with { compileClasspath += configurations.minecraft + configurations.minecraftDeps runtimeClasspath += configurations.minecraft + configurations.minecraftDeps + configurations.forgeGradleStartClass } // Also need to reconfigure the reobf task, so it can properly re-obfuscates inherited members tasks.reobf.obfOutput.all { artifact -> artifact.getFile() // force resolve artifact.classpath += configurations.minecraft + configurations.minecraftDeps } } } if (FABRIC) { tasks.remapJar { afterEvaluate { // FIXME why does loom overwrite this if we set it immediately? archiveClassifier.set('obf') } } } File configureRelocationOutput = new File(project.buildDir, 'configureRelocation') task configureRelocation() { dependsOn tasks.jar dependsOn configurations.relocate outputs.file(configureRelocationOutput) doLast { def pkgs = files(configurations.relocate).filter { it.exists() }.collect { def tree = it.isDirectory() ? fileTree(it) : zipTree(it) def pkgs = [].toSet() tree.visit { file -> if (!file.directory && file.name.endsWith('.class') && file.path.contains('/')) { def pkg = file.path.substring(0, file.path.lastIndexOf('/')) + '/' if (pkg.startsWith('com/')) { if (pkg.startsWith('com/google/')) { if (!pkg.startsWith('com/google/common')) { pkgs << pkg.substring(0, pkg.indexOf('/', 'com/google/'.length())) } } else if (!pkg.startsWith('com/replaymod')) { pkgs << pkg.substring(0, pkg.indexOf('/', 4)) } } else if (pkg.startsWith('net/')) { if (!pkg.startsWith('net/minecraft') && !pkg.startsWith('net/fabric')) { pkgs << pkg.substring(0, pkg.indexOf('/', 'net/'.length())) } } else if (pkg.startsWith('org/')) { if (pkg.startsWith('org/apache/')) { if (pkg.startsWith('org/apache/commons/')) { if (!pkg.startsWith('org/apache/commons/io')) { pkgs << pkg.substring(0, pkg.indexOf('/', 'org/apache/commons/'.length())) } } else if (!pkg.startsWith('org/apache/logging')) { pkgs << pkg.substring(0, pkg.indexOf('/', 'org/apache/'.length())) } } else if (!pkg.startsWith('org/spongepowered')) { pkgs << pkg.substring(0, pkg.indexOf('/', 4)) } } else if (!pkg.startsWith('javax/')) { // Note: we cannot just use top level packages as those will be too generic and we'll run // into this long standing bug: https://github.com/johnrengelman/shadow/issues/232 def i = pkg.indexOf('/') def i2 = pkg.indexOf('/', i + 1) if (i2 > 0) { pkgs << pkg.substring(0, i2) } } } } pkgs }.flatten().unique() configureRelocationOutput.write(pkgs.join('\n')) } } tasks.removeByName('shadowJar') // we want to base our shadowed jar on the reobfJar output, not the sourceSet output task shadowJar(type: com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { from({ zipTree((FABRIC ? tasks.remapJar : tasks.jar).archiveFile.get()) }) { filesMatching('mixins.jgui.json') { filter { it.replace('de.johni0702', 'com.replaymod.lib.de.johni0702') } } filesMatching('mixins.jgui.refmap.json') { filter { it.replace('de/johni0702', 'com/replaymod/lib/de/johni0702') } } } dependsOn { FABRIC ? tasks.remapJar : (mcVersion >= 10800 ? tasks.reobfJar : tasks.reobf) } manifest.inheritFrom tasks.jar.manifest from project.configurations.shade configurations = [project.configurations.shadow] exclude 'META-INF/INDEX.LIST', 'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA', 'module-info.class' dependsOn tasks.configureRelocation inputs.file(configureRelocationOutput) doFirst { configureRelocationOutput.readLines().each { pkg -> def pkgName = pkg.replace('/', '.') relocate pkgName, 'com.replaymod.lib.' + pkgName } } // No need to shadow netty, MC provides it relocate 'com.github.steveice10.netty', 'io.netty' exclude 'com/github/steveice10/netty/**' minimize { exclude(dependency('.*spongepowered:mixin:.*')) } } tasks.assemble.dependsOn tasks.shadowJar jar { classifier = 'raw' from { files(configurations.shadowReobf).collect { it.isDirectory() ? it : zipTree(it) } } if (!FABRIC) { from files(project.mixinRefMap.canonicalPath) manifest { attributes 'TweakClass': 'org.spongepowered.asm.launch.MixinTweaker', 'TweakOrder': '0', 'FMLCorePluginContainsFMLMod': 'true', 'FMLCorePlugin': 'com.replaymod.core.LoadingPlugin', 'FMLAT': 'replaymod_at.cfg' } } } processResources { // this will ensure that this task is redone when the versions change. inputs.property 'version', { project.version } inputs.property 'mcversion', { mcVersionStr } // replace stuff in mcmod.info (pre 1.13) and mods.toml (1.13+) and fabric.mod.json, nothing else from(sourceSets.main.resources.srcDirs) { spec -> include 'mcmod.info' include 'mods.toml' include 'fabric.mod.json' // replace version and mcversion afterEvaluate { spec.expand 'version': project.version, 'mcversion': mcVersionStr } } // strip comments from (strict) JSON files from(sourceSets.main.resources.srcDirs) { include 'pack.mcmeta' filter { line -> line.trim().startsWith('//') ? '' : line} } // copy everything else, thats not the mcmod.info (or mods.toml or fabric.mod.json) from(sourceSets.main.resources.srcDirs) { exclude 'mcmod.info' exclude 'mods.toml' exclude 'fabric.mod.json' exclude 'pack.mcmeta' } } sourceSets { integrationTest { compileClasspath += main.runtimeClasspath + main.output java { srcDir file('src/integration-test/java') } resources.srcDir file('src/integration-test/resources') } } if (FABRIC) { // not required, fabric manages those by itself just fine } else if (FG3) { task copySrg(dependsOn: 'createMcpToSrg') { doLast { def tsrg = file(project.tasks.createMcpToSrg.output).readLines() def srg = [] def cls = '' for (def line : tsrg) { if (line[0] != '\t') { srg.add('CL: ' + line) cls = line.split(' ')[0] } else { def parts = line.substring(1).split(' ') if (line.contains('(')) { srg.add('MD: ' + cls + '/' + parts[0] + ' ' + parts[1] + ' ' + cls + '/' + parts[2] + ' ' + parts[1]) } else { srg.add('FD: ' + cls + '/' + parts[0] + ' ' + cls + '/' + parts[1]) } } } new File(project.buildDir, 'mcp-srg.srg').write(srg.join('\n')) } } compileJava.dependsOn copySrg } else { task copySrg(type: Copy, dependsOn: 'genSrgs') { from {project.tasks.genSrgs.mcpToSrg} into 'build' } compileJava.dependsOn copySrg } if (!FABRIC && !FG3) { if (mcVersion <= 10710) { reobf.addExtraSrgFile project.mixinSrg } else { reobfJar.addSecondarySrgFile project.mixinSrg } } /* FIXME // Mixin uses multiple HashMaps to generate the refmap. // HashMaps are unordered collections and as such do not produce deterministic output. // To fix that, we simply sort the refmap json file. import groovy.json.JsonSlurper import groovy.json.JsonOutput compileJava.doLast { File refmapFile = mcVersion >= 10800 ? compileJava.ext.refMapFile : project.mixinRefMap if (refmapFile.exists()) { def ordered ordered = { if (it instanceof Map) { def sorted = new TreeMap(it) sorted.replaceAll { k, v -> ordered(v) } sorted } else if (it instanceof List) { it.replaceAll { v -> ordered(v) } } else { it } } def json = JsonOutput.toJson(ordered(new JsonSlurper().parse(refmapFile))) refmapFile.withWriter { it.write json } } } */ if (!FG3 && !FABRIC) { // FIXME task runIntegrationTest(type: JavaExec, dependsOn: ["makeStart", "jar"]) { main = 'GradleStart' standardOutput = System.out errorOutput = System.err workingDir file(minecraft.runDir) def testDir = new File(minecraft.runDir, "integration-test") doFirst { testDir.deleteDir() testDir.mkdirs() } doLast { testDir.deleteDir() } afterEvaluate { def runClient = tasks.getByName("runClient") runIntegrationTest.jvmArgs = runClient.jvmArgs + "-Dfml.noGrab=true" runIntegrationTest.args = runClient.args + "--gameDir" + testDir.canonicalPath runIntegrationTest.classpath runClient.classpath + sourceSets.integrationTest.output } } } defaultTasks 'build'