various fixes, datagen task/run config, mapping insanity

This commit is contained in:
moehreag 2024-07-08 20:45:38 +02:00
parent a415bb5b81
commit 7a2f8aefb0
20 changed files with 630 additions and 97 deletions

View file

@ -24,7 +24,7 @@ repositories {
} }
dependencies { dependencies {
implementation("dev.frogmc:thyroxine:0.0.1-alpha.6") implementation("dev.frogmc:thyroxine:0.0.1-alpha.9")
implementation("org.ow2.asm:asm:9.7") implementation("org.ow2.asm:asm:9.7")
implementation("org.ow2.asm:asm-commons:9.7") implementation("org.ow2.asm:asm-commons:9.7")
implementation("org.ow2.asm:asm-tree:9.7") implementation("org.ow2.asm:asm-tree:9.7")

View file

@ -1,6 +1,9 @@
package dev.frogmc.phytotelma package dev.frogmc.phytotelma
import org.objectweb.asm.Opcodes
object Constants { object Constants {
const val MOJMAP_NAMESPACE = "mojmap"
const val MOD_EXTENSION = ".frogmod" const val MOD_EXTENSION = ".frogmod"
const val MOD_METADATA_FILE = "frog.mod.toml" const val MOD_METADATA_FILE = "frog.mod.toml"
const val MINECRAFT_CONFIGURATION = "minecraftDependency" const val MINECRAFT_CONFIGURATION = "minecraftDependency"
@ -14,4 +17,6 @@ object Constants {
const val RUNT_SERVER_TASK = "runServer" const val RUNT_SERVER_TASK = "runServer"
const val CLEAR_LOCAL_CACHE_TASK = "clearLocalCache" const val CLEAR_LOCAL_CACHE_TASK = "clearLocalCache"
const val CLEAR_GLOBAL_CACHE_TASK = "clearGlobalCache" const val CLEAR_GLOBAL_CACHE_TASK = "clearGlobalCache"
const val ASM_VERSION = Opcodes.ASM9
const val ALTERNATIVE_RUNTIME_JAR_NAME = "runtime.jar"
} }

View file

@ -1,21 +1,29 @@
package dev.frogmc.phytotelma package dev.frogmc.phytotelma
import com.electronwill.nightconfig.core.CommentedConfig
import com.electronwill.nightconfig.core.file.FileNotFoundAction import com.electronwill.nightconfig.core.file.FileNotFoundAction
import com.electronwill.nightconfig.core.io.WritingMode
import com.electronwill.nightconfig.toml.TomlParser import com.electronwill.nightconfig.toml.TomlParser
import com.electronwill.nightconfig.toml.TomlWriter
import dev.frogmc.phytotelma.accesswidener.AccessWidener import dev.frogmc.phytotelma.accesswidener.AccessWidener
import dev.frogmc.phytotelma.build.PhytotelmaBuildTask import dev.frogmc.phytotelma.build.PhytotelmaBuildTask
import dev.frogmc.phytotelma.common.Env import dev.frogmc.phytotelma.common.Env
import dev.frogmc.phytotelma.ext.PhytotelmaGradleExtension import dev.frogmc.phytotelma.ext.PhytotelmaGradleExtension
import dev.frogmc.phytotelma.ext.PhytotelmaGradleExtensionImpl import dev.frogmc.phytotelma.ext.PhytotelmaGradleExtensionImpl
import dev.frogmc.phytotelma.mappings.renameDstNamespace import dev.frogmc.phytotelma.mappings.renameDstNamespace
import dev.frogmc.phytotelma.mixin.remapper.MixinAnnotationRemapper
import dev.frogmc.phytotelma.nest.NestStripper
import dev.frogmc.phytotelma.nest.Nester
import dev.frogmc.phytotelma.run.AssetDownloader import dev.frogmc.phytotelma.run.AssetDownloader
import dev.frogmc.phytotelma.run.RunConfigGenerator import dev.frogmc.phytotelma.run.RunConfigGenerator
import dev.frogmc.phytotelma.run.task.RunGameTask import dev.frogmc.phytotelma.run.task.RunGameTask
import dev.frogmc.phytotelma.vineflower.FrogJavadocProvider import dev.frogmc.phytotelma.vineflower.FrogJavadocProvider
import dev.frogmc.thyroxine.RemappingStep
import dev.frogmc.thyroxine.Thyroxine import dev.frogmc.thyroxine.Thyroxine
import dev.frogmc.thyroxine.api.Mapper import dev.frogmc.thyroxine.api.Mapper
import dev.frogmc.thyroxine.api.data.MappingBundle import dev.frogmc.thyroxine.api.data.MappingBundle
import dev.frogmc.thyroxine.provider.MojmapProvider import dev.frogmc.thyroxine.provider.MojmapProvider
import dev.frogmc.thyroxine.writer.tiny.TinyV2Writer
import net.fabricmc.fernflower.api.IFabricJavadocProvider import net.fabricmc.fernflower.api.IFabricJavadocProvider
import org.gradle.api.Plugin import org.gradle.api.Plugin
import org.gradle.api.Project import org.gradle.api.Project
@ -27,6 +35,7 @@ import org.jetbrains.java.decompiler.main.Fernflower
import org.jetbrains.java.decompiler.main.decompiler.PrintStreamLogger import org.jetbrains.java.decompiler.main.decompiler.PrintStreamLogger
import org.jetbrains.java.decompiler.main.decompiler.SingleFileSaver import org.jetbrains.java.decompiler.main.decompiler.SingleFileSaver
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences
import org.objectweb.asm.commons.ClassRemapper
import java.io.OutputStream import java.io.OutputStream
import java.io.PrintStream import java.io.PrintStream
import java.net.URI import java.net.URI
@ -186,20 +195,33 @@ class PhytotelmaPlugin : Plugin<Project> {
project.tasks.getByName(JavaPlugin.JAR_TASK_NAME).actions.addLast { task -> project.tasks.getByName(JavaPlugin.JAR_TASK_NAME).actions.addLast { task ->
val storage = ProjectStorage.get(project) val storage = ProjectStorage.get(project)
if (storage.targetNamespace != "mojmap") { if (storage.targetNamespace != Constants.MOJMAP_NAMESPACE) {
val mappings = MappingBundle.merge( val mappings = MappingBundle.merge(
storage.mappings!!.reverse(), MojmapProvider.get( storage.mappings!!.reverse(), MojmapProvider.get(
storage.minecraftVersion!!, storage.minecraftVersion!!,
globalCacheDir.resolve("net/minecraft/client/${storage.minecraftVersion}/client-${storage.minecraftVersion}.txt") globalCacheDir.resolve("net/minecraft/client/${storage.minecraftVersion}/client-${storage.minecraftVersion}.txt")
).orElseThrow().reverse().renameDstNamespace("mojmap") ).orElseThrow().reverse().renameDstNamespace(Constants.MOJMAP_NAMESPACE)
).forNamespaces(storage.targetNamespace, "mojmap") ).forNamespaces(storage.targetNamespace, Constants.MOJMAP_NAMESPACE)
val parser = TomlParser() val includeConfiguration = project.configurations.findByName(Constants.INCLUDE_CONFIGURATION)
task.outputs.files.forEach { file -> task.outputs.files.forEach { file ->
val temp = Files.createTempFile("", file.name) val temp = Files.createTempFile("", file.name)
Files.copy(file.toPath(), temp, StandardCopyOption.REPLACE_EXISTING) Files.copy(file.toPath(), temp, StandardCopyOption.REPLACE_EXISTING)
FileSystems.newFileSystem(temp).use { fs -> FileSystems.newFileSystem(temp).use { fs ->
if (includeConfiguration != null) {
val jijPath = fs.getPath("META-INF/jars")
val files = Nester.run(includeConfiguration, jijPath).map { it.toml() }.toList()
if (files.isNotEmpty()) {
val manifest = fs.getPath(Constants.MOD_METADATA_FILE)
val config: CommentedConfig =
tomlParser.parse(manifest, FileNotFoundAction.THROW_ERROR)
if (!config.add("frog.extensions.included_jars", files)) {
println("Failed to add included jars to mod manifest, make sure it doesn't include a key at 'frog.extensions.included_jars'!")
}
tomlWriter.write(config, manifest, WritingMode.REPLACE)
}
}
val metadata = fs.getPath(Constants.MOD_METADATA_FILE) val metadata = fs.getPath(Constants.MOD_METADATA_FILE)
parser.parse(metadata, FileNotFoundAction.READ_NOTHING) tomlParser.parse(metadata, FileNotFoundAction.READ_NOTHING)
.get<String>("frog.extensions.accesswidener")?.let { name -> .get<String>("frog.extensions.accesswidener")?.let { name ->
val aw = metadata.resolveSibling(name) val aw = metadata.resolveSibling(name)
AccessWidener.checkAW(aw, ProjectStorage.get(project).remappedGameJarPath!!) AccessWidener.checkAW(aw, ProjectStorage.get(project).remappedGameJarPath!!)
@ -239,7 +261,13 @@ class PhytotelmaPlugin : Plugin<Project> {
aw.writeText(buffer) aw.writeText(buffer)
} }
} }
Thyroxine.remap(mappings, temp, file.toPath(), false, false) Thyroxine.remap(
mappings,
temp,
file.toPath(),
false,
defaultRemappingSteps(ProjectStorage.get(project).remappedGameJarPath!!)
)
Files.deleteIfExists(temp) Files.deleteIfExists(temp)
} }
} }
@ -267,16 +295,29 @@ class PhytotelmaPlugin : Plugin<Project> {
} }
} }
private fun defaultRemappingSteps(sourceNsJar: Path): MutableList<RemappingStep> {
return mutableListOf(
RemappingStep(::ClassRemapper),
RemappingStep { cv, mapper -> MixinAnnotationRemapper(cv, mapper, sourceNsJar) },
)
}
private fun remapModDependencies(project: Project) { private fun remapModDependencies(project: Project) {
val out = System.out val out = System.out
// Mute the output from thyroxine as there may be a lot of remapping operations here // Mute the output from thyroxine as there may be a lot of remapping operations here
System.setOut(PrintStream(OutputStream.nullOutputStream())) System.setOut(PrintStream(OutputStream.nullOutputStream()))
val mojmapGameJar = ProjectStorage.get(project).remappedGameJarPath!!.resolveSibling("mojmap.jar")
val version = ProjectStorage.get(project).minecraftVersion!!
val officialJar = VersionChecker.downloadClient(project, version)
ModConfigurations.configurations.forEach { conf -> ModConfigurations.configurations.forEach { conf ->
val artifacts = project.configurations.getByName("mod" + conf.name.capitalized()) val artifacts = project.configurations.getByName("mod" + conf.name.capitalized())
.resolvedConfiguration.resolvedArtifacts .resolvedConfiguration.resolvedArtifacts
if (artifacts.isEmpty()) { if (artifacts.isEmpty()) {
return return
} }
if (mojmapGameJar.notExists()) {
Thyroxine.run(version, officialJar, mojmapGameJar, true, false)
}
val target = project.configurations.create("mod" + conf.name.capitalized() + "Mapped") { c -> val target = project.configurations.create("mod" + conf.name.capitalized() + "Mapped") { c ->
c.isTransitive = false c.isTransitive = false
conf.classpathNames.forEach { conf.classpathNames.forEach {
@ -284,12 +325,16 @@ class PhytotelmaPlugin : Plugin<Project> {
} }
} }
val storage = ProjectStorage.get(project) val storage = ProjectStorage.get(project)
val mappings = MappingBundle.merge( TinyV2Writer.write(storage.mappings!!,
storage.mappings!!.reverse(), MojmapProvider.get( project.projectDir.resolve("storageMappings.tiny").writer())
val officialStore = storage.mappings!!.forNamespaces(storage.targetNamespace!!, "official").reverse()
val mojOfficial = MojmapProvider.get(
storage.minecraftVersion!!, storage.minecraftVersion!!,
globalCacheDir.resolve("net/minecraft/client/${storage.minecraftVersion}/client-${storage.minecraftVersion}.txt") globalCacheDir.resolve("net/minecraft/client/${storage.minecraftVersion}/client-${storage.minecraftVersion}.txt")
).orElseThrow().reverse().renameDstNamespace("mojmap") ).orElseThrow().reverse().renameDstNamespace(Constants.MOJMAP_NAMESPACE).also {
).forNamespaces("mojmap", storage.targetNamespace) TinyV2Writer.write(it,
project.projectDir.resolve("mojmap.tiny").writer())
}.data[0].reverse()
val targetPath = project.layout.buildDirectory.asFile.get().toPath().resolve("remappedMods") val targetPath = project.layout.buildDirectory.asFile.get().toPath().resolve("remappedMods")
.resolve("dev/frogmc/phytotelma/remapped_mods") .resolve("dev/frogmc/phytotelma/remapped_mods")
val remappedPaths = mutableListOf<Path>() val remappedPaths = mutableListOf<Path>()
@ -297,10 +342,10 @@ class PhytotelmaPlugin : Plugin<Project> {
val group = artifact.moduleVersion.id.group val group = artifact.moduleVersion.id.group
val name = artifact.moduleVersion.id.name val name = artifact.moduleVersion.id.name
val groupname = (group + "_" + name).replace(".", "_") val groupname = (group + "_" + name).replace(".", "_")
val version = artifact.moduleVersion.id.version val artifactVersion = artifact.moduleVersion.id.version
val classifier = artifact.classifier val classifier = artifact.classifier
val remappedPath = targetPath.resolve(groupname).resolve(version) val remappedPath = targetPath.resolve(groupname).resolve(artifactVersion)
.resolve(groupname + "-" + version + (classifier?.let { "-$it" } ?: "") + ".jar") .resolve(groupname + "-" + artifactVersion + (classifier?.let { "-$it" } ?: "") + ".jar")
remappedPath.createParentDirectories() remappedPath.createParentDirectories()
remappedPaths.add(remappedPath) remappedPaths.add(remappedPath)
@ -312,23 +357,47 @@ class PhytotelmaPlugin : Plugin<Project> {
"\t<modelVersion>4.0.0</modelVersion>\n" + "\t<modelVersion>4.0.0</modelVersion>\n" +
"\t<groupId>dev.frogmc.phytotelma.remapped_mods</groupId>\n" + "\t<groupId>dev.frogmc.phytotelma.remapped_mods</groupId>\n" +
"\t<artifactId>$groupname</artifactId>\n" + "\t<artifactId>$groupname</artifactId>\n" +
"\t<version>$version</version>\n" + "\t<version>$artifactVersion</version>\n" +
"</project>" "</project>"
) )
remappedPaths.add(pom) remappedPaths.add(pom)
Thyroxine.remap(mappings, artifact.file.toPath(), remappedPath, false, false) 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(
officialJar
)
)
Files.deleteIfExists(temp)
NestStripper.stripJij(remappedPath)
project.dependencies.add( project.dependencies.add(
target.name, target.name,
"dev.frogmc.phytotelma.remapped_mods:$groupname:$version" + (classifier?.let { ":$it" } ?: "") "dev.frogmc.phytotelma.remapped_mods:$groupname:$artifactVersion" + (classifier?.let { ":$it" } ?: "")
) )
} }
Files.deleteIfExists(mojmapGameJar)
} }
System.setOut(out) System.setOut(out)
} }
companion object { companion object {
lateinit var globalCacheDir: Path lateinit var globalCacheDir: Path
val tomlParser = TomlParser()
val tomlWriter = TomlWriter()
} }
} }
fun Project.getId(): String {
var path = name
var parent: Project? = this
while ((parent?.parent.also { parent = it }) != null) {
path = parent!!.name + "." + path
}
return path
}

View file

@ -3,18 +3,19 @@ package dev.frogmc.phytotelma
import dev.frogmc.thyroxine.api.data.MappingBundle import dev.frogmc.thyroxine.api.data.MappingBundle
import org.gradle.api.Project import org.gradle.api.Project
import java.nio.file.Path import java.nio.file.Path
import java.util.concurrent.ConcurrentHashMap
object ProjectStorage { object ProjectStorage {
private val data = mutableMapOf<Project, ProjectData>() private val data = ConcurrentHashMap<String, ProjectData>()
fun get(project: Project): ProjectData { fun get(project: Project): ProjectData {
if (!data.containsKey(project)) { val id = project.getId()
data[project] = ProjectData(null, null, null, null, null, null) if (!data.containsKey(id)) {
data[id] = ProjectData(null, null, null, null, null, null)
} }
return data[project]!! return data[id]!!
} }
} }
class ProjectData( class ProjectData(

View file

@ -2,21 +2,20 @@
package dev.frogmc.phytotelma package dev.frogmc.phytotelma
import com.google.common.hash.Hashing
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.GsonBuilder import com.google.gson.GsonBuilder
import dev.frogmc.phytotelma.common.CachingHttpClient import dev.frogmc.phytotelma.common.CachingHttpClient
import org.gradle.api.Project import org.gradle.api.Project
import java.net.URI import java.net.URI
import java.nio.file.Path import java.nio.file.Path
import kotlin.io.path.createDirectories import kotlin.io.path.*
import kotlin.io.path.exists
import kotlin.io.path.writeBytes
import kotlin.io.path.writeText
object VersionChecker { object VersionChecker {
private var validVersions = mutableListOf<VersionUrl>() private var validVersions = mutableListOf<VersionUrl>()
private var versionData: VersionData? = null private var versionData: VersionData? = null
@Suppress("DEPRECATION")
fun downloadClient(project: Project, version: String): Path { fun downloadClient(project: Project, version: String): Path {
fetchVersionData(version) fetchVersionData(version)
@ -25,9 +24,11 @@ object VersionChecker {
val downloadDirectory = PhytotelmaPlugin.globalCacheDir.resolve("net/minecraft/client/$version/") val downloadDirectory = PhytotelmaPlugin.globalCacheDir.resolve("net/minecraft/client/$version/")
val downloadFile = downloadDirectory.resolve("client-$version.jar") val downloadFile = downloadDirectory.resolve("client-$version.jar")
if (!project.gradle.startParameter.isRefreshDependencies && downloadFile.exists()) { if (!project.gradle.startParameter.isRefreshDependencies && downloadFile.exists()) {
println("Client already downloaded! Assuming it's valid. FIXME: Add checksum validation.") if (clientData.sha1 == Hashing.sha1().hashBytes(downloadFile.readBytes()).toString()) {
return downloadFile return downloadFile
} }
}
println("Downloading client...")
downloadDirectory.createDirectories() downloadDirectory.createDirectories()
val raw = rawDownload(clientData.url) val raw = rawDownload(clientData.url)
downloadFile.writeBytes(raw) downloadFile.writeBytes(raw)

View file

@ -4,6 +4,7 @@ import com.electronwill.nightconfig.core.file.FileNotFoundAction
import com.electronwill.nightconfig.toml.TomlParser import com.electronwill.nightconfig.toml.TomlParser
import com.google.common.hash.Hashing import com.google.common.hash.Hashing
import dev.frogmc.phytotelma.Constants import dev.frogmc.phytotelma.Constants
import dev.frogmc.phytotelma.getId
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.artifacts.ProjectDependency import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.plugins.JavaPlugin import org.gradle.api.plugins.JavaPlugin
@ -22,7 +23,7 @@ object AccessWidener {
private val PARSER = TomlParser() private val PARSER = TomlParser()
private val awHashes = mutableMapOf<Project, String>() private val awHashes = mutableMapOf<String, String>()
private val HEADER = Pattern.compile("accessWidener\\s+v[12]\\s+.*").asMatchPredicate() private val HEADER = Pattern.compile("accessWidener\\s+v[12]\\s+.*").asMatchPredicate()
private val SEPARATOR = "[\\t ]+".toRegex() private val SEPARATOR = "[\\t ]+".toRegex()
@ -65,8 +66,8 @@ object AccessWidener {
fun needsUpdate(project: Project): Boolean { fun needsUpdate(project: Project): Boolean {
getAWFile(project)?.let { getAWFile(project)?.let {
val hash = Hashing.sha256().hashBytes(it.readBytes()).toString() val hash = Hashing.sha256().hashBytes(it.readBytes()).toString()
if (!awHashes.containsKey(project) || hash != awHashes[project]) { if (!awHashes.containsKey(project.getId()) || hash != awHashes[project.getId()]) {
awHashes[project] = hash awHashes[project.getId()] = hash
return true return true
} }
} }

View file

@ -1,15 +1,10 @@
package dev.frogmc.phytotelma.build package dev.frogmc.phytotelma.build
import com.electronwill.nightconfig.core.CommentedConfig
import com.electronwill.nightconfig.core.file.FileNotFoundAction
import com.electronwill.nightconfig.core.io.WritingMode
import com.electronwill.nightconfig.toml.TomlParser
import com.electronwill.nightconfig.toml.TomlWriter
import dev.frogmc.phytotelma.Constants import dev.frogmc.phytotelma.Constants
import dev.frogmc.phytotelma.ProjectStorage import dev.frogmc.phytotelma.ProjectStorage
import dev.frogmc.phytotelma.nest.Nester
import org.gradle.api.DefaultTask import org.gradle.api.DefaultTask
import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.OutputFile
import org.gradle.api.tasks.TaskAction
import org.gradle.api.tasks.bundling.AbstractArchiveTask import org.gradle.api.tasks.bundling.AbstractArchiveTask
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.nio.file.FileSystems import java.nio.file.FileSystems
@ -28,7 +23,6 @@ abstract class PhytotelmaBuildTask : DefaultTask() {
init { init {
group = Constants.TASK_GROUP group = Constants.TASK_GROUP
val includeConfiguration = project.configurations.findByName(Constants.INCLUDE_CONFIGURATION)
val jarTask = project.tasks.getByName("jar") val jarTask = project.tasks.getByName("jar")
dependsOn.add(jarTask) dependsOn.add(jarTask)
@ -41,25 +35,15 @@ abstract class PhytotelmaBuildTask : DefaultTask() {
outputFile = outFile outputFile = outFile
outputs.upToDateWhen { false } outputs.upToDateWhen { false }
inputs.files(jarTask.outputs) inputs.files(jarTask.outputs)
}
actions.add { task -> @TaskAction
task.inputs.files.forEach { file -> fun process() {
this.inputs.files.forEach { file ->
if (file.name.endsWith(".jar") && !(file.name.contains("-dev.") || file.name.contains("-sources."))) { if (file.name.endsWith(".jar") && !(file.name.contains("-dev.") || file.name.contains("-sources."))) {
Files.copy(file.toPath(), outFile, StandardCopyOption.REPLACE_EXISTING) Files.copy(file.toPath(), outputFile, StandardCopyOption.REPLACE_EXISTING)
FileSystems.newFileSystem(outFile).use { fs -> FileSystems.newFileSystem(outputFile).use { fs ->
if (includeConfiguration != null) {
val jijPath = fs.getPath("META-INF/jars")
val files = Nester.run(includeConfiguration, jijPath).map { it.toml() }.toList()
if (files.isNotEmpty()) {
val manifest = fs.getPath(Constants.MOD_METADATA_FILE)
val config: CommentedConfig =
TomlParser().parse(manifest, FileNotFoundAction.THROW_ERROR)
if (!config.add("frog.extensions.included_jars", files)) {
println("Failed to add included jars to mod manifest, make sure it doesn't include a key at 'frog.extensions.included_jars'!")
}
TomlWriter().write(config, manifest, WritingMode.REPLACE)
}
}
val manifest = fs.getPath("META-INF/MANIFEST.MF") val manifest = fs.getPath("META-INF/MANIFEST.MF")
val lines = manifest.readLines().filter { it.isNotBlank() } val lines = manifest.readLines().filter { it.isNotBlank() }
.plus( .plus(
@ -72,8 +56,7 @@ abstract class PhytotelmaBuildTask : DefaultTask() {
) )
manifest.writeLines(lines, StandardCharsets.UTF_8) manifest.writeLines(lines, StandardCharsets.UTF_8)
} }
println("Built mod to ${outFile.toUri()}") println("Built mod to ${outputFile.toUri()}")
}
} }
} }
} }

View file

@ -1,5 +1,6 @@
package dev.frogmc.phytotelma.ext package dev.frogmc.phytotelma.ext
import dev.frogmc.phytotelma.Constants
import dev.frogmc.phytotelma.PhytotelmaPlugin import dev.frogmc.phytotelma.PhytotelmaPlugin
import dev.frogmc.phytotelma.mappings.filterClasses import dev.frogmc.phytotelma.mappings.filterClasses
import dev.frogmc.phytotelma.mappings.renameDstNamespace import dev.frogmc.phytotelma.mappings.renameDstNamespace
@ -43,7 +44,7 @@ abstract class MinecraftConfiguration @Inject constructor(
val cacheDir = PhytotelmaPlugin.globalCacheDir val cacheDir = PhytotelmaPlugin.globalCacheDir
mappingsName = "mojmap(${version.get()})+parchment(${conf.gameVersion.get()}, ${conf.version.get()})" mappingsName = "mojmap(${version.get()})+parchment(${conf.gameVersion.get()}, ${conf.version.get()})"
targetNamespace = "mojmap" targetNamespace = Constants.MOJMAP_NAMESPACE
return@provider MappingBundle.merge( return@provider MappingBundle.merge(
MojmapProvider.get( MojmapProvider.get(
@ -72,7 +73,7 @@ abstract class MinecraftConfiguration @Inject constructor(
return@provider ParchmentProvider.getParchment( return@provider ParchmentProvider.getParchment(
version.get(), version.get(),
cacheDir.resolve("org/parchmentmc/parchment/${conf.gameVersion.get()}/${conf.version.get()}") cacheDir.resolve("org/parchmentmc/parchment/${conf.gameVersion.get()}/${conf.version.get()}")
).renameNamespaces("mojmap", "parchment") ).renameNamespaces(Constants.MOJMAP_NAMESPACE, "parchment")
} }
} }
@ -80,7 +81,7 @@ abstract class MinecraftConfiguration @Inject constructor(
return project.provider { return project.provider {
val cacheDir = PhytotelmaPlugin.globalCacheDir val cacheDir = PhytotelmaPlugin.globalCacheDir
mappingsName = "mojmap(${version.get()})" mappingsName = "mojmap(${version.get()})"
targetNamespace = "mojmap" targetNamespace = Constants.MOJMAP_NAMESPACE
return@provider MojmapProvider.get( return@provider MojmapProvider.get(
version.get(), version.get(),
cacheDir.resolve("net/minecraft/client/${version.get()}/client-${version.get()}.txt") cacheDir.resolve("net/minecraft/client/${version.get()}/client-${version.get()}.txt")
@ -101,8 +102,7 @@ abstract class MinecraftConfiguration @Inject constructor(
return@provider twoStepMappings( return@provider twoStepMappings(
"net.fabricmc:intermediary:${version.get()}:v2", "net.fabricmc:intermediary:${version.get()}:v2",
"org.quiltmc:quilt-mappings:${conf.version.get()}:intermediary-v2" "org.quiltmc:quilt-mappings:${conf.version.get()}:intermediary-v2"
).filterClasses { !it.startsWith("net/minecraft/unmapped") } ).flatten().renameDstNamespace(targetNamespace)
.flatten(true).renameDstNamespace(targetNamespace)
} }
} }
@ -119,8 +119,7 @@ abstract class MinecraftConfiguration @Inject constructor(
return@provider twoStepMappings( return@provider twoStepMappings(
"net.fabricmc:intermediary:${version.get()}:v2", "net.fabricmc:intermediary:${version.get()}:v2",
"net.fabricmc:yarn:${conf.version.get()}:v2" "net.fabricmc:yarn:${conf.version.get()}:v2"
).filterClasses { !it.startsWith("net/minecraft/class_") } ).flatten(true).renameDstNamespace(targetNamespace)
.flatten(true).renameDstNamespace(targetNamespace)
} }
} }
@ -178,12 +177,15 @@ abstract class MinecraftConfiguration @Inject constructor(
var name = "layer[" var name = "layer["
val layers = conf.layers.get().mapIndexed { index, provider -> val layers = conf.layers.get().mapIndexed { index, provider ->
val bundle = (provider.get() as MappingBundle).flatten(true)
if (index == 0) { if (index == 0) {
val bundle = (provider.get() as MappingBundle).flatten()
name += mappingsName name += mappingsName
back.insert(bundle) back.insert(bundle)
return@mapIndexed 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" name += ", $mappingsName"
back = back.flatten(false) back = back.flatten(false)

View file

@ -1,5 +1,6 @@
package dev.frogmc.phytotelma.ext package dev.frogmc.phytotelma.ext
import dev.frogmc.phytotelma.ext.datagen.DatagenExtension
import org.gradle.api.Action import org.gradle.api.Action
@Suppress("unused") @Suppress("unused")
@ -10,4 +11,6 @@ interface PhytotelmaGradleExtension {
fun loader(action: Action<VersionConfiguration>) fun loader(action: Action<VersionConfiguration>)
fun froglib(action: Action<VersionConfiguration>) fun froglib(action: Action<VersionConfiguration>)
fun datagen(action: Action<DatagenExtension>)
} }

View file

@ -4,12 +4,19 @@ import dev.frogmc.phytotelma.Constants
import dev.frogmc.phytotelma.ProjectStorage import dev.frogmc.phytotelma.ProjectStorage
import dev.frogmc.phytotelma.VersionChecker import dev.frogmc.phytotelma.VersionChecker
import dev.frogmc.phytotelma.accesswidener.AccessWidener import dev.frogmc.phytotelma.accesswidener.AccessWidener
import dev.frogmc.phytotelma.ext.datagen.DatagenExtension
import dev.frogmc.phytotelma.run.AssetDownloader import dev.frogmc.phytotelma.run.AssetDownloader
import dev.frogmc.phytotelma.run.RunConfigGenerator 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.Thyroxine
import dev.frogmc.thyroxine.api.data.MappingData
import org.gradle.api.Action import org.gradle.api.Action
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.model.ObjectFactory import org.gradle.api.model.ObjectFactory
import java.nio.file.Files
import javax.inject.Inject import javax.inject.Inject
import kotlin.io.path.createParentDirectories import kotlin.io.path.createParentDirectories
import kotlin.io.path.notExists import kotlin.io.path.notExists
@ -51,6 +58,22 @@ abstract class PhytotelmaGradleExtensionImpl @Inject constructor(
mappings.forNamespaces("official", mcConf.targetNamespace), mappings.forNamespaces("official", mcConf.targetNamespace),
clientJar, remappedJar, true, true clientJar, remappedJar, true, true
) )
VersionChecker.savePomFile(version, remappedJar.parent)
if (mcConf.targetNamespace != Constants.MOJMAP_NAMESPACE) {
Thyroxine.remap(
MappingData("", ""),
remappedJar,
remappedJar.resolveSibling(Constants.ALTERNATIVE_RUNTIME_JAR_NAME),
false,
listOf(RemappingStep { classVisitor, _ ->
RuntimeAccessFixVisitor(classVisitor)
})
)
} else {
Files.deleteIfExists(remappedJar.resolveSibling(Constants.ALTERNATIVE_RUNTIME_JAR_NAME))
}
RunConfigGenerator.generate(project)
} }
project.dependencies.add( project.dependencies.add(
@ -60,10 +83,8 @@ abstract class PhytotelmaGradleExtensionImpl @Inject constructor(
VersionChecker.getDependencies(version) { VersionChecker.getDependencies(version) {
project.dependencies.add(Constants.MINECRAFT_CONFIGURATION, it) project.dependencies.add(Constants.MINECRAFT_CONFIGURATION, it)
} }
VersionChecker.savePomFile(version, remappedJar.parent)
project.dependencies.add(Constants.MINECRAFT_CONFIGURATION, "net.minecraft:client:$version:remapped") project.dependencies.add(Constants.MINECRAFT_CONFIGURATION, "net.minecraft:client:$version:remapped")
RunConfigGenerator.generate(project)
AssetDownloader.download(project) AssetDownloader.download(project)
if (applyAW) { if (applyAW) {
@ -81,7 +102,7 @@ abstract class PhytotelmaGradleExtensionImpl @Inject constructor(
if (!conf.version.isPresent) { if (!conf.version.isPresent) {
error("No loader version provided!") error("No loader version provided!")
} }
project.dependencies.add("implementation", "dev.frogmc:frogloader:${conf.version.get()}") project.dependencies.add(Constants.MINECRAFT_CONFIGURATION, "dev.frogmc:frogloader:${conf.version.get()}")
} }
override fun froglib(action: Action<VersionConfiguration>) { override fun froglib(action: Action<VersionConfiguration>) {
@ -92,4 +113,11 @@ abstract class PhytotelmaGradleExtensionImpl @Inject constructor(
} }
project.dependencies.add("modImplementation", "dev.frogmc:froglib:${conf.version.get()}") project.dependencies.add("modImplementation", "dev.frogmc:froglib:${conf.version.get()}")
} }
override fun datagen(action: Action<DatagenExtension>) {
val conf = objects.newInstance(DatagenExtension::class.java)
action.execute(conf)
DatagenConfigGenerator.generate(project, conf)
project.tasks.register("runDatagen", DatagenTask::class.java, conf)
}
} }

View file

@ -0,0 +1,12 @@
package dev.frogmc.phytotelma.ext.datagen
import org.gradle.api.Project
import org.gradle.api.model.ObjectFactory
import org.gradle.api.provider.Property
import javax.inject.Inject
abstract class DatagenExtension @Inject constructor(project: Project, objects: ObjectFactory) {
val generatorClass: Property<String> = objects.property(String::class.java).unset()
}

View file

@ -0,0 +1,248 @@
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.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 val mojmapGameJar: 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 visitInnerClass(name: String?, outerName: String?, innerName: String?, access: Int) {
super.visitInnerClass(name, outerName, innerName, access)
}
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()
FileSystems.newFileSystem(mojmapGameJar).use { fs ->
fs.getPath("$target.class").takeIf { it.exists() }
?.readBytes()?.let(::ClassReader).let { it?.accept(targetNode, 0) }
}
}
super.visitEnd()
}
}
}
override fun visitMethod(
access: Int,
name: String?,
descriptor: String?,
signature: String?,
exceptions: Array<out String>?
): MethodVisitor? {
return super.visitMethod(access, name, descriptor, signature, exceptions)?.let {
if (targetNode != null) {
MixinMethodVisitor(it, mapper, targetNode!!)
} else it
}
}
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 == "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) {
newVal = mapper.mapMethodName(
owner.name,
methodName,
method.desc
) + mapper.mapMethodDesc(method.desc)
println("Mapped mixin target (method) to new name: $newVal from $methodName (with found ${method.desc})")
}
} else {
newVal = mapper.mapMethodName(
owner.name,
methodName,
desc
) + mapper.mapMethodDesc(desc)
}
} else if (name == "target") {
newVal = remapMemberInfo(value, mapper)
println("target remap: $value to $newVal")
}
}
if (newVal != value) {
println("new annotation value: $newVal instead of $value")
}
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

@ -0,0 +1,25 @@
package dev.frogmc.phytotelma.nest
import dev.frogmc.phytotelma.Constants
import dev.frogmc.phytotelma.PhytotelmaPlugin
import java.nio.file.FileSystems
import java.nio.file.Path
import kotlin.io.path.exists
import kotlin.io.path.inputStream
import kotlin.io.path.outputStream
object NestStripper {
fun stripJij(modJar: Path) {
FileSystems.newFileSystem(modJar).use { fs ->
val path = fs.getPath(Constants.MOD_METADATA_FILE)
path.takeIf { it.exists() }?.apply {
val config = PhytotelmaPlugin.tomlParser.parse(this.inputStream())
config.remove<Any>("frog.extensions.included_jars")
PhytotelmaPlugin.tomlWriter.write(config, this.outputStream())
}
}
}
}

View file

@ -2,9 +2,9 @@ package dev.frogmc.phytotelma.nest
import com.electronwill.nightconfig.core.Config import com.electronwill.nightconfig.core.Config
import com.electronwill.nightconfig.core.UnmodifiableConfig import com.electronwill.nightconfig.core.UnmodifiableConfig
import com.electronwill.nightconfig.toml.TomlParser
import com.google.common.jimfs.Jimfs import com.google.common.jimfs.Jimfs
import dev.frogmc.phytotelma.Constants import dev.frogmc.phytotelma.Constants
import dev.frogmc.phytotelma.PhytotelmaPlugin
import org.gradle.api.artifacts.Configuration import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ProjectDependency import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.plugins.JavaPlugin import org.gradle.api.plugins.JavaPlugin
@ -135,7 +135,7 @@ object Nester {
} }
private fun readModId(toml: Path): String { private fun readModId(toml: Path): String {
return TomlParser().parse(toml.inputStream()).get("frog.mod.id") return PhytotelmaPlugin.tomlParser.parse(toml.inputStream()).get("frog.mod.id")
} }
} }

View file

@ -1,5 +1,6 @@
package dev.frogmc.phytotelma.run package dev.frogmc.phytotelma.run
import dev.frogmc.phytotelma.Constants
import dev.frogmc.phytotelma.PhytotelmaPlugin import dev.frogmc.phytotelma.PhytotelmaPlugin
import dev.frogmc.phytotelma.ProjectStorage import dev.frogmc.phytotelma.ProjectStorage
import dev.frogmc.phytotelma.VersionChecker import dev.frogmc.phytotelma.VersionChecker
@ -15,26 +16,29 @@ import kotlin.io.path.createParentDirectories
import kotlin.io.path.notExists import kotlin.io.path.notExists
object RunConfigGenerator { object RunConfigGenerator {
private const val LOG4J_CONFIG_PATH = ".gradle/phytotelma/log4j.xml" val ADAPTERS = arrayOf(EclipseAdapter(), IdeaAdapter())
private const val ASSET_DIR = "assets"
private val ADAPTERS = arrayOf(EclipseAdapter(), IdeaAdapter())
fun generate(project: Project) { fun generate(project: Project) {
val log4jPath = project.rootDir.resolve(LOG4J_CONFIG_PATH).toPath().absolute()
if (log4jPath.notExists()) {
log4jPath.createParentDirectories()
RunConfigGenerator::class.java.getResourceAsStream("/log4j.xml").use { input ->
Files.copy(input!!, log4jPath)
}
}
val assetPath = PhytotelmaPlugin.globalCacheDir.resolve(ASSET_DIR).absolute() val assetPath = PhytotelmaPlugin.globalCacheDir.resolve("assets").absolute()
val assetIndexPath = assetPath.resolve("indexes") val assetIndexPath = assetPath.resolve("indexes")
if (assetIndexPath.notExists()) { if (assetIndexPath.notExists()) {
assetIndexPath.createDirectories() assetIndexPath.createDirectories()
} }
val indexId = VersionChecker.fetchVersionData(ProjectStorage.get(project).minecraftVersion!!).assetIndex.id
val log4jPath = project.rootDir.resolve(".gradle/phytotelma/log4j.xml").toPath().absolute()
if (log4jPath.notExists()) {
log4jPath.createParentDirectories()
this::class.java.getResourceAsStream("/log4j.xml").use { input ->
Files.copy(input!!, log4jPath)
}
}
val projectData = ProjectStorage.get(project) val projectData = ProjectStorage.get(project)
val indexId = VersionChecker.fetchVersionData(projectData.minecraftVersion!!).assetIndex.id var runtimeGameJar = projectData.remappedGameJarPath!!
if (projectData.targetNamespace != Constants.MOJMAP_NAMESPACE) {
runtimeGameJar = runtimeGameJar.resolveSibling(Constants.ALTERNATIVE_RUNTIME_JAR_NAME)
}
val projectName = if (project.rootDir == project.projectDir) "" else " (${project.name})" val projectName = if (project.rootDir == project.projectDir) "" else " (${project.name})"
@ -50,14 +54,15 @@ object RunConfigGenerator {
mutableListOf( mutableListOf(
"-Xmx${project.properties.getOrDefault("frogmc.gameHeap", "2048M")}", "-Xmx${project.properties.getOrDefault("frogmc.gameHeap", "2048M")}",
"-Dfrogmc.development=true", "-Dfrogmc.development=true",
"-Dfrogmc.plugin.minecraft.gameJar=${projectData.remappedGameJarPath}", "-Dfrogmc.plugin.minecraft.gameJar=${runtimeGameJar}",
"-Dlog4j2.configurationFile=$log4jPath", "-Dlog4j2.configurationFile=$log4jPath",
"-Dlog4j2.formatMsgLookups=true" "-Dlog4j2.formatMsgNoLookups=true"
).apply { ).also {
if (project.gradle.startParameter.consoleOutput != ConsoleOutput.Plain) { if (project.gradle.startParameter.consoleOutput != ConsoleOutput.Plain) {
add("-Dfrogmc.log.disableAnsi=false") it.add("-Dfrogmc.log.disableAnsi=false")
} }
}.toTypedArray(), if (env == Env.CLIENT) { }.toTypedArray(),
if (env == Env.CLIENT) {
arrayOf( arrayOf(
"--assetsDir", assetPath.toString(), "--assetsDir", assetPath.toString(),
"--version", "FrogMC", "--version", "FrogMC",

View file

@ -0,0 +1,49 @@
package dev.frogmc.phytotelma.run
import dev.frogmc.phytotelma.Constants
import org.objectweb.asm.ClassVisitor
import org.objectweb.asm.FieldVisitor
import org.objectweb.asm.MethodVisitor
import org.objectweb.asm.Opcodes
class RuntimeAccessFixVisitor(cv: ClassVisitor): ClassVisitor(Constants.ASM_VERSION, cv) {
private fun fixAccess(access: Int): Int {
return if ((access and 0x7) != Opcodes.ACC_PRIVATE) (access and 0x7.inv()) or Opcodes.ACC_PUBLIC else access
}
override fun visit(
version: Int,
access: Int,
name: String?,
signature: String?,
superName: String?,
interfaces: Array<out String>?
) {
super.visit(version, fixAccess(access), name, signature, superName, interfaces)
}
override fun visitField(
access: Int,
name: String?,
descriptor: String?,
signature: String?,
value: Any?
): FieldVisitor {
return super.visitField(fixAccess(access), name, descriptor, signature, value)
}
override fun visitInnerClass(name: String?, outerName: String?, innerName: String?, access: Int) {
super.visitInnerClass(name, outerName, innerName, fixAccess(access))
}
override fun visitMethod(
access: Int,
name: String?,
descriptor: String?,
signature: String?,
exceptions: Array<out String>?
): MethodVisitor {
return super.visitMethod(fixAccess(access), name, descriptor, signature, exceptions)
}
}

View file

@ -38,6 +38,7 @@ class IdeaAdapter : RunConfigAdapter {
val runDir = project.projectDir.resolve("run") val runDir = project.projectDir.resolve("run")
runDir.mkdirs() runDir.mkdirs()
file.writer(StandardCharsets.UTF_8).use { file.writer(StandardCharsets.UTF_8).use {
it.write( it.write(
""" """
@ -57,6 +58,7 @@ class IdeaAdapter : RunConfigAdapter {
""".trimIndent() """.trimIndent()
) )
} }
} }
override fun shouldGenerate(): Boolean { override fun shouldGenerate(): Boolean {

View file

@ -0,0 +1,60 @@
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
import org.gradle.api.Project
import org.gradle.api.logging.configuration.ConsoleOutput
import java.nio.file.Files
import kotlin.io.path.absolute
import kotlin.io.path.createParentDirectories
import kotlin.io.path.notExists
object DatagenConfigGenerator {
fun generate(project: Project, conf: DatagenExtension) {
val projectName = if (project.rootDir == project.projectDir) "" else " (${project.name})"
val log4jPath = project.rootDir.resolve(".gradle/phytotelma/log4j.xml").toPath().absolute()
if (log4jPath.notExists()) {
log4jPath.createParentDirectories()
this::class.java.getResourceAsStream("/log4j.xml").use { input ->
Files.copy(input!!, log4jPath)
}
}
val projectData = ProjectStorage.get(project)
var runtimeGameJar = projectData.remappedGameJarPath!!
if (projectData.targetNamespace != Constants.MOJMAP_NAMESPACE) {
runtimeGameJar = runtimeGameJar.resolveSibling(Constants.ALTERNATIVE_RUNTIME_JAR_NAME)
}
for (adapter in RunConfigGenerator.ADAPTERS) {
if (!adapter.shouldGenerate())
continue
adapter.generate(
project,
"Minecraft (Data Generation)$projectName",
"dev.frogmc.frogloader.impl.launch.server.FrogServer",
mutableListOf(
"-Xmx${project.properties.getOrDefault("frogmc.gameHeap", "2048M")}",
"-Dfrogmc.development=true",
"-Dfrogmc.plugin.minecraft.gameJar=${runtimeGameJar}",
"-Dlog4j2.configurationFile=$log4jPath",
"-Dlog4j2.formatMsgNoLookups=true",
"-Dfrog.datagen.enabled=true",
"-Dfrog.datagen.modid=${Datagen.readModId(project)}",
"-Dfrog.datagen.generator=${conf.generatorClass.get()}"
).also {
if (project.gradle.startParameter.consoleOutput != ConsoleOutput.Plain) {
it.add("-Dfrogmc.log.disableAnsi=false")
}
}.toTypedArray(),
arrayOf("--nogui")
)
}
}
}

View file

@ -0,0 +1,33 @@
package dev.frogmc.phytotelma.run.datagen
import com.electronwill.nightconfig.toml.TomlParser
import dev.frogmc.phytotelma.Constants
import dev.frogmc.phytotelma.common.Env
import dev.frogmc.phytotelma.ext.datagen.DatagenExtension
import dev.frogmc.phytotelma.run.task.RunGameTask
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginExtension
import javax.inject.Inject
object Datagen {
fun readModId(project: Project): String {
project.extensions.findByType(JavaPluginExtension::class.java)?.sourceSets?.forEach { set ->
set.resources.filter { it.name == Constants.MOD_METADATA_FILE }.firstOrNull {
return TomlParser().parse(it.readText()).get("frog.mod.id")
}
}
error("Mod ID could not be read!")
}
}
abstract class DatagenTask @Inject constructor(conf: DatagenExtension) : RunGameTask(Env.SERVER) {
init {
jvmArguments.addAll(
"-Dfrog.datagen.enabled=true",
"-Dfrog.datagen.modid=${Datagen.readModId(project)}",
"-Dfrog.datagen.generator=${conf.generatorClass.get()}"
)
}
}

View file

@ -54,10 +54,16 @@ abstract class RunGameTask @Inject constructor(env: Env) : JavaExec() {
SourceSet.MAIN_SOURCE_SET_NAME SourceSet.MAIN_SOURCE_SET_NAME
).runtimeClasspath.filter { it.exists() }) ).runtimeClasspath.filter { it.exists() })
val projectData = ProjectStorage.get(project)
var runtimeGameJar = projectData.remappedGameJarPath!!
if (projectData.targetNamespace != Constants.MOJMAP_NAMESPACE) {
runtimeGameJar = runtimeGameJar.resolveSibling(Constants.ALTERNATIVE_RUNTIME_JAR_NAME)
}
jvmArguments.addAll( jvmArguments.addAll(
"-Xmx${project.properties.getOrDefault("frogmc.gameHeap", "2048M")}", "-Xmx${project.properties.getOrDefault("frogmc.gameHeap", "2048M")}",
"-Dfrogmc.development=true", "-Dfrogmc.development=true",
"-Dfrogmc.plugin.minecraft.gameJar=${ProjectStorage.get(project).remappedGameJarPath}", "-Dfrogmc.plugin.minecraft.gameJar=${runtimeGameJar}",
"-Dlog4j2.configurationFile=$log4jPath", "-Dlog4j2.configurationFile=$log4jPath",
"-Dlog4j2.formatMsgNoLookups=true", "-Dlog4j2.formatMsgNoLookups=true",
writeArgFile() writeArgFile()