mirror of
https://github.com/architectury/architectury-plugin.git
synced 2026-03-28 04:07:01 -05:00
Bump as 3.0, migrate to archtiectury runtime transformer
This commit is contained in:
@@ -26,10 +26,9 @@ logger.lifecycle(":building architectury plugin v${version}")
|
||||
sourceCompatibility = targetCompatibility = 1.8
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
maven { url "https://maven.fabricmc.net/" }
|
||||
maven { url "https://files.minecraftforge.net/maven/" }
|
||||
maven { url "https://dl.bintray.com/shedaniel/cloth/" }
|
||||
maven { url "https://maven.shedaniel.me/" }
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
@@ -44,6 +43,7 @@ dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.72"
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect:1.3.72"
|
||||
implementation "gradle.plugin.org.jetbrains.gradle.plugin.idea-ext:gradle-idea-ext:0.10"
|
||||
implementation "me.shedaniel:architectury-transformer:$transformer_version"
|
||||
implementation "me.shedaniel:forgified-fabric-loom:$loom_version"
|
||||
runtime "me.shedaniel:forgified-fabric-loom:$loom_version"
|
||||
implementation "net.fabricmc:tiny-remapper:0.3.0.70"
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
kotlin.code.style=official
|
||||
loom_version=0.6.54
|
||||
base_version=2.0
|
||||
loom_version=0.6.67
|
||||
transformer_version=2.0.7
|
||||
base_version=3.0
|
||||
@@ -1,6 +1,8 @@
|
||||
package me.shedaniel.architect.plugin
|
||||
|
||||
import me.shedaniel.architect.plugin.transformers.*
|
||||
import me.shedaniel.architect.plugin.transformers.AddRefmapName
|
||||
import me.shedaniel.architect.plugin.transformers.RemapMixinVariables
|
||||
import me.shedaniel.architectury.transformer.transformers.*
|
||||
import net.fabricmc.loom.util.LoggerFilter
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
@@ -43,49 +45,32 @@ class ArchitectPlugin : Plugin<Project> {
|
||||
|
||||
project.extensions.create("architectury", ArchitectPluginExtension::class.java, project)
|
||||
|
||||
project.configurations.create("transformFabric")
|
||||
project.configurations.create("transformForge")
|
||||
|
||||
project.tasks.register("transformProductionFabric", TransformingTask::class.java) {
|
||||
it.group = "Architectury"
|
||||
it(RemapMixinVariables)
|
||||
it(TransformExpectPlatform)
|
||||
it(TransformInjectables)
|
||||
it(AddRefmapName)
|
||||
}
|
||||
|
||||
project.tasks.register("transformDevelopmentFabric", TransformingTask::class.java) {
|
||||
it.group = "Architectury"
|
||||
it(GenerateFakeFabricModJson)
|
||||
it(TransformExpectPlatform)
|
||||
it(TransformInjectables)
|
||||
it += RemapMixinVariables(project)
|
||||
it += TransformExpectPlatform()
|
||||
it += RemapInjectables()
|
||||
it += AddRefmapName(project)
|
||||
}
|
||||
|
||||
project.tasks.register("transformProductionForge", TransformingTask::class.java) {
|
||||
it.group = "Architectury"
|
||||
it(RemapMixinVariables)
|
||||
it(TransformExpectPlatform)
|
||||
it(TransformInjectables)
|
||||
it(AddRefmapName)
|
||||
it += RemapMixinVariables(project)
|
||||
it += TransformExpectPlatform()
|
||||
it += RemapInjectables()
|
||||
it += AddRefmapName(project)
|
||||
|
||||
it(TransformForgeBytecode)
|
||||
it(RemoveFabricModJson)
|
||||
it(TransformForgeEnvironment)
|
||||
it(FixForgeMixin)
|
||||
}
|
||||
|
||||
project.tasks.register("transformDevelopmentForge", TransformingTask::class.java) {
|
||||
it.group = "Architectury"
|
||||
it(TransformExpectPlatform)
|
||||
it(TransformInjectables)
|
||||
|
||||
it(TransformForgeBytecode)
|
||||
it(RemoveFabricModJson)
|
||||
it(TransformForgeEnvironment)
|
||||
it(GenerateFakeForgeMod)
|
||||
it(FixForgeMixin)
|
||||
it += TransformForgeAnnotations()
|
||||
it += TransformForgeEnvironment()
|
||||
it += FixForgeMixin()
|
||||
}
|
||||
|
||||
project.repositories.apply {
|
||||
mavenCentral()
|
||||
maven { it.url = URI("https://dl.bintray.com/shedaniel/cloth") }
|
||||
maven { it.url = URI("https://maven.shedaniel.me/") }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,41 +2,177 @@
|
||||
|
||||
package me.shedaniel.architect.plugin
|
||||
|
||||
import me.shedaniel.architectury.transformer.Transformer
|
||||
import me.shedaniel.architectury.transformer.transformers.*
|
||||
import net.fabricmc.loom.LoomGradleExtension
|
||||
import net.fabricmc.loom.task.RemapJarTask
|
||||
import org.gradle.api.Action
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.tasks.bundling.AbstractArchiveTask
|
||||
import java.io.File
|
||||
import java.lang.IllegalStateException
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
import java.util.function.Consumer
|
||||
import java.util.jar.JarOutputStream
|
||||
import java.util.jar.Manifest
|
||||
|
||||
open class ArchitectPluginExtension(val project: Project) {
|
||||
var transformerVersion = "2.0.7"
|
||||
var minecraft = ""
|
||||
var injectInjectables = true
|
||||
private val transforms = mutableMapOf<String, Transform>()
|
||||
private var transformedLoom = false
|
||||
private val agentFile by lazy {
|
||||
project.gradle.rootProject.file(".gradle/architectury/architectury-transformer-agent.jar").also {
|
||||
it.parentFile.mkdirs()
|
||||
}
|
||||
}
|
||||
private val mainClassTransformerFile by lazy {
|
||||
project.file(".gradle/architectury/.main_class").also {
|
||||
it.parentFile.mkdirs()
|
||||
}
|
||||
}
|
||||
private val runtimeTransformerFile by lazy {
|
||||
project.file(".gradle/architectury/.transforms").also {
|
||||
it.parentFile.mkdirs()
|
||||
}
|
||||
}
|
||||
private val propertiesTransformerFile by lazy {
|
||||
project.file(".gradle/architectury/.properties").also {
|
||||
it.parentFile.mkdirs()
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
project.afterEvaluate {
|
||||
if (transforms.isNotEmpty()) {
|
||||
val transformPaths = mutableMapOf<Path, List<Class<Transformer>>>()
|
||||
for (transform in transforms.values) {
|
||||
project.configurations.getByName(transform.configName).forEach {
|
||||
transformPaths[it.toPath()] = transform.transformers
|
||||
}
|
||||
}
|
||||
transformPaths.asSequence().flatMap { it.value.asSequence().map { c -> it.key to c } }
|
||||
.joinToString(File.pathSeparator) { "${it.first}|${it.second.name}" }
|
||||
.also {
|
||||
runtimeTransformerFile.writeText(it)
|
||||
}
|
||||
}
|
||||
|
||||
val properties = Properties()
|
||||
properties().forEach { (key, value) ->
|
||||
System.setProperty(key, value)
|
||||
properties.setProperty(key, value)
|
||||
}
|
||||
propertiesTransformerFile.writer().use {
|
||||
properties.store(it, "Architectury Runtime Transformer Properties")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun properties(): Map<String, String> {
|
||||
val loom = project.extensions.findByType(LoomGradleExtension::class.java) ?: return mapOf()
|
||||
return mutableMapOf(
|
||||
BuiltinProperties.MIXIN_MAPPINGS to loom.allMixinMappings.joinToString(File.pathSeparator),
|
||||
BuiltinProperties.INJECT_INJECTABLES to injectInjectables.toString(),
|
||||
BuiltinProperties.UNIQUE_IDENTIFIER to project.projectUniqueIdentifier(),
|
||||
BuiltinProperties.COMPILE_CLASSPATH to project.configurations.getByName("compileClasspath")
|
||||
.joinToString(File.pathSeparator),
|
||||
BuiltinProperties.MAPPINGS_WITH_SRG to loom.mappingsProvider.tinyMappingsWithSrg.toString(),
|
||||
BuiltinProperties.REFMAP_NAME to loom.refmapName,
|
||||
BuiltinProperties.MCMETA_VERSION to "4"
|
||||
)
|
||||
}
|
||||
|
||||
fun transform(name: String, action: Action<Transform>) {
|
||||
transforms.getOrPut(name) {
|
||||
Transform("development" + name.capitalize()).also { transform ->
|
||||
project.configurations.create(transform.configName)
|
||||
|
||||
if (!transformedLoom) {
|
||||
val architecturyJavaAgents = project.configurations.create("architecturyJavaAgents") {
|
||||
project.configurations.getByName("runtimeOnly").extendsFrom(it)
|
||||
}
|
||||
transformedLoom = true
|
||||
|
||||
with(project.dependencies) {
|
||||
add("runtimeOnly", "me.shedaniel:architectury-transformer:$transformerVersion:runtime")
|
||||
add("architecturyJavaAgents", "me.shedaniel:architectury-transformer:$transformerVersion:agent")
|
||||
}
|
||||
|
||||
val loom = project.extensions.getByType(LoomGradleExtension::class.java)
|
||||
loom.settingsPostEdit.add(Consumer { config ->
|
||||
val s = config.mainClass
|
||||
config.mainClass = "me.shedaniel.architectury.transformer.TransformerRuntime"
|
||||
mainClassTransformerFile.writeText(s)
|
||||
config.vmArgs += " -Darchitectury.main.class=$mainClassTransformerFile"
|
||||
config.vmArgs += " -Darchitectury.runtime.transformer=$runtimeTransformerFile"
|
||||
config.vmArgs += " -Darchitectury.properties=$propertiesTransformerFile"
|
||||
config.vmArgs += " -Djdk.attach.allowAttachSelf=true"
|
||||
if (architecturyJavaAgents.toList().size == 1) {
|
||||
architecturyJavaAgents.first().copyTo(agentFile, overwrite = true)
|
||||
config.vmArgs += " -javaagent:${agentFile.absolutePath}"
|
||||
} else {
|
||||
throw IllegalStateException("Illegal Count of Architectury Java Agents! " + architecturyJavaAgents.toList().joinToString(", "))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}.also {
|
||||
action.execute(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun fabric() {
|
||||
transform("fabric", Action {
|
||||
it.setupFabricTransforms()
|
||||
})
|
||||
}
|
||||
|
||||
fun forge() {
|
||||
transform("forge", Action {
|
||||
it.setupForgeTransforms()
|
||||
})
|
||||
}
|
||||
|
||||
fun common() {
|
||||
common(true)
|
||||
common {}
|
||||
}
|
||||
|
||||
data class CommonSettings(
|
||||
var forgeEnabled: Boolean = true
|
||||
)
|
||||
|
||||
fun platformSetupLoomIde() {
|
||||
val loomExtension = project.extensions.getByType(LoomGradleExtension::class.java)
|
||||
loomExtension.autoGenIDERuns = true
|
||||
loomExtension.runConfigs.forEach { it.isIdeConfigGenerated = true }
|
||||
loomExtension.runConfigs.whenObjectAdded { it.isIdeConfigGenerated = true }
|
||||
loomExtension.addTaskBeforeRun("\$PROJECT_DIR\$/${project.name}:classes")
|
||||
}
|
||||
|
||||
fun common(forgeEnabled: Boolean) {
|
||||
common {
|
||||
this.forgeEnabled = forgeEnabled
|
||||
}
|
||||
}
|
||||
|
||||
fun common(action: CommonSettings.() -> Unit) {
|
||||
common(Action { it.action() })
|
||||
}
|
||||
|
||||
fun common(action: Action<CommonSettings>) {
|
||||
val settings = CommonSettings().also { action.execute(it) }
|
||||
if (injectInjectables) {
|
||||
with(project.dependencies) {
|
||||
add("compileOnly", "me.shedaniel:architectury-injectables:1.0.4")
|
||||
}
|
||||
}
|
||||
|
||||
if (forgeEnabled) {
|
||||
if (settings.forgeEnabled) {
|
||||
project.configurations.create("transformProductionForge")
|
||||
project.configurations.create("transformDevelopmentForge")
|
||||
}
|
||||
project.configurations.create("transformProductionFabric")
|
||||
project.configurations.create("transformDevelopmentFabric")
|
||||
|
||||
val buildTask = project.tasks.getByName("build")
|
||||
val jarTask = project.tasks.getByName("jar") {
|
||||
@@ -55,19 +191,6 @@ open class ArchitectPluginExtension(val project: Project) {
|
||||
buildTask.dependsOn(it)
|
||||
it.outputs.upToDateWhen { false }
|
||||
} as TransformingTask
|
||||
val transformDevelopmentFabricTask = project.tasks.getByName("transformDevelopmentFabric") {
|
||||
it as TransformingTask
|
||||
|
||||
it.archiveClassifier.set("transformDevelopmentFabric")
|
||||
it.input.set(jarTask.archiveFile.get())
|
||||
|
||||
project.artifacts.add("transformDevelopmentFabric", it)
|
||||
it.dependsOn(jarTask)
|
||||
buildTask.dependsOn(it)
|
||||
it.outputs.upToDateWhen { false }
|
||||
} as TransformingTask
|
||||
|
||||
transformProductionFabricTask.dependsOn(transformDevelopmentFabricTask)
|
||||
|
||||
val remapJarTask = project.tasks.getByName("remapJar") {
|
||||
it as RemapJarTask
|
||||
@@ -78,7 +201,7 @@ open class ArchitectPluginExtension(val project: Project) {
|
||||
it.mustRunAfter(transformProductionFabricTask)
|
||||
} as RemapJarTask
|
||||
|
||||
if (forgeEnabled) {
|
||||
if (settings.forgeEnabled) {
|
||||
val transformProductionForgeTask = project.tasks.getByName("transformProductionForge") {
|
||||
it as TransformingTask
|
||||
|
||||
@@ -91,30 +214,12 @@ open class ArchitectPluginExtension(val project: Project) {
|
||||
it.outputs.upToDateWhen { false }
|
||||
} as TransformingTask
|
||||
|
||||
val transformDevelopmentForgeTask = project.tasks.getByName("transformDevelopmentForge") {
|
||||
it as TransformingTask
|
||||
|
||||
it.input.set(jarTask.archiveFile.get())
|
||||
it.archiveClassifier.set("transformDevelopmentForge")
|
||||
|
||||
project.artifacts.add("transformDevelopmentForge", it) { artifact ->
|
||||
artifact.builtBy(it)
|
||||
}
|
||||
it.dependsOn(jarTask)
|
||||
buildTask.dependsOn(it)
|
||||
it.outputs.upToDateWhen { false }
|
||||
} as TransformingTask
|
||||
|
||||
transformProductionForgeTask.dependsOn(transformDevelopmentForgeTask)
|
||||
|
||||
transformProductionForgeTask.archiveFile.get().asFile.takeUnless { it.exists() }?.createEmptyJar()
|
||||
transformDevelopmentForgeTask.archiveFile.get().asFile.takeUnless { it.exists() }?.createEmptyJar()
|
||||
|
||||
project.extensions.getByType(LoomGradleExtension::class.java).generateSrgTiny = true
|
||||
}
|
||||
|
||||
transformProductionFabricTask.archiveFile.get().asFile.takeUnless { it.exists() }?.createEmptyJar()
|
||||
transformDevelopmentFabricTask.archiveFile.get().asFile.takeUnless { it.exists() }?.createEmptyJar()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,3 +227,27 @@ private fun File.createEmptyJar() {
|
||||
parentFile.mkdirs()
|
||||
JarOutputStream(outputStream(), Manifest()).close()
|
||||
}
|
||||
|
||||
data class Transform(val configName: String, val transformers: MutableList<Class<Transformer>> = mutableListOf()) {
|
||||
fun setupFabricTransforms() {
|
||||
this += GenerateFakeFabricMod::class.java
|
||||
this += TransformExpectPlatform::class.java
|
||||
this += RemapInjectables::class.java
|
||||
}
|
||||
|
||||
fun setupForgeTransforms() {
|
||||
this += TransformExpectPlatform::class.java
|
||||
this += RemapInjectables::class.java
|
||||
|
||||
this += TransformForgeAnnotations::class.java
|
||||
this += TransformForgeEnvironment::class.java
|
||||
this += GenerateFakeForgeMod::class.java
|
||||
this += FixForgeMixin::class.java
|
||||
}
|
||||
|
||||
operator fun <T : Transformer> plusAssign(transformer: Class<T>) {
|
||||
transformers.add(transformer as Class<Transformer>)
|
||||
}
|
||||
|
||||
fun <T : Transformer> add(transformer: Class<T>) = plusAssign(transformer as Class<Transformer>)
|
||||
}
|
||||
@@ -1,97 +1,43 @@
|
||||
package me.shedaniel.architect.plugin
|
||||
|
||||
import me.shedaniel.architect.plugin.utils.GradleSupport
|
||||
import me.shedaniel.architectury.transformer.Transform
|
||||
import me.shedaniel.architectury.transformer.Transformer
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.tasks.Input
|
||||
import org.gradle.api.tasks.InputFile
|
||||
import org.gradle.api.tasks.Internal
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
import org.gradle.jvm.tasks.Jar
|
||||
import java.io.File
|
||||
import java.io.ObjectOutputStream
|
||||
import java.io.Serializable
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.StandardCopyOption
|
||||
import java.util.*
|
||||
import kotlin.properties.Delegates
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.ExperimentalTime
|
||||
import kotlin.time.nanoseconds
|
||||
|
||||
open class TransformingTask : Jar() {
|
||||
@InputFile
|
||||
val input: RegularFileProperty = GradleSupport.getFileProperty(project)
|
||||
|
||||
@Input
|
||||
@Internal
|
||||
val transformers = mutableListOf<Transformer>()
|
||||
|
||||
@ExperimentalTime
|
||||
@TaskAction
|
||||
fun doTask() {
|
||||
val input: Path = this.input.asFile.get().toPath()
|
||||
val taskOutputs = transformers.mapIndexed { index, _ ->
|
||||
project.file("build")
|
||||
.resolve("architectury-plugin/" + input.toFile().nameWithoutExtension + "-intermediate-${index}.jar")
|
||||
.toPath()
|
||||
}
|
||||
val output: Path = this.archiveFile.get().asFile.toPath()
|
||||
|
||||
transformers.forEachIndexed { index, transformer ->
|
||||
val i = if (index == 0) input else taskOutputs[index - 1]
|
||||
val o = taskOutputs[index]
|
||||
|
||||
Files.deleteIfExists(o)
|
||||
Files.createDirectories(o.parent)
|
||||
runCatching {
|
||||
var skipped = false
|
||||
measureTime {
|
||||
try {
|
||||
transformer(project, i, o)
|
||||
} catch (ignored: TransformerStepSkipped) {
|
||||
skipped = true
|
||||
}
|
||||
if (index != 0) {
|
||||
Files.deleteIfExists(i)
|
||||
}
|
||||
}.let { duration ->
|
||||
if (skipped) {
|
||||
project.logger.lifecycle(":skipped transforming step ${index + 1}/${transformers.size} [${transformer::class.simpleName}] in $duration")
|
||||
} else {
|
||||
project.logger.lifecycle(":finished transforming step ${index + 1}/${transformers.size} [${transformer::class.simpleName}] in $duration")
|
||||
}
|
||||
}
|
||||
}.onFailure {
|
||||
throw RuntimeException(
|
||||
"Failed transformer step ${index + 1}/${transformers.size} [${transformer::class.simpleName}]",
|
||||
it
|
||||
)
|
||||
}
|
||||
|
||||
runCatching {
|
||||
o.toFile().also { it.renameTo(it) }
|
||||
}.onFailure {
|
||||
throw RuntimeException(
|
||||
"Transformer step ${index + 1}/${transformers.size} [${transformer::class.simpleName}] did not properly close the output file!",
|
||||
it
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Files.move(taskOutputs.last(), output, StandardCopyOption.REPLACE_EXISTING)
|
||||
Transform.runTransformers(input, output, transformers)
|
||||
}
|
||||
|
||||
operator fun invoke(transformer: Transformer) {
|
||||
transformers.add(transformer)
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalTime
|
||||
private inline fun measureTime(block: () -> Unit): Duration {
|
||||
val current = System.nanoTime()
|
||||
block()
|
||||
val finished = System.nanoTime()
|
||||
return (finished - current).nanoseconds
|
||||
operator fun plusAssign(transformer: Transformer) {
|
||||
transformers.add(transformer)
|
||||
}
|
||||
}
|
||||
|
||||
fun Project.projectUniqueIdentifier(): String {
|
||||
@@ -109,13 +55,3 @@ fun Project.projectUniqueIdentifier(): String {
|
||||
if (project.rootProject != project) name = project.rootProject.name + "_" + name
|
||||
return "architectury_inject_${name}_$id".filter { Character.isJavaIdentifierPart(it) }
|
||||
}
|
||||
|
||||
interface Transformer : Serializable {
|
||||
operator fun invoke(project: Project, input: Path, output: Path)
|
||||
|
||||
@JvmDefault
|
||||
fun writeObject(s: ObjectOutputStream) {
|
||||
}
|
||||
}
|
||||
|
||||
object TransformerStepSkipped : Throwable()
|
||||
@@ -1,44 +1,57 @@
|
||||
package me.shedaniel.architect.plugin.transformers
|
||||
|
||||
import me.shedaniel.architect.plugin.Transformer
|
||||
import me.shedaniel.architect.plugin.TransformerStepSkipped
|
||||
import com.google.gson.JsonObject
|
||||
import me.shedaniel.architectury.transformer.Transformer
|
||||
import me.shedaniel.architectury.transformer.TransformerStepSkipped
|
||||
import me.shedaniel.architectury.transformer.transformers.base.AssetEditTransformer
|
||||
import me.shedaniel.architectury.transformer.transformers.base.edit.AssetEditSink
|
||||
import me.shedaniel.architectury.transformer.transformers.base.edit.TransformerContext
|
||||
import net.fabricmc.loom.LoomGradleExtension
|
||||
import net.fabricmc.loom.LoomGradlePlugin
|
||||
import org.gradle.api.Project
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
|
||||
object AddRefmapName : Transformer {
|
||||
override fun invoke(project: Project, input: Path, output: Path) {
|
||||
Files.copy(input, output)
|
||||
class AddRefmapName(private val project: Project) : AssetEditTransformer {
|
||||
override fun doEdit(context: TransformerContext, sink: AssetEditSink) {
|
||||
val loomExtension = project.extensions.getByType(LoomGradleExtension::class.java)
|
||||
var refmapHelperClass: Class<*>? = null
|
||||
runCatching {
|
||||
refmapHelperClass = Class.forName("net.fabricmc.loom.util.MixinRefmapHelper")
|
||||
}.onFailure {
|
||||
runCatching {
|
||||
refmapHelperClass = Class.forName("net.fabricmc.loom.build.MixinRefmapHelper")
|
||||
}.onFailure {
|
||||
throw ClassNotFoundException("Failed to find MixinRefmapHelper!")
|
||||
|
||||
val mixins = mutableSetOf<String>()
|
||||
sink.handle { path, bytes ->
|
||||
// Check JSON file in root directory
|
||||
if (path.endsWith(".json") && !path.contains("/") && !path.contains("\\")) {
|
||||
try {
|
||||
val json =
|
||||
LoomGradlePlugin.GSON.fromJson(ByteArrayInputStream(bytes).reader(), JsonObject::class.java)
|
||||
if (json != null) {
|
||||
val hasMixins = json.has("mixins") && json["mixins"].isJsonArray
|
||||
val hasClient = json.has("client") && json["client"].isJsonArray
|
||||
val hasServer = json.has("server") && json["server"].isJsonArray
|
||||
if (json.has("package") && (hasMixins || hasClient || hasServer)) {
|
||||
if (!json.has("refmap") || !json.has("minVersion")) {
|
||||
mixins.add(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
mixins.forEach { path ->
|
||||
sink.transformFile(path) {
|
||||
val json: JsonObject = LoomGradlePlugin.GSON.fromJson<JsonObject>(
|
||||
ByteArrayInputStream(it).reader(),
|
||||
JsonObject::class.java
|
||||
)
|
||||
|
||||
val method = refmapHelperClass!!.getDeclaredMethod(
|
||||
"addRefmapName",
|
||||
String::class.java,
|
||||
String::class.java,
|
||||
Path::class.java
|
||||
)
|
||||
if (
|
||||
method.invoke(
|
||||
null,
|
||||
loomExtension.getRefmapName(),
|
||||
loomExtension.mixinJsonVersion,
|
||||
output
|
||||
) as Boolean
|
||||
) {
|
||||
project.logger.debug("Transformed mixin reference maps in output JAR!")
|
||||
} else {
|
||||
throw TransformerStepSkipped
|
||||
if (!json.has("refmap")) {
|
||||
json.addProperty("refmap", loomExtension.getRefmapName())
|
||||
}
|
||||
|
||||
LoomGradlePlugin.GSON.toJson(json).toByteArray()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,220 +0,0 @@
|
||||
package me.shedaniel.architect.plugin.transformers
|
||||
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.JsonParser
|
||||
import me.shedaniel.architect.plugin.Transformer
|
||||
import me.shedaniel.architect.plugin.TransformerStepSkipped
|
||||
import net.fabricmc.loom.LoomGradleExtension
|
||||
import org.gradle.api.Project
|
||||
import org.zeroturnaround.zip.ZipUtil
|
||||
import java.io.File
|
||||
import java.io.IOException
|
||||
import java.io.InputStreamReader
|
||||
import java.io.StringReader
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.util.jar.Manifest
|
||||
import java.util.zip.ZipEntry
|
||||
|
||||
object FixForgeMixin : Transformer {
|
||||
override fun invoke(project: Project, input: Path, output: Path) {
|
||||
Files.copy(input, output)
|
||||
fixMixins(project, output.toFile())
|
||||
}
|
||||
|
||||
private fun fixMixins(project: Project, output: File) {
|
||||
val loomExtension = project.extensions.getByType(LoomGradleExtension::class.java)
|
||||
val gson = GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create()
|
||||
val mixinConfigs = mutableListOf<String>()
|
||||
val refmap = loomExtension.getRefmapName()
|
||||
ZipUtil.iterate(output) { stream, entry ->
|
||||
if (!entry.isDirectory && entry.name.endsWith(".json") &&
|
||||
!entry.name.contains("/") && !entry.name.contains("\\")
|
||||
) {
|
||||
try {
|
||||
InputStreamReader(stream).use { reader ->
|
||||
val json: JsonObject? = gson.fromJson<JsonObject>(reader, JsonObject::class.java)
|
||||
if (json != null) {
|
||||
val hasMixins = json.has("mixins") && json["mixins"].isJsonArray
|
||||
val hasClient = json.has("client") && json["client"].isJsonArray
|
||||
val hasServer = json.has("server") && json["server"].isJsonArray
|
||||
if (json.has("package") && (hasMixins || hasClient || hasServer)) {
|
||||
mixinConfigs.add(entry.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ignored: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mixinConfigs.isNotEmpty()) {
|
||||
if (ZipUtil.containsEntry(output, "META-INF/MANIFEST.MF")) {
|
||||
ZipUtil.transformEntry(output, "META-INF/MANIFEST.MF") { input, zipEntry, out ->
|
||||
val manifest = Manifest(input)
|
||||
manifest.mainAttributes.putValue("MixinConfigs", mixinConfigs.joinToString(","))
|
||||
out.putNextEntry(ZipEntry(zipEntry.name))
|
||||
manifest.write(out)
|
||||
out.closeEntry()
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ZipUtil.containsEntry(output, refmap)) {
|
||||
ZipUtil.transformEntry(output, refmap) { input, zipEntry, out ->
|
||||
val refmapElement: JsonObject = JsonParser().parse(InputStreamReader(input)).asJsonObject.deepCopy()
|
||||
if (refmapElement.has("mappings")) {
|
||||
refmapElement["mappings"].asJsonObject.entrySet().forEach { (_, value) ->
|
||||
remapRefmap(project, value.asJsonObject)
|
||||
}
|
||||
}
|
||||
if (refmapElement.has("data")) {
|
||||
val data = refmapElement["data"].asJsonObject
|
||||
if (data.has("named:intermediary")) {
|
||||
data.add("searge", data["named:intermediary"].deepCopy().also {
|
||||
it.asJsonObject.entrySet().forEach { (_, value) ->
|
||||
remapRefmap(project, value.asJsonObject)
|
||||
}
|
||||
})
|
||||
data.remove("named:intermediary")
|
||||
}
|
||||
}
|
||||
out.putNextEntry(ZipEntry(zipEntry.name))
|
||||
out.write(gson.toJson(refmapElement).toByteArray())
|
||||
out.closeEntry()
|
||||
}
|
||||
} else {
|
||||
project.logger.info("Failed to locate refmap: $refmap")
|
||||
if (mixinConfigs.isEmpty()) {
|
||||
throw TransformerStepSkipped
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun remapRefmap(project: Project, obj: JsonObject) {
|
||||
val srg = project.extensions.getByType(LoomGradleExtension::class.java).mappingsProvider.mappingsWithSrg
|
||||
val methodPattern = "L(.*);(.*)(\\(.*)".toRegex()
|
||||
val methodPatternWithoutClass = "(.*)(\\(.*)".toRegex()
|
||||
val fieldPattern = "L(.*);(.*):(.*)".toRegex()
|
||||
val fieldPatternWithoutClass = "(.*):(.*)".toRegex()
|
||||
|
||||
obj.keySet().forEach { key ->
|
||||
val originalRef = obj[key].asString
|
||||
|
||||
val methodMatch = methodPattern.matchEntire(originalRef)
|
||||
val fieldMatch = fieldPattern.matchEntire(originalRef)
|
||||
val fieldMatchWithoutClass = fieldPatternWithoutClass.matchEntire(originalRef)
|
||||
val methodMatchWithoutClass = methodPatternWithoutClass.matchEntire(originalRef)
|
||||
val classMatch = srg.classes.firstOrNull { it.getName("intermediary") == originalRef }
|
||||
|
||||
when {
|
||||
methodMatch != null -> {
|
||||
val matchedClass =
|
||||
srg.classes.firstOrNull { it.getName("intermediary") == methodMatch.groups[1]!!.value }
|
||||
val replacementName: String = srg.classes.asSequence()
|
||||
.flatMap { it.methods.asSequence() }
|
||||
.filter { it.getName("intermediary") == methodMatch.groups[2]!!.value }
|
||||
.firstOrNull { it.getDescriptor("intermediary") == methodMatch.groups[3]!!.value }
|
||||
?.getName("srg") ?: methodMatch.groups[2]!!.value
|
||||
obj.addProperty(
|
||||
key, originalRef
|
||||
.replaceFirst(
|
||||
methodMatch.groups[1]!!.value,
|
||||
matchedClass?.getName("srg") ?: methodMatch.groups[1]!!.value
|
||||
)
|
||||
.replaceFirst(methodMatch.groups[2]!!.value, replacementName)
|
||||
.replaceFirst(methodMatch.groups[3]!!.value, methodMatch.groups[3]!!.value.remapDescriptor {
|
||||
srg.classes.firstOrNull { def -> def.getName("intermediary") == it }?.getName("srg")
|
||||
?: it
|
||||
})
|
||||
)
|
||||
}
|
||||
fieldMatch != null -> {
|
||||
val matchedClass =
|
||||
srg.classes.firstOrNull { it.getName("intermediary") == fieldMatch.groups[1]!!.value }
|
||||
val replacementName: String = srg.classes.asSequence()
|
||||
.flatMap { it.fields.asSequence() }
|
||||
.filter { it.getName("intermediary") == fieldMatch.groups[2]!!.value }
|
||||
.firstOrNull { it.getDescriptor("intermediary") == fieldMatch.groups[3]!!.value }
|
||||
?.getName("srg") ?: fieldMatch.groups[2]!!.value
|
||||
obj.addProperty(
|
||||
key, originalRef
|
||||
.replaceFirst(
|
||||
fieldMatch.groups[1]!!.value,
|
||||
matchedClass?.getName("srg") ?: fieldMatch.groups[1]!!.value
|
||||
)
|
||||
.replaceFirst(fieldMatch.groups[2]!!.value, replacementName)
|
||||
.replaceFirst(fieldMatch.groups[3]!!.value, fieldMatch.groups[3]!!.value.remapDescriptor {
|
||||
srg.classes.firstOrNull { def -> def.getName("intermediary") == it }?.getName("srg")
|
||||
?: it
|
||||
})
|
||||
)
|
||||
}
|
||||
fieldMatchWithoutClass != null -> {
|
||||
val replacementName: String = srg.classes.asSequence()
|
||||
.flatMap { it.fields.asSequence() }
|
||||
.filter { it.getName("intermediary") == fieldMatchWithoutClass.groups[1]!!.value }
|
||||
.firstOrNull { it.getDescriptor("intermediary") == fieldMatchWithoutClass.groups[2]!!.value }
|
||||
?.getName("srg") ?: fieldMatchWithoutClass.groups[1]!!.value
|
||||
obj.addProperty(
|
||||
key, originalRef
|
||||
.replaceFirst(fieldMatchWithoutClass.groups[1]!!.value, replacementName)
|
||||
.replaceFirst(fieldMatchWithoutClass.groups[2]!!.value, fieldMatchWithoutClass.groups[2]!!.value.remapDescriptor {
|
||||
srg.classes.firstOrNull { def -> def.getName("intermediary") == it }?.getName("srg")
|
||||
?: it
|
||||
})
|
||||
)
|
||||
}
|
||||
methodMatchWithoutClass != null -> {
|
||||
val replacementName: String = srg.classes.asSequence()
|
||||
.flatMap { it.methods.asSequence() }
|
||||
.filter { it.getName("intermediary") == methodMatchWithoutClass.groups[1]!!.value }
|
||||
.firstOrNull { it.getDescriptor("intermediary") == methodMatchWithoutClass.groups[2]!!.value }
|
||||
?.getName("srg") ?: methodMatchWithoutClass.groups[1]!!.value
|
||||
obj.addProperty(
|
||||
key, originalRef
|
||||
.replaceFirst(methodMatchWithoutClass.groups[1]!!.value, replacementName)
|
||||
.replaceFirst(
|
||||
methodMatchWithoutClass.groups[2]!!.value,
|
||||
methodMatchWithoutClass.groups[2]!!.value.remapDescriptor {
|
||||
srg.classes.firstOrNull { def -> def.getName("intermediary") == it }?.getName("srg")
|
||||
?: it
|
||||
})
|
||||
)
|
||||
}
|
||||
classMatch != null -> obj.addProperty(key, classMatch.getName("srg"))
|
||||
else -> project.logger.warn("Failed to remap refmap value: $originalRef")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun String.remapDescriptor(classMappings: (String) -> String): String {
|
||||
return try {
|
||||
val reader = StringReader(this)
|
||||
val result = StringBuilder()
|
||||
var insideClassName = false
|
||||
val className = StringBuilder()
|
||||
while (true) {
|
||||
val c: Int = reader.read()
|
||||
if (c == -1) {
|
||||
break
|
||||
}
|
||||
if (c == ';'.toInt()) {
|
||||
insideClassName = false
|
||||
result.append(classMappings(className.toString()))
|
||||
}
|
||||
if (insideClassName) {
|
||||
className.append(c.toChar())
|
||||
} else {
|
||||
result.append(c.toChar())
|
||||
}
|
||||
if (!insideClassName && c == 'L'.toInt()) {
|
||||
insideClassName = true
|
||||
className.setLength(0)
|
||||
}
|
||||
}
|
||||
result.toString()
|
||||
} catch (e: IOException) {
|
||||
throw AssertionError(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
package me.shedaniel.architect.plugin.transformers
|
||||
|
||||
import me.shedaniel.architect.plugin.Transformer
|
||||
import org.gradle.api.Project
|
||||
import org.zeroturnaround.zip.ByteSource
|
||||
import org.zeroturnaround.zip.ZipUtil
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
|
||||
object GenerateFakeFabricModJson : Transformer {
|
||||
override fun invoke(project: Project, input: Path, output: Path) {
|
||||
Files.copy(input, output)
|
||||
val fakeModId = "generated_" + UUID.randomUUID().toString().filterNot { it == '-' }.take(7)
|
||||
ZipUtil.addOrReplaceEntries(
|
||||
output.toFile(), arrayOf(
|
||||
ByteSource(
|
||||
"fabric.mod.json", """{
|
||||
"schemaVersion": 1,
|
||||
"id": "$fakeModId",
|
||||
"name": "Generated Mod (Please Ignore)",
|
||||
"version": "1.0.0",
|
||||
"custom": {
|
||||
"fabric-loom:generated": true
|
||||
}
|
||||
}""".toByteArray()
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
package me.shedaniel.architect.plugin.transformers
|
||||
|
||||
import me.shedaniel.architect.plugin.Transformer
|
||||
import org.gradle.api.Project
|
||||
import org.objectweb.asm.ClassWriter
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.zeroturnaround.zip.ByteSource
|
||||
import org.zeroturnaround.zip.ZipUtil
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
|
||||
object GenerateFakeForgeMod : Transformer {
|
||||
override fun invoke(project: Project, input: Path, output: Path) {
|
||||
val fakeModId = "generated_" + UUID.randomUUID().toString().filterNot { it == '-' }.take(7)
|
||||
Files.copy(input, output)
|
||||
ZipUtil.addEntries(
|
||||
output.toFile(), arrayOf(
|
||||
ByteSource(
|
||||
"META-INF/mods.toml",
|
||||
"""modLoader = "javafml"
|
||||
loaderVersion = "[33,)"
|
||||
license = "Generated"
|
||||
[[mods]]
|
||||
modId = "$fakeModId"""".toByteArray()
|
||||
),
|
||||
ByteSource(
|
||||
"pack.mcmeta",
|
||||
"""{"pack":{"description":"Generated","pack_format":4}}""".toByteArray()
|
||||
),
|
||||
ByteSource(
|
||||
"generated/$fakeModId.class",
|
||||
ClassWriter(0).let { classWriter ->
|
||||
classWriter.visit(52, Opcodes.ACC_PUBLIC, "generated/$fakeModId", null, "java/lang/Object", null)
|
||||
val modAnnotation = classWriter.visitAnnotation("Lnet/minecraftforge/fml/common/Mod;", false)
|
||||
modAnnotation.visit("value", fakeModId)
|
||||
modAnnotation.visitEnd()
|
||||
classWriter.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, arrayOf()).also {
|
||||
it.visitVarInsn(Opcodes.ALOAD, 0)
|
||||
it.visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false)
|
||||
it.visitInsn(Opcodes.RETURN)
|
||||
it.visitMaxs(1, 1)
|
||||
it.visitEnd()
|
||||
}
|
||||
classWriter.visitEnd()
|
||||
classWriter.toByteArray()
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,65 +1,21 @@
|
||||
package me.shedaniel.architect.plugin.transformers
|
||||
|
||||
import me.shedaniel.architect.plugin.Transformer
|
||||
import me.shedaniel.architect.plugin.TransformerStepSkipped
|
||||
import me.shedaniel.architect.plugin.utils.validateJarFs
|
||||
import me.shedaniel.architectury.transformer.shadowed.impl.net.fabricmc.tinyremapper.IMappingProvider
|
||||
import me.shedaniel.architectury.transformer.shadowed.impl.net.fabricmc.tinyremapper.TinyUtils
|
||||
import me.shedaniel.architectury.transformer.transformers.base.TinyRemapperTransformer
|
||||
import net.fabricmc.loom.LoomGradleExtension
|
||||
import net.fabricmc.loom.util.LoggerFilter
|
||||
import net.fabricmc.tinyremapper.OutputConsumerPath
|
||||
import net.fabricmc.tinyremapper.TinyRemapper
|
||||
import net.fabricmc.tinyremapper.TinyUtils
|
||||
import org.gradle.api.Project
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
|
||||
object RemapMixinVariables : Transformer {
|
||||
override fun invoke(project: Project, input: Path, output: Path) {
|
||||
class RemapMixinVariables(private val project: Project) : TinyRemapperTransformer {
|
||||
override fun collectMappings(): MutableList<IMappingProvider> {
|
||||
val loomExtension = project.extensions.getByType(LoomGradleExtension::class.java)
|
||||
var remapperBuilder = TinyRemapper.newRemapper()
|
||||
var requiresRemap = false
|
||||
for (mixinMapFile in loomExtension.allMixinMappings) {
|
||||
if (mixinMapFile.exists()) {
|
||||
if (!requiresRemap) {
|
||||
requiresRemap = Files.readAllLines(mixinMapFile.toPath()).count { it.isNotBlank() } > 1
|
||||
}
|
||||
remapperBuilder = remapperBuilder.withMappings(
|
||||
TinyUtils.createTinyMappingProvider(
|
||||
mixinMapFile.toPath(),
|
||||
"named",
|
||||
"intermediary"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if (!requiresRemap) {
|
||||
Files.copy(input, output)
|
||||
throw TransformerStepSkipped
|
||||
}
|
||||
|
||||
val remapper = remapperBuilder.build()
|
||||
|
||||
val classpathFiles: Set<File> = LinkedHashSet(
|
||||
project.configurations.getByName("compileClasspath").files
|
||||
)
|
||||
val classpath = classpathFiles.asSequence().map { obj: File -> obj.toPath() }.filter { p: Path ->
|
||||
input != p && Files.exists(p)
|
||||
}.toList().toTypedArray()
|
||||
|
||||
LoggerFilter.replaceSystemOut()
|
||||
try {
|
||||
project.validateJarFs(output)
|
||||
OutputConsumerPath.Builder(output).build().use { outputConsumer ->
|
||||
outputConsumer.addNonClassFiles(input)
|
||||
remapper.readClassPath(*classpath)
|
||||
remapper.readInputs(input)
|
||||
remapper.apply(outputConsumer)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException("Failed to remap $input to $output", e)
|
||||
} finally {
|
||||
remapper.finish()
|
||||
}
|
||||
return loomExtension.allMixinMappings.asSequence().filter(File::exists).map {
|
||||
TinyUtils.createTinyMappingProvider(
|
||||
it.toPath(),
|
||||
"named",
|
||||
"intermediary"
|
||||
)
|
||||
}.toMutableList()
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package me.shedaniel.architect.plugin.transformers
|
||||
|
||||
import me.shedaniel.architect.plugin.Transformer
|
||||
import me.shedaniel.architect.plugin.TransformerStepSkipped
|
||||
import org.gradle.api.Project
|
||||
import org.zeroturnaround.zip.ZipUtil
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
|
||||
object RemoveFabricModJson : Transformer {
|
||||
override fun invoke(project: Project, input: Path, output: Path) {
|
||||
Files.copy(input, output)
|
||||
if (ZipUtil.containsEntry(output.toFile(), "fabric.mod.json")) {
|
||||
ZipUtil.removeEntry(output.toFile(), "fabric.mod.json")
|
||||
} else {
|
||||
throw TransformerStepSkipped
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,206 +0,0 @@
|
||||
package me.shedaniel.architect.plugin.transformers
|
||||
|
||||
import me.shedaniel.architect.plugin.ArchitectPluginExtension
|
||||
import me.shedaniel.architect.plugin.Transformer
|
||||
import me.shedaniel.architect.plugin.projectUniqueIdentifier
|
||||
import me.shedaniel.architect.plugin.utils.ClassTransformer
|
||||
import me.shedaniel.architect.plugin.utils.Transform
|
||||
import org.gradle.api.Project
|
||||
import org.objectweb.asm.ClassReader
|
||||
import org.objectweb.asm.ClassWriter
|
||||
import org.objectweb.asm.Handle
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.objectweb.asm.commons.ClassRemapper
|
||||
import org.objectweb.asm.commons.Remapper
|
||||
import org.objectweb.asm.tree.*
|
||||
import org.zeroturnaround.zip.ZipUtil
|
||||
import java.io.InputStream
|
||||
import java.lang.invoke.CallSite
|
||||
import java.lang.invoke.MethodHandles
|
||||
import java.lang.invoke.MethodType
|
||||
import java.nio.file.Path
|
||||
import java.util.zip.ZipEntry
|
||||
|
||||
object TransformExpectPlatform : Transformer {
|
||||
override fun invoke(project: Project, input: Path, output: Path) {
|
||||
Transform.transform(input, output, transformExpectPlatform(project))
|
||||
}
|
||||
|
||||
fun transformExpectPlatform(project: Project): ClassTransformer {
|
||||
val projectUniqueIdentifier by lazy { project.projectUniqueIdentifier() }
|
||||
var injectedClass = !project.extensions.getByType(ArchitectPluginExtension::class.java).injectInjectables
|
||||
return { clazz, classAdder ->
|
||||
if (!injectedClass) {
|
||||
injectedClass = true
|
||||
Transform::class.java.getResourceAsStream("/annotations-inject/injection.jar").use { stream ->
|
||||
ZipUtil.iterate(stream) { input: InputStream, entry: ZipEntry ->
|
||||
if (entry.name.endsWith(".class")) {
|
||||
val newName = "$projectUniqueIdentifier/${
|
||||
entry.name.substringBeforeLast(".class").substringAfterLast('/')
|
||||
}"
|
||||
classAdder(newName, input.readBytes().let {
|
||||
val node = ClassNode(Opcodes.ASM8)
|
||||
ClassReader(it).accept(node, ClassReader.EXPAND_FRAMES)
|
||||
val writer = ClassWriter(ClassWriter.COMPUTE_MAXS)
|
||||
val remapper = ClassRemapper(writer, object : Remapper() {
|
||||
override fun map(internalName: String?): String {
|
||||
if (internalName?.startsWith("me/shedaniel/architect/plugin/callsite") == true) {
|
||||
return internalName.replace(
|
||||
"me/shedaniel/architect/plugin/callsite",
|
||||
projectUniqueIdentifier
|
||||
)
|
||||
}
|
||||
return super.map(internalName)
|
||||
}
|
||||
})
|
||||
node.apply {
|
||||
name = newName
|
||||
}.accept(remapper)
|
||||
|
||||
writer.toByteArray()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
clazz.methods.mapNotNull { method ->
|
||||
when {
|
||||
method?.visibleAnnotations?.any { it.desc == TransformInjectables.expectPlatform } == true -> method to "me/shedaniel/architectury/PlatformMethods"
|
||||
method?.invisibleAnnotations?.any { it.desc == TransformInjectables.expectPlatformNew } == true -> {
|
||||
method to "$projectUniqueIdentifier/PlatformMethods"
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}.forEach { (method, platformMethodsClass) ->
|
||||
if (method.access and Opcodes.ACC_STATIC == 0) {
|
||||
System.err.println("@ExpectPlatform can only apply to static methods!")
|
||||
} else {
|
||||
method.instructions.clear()
|
||||
val endOfDesc = method.desc.lastIndexOf(')')
|
||||
val returnValue = method.desc.substring(endOfDesc + 1)
|
||||
val args = method.desc.substring(1, endOfDesc)
|
||||
var cursor = 0
|
||||
var inClass = false
|
||||
var index = 0
|
||||
while (cursor < args.length) {
|
||||
val char = args[cursor]
|
||||
if (inClass) {
|
||||
if (char == ';') {
|
||||
method.instructions.addLoad(char, index++)
|
||||
inClass = false
|
||||
}
|
||||
} else when (char) {
|
||||
'[' -> Unit
|
||||
'L' -> inClass = true
|
||||
else -> method.instructions.addLoad(char, when (char) {
|
||||
'J', 'D' -> index.also { index += 2 }
|
||||
else -> index++
|
||||
})
|
||||
}
|
||||
cursor++
|
||||
}
|
||||
|
||||
val methodType = MethodType.methodType(
|
||||
CallSite::class.java,
|
||||
MethodHandles.Lookup::class.java,
|
||||
String::class.java,
|
||||
MethodType::class.java
|
||||
)
|
||||
|
||||
val handle = Handle(
|
||||
Opcodes.H_INVOKESTATIC,
|
||||
platformMethodsClass,
|
||||
"platform",
|
||||
methodType.toMethodDescriptorString(),
|
||||
false
|
||||
)
|
||||
|
||||
method.instructions.add(
|
||||
InvokeDynamicInsnNode(
|
||||
method.name,
|
||||
method.desc,
|
||||
handle
|
||||
)
|
||||
)
|
||||
|
||||
method.instructions.addReturn(returnValue.first { it != '[' })
|
||||
method.maxStack = -1
|
||||
}
|
||||
}
|
||||
|
||||
clazz
|
||||
}
|
||||
}
|
||||
|
||||
private fun InsnList.addLoad(type: Char, index: Int) {
|
||||
when (type) {
|
||||
';' -> add(
|
||||
VarInsnNode(
|
||||
Opcodes.ALOAD,
|
||||
index
|
||||
)
|
||||
)
|
||||
'I', 'S', 'B', 'C', 'Z' -> add(
|
||||
VarInsnNode(
|
||||
Opcodes.ILOAD,
|
||||
index
|
||||
)
|
||||
)
|
||||
'F' -> add(
|
||||
VarInsnNode(
|
||||
Opcodes.FLOAD,
|
||||
index
|
||||
)
|
||||
)
|
||||
'J' -> add(
|
||||
VarInsnNode(
|
||||
Opcodes.LLOAD,
|
||||
index
|
||||
)
|
||||
)
|
||||
'D' -> add(
|
||||
VarInsnNode(
|
||||
Opcodes.DLOAD,
|
||||
index
|
||||
)
|
||||
)
|
||||
else -> throw IllegalStateException("Invalid Type: $type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun InsnList.addReturn(type: Char) {
|
||||
when (type) {
|
||||
'L' -> add(
|
||||
InsnNode(
|
||||
Opcodes.ARETURN
|
||||
)
|
||||
)
|
||||
'I', 'S', 'B', 'C', 'Z' -> add(
|
||||
InsnNode(
|
||||
Opcodes.IRETURN
|
||||
)
|
||||
)
|
||||
'F' -> add(
|
||||
InsnNode(
|
||||
Opcodes.FRETURN
|
||||
)
|
||||
)
|
||||
'J' -> add(
|
||||
InsnNode(
|
||||
Opcodes.LRETURN
|
||||
)
|
||||
)
|
||||
'D' -> add(
|
||||
InsnNode(
|
||||
Opcodes.DRETURN
|
||||
)
|
||||
)
|
||||
'V' -> add(
|
||||
InsnNode(
|
||||
Opcodes.RETURN
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
package me.shedaniel.architect.plugin.transformers
|
||||
|
||||
import me.shedaniel.architect.plugin.Transformer
|
||||
import me.shedaniel.architect.plugin.utils.Transform
|
||||
import org.gradle.api.Project
|
||||
import org.objectweb.asm.Opcodes
|
||||
import org.objectweb.asm.tree.AnnotationNode
|
||||
import org.objectweb.asm.tree.MethodInsnNode
|
||||
import java.nio.file.Path
|
||||
|
||||
object TransformForgeBytecode : Transformer {
|
||||
val forgeEvent = "Lme/shedaniel/architectury/ForgeEvent;"
|
||||
val forgeEventCancellable = "Lme/shedaniel/architectury/ForgeEventCancellable;"
|
||||
val cancellable = "Lnet/minecraftforge/eventbus/api/Cancelable;"
|
||||
|
||||
private val environmentClass = "net/fabricmc/api/Environment"
|
||||
|
||||
override fun invoke(project: Project, input: Path, output: Path) {
|
||||
Transform.transform(input, output) { node, classAdder ->
|
||||
if (node.access and Opcodes.ACC_INTERFACE == 0) {
|
||||
if (node.visibleAnnotations?.any { it.desc == forgeEvent || it.desc == forgeEventCancellable } == true) {
|
||||
node.superName = "net/minecraftforge/eventbus/api/Event"
|
||||
node.methods.forEach {
|
||||
if (it.name == "<init>") {
|
||||
for (insnNode in it.instructions) {
|
||||
if (insnNode.opcode == Opcodes.INVOKESPECIAL) {
|
||||
insnNode as MethodInsnNode
|
||||
if (insnNode.name == "<init>" && insnNode.owner == "java/lang/Object") {
|
||||
insnNode.owner = "net/minecraftforge/eventbus/api/Event"
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
node.signature?.let {
|
||||
node.signature = it.substringBeforeLast('L') + "Lnet/minecraftforge/eventbus/api/Event;"
|
||||
}
|
||||
// if @ForgeEventCancellable, add the cancellable annotation from forge
|
||||
node.visibleAnnotations.apply {
|
||||
if (any { it.desc == forgeEventCancellable }) {
|
||||
add(AnnotationNode(cancellable))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
node.visibleAnnotations = (node.visibleAnnotations ?: mutableListOf()).apply {
|
||||
val invisibleEnvironments =
|
||||
node.invisibleAnnotations?.filter { it.desc == "L${environmentClass};" } ?: emptyList()
|
||||
node.invisibleAnnotations?.removeAll(invisibleEnvironments)
|
||||
addAll(invisibleEnvironments)
|
||||
}
|
||||
node.fields.forEach { field ->
|
||||
field.visibleAnnotations = (field.visibleAnnotations ?: mutableListOf()).apply {
|
||||
val invisibleEnvironments =
|
||||
field.invisibleAnnotations?.filter { it.desc == "L${environmentClass};" } ?: emptyList()
|
||||
field.invisibleAnnotations?.removeAll(invisibleEnvironments)
|
||||
addAll(invisibleEnvironments)
|
||||
}
|
||||
}
|
||||
node.methods.forEach { method ->
|
||||
method.visibleAnnotations = (method.visibleAnnotations ?: mutableListOf()).apply {
|
||||
val invisibleEnvironments =
|
||||
method.invisibleAnnotations?.filter { it.desc == "L${environmentClass};" } ?: emptyList()
|
||||
method.invisibleAnnotations?.removeAll(invisibleEnvironments)
|
||||
addAll(invisibleEnvironments)
|
||||
}
|
||||
}
|
||||
node
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,104 +0,0 @@
|
||||
package me.shedaniel.architect.plugin.transformers
|
||||
|
||||
import me.shedaniel.architect.plugin.Transformer
|
||||
import me.shedaniel.architect.plugin.TransformerStepSkipped
|
||||
import me.shedaniel.architect.plugin.utils.validateJarFs
|
||||
import net.fabricmc.loom.LoomGradleExtension
|
||||
import net.fabricmc.loom.util.LoggerFilter
|
||||
import net.fabricmc.tinyremapper.*
|
||||
import org.gradle.api.Project
|
||||
import org.objectweb.asm.ClassWriter
|
||||
import org.objectweb.asm.Opcodes
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
|
||||
object TransformForgeEnvironment : Transformer {
|
||||
override fun invoke(project: Project, input: Path, output: Path) {
|
||||
val remapperBuilder: TinyRemapper.Builder = TinyRemapper.newRemapper()
|
||||
.withMappings(remapEnvironment())
|
||||
.skipLocalVariableMapping(true)
|
||||
|
||||
mapMixin(project, remapperBuilder)
|
||||
|
||||
val classpathFiles: Set<File> = LinkedHashSet(
|
||||
project.configurations.getByName("compileClasspath").files
|
||||
)
|
||||
val classpath = classpathFiles.asSequence().map { obj: File -> obj.toPath() }
|
||||
.filter { p: Path -> input != p && Files.exists(p) }.toList().toTypedArray()
|
||||
val remapper = remapperBuilder.build()
|
||||
|
||||
LoggerFilter.replaceSystemOut()
|
||||
try {
|
||||
project.validateJarFs(output)
|
||||
OutputConsumerPath.Builder(output).build().use { outputConsumer ->
|
||||
outputConsumer.addNonClassFiles(input, NonClassCopyMode.FIX_META_INF, null)
|
||||
remapper.readClassPath(*classpath)
|
||||
remapper.readInputs(input)
|
||||
remapper.apply(outputConsumer)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException("Failed to remap $input to $output", e)
|
||||
} finally {
|
||||
remapper.finish()
|
||||
}
|
||||
}
|
||||
|
||||
private fun remapEnvironment(): IMappingProvider = IMappingProvider { out ->
|
||||
out.acceptClass("net/fabricmc/api/Environment", "net/minecraftforge/api/distmarker/OnlyIn")
|
||||
out.acceptClass("net/fabricmc/api/EnvType", "net/minecraftforge/api/distmarker/Dist")
|
||||
out.acceptField(
|
||||
IMappingProvider.Member("net/fabricmc/api/EnvType", "SERVER", "Lnet/fabricmc/api/EnvType;"),
|
||||
"DEDICATED_SERVER"
|
||||
)
|
||||
}
|
||||
|
||||
private fun mapMixin(project: Project, remapperBuilder: TinyRemapper.Builder) {
|
||||
val loomExtension = project.extensions.getByType(LoomGradleExtension::class.java)
|
||||
val srg = project.extensions.getByType(LoomGradleExtension::class.java).mappingsProvider.mappingsWithSrg
|
||||
for (mixinMapFile in loomExtension.allMixinMappings) {
|
||||
if (mixinMapFile.exists()) {
|
||||
remapperBuilder.withMappings { sink ->
|
||||
TinyUtils.createTinyMappingProvider(mixinMapFile.toPath(), "named", "intermediary").load(object :
|
||||
IMappingProvider.MappingAcceptor {
|
||||
override fun acceptClass(srcName: String, dstName: String) {
|
||||
sink.acceptClass(dstName, srg.classes
|
||||
.firstOrNull { it.getName("intermediary") == dstName }
|
||||
?.getName("srg") ?: dstName
|
||||
)
|
||||
}
|
||||
|
||||
override fun acceptMethod(method: IMappingProvider.Member, dstName: String) {
|
||||
sink.acceptMethod(
|
||||
IMappingProvider.Member(method.owner, dstName, method.desc),
|
||||
srg.classes
|
||||
.flatMap { it.methods }
|
||||
.firstOrNull { it.getName("intermediary") == dstName }
|
||||
?.getName("srg") ?: dstName)
|
||||
}
|
||||
|
||||
override fun acceptField(field: IMappingProvider.Member, dstName: String) {
|
||||
sink.acceptField(
|
||||
IMappingProvider.Member(field.owner, dstName, field.desc),
|
||||
srg.classes
|
||||
.flatMap { it.fields }
|
||||
.firstOrNull { it.getName("intermediary") == dstName }
|
||||
?.getName("srg") ?: dstName)
|
||||
}
|
||||
|
||||
override fun acceptMethodArg(method: IMappingProvider.Member, lvIndex: Int, dstName: String) {}
|
||||
|
||||
override fun acceptMethodVar(
|
||||
method: IMappingProvider.Member,
|
||||
lvIndex: Int,
|
||||
startOpIdx: Int,
|
||||
asmIndex: Int,
|
||||
dstName: String
|
||||
) {
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +0,0 @@
|
||||
package me.shedaniel.architect.plugin.transformers
|
||||
|
||||
import me.shedaniel.architect.plugin.ArchitectPluginExtension
|
||||
import me.shedaniel.architect.plugin.Transformer
|
||||
import me.shedaniel.architect.plugin.TransformerStepSkipped
|
||||
import me.shedaniel.architect.plugin.projectUniqueIdentifier
|
||||
import me.shedaniel.architect.plugin.utils.validateJarFs
|
||||
import net.fabricmc.loom.util.LoggerFilter
|
||||
import net.fabricmc.tinyremapper.IMappingProvider
|
||||
import net.fabricmc.tinyremapper.NonClassCopyMode
|
||||
import net.fabricmc.tinyremapper.OutputConsumerPath
|
||||
import net.fabricmc.tinyremapper.TinyRemapper
|
||||
import org.gradle.api.Project
|
||||
import java.io.File
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
|
||||
object TransformInjectables : Transformer {
|
||||
const val expectPlatform = "Lme/shedaniel/architectury/ExpectPlatform;"
|
||||
const val expectPlatformNew = "Lme/shedaniel/architectury/annotations/ExpectPlatform;"
|
||||
|
||||
override fun invoke(project: Project, input: Path, output: Path) {
|
||||
if (project.extensions.getByType(ArchitectPluginExtension::class.java).injectInjectables) {
|
||||
transformArchitecturyInjectables(project, input, output)
|
||||
} else {
|
||||
Files.copy(input, output)
|
||||
throw TransformerStepSkipped
|
||||
}
|
||||
}
|
||||
|
||||
private fun transformArchitecturyInjectables(project: Project, input: Path, output: Path) {
|
||||
val remapper = TinyRemapper.newRemapper()
|
||||
.withMappings { sink ->
|
||||
sink.acceptClass(
|
||||
"me/shedaniel/architectury/targets/ArchitecturyTarget",
|
||||
project.projectUniqueIdentifier() + "/PlatformMethods"
|
||||
)
|
||||
sink.acceptMethod(
|
||||
IMappingProvider.Member(
|
||||
"me/shedaniel/architectury/targets/ArchitecturyTarget",
|
||||
"getCurrentTarget",
|
||||
"()Ljava/lang/String;"
|
||||
), "getModLoader"
|
||||
)
|
||||
}
|
||||
.build()
|
||||
|
||||
val classpathFiles: Set<File> = LinkedHashSet(
|
||||
project.configurations.getByName("compileClasspath").files
|
||||
)
|
||||
val classpath = classpathFiles.asSequence().map { obj: File -> obj.toPath() }
|
||||
.filter { p: Path -> Files.exists(p) }.toList().toTypedArray()
|
||||
|
||||
LoggerFilter.replaceSystemOut()
|
||||
try {
|
||||
project.validateJarFs(output)
|
||||
OutputConsumerPath.Builder(output).build().use { outputConsumer ->
|
||||
outputConsumer.addNonClassFiles(input, NonClassCopyMode.UNCHANGED, null)
|
||||
remapper.readClassPath(*classpath)
|
||||
remapper.readInputs(input)
|
||||
remapper.apply(outputConsumer)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
throw RuntimeException("Failed to remap $input to $output", e)
|
||||
} finally {
|
||||
remapper.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
package me.shedaniel.architect.plugin.utils
|
||||
|
||||
import org.gradle.api.Project
|
||||
import java.net.URI
|
||||
import java.nio.file.FileSystemNotFoundException
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.ProviderNotFoundException
|
||||
import java.nio.file.spi.FileSystemProvider
|
||||
|
||||
fun Project.validateJarFs(path: Path) {
|
||||
val uri = URI("jar:" + path.toUri().toString())
|
||||
val provider = FileSystemProvider.installedProviders().firstOrNull { it.scheme == uri.scheme }
|
||||
?: throw ProviderNotFoundException("Provider \"${uri.scheme}\" not found")
|
||||
try {
|
||||
val fs = provider.getFileSystem(uri)
|
||||
val cl = fs.javaClass.classLoader
|
||||
if (fs.isOpen) {
|
||||
logger.error("Detected open FS on $path! Forcefully closing the FS! The FS is created on $cl!")
|
||||
fs.close()
|
||||
}
|
||||
} catch (ignored: FileSystemNotFoundException) {
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user