Merge pull request 'add jij inclusions' (#3) from owlsys/jij into mistress

Reviewed-on: https://git-esnesnon.ecorous.org/esnesnon/phytotelma/pulls/3
Reviewed-by: Ecorous <ecorous@outlook.com>
This commit is contained in:
owlsys 2024-06-01 12:19:25 -04:00
commit 709f5fbbe7
12 changed files with 590 additions and 70 deletions

1
.gitignore vendored
View file

@ -37,3 +37,4 @@ bin/
### Mac OS ### ### Mac OS ###
.DS_Store .DS_Store
.kotlin/

View file

@ -1,5 +1,5 @@
plugins { plugins {
kotlin("jvm") version "1.9.23" kotlin("jvm") version "2.0.0"
`java-gradle-plugin` `java-gradle-plugin`
`maven-publish` `maven-publish`
} }
@ -21,21 +21,30 @@ repositories {
} }
dependencies { dependencies {
implementation("org.ecorous.esnesnon:nonsense-remapper:1.0.0-SNAPSHOT") implementation("org.ecorous.esnesnon:thyroxine:1.0.0-SNAPSHOT")
implementation("org.ow2.asm:asm:9.7")
implementation("com.google.code.gson:gson:2.10.1") implementation("com.google.code.gson:gson:2.10.1")
implementation("org.vineflower:vineflower:1.10.1") implementation("org.vineflower:vineflower:1.10.1")
testImplementation(kotlin("test")) testImplementation(kotlin("test"))
implementation("com.electronwill.night-config:toml:3.7.2")
implementation("com.google.jimfs:jimfs:1.3.0")
} }
gradlePlugin { gradlePlugin {
plugins { plugins {
create("nonsense-gradle") { create("phytotelma") {
id = "org.ecorous.esnesnon.nonsense-gradle" id = "org.ecorous.esnesnon.phytotelma"
implementationClass = "org.ecorous.esnesnon.gradle.NonsenseGradlePlugin" implementationClass = "org.ecorous.esnesnon.gradle.PhytotelmaPlugin"
} }
} }
} }
tasks.jar {
manifest {
attributes["Implementation-Version"] = version
}
}
tasks.test { tasks.test {
useJUnitPlatform() useJUnitPlatform()
} }

View file

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

View file

@ -1,7 +1,14 @@
package org.ecorous.esnesnon.gradle package org.ecorous.esnesnon.gradle
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 net.fabricmc.fernflower.api.IFabricJavadocProvider import net.fabricmc.fernflower.api.IFabricJavadocProvider
import org.ecorous.esnesnon.gradle.accesswidener.AccessWidener
import org.ecorous.esnesnon.gradle.common.Env import org.ecorous.esnesnon.gradle.common.Env
import org.ecorous.esnesnon.gradle.nest.Nester
import org.ecorous.esnesnon.gradle.run.AssetDownloader import org.ecorous.esnesnon.gradle.run.AssetDownloader
import org.ecorous.esnesnon.gradle.run.RunConfigGenerator import org.ecorous.esnesnon.gradle.run.RunConfigGenerator
import org.ecorous.esnesnon.gradle.run.task.RunGameTask import org.ecorous.esnesnon.gradle.run.task.RunGameTask
@ -15,26 +22,27 @@ 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 java.io.PrintStream import java.io.PrintStream
import java.net.URI import java.net.URI
import java.nio.charset.StandardCharsets
import java.nio.file.FileSystems
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Path import java.nio.file.Path
import java.nio.file.StandardCopyOption import java.nio.file.StandardCopyOption
import kotlin.io.path.createDirectories import kotlin.io.path.*
import kotlin.io.path.deleteExisting
import kotlin.io.path.exists
class NonsenseGradlePlugin : Plugin<Project> { class PhytotelmaPlugin : Plugin<Project> {
private val taskGroup = "frog"
override fun apply(project: Project) { override fun apply(project: Project) {
println("> Applying Nonsense Gradle Plugin") println("> Applying Nonsense Gradle Plugin")
nonsenseCacheDir = project.gradle.gradleUserHomeDir.resolve("caches/nonsense-gradle/").toPath() nonsenseCacheDir = project.gradle.gradleUserHomeDir.resolve("caches/nonsense-gradle/").toPath()
nonsenseCacheDir.createDirectories() nonsenseCacheDir.createDirectories()
project.apply { project.plugins.let {
mapOf("plugin" to "java-library") it.apply("java-library")
mapOf("plugin" to "eclipse") it.apply("eclipse")
mapOf("plugin" to "idea") it.apply("idea")
mapOf("plugin" to "maven-publish")
} }
project.repositories.apply { project.repositories.apply {
@ -58,11 +66,16 @@ class NonsenseGradlePlugin : Plugin<Project> {
project.repositories.mavenCentral() project.repositories.mavenCentral()
project.task("genSources").apply { project.task("genSources").apply {
group = "nonsense" group = taskGroup
doFirst { doFirst {
val fileName = remappedGameJarPath.fileName.toString() val fileName = remappedGameJarPath.fileName.toString()
val output = val output =
remappedGameJarPath.resolveSibling(fileName.substring(0, fileName.length - 13) + "-sources.jar") remappedGameJarPath.resolveSibling(
fileName.substring(
0,
fileName.lastIndexOf("-")
) + "-sources.jar"
)
if (output.exists()) { if (output.exists()) {
println("Output $output already exists, deleting!") println("Output $output already exists, deleting!")
output.deleteExisting() output.deleteExisting()
@ -101,7 +114,7 @@ class NonsenseGradlePlugin : Plugin<Project> {
} }
project.task("genRunConfigs").apply { project.task("genRunConfigs").apply {
group = "nonsense" group = taskGroup
doFirst { doFirst {
RunConfigGenerator.generate(project) RunConfigGenerator.generate(project)
} }
@ -111,28 +124,61 @@ class NonsenseGradlePlugin : Plugin<Project> {
project.tasks.register("runServer", RunGameTask::class.java, Env.SERVER) project.tasks.register("runServer", RunGameTask::class.java, Env.SERVER)
project.task("downloadAssets").apply { project.task("downloadAssets").apply {
group = "nonsense" group = taskGroup
doFirst { doFirst {
AssetDownloader.download() AssetDownloader.download()
} }
} }
project.tasks.getByName("jar").actions.addLast { val includeConfiguration = project.configurations.register("include") {
it.outputs.files.forEach {file -> it.isCanBeResolved = true
val output = file.toPath().parent.resolveSibling("frog") it.isCanBeConsumed = false
output.createDirectories() }
if (file.name.endsWith(".jar") && !(file.name.contains("-dev.") || file.name.contains("-sources."))){
Files.copy(file.toPath(), output.resolve(file.name.substring(0, file.name.length-4)+".frogmod"), StandardCopyOption.REPLACE_EXISTING) project.tasks.getByName("jar") { task ->
task.outputs.upToDateWhen { false }
task.actions.addLast { _ ->
task.outputs.files.forEach { file ->
val output = file.toPath().parent.resolveSibling("frog")
output.createDirectories()
if (file.name.endsWith(".jar") && !(file.name.contains("-dev.") || file.name.contains("-sources."))) {
val outFile = output.resolve(file.name.substring(0, file.name.length - 4) + ".frogmod")
Files.copy(file.toPath(), outFile, StandardCopyOption.REPLACE_EXISTING)
FileSystems.newFileSystem(outFile).use { fs ->
if (includeConfiguration.isPresent) {
val jijPath = fs.getPath("META-INF/jars")
val files = Nester.run(includeConfiguration.get(), jijPath).map { it.toml() }.toList()
if (files.isNotEmpty()) {
val manifest = fs.getPath("frog.mod.toml")
val config: CommentedConfig =
TomlParser().parse(manifest, FileNotFoundAction.THROW_ERROR)
if (!config.add("frog.extensions.included_jars", listOf(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 lines = manifest.readLines().filter { it.isNotBlank() }
.plus("""
Built-By: Phytotelma ${this.javaClass.`package`.implementationVersion}
Target-Namespace: Mojmap
Built-For: Minecraft $minecraftVersion
""".trimIndent()
)
manifest.writeLines(lines, StandardCharsets.UTF_8)
}
println("Built mod to ${outFile.toUri()}")
}
} }
} }
} }
val includeConfiguration = project.configurations.create("include").apply { project.tasks.register("updateAccesswidener") {
extendsFrom(project.configurations.getByName("implementation")) it.group = taskGroup
} it.actions.addFirst {
AccessWidener.apply(project, remappedGameJarPath)
project.tasks.getByName("jar") { }
it.outputs.files(includeConfiguration.artifacts.files)
} }
} }

View file

@ -17,7 +17,7 @@ object VersionChecker {
fetchVersionData(version) fetchVersionData(version)
val clientData = fetchClientDownload(version) val clientData = fetchClientDownload(version)
// download client data // download client data
val downloadDirectory = NonsenseGradlePlugin.nonsenseCacheDir.resolve("net/minecraft/client/$version/") val downloadDirectory = PhytotelmaPlugin.nonsenseCacheDir.resolve("net/minecraft/client/$version/")
println("Directory: "+downloadDirectory.absolutePathString()) println("Directory: "+downloadDirectory.absolutePathString())
val downloadFile = downloadDirectory.resolve("client-$version.jar") val downloadFile = downloadDirectory.resolve("client-$version.jar")
if (downloadFile.exists()) { if (downloadFile.exists()) {

View file

@ -0,0 +1,296 @@
package org.ecorous.esnesnon.gradle.accesswidener
import com.electronwill.nightconfig.core.file.FileNotFoundAction
import com.electronwill.nightconfig.toml.TomlParser
import com.google.common.hash.Hashing
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginExtension
import org.objectweb.asm.*
import java.nio.charset.StandardCharsets
import java.nio.file.FileSystems
import java.nio.file.Path
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.ConcurrentSkipListSet
import java.util.regex.Pattern
import java.util.stream.Stream
import kotlin.io.path.bufferedReader
import kotlin.io.path.exists
import kotlin.io.path.readBytes
import kotlin.io.path.writeBytes
object AccessWidener {
private val PARSER = TomlParser()
private var awHash = ""
private val HEADER = Pattern.compile("accessWidener\\s+v[12]\\s+.*").asMatchPredicate()
private val SEPARATOR = "[\\t ]+".toRegex()
private fun getAWFile(project: Project): Path? {
project.extensions.getByType(JavaPluginExtension::class.java).sourceSets.forEach { set ->
set.resources.filter { it.name == "frog.mod.toml" }.firstOrNull {
if (it == null) {
println("Please make sure a 'frog.mod.toml' file is present in the mod's resources!")
return null
}
return PARSER.parse(it, FileNotFoundAction.READ_NOTHING)
.get<String>("frog.extensions.frog_aw")?.let { name ->
return it.resolveSibling(name).toPath()
}
}
}
println("Please make sure a 'frog.mod.toml' file is present in the mod's resources!")
return null
}
private fun findDependencyAWs(project: Project): List<Path> {
return project.configurations.getByName("runtimeClasspath").resolvedConfiguration.firstLevelModuleDependencies.flatMap { dep ->
dep.moduleArtifacts
}.map { it.file }.map { it.toPath() }.toList()
}
fun needsUpdate(project: Project): Boolean {
getAWFile(project)?.let {
val hash = Hashing.sha256().hashBytes(it.readBytes()).toString()
if (hash != awHash) {
awHash = hash
return true
}
}
return false
}
private fun readAW(project: Project): Stream<String> {
val awFile = getAWFile(project)
println("Found accesswidener in project: $awFile")
return awFile?.bufferedReader(StandardCharsets.UTF_8)?.lines()?.map { it.replace("transitive-", "") }
?: Stream.empty()
}
private fun readTransitiveAW(path: Path): Stream<String> {
return path.bufferedReader(StandardCharsets.UTF_8).lines().filter { it.startsWith("transitive-") }
}
private fun readAllAWs(project: Project): Stream<Entry> {
return Stream.concat(
findDependencyAWs(project).stream().flatMap { i -> readTransitiveAW(i) },
readAW(project)
)
.filter { it.isNotBlank() }
.map { if (it.contains("#")) it.split("#")[0] else it }.filter { !HEADER.test(it) }
.filter { it.isNotBlank() }
.unordered().distinct()
.map { it.split(SEPARATOR) }.filter { it.isNotEmpty() }.map { Entry(it) }
}
fun apply(project: Project, input: Path) {
val startTime = System.currentTimeMillis()
val classNames: MutableSet<String> = ConcurrentSkipListSet()
val classMap: MutableMap<String, Entry> = ConcurrentHashMap()
val methods: MutableMap<String, MutableMap<String, Entry>> = ConcurrentHashMap()
val fields: MutableMap<String, MutableMap<String, Entry>> = ConcurrentHashMap()
val mutations: MutableMap<String, MutableMap<String, Entry>> = ConcurrentHashMap()
val entries = readAllAWs(project)
entries.forEach { e ->
classNames.add(e.className)
if ("class" == e.targetType) {
if (e.type == AccessType.MUTABLE) {
throw IllegalArgumentException("aw format error: classes can not have a 'mutable' modifier (at: $e)")
}
if (!classMap.containsKey(e.className)) {
classMap[e.className] = e
} else {
val other = classMap[e.className]!!
if (e.isAccessGreaterThan(other)) {
classMap[e.className] = e
}
}
} else if ("method" == e.targetType) {
if (e.type == AccessType.MUTABLE) {
throw IllegalArgumentException("aw format error: methods can not have a 'mutable' modifier (at: $e)")
}
val map = methods.computeIfAbsent(e.className) { ConcurrentHashMap() }
val id = e.name + e.descriptor
if (!map.containsKey(id)) {
map[id] = e
} else {
val other = map[id]!!
if (e.isAccessGreaterThan(other)) {
classMap[id] = e
}
}
} else if ("field" == e.targetType) {
if (e.type == AccessType.EXTENDABLE) {
throw IllegalArgumentException("aw format error: fields can not have a 'extendable' modifier (at: $e)")
}
val map = fields.computeIfAbsent(e.className) { ConcurrentHashMap() }
val id = e.name + e.descriptor
if (e.type == AccessType.MUTABLE) {
mutations.computeIfAbsent(e.className) { ConcurrentHashMap() }.putIfAbsent(id, e)
} else {
if (!map.containsKey(id)) {
map[id] = e
} else {
val other = map[id]!!
if (e.isAccessGreaterThan(other)) {
classMap[id] = e
}
}
}
}
}
FileSystems.newFileSystem(input).use {
classNames.forEach { className ->
val file = it.getPath("/$className.class")
if (file.exists()) {
val reader = ClassReader(file.readBytes())
val writer = ClassWriter(0)
val mapper = AWClassVisitor(writer, classMap, fields, methods, mutations, className)
reader.accept(mapper, 0)
file.writeBytes(writer.toByteArray())
}
}
}
println(
"Applied AccessWideners (${
"%.2fs".format(
(System.currentTimeMillis() - startTime) / 1000f
)
})"
)
}
}
data class Entry(
val type: AccessType,
val targetType: String,
val className: String,
val name: String,
val descriptor: String
) {
constructor(line: List<String>) : this(
AccessType.of(line[0]),
line[1],
line[2],
line[3],
line[4]
)
fun isAccessGreaterThan(other: Entry): Boolean {
return Access.of(type.access).index < Access.of(other.type.access).index
}
}
enum class AccessType(val id: String, val access: Int) {
ACCESSIBLE("accessible", Opcodes.ACC_PUBLIC),
EXTENDABLE("extendable", Opcodes.ACC_PROTECTED),
MUTABLE("mutable", Opcodes.ACC_FINAL.inv());
companion object {
fun of(name: String): AccessType {
return entries.firstOrNull { a -> a.id == name }
?: throw IllegalArgumentException("Access type '$name' is not known. Known types: ${entries.map { it.id }} (and transitive variants)")
}
}
}
private enum class Access(val index: Int) {
PUBLIC(1), PROTECTED(2), PACKAGE_PRIVATE(3), PRIVATE(4);
companion object {
fun of(access: Int): Access {
if ((access and Opcodes.ACC_PUBLIC) != 0) {
return PUBLIC
}
if ((access and Opcodes.ACC_PROTECTED) != 0) {
return PROTECTED
}
if ((access and Opcodes.ACC_PRIVATE) != 0) {
return PRIVATE
}
return PACKAGE_PRIVATE
}
}
}
class AWClassVisitor(
visitor: ClassVisitor,
private val classMap: Map<String, Entry>,
private val fields: Map<String, Map<String, Entry>>,
private val methods: Map<String, Map<String, Entry>>,
private val mutations: Map<String, Map<String, Entry>>,
private val className: String
) : ClassVisitor(Opcodes.ASM9, visitor) {
override fun visit(
version: Int,
acc: Int,
name: String?,
signature: String?,
superName: String?,
interfaces: Array<out String>?
) {
var access = acc
val e = classMap[className]
if (e != null) {
access = access and (Opcodes.ACC_PRIVATE or Opcodes.ACC_PROTECTED or Opcodes.ACC_PUBLIC).inv()
access = access or e.type.access
}
if (fields.containsKey(className) || methods.containsKey(className) || mutations.containsKey(
className
)
) { // make all classes with modifications public as well
access = access and (Opcodes.ACC_PRIVATE or Opcodes.ACC_PROTECTED or Opcodes.ACC_PUBLIC).inv()
access = access or Opcodes.ACC_PUBLIC
}
super.visit(version, access, name, signature, superName, interfaces)
}
override fun visitField(
acc: Int,
name: String?,
descriptor: String?,
signature: String?,
value: Any?
): FieldVisitor {
var access = acc
var map = fields[className]
if (map != null) {
val e = map[name + descriptor]
if (e != null) {
access =
access and (Opcodes.ACC_PRIVATE or Opcodes.ACC_PROTECTED or Opcodes.ACC_PUBLIC).inv() // remove all access modifiers
access = access or e.type.access // re-add the new one
}
}
map = mutations[className]
if (map != null) {
val e = map[name + descriptor]
if (e != null) {
access = access and Opcodes.ACC_FINAL.inv() // always AccessType.MUTABLE
}
}
return super.visitField(access, name, descriptor, signature, value)
}
override fun visitMethod(
acc: Int,
name: String?,
descriptor: String?,
signature: String?,
exceptions: Array<out String>?
): MethodVisitor {
var access = acc
val map = methods[className]
if (map != null) {
val e = map[name + descriptor]
if (e != null) {
access =
access and ((Opcodes.ACC_PRIVATE or Opcodes.ACC_PROTECTED or Opcodes.ACC_PUBLIC).inv())// remove all access modifiers
access = access or (e.type.access)// re-add the new one
}
}
return super.visitMethod(access, name, descriptor, signature, exceptions)
}
}

View file

@ -1,6 +1,6 @@
package org.ecorous.esnesnon.gradle.common package org.ecorous.esnesnon.gradle.common
import org.ecorous.esnesnon.gradle.NonsenseGradlePlugin import org.ecorous.esnesnon.gradle.PhytotelmaPlugin
import java.io.InputStream import java.io.InputStream
import java.net.URI import java.net.URI
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
@ -39,7 +39,7 @@ object CachingHttpClient {
} }
private fun getCacheFile(uri: URI): Path { private fun getCacheFile(uri: URI): Path {
val path = NonsenseGradlePlugin.nonsenseCacheDir.resolve("httpCache") val path = PhytotelmaPlugin.nonsenseCacheDir.resolve("httpCache")
.resolve(uri.host+uri.rawPath) // Use rawPath to ensure ASCII compat (since this will be a filesystem dir) .resolve(uri.host+uri.rawPath) // Use rawPath to ensure ASCII compat (since this will be a filesystem dir)
path.createParentDirectories() path.createParentDirectories()
return path return path

View file

@ -1,43 +1,63 @@
package org.ecorous.esnesnon.gradle.ext package org.ecorous.esnesnon.gradle.ext
import org.ecorous.esnesnon.gradle.NonsenseGradlePlugin import org.ecorous.esnesnon.gradle.PhytotelmaPlugin
import org.ecorous.esnesnon.gradle.VersionChecker import org.ecorous.esnesnon.gradle.VersionChecker
import org.ecorous.esnesnon.gradle.accesswidener.AccessWidener
import org.ecorous.esnesnon.gradle.run.RunConfigGenerator import org.ecorous.esnesnon.gradle.run.RunConfigGenerator
import org.ecorous.esnesnon.nonsense_remapper.NonsenseRemapper import org.ecorous.esnesnon.nonsense_remapper.Thyroxine
import org.ecorous.esnesnon.nonsense_remapper.parser.ProguardParser import org.ecorous.esnesnon.nonsense_remapper.parser.ProguardParser
import org.ecorous.esnesnon.nonsense_remapper.provider.MojmapProvider import org.ecorous.esnesnon.nonsense_remapper.provider.MojmapProvider
import org.ecorous.esnesnon.nonsense_remapper.provider.ParchmentProvider import org.ecorous.esnesnon.nonsense_remapper.provider.ParchmentProvider
import org.gradle.api.Project import org.gradle.api.Project
import org.gradle.api.provider.Provider
import kotlin.io.path.notExists import kotlin.io.path.notExists
fun Project.minecraft( fun Project.minecraft(
version: String, version: String,
parchmentVersion: String = ParchmentProvider.findForMinecraftVersion(version) parchmentVersion: String? = null
): Project { // return self to allow for chaining ): Project { // return self to allow for chaining
if (VersionChecker.validateVersion(version)) { if (VersionChecker.validateVersion(version)) {
NonsenseGradlePlugin.minecraftVersion = version println("Setting up Minecraft...")
NonsenseGradlePlugin.parchmentVersion = parchmentVersion val parchment = parchmentVersion ?: kotlin.runCatching { ParchmentProvider.findForMinecraftVersion(version) }
.getOrDefault("")
PhytotelmaPlugin.minecraftVersion = version
PhytotelmaPlugin.parchmentVersion = parchment
println("Valid version! $version") println("Valid version! $version")
val clientData = VersionChecker.fetchClientDownload(version)
println("Client data: ${clientData.url}")
// download client data
println("Downloading client...")
val clientJar = VersionChecker.downloadClient(version) val clientJar = VersionChecker.downloadClient(version)
println("Downloaded client!")
val remappedJar = clientJar.resolveSibling("client-$version-remapped.jar") val remappedJar = clientJar.resolveSibling("client-$version-remapped.jar")
NonsenseGradlePlugin.remappedGameJarPath = remappedJar PhytotelmaPlugin.remappedGameJarPath = remappedJar
println("Time to setup Minecraft!") println("Time to setup Minecraft!")
if (remappedJar.notExists()) { val applyAW = AccessWidener.needsUpdate(this)
if (remappedJar.notExists() || applyAW) {
println("Remapping the game...") println("Remapping the game...")
val data = ProguardParser.read(MojmapProvider.get(version, clientJar.resolveSibling("client-$version.txt")).orElseThrow()).reverse() val data = kotlin.runCatching {
val paramMappings = ParchmentProvider.getParchment( ProguardParser.read(
version, parchmentVersion, MojmapProvider.get(
NonsenseGradlePlugin.nonsenseCacheDir.resolve("org/parchmentmc/parchment/$version/$parchmentVersion") version,
) clientJar.resolveSibling("client-$version.txt")
NonsenseRemapper.remap(data, clientJar, remappedJar, true, paramMappings) ).orElseThrow()
).reverse()
}.getOrNull()
val paramMappings = if (parchment.isNotEmpty()) kotlin.runCatching {
ParchmentProvider.getParchment(
version, parchment,
PhytotelmaPlugin.nonsenseCacheDir.resolve("org/parchmentmc/parchment/$version/$parchment")
)
}.getOrNull() else {
println("Parameter mappings will not be present as no parchment version was found. It may be possible to declare it manually.")
null
}
if (paramMappings == null && parchment.isNotEmpty()) {
println("Parameter mappings will not be present as the version $parchmentVersion for minecraft version $version could not be found")
}
if (data != null) {
Thyroxine.remap(data, clientJar, remappedJar, true, paramMappings)
} else {
error("Failed to remap the game as no mojmap version was found for game version $version. Other mapping formats may be implemented in the future.")
}
} }
println("Adding dependencies...") println("Adding dependencies...")
dependencies.add("implementation","net.minecrell:terminalconsoleappender:1.2.0") dependencies.add("implementation", "net.minecrell:terminalconsoleappender:1.2.0")
VersionChecker.getDependencies(version) { VersionChecker.getDependencies(version) {
dependencies.add("implementation", it) dependencies.add("implementation", it)
} }
@ -45,6 +65,14 @@ fun Project.minecraft(
println("Generating run configurations...") println("Generating run configurations...")
RunConfigGenerator.generate(this) RunConfigGenerator.generate(this)
if (applyAW) {
project.afterEvaluate {
println("Applying AccessWideners..")
AccessWidener.apply(this, remappedJar)
}
}
println("Done!") println("Done!")
} else { } else {
println("Invalid version! $version") println("Invalid version! $version")
@ -53,6 +81,17 @@ fun Project.minecraft(
return this return this
} }
fun Project.loader(version: String){ fun Project.minecraft(
dependencies.add("implementation", "org.ecorous.esnesnon:nonsense-loader:$version") version: Provider<String>,
parchmentVersion: Provider<String> = provider { null }
): Project {
return minecraft(version.get(), parchmentVersion.orNull)
}
fun Project.loader(version: String) {
dependencies.add("implementation", "org.ecorous.esnesnon:frogloader:$version")
}
fun Project.loader(version: Provider<String>) {
return loader(version.get())
} }

View file

@ -0,0 +1,132 @@
package org.ecorous.esnesnon.gradle.nest
import com.electronwill.nightconfig.core.Config
import com.electronwill.nightconfig.core.UnmodifiableConfig
import com.google.common.jimfs.Jimfs
import org.gradle.api.artifacts.Configuration
import org.gradle.api.artifacts.ProjectDependency
import org.gradle.api.plugins.JavaPlugin
import org.gradle.api.tasks.bundling.AbstractArchiveTask
import java.nio.file.FileSystem
import java.nio.file.FileSystems
import java.nio.file.Files
import java.nio.file.Path
import java.util.*
import kotlin.io.path.*
object Nester {
fun run(configuration: Configuration, path: Path): Collection<NestedJar> {
val files = mutableListOf<NestedJar>()
Jimfs.newFileSystem(com.google.common.jimfs.Configuration.unix()).use { memFs ->
configuration.dependencies.filterIsInstance<ProjectDependency>().forEach { dependency ->
val project = dependency.dependencyProject
project.getTasksByName(JavaPlugin.JAR_TASK_NAME, false).map { t -> t as AbstractArchiveTask }
.forEach { task ->
val location = task.archiveFile.map {
addMetadata(
it.asFile.toPath(),
dependency.group!!,
dependency.name,
dependency.version!!,
task.archiveClassifier.orNull,
memFs
)
}.get()
path.createDirectories()
val target = path.resolve(location.fileName.toString())
Files.copy(location, target)
files.add(NestedJar(("${dependency.group}_${dependency.name}${if (task.archiveClassifier.isPresent) "_${task.archiveClassifier}" else ""}").replace(
"\\.".toRegex(),
"_"
).lowercase(Locale.ROOT), target.absolute().toString()))
}
}
configuration.resolvedConfiguration.firstLevelModuleDependencies.forEach { dep ->
dep.moduleArtifacts.forEach { artifact ->
val location = addMetadata(
artifact.file.toPath(),
dep.moduleGroup,
dep.moduleName,
dep.moduleVersion,
artifact.classifier,
memFs
)
path.createDirectories()
val target = path.resolve(location.fileName.toString())
Files.copy(location, target)
files.add(NestedJar(("${dep.moduleGroup}_${dep.moduleName}${if (artifact.classifier != null) "_${artifact.classifier}" else ""}").replace(
"\\.".toRegex(),
"_"
).lowercase(Locale.ROOT), target.absolute().toString()))
}
}
}
return files
}
private fun addMetadata(
input: Path,
group: String,
name: String,
version: String,
classifier: String?,
memFs: FileSystem
): Path {
FileSystems.newFileSystem(input).use { original ->
val toml = original.getPath("frog.mod.toml")
if (toml.notExists()) {
val tempFile = memFs.getPath(
"${
group.replace(
".",
"/"
)
}/${name}/${version}/${input.name}/${group.replace(".", "_")}_${input.name}"
)
if (tempFile.exists()) {
FileSystems.newFileSystem(tempFile).use {
if (it.getPath("frog.mod.toml").exists()){
return tempFile
}
}
}
tempFile.createParentDirectories()
input.copyTo(tempFile)
FileSystems.newFileSystem(tempFile).use {
it.getPath("frog.mod.toml").writeText(
"""
[frog]
manifest_version = "1.0.0"
[frog.mod]
id = "${
("${group}_$name${if (classifier != null) "_$classifier" else ""}").replace(
"\\.".toRegex(),
"_"
).lowercase(Locale.ROOT)
}"
name = "$name"
version = "${version.replace("(\\d+\\.\\d+\\.\\d+)(.*)".toRegex(), "$1+$2")}"
license = ""
[frog.extensions]
phytotelma.generated = true
""".trimIndent()
)
}
return tempFile
}
return input
}
}
}
class NestedJar(val id: String, val path: String){
fun toml(): UnmodifiableConfig {
val config = Config.inMemory()
config.add("id", id)
config.add("path", path)
return config
}
}

View file

@ -2,7 +2,7 @@ package org.ecorous.esnesnon.gradle.run
import com.google.gson.Gson import com.google.gson.Gson
import com.google.gson.JsonObject import com.google.gson.JsonObject
import org.ecorous.esnesnon.gradle.NonsenseGradlePlugin import org.ecorous.esnesnon.gradle.PhytotelmaPlugin
import org.ecorous.esnesnon.gradle.VersionChecker import org.ecorous.esnesnon.gradle.VersionChecker
import org.ecorous.esnesnon.gradle.common.CachingHttpClient import org.ecorous.esnesnon.gradle.common.CachingHttpClient
import java.net.URI import java.net.URI
@ -15,8 +15,8 @@ object AssetDownloader {
private const val ASSETS_URL = "https://resources.download.minecraft.net" private const val ASSETS_URL = "https://resources.download.minecraft.net"
fun download() { fun download() {
val version = NonsenseGradlePlugin.minecraftVersion val version = PhytotelmaPlugin.minecraftVersion
val path = NonsenseGradlePlugin.nonsenseCacheDir.resolve("assets") val path = PhytotelmaPlugin.nonsenseCacheDir.resolve("assets")
val id = VersionChecker.downloadAssetIndex(version, path.resolve("indexes")) val id = VersionChecker.downloadAssetIndex(version, path.resolve("indexes"))
val index = Gson().fromJson( val index = Gson().fromJson(

View file

@ -1,6 +1,6 @@
package org.ecorous.esnesnon.gradle.run package org.ecorous.esnesnon.gradle.run
import org.ecorous.esnesnon.gradle.NonsenseGradlePlugin import org.ecorous.esnesnon.gradle.PhytotelmaPlugin
import org.ecorous.esnesnon.gradle.VersionChecker import org.ecorous.esnesnon.gradle.VersionChecker
import org.ecorous.esnesnon.gradle.common.Env import org.ecorous.esnesnon.gradle.common.Env
import org.ecorous.esnesnon.gradle.run.adapter.EclipseAdapter import org.ecorous.esnesnon.gradle.run.adapter.EclipseAdapter
@ -27,15 +27,12 @@ object RunConfigGenerator {
} }
} }
val assetPath = NonsenseGradlePlugin.nonsenseCacheDir.resolve(ASSET_DIR).absolute() val assetPath = PhytotelmaPlugin.nonsenseCacheDir.resolve(ASSET_DIR).absolute()
val assetIndexPath = assetPath.resolve("indexes") val assetIndexPath = assetPath.resolve("indexes")
if (assetIndexPath.notExists()) { if (assetIndexPath.notExists()) {
assetIndexPath.createDirectories() assetIndexPath.createDirectories()
} }
val indexId = VersionChecker.downloadAssetIndex(NonsenseGradlePlugin.minecraftVersion, assetIndexPath) val indexId = VersionChecker.downloadAssetIndex(PhytotelmaPlugin.minecraftVersion, assetIndexPath)
val runConfigPath = project.rootDir.resolve(".idea").resolve("runConfigurations")
runConfigPath.mkdirs()
for (adapter in ADAPTERS) { for (adapter in ADAPTERS) {
if (!adapter.shouldGenerate()) if (!adapter.shouldGenerate())
@ -49,7 +46,7 @@ object RunConfigGenerator {
mutableListOf( mutableListOf(
"-Xmx${project.properties.getOrDefault("nonsense.gameHeap", "2048M")}", "-Xmx${project.properties.getOrDefault("nonsense.gameHeap", "2048M")}",
"-Dnonsense.development=true", "-Dnonsense.development=true",
"-Dnonsense.plugin.minecraft.gameJar=${NonsenseGradlePlugin.remappedGameJarPath}", "-Dnonsense.plugin.minecraft.gameJar=${PhytotelmaPlugin.remappedGameJarPath}",
"-Dlog4j2.configurationFile=$log4jPath", "-Dlog4j2.configurationFile=$log4jPath",
"-Dlog4j2.formatMsgLookups=true" "-Dlog4j2.formatMsgLookups=true"
).apply { ).apply {

View file

@ -1,6 +1,6 @@
package org.ecorous.esnesnon.gradle.run.task package org.ecorous.esnesnon.gradle.run.task
import org.ecorous.esnesnon.gradle.NonsenseGradlePlugin import org.ecorous.esnesnon.gradle.PhytotelmaPlugin
import org.ecorous.esnesnon.gradle.VersionChecker import org.ecorous.esnesnon.gradle.VersionChecker
import org.ecorous.esnesnon.gradle.common.Env import org.ecorous.esnesnon.gradle.common.Env
import org.gradle.api.file.ConfigurableFileCollection import org.gradle.api.file.ConfigurableFileCollection
@ -21,13 +21,13 @@ abstract class RunGameTask @Inject constructor(env: Env) : JavaExec() {
private val classpath: ConfigurableFileCollection = project.objects.fileCollection() private val classpath: ConfigurableFileCollection = project.objects.fileCollection()
init { init {
group = "nonsense" group = "frog"
val assetPath = NonsenseGradlePlugin.nonsenseCacheDir.resolve("assets").absolute() val assetPath = PhytotelmaPlugin.nonsenseCacheDir.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.downloadAssetIndex(NonsenseGradlePlugin.minecraftVersion, assetIndexPath) val indexId = VersionChecker.downloadAssetIndex(PhytotelmaPlugin.minecraftVersion, assetIndexPath)
val log4jPath = project.rootDir.resolve(".gradle/nonsense-gradle/log4j.xml").toPath().absolute() val log4jPath = project.rootDir.resolve(".gradle/nonsense-gradle/log4j.xml").toPath().absolute()
if (log4jPath.notExists()) { if (log4jPath.notExists()) {
log4jPath.createParentDirectories() log4jPath.createParentDirectories()
@ -55,7 +55,7 @@ abstract class RunGameTask @Inject constructor(env: Env) : JavaExec() {
jvmArguments.addAll( jvmArguments.addAll(
"-Xmx${project.properties.getOrDefault("nonsense.gameHeap", "2048M")}", "-Xmx${project.properties.getOrDefault("nonsense.gameHeap", "2048M")}",
"-Dnonsense.development=true", "-Dnonsense.development=true",
"-Dnonsense.plugin.minecraft.gameJar=${NonsenseGradlePlugin.remappedGameJarPath}", "-Dnonsense.plugin.minecraft.gameJar=${PhytotelmaPlugin.remappedGameJarPath}",
"-Dlog4j2.configurationFile=$log4jPath", "-Dlog4j2.configurationFile=$log4jPath",
"-Dlog4j2.formatMsgNoLookups=true", "-Dlog4j2.formatMsgNoLookups=true",
writeArgFile() writeArgFile()