Datagen insanity, dependency, mixin & AW remapping #4
|
@ -126,6 +126,12 @@ class PhytotelmaPlugin : Plugin<Project> {
|
|||
}
|
||||
|
||||
setupTasks(project)
|
||||
|
||||
project.afterEvaluate {
|
||||
project.afterEvaluate {
|
||||
ProjectStorage.write(project)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setupTasks(project: Project) {
|
||||
|
|
|
@ -1,21 +1,74 @@
|
|||
package dev.frogmc.phytotelma
|
||||
|
||||
import com.google.gson.FieldNamingPolicy
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.TypeAdapter
|
||||
import com.google.gson.stream.JsonReader
|
||||
import com.google.gson.stream.JsonToken
|
||||
import com.google.gson.stream.JsonWriter
|
||||
import dev.frogmc.thyroxine.api.data.DocumentationData
|
||||
import dev.frogmc.thyroxine.api.data.DocumentationData.Field
|
||||
import dev.frogmc.thyroxine.api.data.DocumentationData.Method
|
||||
import dev.frogmc.thyroxine.api.data.MappingBundle
|
||||
import dev.frogmc.thyroxine.api.data.MappingData
|
||||
import dev.frogmc.thyroxine.api.data.Member
|
||||
import org.gradle.api.Project
|
||||
import java.nio.file.Path
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.*
|
||||
import kotlin.io.path.*
|
||||
|
||||
object ProjectStorage {
|
||||
|
||||
private val data = ConcurrentHashMap<String, ProjectData>()
|
||||
private val data = WeakHashMap<String, ProjectData>()
|
||||
private val gson = GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
|
||||
.registerTypeAdapter(ProjectData::class.java, ProjectDataTypeAdapter())
|
||||
.registerTypeAdapter(MappingBundle::class.java, MappingBundleTypeAdapter())
|
||||
.create()
|
||||
|
||||
fun get(project: Project): ProjectData {
|
||||
val id = project.getId()
|
||||
if (!data.containsKey(id)) {
|
||||
val cacheFile = getCacheFile(project)
|
||||
val projectData = cacheFile.takeIf { it.exists() }?.let { read(it) }
|
||||
if (projectData != null) {
|
||||
data[id] = projectData
|
||||
} else {
|
||||
println("Creating project data store for $id, size: ${data.size}, ${this.hashCode()}")
|
||||
data[id] = ProjectData(null, null, null, null, null, null)
|
||||
}
|
||||
}
|
||||
return data[id]!!
|
||||
}
|
||||
|
||||
private fun getCacheFile(project: Project): Path {
|
||||
return project.projectDir.resolve(".gradle/phytotelma/${project.getId()}_data").toPath()
|
||||
}
|
||||
|
||||
fun write(project: Project) {
|
||||
write(getCacheFile(project), data[project.getId()]!!)
|
||||
}
|
||||
|
||||
private fun write(path: Path, projectData: ProjectData) {
|
||||
/**
|
||||
* If you're wondering "why not use the stream-based API, it should offer better performance *especially*
|
||||
* for huge documents like this!"
|
||||
* The answer is because it doesn't work. Somewhere in the buffer handling the document just
|
||||
* gets cut off and left in an invalid state. Yes, 30+ MiB Strings aren't ideal either.
|
||||
* But I'd honestly prefer that nightmare over invalid json files.
|
||||
*/
|
||||
path.writeText(gson.toJson(projectData))
|
||||
}
|
||||
|
||||
private fun read(path: Path): ProjectData? {
|
||||
val reader = path.takeIf { it.exists() }?.reader()
|
||||
if (reader != null) {
|
||||
val data = gson.fromJson(reader, ProjectData::class.java)
|
||||
return data
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
class ProjectData(
|
||||
|
@ -26,3 +79,312 @@ class ProjectData(
|
|||
var mappingsName: String?,
|
||||
var targetNamespace: String?
|
||||
)
|
||||
|
||||
class ProjectDataTypeAdapter : TypeAdapter<ProjectData>() {
|
||||
override fun write(out: JsonWriter, value: ProjectData) {
|
||||
out.beginObject()
|
||||
out.serializeNulls = true
|
||||
out.name("local_cache_dir").value(value.localCacheDir?.absolutePathString())
|
||||
out.name("minecraft_version").value(value.minecraftVersion)
|
||||
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.endObject()
|
||||
}
|
||||
|
||||
override fun read(r: JsonReader): ProjectData {
|
||||
r.beginObject()
|
||||
val data = ProjectData(null, null, null, null, null, null)
|
||||
while (r.peek() != JsonToken.END_OBJECT) {
|
||||
val name = r.nextName()
|
||||
if (r.peek() == JsonToken.STRING) {
|
||||
val value = r.nextString()
|
||||
when (name) {
|
||||
"local_cache_dir" -> {
|
||||
data.localCacheDir = Path.of(value)
|
||||
}
|
||||
|
||||
"minecraft_version" -> {
|
||||
data.minecraftVersion = 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)
|
||||
} else {
|
||||
r.skipValue()
|
||||
}
|
||||
}
|
||||
r.endObject()
|
||||
return data
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class MappingBundleTypeAdapter : TypeAdapter<MappingBundle>() {
|
||||
override fun write(out: JsonWriter, value: MappingBundle) {
|
||||
out.beginObject()
|
||||
out.name("data").beginArray()
|
||||
value.data.forEach {
|
||||
out.beginObject()
|
||||
out.name("srcNs").value(it.srcNamespace)
|
||||
out.name("dstNs").value(it.dstNamespace)
|
||||
out.name("classes").beginObject()
|
||||
it.classes.forEach { (k, v) ->
|
||||
out.name(k).value(v)
|
||||
}
|
||||
out.endObject()
|
||||
out.name("methods").beginArray()
|
||||
it.methods.forEach { (k, v) ->
|
||||
out.beginObject()
|
||||
out.name("owner").value(k.owner)
|
||||
out.name("desc").value(k.descriptor)
|
||||
out.name("name").value(k.name)
|
||||
out.name("value").value(v)
|
||||
out.endObject()
|
||||
}
|
||||
out.endArray()
|
||||
out.name("fields").beginArray()
|
||||
it.fields.forEach { (k, v) ->
|
||||
out.beginObject()
|
||||
out.name("owner").value(k.owner)
|
||||
out.name("desc").value(k.descriptor)
|
||||
out.name("name").value(k.name)
|
||||
out.name("value").value(v)
|
||||
out.endObject()
|
||||
}
|
||||
out.endArray()
|
||||
out.name("parameters").beginArray()
|
||||
it.parameters.forEach { (k, v) ->
|
||||
out.beginObject()
|
||||
out.name("owner").value(k.owner)
|
||||
out.name("desc").value(k.descriptor)
|
||||
out.name("name").value(k.name)
|
||||
out.name("value").beginObject()
|
||||
v.forEach { (i, s) ->
|
||||
out.name("$i").value(s)
|
||||
}
|
||||
out.endObject()
|
||||
out.endObject()
|
||||
}
|
||||
out.endArray()
|
||||
out.endObject()
|
||||
}
|
||||
out.endArray()
|
||||
out.name("documentation").beginArray()
|
||||
value.documentation.forEach {
|
||||
out.beginObject()
|
||||
out.name("ns").value(it.namespace)
|
||||
out.name("classes").beginArray()
|
||||
it.classes.forEach { c ->
|
||||
out.beginObject()
|
||||
out.name("name").value(c.name)
|
||||
out.name("javadoc").beginArray()
|
||||
c.javadoc.forEach { d ->
|
||||
out.value(d)
|
||||
}
|
||||
out.endArray()
|
||||
out.name("methods").beginArray()
|
||||
c.methods.forEach { m ->
|
||||
out.beginObject()
|
||||
out.name("name").value(m.name)
|
||||
out.name("desc").value(m.descriptor)
|
||||
out.name("javadoc").beginArray()
|
||||
m.javadoc.forEach { d ->
|
||||
out.value(d)
|
||||
}
|
||||
out.endArray()
|
||||
out.endObject()
|
||||
}
|
||||
out.endArray()
|
||||
out.name("fields").beginArray()
|
||||
c.fields.forEach { f ->
|
||||
out.beginObject()
|
||||
out.name("name").value(f.name)
|
||||
out.name("desc").value(f.descriptor)
|
||||
out.name("javadoc").beginArray()
|
||||
f.javadoc.forEach { d ->
|
||||
out.value(d)
|
||||
}
|
||||
out.endArray()
|
||||
out.endObject()
|
||||
}
|
||||
out.endArray()
|
||||
out.endObject()
|
||||
}
|
||||
out.endArray()
|
||||
out.endObject()
|
||||
}
|
||||
out.endArray()
|
||||
out.endObject()
|
||||
}
|
||||
|
||||
override fun read(r: JsonReader): MappingBundle {
|
||||
val out = MappingBundle()
|
||||
r.beginObject()
|
||||
r.nextName()
|
||||
r.beginArray()
|
||||
while (r.peek() != JsonToken.END_ARRAY) {
|
||||
r.beginObject()
|
||||
out.data.add(readData(r))
|
||||
r.endObject()
|
||||
}
|
||||
r.endArray()
|
||||
r.nextName()
|
||||
r.beginArray()
|
||||
while (r.peek() != JsonToken.END_ARRAY) {
|
||||
r.beginObject()
|
||||
r.nextName()
|
||||
val namespace = r.nextString()
|
||||
val data = DocumentationData(namespace)
|
||||
out.documentation.add(data)
|
||||
r.nextName()
|
||||
r.beginArray()
|
||||
while (r.peek() != JsonToken.END_ARRAY) {
|
||||
r.beginObject()
|
||||
r.nextName()
|
||||
val name = r.nextString()
|
||||
r.nextName()
|
||||
val javadoc = mutableListOf<String>()
|
||||
r.beginArray()
|
||||
while (r.peek() != JsonToken.END_ARRAY) {
|
||||
javadoc.add(r.nextString())
|
||||
}
|
||||
r.endArray()
|
||||
r.nextName()
|
||||
val methods = mutableListOf<Method>()
|
||||
r.beginArray()
|
||||
while (r.peek() != JsonToken.END_ARRAY) {
|
||||
r.beginObject()
|
||||
r.nextName()
|
||||
val methodName = r.nextString()
|
||||
r.nextName()
|
||||
val desc = r.nextString()
|
||||
r.nextName()
|
||||
val methodJavadoc = mutableListOf<String>()
|
||||
r.beginArray()
|
||||
while (r.peek() != JsonToken.END_ARRAY) {
|
||||
methodJavadoc.add(r.nextString())
|
||||
}
|
||||
r.endArray()
|
||||
methods.add(Method(methodName, desc, methodJavadoc))
|
||||
r.endObject()
|
||||
}
|
||||
r.endArray()
|
||||
r.nextName()
|
||||
val fields = mutableListOf<Field>()
|
||||
r.beginArray()
|
||||
while (r.peek() != JsonToken.END_ARRAY) {
|
||||
r.beginObject()
|
||||
r.nextName()
|
||||
val fieldName = r.nextString()
|
||||
r.nextName()
|
||||
val desc = r.nextString()
|
||||
r.nextName()
|
||||
val fieldJavadoc = mutableListOf<String>()
|
||||
r.beginArray()
|
||||
while (r.peek() != JsonToken.END_ARRAY) {
|
||||
fieldJavadoc.add(r.nextString())
|
||||
}
|
||||
r.endArray()
|
||||
fields.add(Field(fieldName, desc, fieldJavadoc))
|
||||
r.endObject()
|
||||
}
|
||||
r.endArray()
|
||||
data.classes.add(DocumentationData.Class(name, javadoc, fields, methods))
|
||||
r.endObject()
|
||||
}
|
||||
r.endArray()
|
||||
r.endObject()
|
||||
}
|
||||
r.endArray()
|
||||
r.endObject()
|
||||
|
||||
return out
|
||||
}
|
||||
|
||||
private fun readData(r: JsonReader): MappingData {
|
||||
r.nextName()
|
||||
val srcNs = r.nextString()
|
||||
r.nextName()
|
||||
val dstNs = r.nextString()
|
||||
r.nextName()
|
||||
r.beginObject()
|
||||
val classes = mutableMapOf<String, String>()
|
||||
while (r.peek() != JsonToken.END_OBJECT) {
|
||||
classes[r.nextName()] = r.nextString()
|
||||
}
|
||||
r.endObject()
|
||||
r.nextName()
|
||||
r.beginArray()
|
||||
val methods = mutableMapOf<Member, String>()
|
||||
while (r.peek() != JsonToken.END_ARRAY) {
|
||||
r.beginObject()
|
||||
r.nextName()
|
||||
val owner = r.nextString()
|
||||
r.nextName()
|
||||
val desc = r.nextString()
|
||||
r.nextName()
|
||||
val name = r.nextString()
|
||||
r.nextName()
|
||||
val value = r.nextString()
|
||||
methods[Member(owner, name, desc)] = value
|
||||
r.endObject()
|
||||
}
|
||||
r.endArray()
|
||||
r.nextName()
|
||||
r.beginArray()
|
||||
val fields = mutableMapOf<Member, String>()
|
||||
while (r.peek() != JsonToken.END_ARRAY) {
|
||||
r.beginObject()
|
||||
r.nextName()
|
||||
val owner = r.nextString()
|
||||
r.nextName()
|
||||
val desc = r.nextString()
|
||||
r.nextName()
|
||||
val name = r.nextString()
|
||||
r.nextName()
|
||||
val value = r.nextString()
|
||||
fields[Member(owner, name, desc)] = value
|
||||
r.endObject()
|
||||
}
|
||||
r.endArray()
|
||||
r.nextName()
|
||||
r.beginArray()
|
||||
val parameters = mutableMapOf<Member, MutableMap<Int, String>>()
|
||||
while (r.peek() != JsonToken.END_ARRAY) {
|
||||
r.beginObject()
|
||||
r.nextName()
|
||||
val owner = r.nextString()
|
||||
r.nextName()
|
||||
val desc = r.nextString()
|
||||
r.nextName()
|
||||
val name = r.nextString()
|
||||
r.nextName()
|
||||
r.beginObject()
|
||||
val values = mutableMapOf<Int, String>()
|
||||
while (r.peek() != JsonToken.END_OBJECT) {
|
||||
values[r.nextName().toInt()] = r.nextString()
|
||||
}
|
||||
r.endObject()
|
||||
parameters[Member(owner, name, desc)] = values
|
||||
r.endObject()
|
||||
}
|
||||
r.endArray()
|
||||
return MappingData(srcNs, dstNs, classes, methods, fields, parameters)
|
||||
}
|
||||
|
||||
}
|
|
@ -74,6 +74,10 @@ object AccessWidener {
|
|||
return false
|
||||
}
|
||||
|
||||
fun hasAW(project: Project): Boolean {
|
||||
return getAWFile(project) != null
|
||||
}
|
||||
|
||||
fun checkAW(path: Path, gamePath: Path) {
|
||||
val reader = path.bufferedReader(StandardCharsets.UTF_8)
|
||||
if (!HEADER.test(reader.readLine() ?: "")) {
|
||||
|
|
|
@ -49,7 +49,7 @@ abstract class PhytotelmaGradleExtensionImpl @Inject constructor(
|
|||
projectData.localCacheDir!!.resolve("net/minecraft/client/$version/client-$version-remapped.jar")
|
||||
remappedJar.createParentDirectories()
|
||||
projectData.remappedGameJarPath = remappedJar
|
||||
val applyAW = AccessWidener.needsUpdate(project)
|
||||
var applyAW = AccessWidener.needsUpdate(project)
|
||||
if (remappedJar.notExists() || applyAW || mcConf.mappingsName != projectData.mappingsName) {
|
||||
projectData.mappingsName = mcConf.mappingsName
|
||||
println("Remapping the game...")
|
||||
|
@ -59,21 +59,8 @@ abstract class PhytotelmaGradleExtensionImpl @Inject constructor(
|
|||
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)
|
||||
applyAW = AccessWidener.hasAW(project)
|
||||
}
|
||||
|
||||
project.dependencies.add(
|
||||
|
@ -91,6 +78,19 @@ abstract class PhytotelmaGradleExtensionImpl @Inject constructor(
|
|||
project.afterEvaluate {
|
||||
println("Applying AccessWideners...")
|
||||
AccessWidener.apply(project, remappedJar)
|
||||
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))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ 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 org.gradle.api.plugins.JavaPluginExtension
|
||||
import java.nio.file.Files
|
||||
import kotlin.io.path.absolute
|
||||
import kotlin.io.path.createParentDirectories
|
||||
|
@ -47,7 +48,12 @@ object DatagenConfigGenerator {
|
|||
"-Dlog4j2.formatMsgNoLookups=true",
|
||||
"-Dfrog.datagen.enabled=true",
|
||||
"-Dfrog.datagen.modid=${Datagen.readModId(project)}",
|
||||
"-Dfrog.datagen.generator=${conf.generatorClass.get()}"
|
||||
"-Dfrog.datagen.generator=${conf.generatorClass.get()}",
|
||||
"-Dfrog.datagen.output=${
|
||||
project.extensions.findByType(JavaPluginExtension::class.java)?.sourceSets?.maybeCreate(
|
||||
"generated"
|
||||
)?.resources?.srcDirs?.first()?.toString()
|
||||
}"
|
||||
).also {
|
||||
if (project.gradle.startParameter.consoleOutput != ConsoleOutput.Plain) {
|
||||
it.add("-Dfrogmc.log.disableAnsi=false")
|
||||
|
|
|
@ -26,7 +26,12 @@ abstract class DatagenTask @Inject constructor(conf: DatagenExtension) : RunGame
|
|||
jvmArguments.addAll(
|
||||
"-Dfrog.datagen.enabled=true",
|
||||
"-Dfrog.datagen.modid=${Datagen.readModId(project)}",
|
||||
"-Dfrog.datagen.generator=${conf.generatorClass.get()}"
|
||||
"-Dfrog.datagen.generator=${conf.generatorClass.get()}",
|
||||
"-Dfrog.datagen.output=${
|
||||
project.extensions.findByType(JavaPluginExtension::class.java)?.sourceSets?.maybeCreate(
|
||||
"generated"
|
||||
)?.resources?.srcDirs?.first()?.toString()
|
||||
}"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue