Compare commits

..

3 commits

Author SHA1 Message Date
moehreag c2d6800981 remove support for non-mojmap mappings
All checks were successful
Publish to snapshot maven / build (push) Successful in 59s
2024-10-28 14:53:05 +01:00
moehreag cbdfac65fc broken-refmap-remapping 2024-10-28 14:15:43 +01:00
moehreag 54fb5fe91e add refmap generation and remapping 2024-10-09 15:36:23 +02:00
11 changed files with 72 additions and 876 deletions

View file

@ -7,7 +7,7 @@ plugins {
}
group = "dev.frogmc"
version = "0.0.1-alpha.27" + ("+local".takeUnless { project.hasProperty("FrogMCSnapshotsMavenPassword") } ?: "")
version = "0.0.1-alpha.28" + ("+local".takeUnless { project.hasProperty("FrogMCSnapshotsMavenPassword") } ?: "")
repositories {
maven {
@ -24,7 +24,7 @@ repositories {
}
dependencies {
implementation("dev.frogmc:thyroxine:0.0.1-alpha.17")
implementation("dev.frogmc:thyroxine:0.0.1-alpha.18")
implementation("org.ow2.asm:asm:9.7")
implementation("org.ow2.asm:asm-commons:9.7")
implementation("org.ow2.asm:asm-tree:9.7")

View file

@ -2,4 +2,3 @@ plugins {
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
}
rootProject.name = "phytotelma"

View file

@ -8,40 +8,28 @@ import dev.frogmc.phytotelma.build.PhytotelmaBuildTask
import dev.frogmc.phytotelma.common.Env
import dev.frogmc.phytotelma.ext.PhytotelmaGradleExtension
import dev.frogmc.phytotelma.ext.PhytotelmaGradleExtensionImpl
import dev.frogmc.phytotelma.mappings.renameDstNamespace
import dev.frogmc.phytotelma.mixin.remapper.MixinAnnotationRemapper
import dev.frogmc.phytotelma.nest.NestStripper
import dev.frogmc.phytotelma.run.AssetDownloader
import dev.frogmc.phytotelma.run.RunConfigGenerator
import dev.frogmc.phytotelma.run.task.RunGameTask
import dev.frogmc.phytotelma.vineflower.FrogJavadocProvider
import dev.frogmc.thyroxine.RemappingStep
import dev.frogmc.thyroxine.Thyroxine
import dev.frogmc.thyroxine.api.Mapper
import dev.frogmc.thyroxine.api.data.MappingBundle
import dev.frogmc.thyroxine.api.data.MappingData
import dev.frogmc.thyroxine.provider.MojmapProvider
import dev.frogmc.thyroxine.writer.tiny.TinyV2Writer
import net.fabricmc.fernflower.api.IFabricJavadocProvider
import org.gradle.api.Plugin
import org.gradle.api.Project
import org.gradle.api.plugins.JavaBasePlugin
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.tasks.Delete
import org.gradle.internal.impldep.org.jsoup.helper.Consumer
import org.jetbrains.java.decompiler.main.Fernflower
import org.jetbrains.java.decompiler.main.decompiler.PrintStreamLogger
import org.jetbrains.java.decompiler.main.decompiler.SingleFileSaver
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences
import org.objectweb.asm.commons.ClassRemapper
import java.io.OutputStream
import java.io.PrintStream
import java.net.URI
import java.nio.charset.StandardCharsets
import java.nio.file.*
import java.nio.file.attribute.BasicFileAttributes
import java.nio.file.FileSystems
import java.nio.file.Files
import java.nio.file.Path
import java.nio.file.StandardCopyOption
import java.time.LocalDateTime
import java.util.*
import kotlin.io.path.*
@ -62,10 +50,6 @@ class PhytotelmaPlugin : Plugin<Project> {
}
project.repositories.apply {
maven {
it.name = "Remapped Mod Dependencies"
it.url = project.layout.buildDirectory.asFile.get().resolve("remappedMods").toURI()
}
maven {
it.name = "Minecraft/Local"
it.url = localCacheDir.toUri()
@ -91,23 +75,6 @@ class PhytotelmaPlugin : Plugin<Project> {
PhytotelmaGradleExtensionImpl::class.java
)
ModConfigurations.configurations.forEach { conf ->
project.configurations.create("mod" + conf.name.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.ROOT) else it.toString() }) { c ->
c.isCanBeResolved = true
c.isCanBeConsumed = false
when (conf.dependencyType) {
DependencyType.RUNTIME -> project.configurations.getByName(JavaPlugin.RUNTIME_ELEMENTS_CONFIGURATION_NAME)
.extendsFrom(c)
DependencyType.COMPILE -> project.configurations.getByName(JavaPlugin.API_ELEMENTS_CONFIGURATION_NAME)
.extendsFrom(c)
DependencyType.NONE -> {}
}
}
}
project.configurations.register(Constants.INCLUDE_CONFIGURATION) {
it.isCanBeResolved = true
it.isCanBeConsumed = false
@ -142,7 +109,7 @@ class PhytotelmaPlugin : Plugin<Project> {
projectData.remappedGameJarPath!!.resolveSibling(
fileName.substring(
0,
fileName.lastIndexOf("-")
fileName.lastIndexOf(".")
) + "-sources.jar"
)
if (output.exists()) {
@ -150,10 +117,10 @@ class PhytotelmaPlugin : Plugin<Project> {
output.deleteExisting()
}
val options = mutableMapOf<String, Any>()
println("Preparing Parchment...")
println("Preparing javadocs...")
val javadocs = ProjectStorage.get(project).mappings!!
options[IFabricJavadocProvider.PROPERTY_NAME] =
FrogJavadocProvider(javadocs.get(ProjectStorage.get(project).targetNamespace))
FrogJavadocProvider(javadocs.get("named"))
println("Decompiling...")
val logger = PrintStreamLogger(PrintStream(System.out))
@ -200,21 +167,6 @@ class PhytotelmaPlugin : Plugin<Project> {
project.tasks.getByName(JavaPlugin.JAR_TASK_NAME).outputs.upToDateWhen { false }
project.tasks.getByName(JavaPlugin.JAR_TASK_NAME).actions.addLast { task ->
val storage = ProjectStorage.get(project)
if (storage.targetNamespace != Constants.MOJMAP_NAMESPACE) {
val moj = if (storage.intermediaryNs == Constants.MOJMAP_NAMESPACE) {
MojmapProvider.get(
storage.minecraftVersion!!,
globalCacheDir.resolve("net/minecraft/client/${storage.minecraftVersion}/client-${storage.minecraftVersion}.txt"),
globalCacheDir.resolve("net/minecraft/server/${storage.minecraftVersion}/server-${storage.minecraftVersion}.txt")
).reverse().renameDstNamespace(Constants.MOJMAP_NAMESPACE)
} else null
val mappings = (moj?.let {
MappingBundle.merge(
storage.mappings!!.reverse(), it
)
} ?: storage.mappings!!).forNamespaces(storage.targetNamespace, storage.intermediaryNs)
task.outputs.files.forEach { file ->
val temp = Files.createTempFile("", file.name)
Files.copy(file.toPath(), temp, StandardCopyOption.REPLACE_EXISTING)
@ -225,29 +177,19 @@ class PhytotelmaPlugin : Plugin<Project> {
.plus(
"""
Built-By: Phytotelma ${this.javaClass.`package`.implementationVersion}
Target-Namespace: ${data.intermediaryNs}
Target-Namespace: Mojmap
Built-For: Minecraft ${data.minecraftVersion}
Build-Date: ${LocalDateTime.now()}
""".trimIndent()
).plus(data.jarManifestProperties.entries.joinToString("\n") { it.key+": "+it.value})
).plus(data.jarManifestProperties.entries.joinToString("\n") { it.key + ": " + it.value })
manifest.writeLines(lines, StandardCharsets.UTF_8)
val metadata = fs.getPath(Constants.MOD_METADATA_FILE)
tomlParser.parse(metadata, FileNotFoundAction.READ_NOTHING)
.get<String>("frog.extensions.frogloader.accesswidener")?.let { name ->
val aw = metadata.resolveSibling(name)
AccessWidener.checkAW(aw, ProjectStorage.get(project).remappedGameJarPath!!)
remapAccesswidener(mappings, aw)
}
}
Thyroxine.remap(
mappings,
temp,
file.toPath(),
false,
defaultRemappingSteps(ProjectStorage.get(project).remappedGameJarPath!!)
)
Files.deleteIfExists(temp)
}
}
}
@ -267,184 +209,6 @@ class PhytotelmaPlugin : Plugin<Project> {
task.group = Constants.TASK_GROUP
task.delete = setOf(globalCacheDir)
}
project.afterEvaluate {
remapModDependencies(project)
}
}
private fun defaultRemappingSteps(vararg sourceNSJars: Path): MutableList<RemappingStep> {
return mutableListOf(
RemappingStep(::ClassRemapper),
RemappingStep { cv, mapper -> MixinAnnotationRemapper(cv, mapper, *sourceNSJars) },
)
}
private fun remapModDependencies(project: Project) {
val out = System.out
// Mute the output from thyroxine as there may be a lot of remapping operations here
System.setOut(PrintStream(OutputStream.nullOutputStream()))
val mojmapGameJar = ProjectStorage.get(project).remappedGameJarPath!!.resolveSibling("mojmap.jar")
val version = ProjectStorage.get(project).minecraftVersion!!
val officialClientJar = VersionChecker.downloadClient(project, version)
val officialServerJar = VersionChecker.downloadServer(project, version)
ModConfigurations.configurations.forEach { conf ->
val artifacts = project.configurations.getByName("mod" + conf.name.replaceFirstChar {
if (it.isLowerCase()) it.titlecase(
Locale.getDefault()
) else it.toString()
})
.resolvedConfiguration.resolvedArtifacts
if (artifacts.isEmpty()) {
return
}
val mojmap = MojmapProvider.get(version,
globalCacheDir.resolve("net/minecraft/client/${version}/client-${version}.txt"),
globalCacheDir.resolve("net/minecraft/server/${version}/server-${version}.txt"))
if (mojmapGameJar.notExists()) {
val remappedClient = officialClientJar.resolveSibling("client-$version-mojmap.jar")
val remappedServer = officialServerJar.resolveSibling("server-$version-mojmap.jar")
Thyroxine.remap(mojmap.data.first(), officialClientJar, remappedClient, true, false)
Thyroxine.remap(mojmap.data.first(), officialServerJar, remappedServer, true, false)
FileSystems.newFileSystem(mojmapGameJar, mutableMapOf<String, String>("create" to "true")).use { mergedFs ->
val consumer = object : Consumer<FileSystem> {
override fun accept(fs: FileSystem) {
Files.walkFileTree(fs.getPath("/"), object : SimpleFileVisitor<Path>() {
override fun visitFile(file: Path, attrs: BasicFileAttributes): FileVisitResult {
val target = mergedFs.getPath(file.toString())
target.createParentDirectories()
if (target.notExists()) {
file.copyTo(target)
}
return super.visitFile(file, attrs)
}
})
}
}
FileSystems.newFileSystem(remappedClient).use { consumer.accept(it) }
FileSystems.newFileSystem(remappedServer).use { consumer.accept(it) }
}
}
val target = project.configurations.create("mod" + conf.name.replaceFirstChar {
if (it.isLowerCase()) it.titlecase(
Locale.getDefault()
) else it.toString()
} + "Mapped") { c ->
c.isTransitive = false
conf.classpathNames.forEach {
project.configurations.getByName(it).extendsFrom(c)
}
}
val storage = ProjectStorage.get(project)
TinyV2Writer.write(
storage.mappings!!,
project.projectDir.resolve("storageMappings.tiny").writer()
)
val officialStore = storage.mappings!!.forNamespaces(storage.targetNamespace!!, "official").reverse()
TinyV2Writer.write(
MappingBundle(officialStore),
project.projectDir.resolve("officialStore.tiny").toPath().writer()
)
val mojOfficial = mojmap.reverse().renameDstNamespace(Constants.MOJMAP_NAMESPACE).data[0].reverse()
val targetPath = project.layout.buildDirectory.asFile.get().toPath().resolve("remappedMods")
.resolve("dev/frogmc/phytotelma/remapped_mods")
val remappedPaths = mutableListOf<Path>()
artifacts.forEach { artifact ->
val group = artifact.moduleVersion.id.group
val name = artifact.moduleVersion.id.name
val groupname = (group + "_" + name).replace(".", "_")
val artifactVersion = artifact.moduleVersion.id.version
val classifier = artifact.classifier
val remappedPath = targetPath.resolve(groupname).resolve(artifactVersion)
.resolve(groupname + "-" + artifactVersion + (classifier?.let { "-$it" } ?: "") + ".jar")
remappedPath.createParentDirectories()
remappedPaths.add(remappedPath)
val pom = remappedPath.resolveSibling(remappedPath.fileName.toString().removeSuffix(".jar") + ".pom")
pom.writeText(
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<project xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\" xmlns=\"http://maven.apache.org/POM/4.0.0\"\n" +
"\t\t xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n" +
"\t<modelVersion>4.0.0</modelVersion>\n" +
"\t<groupId>dev.frogmc.phytotelma.remapped_mods</groupId>\n" +
"\t<artifactId>$groupname</artifactId>\n" +
"\t<version>$artifactVersion</version>\n" +
"</project>"
)
remappedPaths.add(pom)
val temp = remappedPath.resolveSibling(remappedPath.fileName.toString() + ".tmp")
Thyroxine.remap(
mojOfficial, artifact.file.toPath(), temp, false, defaultRemappingSteps(
mojmapGameJar
), mojmapGameJar
)
Thyroxine.remap(
officialStore, temp, remappedPath, false, defaultRemappingSteps(
officialClientJar, officialServerJar
), officialClientJar, officialServerJar
)
Files.deleteIfExists(temp)
NestStripper.stripJij(remappedPath)
FileSystems.newFileSystem(remappedPath).use { fs ->
val metadata = fs.getPath(Constants.MOD_METADATA_FILE)
tomlParser.parse(metadata, FileNotFoundAction.READ_NOTHING)
.get<String>("frog.extensions.frogloader.accesswidener")?.let { name ->
val aw = metadata.resolveSibling(name)
remapAccesswidener(mojOfficial, aw)
remapAccesswidener(officialStore, aw)
AccessWidener.checkAW(aw, ProjectStorage.get(project).remappedGameJarPath!!)
}
}
project.dependencies.add(
target.name,
"dev.frogmc.phytotelma.remapped_mods:$groupname:$artifactVersion" + (classifier?.let { ":$it" }
?: "")
)
}
Files.deleteIfExists(mojmapGameJar)
}
System.setOut(out)
}
private fun remapAccesswidener(mappings: MappingData, aw: Path) {
val mapper = Mapper(mappings) { listOf() }
val buffer = buildString {
aw.forEachLine {
if ((it.contains("\t") || it.contains(" ")) && !it.startsWith("#")) {
val parts = it.split("[\\t #]+".toRegex()).toMutableList()
if (parts.size > 2) {
val type = parts[1]
when (type) {
"class" -> {
parts[2] = mapper.map(parts[2])
}
"field" -> {
parts[3] = mapper.mapFieldName(parts[2], parts[3], parts[4])
parts[4] = mapper.mapDesc(parts[4])
parts[2] = mapper.map(parts[2])
}
"method" -> {
parts[3] = mapper.mapMethodName(parts[2], parts[3], parts[4])
parts[4] = mapper.mapMethodDesc(parts[4])
parts[2] = mapper.map(parts[2])
}
}
appendLine(parts.joinToString(" "))
return@forEachLine
}
}
appendLine(it)
}
}
aw.writeText(buffer)
}
companion object {

View file

@ -71,15 +71,13 @@ object ProjectStorage {
class ProjectData(
var localCacheDir: Path?,
var minecraftVersion: String?,
var parchmentVersion: String?,
var remappedGameJarPath: Path?,
var mappings: MappingBundle?,
var mappingsName: String?,
var intermediaryNs: String?,
var targetNamespace: String?,
var manifestUrl: String?,
var jarManifestProperties: MutableMap<String, String>
) {
internal constructor() : this(null, null, null, null, null, null, null, null, mutableMapOf())
internal constructor() : this(null,null, null, null, null, null, mutableMapOf())
}
class ProjectDataTypeAdapter : TypeAdapter<ProjectData>() {
@ -91,8 +89,6 @@ class ProjectDataTypeAdapter : TypeAdapter<ProjectData>() {
out.name("remapped_game_jar_path").value(value.remappedGameJarPath?.absolutePathString())
out.name("mappings")
value.mappings?.let { MappingBundleTypeAdapter.write(out, it) } ?: out.nullValue()
out.name("mappings_name").value(value.mappingsName)
out.name("target_namespace").value(value.targetNamespace)
out.name("jar_manifest_properties")
out.beginObject()
value.jarManifestProperties.forEach { (s, s2) ->
@ -118,17 +114,13 @@ class ProjectDataTypeAdapter : TypeAdapter<ProjectData>() {
data.minecraftVersion = value
}
"parchment_version" -> {
data.parchmentVersion = value
}
"remapped_game_jar_path" -> {
data.remappedGameJarPath = Path.of(value)
}
"mappings_name" -> {
data.mappingsName = value
}
"target_namespace" -> {
data.targetNamespace = value
}
}
} else if (name == "mappings") {
data.mappings = MappingBundleTypeAdapter.read(r)

View file

@ -1,25 +1,10 @@
package dev.frogmc.phytotelma.ext
import dev.frogmc.phytotelma.Constants
import dev.frogmc.phytotelma.PhytotelmaPlugin
import dev.frogmc.phytotelma.ProjectStorage
import dev.frogmc.phytotelma.mappings.filterClasses
import dev.frogmc.phytotelma.mappings.renameDstNamespace
import dev.frogmc.phytotelma.mappings.renameNamespaces
import dev.frogmc.thyroxine.api.data.MappingBundle
import dev.frogmc.thyroxine.parser.tiny.TinyV2Parser
import dev.frogmc.thyroxine.provider.MojmapProvider
import dev.frogmc.thyroxine.provider.ParchmentProvider
import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import org.gradle.api.provider.Provider
import java.nio.charset.StandardCharsets
import java.nio.file.FileSystems
import java.nio.file.Path
import javax.inject.Inject
import kotlin.io.path.readText
@Suppress("MemberVisibilityCanBePrivate", "unused")
abstract class MinecraftConfiguration @Inject constructor(
@ -28,263 +13,9 @@ abstract class MinecraftConfiguration @Inject constructor(
) {
val version: Property<String> = objects.property(String::class.java).unset()
var mappings: Provider<MappingBundle> = mojmapParchment()
val intermediaryNamespace: Property<String> = objects.property(String::class.java).convention(Constants.MOJMAP_NAMESPACE)
val parchmentVersion: Property<String> = objects.property(String::class.java).unset()
val manifestUrl: Property<String> = objects.property(String::class.java).convention(Constants.MOJANG_MANIFEST_URL)
internal lateinit var mappingsName: String
internal lateinit var targetNamespace: String
fun mojmapParchment(): Provider<MappingBundle> {
return mojmapParchment {}
}
fun mojmapParchment(action: Action<ParchmentConfiguration>): Provider<MappingBundle> {
return project.provider {
val conf = objects.newInstance(ParchmentConfiguration::class.java)
conf.gameVersion.convention(version)
conf.version.convention(project.provider { ParchmentProvider.findForMinecraftVersion(conf.gameVersion.get()) })
action.execute(conf)
val cacheDir = PhytotelmaPlugin.globalCacheDir
mappingsName = "mojmap(${version.get()})+parchment(${conf.gameVersion.get()}, ${conf.version.get()})"
targetNamespace = Constants.MOJMAP_NAMESPACE
return@provider MappingBundle.merge(
MojmapProvider.get(
version.get(),
cacheDir.resolve("net/minecraft/client/${version.get()}/client-${version.get()}.txt"),
cacheDir.resolve("net/minecraft/server/${version.get()}/server-${version.get()}.txt")
).reverse(),
ParchmentProvider.getParchment(
conf.gameVersion.get(),
cacheDir.resolve("org/parchmentmc/parchment/${conf.gameVersion.get()}/${conf.version.get()}")
)
).renameDstNamespace(targetNamespace)
}
}
fun parchment(action: Action<ParchmentConfiguration>): Provider<MappingBundle> {
return project.provider {
val conf = objects.newInstance(ParchmentConfiguration::class.java)
conf.gameVersion.convention(version)
conf.version.convention(project.provider { ParchmentProvider.findForMinecraftVersion(conf.gameVersion.get()) })
action.execute(conf)
val cacheDir = PhytotelmaPlugin.globalCacheDir
mappingsName = "parchment(${conf.gameVersion.get()}, ${conf.version.get()})"
targetNamespace = "parchment"
return@provider ParchmentProvider.getParchment(
conf.gameVersion.get(),
cacheDir.resolve("org/parchmentmc/parchment/${conf.gameVersion.get()}/${conf.version.get()}")
).renameNamespaces(Constants.MOJMAP_NAMESPACE, "parchment")
}
}
fun mojmap(): Provider<MappingBundle> {
return project.provider {
val cacheDir = PhytotelmaPlugin.globalCacheDir
mappingsName = "mojmap(${version.get()})"
targetNamespace = Constants.MOJMAP_NAMESPACE
return@provider MojmapProvider.get(
version.get(),
cacheDir.resolve("net/minecraft/client/${version.get()}/client-${version.get()}.txt"),
cacheDir.resolve("net/minecraft/server/${version.get()}/server-${version.get()}.txt")
).reverse().renameDstNamespace(targetNamespace)
}
}
fun quiltMappings(action: Action<VersionConfiguration>): Provider<MappingBundle> {
return project.provider {
val conf = objects.newInstance(VersionConfiguration::class.java)
action.execute(conf)
if (!conf.version.isPresent()) {
error("No version provided for quilt mappings!")
}
mappingsName = "quilt-mappings(${conf.version.get()})"
targetNamespace = "quilt-mappings"
// Use qm via intermediary because hashed publications are broken
return@provider twoStepMappings(
"net.fabricmc:intermediary:${version.get()}:v2",
"org.quiltmc:quilt-mappings:${conf.version.get()}:intermediary-v2"
).flatten().renameDstNamespace(targetNamespace)
}
}
fun yarn(action: Action<VersionConfiguration>): Provider<MappingBundle> {
return project.provider {
val conf = objects.newInstance(VersionConfiguration::class.java)
action.execute(conf)
if (!conf.version.isPresent) {
error("No version provided for yarn!")
}
mappingsName = "yarn(${conf.version.get()})"
targetNamespace = "yarn"
// Use qm via intermediary because hashed publications are broken
return@provider twoStepMappings(
"net.fabricmc:intermediary:${version.get()}:v2",
"net.fabricmc:yarn:${conf.version.get()}:v2"
).flatten(true).renameDstNamespace(targetNamespace)
}
}
fun feather(action: Action<VersionConfiguration>): Provider<MappingBundle> {
return project.provider {
val conf = objects.newInstance(VersionConfiguration::class.java)
action.execute(conf)
if (!conf.version.isPresent) {
error("No version provided for feather!")
}
mappingsName = "feather(${conf.version.get()})"
targetNamespace = "feather"
intermediaryNamespace.set("intermediary")
ProjectStorage.get(project).jarManifestProperties["Calamus-Generation"] = "2"
return@provider twoStepMappings(
"net.ornithemc:calamus-intermediary-gen2:${version.get()}:v2",
"net.ornithemc:feather-gen2:${conf.version.get()}:v2"
).flatten(true).renameDstNamespace(targetNamespace)
}
}
fun twoStepMappings(action: Action<TwoStepMappingsConfiguration>): Provider<MappingBundle> {
return project.provider {
val conf = objects.newInstance(TwoStepMappingsConfiguration::class.java)
action.execute(conf)
if (!conf.first.isPresent) {
error("No version provided for first mapping step!")
}
if (!conf.second.isPresent) {
error("No version provided for second mapping step!")
}
mappingsName = "custom/two-step(${conf.first.get()}, ${conf.second.get()})"
val bundle = twoStepMappings(conf.first.get(), conf.second.get())
targetNamespace = bundle.flattenData().dstNamespace
return@provider bundle
}
}
fun tinyMappings(action: Action<NameConfiguration>): Provider<MappingBundle> {
return project.provider {
val conf = objects.newInstance(NameConfiguration::class.java)
action.execute(conf)
if (!conf.name.isPresent) {
error("No maven coordinate provided for tiny mappings!")
}
mappingsName = "custom/tiny(${conf.name.get()})"
val bundle = tinyMappings(conf.name.get())
targetNamespace = bundle.flattenData().dstNamespace
return@provider bundle
}
}
fun mappings(action: Action<MappingConfiguration>): Provider<MappingBundle> {
return project.provider {
val conf = objects.newInstance(MappingConfiguration::class.java)
action.execute(conf)
if (!conf.mappings.isPresent) {
error("No mappings provided!")
}
val bundle = conf.mappings.get()
mappingsName = "custom(${bundle.srcNamespaces()}, ${bundle.dstNamespaces()})"
targetNamespace = bundle.flattenData().dstNamespace
return@provider bundle
}
}
fun layer(action: Action<LayerConfiguration>): Provider<MappingBundle> {
return project.provider {
val conf = objects.newInstance(LayerConfiguration::class.java)
action.execute(conf)
var back = MappingBundle()
var name = "layer["
if (conf.layers.get().isEmpty()) {
mappingsName = "$name]"
targetNamespace = "official"
return@provider MappingBundle()
}
val layers = conf.layers.get().mapIndexed { index, provider ->
if (index == 0) {
val bundle = (provider.get() as MappingBundle).flatten()
name += mappingsName
back.insert(bundle)
return@mapIndexed bundle
}
val bundle = (provider.get() as MappingBundle)
.filterClasses { !it.startsWith("net/minecraft/unmapped") && !it.startsWith("net/minecraft/class_") }
.flatten(true)
name += ", $mappingsName"
back = back.flatten(false)
val prevDst = back.data[0].dstNamespace
val dst = bundle.data[0].dstNamespace
val remapped = MappingBundle.merge(bundle.reverse(), back)
.flatten(prevDst, dst)
.insert(back.docsForNamespace(dst))
back.insert(remapped)
return@mapIndexed remapped
}
mappingsName = "$name]"
val result = MappingBundle.merge(*layers.toTypedArray())
.flatten("official", layers.last().dstNamespaces()[0])
targetNamespace = result.data[0].dstNamespace
return@provider result
}
}
private fun twoStepMappings(intermediary: String, mappings: String): MappingBundle {
return MappingBundle.merge(tinyMappings(intermediary), tinyMappings(mappings))
}
private fun tinyMappings(name: String): MappingBundle {
mavenMappings(name).let { path ->
FileSystems.newFileSystem(path).use {
return TinyV2Parser.parse(it.getPath("mappings/mappings.tiny").readText(StandardCharsets.UTF_8))
}
}
}
@Suppress("UnstableApiUsage")
private fun mavenMappings(name: String): Path {
val configuration = project.configurations.create("mappings") {
it.isCanBeResolved = true
it.isCanBeDeclared = true
}
configuration.dependencies.add(project.dependencies.create(name))
val path = configuration.resolve().first().toPath()
project.configurations.remove(configuration)
return path
}
abstract class TwoStepMappingsConfiguration @Inject constructor(objects: ObjectFactory) {
val first: Property<String> = objects.property(String::class.java).unset()
val second: Property<String> = objects.property(String::class.java).unset()
}
abstract class ParchmentConfiguration @Inject constructor(objects: ObjectFactory) {
val gameVersion: Property<String> = objects.property(String::class.java).unset()
val version: Property<String> = objects.property(String::class.java).unset()
}
abstract class LayerConfiguration @Inject constructor(objects: ObjectFactory) {
internal val layers = objects.listProperty(Provider::class.java)
fun add(mappings: Provider<MappingBundle>): LayerConfiguration {
layers.add(mappings)
return this
}
}
abstract class MappingConfiguration @Inject constructor(objects: ObjectFactory) {
val mappings: Property<MappingBundle> = objects.property(MappingBundle::class.java).unset()
}
}
abstract class NameConfiguration @Inject constructor(objects: ObjectFactory) {
val name: Property<String> = objects.property(String::class.java).unset()
}
abstract class VersionConfiguration @Inject constructor(objects: ObjectFactory) {

View file

@ -7,19 +7,19 @@ import dev.frogmc.phytotelma.accesswidener.AccessWidener
import dev.frogmc.phytotelma.ext.datagen.DatagenExtension
import dev.frogmc.phytotelma.run.AssetDownloader
import dev.frogmc.phytotelma.run.RunConfigGenerator
import dev.frogmc.phytotelma.run.RuntimeAccessFixVisitor
import dev.frogmc.phytotelma.run.datagen.DatagenConfigGenerator
import dev.frogmc.phytotelma.run.datagen.DatagenTask
import dev.frogmc.thyroxine.RemappingStep
import dev.frogmc.thyroxine.Thyroxine
import dev.frogmc.thyroxine.api.data.MappingData
import dev.frogmc.thyroxine.api.data.MappingBundle
import dev.frogmc.thyroxine.provider.MojmapProvider
import dev.frogmc.thyroxine.provider.ParchmentProvider
import org.gradle.api.Action
import org.gradle.api.Project
import org.gradle.api.model.ObjectFactory
import org.gradle.api.plugins.JavaPlugin
import org.gradle.internal.impldep.org.jsoup.helper.Consumer
import java.nio.file.*
import java.nio.file.attribute.BasicFileAttributes
import java.util.function.Consumer
import javax.inject.Inject
import kotlin.io.path.copyTo
import kotlin.io.path.createParentDirectories
@ -38,14 +38,26 @@ abstract class PhytotelmaGradleExtensionImpl @Inject constructor(
error("No Minecraft version provided!")
}
val version = mcConf.version.get()
val parchmentVersion = mcConf.parchmentVersion.get()
val projectData = ProjectStorage.get(project)
projectData.manifestUrl = mcConf.manifestUrl.get()
if (VersionChecker.validateVersion(project, version, offlineMode = project.gradle.startParameter.isOffline)) {
projectData.minecraftVersion = version
val mappings = mcConf.mappings.get()
println("Using mappings: " + mcConf.mappingsName)
val mappings = MappingBundle.merge(
MappingBundle.merge(
MojmapProvider.get(
version,
projectData.localCacheDir!!.resolve("net/minecraft/client/${version}/client-${version}.txt"),
projectData.localCacheDir!!.resolve("net/minecraft/server/${version}/server-${version}.txt")
).reverse(),
ParchmentProvider.getParchment(
version,
projectData.localCacheDir!!.resolve("org/parchmentmc/parchment/${version}/${parchmentVersion}")
)
)
)
projectData.mappings = mappings
val clientJar = VersionChecker.downloadClient(project, version)
@ -56,18 +68,19 @@ abstract class PhytotelmaGradleExtensionImpl @Inject constructor(
projectData.localCacheDir!!.resolve("net/minecraft/server/$version/server-$version-remapped.jar")
remappedClientJar.createParentDirectories()
remappedServerJar.createParentDirectories()
if (remappedClientJar.notExists() || mcConf.mappingsName != projectData.mappingsName) {
val flattened = mappings.flattenData()
if (remappedClientJar.notExists() || parchmentVersion != projectData.parchmentVersion) {
println("Remapping client...")
Thyroxine.remap(
mappings.forNamespaces("official", mcConf.targetNamespace),
flattened,
clientJar, remappedClientJar, true, true
)
VersionChecker.saveClientPomFile(version, remappedClientJar.parent)
}
if (remappedServerJar.notExists() || mcConf.mappingsName != projectData.mappingsName) {
if (remappedServerJar.notExists() || parchmentVersion != projectData.parchmentVersion) {
println("Remapping server...")
Thyroxine.remap(
mappings.forNamespaces("official", mcConf.targetNamespace),
flattened,
serverJar, remappedServerJar, true, true
)
VersionChecker.saveServerPomFile(version, remappedServerJar.parent)
@ -76,12 +89,11 @@ abstract class PhytotelmaGradleExtensionImpl @Inject constructor(
projectData.localCacheDir!!.resolve("net/minecraft/minecraft-merged/$version/minecraft-merged-$version.jar")
projectData.remappedGameJarPath = mergedJar
var applyAW = AccessWidener.needsUpdate(project)
projectData.intermediaryNs = mcConf.intermediaryNamespace.get()
if (mergedJar.notExists() || applyAW || mcConf.mappingsName != projectData.mappingsName) {
projectData.mappingsName = mcConf.mappingsName
if (mergedJar.notExists() || applyAW || parchmentVersion != projectData.parchmentVersion) {
projectData.parchmentVersion = parchmentVersion
println("Merging game...")
FileSystems.newFileSystem(mergedJar, mutableMapOf<String, String>("create" to "true")).use { mergedFs ->
mergedJar.createParentDirectories()
FileSystems.newFileSystem(mergedJar, mapOf("create" to "true")).use { mergedFs ->
val consumer = object : Consumer<FileSystem> {
override fun accept(fs: FileSystem) {
Files.walkFileTree(fs.getPath("/"), object : SimpleFileVisitor<Path>() {
@ -99,10 +111,7 @@ abstract class PhytotelmaGradleExtensionImpl @Inject constructor(
FileSystems.newFileSystem(remappedClientJar).use { consumer.accept(it) }
FileSystems.newFileSystem(remappedServerJar).use { consumer.accept(it) }
}
projectData.targetNamespace = mcConf.targetNamespace
VersionChecker.saveMergedPomFile(version, mergedJar.parent)
RunConfigGenerator.generate(project)
applyAW = AccessWidener.hasAW(project)
@ -130,21 +139,6 @@ abstract class PhytotelmaGradleExtensionImpl @Inject constructor(
AccessWidener.apply(project, mergedJar)
}
}
project.afterEvaluate {
if (mcConf.targetNamespace != Constants.MOJMAP_NAMESPACE) {
Thyroxine.remap(
MappingData("", ""),
mergedJar,
mergedJar.resolveSibling(Constants.ALTERNATIVE_RUNTIME_JAR_NAME),
false,
listOf(RemappingStep { classVisitor, _ ->
RuntimeAccessFixVisitor(classVisitor)
})
)
} else {
Files.deleteIfExists(mergedJar.resolveSibling(Constants.ALTERNATIVE_RUNTIME_JAR_NAME))
}
}
}
}

View file

@ -1,275 +0,0 @@
package dev.frogmc.phytotelma.mixin.remapper
import com.google.common.base.Strings
import dev.frogmc.phytotelma.Constants
import org.objectweb.asm.*
import org.objectweb.asm.commons.AnnotationRemapper
import org.objectweb.asm.commons.ClassRemapper
import org.objectweb.asm.commons.FieldRemapper
import org.objectweb.asm.commons.MethodRemapper
import org.objectweb.asm.commons.Remapper
import org.objectweb.asm.tree.ClassNode
import java.nio.file.FileSystems
import java.nio.file.Path
import kotlin.io.path.exists
import kotlin.io.path.readBytes
class MixinAnnotationRemapper(
cv: ClassVisitor,
private val mapper: Remapper,
private vararg val contextJars: Path
) : ClassRemapper(Constants.ASM_VERSION, cv, mapper) {
private var target: String? = null
private var targetNode: ClassNode? = null
override fun visit(
version: Int,
access: Int,
name: String?,
signature: String?,
superName: String?,
interfaces: Array<out String>?
) {
super.visit(version, access, name, signature, superName, interfaces)
}
override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor? {
if (descriptor == null || descriptor != "Lorg/spongepowered/asm/mixin/Mixin;") {
return super.visitAnnotation(descriptor, visible)
}
return object : AnnotationVisitor(Constants.ASM_VERSION, super.visitAnnotation(descriptor, visible)) {
override fun visitArray(name: String?): AnnotationVisitor {
return object : AnnotationVisitor(Constants.ASM_VERSION, super.visitArray(name)) {
override fun visit(none: String?, value: Any?) {
var newVal = value
if (value is Type) {
target = value.internalName
} else if (
value is String
) {
if (target == null) {
target = value
}
newVal = mapper.map(value)
}
super.visit(none, newVal)
}
}
}
override fun visit(name: String?, value: Any?) {
val newVal = when (value) {
is String -> {
target = value
mapper.map(value)
}
is Type -> {
target = value.internalName
value
}
else -> value
}
super.visit(name, newVal)
}
override fun visitEnd() {
if (target != null) {
targetNode = ClassNode()
for (gameJar in contextJars) {
FileSystems.newFileSystem(gameJar).use { fs ->
fs.getPath("$target.class").takeIf { it.exists() }
?.readBytes()?.let(::ClassReader).let { it?.accept(targetNode, 0) }
}
if (targetNode != null) {
break
}
}
}
super.visitEnd()
}
}
}
override fun visitMethod(
access: Int,
name: String?,
descriptor: String?,
signature: String?,
exceptions: Array<out String>?
): MethodVisitor? {
val newMethodName = target?.let { mapper.mapMethodName(it, name, descriptor) }?: name
return super.visitMethod(access, newMethodName, descriptor, signature, exceptions)?.let {
if (targetNode != null) {
MixinMethodVisitor(it, mapper, targetNode!!)
} else it
}
}
override fun visitField(
access: Int,
name: String?,
descriptor: String?,
signature: String?,
value: Any?
): FieldVisitor? {
val newFieldName = target?.let { mapper.mapFieldName(it, name, descriptor) }?:name
return super.visitField(access, newFieldName, descriptor, signature, value)?.let {
if (targetNode != null) {
MixinFieldVisitor(it, mapper, targetNode!!)
} else it
}
}
class MixinFieldVisitor(fv: FieldVisitor, private val mapper: Remapper, private val targetNode: ClassNode) :
FieldRemapper(Constants.ASM_VERSION, fv, mapper) {
override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor {
return MixinAnnotationVisitor(super.visitAnnotation(descriptor, visible), descriptor!!, mapper, targetNode)
}
}
class MixinMethodVisitor(mv: MethodVisitor, private val mapper: Remapper, private val targetNode: ClassNode) :
MethodRemapper(Constants.ASM_VERSION, mv, mapper) {
override fun visitAnnotation(descriptor: String?, visible: Boolean): AnnotationVisitor {
return MixinAnnotationVisitor(super.visitAnnotation(descriptor, visible), descriptor!!, mapper, targetNode)
}
}
class MixinAnnotationVisitor(
av: AnnotationVisitor,
private val desc: String,
private val mapper: Remapper,
private val owner: ClassNode,
private var annotationName: String = ""
) :
AnnotationRemapper(Constants.ASM_VERSION, desc, av, mapper) {
override fun visitAnnotation(name: String?, descriptor: String?): AnnotationVisitor {
return MixinAnnotationVisitor(super.visitAnnotation(name, descriptor), descriptor!!, mapper, owner)
}
override fun visitArray(name: String?): AnnotationVisitor {
return MixinAnnotationVisitor(super.visitArray(name), desc, mapper, owner, name!!)
}
override fun visit(n: String?, value: Any?) {
val name = n ?: annotationName
var newVal = value
if (value is String) {
if (desc == "Lorg/spongepowered/asm/mixin/gen/Invoker;") {
val methodName = value.substringBefore("(")
val desc = value.indexOf("(").takeIf { it > -1 }?.let(value::substring)
if (desc == null) {
val method = owner.methods.find { it.name == methodName }
if (method != null) {
newVal = mapper.mapMethodName(owner.name, methodName, method.desc)
}
} else {
newVal = mapper.mapMethodName(owner.name, methodName, desc)
}
} else if (desc == "Lorg/spongepowered/asm/mixin/gen/Accessor;") {
val field = owner.fields.find { it.name == value }
if (field != null) {
newVal = mapper.mapFieldName(owner.name, value, field.desc)
}
} else if (name == "aliases") {
val field = owner.fields.find { it.name == value }
if (field != null) {
newVal = mapper.mapFieldName(owner.name, value, field.desc)
}
} else if (name == "method") {
val methodName = value.substringBefore("(")
val desc = value.indexOf("(").takeIf { it > -1 }?.let(value::substring)
if (desc == null) {
val method = owner.methods.find { it.name == methodName }
if (method != null) {
val newName = mapper.mapMethodName(
owner.name,
methodName,
method.desc
)
newVal = newName + mapper.mapMethodDesc(method.desc)
}
} else {
val newName = mapper.mapMethodName(
owner.name,
methodName,
desc
)
newVal = newName + mapper.mapMethodDesc(desc)
}
} else if (name == "target") {
newVal = remapMemberInfo(value, mapper)
}
}
super.visit(name, newVal)
}
private fun remapMemberInfo(input: String, mapper: Remapper): String {
var desc: String? = null
var owner: String? = null
var name = Strings.nullToEmpty(input).replace("\\s".toRegex(), "")
var tail = ""
val arrowPos: Int = name.indexOf("->")
if (arrowPos > -1) {
tail = name.substring(arrowPos + 2)
name = name.substring(0, arrowPos)
}
val lastDotPos = name.lastIndexOf('.')
val semiColonPos = name.indexOf(';')
if (lastDotPos > -1) {
owner = name.substring(0, lastDotPos).replace('.', '/')
name = name.substring(lastDotPos + 1)
} else if (semiColonPos > -1 && name.startsWith("L")) {
owner = name.substring(1, semiColonPos).replace('.', '/')
name = name.substring(semiColonPos + 1)
}
val parenPos = name.indexOf('(')
val colonPos = name.indexOf(':')
if (parenPos > -1) {
desc = name.substring(parenPos)
name = name.substring(0, parenPos)
} else if (colonPos > -1) {
desc = name.substring(colonPos + 1)
name = name.substring(0, colonPos)
}
if ((name.indexOf('/') > -1 || name.indexOf('.') > -1) && owner == null) {
owner = name
name = ""
}
var quantifier = ""
if (name.endsWith("*")) {
quantifier = "*"
name = name.substring(0, name.length - 1)
} else if (name.endsWith("+")) {
quantifier = "+"
name = name.substring(0, name.length - 1)
} else if (name.endsWith("}")) {
quantifier = ""
val bracePos = name.indexOf("{")
if (bracePos >= 0) {
quantifier = name.substring(bracePos)
name = name.substring(0, bracePos)
}
} else if (name.indexOf("{") >= 0) {
quantifier = ""
}
val mappedOwner = mapper.map(owner!!)
val mappedDesc = mapper.mapMethodDesc(desc!!)
val mappedName = mapper.mapMethodName(owner, name, desc)
return "L$mappedOwner;$mappedName$mappedDesc$quantifier$tail"
}
}
}

View file

@ -1,6 +1,5 @@
package dev.frogmc.phytotelma.run
import dev.frogmc.phytotelma.Constants
import dev.frogmc.phytotelma.PhytotelmaPlugin
import dev.frogmc.phytotelma.ProjectStorage
import dev.frogmc.phytotelma.VersionChecker
@ -35,11 +34,7 @@ object RunConfigGenerator {
}
val projectData = ProjectStorage.get(project)
var runtimeGameJar = projectData.remappedGameJarPath!!
if (projectData.targetNamespace != Constants.MOJMAP_NAMESPACE) {
runtimeGameJar = runtimeGameJar.resolveSibling(Constants.ALTERNATIVE_RUNTIME_JAR_NAME)
}
val runtimeGameJar = projectData.remappedGameJarPath!!
val projectName = if (project.rootDir == project.projectDir) "" else " (${project.name})"
for (adapter in ADAPTERS) {

View file

@ -1,6 +1,5 @@
package dev.frogmc.phytotelma.run.datagen
import dev.frogmc.phytotelma.Constants
import dev.frogmc.phytotelma.ProjectStorage
import dev.frogmc.phytotelma.ext.datagen.DatagenExtension
import dev.frogmc.phytotelma.run.RunConfigGenerator
@ -27,10 +26,7 @@ object DatagenConfigGenerator {
}
val projectData = ProjectStorage.get(project)
var runtimeGameJar = projectData.remappedGameJarPath!!
if (projectData.targetNamespace != Constants.MOJMAP_NAMESPACE) {
runtimeGameJar = runtimeGameJar.resolveSibling(Constants.ALTERNATIVE_RUNTIME_JAR_NAME)
}
val runtimeGameJar = projectData.remappedGameJarPath!!
for (adapter in RunConfigGenerator.ADAPTERS) {
if (!adapter.shouldGenerate())

View file

@ -55,11 +55,7 @@ abstract class RunGameTask @Inject constructor(env: Env) : JavaExec() {
SourceSet.MAIN_SOURCE_SET_NAME
).runtimeClasspath.filter { it.exists() })
var runtimeGameJar = projectData.remappedGameJarPath!!
if (projectData.targetNamespace != Constants.MOJMAP_NAMESPACE) {
println("Using alternative runtime jar to make use package access fixing")
runtimeGameJar = runtimeGameJar.resolveSibling(Constants.ALTERNATIVE_RUNTIME_JAR_NAME)
}
val runtimeGameJar = projectData.remappedGameJarPath!!
jvmArguments.addAll(
"-Xmx${project.properties.getOrDefault("frogmc.gameHeap", "2048M")}",

View file

@ -12,17 +12,21 @@ class FrogJavadocProvider(data: DocumentationData) : IFabricJavadocProvider {
private val classMap = data.classes.stream().collect(Collectors.toMap({ it.name() }, { it }))
override fun getClassDoc(structClass: StructClass): String? {
return classMap[structClass.qualifiedName]?.javadoc?.joinToString("\n")
if (structClass.qualifiedName.contains("package-info")) {
println(structClass)
}
println(structClass)
return classMap[structClass.qualifiedName]?.javadoc?.takeUnless { it.isEmpty() }?.joinToString("\n")
}
override fun getFieldDoc(structClass: StructClass, structField: StructField): String? {
return classMap[structClass.qualifiedName]
?.getField(structField.name, structField.descriptor)
?.orElse(null)?.javadoc?.joinToString("\n")
?.orElse(null)?.javadoc?.takeUnless { it.isEmpty() }?.joinToString("\n")
}
override fun getMethodDoc(structClass: StructClass, structMethod: StructMethod): String? {
return classMap[structClass.qualifiedName]?.getMethod(structMethod.name, structMethod.descriptor)
?.orElse(null)?.javadoc?.joinToString("\n")
?.orElse(null)?.javadoc?.takeUnless { it.isEmpty() }?.joinToString("\n")
}
}