diff --git a/build.gradle.kts b/build.gradle.kts index 8cb7312..13534e8 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,6 +13,13 @@ repositories { gradlePluginPortal() maven("https://maven.fabricmc.net/") maven("https://maven.neoforged.net/releases/") + maven{ + url = uri("https://nexus.gtnewhorizons.com/repository/public/") + mavenContent { + includeGroupByRegex("com\\.gtnewhorizons\\..+") + includeGroup("com.gtnewhorizons") + } + } } fun String.capitalize(): String { @@ -54,12 +61,12 @@ dependencies { implementation(plugin("net.neoforged.moddev", prop = "deps.moddevgradle")) implementation(plugin("net.neoforged.moddev.legacyforge", prop = "deps.moddevgradle")) implementation(plugin("me.modmuss50.mod-publish-plugin", prop = "deps.mpp")) - implementation(plugin("com.gradleup.shadow", prop = "deps.shadow")) - + implementation(plugin("com.gtnewhorizons.retrofuturagradle", prop = "deps.retrofuturagradle")) // Libraries used within the plugin implementation("com.google.code.gson:gson:2.11.0") implementation("com.electronwill.night-config:toml:3.8.1") implementation("org.semver4j:semver4j:5.5.0") + implementation("net.neoforged:srgutils:1.0.11") // Libraries used for testing diff --git a/gradle.properties b/gradle.properties index 8131a3c..710082e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,4 +4,4 @@ org.gradle.jvmargs=-Xmx4G deps.loom=1.14.9 deps.moddevgradle=2.0.137 deps.mpp=0.8.4 -deps.shadow=8.3.6 +deps.retrofuturagradle=2.0.2 diff --git a/src/main/kotlin/dev/isxander/modstitch/base/BaseCommonImpl.kt b/src/main/kotlin/dev/isxander/modstitch/base/BaseCommonImpl.kt index 9dbd967..1c410cf 100644 --- a/src/main/kotlin/dev/isxander/modstitch/base/BaseCommonImpl.kt +++ b/src/main/kotlin/dev/isxander/modstitch/base/BaseCommonImpl.kt @@ -1,14 +1,11 @@ package dev.isxander.modstitch.base -import dev.isxander.modstitch.* -import dev.isxander.modstitch.base.extensions.* -import dev.isxander.modstitch.util.Platform -import dev.isxander.modstitch.util.afterSuccessfulEvaluate -import dev.isxander.modstitch.util.mainSourceSet -import dev.isxander.modstitch.util.platform -import dev.isxander.modstitch.util.printVersion +import dev.isxander.modstitch.PlatformPlugin +import dev.isxander.modstitch.base.extensions.ModstitchExtension +import dev.isxander.modstitch.base.extensions.ModstitchExtensionImpl +import dev.isxander.modstitch.base.extensions.modstitch +import dev.isxander.modstitch.util.* import org.gradle.api.Action -import org.gradle.api.JavaVersion import org.gradle.api.Project import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.dsl.RepositoryHandler @@ -20,6 +17,7 @@ import org.gradle.api.tasks.SourceSetContainer import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.compile.JavaCompile import org.gradle.api.tasks.testing.junitplatform.JUnitPlatformOptions +import org.gradle.jvm.toolchain.JavaLanguageVersion import org.gradle.kotlin.dsl.* import org.gradle.language.jvm.tasks.ProcessResources import org.gradle.plugins.ide.idea.model.IdeaModel @@ -75,7 +73,10 @@ abstract class BaseCommonImpl( applyMetadataStringReplacements(target) // Create modstitch remap configurations - createProxyConfigurations(target, target.extensions.getByType().getByName(SourceSet.MAIN_SOURCE_SET_NAME)) + createProxyConfigurations( + target, + target.extensions.getByType().getByName(SourceSet.MAIN_SOURCE_SET_NAME) + ) // Jar-in-jar support target.configurations.create("modstitchJiJ") { @@ -89,8 +90,10 @@ abstract class BaseCommonImpl( group = "modstitch/internal" dependsOn("processResources") - source(msExt.modLoaderManifest.map { listOf(project.mainSourceSet!!.output.resourcesDir!!.resolve(it)) }.orElse(listOf())) - mixins.value(target.provider { msExt.mixin.configs.map { it.resolved() } }.zip(msExt.mixin.addMixinsToModManifest) { configs, addToManifest -> if (!addToManifest) emptyList() else configs }) + source(msExt.modLoaderManifest.map { listOf(project.mainSourceSet!!.output.resourcesDir!!.resolve(it)) } + .orElse(listOf())) + mixins.value(target.provider { msExt.mixin.configs.map { it.resolved() } } + .zip(msExt.mixin.addMixinsToModManifest) { configs, addToManifest -> if (!addToManifest) emptyList() else configs }) classTweakers.value(msExt.classTweakerName.zip(msExt.classTweaker) { n, _ -> listOf(n) }.orElse(listOf())) }.also { target.tasks["processResources"].finalizedBy(it) } } @@ -147,17 +150,13 @@ abstract class BaseCommonImpl( } target.extensions.configure { - target.afterSuccessfulEvaluate { - val requestedJavaVersion = target.modstitch.javaVersion.orNull - - if (requestedJavaVersion != null) { - JavaVersion.toVersion(requestedJavaVersion).let { - targetCompatibility = it - sourceCompatibility = it - } - } else { - target.logger.warn("No Java version specified in modstitch configuration. Not applying any Java version settings.") - } + toolchain.languageVersion.set( + target.modstitch.javaVersion.map { JavaLanguageVersion.of(it) } + ) + } + target.afterSuccessfulEvaluate { + if (!target.modstitch.javaVersion.isPresent) { + target.logger.warn("No Java version specified in modstitch configuration. Not applying any Java version settings.") } } } @@ -244,7 +243,12 @@ abstract class BaseCommonImpl( createProxyConfigurations(target, FutureNamedDomainObjectProvider.from(target.configurations, it)) } } - abstract fun createProxyConfigurations(target: Project, configuration: FutureNamedDomainObjectProvider, defer: Boolean = false) + + abstract fun createProxyConfigurations( + target: Project, + configuration: FutureNamedDomainObjectProvider, + defer: Boolean = false, + ) abstract fun configureJiJConfiguration(target: Project, configuration: Configuration) diff --git a/src/main/kotlin/dev/isxander/modstitch/base/BasePlugin.kt b/src/main/kotlin/dev/isxander/modstitch/base/BasePlugin.kt index 83605d0..a118c48 100644 --- a/src/main/kotlin/dev/isxander/modstitch/base/BasePlugin.kt +++ b/src/main/kotlin/dev/isxander/modstitch/base/BasePlugin.kt @@ -7,6 +7,7 @@ import dev.isxander.modstitch.base.loom.BaseLoomImpl import dev.isxander.modstitch.base.loom.LoomType import dev.isxander.modstitch.base.moddevgradle.BaseModDevGradleImpl import dev.isxander.modstitch.base.moddevgradle.MDGType +import dev.isxander.modstitch.base.retrofuturagradle.BaseRetroFuturaGradleImpl import dev.isxander.modstitch.util.RegisteredGradlePlugin @RegisteredGradlePlugin @@ -17,6 +18,7 @@ class BasePlugin : ModstitchExtensionPlugin("base", platforms) { Platform.LoomRemap to BaseLoomImpl(LoomType.Remap), Platform.MDG to BaseModDevGradleImpl(MDGType.Regular), Platform.MDGLegacy to BaseModDevGradleImpl(MDGType.Legacy), + Platform.RFG to BaseRetroFuturaGradleImpl() ) } } diff --git a/src/main/kotlin/dev/isxander/modstitch/base/extensions/Modstitch.kt b/src/main/kotlin/dev/isxander/modstitch/base/extensions/Modstitch.kt index ffa4a54..2accb81 100644 --- a/src/main/kotlin/dev/isxander/modstitch/base/extensions/Modstitch.kt +++ b/src/main/kotlin/dev/isxander/modstitch/base/extensions/Modstitch.kt @@ -3,6 +3,7 @@ package dev.isxander.modstitch.base.extensions import dev.isxander.modstitch.base.* import dev.isxander.modstitch.base.loom.BaseLoomExtension import dev.isxander.modstitch.base.moddevgradle.BaseModDevGradleExtension +import dev.isxander.modstitch.base.retrofuturagradle.BaseRetroFuturaGradleExtension import dev.isxander.modstitch.util.* import org.gradle.api.Action import org.gradle.api.NamedDomainObjectContainer @@ -28,6 +29,10 @@ interface ModstitchExtension { * - When target platform is `loom`, this property is equivalent to `loom.fabricLoaderVersion`. * - When target platform is `moddevgradle`, this property is equivalent to `moddevgradle.neoForgeVersion`. * - When target platform is `moddevgradle-legacy`, this property is equivalent to `moddevgradle.forgeVersion`. + * - When target platform is `retrofuturagradle`, this property is equivalent to `retrofuturagradle.forgeVersion`. + * + * On retrofuturagradle, the property only serves as a check, because retrofuturagradle forces specific forge + * versions */ val modLoaderVersion: Property @@ -213,6 +218,12 @@ interface ModstitchExtension { */ fun moddevgradle(action: Action) {} + /** + * Configures the RetroFuturaGradle extension + * The action is only executed if the active platform is RetroFuturaGradle + */ + fun retrofuturagradle(action: Action) {} + val templatesSourceDirectorySet: SourceDirectorySet /** @@ -232,6 +243,7 @@ open class ModstitchExtensionImpl @Inject constructor( Platform.Loom, Platform.LoomRemap -> project.extensions.getByType().fabricLoaderVersion Platform.MDG -> project.extensions.getByType().neoForgeVersion Platform.MDGLegacy -> project.extensions.getByType().forgeVersion + Platform.RFG -> project.extensions.getByType().forgeVersion } override val minecraftVersion = objects.property() @@ -301,6 +313,7 @@ open class ModstitchExtensionImpl @Inject constructor( override fun loom(action: Action) = platformExtension(action) override fun moddevgradle(action: Action) = platformExtension(action) + override fun retrofuturagradle(action: Action) = platformExtension(action) private inline fun platformExtension(action: Action) { val platformExtension = plugin.platformExtension @@ -312,14 +325,12 @@ open class ModstitchExtensionImpl @Inject constructor( internal var _finalJarTaskName: String? = null set(value) { field = value - println("Final jar task set to $value") } override val finalJarTask: TaskProvider get() = _finalJarTaskName?.let { project.tasks.named(it) } ?: error("Final jar task not set") internal var _namedJarTaskName: String? = null set(value) { field = value - println("Named jar task set to $value") } override val namedJarTask: TaskProvider get() = _namedJarTaskName?.let { project.tasks.named(it) } ?: error("Named jar task not set") diff --git a/src/main/kotlin/dev/isxander/modstitch/base/loom/BaseLoomImpl.kt b/src/main/kotlin/dev/isxander/modstitch/base/loom/BaseLoomImpl.kt index ec0f075..10c5e6b 100644 --- a/src/main/kotlin/dev/isxander/modstitch/base/loom/BaseLoomImpl.kt +++ b/src/main/kotlin/dev/isxander/modstitch/base/loom/BaseLoomImpl.kt @@ -15,8 +15,7 @@ import org.gradle.api.tasks.TaskProvider import org.gradle.api.tasks.testing.Test import org.gradle.api.tasks.testing.junitplatform.JUnitPlatformOptions import org.gradle.kotlin.dsl.* -import org.gradle.kotlin.dsl.assign -import org.gradle.kotlin.dsl.getByType +import org.gradle.kotlin.dsl.accessors.runtime.maybeRegister import org.gradle.language.jvm.tasks.ProcessResources class BaseLoomImpl( @@ -162,10 +161,15 @@ class BaseLoomImpl( // loom run configs does not support gradle lazy evaluation target.afterSuccessfulEvaluate { - target.extensions.getByType().runs.register(modstitch.name) loom@{ + val loomExt = target.extensions.getByType() + maybeRegister(loomExt.runs, modstitch.name) loom@{ val loom = this@loom - modstitch.gameDirectory.orNull?.let { loom.runDir = it.asFile.absolutePath } + // loom uses a relative path from the project directory + modstitch.gameDirectory.orNull?.let { + loom.runDir = it + .asFile.relativeTo(target.projectDir).path + } modstitch.mainClass.orNull?.let { loom.mainClass = it } modstitch.jvmArgs.orNull?.let { loom.vmArgs.addAll(it) } modstitch.programArgs.orNull?.let { loom.programArgs.addAll(it) } @@ -231,13 +235,20 @@ class BaseLoomImpl( target.loom.createRemapConfigurations(sourceSet) } } else { - createProxyConfigurations(target, FutureNamedDomainObjectProvider.from(target.configurations, Constants.Configurations.LOCAL_RUNTIME)) + createProxyConfigurations( + target, + FutureNamedDomainObjectProvider.from(target.configurations, Constants.Configurations.LOCAL_RUNTIME) + ) } super.createProxyConfigurations(target, sourceSet) } - override fun createProxyConfigurations(target: Project, configuration: FutureNamedDomainObjectProvider, defer: Boolean) { + override fun createProxyConfigurations( + target: Project, + configuration: FutureNamedDomainObjectProvider, + defer: Boolean, + ) { if (defer) error("Cannot defer proxy configuration creation in Loom") // for no-remap, this "remap configuration" is just the regular configuration @@ -247,19 +258,30 @@ class BaseLoomImpl( .find { it.targetConfigurationName.get() == configuration.name } ?.name ?: error("Loom has not created a remap configuration for ${configuration.name}, modstitch cannot proxy it.") + LoomType.NoRemap -> configuration.name } val proxyModConfigurationName = configuration.name.addCamelCasePrefix("modstitchMod") val proxyRegularConfigurationName = configuration.name.addCamelCasePrefix("modstitch") - target.configurations.create(proxyModConfigurationName) proxy@{ - target.configurations.named(remapConfigurationName) { - extendsFrom(this@proxy) - } + + val proxyModConf = target.configurations.register(proxyModConfigurationName) proxy@{ + isCanBeResolved = false + isCanBeConsumed = false + isCanBeDeclared = true } - target.configurations.create(proxyRegularConfigurationName) proxy@{ + + target.configurations.named(remapConfigurationName) { + extendsFrom(proxyModConf.get()) + } + + target.configurations.register(proxyRegularConfigurationName) proxy@{ configuration.get().extendsFrom(this@proxy) + isCanBeResolved = false + isCanBeConsumed = false + isCanBeDeclared = true + } } diff --git a/src/main/kotlin/dev/isxander/modstitch/base/moddevgradle/BaseModDevGradleImpl.kt b/src/main/kotlin/dev/isxander/modstitch/base/moddevgradle/BaseModDevGradleImpl.kt index 4b37074..a98284c 100644 --- a/src/main/kotlin/dev/isxander/modstitch/base/moddevgradle/BaseModDevGradleImpl.kt +++ b/src/main/kotlin/dev/isxander/modstitch/base/moddevgradle/BaseModDevGradleImpl.kt @@ -287,10 +287,13 @@ class BaseModDevGradleImpl( return target.afterSuccessfulEvaluate { action(configuration.get()) } } - target.configurations.create(proxyModConfigurationName) proxy@{ + target.configurations.register(proxyModConfigurationName) proxy@{ deferred { it.extendsFrom(this@proxy) } + isCanBeResolved = false + isCanBeConsumed = false + isCanBeDeclared = true target.afterSuccessfulEvaluate { if (type == MDGType.Legacy) { @@ -299,10 +302,13 @@ class BaseModDevGradleImpl( } } - target.configurations.create(proxyRegularConfigurationName) proxy@{ + target.configurations.register(proxyRegularConfigurationName) proxy@{ deferred { it.extendsFrom(this@proxy) } + isCanBeResolved = false + isCanBeConsumed = false + isCanBeDeclared = true target.afterSuccessfulEvaluate { target.configurations.named("additionalRuntimeClasspath") { diff --git a/src/main/kotlin/dev/isxander/modstitch/base/retrofuturagradle/AppendRFGMetadataTask.kt b/src/main/kotlin/dev/isxander/modstitch/base/retrofuturagradle/AppendRFGMetadataTask.kt new file mode 100644 index 0000000..d5557ae --- /dev/null +++ b/src/main/kotlin/dev/isxander/modstitch/base/retrofuturagradle/AppendRFGMetadataTask.kt @@ -0,0 +1,9 @@ +package dev.isxander.modstitch.base.retrofuturagradle + +import dev.isxander.modstitch.base.AppendModMetadataTask +import java.io.File + +abstract class AppendRFGMetadataTask : AppendModMetadataTask() { + override fun appendModMetadata(file: File) { + } +} diff --git a/src/main/kotlin/dev/isxander/modstitch/base/retrofuturagradle/BaseRetroFuturaGradleExtension.kt b/src/main/kotlin/dev/isxander/modstitch/base/retrofuturagradle/BaseRetroFuturaGradleExtension.kt new file mode 100644 index 0000000..01f068a --- /dev/null +++ b/src/main/kotlin/dev/isxander/modstitch/base/retrofuturagradle/BaseRetroFuturaGradleExtension.kt @@ -0,0 +1,127 @@ +package dev.isxander.modstitch.base.retrofuturagradle + +import com.gtnewhorizons.retrofuturagradle.MinecraftExtension +import dev.isxander.modstitch.base.extensions.ModstitchExtension +import dev.isxander.modstitch.util.MinecraftVersion +import dev.isxander.modstitch.util.NotExistsDelegate +import org.gradle.api.Action +import org.gradle.api.Project +import org.gradle.api.model.ObjectFactory +import org.gradle.api.provider.ListProperty +import org.gradle.api.provider.Property +import org.gradle.kotlin.dsl.getByType +import org.gradle.kotlin.dsl.invoke +import org.gradle.kotlin.dsl.listProperty +import org.gradle.kotlin.dsl.property +import javax.inject.Inject + +interface BaseRetroFuturaGradleExtension { + /** + * The version of Forge to use + * + * This property is actually only a verification, RetroFuturaGradle only accepts + * a single forge version for 1.7.10, and another specific one for 1.12.2 + */ + val forgeVersion: Property + + /** + * The version of MCP to use + */ + val mcpVersion: Property + + /** + * The MCP channel to use + */ + val mcpChannel: Property + + /** + * Sets the FMLCorePlugin manifest attribute + */ + val coreModClassName: Property + + /** + * Sets the FMLCorePluginContainsFMLMod manifest attribute + */ + val hasModAndCoreMod: Property + + /** + * The required dependencies for mixins to work + * + * These will be UniMixins on 1.7.10, and MixinBooter on 1.12.2 + * + * This property is provided to filter them out when shading, since they are mod dependencies + * and will have to be provided at runtime + */ + val mixinsDependencies: ListProperty + + /** + * The underlying platform-specific extension + * + * Attempting to access this property on another platform than `retrofuturagradle` + * will throw an error + */ + val minecraft: MinecraftExtension + + /** + * Configures the Minecraft extension + * + * This action will only be executed on the `retrofuturagradle` platform + */ + fun minecraft(action: Action) + +} + +open class BaseRetroFuturaGradleExtensionImpl @Inject constructor( + objects: ObjectFactory, + @Transient private val project: Project, +) : BaseRetroFuturaGradleExtension { + override val forgeVersion: Property = objects.property() + override val mcpVersion: Property = objects.property() + override val mcpChannel: Property = objects.property() + override val coreModClassName: Property = objects.property() + override val hasModAndCoreMod: Property = objects.property() + override val minecraft: MinecraftExtension + get() = project.extensions.getByType() + override val mixinsDependencies: ListProperty = objects.listProperty() + + override fun minecraft(action: Action) = action(minecraft) + + init { + forgeVersion.finalizeValueOnRead() + mcpVersion.finalizeValueOnRead() + mcpChannel.finalizeValueOnRead() + coreModClassName.finalizeValueOnRead() + hasModAndCoreMod.finalizeValueOnRead() + mixinsDependencies.finalizeValueOnRead() + val mixinBooterDeps = listOf( + "zone.rong:mixinbooter:10.7", + "org.ow2.asm:asm-debug-all:5.2" + ) + val unimixinsMixin = "com.github.LegacyModdingMC.UniMixins:unimixins-all-1.7.10:0.2.1" + val mixinDependency = project.provider { + project.extensions.getByType() + }.flatMap { + it.minecraftVersion + }.map { + MinecraftVersion.Companion.parseOrderableOrNull(it)?.let { + if (it == MinecraftVersion.LegacyRelease(12, 2)) { + mixinBooterDeps + } else if (it == MinecraftVersion.LegacyRelease(7, 10)) { + listOf(unimixinsMixin) + } else null + } + } + mixinsDependencies.convention(mixinDependency) + } +} + +open class BaseRetroFuturaGradleExtensionDummy : BaseRetroFuturaGradleExtension { + override val forgeVersion: Property by NotExistsDelegate() + override val mcpVersion: Property by NotExistsDelegate() + override val mcpChannel: Property by NotExistsDelegate() + override val coreModClassName: Property by NotExistsDelegate() + override val hasModAndCoreMod: Property by NotExistsDelegate() + override val minecraft: MinecraftExtension by NotExistsDelegate() + override val mixinsDependencies: ListProperty by NotExistsDelegate() + override fun minecraft(action: Action) {} +} diff --git a/src/main/kotlin/dev/isxander/modstitch/base/retrofuturagradle/BaseRetroFuturaGradleImpl.kt b/src/main/kotlin/dev/isxander/modstitch/base/retrofuturagradle/BaseRetroFuturaGradleImpl.kt new file mode 100644 index 0000000..1d89dd6 --- /dev/null +++ b/src/main/kotlin/dev/isxander/modstitch/base/retrofuturagradle/BaseRetroFuturaGradleImpl.kt @@ -0,0 +1,347 @@ +package dev.isxander.modstitch.base.retrofuturagradle + +import com.gtnewhorizons.retrofuturagradle.MinecraftExtension +import com.gtnewhorizons.retrofuturagradle.UserDevPlugin +import com.gtnewhorizons.retrofuturagradle.mcp.JSTTransformerTask +import com.gtnewhorizons.retrofuturagradle.mcp.MCPTasks +import com.gtnewhorizons.retrofuturagradle.minecraft.RunMinecraftTask +import com.gtnewhorizons.retrofuturagradle.modutils.ModUtils +import com.gtnewhorizons.retrofuturagradle.util.Distribution +import dev.isxander.modstitch.base.BaseCommonImpl +import dev.isxander.modstitch.base.FutureNamedDomainObjectProvider +import dev.isxander.modstitch.base.extensions.ModstitchExtension +import dev.isxander.modstitch.base.extensions.modstitch +import dev.isxander.modstitch.base.moddevgradle.GenerateAccessTransformerTask +import dev.isxander.modstitch.util.* +import net.neoforged.srgutils.IMappingFile +import org.gradle.api.Action +import org.gradle.api.Project +import org.gradle.api.artifacts.Configuration +import org.gradle.api.artifacts.FileCollectionDependency +import org.gradle.api.artifacts.dsl.RepositoryHandler +import org.gradle.api.internal.tasks.JvmConstants +import org.gradle.api.plugins.JavaPluginExtension +import org.gradle.api.provider.Property +import org.gradle.api.provider.Provider +import org.gradle.api.tasks.TaskProvider +import org.gradle.api.tasks.testing.junitplatform.JUnitPlatformOptions +import org.gradle.jvm.tasks.Jar +import org.gradle.jvm.toolchain.JvmVendorSpec +import org.gradle.kotlin.dsl.* +import org.gradle.language.jvm.tasks.ProcessResources + +class BaseRetroFuturaGradleImpl : BaseCommonImpl( + Platform.RFG, + AppendRFGMetadataTask::class.java +) { + + override val platformExtensionInfo: PlatformExtensionInfo = PlatformExtensionInfo( + "msRetroFuturaGradle", + BaseRetroFuturaGradleExtension::class, + BaseRetroFuturaGradleExtensionImpl::class, + BaseRetroFuturaGradleExtensionDummy::class + ) + + override fun applyPlugins(target: Project) { + super.applyPlugins(target) + target.pluginManager.apply(UserDevPlugin::class.java) + } + + override fun applyDefaultRepositories(repositories: RepositoryHandler) { + super.applyDefaultRepositories(repositories) + + repositories.maven("https://jitpack.io") { + name = "JitPack UniMixins" + mavenContent { + includeGroup("com.github.LegacyModdingMC.UniMixins") + } + } + repositories.maven("https://maven.cleanroommc.com") { + name = "MixinBooter" + mavenContent { + includeModule("zone.rong", "mixinbooter") + } + } + } + + override fun applyJavaSettings(target: Project) { + super.applyJavaSettings(target) + target.extensions.configure { + toolchain { + // https://github.com/MinecraftForge/ForgeGradle/issues/597 + // Important for stable decompilation output or else you get failed + // patches with: cannot find hunk target + vendor.set(JvmVendorSpec.AZUL) + } + } + } + + override fun apply(target: Project) { + val ext = createRealPlatformExtension(target)!! + super.apply(target) + val modstitch = target.modstitch + + modstitch._namedJarTaskName = JvmConstants.JAR_TASK_NAME + modstitch._finalJarTaskName = "reobfJar" + + modstitch.modLoaderManifest.convention(platform.modManifest) + val minecraft = target.extensions.getByType() + minecraft.mcVersion.set(modstitch.minecraftVersion) + minecraft.mcpMappingChannel.set(ext.mcpChannel) + minecraft.mcpMappingVersion.set(ext.mcpVersion) + minecraft.extraRunJvmArguments.addAll( + ext.coreModClassName.map { + listOf("-Dfml.coreMods.load=$it") + }.orElse(emptyList()) + ) + + configureLegacyMixin(target) + + applyRuns(target) + } + + override fun applyClassTweaker(target: Project) { + val modstitch = target.extensions.getByType() + val defaultAccessTransformerName = modstitch.metadata.modId.map { "META-INF/${it}_at.cfg" } + val generatedAccessTransformer = defaultAccessTransformerName.flatMap { + target.layout.buildDirectory + .file("modstitch/$it") + }.zip(modstitch.classTweaker) { x, _ -> x } + val generatedAccessTransformersList = generatedAccessTransformer.map { listOf(it) }.orElse(listOf()) + val classTweakerName = modstitch.classTweakerName.convention(defaultAccessTransformerName) + .map { + if (!it.startsWith("META-INF")) { + error("Access transformer name must be placed in META-INF/") + } else it + } + val classTweakerPath = classTweakerName.map { it.split('\\', '/') } + modstitch.classTweaker.finalizeValueOnRead() + modstitch.classTweakerName.finalizeValueOnRead() + modstitch.validateClassTweaker.finalizeValueOnRead() + + + val mcpTasks = target.extensions.getByType() + + // Convert's RetroFuturaGradle's SRG mappings into TSRG for the access transformer conversion + val convertMappingsTask = target.tasks.register("convertMappingsForAccessTransformers") { + sourceMappingsFile.set(mcpTasks.taskGenerateForgeSrgMappings.flatMap { it.mcpToSrg }) + targetFormat.set(IMappingFile.Format.TSRG) + convertedFile.set(target.layout.buildDirectory.file("generated/convertedMappings.txt")) + } + + val generateAccessTransformerTask = + target.tasks.register("generateAccessTransformer") { + group = "modstitch/internal" + description = "Generates an access transformer." + classTweaker.set(modstitch.classTweaker) + mappings.set( + convertMappingsTask.flatMap { it.convertedFile } + ) + accessTransformer.set(generatedAccessTransformer) + } + + target.tasks.named("applyJST") { + this.accessTransformerFiles.setFrom(generateAccessTransformerTask) + } + + target.tasks.named("processResources") { + dependsOn(generateAccessTransformerTask) + from(generatedAccessTransformersList) { + rename { classTweakerPath.get().last() } + into(classTweakerPath.map { it.dropLast(1).joinToString("/") }) + } + } + + // Sets the name of the access transformer files in the jar manifest + target.tasks.named("jar") { + manifest { + attributes["FMLAT"] = classTweakerPath.get().last() + } + } + + } + + private fun applyRuns(target: Project) { + val modstitch = target.extensions.getByType() + val ext = target.extensions.getByType() + + + modstitch.runs.whenObjectAdded { + val config = this + val taskName = "run${config.name.replaceFirstChar(Char::uppercaseChar)}" + + modstitch.onEnable { + config.side.finalizeValueOnRead() + target.tasks.maybeRegister( + taskName, + config.side.map { + when (it) { + Side.Both -> error("Unknown side for RetroFuturaGradle: $it") + Side.Client -> Distribution.CLIENT + Side.Server -> Distribution.DEDICATED_SERVER + } + } + ) { + group = "modstitch/runs" + config.gameDirectory.finalizeValueOnRead() + config.gameDirectory.orNull?.let { + this.workingDir = it.asFile + } + config.mainClass.orNull?.let { + this.mainClass.set(it) + } + this.jvmArguments.addAll(config.jvmArgs) + this.extraArgs.addAll(config.programArgs) + this.environment.putAll(config.environmentVariables.get()) + } + val bool = target.gradle.startParameter.taskNames.firstOrNull() == "build" + target.tasks.named("jar") { + manifest { + val attributeMap = mutableMapOf() + val coreModClassName = ext.coreModClassName.orNull + if (coreModClassName != null) { + attributeMap["FMLCorePlugin"] = coreModClassName + if (ext.hasModAndCoreMod.get()) { + attributeMap["FMLCorePluginContainsFMLMod"] = true.toString() + attributeMap["ForceLoadAsMod"] = bool.toString() + } + attributes.putAll(attributeMap) + } + } + } + } + } + + } + + override fun finalize(target: Project) { + val ext = target.extensions.getByType() + val minecraft = target.extensions.getByType() + check(minecraft.forgeVersion.get() == ext.forgeVersion.get()) { + "Unsupported forge version ${ext.forgeVersion.get()} for RetroFuturaGradle, expected ${minecraft.forgeVersion.get()} " + + "RetroFuturaGradle only supports 1 version for 1.7.10 and another one for 1.12.2" + } + super.finalize(target) + } + + override fun applyUnitTesting(target: Project, testFrameworkConfigure: Action) { + throw UnsupportedOperationException("RetroFuturaGradle does not support unit testing") + } + + override fun createProxyConfigurations( + target: Project, + configuration: FutureNamedDomainObjectProvider, + defer: Boolean, + ) { + val proxyModConfigurationName = configuration.name.addCamelCasePrefix("modstitchMod") + val proxyRegularConfigurationName = configuration.name.addCamelCasePrefix("modstitch") + + // already created + if (target.configurations.find { it.name == proxyModConfigurationName } != null) { + return + } + fun deferred(action: (Configuration) -> Unit) { + if (!defer) return action(configuration.get()) + return target.afterSuccessfulEvaluate { action(configuration.get()) } + } + + val rfg = target.dependencies.extensions.getByType() + target.configurations.register(proxyModConfigurationName) proxy@{ + deferred { + it.extendsFrom(this@proxy) + } + + isCanBeResolved = false + isCanBeConsumed = false + isCanBeDeclared = true + + dependencies.configureEach { + if (this is FileCollectionDependency) { + rfg.deobf(this.files) + } else { + rfg.deobf( + mapOf( + "group" to this.group.orEmpty(), + "name" to this.name.orEmpty(), + "version" to this.version.orEmpty(), + //todo: RFG also checks the classifier but IDK how to get it from here + ) + ) + } + } + } + + + + target.configurations.register(proxyRegularConfigurationName) proxy@{ + deferred { + it.extendsFrom(this@proxy) + } + isCanBeResolved = false + isCanBeConsumed = false + isCanBeDeclared = true + + } + } + + override fun configureJiJConfiguration(target: Project, configuration: Configuration) { + target.afterSuccessfulEvaluate { + configuration.dependencies.whenObjectAdded { + error("RetroFuturaGradle does not support JarInJar, please use the 'modstitch' configuration instead") + } + } + } + + + private fun configureLegacyMixin(target: Project) { + val modstitch = target.extensions.getByType() + val ext = target.extensions.getByType() + val stitchedMixin = modstitch.mixin + ext.mixinsDependencies.finalizeValueOnRead() + + addMixinDependencies(target, ext.mixinsDependencies) + val modUtils = target.extensions.getByType() + + // https://github.com/GTNewHorizons/RetroFuturaGradle/issues/95 + @Suppress("UNCHECKED_CAST") + val mixinRefMap = ModUtils::class.java.getDeclaredField("mixinRefMap").apply { + isAccessible = true + }.get(modUtils) as Property + mixinRefMap.set(modstitch.metadata.modId.map { "$it.refmap.json" }) + + + stitchedMixin.mixinSourceSets.whenObjectAdded obj@{ + modUtils.mixinSourceSet.set(target.sourceSets[this.sourceSetName.get()]) + } + target.afterEvaluate { + if (stitchedMixin.mixinSourceSets.size > 1) { + //technically it does but we'll see later + error("RetroFuturaGradle does not support multiple mixin source sets") + } + if (stitchedMixin.configs.size > 1) { + error("RetroFuturaGradle does not support multiple mixin configs") + } + } + } + + override fun onEnable(target: Project, action: Action) { + target.afterSuccessfulEvaluate(action) + } + + private fun addMixinDependencies( + target: Project, + dependencies: Provider>, + ) { + target.afterSuccessfulEvaluate { + target.dependencies { + val implementation = target.configurations.named(JvmConstants.IMPLEMENTATION_CONFIGURATION_NAME) + val annotationProcessor = + target.configurations.getByName(JvmConstants.ANNOTATION_PROCESSOR_CONFIGURATION_NAME) + dependencies.get().forEach { + implementation(it) + annotationProcessor(it) + } + } + } + } +} diff --git a/src/main/kotlin/dev/isxander/modstitch/base/retrofuturagradle/ConvertMappingsTask.kt b/src/main/kotlin/dev/isxander/modstitch/base/retrofuturagradle/ConvertMappingsTask.kt new file mode 100644 index 0000000..decf99f --- /dev/null +++ b/src/main/kotlin/dev/isxander/modstitch/base/retrofuturagradle/ConvertMappingsTask.kt @@ -0,0 +1,34 @@ +package dev.isxander.modstitch.base.retrofuturagradle + +import net.neoforged.srgutils.IMappingFile +import org.gradle.api.DefaultTask +import org.gradle.api.file.RegularFileProperty +import org.gradle.api.provider.Property +import org.gradle.api.tasks.CacheableTask +import org.gradle.api.tasks.Input +import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.OutputFile +import org.gradle.api.tasks.PathSensitive +import org.gradle.api.tasks.PathSensitivity +import org.gradle.api.tasks.TaskAction + +@CacheableTask +abstract class ConvertMappingsTask : DefaultTask() { + + @get:InputFile + @get:PathSensitive(PathSensitivity.RELATIVE) + abstract val sourceMappingsFile: RegularFileProperty + + @get:Input + abstract val targetFormat: Property + + @get:OutputFile + abstract val convertedFile: RegularFileProperty + + @TaskAction + fun convert() { + val loadedMappings = IMappingFile.load(this.sourceMappingsFile.get().asFile) + loadedMappings.write(convertedFile.get().asFile.toPath(), targetFormat.get(), false) + } + +} diff --git a/src/main/kotlin/dev/isxander/modstitch/util/ClassTweaker.kt b/src/main/kotlin/dev/isxander/modstitch/util/ClassTweaker.kt index ead8cfc..bb3af23 100644 --- a/src/main/kotlin/dev/isxander/modstitch/util/ClassTweaker.kt +++ b/src/main/kotlin/dev/isxander/modstitch/util/ClassTweaker.kt @@ -85,7 +85,8 @@ internal data class ClassTweaker( .append(' ') .append(entry.fieldName) .append(' ') - .appendLine(entry.fieldDescriptor ?: "") + .appendLine() + // Forge's AccessTransformers don't use field descriptors is ClassTweakerEntry.AccessModifier.Method -> writer .append(' ') .append(entry.methodName) diff --git a/src/main/kotlin/dev/isxander/modstitch/util/Platform.kt b/src/main/kotlin/dev/isxander/modstitch/util/Platform.kt index c93424a..fceed0d 100644 --- a/src/main/kotlin/dev/isxander/modstitch/util/Platform.kt +++ b/src/main/kotlin/dev/isxander/modstitch/util/Platform.kt @@ -6,7 +6,8 @@ enum class Platform(val friendlyName: String, val modManifest: String) { Loom("fabric-loom", "fabric.mod.json"), LoomRemap("fabric-loom-remap", "fabric.mod.json"), MDG("moddevgradle", "META-INF/neoforge.mods.toml"), - MDGLegacy("moddevgradle-legacy", "META-INF/mods.toml"); + MDGLegacy("moddevgradle-legacy", "META-INF/mods.toml"), + RFG("retrofuturagradle", "mcmod.info"); val isModDevGradle: Boolean get() = this in listOf(MDG, MDGLegacy) diff --git a/src/main/kotlin/dev/isxander/modstitch/util/ProjectUtils.kt b/src/main/kotlin/dev/isxander/modstitch/util/ProjectUtils.kt index 9fd01d2..1943c08 100644 --- a/src/main/kotlin/dev/isxander/modstitch/util/ProjectUtils.kt +++ b/src/main/kotlin/dev/isxander/modstitch/util/ProjectUtils.kt @@ -2,9 +2,12 @@ package dev.isxander.modstitch.util import org.gradle.api.Action import org.gradle.api.Project +import org.gradle.api.Task import org.gradle.api.provider.Provider import org.gradle.api.tasks.SourceSet import org.gradle.api.tasks.SourceSetContainer +import org.gradle.api.tasks.TaskContainer +import org.gradle.kotlin.dsl.withType /** * Gets the [SourceSetContainer] from the project's extensions, if available. @@ -35,6 +38,27 @@ internal fun Project.afterSuccessfulEvaluate(action: Action) = project. } } +/** + * Registers or configures the task with the given name and type + * + * @param name The name of the task to configure or register + * @param constructorArgs The constructor arguments of the task + * @param configure The configuration block + */ +internal inline fun TaskContainer.maybeRegister( + name: String, + vararg constructorArgs: Any, + configure: Action, +) { + + if (name in names) { + this.withType().named(name, configure) + } else { + val register = this.register(name, T::class.java, *constructorArgs) + register.configure(configure) + } +} + /** * Zips three [Provider]s together, rather than the usual two. */