diff --git a/build.gradle b/build.gradle index e6ddebb5..a99d4771 100644 --- a/build.gradle +++ b/build.gradle @@ -1,310 +1,1535 @@ +//version: 1720840170 +/* + * DO NOT CHANGE THIS FILE! + * Also, you may replace this file at any time if there is an update available. + * Please check https://github.com/GregTechCEu/Buildscripts/blob/master/build.gradle for updates. + * You can also run ./gradlew updateBuildScript to update your buildscript. + */ + +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar +import com.gtnewhorizons.retrofuturagradle.MinecraftExtension +import com.gtnewhorizons.retrofuturagradle.mcp.MCPTasks +import com.gtnewhorizons.retrofuturagradle.minecraft.MinecraftTasks +import com.gtnewhorizons.retrofuturagradle.mcp.ReobfuscatedJar +import com.gtnewhorizons.retrofuturagradle.minecraft.RunMinecraftTask +import com.gtnewhorizons.retrofuturagradle.util.Distribution +import com.modrinth.minotaur.dependencies.ModDependency +import com.modrinth.minotaur.dependencies.VersionDependency +import de.undercouch.gradle.tasks.download.DownloadExtension +import org.apache.commons.io.FileUtils +import org.gradle.api.internal.artifacts.configurations.DefaultUnlockedConfiguration +import org.gradle.api.tasks.testing.logging.TestExceptionFormat +import org.gradle.api.tasks.testing.logging.TestLogEvent +import org.gradle.internal.logging.text.StyledTextOutputFactory import org.jetbrains.gradle.ext.Gradle +import javax.inject.Inject + +import static org.gradle.internal.logging.text.StyledTextOutput.Style + plugins { id 'java' id 'java-library' - id 'maven-publish' - id 'org.jetbrains.gradle.plugin.idea-ext' version '1.1.7' + id 'base' id 'eclipse' - id 'com.gtnewhorizons.retrofuturagradle' version '1.3.33' - id 'com.matthewprenger.cursegradle' version '1.4.0' + id 'maven-publish' + id 'org.jetbrains.gradle.plugin.idea-ext' version '1.1.8' + id 'com.gtnewhorizons.retrofuturagradle' version '1.4.0' + id 'net.darkhax.curseforgegradle' version '1.1.24' apply false + id 'com.modrinth.minotaur' version '2.8.7' apply false + id 'com.diffplug.spotless' version '6.13.0' apply false + id 'com.palantir.git-version' version '3.0.0' apply false + id 'com.github.johnrengelman.shadow' version '8.1.1' apply false + id 'org.jetbrains.kotlin.jvm' version '1.8.0' apply false + id 'org.jetbrains.kotlin.kapt' version '1.8.0' apply false + id 'com.google.devtools.ksp' version '1.8.0-1.0.9' apply false + id 'de.undercouch.download' version '5.6.0' apply false +} + +def out = services.get(StyledTextOutputFactory).create('an-output') + + +// Project properties + +// Required properties: we don't know how to handle these being missing gracefully +checkPropertyExists("modName") +checkPropertyExists("modId") +checkPropertyExists("modGroup") +checkPropertyExists("minecraftVersion") // hard-coding this makes it harder to immediately tell what version a mod is in (even though this only really supports 1.12.2) +checkPropertyExists("apiPackage") +checkPropertyExists("accessTransformersFile") +checkPropertyExists("usesMixins") +checkPropertyExists("mixinsPackage") +checkPropertyExists("coreModClass") +checkPropertyExists("containsMixinsAndOrCoreModOnly") + +// Optional properties: we can assume some default behavior if these are missing +propertyDefaultIfUnset("modVersion", "") +propertyDefaultIfUnset("includeMCVersionJar", false) +propertyDefaultIfUnset("autoUpdateBuildScript", false) +propertyDefaultIfUnset("modArchivesBaseName", project.modId) +propertyDefaultIfUnsetWithEnvVar("developmentEnvironmentUserName", "Developer", "DEV_USERNAME") +propertyDefaultIfUnset("additionalJavaArguments", "") +propertyDefaultIfUnset("enableJava17RunTasks", false) +propertyDefaultIfUnset("generateGradleTokenClass", "") +propertyDefaultIfUnset("gradleTokenModId", "") +propertyDefaultIfUnset("gradleTokenModName", "") +propertyDefaultIfUnset("gradleTokenVersion", "") +propertyDefaultIfUnset("useSrcApiPath", false) +propertyDefaultIfUnset("includeWellKnownRepositories", true) +propertyDefaultIfUnset("includeCommonDevEnvMods", true) +propertyDefaultIfUnset("stripForgeRequirements", false) +propertyDefaultIfUnset("noPublishedSources", false) +propertyDefaultIfUnset("forceEnableMixins", false) +propertyDefaultIfUnset("mixinConfigRefmap", "mixins.${project.modId}.refmap.json") +propertyDefaultIfUnsetWithEnvVar("enableCoreModDebug", false, "CORE_MOD_DEBUG") +propertyDefaultIfUnset("generateMixinConfig", true) +propertyDefaultIfUnset("usesShadowedDependencies", false) +propertyDefaultIfUnset("minimizeShadowedDependencies", true) +propertyDefaultIfUnset("relocateShadowedDependencies", true) +propertyDefaultIfUnset("separateRunDirectories", false) +propertyDefaultIfUnset("versionDisplayFormat", '$MOD_NAME \u2212 $VERSION') +propertyDefaultIfUnsetWithEnvVar("modrinthProjectId", "", "MODRINTH_PROJECT_ID") +propertyDefaultIfUnset("modrinthRelations", "") +propertyDefaultIfUnsetWithEnvVar("curseForgeProjectId", "", "CURSEFORGE_PROJECT_ID") +propertyDefaultIfUnset("curseForgeRelations", "") +propertyDefaultIfUnsetWithEnvVar("releaseType", "release", "RELEASE_TYPE") +propertyDefaultIfUnset("generateDefaultChangelog", false) +propertyDefaultIfUnset("customMavenPublishUrl", "") +propertyDefaultIfUnset("mavenArtifactGroup", getDefaultArtifactGroup()) +propertyDefaultIfUnset("enableModernJavaSyntax", false) +propertyDefaultIfUnset("enableSpotless", false) +propertyDefaultIfUnset("enableJUnit", false) +propertyDefaultIfUnsetWithEnvVar("deploymentDebug", false, "DEPLOYMENT_DEBUG") + + +// Project property assertions + +final String javaSourceDir = 'src/main/java/' +final String scalaSourceDir = 'src/main/scala/' +final String kotlinSourceDir = 'src/main/kotlin/' + +final String modGroupPath = modGroup.toString().replace('.' as char, '/' as char) +final String apiPackagePath = apiPackage.toString().replace('.' as char, '/' as char) + +String targetPackageJava = javaSourceDir + modGroupPath +String targetPackageScala = scalaSourceDir + modGroupPath +String targetPackageKotlin = kotlinSourceDir + modGroupPath + +if (!getFile(targetPackageJava).exists() && !getFile(targetPackageScala).exists() && !getFile(targetPackageKotlin).exists()) { + throw new GradleException("Could not resolve \"modGroup\"! Could not find ${targetPackageJava} or ${targetPackageScala} or ${targetPackageKotlin}") +} + +if (apiPackage) { + final String endApiPath = modGroupPath + '/' + apiPackagePath + if (useSrcApiPath.toBoolean()) { + targetPackageJava = 'src/api/java/' + endApiPath + targetPackageScala = 'src/api/scala/' + endApiPath + targetPackageKotlin = 'src/api/kotlin/' + endApiPath + } else { + targetPackageJava = javaSourceDir + endApiPath + targetPackageScala = scalaSourceDir + endApiPath + targetPackageKotlin = kotlinSourceDir + endApiPath + } + if (!getFile(targetPackageJava).exists() && !getFile(targetPackageScala).exists() && !getFile(targetPackageKotlin).exists()) { + throw new GradleException("Could not resolve \"apiPackage\"! Could not find ${targetPackageJava} or ${targetPackageScala} or ${targetPackageKotlin}") + } +} + +if (accessTransformersFile) { + for (atFile in accessTransformersFile.split(",")) { + String targetFile = 'src/main/resources/' + atFile.trim() + if (!getFile(targetFile).exists()) { + throw new GradleException("Could not resolve \"accessTransformersFile\"! Could not find " + targetFile) + } + tasks.deobfuscateMergedJarToSrg.accessTransformerFiles.from(targetFile) + tasks.srgifyBinpatchedJar.accessTransformerFiles.from(targetFile) + } +} + +if (usesMixins.toBoolean()) { + if (mixinsPackage.isEmpty()) { + throw new GradleException("\"usesMixins\" requires \"mixinsPackage\" to be set!") + } + final String mixinPackagePath = mixinsPackage.toString().replaceAll('\\.', '/') + targetPackageJava = javaSourceDir + modGroupPath + '/' + mixinPackagePath + targetPackageScala = scalaSourceDir + modGroupPath + '/' + mixinPackagePath + targetPackageKotlin = kotlinSourceDir + modGroupPath + '/' + mixinPackagePath + if (!getFile(targetPackageJava).exists() && !getFile(targetPackageScala).exists() && !getFile(targetPackageKotlin).exists()) { + throw new GradleException("Could not resolve \"mixinsPackage\"! Could not find ${targetPackageJava} or ${targetPackageScala} or ${targetPackageKotlin}") + } +} + +if (coreModClass) { + final String coreModPath = coreModClass.toString().replaceAll('\\.', '/') + String targetFileJava = javaSourceDir + modGroupPath + '/' + coreModPath + '.java' + String targetFileScala = scalaSourceDir + modGroupPath + '/' + coreModPath + '.scala' + String targetFileScalaJava = scalaSourceDir + modGroupPath + '/' + coreModPath + '.java' + String targetFileKotlin = kotlinSourceDir + modGroupPath + '/' + coreModPath + '.kt' + if (!getFile(targetFileJava).exists() && !getFile(targetFileScala).exists() && !getFile(targetFileScalaJava).exists() && !getFile(targetFileKotlin).exists()) { + throw new GradleException("Could not resolve \"coreModClass\"! Could not find ${targetFileJava} or ${targetFileScala} or ${targetFileScalaJava} or ${targetFileKotlin}") + } +} + + +// Plugin application + +// Scala +if (getFile('src/main/scala').exists()) { + apply plugin: 'scala' +} + +if (getFile('src/main/kotlin').exists()) { + apply plugin: 'org.jetbrains.kotlin.jvm' +} + +// Kotlin +pluginManager.withPlugin('org.jetbrains.kotlin.jvm') { + kotlin { + jvmToolchain(8) + } + def disabledKotlinTaskList = [ + "kaptGenerateStubsMcLauncherKotlin", + "kaptGenerateStubsPatchedMcKotlin", + "kaptGenerateStubsInjectedTagsKotlin", + "compileMcLauncherKotlin", + "compilePatchedMcKotlin", + "compileInjectedTagsKotlin", + "kaptMcLauncherKotlin", + "kaptPatchedMcKotlin", + "kaptInjectedTagsKotlin", + "kspMcLauncherKotlin", + "kspPatchedMcKotlin", + "kspInjectedTagsKotlin", + ] + tasks.configureEach { task -> + if (task.name in disabledKotlinTaskList) { + task.enabled = false + } + } +} + +// Spotless +//noinspection GroovyAssignabilityCheck +project.extensions.add(com.diffplug.blowdryer.Blowdryer, 'Blowdryer', com.diffplug.blowdryer.Blowdryer) // make Blowdryer available in plugin application +if (enableSpotless.toBoolean()) { + apply plugin: 'com.diffplug.spotless' + + // Spotless auto-formatter + // See https://github.com/diffplug/spotless/tree/main/plugin-gradle + // Can be locally toggled via spotless:off/spotless:on comments + spotless { + encoding 'UTF-8' + + format 'misc', { + target '.gitignore' + + trimTrailingWhitespace() + indentWithSpaces(4) + endWithNewline() + } + java { + target 'src/main/java/**/*.java', 'src/test/java/**/*.java' // exclude api as they are not our files + + def orderFile = project.file('spotless.importorder') + if (!orderFile.exists()) { + orderFile = Blowdryer.file('spotless.importorder') + } + def formatFile = project.file('spotless.eclipseformat.xml') + if (!formatFile.exists()) { + formatFile = Blowdryer.file('spotless.eclipseformat.xml') + } + + toggleOffOn() + importOrderFile(orderFile) + removeUnusedImports() + endWithNewline() + //noinspection GroovyAssignabilityCheck + eclipse('4.19.0').configFile(formatFile) + } + kotlin { + target 'src/*/kotlin/**/*.kt' + + toggleOffOn() + ktfmt('0.39') + + trimTrailingWhitespace() + indentWithSpaces(4) + endWithNewline() + } + scala { + target 'src/*/scala/**/*.scala' + scalafmt('3.7.1') + } + } +} + +// Git version checking, also checking for if this is a submodule +if (project.file('.git/HEAD').isFile() || project.file('.git').isFile()) { + apply plugin: 'com.palantir.git-version' +} + +// Shadowing +if (usesShadowedDependencies.toBoolean()) { + apply plugin: 'com.github.johnrengelman.shadow' } -version = project.mod_version -group = project.maven_group -archivesBaseName = project.archives_base_name -// Set the toolchain version to decouple the Java we run Gradle with from the Java used to compile and run the mod +// Configure Java + java { toolchain { - languageVersion.set(JavaLanguageVersion.of(8)) - // Azul covers the most platforms for Java 8 toolchains, crucially including MacOS arm64 - vendor.set(org.gradle.jvm.toolchain.JvmVendorSpec.AZUL) + if (enableModernJavaSyntax.toBoolean()) { + languageVersion.set(JavaLanguageVersion.of(17)) + } else { + languageVersion.set(JavaLanguageVersion.of(8)) + } + // Azul covers the most platforms for Java 8+ toolchains, crucially including MacOS arm64 + vendor.set(JvmVendorSpec.AZUL) + } + if (!noPublishedSources.toBoolean()) { + withSourcesJar() } - // Generate sources and javadocs jars when building and publishing - withSourcesJar() - // withJavadocJar() } tasks.withType(JavaCompile).configureEach { options.encoding = 'UTF-8' + + if (enableModernJavaSyntax.toBoolean()) { + if (it.name in ['compileMcLauncherJava', 'compilePatchedMcJava']) { + return + } + + sourceCompatibility = 17 + options.release.set(8) + + javaCompiler.set(javaToolchains.compilerFor { + languageVersion.set(JavaLanguageVersion.of(17)) + vendor.set(JvmVendorSpec.AZUL) + }) + } } -configurations { - embed - implementation.extendsFrom(embed) +tasks.withType(ScalaCompile).configureEach { + options.encoding = 'UTF-8' +} + + +// Allow others using this buildscript to have custom gradle code run +if (getFile('addon.gradle').exists()) { + apply from: 'addon.gradle' +} else if (getFile('addon.gradle.kts').exists()) { + apply from: 'addon.gradle.kts' +} + + +// Configure Minecraft + +// Try to gather mod version from git tags if version is not manually specified +if (!modVersion) { + try { + modVersion = gitVersion() + } catch (Exception ignored) { + out.style(Style.Failure).text( + "Mod version could not be determined! Property 'modVersion' is not set, and either git is not installed or no git tags exist.\n" + + "Either specify a mod version in 'gradle.properties', or create at least one tag in git for this project." + ) + modVersion = 'NO-GIT-TAG-SET' + } +} + +if (includeMCVersionJar.toBoolean()){ + version = "${minecraftVersion}-${modVersion}" +} +else { + version = modVersion +} + +group = modGroup + +base { + archivesName = modArchivesBaseName } minecraft { - mcVersion = '1.12.2' + mcVersion = minecraftVersion + username = developmentEnvironmentUserName.toString() + useDependencyAccessTransformers = true - // MCP Mappings - mcpMappingChannel = 'stable' - mcpMappingVersion = '39' + // Automatic token injection with RetroFuturaGradle + if (gradleTokenModId) { + injectedTags.put gradleTokenModId, modId + } + if (gradleTokenModName) { + injectedTags.put gradleTokenModName, modName + } + if (gradleTokenVersion) { + injectedTags.put gradleTokenVersion, modVersion + } - // Set username here, the UUID will be looked up automatically - username = 'Developer' + // JVM arguments + extraRunJvmArguments.add("-ea:${modGroup}") + if (usesMixins.toBoolean()) { + extraRunJvmArguments.addAll([ + '-Dmixin.hotSwap=true', + '-Dmixin.checks.interfaces=true', + '-Dmixin.debug.export=true' + ]) + } - // Add any additional tweaker classes here - // extraTweakClasses.add('org.spongepowered.asm.launch.MixinTweaker') + if (enableCoreModDebug.toBoolean()) { + extraRunJvmArguments.addAll([ + '-Dlegacy.debugClassLoading=true', + '-Dlegacy.debugClassLoadingFiner=true', + '-Dlegacy.debugClassLoadingSave=true' + ]) + } - // Add various JVM arguments here for runtime - def args = ["-ea:${project.group}"] - if (project.use_coremod.toBoolean()) { - args << '-Dfml.coreMods.load=' + coremod_plugin_class_name + if (additionalJavaArguments.size() != 0) { + extraRunJvmArguments.addAll(additionalJavaArguments.split(';')) } - if (project.use_mixins.toBoolean()) { - args << '-Dmixin.hotSwap=true' - args << '-Dmixin.checks.interfaces=true' - args << '-Dmixin.debug.export=true' + + if (enableJava17RunTasks.toBoolean()) { + lwjgl3Version = "3.3.2" } - extraRunJvmArguments.addAll(args) +} - // Include and use dependencies' Access Transformer files - useDependencyAccessTransformers = true +if (coreModClass) { + for (runTask in ['runClient', 'runServer']) { + tasks.named(runTask).configure { + extraJvmArgs.add("-Dfml.coreMods.load=${modGroup}.${coreModClass}") + } + } +} - // Add any properties you want to swap out for a dynamic value at build time here - // Any properties here will be added to a class at build time, the name can be configured below - injectedTags.put('MOD_ID', project.mod_id) - injectedTags.put('MOD_NAME', project.mod_name) - injectedTags.put('VERSION', project.version) +if (generateGradleTokenClass) { + tasks.injectTags.outputClassName.set(generateGradleTokenClass) } -// Generate a group.mod_id.Tags class -tasks.injectTags.configure { - // Change Tags class' name here: - outputClassName.set("${project.group}.${project.mod_id}.Tags") +tasks.named('processIdeaSettings').configure { + dependsOn('injectTags') } -repositories { - maven { - name 'CleanroomMC Maven' - url 'https://maven.cleanroommc.com' - } - maven { - name 'SpongePowered Maven' - url 'https://repo.spongepowered.org/maven' - } - maven { - name 'CurseMaven' - url 'https://cursemaven.com' - content { - includeGroup 'curse.maven' + +// Repositories + +// Allow unsafe repos but warn +repositories.configureEach { repo -> + if (repo instanceof UrlArtifactRepository) { + if (repo.getUrl() != null && repo.getUrl().getScheme() == "http" && !repo.allowInsecureProtocol) { + logger.warn("Deprecated: Allowing insecure connections for repo '${repo.name}' - add 'allowInsecureProtocol = true'") + repo.allowInsecureProtocol = true } } - maven { - name = 'Mod Maven' - url = 'https://modmaven.dev' +} + +// Allow adding custom repositories to the buildscript +if (getFile('repositories.gradle').exists()) { + apply from: 'repositories.gradle' +} else if (getFile('repositories.gradle.kts').exists()) { + apply from: 'repositories.gradle.kts' +} + +repositories { + if (includeWellKnownRepositories.toBoolean() || includeCommonDevEnvMods.toBoolean()) { + exclusiveContent { + forRepository { + //noinspection ForeignDelegate + maven { + name = 'Curse Maven' + url = 'https://www.cursemaven.com' + // url = 'https://beta.cursemaven.com' + } + } + filter { + includeGroup 'curse.maven' + } + } + exclusiveContent { + forRepository { + //noinspection ForeignDelegate + maven { + name = 'Modrinth' + url = 'https://api.modrinth.com/maven' + } + } + filter { + includeGroup 'maven.modrinth' + } + } + maven { + name 'Cleanroom Maven' + url 'https://maven.cleanroommc.com' + } + maven { + name 'BlameJared Maven' + url 'https://maven.blamejared.com' + } + maven { + name 'GTNH Maven' + url 'https://nexus.gtnewhorizons.com/repository/public/' + } + maven { + name 'GTCEu Maven' + url 'https://maven.gtceu.com' + } } - exclusiveContent { - forRepository { + if (usesMixins.toBoolean() || forceEnableMixins.toBoolean()) { + // need to add this here even if we did not above + if (!includeWellKnownRepositories.toBoolean()) { maven { - name = "Modrinth" - url = "https://api.modrinth.com/maven" + name 'Cleanroom Maven' + url 'https://maven.cleanroommc.com' } } - filter { - includeGroup "maven.modrinth" - } } mavenLocal() // Must be last for caching to work } + +// Dependencies + +// Configure dependency configurations +configurations { + embed + implementation.extendsFrom(embed) + + if (usesShadowedDependencies.toBoolean()) { + for (config in [compileClasspath, runtimeClasspath, testCompileClasspath, testRuntimeClasspath]) { + config.extendsFrom(shadowImplementation) + config.extendsFrom(shadowCompile) + } + } + + create("runtimeOnlyNonPublishable") { + description = "Runtime only dependencies that are not published alongside the jar" + canBeConsumed = false + canBeResolved = false + } + create("devOnlyNonPublishable") { + description = "Runtime and compiletime dependencies that are not published alongside the jar (compileOnly + runtimeOnlyNonPublishable)" + canBeConsumed = false + canBeResolved = false + } + + compileOnly.extendsFrom(devOnlyNonPublishable) + runtimeOnlyNonPublishable.extendsFrom(devOnlyNonPublishable) + runtimeClasspath.extendsFrom(runtimeOnlyNonPublishable) + testRuntimeClasspath.extendsFrom(runtimeOnlyNonPublishable) +} + +String mixinProviderSpec = 'zone.rong:mixinbooter:9.1' dependencies { - // Libraries - if (project.use_assetmover.toBoolean()) { - implementation 'com.cleanroommc:assetmover:2.5' - } - if (project.use_mixins.toBoolean()) { - implementation 'zone.rong:mixinbooter:9.1' - } - implementation 'com.cleanroommc:configanytime:2.0' - - implementation 'com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2' - embed 'com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2' - implementation 'com.udojava:EvalEx:2.7' - embed 'com.udojava:EvalEx:2.7' - - // Mods - compileOnly rfg.deobf('cofh:CoFHCore:1.12.2-+:universal') - compileOnly rfg.deobf('com.teamacronymcoders.base:base:1.12.2-3.14.0') - compileOnly rfg.deobf('com.teamacronymcoders:ContentTweaker:1.12.2-4.10.0') - compileOnly rfg.deobf('CraftTweaker2:CraftTweaker2-MC1120-Main:1.12-4.1.20.684') - compileOnly rfg.deobf('curse.maven:advent-of-ascension-311054:3054253') - compileOnly rfg.deobf('curse.maven:abyssalcraft-53686:3425234') - compileOnly rfg.deobf('curse.maven:astral-sorcery-241721:3044416') - compileOnly rfg.deobf('curse.maven:atomicstrykers-infernal-mobs-227875:3431758') - compileOnly rfg.deobf('curse.maven:baubles-227083:2518667') - compileOnly rfg.deobf('curse.maven:binnies-mods-223525:2916129') - compileOnly rfg.deobf('curse.maven:biomes-o-plenty-220318:2842510') - compileOnly rfg.deobf('curse.maven:blood-magic-224791:2822288') - compileOnly rfg.deobf('curse.maven:botania-225643:3330934') - compileOnly rfg.deobf('curse.maven:ceramics-250617:3158763') - compileOnly rfg.deobf('curse.maven:chameleon-230497:2450900') - compileOnly rfg.deobf('curse.maven:chickens-241941:2537643') - compileOnly rfg.deobf('curse.maven:codechickenlib-242818:2779848') - compileOnly rfg.deobf('curse.maven:collective-342584:3533131') - compileOnly rfg.deobf('curse.maven:cqrepoured-303422:3953103') - compileOnly rfg.deobf('curse.maven:elementary-staffs-346007:2995593') - compileOnly rfg.deobf('curse.maven:elenaidodge2-442962:3343308') - compileOnly rfg.deobf('curse.maven:epic-siege-mod-229449:3356157') - compileOnly rfg.deobf('curse.maven:forestry-59751:2918418') - compileOnly rfg.deobf('curse.maven:modtweaker-220954:3840577') - compileOnly rfg.deobf('curse.maven:nuclearcraft-226254:3784145') - compileOnly rfg.deobf('curse.maven:openblocks-228816:2699056') - compileOnly rfg.deobf('curse.maven:opencomputers-223008:5274236') - compileOnly rfg.deobf('curse.maven:reborn-core-237903:3330308') - compileOnly rfg.deobf('curse.maven:reskillable-286382:2815686') - compileOnly rfg.deobf('curse.maven:requious-frakto-336748:3218640') - compileOnly rfg.deobf('curse.maven:roost-277711:2702080') - compileOnly rfg.deobf('curse.maven:simpledifficulty-360779:3613814') - compileOnly rfg.deobf('curse.maven:storage-drawers-223852:2952606') - compileOnly rfg.deobf('curse.maven:tech-reborn-233564:2966851') - compileOnly rfg.deobf('curse.maven:thaumcraft-223628:2629023') - compileOnly rfg.deobf('curse.maven:the-erebus-220698:3211974') - compileOnly rfg.deobf('curse.maven:thermal-expansion-69163:2926431') - compileOnly rfg.deobf('slimeknights.mantle:Mantle:1.12-1.3.3.56') - compileOnly rfg.deobf('slimeknights:TConstruct:1.12.2-2.13.0.190') - compileOnly rfg.deobf('net.darkhax.bookshelf:Bookshelf-1.12.2:2.3.590') - compileOnly rfg.deobf('net.darkhax.gamestages:GameStages-1.12.2:2.0.120') - compileOnly rfg.deobf('net.darkhax.itemstages:ItemStages-1.12.2:2.0.51') - compileOnly rfg.deobf('net.darkhax.mobstages:MobStages-1.12.2:2.0.13') - compileOnly rfg.deobf('curse.maven:mcjtylib-233105:2745846') - compileOnly rfg.deobf('curse.maven:rftools-224641:2861573') - compileOnly rfg.deobf('curse.maven:rftools-dimensions-240950:2707390') - compileOnly rfg.deobf('curse.maven:actuallyaditions-228404:2844115') - compileOnly rfg.deobf('curse.maven:extrautilities-225561:2678374') - compileOnly rfg.deobf('curse.maven:steamworld-282607:2641200') - compileOnly 'curse.maven:applecore-224472:2969118' - compileOnly 'curse.maven:arcanearchives-311357:3057332' - compileOnly 'curse.maven:bewitchment-285439:3044569' - compileOnly 'curse.maven:chisel-235279:2915375' - compileOnly 'curse.maven:cofhworld-271384:2920434' - compileOnly 'curse.maven:compactmachines-224218:2707509' - compileOnly 'curse.maven:effortlessbuilding-302113:2847346' - compileOnly 'curse.maven:endercore-231868:2972849' - compileOnly 'curse.maven:enderio-64578:2989201' - compileOnly 'curse.maven:enderstorage-245174:2755787' - compileOnly 'curse.maven:extrautilities-225561:2678374' - compileOnly 'curse.maven:forgemultipartcbe-258426:2755790' // aka "CB Multipart" - compileOnly 'curse.maven:guideapi-228832:2645992' - compileOnly 'curse.maven:hwyla-253449:2568751' - compileOnly 'curse.maven:industrialcraft-242638:3078604' - compileOnly 'curse.maven:ironbackpacks-227049:2564573' - compileOnly 'curse.maven:mekanism-268560:2835175' - compileOnly 'curse.maven:modular-routers-250294:2954953' - compileOnly 'curse.maven:mrtjpcore-229002:2735197' - compileOnly 'curse.maven:netherchest-268888:2655413' - compileOnly 'curse.maven:netherrocks-226140:2628297' - compileOnly 'curse.maven:patchouli-306770:3162874' - compileOnly 'curse.maven:plustic-376903:4703532' - compileOnly 'curse.maven:projectredbase-228702:2745545' - compileOnly 'curse.maven:projectredworld-229049:2745551' - compileOnly 'curse.maven:quark-243121:2924091' - compileOnly 'curse.maven:railcraft-51195:3853491' - compileOnly 'curse.maven:simplyjetpacks2-251792:3294422' - compileOnly 'curse.maven:tardis-290247:2903453' - compileOnly 'curse.maven:teslacorelib-254602:2891841' - compileOnly 'curse.maven:thaumicwonders-316704:2787954' - compileOnly 'curse.maven:thefarlanders-336432:2805139' - compileOnly 'curse.maven:thermalfoundation-222880:2926428' - compileOnly 'curse.maven:thespiceoflife-220811:2571951' - compileOnly 'curse.maven:tinkerscomplement-272671:2843439' - compileOnly 'curse.maven:tinyprogressions-250850:2721018' - compileOnly 'maven.modrinth:industrial-foregoing:1.12.13-237' - // runtimeOnly 'TechReborn:TechReborn-ModCompatibility-1.12.2:1.4.0.76:universal' - // runtimeOnly 'curse.maven:openmodslib-228815:2699055' - - if (project.use_mixins.toBoolean()) { - String mixin = modUtils.enableMixins("zone.rong:mixinbooter:9.1", "universaltweaks.refmap.json") - api(mixin) { + if (usesMixins.toBoolean()) { + annotationProcessor 'org.ow2.asm:asm-debug-all:5.2' + // should use 24.1.1 but 30.0+ has a vulnerability fix + annotationProcessor 'com.google.guava:guava:30.0-jre' + // should use 2.8.6 but 2.8.9+ has a vulnerability fix + annotationProcessor 'com.google.code.gson:gson:2.8.9' + + mixinProviderSpec = modUtils.enableMixins(mixinProviderSpec, mixinConfigRefmap) + api (mixinProviderSpec) { transitive = false } - annotationProcessor 'org.ow2.asm:asm-debug-all:5.2' - annotationProcessor 'com.google.guava:guava:24.1.1-jre' - annotationProcessor 'com.google.code.gson:gson:2.8.6' - annotationProcessor(mixin) { + + annotationProcessor(mixinProviderSpec) { + transitive = false + } + } else if (forceEnableMixins.toBoolean()) { + runtimeOnlyNonPublishable(mixinProviderSpec) + } + + if (enableJUnit.toBoolean()) { + testImplementation 'org.hamcrest:hamcrest:2.2' + testImplementation 'org.junit.jupiter:junit-jupiter:5.9.2' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' + } + + if (enableModernJavaSyntax.toBoolean()) { + annotationProcessor 'com.github.bsideup.jabel:jabel-javac-plugin:1.0.1' + compileOnly('com.github.bsideup.jabel:jabel-javac-plugin:1.0.1') { transitive = false } + // workaround for https://github.com/bsideup/jabel/issues/174 + annotationProcessor 'net.java.dev.jna:jna-platform:5.13.0' + // Allow jdk.unsupported classes like sun.misc.Unsafe, workaround for JDK-8206937 and fixes Forge crashes in tests. + patchedMinecraft 'me.eigenraven.java8unsupported:java-8-unsupported-shim:1.0.0' + + // allow Jabel to work in tests + testAnnotationProcessor "com.github.bsideup.jabel:jabel-javac-plugin:1.0.1" + testCompileOnly("com.github.bsideup.jabel:jabel-javac-plugin:1.0.1") { + transitive = false // We only care about the 1 annotation class + } + testCompileOnly "me.eigenraven.java8unsupported:java-8-unsupported-shim:1.0.0" + } + + compileOnlyApi 'org.jetbrains:annotations:24.1.0' + annotationProcessor 'org.jetbrains:annotations:24.1.0' + patchedMinecraft('net.minecraft:launchwrapper:1.17.2') { + transitive = false + } + + if ((usesMixins.toBoolean() || forceEnableMixins.toBoolean()) && stripForgeRequirements.toBoolean()) { + runtimeOnlyNonPublishable 'com.cleanroommc:strip-latest-forge-requirements:1.0' + } + + if (includeCommonDevEnvMods.toBoolean()) { + if (!(modId.equals('jei'))) { + implementation 'mezz.jei:jei_1.12.2:4.16.1.302' + } + if (!(modId.equals('theoneprobe'))) { + //noinspection DependencyNotationArgument + implementation rfg.deobf('curse.maven:top-245211:2667280') // TOP 1.4.28 + } } } -// Adds Access Transformer files to tasks -if (project.use_access_transformer.toBoolean()) { - for (File at : sourceSets.getByName("main").resources.files) { - if (at.name.toLowerCase().endsWith("_at.cfg")) { - tasks.deobfuscateMergedJarToSrg.accessTransformerFiles.from(at) - tasks.srgifyBinpatchedJar.accessTransformerFiles.from(at) +pluginManager.withPlugin('org.jetbrains.kotlin.kapt') { + if (usesMixins.toBoolean()) { + dependencies { + kapt(mixinProviderSpec) } } } +configurations.configureEach { + resolutionStrategy.dependencySubstitution { + substitute module('org.scala-lang:scala-library:2.11.1') using module('org.scala-lang:scala-library:2.11.5') because('To allow mixing with Java 8 targets') + } +} + +if (getFile('dependencies.gradle').exists()) { + apply from: 'dependencies.gradle' +} else if (getFile('dependencies.gradle.kts').exists()) { + apply from: 'dependencies.gradle.kts' +} + + +// Test configuration + +// Ensure tests have access to minecraft classes +sourceSets { + test { + java { + compileClasspath += patchedMc.output + mcLauncher.output + runtimeClasspath += patchedMc.output + mcLauncher.output + } + } +} + +test { + // ensure tests are run with java8 + javaLauncher = javaToolchains.launcherFor { + languageVersion = JavaLanguageVersion.of(8) + }.get() + + testLogging { + events TestLogEvent.STARTED, TestLogEvent.PASSED, TestLogEvent.FAILED + exceptionFormat TestExceptionFormat.FULL + showExceptions true + showStackTraces true + showCauses true + showStandardStreams true + } + + if (enableJUnit.toBoolean()) { + useJUnitPlatform() + } +} + + +// Resource processing and jar building + processResources { - // This will ensure that this task is redone when the versions change - inputs.property 'version', project.version - inputs.property 'mcversion', project.minecraft.version + // this will ensure that this task is redone when the versions change. + inputs.property 'version', modVersion + inputs.property 'mcversion', minecraftVersion + // Blowdryer puts these files into the resource directory, so + // exclude them from builds (doesn't hurt to exclude even if not present) + exclude('spotless.importorder') + exclude('spotless.eclipseformat.xml') - // Replace various properties in mcmod.info and pack.mcmeta if applicable - filesMatching(['mcmod.info', 'pack.mcmeta']) { fcd -> - // Replace version and mcversion - fcd.expand('version': project.version, - 'mcversion': project.minecraft.version) + // replace stuff in mcmod.info, nothing else + filesMatching('mcmod.info') { fcd -> + fcd.expand( + 'version': modVersion, + 'mcversion': minecraftVersion, + 'modid': modId, + 'modname': modName + ) } - if (project.use_access_transformer.toBoolean()) { - rename '(.+_at.cfg)', 'META-INF/$1' // Make sure Access Transformer files are in META-INF folder + if (accessTransformersFile) { + String[] ats = accessTransformersFile.split(',') + ats.each { at -> + rename "(${at})", 'META-INF/$1' + } + } +} + +// Automatically generate a mixin json file if it does not already exist +tasks.register('generateAssets') { + group = 'GT Buildscript' + description = 'Generates a pack.mcmeta, mcmod.info, or mixins.{modid}.json if needed' + doLast { + // pack.mcmeta + def packMcmetaFile = getFile('src/main/resources/pack.mcmeta') + if (!packMcmetaFile.exists()) { + packMcmetaFile.text = """{ + "pack": { + "pack_format": 3, + "description": "${modName} Resource Pack" + } +} +""" + } + + // mcmod.info + def mcmodInfoFile = getFile('src/main/resources/mcmod.info') + if (!mcmodInfoFile.exists()) { + mcmodInfoFile.text = """[{ + "modid": "\${modid}", + "name": "\${modname}", + "description": "An example mod for Minecraft 1.12.2 with Forge", + "version": "\${version}", + "mcversion": "\${mcversion}", + "logoFile": "", + "url": "", + "authorList": [], + "credits": "", + "dependencies": [] +}] +""" + } + + // mixins.{modid}.json + if (usesMixins.toBoolean() && generateMixinConfig.toBoolean()) { + def mixinConfigFile = getFile("src/main/resources/mixins.${modId}.json") + if (!mixinConfigFile.exists()) { + + mixinConfigFile.text = """{ + "package": "${modGroup}.${mixinsPackage}", + "refmap": "${mixinConfigRefmap}", + "target": "@env(DEFAULT)", + "minVersion": "0.8", + "compatibilityLevel": "JAVA_8", + "mixins": [], + "client": [], + "server": [] +} +""" + } + } } } +tasks.named('processResources').configure { + dependsOn('generateAssets') +} + jar { manifest { - def attribute_map = [:] - if (project.use_coremod.toBoolean()) { - attribute_map['FMLCorePlugin'] = project.coremod_plugin_class_name - if (project.include_mod.toBoolean()) { - attribute_map['FMLCorePluginContainsFMLMod'] = true - attribute_map['ForceLoadAsMod'] = project.gradle.startParameter.taskNames[0] == "build" - } + attributes(getManifestAttributes()) + } + + // Add all embedded dependencies into the jar + from provider { + configurations.embed.collect { + it.isDirectory() ? it : zipTree(it) } - if (project.use_access_transformer.toBoolean()) { - attribute_map['FMLAT'] = project.mod_id + '_at.cfg' + } + + if (useSrcApiPath && apiPackage) { + from sourceSets.api.output + dependsOn apiClasses + + include "${modGroupPath}/**" + include "assets/**" + include "mcmod.info" + include "pack.mcmeta" + if (accessTransformersFile) { + include "META-INF/${accessTransformersFile}" } - attributes(attribute_map) } - // Add all embedded dependencies into the jar - from(provider { configurations.embed.collect { it.isDirectory() ? it : zipTree(it) } }) +} + +// Configure default run tasks +if (separateRunDirectories.toBoolean()) { + runClient { + workingDir = file('run/client') + } + + runServer { + workingDir = file('run/server') + } +} + +// Create API library jar +tasks.register('apiJar', Jar) { + archiveClassifier.set 'api' + if (useSrcApiPath) { + from(sourceSets.api.java) { + include "${modGroupPath}/${apiPackagePath}/**" + } + from(sourceSets.api.output) { + include "${modGroupPath}/${apiPackagePath}/**" + } + } else { + from(sourceSets.main.java) { + include "${modGroupPath}/${apiPackagePath}/**" + } + + from(sourceSets.main.output) { + include "${modGroupPath}/${apiPackagePath}/**" + } + } +} + +// Configure shadow jar task +if (usesShadowedDependencies.toBoolean()) { + tasks.named('shadowJar', ShadowJar).configure { + manifest { + attributes(getManifestAttributes()) + } + // Only shadow classes that are actually used, if enabled + if (minimizeShadowedDependencies.toBoolean()) { + minimize() + } + configurations = [ + project.configurations.shadowImplementation, + project.configurations.shadowCompile + ] + archiveClassifier.set('dev') + if (relocateShadowedDependencies.toBoolean()) { + relocationPrefix = modGroup + '.shadow' + enableRelocation = true + } + } + configurations.runtimeElements.outgoing.artifacts.clear() + configurations.apiElements.outgoing.artifacts.clear() + configurations.runtimeElements.outgoing.artifact(tasks.named('shadowJar', ShadowJar)) + configurations.apiElements.outgoing.artifact(tasks.named('shadowJar', ShadowJar)) + tasks.named('jar', Jar) { + enabled = false + finalizedBy(tasks.shadowJar) + } + tasks.named('reobfJar', ReobfuscatedJar) { + inputJar.set(tasks.named('shadowJar', ShadowJar).flatMap({it.archiveFile})) + } + AdhocComponentWithVariants javaComponent = (AdhocComponentWithVariants) project.components.findByName('java') + javaComponent.withVariantsFromConfiguration(configurations.shadowRuntimeElements) { + skip() + } + for (runTask in ['runClient', 'runServer']) { + tasks.named(runTask).configure { + dependsOn('shadowJar') + } + } +} + +def getManifestAttributes() { + def attributes = [:] + if (coreModClass) { + attributes['FMLCorePlugin'] = "${modGroup}.${coreModClass}" + } + if (!containsMixinsAndOrCoreModOnly.toBoolean() && (usesMixins.toBoolean() || coreModClass)) { + attributes['FMLCorePluginContainsFMLMod'] = true + } + if (accessTransformersFile) { + attributes['FMLAT'] = accessTransformersFile.toString() + } + + if (usesMixins.toBoolean()) { + attributes['ForceLoadAsMod'] = !containsMixinsAndOrCoreModOnly.toBoolean() + } + return attributes +} + + +// LWJGL3ify setup +if (enableJava17RunTasks.toBoolean()) { + + apply plugin: 'de.undercouch.download' + + ext.java17Toolchain = (JavaToolchainSpec spec) -> { + spec.languageVersion.set(JavaLanguageVersion.of(17)) + spec.vendor.set(JvmVendorSpec.matching("jetbrains")) + } + ext.java21Toolchain = (JavaToolchainSpec spec) -> { + spec.languageVersion.set(JavaLanguageVersion.of(21)) + spec.vendor.set(JvmVendorSpec.matching("jetbrains")) + } + + ext.java17DependenciesCfg = (DefaultUnlockedConfiguration) configurations.create("java17Dependencies") { + extendsFrom(configurations.getByName("runtimeClasspath")) // Ensure consistent transitive dependency resolution + canBeConsumed = false + } + ext.java17PatchDependenciesCfg = (DefaultUnlockedConfiguration) configurations.create("java17PatchDependencies") { + canBeConsumed = false + } + + dependencies { + if (modId != 'lwjgl3ify') { + java17Dependencies("io.github.twilightflower:lwjgl3ify:1.0.0") + } + java17PatchDependencies("io.github.twilightflower:lwjgl3ify:1.0.0:forgePatches") { + transitive = false + } + } + + ext.java17JvmArgs = [ + "-Dfile.encoding=UTF-8", + "-Djava.system.class.loader=com.gtnewhorizons.retrofuturabootstrap.RfbSystemClassLoader", + "-Djava.security.manager=allow", + "--add-opens", "java.base/jdk.internal.loader=ALL-UNNAMED", + "--add-opens", "java.base/java.net=ALL-UNNAMED", + "--add-opens", "java.base/java.nio=ALL-UNNAMED", + "--add-opens", "java.base/java.io=ALL-UNNAMED", + "--add-opens", "java.base/java.lang=ALL-UNNAMED", + "--add-opens", "java.base/java.lang.reflect=ALL-UNNAMED", + "--add-opens", "java.base/java.text=ALL-UNNAMED", + "--add-opens", "java.base/java.util=ALL-UNNAMED", + "--add-opens", "java.base/jdk.internal.reflect=ALL-UNNAMED", + "--add-opens", "java.base/sun.nio.ch=ALL-UNNAMED", + "--add-opens", "jdk.naming.dns/com.sun.jndi.dns=ALL-UNNAMED,java.naming", + "--add-opens", "java.desktop/sun.awt=ALL-UNNAMED", + "--add-opens", "java.desktop/sun.awt.image=ALL-UNNAMED", + "--add-opens", "java.desktop/com.sun.imageio.plugins.png=ALL-UNNAMED", + "--add-opens", "jdk.dynalink/jdk.dynalink.beans=ALL-UNNAMED", + "--add-opens", "java.sql.rowset/javax.sql.rowset.serial=ALL-UNNAMED" + ] + + ext.hotswapJvmArgs = [ + // DCEVM advanced hot reload + "-XX:+AllowEnhancedClassRedefinition", + "-XX:HotswapAgent=fatjar" + ] + + ext.setupHotswapAgent17 = tasks.register("setupHotswapAgent17", SetupHotswapAgentTask, t -> { + t.setTargetForToolchain(java17Toolchain) + }) + + ext.setupHotswapAgent21 = tasks.register("setupHotswapAgent21", SetupHotswapAgentTask, t -> { + t.setTargetForToolchain(java21Toolchain) + }) + + def runClient17Task = tasks.register("runClient17", RunHotswappableMinecraftTask, Distribution.CLIENT, "runClient") + runClient17Task.configure { + dependsOn(setupHotswapAgent17) + setup(project) + javaLauncher = project.javaToolchains.launcherFor(project.java17Toolchain) + } + + def runServer17Task = tasks.register("runServer17", RunHotswappableMinecraftTask, Distribution.DEDICATED_SERVER, "runServer") + runServer17Task.configure { + dependsOn(setupHotswapAgent17) + setup(project) + javaLauncher = project.javaToolchains.launcherFor(project.java17Toolchain) + } + + def runClient21Task = tasks.register("runClient21", RunHotswappableMinecraftTask, Distribution.CLIENT, "runClient") + runClient21Task.configure { + dependsOn(setupHotswapAgent21) + setup(project) + javaLauncher = project.javaToolchains.launcherFor(project.java21Toolchain) + } + + def runServer21Task = tasks.register("runServer21", RunHotswappableMinecraftTask, Distribution.DEDICATED_SERVER, "runServer") + runServer21Task.configure { + dependsOn(setupHotswapAgent21) + setup(project) + javaLauncher = project.javaToolchains.launcherFor(project.java21Toolchain) + } +} + +abstract class RunHotswappableMinecraftTask extends RunMinecraftTask { + + // IntelliJ doesn't seem to allow pre-set commandline arguments, so we also support an env variable + private boolean enableHotswap = Boolean.valueOf(System.getenv("HOTSWAP")) + + public final Distribution side + public final String superTask + + @Input + boolean getEnableHotswap() { + return enableHotswap + } + + @Option(option = "hotswap", description = "Enables HotSwapAgent for enhanced class reloading under a debugger") + boolean setEnableHotswap(boolean enable) { + enableHotswap = enable + } + + @Inject + RunHotswappableMinecraftTask(Distribution side, String superTask, org.gradle.api.invocation.Gradle gradle) { + super(side, gradle) + + this.side = side + this.superTask = superTask + setGroup("Modded Minecraft") + setDescription("Runs the modded " + side.name().toLowerCase(Locale.ROOT) + " using modern Java and lwjgl3ify") + this.getLwjglVersion().set(3) + } + + void setup(Project project) { + final MinecraftExtension minecraft = project.getExtensions().getByType(MinecraftExtension.class) + final MCPTasks mcpTasks = project.getExtensions().getByType(MCPTasks.class) + final MinecraftTasks mcTasks = project.getExtensions().getByType(MinecraftTasks.class) + + this.getExtraJvmArgs().addAll((List) project.property("java17JvmArgs")) + if (getEnableHotswap()) { + this.getExtraJvmArgs().addAll((List) project.property("hotswapJvmArgs")) + } + + this.classpath(project.property("java17PatchDependenciesCfg")) + this.classpath(mcpTasks.getTaskPackageMcLauncher()) + this.classpath(mcpTasks.getTaskPackagePatchedMc()) + this.classpath(mcpTasks.getPatchedConfiguration()) + this.classpath(project.getTasks().named("jar")) + this.classpath(project.property("java17DependenciesCfg")) + + super.setup(project) + + dependsOn( + mcpTasks.getLauncherSources().getClassesTaskName(), + mcTasks.getTaskDownloadVanillaAssets(), + mcpTasks.getTaskPackagePatchedMc(), + "jar" + ) + + getMainClass().set((side == Distribution.CLIENT) ? "GradleStart" : "GradleStartServer") + getUsername().set(minecraft.getUsername()) + getUserUUID().set(minecraft.getUserUUID()) + if (side == Distribution.DEDICATED_SERVER) { + getExtraArgs().add("nogui") + } + + systemProperty("gradlestart.bouncerClient", "com.gtnewhorizons.retrofuturabootstrap.Main") + systemProperty("gradlestart.bouncerServer", "com.gtnewhorizons.retrofuturabootstrap.Main") + + if (project.usesMixins.toBoolean()) { + this.extraJvmArgs.addAll(project.provider(() -> { + def mixinCfg = project.configurations.detachedConfiguration(project.dependencies.create(project.mixinProviderSpec)) + mixinCfg.canBeConsumed = false + mixinCfg.canBeResolved = true + mixinCfg.transitive = false + enableHotswap ? ["-javaagent:" + mixinCfg.singleFile.absolutePath] : [] + })) + } + } +} + +abstract class SetupHotswapAgentTask extends DefaultTask { + + @OutputFile + abstract RegularFileProperty getTargetFile() + + void setTargetForToolchain(Action spec) { + getTargetFile().set(project.javaToolchains.launcherFor(spec).map { + it.metadata.installationPath.file("lib/hotswap/hotswap-agent.jar") + }) + } + + @Inject + SetupHotswapAgentTask() { + setGroup("GT Buildscript") + setDescription("Installs a recent version of HotSwapAgent into the Java runtime directory") + onlyIf("Run only if not already installed", t -> !((SetupHotswapAgentTask) t).getTargetFile().getAsFile().get().exists()) + } + + @TaskAction + void installHSA() { + final String url = 'https://github.com/HotswapProjects/HotswapAgent/releases/download/1.4.2-SNAPSHOT/hotswap-agent-1.4.2-SNAPSHOT.jar' + final File target = getTargetFile().getAsFile().get() + final File parent = target.getParentFile() + FileUtils.forceMkdir(parent) + final DownloadExtension download = getProject().getExtensions().findByType(DownloadExtension.class) + download.run(ds -> { + try { + ds.src(url) + } catch (MalformedURLException e) { + throw new RuntimeException(e) + } + ds.dest(target) + ds.overwrite(false) + ds.tempAndMove(true) + }) + } +} + + +// IDE Configuration + +eclipse { + classpath { + downloadSources = true + downloadJavadoc = true + } } idea { module { - inheritOutputDirs = true + inheritOutputDirs true + downloadJavadoc true + downloadSources true } project { settings { runConfigurations { - "1. Run Client"(Gradle) { - taskNames = ["runClient"] + '1. Setup Workspace'(Gradle) { + taskNames = ['setupDecompWorkspace'] + } + '2. Run Client'(Gradle) { + taskNames = ['runClient'] + } + if (enableJava17RunTasks.toBoolean()) { + '2a. Run Client (Java 17)'(Gradle) { + taskNames = ['runClient17'] + } + '2b. Run Client (Java 21)'(Gradle) { + taskNames = ['runClient21'] + } } - "2. Run Server"(Gradle) { - taskNames = ["runServer"] + '3. Run Server'(Gradle) { + taskNames = ['runServer'] } - "3. Run Obfuscated Client"(Gradle) { - taskNames = ["runObfClient"] + if (enableJava17RunTasks.toBoolean()) { + '3a. Run Server (Java 17)'(Gradle) { + taskNames = ['runServer17'] + } + '3b. Run Server (Java 21)'(Gradle) { + taskNames = ['runServer21'] + } } - "4. Run Obfuscated Server"(Gradle) { - taskNames = ["runObfServer"] + '4. Run Obfuscated Client'(Gradle) { + taskNames = ['runObfClient'] + } + '5. Run Obfuscated Server'(Gradle) { + taskNames = ['runObfServer'] + } + if (enableSpotless.toBoolean()) { + '6. Apply Spotless'(Gradle) { + taskNames = ["spotlessApply"] + } + '7. Build Jars'(Gradle) { + taskNames = ['build'] + } + } else { + '6. Build Jars'(Gradle) { + taskNames = ['build'] + } + } + 'Update Buildscript'(Gradle) { + taskNames = ['updateBuildScript'] + } + 'FAQ'(Gradle) { + taskNames = ['faq'] } } compiler.javac { afterEvaluate { - javacAdditionalOptions = "-encoding utf8" - moduleJavacAdditionalOptions = [(project.name + ".main"): tasks.compileJava.options.compilerArgs.collect { '"' + it + '"' }.join(' ')] + javacAdditionalOptions = '-encoding utf8' + moduleJavacAdditionalOptions = [ + (project.name + '.main'): tasks.compileJava.options.compilerArgs.collect { + '"' + it + '"' + }.join(' ') + ] } } } } } -tasks.named("processIdeaSettings").configure { - dependsOn("injectTags") + +// Deployment +def final modrinthApiKey = providers.environmentVariable('MODRINTH_API_KEY') +def final cfApiKey = providers.environmentVariable('CURSEFORGE_API_KEY') +final boolean isCIEnv = providers.environmentVariable('CI').getOrElse('false').toBoolean() + +if (isCIEnv || deploymentDebug.toBoolean()) { + artifacts { + if (!noPublishedSources.toBoolean()) { + archives sourcesJar + } + if (apiPackage) { + archives apiJar + } + } +} + +// Changelog generation +tasks.register('generateChangelog') { + group = 'GT Buildscript' + description = 'Generate a default changelog of all commits since the last tagged git commit' + onlyIf { + generateDefaultChangelog.toBoolean() + } + doLast { + def lastTag = getLastTag() + + def changelog = runShell(([ + "git", + "log", + "--date=format:%d %b %Y", + "--pretty=%s - **%an** (%ad)", + "${lastTag}..HEAD" + ] + (sourceSets.main.java.srcDirs + sourceSets.main.resources.srcDirs) + .collect { ['--', it] }).flatten()) + + if (changelog) { + changelog = "Changes since ${lastTag}:\n${{("\n" + changelog).replaceAll("\n", "\n* ")}}" + } + def f = getFile('build/changelog.md') + changelog = changelog ?: 'There have been no changes.' + f.write(changelog, 'UTF-8') + + // Set changelog for Modrinth + if (modrinthApiKey.isPresent() || deploymentDebug.toBoolean()) { + modrinth.changelog.set(changelog) + } + } +} + +if (cfApiKey.isPresent() || deploymentDebug.toBoolean()) { + apply plugin: 'net.darkhax.curseforgegradle' + //noinspection UnnecessaryQualifiedReference + tasks.register('curseforge', net.darkhax.curseforgegradle.TaskPublishCurseForge) { + disableVersionDetection() + debugMode = deploymentDebug.toBoolean() + apiToken = cfApiKey.getOrElse('debug_token') + + doFirst { + def mainFile = upload(curseForgeProjectId, reobfJar) + def changelogFile = getChangelog() + def changelogRaw = changelogFile.exists() ? changelogFile.getText('UTF-8') : "" + + mainFile.displayName = versionDisplayFormat.replace('$MOD_NAME', modName).replace('$VERSION', modVersion) + mainFile.releaseType = getReleaseType() + mainFile.changelog = changelogRaw + mainFile.changelogType = 'markdown' + mainFile.addModLoader 'Forge' + mainFile.addJavaVersion "Java 8" + mainFile.addGameVersion minecraftVersion + + if (curseForgeRelations.size() != 0) { + String[] deps = curseForgeRelations.split(';') + deps.each { dep -> + if (dep.size() == 0) { + return + } + String[] parts = dep.split(':') + String type = parts[0], slug = parts[1] + def types = [ + 'req' : 'requiredDependency', 'required': 'requiredDependency', + 'opt' : 'optionalDependency', 'optional': 'optionalDependency', + 'embed' : 'embeddedLibrary', 'embedded': 'embeddedLibrary', + 'incomp': 'incompatible', 'fail' : 'incompatible'] + if (types.containsKey(type)) type = types[type] + if (!(type in ['requiredDependency', 'embeddedLibrary', 'optionalDependency', 'tool', 'incompatible'])) { + throw new Exception('Invalid Curseforge dependency type: ' + type) + } + mainFile.addRelation(slug, type) + } + } + + for (artifact in getSecondaryArtifacts()) { + def additionalFile = mainFile.withAdditionalFile(artifact) + additionalFile.changelog = changelogRaw + } + } + doLast { + // No File IDs in Debug Mode + if (!deploymentDebug.toBoolean()) { + def list = [] + for (def artifact : tasks.curseforge.getUploadArtifacts()) { + list.add(artifact) + for (def additionalArtifact : artifact.getAdditionalArtifacts()) { + list.add(additionalArtifact) + } + } + def summary = "## CurseForge Build Summary (Mod ${modName} | Project ID ${curseForgeProjectId})" + for (def artifact : list) { + def fileId = artifact.getCurseFileId() + def fileName = artifact.getArtifact().getSingleFile().name + println("Uploaded File ${fileName}, With File ID: ${fileId}") + summary = summary + "\n - File: ${fileName} | File ID: ${fileId}" + } + println(summary) + def stepSummary = providers.environmentVariable("GITHUB_STEP_SUMMARY") + if (stepSummary.isPresent()) { + file(stepSummary.get()).write(summary) + } + } + } + } + tasks.curseforge.dependsOn(build) + tasks.curseforge.dependsOn('generateChangelog') +} + +if (modrinthApiKey.isPresent() || deploymentDebug.toBoolean()) { + apply plugin: 'com.modrinth.minotaur' + def final changelogFile = getChangelog() + + modrinth { + token = modrinthApiKey.getOrElse('debug_token') + projectId = modrinthProjectId + versionName = versionDisplayFormat.replace('$MOD_NAME', modName).replace('$VERSION', modVersion) + changelog = changelogFile.exists() ? changelogFile.getText('UTF-8') : "" + versionType = getReleaseType() + versionNumber = modVersion + gameVersions = [minecraftVersion] + loaders = ["forge"] + debugMode = deploymentDebug.toBoolean() + uploadFile = reobfJar + additionalFiles = getSecondaryArtifacts() + } + if (modrinthRelations.size() != 0) { + String[] deps = modrinthRelations.split(';') + deps.each { dep -> + if (dep.size() == 0) { + return + } + String[] parts = dep.split(':') + String[] qual = parts[0].split('-') + addModrinthDep(qual[0], qual.length > 1 ? qual[1] : 'project', parts[1]) + } + } + tasks.modrinth.dependsOn(build) + tasks.modrinth.dependsOn('generateChangelog') +} + +def addModrinthDep(String scope, String type, String name) { + com.modrinth.minotaur.dependencies.Dependency dep + def types = [ + 'req' : 'required', + 'opt' : 'optional', + 'embed' : 'embedded', + 'incomp': 'incompatible', 'fail': 'incompatible'] + if (types.containsKey(scope)) scope = types[scope] + if (!(scope in ['required', 'optional', 'incompatible', 'embedded'])) { + throw new Exception('Invalid modrinth dependency scope: ' + scope) + } + types = ['proj': 'project', '': 'project', 'p': 'project', 'ver': 'version', 'v': 'version'] + if (types.containsKey(type)) type = types[type] + switch (type) { + case 'project': + dep = new ModDependency(name, scope) + break + case 'version': + dep = new VersionDependency(name, scope) + break + default: + throw new Exception('Invalid modrinth dependency type: ' + type) + } + project.modrinth.dependencies.add(dep) +} + +if (customMavenPublishUrl) { + String publishedVersion = modVersion + + publishing { + publications { + create('maven', MavenPublication) { + //noinspection GroovyAssignabilityCheck + from components.java + + if (apiPackage) { + artifact apiJar + } + + // providers is not available here, use System for getting env vars + groupId = System.getenv('ARTIFACT_GROUP_ID') ?: project.mavenArtifactGroup + artifactId = System.getenv('ARTIFACT_ID') ?: project.modArchivesBaseName + version = System.getenv('RELEASE_VERSION') ?: publishedVersion + } + } + + repositories { + maven { + url = customMavenPublishUrl + allowInsecureProtocol = !customMavenPublishUrl.startsWith('https') + credentials { + username = providers.environmentVariable('MAVEN_USER').getOrElse('NONE') + password = providers.environmentVariable('MAVEN_PASSWORD').getOrElse('NONE') + } + } + } + } +} + +def getSecondaryArtifacts() { + def secondaryArtifacts = [usesShadowedDependencies.toBoolean() ? tasks.shadowJar : tasks.jar] + if (!noPublishedSources.toBoolean()) secondaryArtifacts += [sourcesJar] + if (apiPackage) secondaryArtifacts += [apiJar] + return secondaryArtifacts +} + +def getReleaseType() { + String type = project.releaseType + if (!(type in ['release', 'beta', 'alpha'])) { + throw new Exception("Release type invalid! Found \"" + type + "\", allowed: \"release\", \"beta\", \"alpha\"") + } + return type +} + +/* + * If CHANGELOG_LOCATION env var is set, that takes highest precedence. + * Next, if 'generateDefaultChangelog' option is enabled, use that. + * Otherwise, try to use a CHANGELOG.md file at root directory. + */ +def getChangelog() { + def final changelogEnv = providers.environmentVariable('CHANGELOG_LOCATION') + if (changelogEnv.isPresent()) { + return new File(changelogEnv.get()) + } + if (generateDefaultChangelog.toBoolean()) { + return getFile('build/changelog.md') + } + return getFile('CHANGELOG.md') +} + + +// Buildscript updating + +def buildscriptGradleVersion = '8.5' + +tasks.named('wrapper', Wrapper).configure { + gradleVersion = buildscriptGradleVersion +} + +tasks.register('updateBuildScript') { + group = 'GT Buildscript' + description = 'Updates the build script to the latest version' + + if (gradle.gradleVersion != buildscriptGradleVersion && !Boolean.getBoolean('DISABLE_BUILDSCRIPT_GRADLE_UPDATE')) { + dependsOn('wrapper') + } + + doLast { + if (performBuildScriptUpdate()) return + print('Build script already up to date!') + } +} + +if (!project.getGradle().startParameter.isOffline() && !Boolean.getBoolean('DISABLE_BUILDSCRIPT_UPDATE_CHECK') && isNewBuildScriptVersionAvailable()) { + if (autoUpdateBuildScript.toBoolean()) { + performBuildScriptUpdate() + } else { + out.style(Style.SuccessHeader).println("Build script update available! Run 'gradle updateBuildScript'") + if (gradle.gradleVersion != buildscriptGradleVersion) { + out.style(Style.SuccessHeader).println("updateBuildScript can update gradle from ${gradle.gradleVersion} to ${buildscriptGradleVersion}\n") + } + } +} + +static URL availableBuildScriptUrl() { + new URL("https://raw.githubusercontent.com/GregTechCEu/Buildscripts/master/build.gradle") +} + +static URL availableSettingsGradleUrl() { + new URL("https://raw.githubusercontent.com/GregTechCEu/Buildscripts/master/settings.gradle") +} + +boolean performBuildScriptUpdate() { + if (isNewBuildScriptVersionAvailable()) { + def buildscriptFile = getFile("build.gradle") + def settingsFile = getFile("settings.gradle") + availableBuildScriptUrl().withInputStream { i -> buildscriptFile.withOutputStream { it << i } } + availableSettingsGradleUrl().withInputStream { i -> settingsFile.withOutputStream { it << i } } + def out = services.get(StyledTextOutputFactory).create('buildscript-update-output') + out.style(Style.Success).print("Build script updated. Please REIMPORT the project or RESTART your IDE!") + return true + } + return false +} + +boolean isNewBuildScriptVersionAvailable() { + Map parameters = ["connectTimeout": 10000, "readTimeout": 10000] + + String currentBuildScript = getFile("build.gradle").getText() + String currentBuildScriptHash = getVersionHash(currentBuildScript) + String availableBuildScript = availableBuildScriptUrl().newInputStream(parameters).getText() + String availableBuildScriptHash = getVersionHash(availableBuildScript) + + boolean isUpToDate = currentBuildScriptHash.empty || availableBuildScriptHash.empty || currentBuildScriptHash == availableBuildScriptHash + return !isUpToDate +} + +static String getVersionHash(String buildScriptContent) { + String versionLine = buildScriptContent.find("^//version: [a-z0-9]*") + if (versionLine != null) { + return versionLine.split(": ").last() + } + return "" +} + + +// Faq + +tasks.register('faq') { + group = 'GT Buildscript' + description = 'Prints frequently asked questions about building a project' + doLast { + print("\nTo update this buildscript to the latest version, run 'gradlew updateBuildScript' or run the generated run configuration if you are using IDEA.\n" + + "To set up the project, run the 'setupDecompWorkspace' task, which you can run as './gradlew setupDecompWorkspace' in a terminal, or find in the 'modded minecraft' gradle category.\n\n" + + "To add new dependencies to your project, place them in 'dependencies.gradle', NOT in 'build.gradle' as they would be replaced when the script updates.\n" + + "To add new repositories to your project, place them in 'repositories.gradle'.\n" + + "If you need additional gradle code to run, you can place it in a file named 'addon.gradle' (or either of the above, up to you for organization).\n\n" + + "If your build fails to recognize the syntax of newer Java versions, enable Jabel in your 'gradle.properties' under the option name 'enableModernJavaSyntax'.\n" + + "To see information on how to configure your IDE properly for Java 17, see https://github.com/GregTechCEu/Buildscripts/blob/master/docs/jabel.md\n\n" + + "Report any issues or feature requests you have for this build script to https://github.com/GregTechCEu/Buildscripts/issues\n") + } +} + + +// Helpers + +def getDefaultArtifactGroup() { + def lastIndex = project.modGroup.lastIndexOf('.') + return lastIndex < 0 ? project.modGroup : project.modGroup.substring(0, lastIndex) +} + +def getFile(String relativePath) { + return new File(projectDir, relativePath) +} + +def checkPropertyExists(String propertyName) { + if (!project.hasProperty(propertyName)) { + throw new GradleException("This project requires a property \"" + propertyName + "\"! Please add it your \"gradle.properties\". You can find all properties and their description here: https://github.com/GregTechCEu/Buildscripts/blob/main/gradle.properties") + } +} + +def propertyDefaultIfUnset(String propertyName, defaultValue) { + if (!project.hasProperty(propertyName) || project.property(propertyName) == "") { + project.ext.setProperty(propertyName, defaultValue) + } +} + +def propertyDefaultIfUnsetWithEnvVar(String propertyName, defaultValue, String envVarName) { + def envVar = providers.environmentVariable(envVarName) + if (envVar.isPresent()) { + project.ext.setProperty(propertyName, envVar.get()) + } else { + propertyDefaultIfUnset(propertyName, defaultValue) + } +} + +static runShell(command) { + def process = command.execute() + def outputStream = new StringBuffer() + def errorStream = new StringBuffer() + process.waitForProcessOutput(outputStream, errorStream) + + errorStream.toString().with { + if (it) { + throw new GradleException("Error executing ${command}:\n> ${it}") + } + } + return outputStream.toString().trim() +} + +def getLastTag() { + def githubTag = providers.environmentVariable('GITHUB_TAG') + return runShell('git describe --abbrev=0 --tags ' + + (githubTag.isPresent() ? runShell('git rev-list --tags --skip=1 --max-count=1') : '')) } diff --git a/dependencies.gradle b/dependencies.gradle new file mode 100644 index 00000000..9fa7bb98 --- /dev/null +++ b/dependencies.gradle @@ -0,0 +1,165 @@ +//file:noinspection DependencyNotationArgument +//file:noinspection GrUnresolvedAccess +// TODO remove when fixed in RFG ^ +/* + * Add your dependencies here. Supported configurations: + * - api("group:name:version:classifier"): if you use the types from this dependency in the public API of this mod + * Available at runtime and compiletime for mods depending on this mod + * - implementation("g:n:v:c"): if you need this for internal implementation details of the mod, but none of it is visible via the public API + * Available at runtime but not compiletime for mods depending on this mod + * - compileOnly("g:n:v:c"): if the mod you're building doesn't need this dependency during runtime at all, e.g. for optional mods + * Not available at all for mods depending on this mod, only visible at compiletime for this mod + * - compileOnlyApi("g:n:v:c"): like compileOnly, but also visible at compiletime for mods depending on this mod + * Available at compiletime but not runtime for mods depending on this mod + * - runtimeOnlyNonPublishable("g:n:v:c"): if you want to include a mod in this mod's runClient/runServer runs, but not publish it as a dependency + * Not available at all for mods depending on this mod, only visible at runtime for this mod + * - devOnlyNonPublishable("g:n:v:c"): a combination of runtimeOnlyNonPublishable and compileOnly for dependencies present at both compiletime and runtime, + * but not published as Maven dependencies - useful for RFG-deobfuscated dependencies or local testing + * - runtimeOnly("g:n:v:c"): if you don't need this at compile time, but want it to be present at runtime + * Available at runtime for mods depending on this mod + * - annotationProcessor("g:n:v:c"): mostly for java compiler plugins, if you know you need this, use it, otherwise don't worry + * - testCONFIG("g:n:v:c") - replace CONFIG by one of the above (except api), same as above but for the test sources instead of main + * + * - shadowImplementation("g:n:v:c"): effectively the same as API, but the dependency is included in your jar under a renamed package name + * Requires you to enable usesShadowedDependencies in gradle.properties + * For more info, see https://github.com/GregTechCEu/Buildscripts/blob/master/docs/shadow.md + * + * You can exclude transitive dependencies (dependencies of the chosen dependency) by appending { transitive = false } if needed, + * but use this sparingly as it can break using your mod as another mod's dependency if you're not careful. + * + * To depend on obfuscated jars you can use `devOnlyNonPublishable(rfg.deobf("dep:spec:1.2.3"))` to fetch an obfuscated jar from maven, + * or `devOnlyNonPublishable(rfg.deobf(project.files("libs/my-mod-jar.jar")))` to use a file. + * + * To add a mod with CurseMaven, replace '("g:n:v:c")' in the above with 'rfg.deobf("curse.maven:project_slug-project_id:file_id")' + * Example: devOnlyNonPublishable(rfg.deobf("curse.maven:top-245211:2667280")) + * + * Gradle names for some of the configuration can be misleading, compileOnlyApi and runtimeOnly both get published as dependencies in Maven, but compileOnly does not. + * The buildscript adds runtimeOnlyNonPublishable to also have a runtime dependency that's not published. + * + * For more details, see https://docs.gradle.org/8.4/userguide/java_library_plugin.html#sec:java_library_configurations_graph + */ + +// A hash map of mod to debug options that will enable it in runtime. +// Anything that should be handled differently than always being "compileOnly" and also being "runtimeOnly" when debug enabled +// should be handled in the main dependencies function. +// When adding additional elements, sort by alphabetical name +final def mod_dependencies = [ + 'CraftTweaker2:CraftTweaker2-MC1120-Main:1.12-4.1.20.684' : [debug_crafttweaker, debug_stages], + 'TechReborn:TechReborn-ModCompatibility-1.12.2:1.4.0.76:universal': [debug_tech_reborn], + 'codechicken:ChickenASM:1.12-1.0.2.9' : [debug_forgemultipartcbe, debug_project_red], + 'com.blamejared:MTLib:3.0.5.15' : [debug_crafttweaker], + 'com.enderio:endercore:0.5.78' : [debug_enderio], + 'com.teamacronymcoders.base:base:1.12.2-3.14.0' : [debug_crafttweaker], + 'com.teamacronymcoders:ContentTweaker:1.12.2-4.10.0' : [debug_crafttweaker], + 'crazypants:enderio:5.3.72' : [debug_enderio], + 'curse.maven:abyssalcraft-53686:3425234' : [debug_abyssalcraft], + 'curse.maven:actuallyaditions-228404:2844115' : [debug_actually_additions], + 'curse.maven:advent-of-ascension-311054:3054253' : [debug_advent_of_ascension], + 'curse.maven:applecore-224472:2969118' : [debug_the_spice_of_life], + 'curse.maven:arcanearchives-311357:3057332' : [debug_arcane_archives], + 'curse.maven:astral-sorcery-241721:3044416' : [debug_astral_sorcery], + 'curse.maven:atomicstrykers-infernal-mobs-227875:3431758' : [debug_atomicstrykers_infernal_mobs], + 'curse.maven:autoreglib-250363:2746011' : [debug_quark], + 'curse.maven:baubles-227083:2518667' : [debug_astral_sorcery, debug_bewitchment, debug_botania, debug_thaumcraft, debug_thaumic_wonders], + 'curse.maven:bewitchment-285439:3044569' : [debug_bewitchment], + 'curse.maven:binnies-mods-223525:2916129' : [debug_binnies_mods], + 'curse.maven:biomes-o-plenty-220318:2842510' : [debug_biomes_o_plenty], + 'curse.maven:blood-magic-224791:2822288' : [debug_blood_magic], + 'curse.maven:botania-225643:3330934' : [debug_botania], + 'curse.maven:ceramics-250617:3158763' : [debug_ceramics], + 'curse.maven:chameleon-230497:2450900' : [debug_storage_drawers], + 'curse.maven:chickens-241941:2537643' : [debug_chickens], + 'curse.maven:chisel-235279:2915375' : [debug_chisel], + 'curse.maven:codechickenlib-242818:2779848' : [debug_enderstorage, debug_forgemultipartcbe, debug_mrtjpcore, debug_project_red, debug_thermal_expansion], + 'curse.maven:cofh_core-69162:2920433' : [debug_thermal_expansion], + 'curse.maven:cofhworld-271384:2920434' : [debug_thermal_expansion], + 'curse.maven:collective-342584:3533131' : [debug_collective], + 'curse.maven:compactmachines-224218:2707509' : [debug_compact_machines], + 'curse.maven:cqrepoured-303422:3953103' : [debug_cqrepoured], + 'curse.maven:ctm-267602:2915363' : [debug_chisel], + 'curse.maven:effortlessbuilding-302113:2847346' : [debug_effortless_building], + 'curse.maven:elementary-staffs-346007:2995593' : [debug_elementary_staffs], + 'curse.maven:elenaidodge2-442962:3343308' : [debug_elenai_dodge_2], + 'curse.maven:enderstorage-245174:2755787' : [debug_enderstorage], + 'curse.maven:epic-siege-mod-229449:3356157' : [debug_epic_siege_mod], + 'curse.maven:extrautilities-225561:2678374' : [debug_extra_utilities_2], + 'curse.maven:forestry-59751:2918418' : [debug_forestry, debug_binnies_mods], + 'curse.maven:forgemultipartcbe-258426:2755790' : [debug_forgemultipartcbe, debug_project_red], + 'curse.maven:geckolib-388172:4020277' : [debug_cqrepoured], + 'curse.maven:guideapi-228832:2645992' : [debug_blood_magic], + 'curse.maven:hwyla-253449:2568751' : [debug_hwyla], + 'curse.maven:industrialcraft-242638:3078604' : [debug_industrialcraft], + 'curse.maven:ironbackpacks-227049:2564573' : [debug_iron_backpacks], + 'curse.maven:mantle-74924:2713386' : [debug_tinkers_construct], + 'curse.maven:mcjtylib-233105:2745846' : [debug_rftools], + 'curse.maven:mekanism-268560:2835175' : [debug_mekanism], + 'curse.maven:modtweaker-220954:3840577' : [debug_crafttweaker], + 'curse.maven:modular-routers-250294:2954953' : [debug_modular_routers], + 'curse.maven:mrtjpcore-229002:2735197' : [debug_mrtjpcore, debug_project_red], + 'curse.maven:mysticallib-277064:3483816' : [debug_arcane_archives], + 'curse.maven:netherchest-268888:2655413' : [debug_netherchest], + 'curse.maven:netherrocks-226140:2628297' : [debug_netherrocks], + 'curse.maven:nuclearcraft-226254:3784145' : [debug_nuclearcraft], + 'curse.maven:openblocks-228816:2699056' : [debug_openblocks], + 'curse.maven:opencomputers-223008:5274236' : [debug_opencomputers], + 'curse.maven:openmodslib-228815:2699055' : [debug_openblocks], + 'curse.maven:patchouli-306770:3162874' : [debug_bewitchment], + 'curse.maven:plustic-376903:4703532' : [debug_tinkers_construct], + 'curse.maven:projectredbase-228702:2745545' : [debug_project_red], + 'curse.maven:projectredworld-229049:2745551' : [debug_project_red], + 'curse.maven:quark-243121:2924091' : [debug_quark], + 'curse.maven:railcraft-51195:3853491' : [debug_railcraft], + 'curse.maven:reach-fix-556777:4633407' : [debug_cqrepoured], + 'curse.maven:reborn-core-237903:3330308' : [debug_tech_reborn], + 'curse.maven:redstone-flux-270789:2920436' : [debug_thermal_expansion], + 'curse.maven:requious-frakto-336748:3218640' : [debug_requious_frakto], + 'curse.maven:reskillable-286382:2815686' : [debug_reskillable], + 'curse.maven:rftools-224641:2861573' : [debug_rftools], + 'curse.maven:rftools-dimensions-240950:2707390' : [debug_rftools], + 'curse.maven:roost-277711:2702080' : [debug_chickens], + 'curse.maven:shadowfacts-forgelin-248453:2785465' : [debug_industrial_foregoing], + 'curse.maven:simplecore-226136:2629840' : [debug_netherrocks], + 'curse.maven:simpledifficulty-360779:3613814' : [debug_simple_difficulty], + 'curse.maven:simplyjetpacks2-251792:3294422' : [debug_simply_jetpacks_2], + 'curse.maven:steamworld-282607:2641200' : [debug_steamworld], + 'curse.maven:storage-drawers-223852:2952606' : [debug_storage_drawers], + 'curse.maven:tardis-290247:2903453' : [debug_tardis], + 'curse.maven:tech-reborn-233564:2966851' : [debug_tech_reborn], + 'curse.maven:teslacorelib-254602:2891841' : [debug_industrial_foregoing], + 'curse.maven:thaumcraft-223628:2629023' : [debug_thaumcraft, debug_thaumic_wonders], + 'curse.maven:thaumicwonders-316704:2787954' : [debug_thaumic_wonders], + 'curse.maven:the-erebus-220698:3211974' : [debug_erebus], + 'curse.maven:thefarlanders-336432:2805139' : [debug_the_farlanders], + 'curse.maven:thermal-expansion-69163:2926431' : [debug_thermal_expansion], + 'curse.maven:thermalfoundation-222880:2926428' : [debug_thermal_expansion], + 'curse.maven:thespiceoflife-220811:2571951' : [debug_the_spice_of_life], + 'curse.maven:tinkers_construct-74072:2902483' : [debug_tinkers_construct], + 'curse.maven:tinkerscomplement-272671:2843439' : [debug_tinkers_construct], + 'curse.maven:tinyprogressions-250850:2721018' : [debug_tiny_progressions], + 'maven.modrinth:industrial-foregoing:1.12.13-237' : [debug_industrial_foregoing], + 'net.darkhax.bookshelf:Bookshelf-1.12.2:2.3.590' : [debug_stages], + 'net.darkhax.gamestages:GameStages-1.12.2:2.0.120' : [debug_stages], + 'net.darkhax.itemstages:ItemStages-1.12.2:2.0.51' : [debug_stages], + 'net.darkhax.mobstages:MobStages-1.12.2:2.0.13' : [debug_stages], +] + + +dependencies { + // Libraries + implementation 'com.cleanroommc:configanytime:2.0' + + implementation 'com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2' + embed 'com.googlecode.concurrentlinkedhashmap:concurrentlinkedhashmap-lru:1.4.2' + implementation 'com.udojava:EvalEx:2.7' + embed 'com.udojava:EvalEx:2.7' + + // compiles all mods, and selectively enables mods being debugged + mod_dependencies.forEach { key, value -> + final def mod = rfg.deobf(key) + compileOnly mod + if (value.any { it.toBoolean() }) { + runtimeOnly mod + } + } + +} \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index 00c2a4e3..c2fb7a0e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,24 +1,266 @@ -# Sets default memory used for gradle commands. Can be overridden by user or command line properties. -# This is required to provide enough memory for the Minecraft decompilation process. -org.gradle.jvmargs = -Xmx3G -# Mod Information -maven_group = mod.acgaming -mod_id = universaltweaks -mod_name = Universal Tweaks -mod_version = 1.12.0 -archives_base_name = UniversalTweaks-1.12.2 +# START MOD DEBUG CONTROL SECTION + +# Note that these should always be false when committing + +debug_abyssalcraft = false +debug_actually_additions = false +debug_advent_of_ascension = false +debug_arcane_archives = false +debug_astral_sorcery = false +debug_atomicstrykers_infernal_mobs = false +debug_bewitchment = false +debug_binnies_mods = false +debug_biomes_o_plenty = false +debug_blood_magic = false +debug_botania = false +debug_ceramics = false +debug_chickens = false +debug_chisel = false +debug_collective = false +debug_compact_machines = false +debug_cqrepoured = false +debug_crafttweaker = false +debug_effortless_building = false +debug_elementary_staffs = false +debug_elenai_dodge_2 = false +debug_enderio = false +debug_enderstorage = false +debug_epic_siege_mod = false +debug_erebus = false +debug_extra_utilities_2 = false +debug_forestry = false +debug_forgemultipartcbe = false +debug_hwyla = false +debug_industrial_foregoing = false +debug_industrialcraft = false +debug_iron_backpacks = false +debug_mekanism = false +debug_modular_routers = false +debug_mrtjpcore = false +debug_netherchest = false +debug_netherrocks = false +debug_nuclearcraft = false +debug_openblocks = false +debug_opencomputers = false +debug_project_red = false +debug_quark = false +debug_railcraft = false +debug_requious_frakto = false +debug_reskillable = false +debug_rftools = false +debug_simple_difficulty = false +debug_simply_jetpacks_2 = false +debug_stages = false +debug_steamworld = false +debug_storage_drawers = false +debug_tardis = false +debug_tech_reborn = false +debug_thaumcraft = false +debug_thaumic_wonders = false +debug_the_farlanders = false +debug_the_spice_of_life = false +debug_thermal_expansion = false +debug_tinkers_construct = false +debug_tiny_progressions = false + +# END MOD DEBUG CONTROL SECTION + +modName = Universal Tweaks + +# This is a case-sensitive string to identify your mod. Convention is to use lower case. +modId = universaltweaks + +modGroup = mod.acgaming.universaltweaks + +# Version of your mod. +# This field can be left empty if you want your mod's version to be determined by the latest git tag instead. +modVersion = 1.12.0 + +# Whether to use the old jar naming structure (modid-mcversion-version) instead of the new version (modid-version) +includeMCVersionJar = false + +# The name of your jar when you produce builds, not including any versioning info +modArchivesBaseName = UniversalTweaks-1.12.2 + +# Will update your build.gradle automatically whenever an update is available +autoUpdateBuildScript = false + +minecraftVersion = 1.12.2 + +# Select a username for testing your mod with breakpoints. You may leave this empty for a random username each time you +# restart Minecraft in development. Choose this dependent on your mod: +# Do you need consistent player progressing (for example Thaumcraft)? -> Select a name +# Do you need to test how your custom blocks interacts with a player that is not the owner? -> leave name empty +# Alternatively this can be set with the 'DEV_USERNAME' environment variable. +developmentEnvironmentUserName = Developer + +# Additional arguments applied to the JVM when launching minecraft +# Syntax: -arg1=value1;-arg2=value2;... +# Example value: -Dmixin.debug.verify=true;-XX:+UnlockExperimentalVMOptions +additionalJavaArguments = + +# Enables using modern java syntax (up to version 17) via Jabel, while still targeting JVM 8. +# See https://github.com/bsideup/jabel for details on how this works. +# Using this requires that you use a Java 17 JDK for development. +enableModernJavaSyntax = true + +# Enables runClient/runServer tasks for Java 17 and Java 21 using LWJGL3ify. +# This is primarily used to test if your mod is compatible with platforms running +# Minecraft 1.12.2 on modern versions of Java and LWJGL, and assist in fixing any problems with it. +# Using this requires that you use a Java 17/Java 21 JDK for development. +enableJava17RunTasks = false + +# Generate a class with String fields for the mod id, name and version named with the fields below +generateGradleTokenClass = mod.acgaming.universaltweaks.Tags +gradleTokenModId = MOD_ID +gradleTokenModName = MOD_NAME +gradleTokenVersion = VERSION + +# In case your mod provides an API for other mods to implement you may declare its package here. Otherwise, you can +# leave this property empty. +# Example value: apiPackage = api + modGroup = com.myname.mymodid -> com.myname.mymodid.api +apiPackage = + +# If you want to keep your API code in src/api instead of src/main +useSrcApiPath = false + +# Specify the configuration file for Forge's access transformers here. It must be placed into /src/main/resources/ +# There can be multiple files in a comma-separated list. +# Example value: mymodid_at.cfg,jei_at.cfg +accessTransformersFile = universaltweaks_at.cfg + +# Provides setup for Mixins if enabled. If you don't know what mixins are: Keep it disabled! +usesMixins = true +# Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail! +mixinsPackage = . +# Location of the mixin config refmap. If left, blank, defaults to "mixins.${modId}.refmap.json". Target file must have the "json" extension. +mixinConfigRefmap = universaltweaks.refmap.json +# Automatically generates a mixin config json if enabled, with the name mixins.modid.json +generateMixinConfig = false +# Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin! +# Example value: coreModClass = asm.FMLPlugin + modGroup = com.myname.mymodid -> com.myname.mymodid.asm.FMLPlugin +coreModClass = core.UTLoadingPlugin +# If your project is only a consolidation of mixins or a core mod and does NOT contain a 'normal' mod (meaning that +# there is no class annotated with @Mod) you want this to be true. When in doubt: leave it on false! +containsMixinsAndOrCoreModOnly = false + +# Enables Mixins even if this mod doesn't use them, useful if one of the dependencies uses mixins. +forceEnableMixins = false + +# Outputs pre-transformed and post-transformed loaded classes to run/CLASSLOADER_TEMP. Can be used in combination with +# diff to see exactly what your ASM or Mixins are changing in the target file. +# Optionally can be specified with the 'CORE_MOD_DEBUG' env var. Will output a lot of files! +enableCoreModDebug = false -# If any properties changes below this line, run `gradlew setupDecompWorkspace` and refresh gradle again to ensure everything is working correctly. +# Adds CurseMaven, Modrinth Maven, BlameJared maven, and some more well-known 1.12.2 repositories +includeWellKnownRepositories = true -# Boilerplate Options -use_mixins = true -use_coremod = true -use_assetmover = false +# Adds JEI and TheOneProbe to your development environment. Adds them as 'implementation', meaning they will +# be available at compiletime and runtime for your mod (in-game and in-code). +# Overrides the above setting to be always true, as these repositories are needed to fetch the mods +includeCommonDevEnvMods = true -# Access Transformer files should be in the root of `resources` folder and with the filename formatted as: `{mod_id}_at.cfg` -use_access_transformer = true +# Some mods require a specific forge version to launch in. When you need to use one of those mods as a dependency, +# and cannot launch with the forge version required, enable this to strip the forge version requirements from that mod. +# This will add 'strip-latest-forge-requirements' as 'runtimeOnlyNonPublishable'. +# Requires useMixins or forceEnableMixins to be true, as the mod uses mixins to function. +stripForgeRequirements = true -# Coremod Arguments -include_mod = true -coremod_plugin_class_name = mod.acgaming.universaltweaks.core.UTLoadingPlugin \ No newline at end of file + +# If enabled, you may use 'shadowCompile' for dependencies. They will be integrated in your jar. It is your +# responsibility check the licence and request permission for distribution, if required. +usesShadowedDependencies = false +# If disabled, won't remove unused classes from shaded dependencies. Some libraries use reflection to access +# their own classes, making the minimization unreliable. +minimizeShadowedDependencies = true +# If disabled, won't rename the shadowed classes. +relocateShadowedDependencies = true + +# Separate run directories into "run/client" for runClient task, and "run/server" for runServer task. +# Useful for debugging a server and client simultaneously. If not enabled, it will be in the standard location "run/" +separateRunDirectories = false + +# The display name format of versions published to Curse and Modrinth. $MOD_NAME and $VERSION are available variables. +# Default: $MOD_NAME \u2212 $VERSION. \u2212 is the minus character which looks much better than the hyphen minus on Curse. +versionDisplayFormat = $MOD_NAME \u2212 $VERSION + +# Publishing to modrinth requires you to set the MODRINTH_API_KEY environment variable to your current modrinth API token. + +# The project's ID on Modrinth. Can be either the slug or the ID. +# Leave this empty if you don't want to publish on Modrinth. +# Alternatively this can be set with the 'MODRINTH_PROJECT_ID' environment variable. +modrinthProjectId = + +# The project's relations on Modrinth. You can use this to refer to other projects on Modrinth. +# Syntax: scope1-type1:name1;scope2-type2:name2;... +# Where scope can be one of [required, optional, incompatible, embedded], +# type can be one of [project, version], +# and the name is the Modrinth project or version slug/id of the other mod. +# Example: required-project:jei;optional-project:top;incompatible-project:gregtech +modrinthRelations = + + +# Publishing to CurseForge requires you to set the CURSEFORGE_API_KEY environment variable to one of your CurseForge API tokens. + +# The project's numeric ID on CurseForge. You can find this in the About Project box. +# Leave this empty if you don't want to publish on CurseForge. +# Alternatively this can be set with the 'CURSEFORGE_PROJECT_ID' environment variable. +curseForgeProjectId = + +# The project's relations on CurseForge. You can use this to refer to other projects on CurseForge. +# Syntax: type1:name1;type2:name2;... +# Where type can be one of [requiredDependency, embeddedLibrary, optionalDependency, tool, incompatible], +# and the name is the CurseForge project slug of the other mod. +# Example: requiredDependency:railcraft;embeddedLibrary:cofhlib;incompatible:buildcraft +curseForgeRelations = + +# This project's release type on CurseForge and/or Modrinth +# Alternatively this can be set with the 'RELEASE_TYPE' environment variable. +# Allowed types: release, beta, alpha +releaseType = release + +# Generate a default changelog for releases. Requires git to be installed, as it uses it to generate a changelog of +# commits since the last tagged release. +generateDefaultChangelog = false + +# Prevent the source code from being published +noPublishedSources = false + + +# Publish to a custom maven location. Follows a few rules: +# Group ID can be set with the 'ARTIFACT_GROUP_ID' environment variable, default to 'project.group' +# Artifact ID can be set with the 'ARTIFACT_ID' environment variable, default to 'project.name' +# Version can be set with the 'RELEASE_VERSION' environment variable, default to 'modVersion' +# For maven credentials: +# Username is set with the 'MAVEN_USER' environment variable, default to "NONE" +# Password is set with the 'MAVEN_PASSWORD' environment variable, default to "NONE" +customMavenPublishUrl = + +# The group for maven artifacts. Defaults to the 'project.modGroup' until the last '.' (if any). +# So 'mymod' becomes 'mymod' and 'com.myname.mymodid' 'becomes com.myname' +mavenArtifactGroup = mod.acgaming + +# Enable spotless checks +# Enforces code formatting on your source code +# By default this will use the files found here: https://github.com/GregTechCEu/Buildscripts/tree/master/spotless +# to format your code. However, you can create your own version of these files and place them in your project's +# root directory to apply your own formatting options instead. +enableSpotless = false + +# Enable JUnit testing platform used for testing your code. +# Uses JUnit 5. See guide and documentation here: https://junit.org/junit5/docs/current/user-guide/ +enableJUnit = true + +# Deployment debug setting +# Uncomment this to test deployments to CurseForge and Modrinth +# Alternatively, you can set the 'DEPLOYMENT_DEBUG' environment variable. +deploymentDebug = false + + +# Gradle Settings +# Effectively applies the '--stacktrace' flag by default +org.gradle.logging.stacktrace = all +# Sets default memory used for gradle commands. Can be overridden by user or command line properties. +# This is required to provide enough memory for the Minecraft decompilation process. +org.gradle.jvmargs = -Xmx3G \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 37aef8d3..3499ded5 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/repositories.gradle b/repositories.gradle new file mode 100644 index 00000000..5436dca9 --- /dev/null +++ b/repositories.gradle @@ -0,0 +1,12 @@ +// Add any additional repositories for your dependencies here + +repositories { + maven { + name 'SpongePowered Maven' + url 'https://repo.spongepowered.org/maven' + } + maven { + name 'ChickenASM' + url 'https://nexus.covers1624.net/repository/maven-releases/' + } +} \ No newline at end of file diff --git a/settings.gradle b/settings.gradle index 57c8fcdb..771def37 100644 --- a/settings.gradle +++ b/settings.gradle @@ -4,6 +4,7 @@ pluginManagement { // RetroFuturaGradle name 'GTNH Maven' url 'https://nexus.gtnewhorizons.com/repository/public/' + //noinspection GroovyAssignabilityCheck mavenContent { includeGroup 'com.gtnewhorizons' includeGroup 'com.gtnewhorizons.retrofuturagradle' @@ -16,10 +17,14 @@ pluginManagement { } plugins { + id 'com.diffplug.blowdryerSetup' version '1.7.0' // Automatic toolchain provisioning - id 'org.gradle.toolchains.foojay-resolver-convention' version '0.4.0' + id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0' +} + +blowdryerSetup { + repoSubfolder 'spotless' + github 'GregTechCEu/Buildscripts', 'tag', 'v1.0.7' } -// Due to an IntelliJ bug, this has to be done -// rootProject.name = mod_id rootProject.name = rootProject.projectDir.getName() diff --git a/src/main/java/mod/acgaming/universaltweaks/mods/mekanism/mixin/UTContainerPersonalChestMixin.java b/src/main/java/mod/acgaming/universaltweaks/mods/mekanism/mixin/UTContainerPersonalChestMixin.java index de2bfabd..18ffe86f 100644 --- a/src/main/java/mod/acgaming/universaltweaks/mods/mekanism/mixin/UTContainerPersonalChestMixin.java +++ b/src/main/java/mod/acgaming/universaltweaks/mods/mekanism/mixin/UTContainerPersonalChestMixin.java @@ -34,13 +34,13 @@ protected UTContainerPersonalChestMixin(TileEntityPersonalChest tileEntityPerson @Override public boolean canInteractWith(EntityPlayer player) { - if (isBlock) return super.func_75145_c(player); + if (isBlock) return super.canInteractWith(player); final ItemStack currentItem = player.getHeldItemMainhand(); if (itemInventory instanceof InventoryPersonalChest) { final ItemStack stack = ((InventoryPersonalChest) itemInventory).getStack(); - return super.func_75145_c(player) && !stack.isEmpty() && currentItem == stack && BlockStateMachine.MachineType.get(stack) == BlockStateMachine.MachineType.PERSONAL_CHEST; + return super.canInteractWith(player) && !stack.isEmpty() && currentItem == stack && BlockStateMachine.MachineType.get(stack) == BlockStateMachine.MachineType.PERSONAL_CHEST; } return false; }