remove support for non-mojmap mappings
All checks were successful
Publish to snapshot maven / build (push) Successful in 59s

This commit is contained in:
moehreag 2024-10-28 14:44:56 +01:00
parent cbdfac65fc
commit c2d6800981
16 changed files with 56 additions and 1267 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 {

View file

@ -1,62 +0,0 @@
package dev.frogmc.phytotelma.mixin.obfuscation;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.util.Map;
import dev.frogmc.thyroxine.api.data.MappingBundle;
import dev.frogmc.thyroxine.api.data.MappingData;
import dev.frogmc.thyroxine.parser.tiny.TinyV2Parser;
import org.spongepowered.asm.obfuscation.mapping.common.MappingField;
import org.spongepowered.asm.obfuscation.mapping.common.MappingMethod;
import org.spongepowered.tools.obfuscation.mapping.common.MappingProvider;
public class FrogMappingProvider extends MappingProvider {
private final Map<MappingMethod, MappingMethod> methodMap = getMap("methodMap");
private final Map<MappingField, MappingField> fieldMap = getMap("fieldMap");
private final Map<String, String> classMap = getMap("classMap");
public FrogMappingProvider(Messager messager, Filer filer, String from, String to) {
super(messager, filer);
}
@Override
public void read(File input) throws IOException {
if (!input.getName().endsWith(".tiny")) {
return;
}
MappingBundle bundle = TinyV2Parser.parse(Files.readString(input.toPath()));
MappingData data = bundle.data().getFirst();
classMap.putAll(data.classes());
data.fields().forEach((key, value) -> {
fieldMap.put(
new MappingField(key.owner(), key.name(), key.descriptor()),
new MappingField(classMap.get(key.owner()), value, key.descriptor())
);
});
data.methods().forEach((key, value) -> {
methodMap.put(
new MappingMethod(key.owner(), key.name(), key.descriptor()),
new MappingMethod(classMap.get(key.owner()), value, key.descriptor())
);
});
}
// Mixin has guava shadowed which means the actual type is not something we can import
@SuppressWarnings("unchecked")
private <K, V> Map<K, V> getMap(String name) {
try {
Field field = MappingProvider.class.getDeclaredField(name);
field.setAccessible(true);
return (Map<K, V>) field.get(this);
} catch (ReflectiveOperationException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -1,42 +0,0 @@
package dev.frogmc.phytotelma.mixin.obfuscation;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Collections;
import java.util.stream.Collectors;
import dev.frogmc.thyroxine.api.data.MappingBundle;
import dev.frogmc.thyroxine.api.data.MappingData;
import dev.frogmc.thyroxine.api.data.Member;
import dev.frogmc.thyroxine.writer.tiny.TinyV2Writer;
import org.spongepowered.asm.obfuscation.mapping.common.MappingField;
import org.spongepowered.asm.obfuscation.mapping.common.MappingMethod;
import org.spongepowered.tools.obfuscation.ObfuscationType;
import org.spongepowered.tools.obfuscation.mapping.IMappingConsumer;
import org.spongepowered.tools.obfuscation.mapping.common.MappingWriter;
public class FrogMappingWriter extends MappingWriter {
public FrogMappingWriter(Messager messager, Filer filer) {
super(messager, filer);
}
@Override
public void write(String output, ObfuscationType type, IMappingConsumer.MappingSet<MappingField> fields, IMappingConsumer.MappingSet<MappingMethod> methods) {
String[] namespaces = type.getKey().split(":");
MappingData data = new MappingData(namespaces[0], namespaces[1], Collections.emptyMap(),
methods.stream().collect(Collectors.<IMappingConsumer.MappingSet.Pair<MappingMethod>, Member, String>toMap(
p -> new Member(p.from.getOwner(), p.from.getName(), p.from.getDesc()), p -> p.to.getSimpleName())),
fields.stream().collect(Collectors.<IMappingConsumer.MappingSet.Pair<MappingField>, Member, String>toMap(
p -> new Member(p.from.getOwner(), p.from.getName(), p.from.getDesc()), p -> p.to.getSimpleName())),
Collections.emptyMap()
);
MappingBundle bundle = new MappingBundle(data);
try (PrintWriter writer = openFileWriter(output, type + " output mappings")) {
TinyV2Writer.write(bundle, writer);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -1,26 +0,0 @@
package dev.frogmc.phytotelma.mixin.obfuscation;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import org.spongepowered.tools.obfuscation.ObfuscationEnvironment;
import org.spongepowered.tools.obfuscation.ObfuscationType;
import org.spongepowered.tools.obfuscation.mapping.IMappingProvider;
import org.spongepowered.tools.obfuscation.mapping.IMappingWriter;
public class FrogObfuscationEnvironment extends ObfuscationEnvironment {
protected FrogObfuscationEnvironment(ObfuscationType type) {
super(type);
}
@Override
protected IMappingProvider getMappingProvider(Messager messager, Filer filer) {
String[] key = type.getKey().split(":");
return new FrogMappingProvider(messager, filer, key[0], key[1]);
}
@Override
protected IMappingWriter getMappingWriter(Messager messager, Filer filer) {
return new FrogMappingWriter(messager, filer);
}
}

View file

@ -1,60 +0,0 @@
package dev.frogmc.phytotelma.mixin.obfuscation;
import java.util.*;
import org.spongepowered.tools.obfuscation.interfaces.IMixinAnnotationProcessor;
import org.spongepowered.tools.obfuscation.service.IObfuscationService;
import org.spongepowered.tools.obfuscation.service.ObfuscationTypeDescriptor;
public class FrogObfuscationService implements IObfuscationService {
private static final String IN_MAP_FILE = "inMapFile",
IN_MAP_EXTRA_FILES = "inMapExtraFiles",
OUT_MAP_FILE = "outMapFile";
private static String capitalize(String s) {
if (s.length() > 2) {
return Character.toUpperCase(s.charAt(0)) + s.substring(1);
} else if (!s.isEmpty()) {
return s.toUpperCase(Locale.ROOT);
}
return s;
}
private static String namespaced(String name, String from, String to) {
return name + capitalize(from) + capitalize(to);
}
private static void addOptions(Set<String> options, String from, String to) {
options.add(namespaced(IN_MAP_FILE, from, to));
options.add(namespaced(IN_MAP_EXTRA_FILES, from, to));
options.add(namespaced(OUT_MAP_FILE, from, to));
}
@Override
public Set<String> getSupportedOptions() {
Set<String> options = new HashSet<>();
addOptions(options, "moj", "dev");
addOptions(options, "dev", "moj");
addOptions(options, "moj", "moj");
return options;
}
private static ObfuscationTypeDescriptor createObfuscationType(String from, String to) {
return new ObfuscationTypeDescriptor(
from+":"+to,
namespaced(IN_MAP_FILE, from, to),
namespaced(IN_MAP_EXTRA_FILES, from, to),
namespaced(OUT_MAP_FILE, from, to),
FrogObfuscationEnvironment.class
);
}
@Override
public Collection<ObfuscationTypeDescriptor> getObfuscationTypes(IMixinAnnotationProcessor ap) {
return List.of(
createObfuscationType("moj", "dev"),
createObfuscationType("dev", "moj"),
createObfuscationType("moj", "moj")
);
}
}

View file

@ -1 +0,0 @@
dev.frogmc.phytotelma.mixin.obfuscation.FrogObfuscationService

View file

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

View file

@ -8,41 +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.RefmapRemapper
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.logging.LogLevel
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.*
@ -63,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()
@ -92,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
@ -154,7 +120,7 @@ class PhytotelmaPlugin : Plugin<Project> {
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))
@ -201,56 +167,28 @@ 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)
FileSystems.newFileSystem(temp).use { fs ->
val data = ProjectStorage.get(project)
val manifest = fs.getPath("META-INF/MANIFEST.MF")
val lines = manifest.readLines().filter { it.isNotBlank() }
.plus(
"""
task.outputs.files.forEach { file ->
val temp = Files.createTempFile("", file.name)
Files.copy(file.toPath(), temp, StandardCopyOption.REPLACE_EXISTING)
FileSystems.newFileSystem(temp).use { fs ->
val data = ProjectStorage.get(project)
val manifest = fs.getPath("META-INF/MANIFEST.MF")
val lines = manifest.readLines().filter { it.isNotBlank() }
.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 })
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)
}
}
val fs = FileSystems.newFileSystem(ProjectStorage.get(project).remappedGameJarPath)
RefmapRemapper.remapRefmap(file.toPath(), Thyroxine.createMapper(mappings, listOf(fs)), temp)
fs.close()
Thyroxine.remap(
mappings,
temp,
file.toPath(),
false,
defaultRemappingSteps()
)
Files.deleteIfExists(temp)
).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!!)
}
}
}
}
@ -271,195 +209,6 @@ class PhytotelmaPlugin : Plugin<Project> {
task.group = Constants.TASK_GROUP
task.delete = setOf(globalCacheDir)
}
project.afterEvaluate {
remapModDependencies(project)
}
}
private fun defaultRemappingSteps(): MutableList<RemappingStep> {
return mutableListOf(
RemappingStep(::ClassRemapper),
)
}
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
)
Thyroxine.remap(
officialStore, temp, remappedPath, false, defaultRemappingSteps(), officialClientJar, officialServerJar
)
Files.deleteIfExists(temp)
NestStripper.stripJij(remappedPath)
val filesystems = listOf(
FileSystems.newFileSystem(officialClientJar),
FileSystems.newFileSystem(officialServerJar)
)
RefmapRemapper.remapRefmap(
artifact.file.toPath(),
Thyroxine.createMapper(
MappingBundle(listOf(mojOfficial, officialStore), emptyList()).flattenData(),
filesystems
),
remappedPath
)
filesystems.forEach { it.close() }
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

@ -1,31 +1,22 @@
package dev.frogmc.phytotelma.ext
import dev.frogmc.phytotelma.Constants
import dev.frogmc.phytotelma.PhytotelmaPlugin
import dev.frogmc.phytotelma.ProjectStorage
import dev.frogmc.phytotelma.VersionChecker
import dev.frogmc.phytotelma.accesswidener.AccessWidener
import dev.frogmc.phytotelma.ext.datagen.DatagenExtension
import dev.frogmc.phytotelma.mappings.renameDstNamespace
import dev.frogmc.phytotelma.mappings.renameNamespaces
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.MappingBundle
import dev.frogmc.thyroxine.api.data.MappingData
import dev.frogmc.thyroxine.provider.MojmapProvider
import dev.frogmc.thyroxine.writer.tiny.TinyV2Writer
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.api.plugins.JavaPluginExtension
import org.gradle.api.tasks.SourceSet
import org.gradle.api.tasks.compile.JavaCompile
import java.nio.file.*
import java.nio.file.attribute.BasicFileAttributes
import java.util.function.Consumer
@ -33,7 +24,6 @@ import javax.inject.Inject
import kotlin.io.path.copyTo
import kotlin.io.path.createParentDirectories
import kotlin.io.path.notExists
import kotlin.io.path.writer
abstract class PhytotelmaGradleExtensionImpl @Inject constructor(
private val project: Project,
@ -48,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)
@ -66,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)
@ -86,9 +89,8 @@ 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...")
mergedJar.createParentDirectories()
FileSystems.newFileSystem(mergedJar, mapOf("create" to "true")).use { mergedFs ->
@ -111,27 +113,6 @@ abstract class PhytotelmaGradleExtensionImpl @Inject constructor(
}
VersionChecker.saveMergedPomFile(version, mergedJar.parent)
println("Writing mappings...")
if (projectData.intermediaryNs != Constants.MOJMAP_NAMESPACE) {
TinyV2Writer.write(
MappingBundle(mappings.forNamespaces(mcConf.targetNamespace, projectData.intermediaryNs))
.renameNamespaces("dev", projectData.intermediaryNs!!),
ProjectStorage.get(project).localCacheDir?.resolve("mappings.tiny")?.writer()
)
} else {
TinyV2Writer.write(
MappingBundle.merge(
mappings.reverse(),
mcConf.mojmap().get().reverse().renameDstNamespace("moj")
),
ProjectStorage.get(project).localCacheDir?.resolve("mappings.tiny")?.writer()
)
}
projectData.targetNamespace = mcConf.targetNamespace
RunConfigGenerator.generate(project)
applyAW = AccessWidener.hasAW(project)
}
@ -158,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))
}
}
}
}
@ -183,50 +149,6 @@ abstract class PhytotelmaGradleExtensionImpl @Inject constructor(
error("No loader version provided!")
}
project.dependencies.add(Constants.MINECRAFT_CONFIGURATION, "dev.frogmc:frogloader:${conf.version.get()}")
project.afterEvaluate {
project.logger.warn("doing ap stuff")
project.configurations.getByName(JavaPlugin.COMPILE_CLASSPATH_CONFIGURATION_NAME)
.resolvedConfiguration.resolvedArtifacts.map { it.moduleVersion.id }
.find { it.group.equals("net.fabricmc") && it.name.equals("sponge-mixin") }
?.let { mixinDep ->
project.dependencies.add(
JavaPlugin.ANNOTATION_PROCESSOR_CONFIGURATION_NAME,
"dev.frogmc.phytotelma:frog-mixin-obfuscation:" + PhytotelmaPlugin::class.java.`package`.implementationVersion
)
project.dependencies.add(
JavaPlugin.ANNOTATION_PROCESSOR_CONFIGURATION_NAME,
mixinDep.group + ":" + mixinDep.name + ":" + mixinDep.version
)
val options = mapOf(
"defaultObfuscationEnv" to "dev:" + ProjectStorage.get(project).intermediaryNs,
"quiet" to "true",
"inMapFileDevMoj" to ProjectStorage.get(project).localCacheDir!!.resolve("mappings.tiny"),
"outMapFileDevMoj" to project.layout.buildDirectory.file(
"mixin-out-" + (ProjectStorage.get(project).mappingsName + "_" +
project.extensions.getByType(JavaPluginExtension::class.java).sourceSets.getByName(
SourceSet.MAIN_SOURCE_SET_NAME
).name
).replace("[(). +,]".toRegex(), ""))
.get().asFile,
"inMapExtraFilesDevMoj" to ProjectStorage.get(project).remappedGameJarPath,
)
val compileTasks = listOf(JavaPlugin.COMPILE_JAVA_TASK_NAME, JavaPlugin.COMPILE_TEST_JAVA_TASK_NAME)
compileTasks.forEach { name ->
project.tasks.getByName(name)
.takeIf { it is JavaCompile }
.let { it as JavaCompile }
.also { task ->
task.options.compilerArgs.addAll(options
.map { "-A" + it.key + "=" + it.value }
.plus(
"-AoutRefMapFile=" + task.destinationDirectory.asFile.get()
.resolve(project.name + "-refmap.json")
))
}
}
}
}
}
override fun froglib(action: Action<VersionConfiguration>) {

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,125 +0,0 @@
package dev.frogmc.phytotelma.mixin.remapper
import com.google.gson.Gson
import com.google.gson.JsonObject
import org.objectweb.asm.ClassReader
import org.objectweb.asm.Type
import org.objectweb.asm.commons.Remapper
import org.objectweb.asm.tree.ClassNode
import java.nio.charset.StandardCharsets
import java.nio.file.*
import kotlin.io.path.readBytes
import kotlin.io.path.reader
import kotlin.io.path.writer
object RefmapRemapper {
private val gson = Gson()
private const val MIXIN_ANNOTATION = "Lorg/spongepowered/asm/mixin/Mixin;"
fun remapRefmap(jar: Path, remapper: Remapper, outJar: Path) {
FileSystems.newFileSystem(jar).use { fs ->
Files.list(fs.getPath("/"))
.filter { it.fileName.toString().endsWith("-refmap.json") }
.forEach { refmap ->
val json = gson.fromJson(refmap.reader(), JsonObject::class.java)
val mappings = json.get("mappings").asJsonObject
val remapped = JsonObject()
val remappedMappings = JsonObject()
remapped.add("mappings", remappedMappings)
mappings.entrySet().forEach { mapping ->
val mixinName = mapping.key
val entries = mapping.value.asJsonObject
val remappedEntries = remapEntries(entries, fs, remapper)
remappedMappings.add(mixinName, remappedEntries)
}
val data = json.get("data").asJsonObject
val remappedData = JsonObject()
data.entrySet().forEach { entry ->
val namespaces = entry.key
val namespaceMappings = entry.value.asJsonObject
val remappedNS = JsonObject()
namespaceMappings.entrySet().forEach { mapping ->
val mixinName = mapping.key
val entries = mapping.value.asJsonObject
val remappedEntries = remapEntries(entries, fs, remapper)
remappedNS.add(mixinName, remappedEntries)
}
remappedData.add(namespaces, remappedNS)
}
remapped.add("data", remappedData)
FileSystems.newFileSystem(outJar).use { outFs ->
gson.toJson(
remapped, outFs.getPath(refmap.toString()).writer(
StandardCharsets.UTF_8,
StandardOpenOption.TRUNCATE_EXISTING,
StandardOpenOption.WRITE
)
)
}
}
}
}
private fun remapEntries(entries: JsonObject, fs: FileSystem, remapper: Remapper): JsonObject {
val remappedEntries = JsonObject()
entries.entrySet().forEach {
val name = it.key
val value = it.value.asString
if (value.contains(":")) {
val field = value.split(":")
val target = getMixinTarget(fs.getPath("/$name.class"))
val fieldName = field.first()
val fieldDesc = field.last()
remappedEntries.addProperty(
name,
remapper.mapFieldName(target, fieldName, fieldDesc) + ":" + remapper.mapDesc(fieldDesc)
)
} else {
val method = value.split(";", limit = 2)
val owner = method.first() + ";"
val methodName = method.last().substringBefore("(")
val desc = method.last().substringAfter("(")
remappedEntries.addProperty(
methodName,
remapper.map(owner) + remapper.mapMethodName(owner, methodName, desc) + remapper.mapMethodDesc(desc)
)
}
}
return remappedEntries
}
private fun getMixinTarget(mixinClass: Path): String {
val reader = ClassReader(mixinClass.readBytes())
val node = ClassNode()
reader.accept(node, 0)
node.visibleAnnotations.forEach {
if (MIXIN_ANNOTATION == it.desc) {
it.values.forEach { value ->
when (value) {
is String -> return value
is Type -> return value.internalName
is List<*> -> value.forEach { c ->
when (c) {
is String -> return c
is Type -> return c.internalName
}
}
}
}
}
}
return ""
}
}

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")}",