1 @file:Suppress("UnstableApiUsage")
3 package me.shedaniel.plugin.architect
5 import net.fabricmc.loom.LoomGradleExtension
6 import net.fabricmc.loom.util.GradleSupport
7 import net.fabricmc.loom.util.TinyRemapperMappingsHelper
8 import net.fabricmc.mapping.tree.TinyTree
9 import net.fabricmc.tinyremapper.IMappingProvider
10 import net.fabricmc.tinyremapper.NonClassCopyMode
11 import net.fabricmc.tinyremapper.OutputConsumerPath
12 import net.fabricmc.tinyremapper.TinyRemapper
13 import org.gradle.api.file.RegularFileProperty
14 import org.gradle.api.tasks.TaskAction
15 import org.gradle.jvm.tasks.Jar
17 import java.io.FileNotFoundException
18 import java.io.InputStream
20 import java.nio.file.Files
21 import java.nio.file.Path
24 open class RemapMCPTask : Jar() {
25 private val fromM: String = "named"
26 private val toM: String = "official"
27 val input: RegularFileProperty = GradleSupport.getfileProperty(project)
31 val input: Path = this.input.asFile.get().toPath()
32 val output: Path = this.archiveFile.get().asFile.toPath()
34 output.toFile().delete()
36 if (!Files.exists(input)) {
37 throw FileNotFoundException(input.toString())
40 val remapperBuilder: TinyRemapper.Builder = TinyRemapper.newRemapper()
42 val classpathFiles: Set<File> = LinkedHashSet(
43 project.configurations.getByName("compileClasspath").files
45 val classpath = classpathFiles.asSequence().map { obj: File -> obj.toPath() }.filter { p: Path -> input != p && Files.exists(p) }.toList().toTypedArray()
47 val mappings = getMappings()
48 val mojmapToMcpClass = createMojmapToMcpClass(mappings)
49 remapperBuilder.withMappings(remapToMcp(TinyRemapperMappingsHelper.create(mappings, fromM, fromM, false), mojmapToMcpClass))
50 remapperBuilder.ignoreFieldDesc(true)
51 remapperBuilder.skipLocalVariableMapping(true)
53 project.logger.lifecycle(":remapping " + input.fileName)
55 val architectFolder = project.rootProject.buildDir.resolve("tmp/architect")
56 architectFolder.deleteRecursively()
57 architectFolder.mkdirs()
58 val manifestFile = architectFolder.resolve("META-INF/MANIFEST.MF")
59 manifestFile.parentFile.mkdirs()
60 manifestFile.writeText("""
66 val remapper = remapperBuilder.build()
69 OutputConsumerPath.Builder(output).build().use { outputConsumer ->
70 outputConsumer.addNonClassFiles(input, NonClassCopyMode.SKIP_META_INF, null)
71 outputConsumer.addNonClassFiles(architectFolder.toPath(), NonClassCopyMode.UNCHANGED, null)
72 remapper.readClassPath(*classpath)
73 remapper.readInputs(input)
74 remapper.apply(outputConsumer)
76 } catch (e: Exception) {
78 throw RuntimeException("Failed to remap $input to $output", e)
81 architectFolder.deleteRecursively()
84 if (!Files.exists(output)) {
85 throw RuntimeException("Failed to remap $input to $output - file missing!")
89 private fun remapToMcp(parent: IMappingProvider, mojmapToMcpClass: Map<String, String>): IMappingProvider = IMappingProvider {
90 it.acceptClass("net/fabricmc/api/Environment", "net/minecraftforge/api/distmarker/OnlyIn")
91 it.acceptClass("net/fabricmc/api/EnvType", "net/minecraftforge/api/distmarker/Dist")
92 it.acceptField(IMappingProvider.Member("net/fabricmc/api/EnvType", "SERVER", "Lnet/fabricmc/api/EnvType;"), "DEDICATED_SERVER")
94 parent.load(object : IMappingProvider.MappingAcceptor {
95 override fun acceptClass(srcName: String?, dstName: String?) {
96 it.acceptClass(srcName, mojmapToMcpClass[srcName] ?: srcName)
99 override fun acceptMethod(method: IMappingProvider.Member?, dstName: String?) {
102 override fun acceptMethodArg(method: IMappingProvider.Member?, lvIndex: Int, dstName: String?) {
105 override fun acceptMethodVar(method: IMappingProvider.Member?, lvIndex: Int, startOpIdx: Int, asmIndex: Int, dstName: String?) {
108 override fun acceptField(field: IMappingProvider.Member?, dstName: String?) {
113 private fun getMappings(): TinyTree {
114 val loomExtension = project.extensions.getByType(LoomGradleExtension::class.java)
115 return loomExtension.mappingsProvider.mappings
118 private fun getRootExtension(): ArchitectPluginExtension =
119 project.rootProject.extensions.getByType(ArchitectPluginExtension::class.java)
121 private fun createMojmapToMcpClass(mappings: TinyTree): Map<String, String> {
122 val mcpMappings = readMCPMappings(getRootExtension().minecraft)
123 val mutableMap = mutableMapOf<String, String>()
124 mappings.classes.forEach { clazz ->
125 val official = clazz.getName("official")
126 val named = clazz.getName("named")
127 val mcp = mcpMappings[official]
129 mutableMap[named] = mcp
135 private fun readMCPMappings(version: String): Map<String, String> {
136 val file = project.rootProject.file(".gradle/mappings/mcp-$version.tsrg")
137 if (file.exists().not()) {
138 file.parentFile.mkdirs()
139 file.writeText(URL("https://raw.githubusercontent.com/MinecraftForge/MCPConfig/master/versions/release/$version/joined.tsrg").readText())
141 return mutableMapOf<String, String>().also { readMappings(it, file.inputStream()) }
144 private fun readMappings(mutableMap: MutableMap<String, String>, inputStream: InputStream) {
145 inputStream.bufferedReader().forEachLine {
146 if (!it.startsWith("\t")) {
147 val split = it.split(" ")
149 val className = split[1]
150 mutableMap[obf] = className