Compare commits
18 commits
17c4cf3a56
...
461688d5f6
Author | SHA1 | Date | |
---|---|---|---|
moehreag | 461688d5f6 | ||
moehreag | 35afc6a296 | ||
moehreag | 333d5889b7 | ||
moehreag | 3e21b4d8d5 | ||
moehreag | 999dad62bc | ||
moehreag | 5353fe7d60 | ||
moehreag | d68aae3066 | ||
owlsys | 709f5fbbe7 | ||
moehreag | 3f4c38d821 | ||
moehreag | b90330917c | ||
moehreag | 0234a3b49a | ||
moehreag | 7dc23b1279 | ||
moehreag | 67c253f2f0 | ||
moehreag | 80a9fc3fdc | ||
moehreag | 5f8c1538c3 | ||
moehreag | 056350e1dd | ||
moehreag | 4094397909 | ||
moehreag | 61cfc68ddd |
|
@ -18,6 +18,6 @@ jobs:
|
|||
- name: Build
|
||||
run: |
|
||||
chmod +x ./gradlew
|
||||
./gradlew publishAllPublicationsToEsnesnonSnapshotsMavenRepository \
|
||||
-PEsnesnonSnapshotsMavenUsername=${{ secrets.MAVEN_PUSH_USER }} \
|
||||
-PEsnesnonSnapshotsMavenPassword=${{ secrets.MAVEN_PUSH_TOKEN }}
|
||||
./gradlew publishAllPublicationsToFrogMCSnapshotsMavenRepository \
|
||||
-PFrogMCSnapshotsMavenUsername=${{ secrets.MAVEN_PUSH_USER }} \
|
||||
-PFrogMCSnapshotsMavenPassword=${{ secrets.MAVEN_PUSH_TOKEN }}
|
||||
|
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -37,3 +37,4 @@ bin/
|
|||
|
||||
### Mac OS ###
|
||||
.DS_Store
|
||||
.kotlin/
|
||||
|
|
|
@ -1,41 +1,50 @@
|
|||
plugins {
|
||||
kotlin("jvm") version "1.9.23"
|
||||
kotlin("jvm") version "2.0.0"
|
||||
`java-gradle-plugin`
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
group = "org.ecorous.esnesnon"
|
||||
group = "dev.frogmc"
|
||||
version = "0.0.1-SNAPSHOT"
|
||||
|
||||
repositories {
|
||||
maven {
|
||||
name = "Esnesnos Maven Releases"
|
||||
url = uri("https://maven-esnesnon.ecorous.org/releases")
|
||||
name = "FrogMC Maven Releases"
|
||||
url = uri("https://maven.frogmc.dev/releases")
|
||||
}
|
||||
maven {
|
||||
name = "Esnesnos Maven Snapshots"
|
||||
url = uri("https://maven-esnesnon.ecorous.org/snapshots")
|
||||
name = "FrogMC Maven Snapshots"
|
||||
url = uri("https://maven.frogmc.dev/snapshots")
|
||||
}
|
||||
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.ecorous.esnesnon:nonsense-remapper:1.0.0-SNAPSHOT")
|
||||
implementation("org.ecorous.frogmc:thyroxine:1.0.0-SNAPSHOT")
|
||||
implementation("org.ow2.asm:asm:9.7")
|
||||
implementation("com.google.code.gson:gson:2.10.1")
|
||||
implementation("org.vineflower:vineflower:1.10.1")
|
||||
testImplementation(kotlin("test"))
|
||||
implementation("com.electronwill.night-config:toml:3.7.2")
|
||||
implementation("com.google.jimfs:jimfs:1.3.0")
|
||||
}
|
||||
|
||||
gradlePlugin {
|
||||
plugins {
|
||||
create("nonsense-gradle") {
|
||||
id = "org.ecorous.esnesnon.nonsense-gradle"
|
||||
implementationClass = "org.ecorous.esnesnon.gradle.NonsenseGradlePlugin"
|
||||
create("phytotelma") {
|
||||
id = "dev.frogmc.phytotelma"
|
||||
implementationClass = "dev.frogmc.phytotelma.PhytotelmaPlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.jar {
|
||||
manifest {
|
||||
attributes["Implementation-Version"] = version
|
||||
}
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
@ -50,8 +59,8 @@ publishing {
|
|||
|
||||
repositories {
|
||||
maven {
|
||||
name = "EsnesnonSnapshotsMaven"
|
||||
url = uri("https://maven-esnesnon.ecorous.org/snapshots")
|
||||
name = "FrogMCSnapshotsMaven"
|
||||
url = uri("https://maven.frogmc.dev/snapshots")
|
||||
credentials(PasswordCredentials::class)
|
||||
authentication {
|
||||
create<BasicAuthentication>("basic")
|
||||
|
@ -59,8 +68,8 @@ publishing {
|
|||
}
|
||||
|
||||
maven {
|
||||
name = "EsnesnonReleasesMaven"
|
||||
url = uri("https://maven-esnesnon.ecorous.org/releases")
|
||||
name = "FrogMCReleasesMaven"
|
||||
url = uri("https://maven.frogmc.dev/releases")
|
||||
credentials(PasswordCredentials::class)
|
||||
authentication {
|
||||
create<BasicAuthentication>("basic")
|
||||
|
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,6 @@
|
|||
#Sun May 12 17:35:40 BST 2024
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
plugins {
|
||||
id("org.gradle.toolchains.foojay-resolver-convention") version "0.8.0"
|
||||
}
|
||||
rootProject.name = "nonsense-gradle"
|
||||
rootProject.name = "phytotelma"
|
||||
|
||||
|
|
191
src/main/kotlin/dev/frogmc/phytotelma/PhytotelmaPlugin.kt
Normal file
191
src/main/kotlin/dev/frogmc/phytotelma/PhytotelmaPlugin.kt
Normal file
|
@ -0,0 +1,191 @@
|
|||
package dev.frogmc.phytotelma
|
||||
|
||||
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 dev.frogmc.phytotelma.accesswidener.AccessWidener
|
||||
import dev.frogmc.phytotelma.common.Env
|
||||
import dev.frogmc.phytotelma.nest.Nester
|
||||
import dev.frogmc.phytotelma.run.AssetDownloader
|
||||
import dev.frogmc.phytotelma.run.RunConfigGenerator
|
||||
import dev.frogmc.phytotelma.run.task.RunGameTask
|
||||
import dev.frogmc.phytotelma.vineflower.ParchmentJavadocProvider
|
||||
import org.ecorous.frogmc.thyroxine.provider.ParchmentProvider
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.jetbrains.java.decompiler.main.Fernflower
|
||||
import org.jetbrains.java.decompiler.main.decompiler.PrintStreamLogger
|
||||
import org.jetbrains.java.decompiler.main.decompiler.SingleFileSaver
|
||||
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences
|
||||
import java.io.PrintStream
|
||||
import java.net.URI
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.FileSystems
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
import java.nio.file.StandardCopyOption
|
||||
import kotlin.io.path.*
|
||||
|
||||
|
||||
class PhytotelmaPlugin : Plugin<Project> {
|
||||
|
||||
private val taskGroup = "frogmc"
|
||||
|
||||
override fun apply(project: Project) {
|
||||
println("> Applying FrogMC Gradle Plugin")
|
||||
globalCacheDir = project.gradle.gradleUserHomeDir.resolve("caches/phytotelma/").toPath()
|
||||
globalCacheDir.createDirectories()
|
||||
|
||||
project.plugins.let {
|
||||
it.apply("java-library")
|
||||
it.apply("eclipse")
|
||||
it.apply("idea")
|
||||
}
|
||||
|
||||
project.repositories.apply {
|
||||
maven {
|
||||
it.name = "FrogMC Releases"
|
||||
it.url = URI.create("https://maven.frogmc.dev/releases")
|
||||
}
|
||||
maven {
|
||||
it.name = "FrogMC Snapshots"
|
||||
it.url = URI.create("https://maven.frogmc.dev/snapshots")
|
||||
}
|
||||
maven {
|
||||
it.name = "Minecraft/Local"
|
||||
it.url = project.gradle.gradleUserHomeDir.resolve("caches/phytotelma/").toURI()
|
||||
}
|
||||
maven {
|
||||
it.name = "Minecraft Libraries"
|
||||
it.url = URI.create("https://libraries.minecraft.net/")
|
||||
}
|
||||
}
|
||||
project.repositories.mavenCentral()
|
||||
|
||||
project.task("genSources").apply {
|
||||
group = taskGroup
|
||||
doFirst {
|
||||
val fileName = remappedGameJarPath.fileName.toString()
|
||||
val output =
|
||||
remappedGameJarPath.resolveSibling(
|
||||
fileName.substring(
|
||||
0,
|
||||
fileName.lastIndexOf("-")
|
||||
) + "-sources.jar"
|
||||
)
|
||||
if (output.exists()) {
|
||||
println("Output $output already exists, deleting!")
|
||||
output.deleteExisting()
|
||||
}
|
||||
val options = mutableMapOf<String, Any>()
|
||||
println("Preparing Parchment...")
|
||||
val parchment = ParchmentProvider.getParchment(
|
||||
minecraftVersion,
|
||||
parchmentVersion,
|
||||
project.gradle.gradleUserHomeDir.resolve("phytotelma/org/parchmentmc/parchment/$minecraftVersion/$parchmentVersion/")
|
||||
.toPath()
|
||||
)
|
||||
options[IFabricJavadocProvider.PROPERTY_NAME] = ParchmentJavadocProvider(parchment)
|
||||
|
||||
println("Decompiling...")
|
||||
val logger = PrintStreamLogger(PrintStream(System.out))
|
||||
options.putAll(
|
||||
mapOf(
|
||||
IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES to "1",
|
||||
IFernflowerPreferences.BYTECODE_SOURCE_MAPPING to "1",
|
||||
IFernflowerPreferences.REMOVE_SYNTHETIC to "1",
|
||||
IFernflowerPreferences.LOG_LEVEL to "error",
|
||||
IFernflowerPreferences.THREADS to Runtime.getRuntime().availableProcessors().toString(),
|
||||
IFernflowerPreferences.INDENT_STRING to "\t"
|
||||
)
|
||||
)
|
||||
|
||||
val decomp = Fernflower(
|
||||
SingleFileSaver(output.toFile()),
|
||||
options, logger
|
||||
)
|
||||
decomp.addSource(remappedGameJarPath.toFile())
|
||||
|
||||
decomp.decompileContext()
|
||||
}
|
||||
}
|
||||
|
||||
project.task("genRunConfigs").apply {
|
||||
group = taskGroup
|
||||
doFirst {
|
||||
RunConfigGenerator.generate(project)
|
||||
}
|
||||
}
|
||||
|
||||
project.tasks.register("runClient", RunGameTask::class.java, Env.CLIENT)
|
||||
project.tasks.register("runServer", RunGameTask::class.java, Env.SERVER)
|
||||
|
||||
project.task("downloadAssets").apply {
|
||||
group = taskGroup
|
||||
doFirst {
|
||||
AssetDownloader.download()
|
||||
}
|
||||
}
|
||||
|
||||
val includeConfiguration = project.configurations.register("include") {
|
||||
it.isCanBeResolved = true
|
||||
it.isCanBeConsumed = false
|
||||
}
|
||||
|
||||
project.tasks.getByName("jar") { task ->
|
||||
task.outputs.upToDateWhen { false }
|
||||
task.actions.addLast { _ ->
|
||||
task.outputs.files.forEach { file ->
|
||||
val output = file.toPath().parent.resolveSibling("frogmc")
|
||||
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()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
project.tasks.register("updateAccesswidener") {
|
||||
it.group = taskGroup
|
||||
it.actions.addFirst {
|
||||
AccessWidener.apply(project, remappedGameJarPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
lateinit var globalCacheDir: Path
|
||||
lateinit var minecraftVersion: String
|
||||
lateinit var remappedGameJarPath: Path
|
||||
lateinit var parchmentVersion: String
|
||||
}
|
||||
}
|
|
@ -1,8 +1,8 @@
|
|||
package org.ecorous.esnesnon.gradle
|
||||
package dev.frogmc.phytotelma
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import org.ecorous.esnesnon.gradle.common.CachingHttpClient
|
||||
import dev.frogmc.phytotelma.common.CachingHttpClient
|
||||
import java.net.URI
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
|
@ -17,18 +17,16 @@ object VersionChecker {
|
|||
fetchVersionData(version)
|
||||
val clientData = fetchClientDownload(version)
|
||||
// download client data
|
||||
val downloadDirectory = NonsenseGradlePlugin.nonsenseCacheDir.resolve("net/minecraft/client/$version/")
|
||||
println("Directory: "+downloadDirectory.absolutePathString())
|
||||
val downloadDirectory = PhytotelmaPlugin.globalCacheDir.resolve("net/minecraft/client/$version/")
|
||||
val downloadFile = downloadDirectory.resolve("client-$version.jar")
|
||||
if (downloadFile.exists()) {
|
||||
println("Client already downloaded to $downloadFile. Assuming it's valid. FIXME: Add checksum validation.")
|
||||
println("Client already downloaded! Assuming it's valid. FIXME: Add checksum validation.")
|
||||
return downloadFile
|
||||
}
|
||||
downloadDirectory.createDirectories()
|
||||
val raw = rawDownload(clientData.url)
|
||||
downloadFile.writeBytes(raw)
|
||||
savePomFile(version, downloadDirectory)
|
||||
println("Downloaded client to $downloadFile")
|
||||
return downloadFile
|
||||
}
|
||||
|
|
@ -0,0 +1,296 @@
|
|||
package dev.frogmc.phytotelma.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)
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package org.ecorous.esnesnon.gradle.common
|
||||
package dev.frogmc.phytotelma.common
|
||||
|
||||
import org.ecorous.esnesnon.gradle.NonsenseGradlePlugin
|
||||
import dev.frogmc.phytotelma.PhytotelmaPlugin
|
||||
import java.io.InputStream
|
||||
import java.net.URI
|
||||
import java.nio.charset.StandardCharsets
|
||||
|
@ -39,7 +39,7 @@ object CachingHttpClient {
|
|||
}
|
||||
|
||||
private fun getCacheFile(uri: URI): Path {
|
||||
val path = NonsenseGradlePlugin.nonsenseCacheDir.resolve("httpCache")
|
||||
val path = PhytotelmaPlugin.globalCacheDir.resolve("httpCache")
|
||||
.resolve(uri.host+uri.rawPath) // Use rawPath to ensure ASCII compat (since this will be a filesystem dir)
|
||||
path.createParentDirectories()
|
||||
return path
|
|
@ -1,4 +1,4 @@
|
|||
package org.ecorous.esnesnon.gradle.common
|
||||
package dev.frogmc.phytotelma.common
|
||||
|
||||
enum class Env(val id: String, val pascalName: String) {
|
||||
SERVER("server", "Server"),
|
|
@ -1,4 +1,4 @@
|
|||
package org.ecorous.esnesnon.gradle.common
|
||||
package dev.frogmc.phytotelma.common
|
||||
|
||||
fun sanitizeXmlValue(value: String): String {
|
||||
return value.replace("&", "&").replace("\"", """)
|
97
src/main/kotlin/dev/frogmc/phytotelma/ext/Stuff.kt
Normal file
97
src/main/kotlin/dev/frogmc/phytotelma/ext/Stuff.kt
Normal file
|
@ -0,0 +1,97 @@
|
|||
package dev.frogmc.phytotelma.ext
|
||||
|
||||
import dev.frogmc.phytotelma.PhytotelmaPlugin
|
||||
import dev.frogmc.phytotelma.VersionChecker
|
||||
import dev.frogmc.phytotelma.accesswidener.AccessWidener
|
||||
import dev.frogmc.phytotelma.run.RunConfigGenerator
|
||||
import org.ecorous.frogmc.thyroxine.Thyroxine
|
||||
import org.ecorous.frogmc.thyroxine.parser.ProguardParser
|
||||
import org.ecorous.frogmc.thyroxine.provider.MojmapProvider
|
||||
import org.ecorous.frogmc.thyroxine.provider.ParchmentProvider
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.provider.Provider
|
||||
import kotlin.io.path.notExists
|
||||
|
||||
fun Project.minecraft(
|
||||
version: String,
|
||||
parchmentVersion: String? = null
|
||||
): Project { // return self to allow for chaining
|
||||
if (VersionChecker.validateVersion(version)) {
|
||||
println("Setting up Minecraft...")
|
||||
val parchment = parchmentVersion ?: kotlin.runCatching { ParchmentProvider.findForMinecraftVersion(version) }
|
||||
.getOrDefault("")
|
||||
PhytotelmaPlugin.minecraftVersion = version
|
||||
PhytotelmaPlugin.parchmentVersion = parchment
|
||||
println("Valid version! $version")
|
||||
val clientJar = VersionChecker.downloadClient(version)
|
||||
val remappedJar = clientJar.resolveSibling("client-$version-remapped.jar")
|
||||
PhytotelmaPlugin.remappedGameJarPath = remappedJar
|
||||
println("Time to setup Minecraft!")
|
||||
val applyAW = AccessWidener.needsUpdate(this)
|
||||
if (remappedJar.notExists() || applyAW) {
|
||||
println("Remapping the game...")
|
||||
val data = kotlin.runCatching {
|
||||
ProguardParser.read(
|
||||
MojmapProvider.get(
|
||||
version,
|
||||
clientJar.resolveSibling("client-$version.txt")
|
||||
).orElseThrow()
|
||||
).reverse()
|
||||
}.getOrNull()
|
||||
val paramMappings = if (parchment.isNotEmpty()) kotlin.runCatching {
|
||||
ParchmentProvider.getParchment(
|
||||
version, parchment,
|
||||
PhytotelmaPlugin.globalCacheDir.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...")
|
||||
dependencies.add("implementation", "net.minecrell:terminalconsoleappender:1.2.0")
|
||||
VersionChecker.getDependencies(version) {
|
||||
dependencies.add("implementation", it)
|
||||
}
|
||||
dependencies.add("implementation", "net.minecraft:client:$version:remapped")
|
||||
|
||||
println("Generating run configurations...")
|
||||
RunConfigGenerator.generate(this)
|
||||
|
||||
if (applyAW) {
|
||||
project.afterEvaluate {
|
||||
println("Applying AccessWideners..")
|
||||
AccessWidener.apply(this, remappedJar)
|
||||
}
|
||||
}
|
||||
|
||||
println("Done!")
|
||||
} else {
|
||||
println("Invalid version! $version")
|
||||
error("Invalid minecraft version provided: $version")
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun Project.minecraft(
|
||||
version: Provider<String>,
|
||||
parchmentVersion: Provider<String> = provider { null }
|
||||
): Project {
|
||||
return minecraft(version.get(), parchmentVersion.orNull)
|
||||
}
|
||||
|
||||
fun Project.loader(version: String) {
|
||||
dependencies.add("implementation", "dev.frogmc:frogloader:$version")
|
||||
}
|
||||
|
||||
fun Project.loader(version: Provider<String>) {
|
||||
return loader(version.get())
|
||||
}
|
136
src/main/kotlin/dev/frogmc/phytotelma/nest/Nester.kt
Normal file
136
src/main/kotlin/dev/frogmc/phytotelma/nest/Nester.kt
Normal file
|
@ -0,0 +1,136 @@
|
|||
package dev.frogmc.phytotelma.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
|
||||
}
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
package org.ecorous.esnesnon.gradle.run
|
||||
package dev.frogmc.phytotelma.run
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonObject
|
||||
import org.ecorous.esnesnon.gradle.NonsenseGradlePlugin
|
||||
import org.ecorous.esnesnon.gradle.VersionChecker
|
||||
import org.ecorous.esnesnon.gradle.common.CachingHttpClient
|
||||
import dev.frogmc.phytotelma.PhytotelmaPlugin
|
||||
import dev.frogmc.phytotelma.VersionChecker
|
||||
import dev.frogmc.phytotelma.common.CachingHttpClient
|
||||
import java.net.URI
|
||||
import java.nio.charset.StandardCharsets
|
||||
import java.nio.file.Path
|
||||
|
@ -15,8 +15,8 @@ object AssetDownloader {
|
|||
private const val ASSETS_URL = "https://resources.download.minecraft.net"
|
||||
|
||||
fun download() {
|
||||
val version = NonsenseGradlePlugin.minecraftVersion
|
||||
val path = NonsenseGradlePlugin.nonsenseCacheDir.resolve("assets")
|
||||
val version = PhytotelmaPlugin.minecraftVersion
|
||||
val path = PhytotelmaPlugin.globalCacheDir.resolve("assets")
|
||||
val id = VersionChecker.downloadAssetIndex(version, path.resolve("indexes"))
|
||||
|
||||
val index = Gson().fromJson(
|
|
@ -1,4 +1,4 @@
|
|||
package org.ecorous.esnesnon.gradle.run
|
||||
package dev.frogmc.phytotelma.run
|
||||
|
||||
import org.gradle.api.Project
|
||||
|
||||
|
@ -12,5 +12,5 @@ interface RunConfigAdapter {
|
|||
)
|
||||
|
||||
/** Typically, return whether we are running within the IDE which makes use of this format. */
|
||||
fun shouldGenerate(): Boolean;
|
||||
fun shouldGenerate(): Boolean
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
package org.ecorous.esnesnon.gradle.run
|
||||
package dev.frogmc.phytotelma.run
|
||||
|
||||
import org.ecorous.esnesnon.gradle.NonsenseGradlePlugin
|
||||
import org.ecorous.esnesnon.gradle.VersionChecker
|
||||
import org.ecorous.esnesnon.gradle.common.Env
|
||||
import org.ecorous.esnesnon.gradle.run.adapter.EclipseAdapter
|
||||
import org.ecorous.esnesnon.gradle.run.adapter.IdeaAdapter
|
||||
import dev.frogmc.phytotelma.PhytotelmaPlugin
|
||||
import dev.frogmc.phytotelma.VersionChecker
|
||||
import dev.frogmc.phytotelma.common.Env
|
||||
import dev.frogmc.phytotelma.run.adapter.EclipseAdapter
|
||||
import dev.frogmc.phytotelma.run.adapter.IdeaAdapter
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.logging.configuration.ConsoleOutput
|
||||
import java.nio.file.Files
|
||||
|
@ -14,7 +14,7 @@ import kotlin.io.path.createParentDirectories
|
|||
import kotlin.io.path.notExists
|
||||
|
||||
object RunConfigGenerator {
|
||||
private const val LOG4J_CONFIG_PATH = ".gradle/nonsense-gradle/log4j.xml"
|
||||
private const val LOG4J_CONFIG_PATH = ".gradle/phytotelma/log4j.xml"
|
||||
private const val ASSET_DIR = "assets"
|
||||
private val ADAPTERS = arrayOf(EclipseAdapter(), IdeaAdapter())
|
||||
|
||||
|
@ -27,15 +27,12 @@ object RunConfigGenerator {
|
|||
}
|
||||
}
|
||||
|
||||
val assetPath = NonsenseGradlePlugin.nonsenseCacheDir.resolve(ASSET_DIR).absolute()
|
||||
val assetPath = PhytotelmaPlugin.globalCacheDir.resolve(ASSET_DIR).absolute()
|
||||
val assetIndexPath = assetPath.resolve("indexes")
|
||||
if (assetIndexPath.notExists()) {
|
||||
assetIndexPath.createDirectories()
|
||||
}
|
||||
val indexId = VersionChecker.downloadAssetIndex(NonsenseGradlePlugin.minecraftVersion, assetIndexPath)
|
||||
|
||||
val runConfigPath = project.rootDir.resolve(".idea").resolve("runConfigurations")
|
||||
runConfigPath.mkdirs()
|
||||
val indexId = VersionChecker.downloadAssetIndex(PhytotelmaPlugin.minecraftVersion, assetIndexPath)
|
||||
|
||||
for (adapter in ADAPTERS) {
|
||||
if (!adapter.shouldGenerate())
|
||||
|
@ -45,21 +42,21 @@ object RunConfigGenerator {
|
|||
adapter.generate(
|
||||
project,
|
||||
"Minecraft ${env.pascalName}",
|
||||
"org.ecorous.esnesnon.nonsense.loader.impl.launch.${env.id}.Nonsense${env.pascalName}",
|
||||
"dev.frogmc.frogloader.impl.launch.${env.id}.Frog${env.pascalName}",
|
||||
mutableListOf(
|
||||
"-Xmx${project.properties.getOrDefault("nonsense.gameHeap", "2048M")}",
|
||||
"-Dnonsense.development=true",
|
||||
"-Dnonsense.plugin.minecraft.gameJar=${NonsenseGradlePlugin.remappedGameJarPath}",
|
||||
"-Xmx${project.properties.getOrDefault("frogmc.gameHeap", "2048M")}",
|
||||
"-Dfrogmc.development=true",
|
||||
"-Dfrogmc.plugin.minecraft.gameJar=${PhytotelmaPlugin.remappedGameJarPath}",
|
||||
"-Dlog4j2.configurationFile=$log4jPath",
|
||||
"-Dlog4j2.formatMsgLookups=true"
|
||||
).apply {
|
||||
if (project.gradle.startParameter.consoleOutput != ConsoleOutput.Plain){
|
||||
add("-Dnonsense.log.disableAnsi=false")
|
||||
add("-Dfrogmc.log.disableAnsi=false")
|
||||
}
|
||||
}.toTypedArray(), if (env == Env.CLIENT) {
|
||||
arrayOf(
|
||||
"--assetsDir", assetPath.toString(),
|
||||
"--version", "Nonsense",
|
||||
"--version", "FrogMC",
|
||||
"--assetIndex", indexId,
|
||||
"--accessToken", "0"
|
||||
)
|
|
@ -1,8 +1,8 @@
|
|||
package org.ecorous.esnesnon.gradle.run.adapter
|
||||
package dev.frogmc.phytotelma.run.adapter
|
||||
|
||||
import org.ecorous.esnesnon.gradle.common.formatXmlList
|
||||
import org.ecorous.esnesnon.gradle.common.sanitizeXmlValue
|
||||
import org.ecorous.esnesnon.gradle.run.RunConfigAdapter
|
||||
import dev.frogmc.phytotelma.common.formatXmlList
|
||||
import dev.frogmc.phytotelma.common.sanitizeXmlValue
|
||||
import dev.frogmc.phytotelma.run.RunConfigAdapter
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.plugins.ide.eclipse.model.EclipseModel
|
||||
import java.nio.charset.StandardCharsets
|
|
@ -1,8 +1,8 @@
|
|||
package org.ecorous.esnesnon.gradle.run.adapter
|
||||
package dev.frogmc.phytotelma.run.adapter
|
||||
|
||||
import org.ecorous.esnesnon.gradle.common.formatXmlList
|
||||
import org.ecorous.esnesnon.gradle.common.sanitizeXmlValue
|
||||
import org.ecorous.esnesnon.gradle.run.RunConfigAdapter
|
||||
import dev.frogmc.phytotelma.common.formatXmlList
|
||||
import dev.frogmc.phytotelma.common.sanitizeXmlValue
|
||||
import dev.frogmc.phytotelma.run.RunConfigAdapter
|
||||
import org.gradle.api.Project
|
||||
import org.gradle.api.plugins.JavaPluginExtension
|
||||
import org.gradle.api.tasks.SourceSet
|
|
@ -1,8 +1,8 @@
|
|||
package org.ecorous.esnesnon.gradle.run.task
|
||||
package dev.frogmc.phytotelma.run.task
|
||||
|
||||
import org.ecorous.esnesnon.gradle.NonsenseGradlePlugin
|
||||
import org.ecorous.esnesnon.gradle.VersionChecker
|
||||
import org.ecorous.esnesnon.gradle.common.Env
|
||||
import dev.frogmc.phytotelma.PhytotelmaPlugin
|
||||
import dev.frogmc.phytotelma.VersionChecker
|
||||
import dev.frogmc.phytotelma.common.Env
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.FileCollection
|
||||
import org.gradle.api.logging.configuration.ConsoleOutput
|
||||
|
@ -21,14 +21,14 @@ abstract class RunGameTask @Inject constructor(env: Env) : JavaExec() {
|
|||
private val classpath: ConfigurableFileCollection = project.objects.fileCollection()
|
||||
|
||||
init {
|
||||
group = "nonsense"
|
||||
val assetPath = NonsenseGradlePlugin.nonsenseCacheDir.resolve("assets").absolute()
|
||||
group = "frogmc"
|
||||
val assetPath = PhytotelmaPlugin.globalCacheDir.resolve("assets").absolute()
|
||||
val assetIndexPath = assetPath.resolve("indexes")
|
||||
if (assetIndexPath.notExists()) {
|
||||
assetIndexPath.createDirectories()
|
||||
}
|
||||
val indexId = VersionChecker.downloadAssetIndex(NonsenseGradlePlugin.minecraftVersion, assetIndexPath)
|
||||
val log4jPath = project.rootDir.resolve(".gradle/nonsense-gradle/log4j.xml").toPath().absolute()
|
||||
val indexId = VersionChecker.downloadAssetIndex(PhytotelmaPlugin.minecraftVersion, assetIndexPath)
|
||||
val log4jPath = project.rootDir.resolve(".gradle/phytotelma/log4j.xml").toPath().absolute()
|
||||
if (log4jPath.notExists()) {
|
||||
log4jPath.createParentDirectories()
|
||||
this::class.java.getResourceAsStream("/log4j.xml").use { input ->
|
||||
|
@ -40,7 +40,7 @@ abstract class RunGameTask @Inject constructor(env: Env) : JavaExec() {
|
|||
if (env == Env.CLIENT) {
|
||||
listOf(
|
||||
"--assetsDir", assetPath.toString(),
|
||||
"--version", "Nonsense",
|
||||
"--version", "FrogMC",
|
||||
"--assetIndex", indexId,
|
||||
"--accessToken", "0"
|
||||
)
|
||||
|
@ -53,23 +53,23 @@ abstract class RunGameTask @Inject constructor(env: Env) : JavaExec() {
|
|||
).runtimeClasspath.filter { it.exists() })
|
||||
|
||||
jvmArguments.addAll(
|
||||
"-Xmx${project.properties.getOrDefault("nonsense.gameHeap", "2048M")}",
|
||||
"-Dnonsense.development=true",
|
||||
"-Dnonsense.plugin.minecraft.gameJar=${NonsenseGradlePlugin.remappedGameJarPath}",
|
||||
"-Xmx${project.properties.getOrDefault("frogmc.gameHeap", "2048M")}",
|
||||
"-Dfrogmc.development=true",
|
||||
"-Dfrogmc.plugin.minecraft.gameJar=${PhytotelmaPlugin.remappedGameJarPath}",
|
||||
"-Dlog4j2.configurationFile=$log4jPath",
|
||||
"-Dlog4j2.formatMsgNoLookups=true",
|
||||
writeArgFile()
|
||||
)
|
||||
|
||||
if (project.gradle.startParameter.consoleOutput != ConsoleOutput.Plain){
|
||||
jvmArguments.add("-Dnonsense.log.disableAnsi=false")
|
||||
jvmArguments.add("-Dfrogmc.log.disableAnsi=false")
|
||||
}
|
||||
|
||||
mainClass.set("org.ecorous.esnesnon.nonsense.loader.impl.launch.${env.id}.Nonsense${env.pascalName}")
|
||||
mainClass.set("dev.frogmc.frogloader.impl.launch.${env.id}.Frog${env.pascalName}")
|
||||
}
|
||||
|
||||
private fun writeArgFile(): String {
|
||||
val file = Files.createTempFile("nonsense-classpath", ".args")
|
||||
val file = Files.createTempFile("phytotelma-classpath", ".args")
|
||||
|
||||
val content = "-classpath " + classpath.files.stream()
|
||||
.map { it.absolutePath }
|
|
@ -1,25 +1,25 @@
|
|||
package org.ecorous.esnesnon.gradle.vineflower
|
||||
package dev.frogmc.phytotelma.vineflower
|
||||
|
||||
import net.fabricmc.fernflower.api.IFabricJavadocProvider
|
||||
import org.ecorous.esnesnon.nonsense_remapper.api.data.Parchment
|
||||
import org.ecorous.frogmc.thyroxine.api.data.Parchment
|
||||
import org.jetbrains.java.decompiler.struct.StructClass
|
||||
import org.jetbrains.java.decompiler.struct.StructField
|
||||
import org.jetbrains.java.decompiler.struct.StructMethod
|
||||
|
||||
class ParchmentJavadocProvider(private val parchment: Parchment) : IFabricJavadocProvider {
|
||||
override fun getClassDoc(structClass: StructClass): String? {
|
||||
return parchment.getClass(structClass.qualifiedName).orElse(null)?.javadoc?.joinToString { "\n" }
|
||||
return parchment.getClass(structClass.qualifiedName).orElse(null)?.javadoc?.joinToString("\n")
|
||||
}
|
||||
|
||||
override fun getFieldDoc(structClass: StructClass, structField: StructField): String? {
|
||||
return parchment.getClass(structClass.qualifiedName)
|
||||
.flatMap { it.getField(structField.name, structField.descriptor) }
|
||||
.orElse(null)?.javadoc?.joinToString { "\n" }
|
||||
.orElse(null)?.javadoc?.joinToString("\n")
|
||||
}
|
||||
|
||||
override fun getMethodDoc(structClass: StructClass, structMethod: StructMethod): String? {
|
||||
return parchment.getClass(structClass.qualifiedName)
|
||||
.flatMap { it.getMethod(structMethod.name, structMethod.descriptor) }
|
||||
.orElse(null)?.javadoc?.joinToString { "\n" }
|
||||
.orElse(null)?.javadoc?.joinToString("\n")
|
||||
}
|
||||
}
|
|
@ -1,148 +0,0 @@
|
|||
package org.ecorous.esnesnon.gradle
|
||||
|
||||
import net.fabricmc.fernflower.api.IFabricJavadocProvider
|
||||
import org.ecorous.esnesnon.gradle.common.Env
|
||||
import org.ecorous.esnesnon.gradle.run.AssetDownloader
|
||||
import org.ecorous.esnesnon.gradle.run.RunConfigGenerator
|
||||
import org.ecorous.esnesnon.gradle.run.task.RunGameTask
|
||||
import org.ecorous.esnesnon.gradle.vineflower.ParchmentJavadocProvider
|
||||
import org.ecorous.esnesnon.nonsense_remapper.provider.ParchmentProvider
|
||||
import org.gradle.api.Plugin
|
||||
import org.gradle.api.Project
|
||||
import org.jetbrains.java.decompiler.main.Fernflower
|
||||
import org.jetbrains.java.decompiler.main.decompiler.PrintStreamLogger
|
||||
import org.jetbrains.java.decompiler.main.decompiler.SingleFileSaver
|
||||
import org.jetbrains.java.decompiler.main.extern.IFernflowerPreferences
|
||||
import java.io.PrintStream
|
||||
import java.net.URI
|
||||
import java.nio.file.Path
|
||||
import kotlin.io.path.createDirectories
|
||||
import kotlin.io.path.deleteExisting
|
||||
import kotlin.io.path.exists
|
||||
|
||||
|
||||
class NonsenseGradlePlugin : Plugin<Project> {
|
||||
|
||||
override fun apply(project: Project) {
|
||||
println("> Applying Nonsense Gradle Plugin")
|
||||
nonsenseCacheDir = project.gradle.gradleUserHomeDir.resolve("caches/nonsense-gradle/").toPath()
|
||||
nonsenseCacheDir.createDirectories()
|
||||
|
||||
project.apply {
|
||||
mapOf("plugin" to "java-library")
|
||||
mapOf("plugin" to "eclipse")
|
||||
mapOf("plugin" to "idea")
|
||||
mapOf("plugin" to "maven-publish")
|
||||
}
|
||||
|
||||
project.repositories.apply {
|
||||
maven {
|
||||
it.name = "Esnesnon Releases"
|
||||
it.url = URI.create("https://maven-esnesnon.ecorous.org/releases")
|
||||
}
|
||||
maven {
|
||||
it.name = "Esnesnon Snapshots"
|
||||
it.url = URI.create("https://maven-esnesnon.ecorous.org/snapshots")
|
||||
}
|
||||
maven {
|
||||
it.name = "FabricMC"
|
||||
it.url = URI.create("https://maven.fabricmc.net")
|
||||
}
|
||||
maven {
|
||||
it.name = "Minecraft/Local"
|
||||
it.url = project.gradle.gradleUserHomeDir.resolve("caches/nonsense-gradle/").toURI()
|
||||
}
|
||||
maven {
|
||||
it.name = "Minecraft Libraries"
|
||||
it.url = URI.create("https://libraries.minecraft.net/")
|
||||
}
|
||||
}
|
||||
project.repositories.mavenCentral()
|
||||
|
||||
project.task("genSources").apply {
|
||||
group = "nonsense"
|
||||
doFirst {
|
||||
val fileName = remappedGameJarPath.fileName.toString()
|
||||
val output =
|
||||
remappedGameJarPath.resolveSibling(fileName.substring(0, fileName.length - 13) + "-sources.jar")
|
||||
if (output.exists()) {
|
||||
println("Output $output already exists, deleting!")
|
||||
output.deleteExisting()
|
||||
}
|
||||
val options = mutableMapOf<String, Any>()
|
||||
println("Preparing Parchment...")
|
||||
val parchment = ParchmentProvider.getParchment(
|
||||
minecraftVersion,
|
||||
parchmentVersion,
|
||||
project.gradle.gradleUserHomeDir.resolve("nonsense-gradle/org/parchmentmc/parchment/$minecraftVersion/$parchmentVersion/")
|
||||
.toPath()
|
||||
)
|
||||
options[IFabricJavadocProvider.PROPERTY_NAME] = ParchmentJavadocProvider(parchment)
|
||||
|
||||
println("Decompiling...")
|
||||
val logger = PrintStreamLogger(PrintStream(System.out))
|
||||
options.putAll(
|
||||
mapOf(
|
||||
IFernflowerPreferences.DECOMPILE_GENERIC_SIGNATURES to "1",
|
||||
IFernflowerPreferences.BYTECODE_SOURCE_MAPPING to "1",
|
||||
IFernflowerPreferences.REMOVE_SYNTHETIC to "1",
|
||||
IFernflowerPreferences.LOG_LEVEL to "warn",
|
||||
IFernflowerPreferences.THREADS to Runtime.getRuntime().availableProcessors().toString(),
|
||||
IFernflowerPreferences.INDENT_STRING to "\t"
|
||||
)
|
||||
)
|
||||
|
||||
val decomp = Fernflower(
|
||||
SingleFileSaver(output.toFile()),
|
||||
options, logger
|
||||
)
|
||||
decomp.addSource(remappedGameJarPath.toFile())
|
||||
|
||||
decomp.decompileContext()
|
||||
}
|
||||
}
|
||||
|
||||
project.task("genRunConfigs").apply {
|
||||
group = "nonsense"
|
||||
doFirst {
|
||||
RunConfigGenerator.generate(project)
|
||||
}
|
||||
}
|
||||
|
||||
project.tasks.register("runClient", RunGameTask::class.java, Env.CLIENT)
|
||||
project.tasks.register("runServer", RunGameTask::class.java, Env.SERVER)
|
||||
|
||||
project.task("downloadAssets").apply {
|
||||
group = "nonsense"
|
||||
doFirst {
|
||||
AssetDownloader.download()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO mod relocation/renaming: decide on mod file extension
|
||||
/*project.tasks.getByName("jar").actions.addLast {
|
||||
it.outputs.files.forEach {file ->
|
||||
val output = file.toPath().parent.resolveSibling("nonsense-mods")
|
||||
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)+".nonsense"))
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
val includeConfiguration = project.configurations.create("include").apply {
|
||||
extendsFrom(project.configurations.getByName("implementation"))
|
||||
}
|
||||
|
||||
project.tasks.getByName("jar").actions.addLast {
|
||||
it.outputs.files(includeConfiguration.artifacts.files)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
lateinit var nonsenseCacheDir: Path
|
||||
lateinit var minecraftVersion: String
|
||||
lateinit var remappedGameJarPath: Path
|
||||
lateinit var parchmentVersion: String
|
||||
}
|
||||
}
|
|
@ -1,61 +0,0 @@
|
|||
package org.ecorous.esnesnon.gradle.ext
|
||||
|
||||
import org.ecorous.esnesnon.gradle.NonsenseGradlePlugin
|
||||
import org.ecorous.esnesnon.gradle.VersionChecker
|
||||
import org.ecorous.esnesnon.gradle.run.RunConfigGenerator
|
||||
import org.ecorous.esnesnon.nonsense_remapper.NonsenseRemapper
|
||||
import org.ecorous.esnesnon.nonsense_remapper.parser.ProguardParser
|
||||
import org.ecorous.esnesnon.nonsense_remapper.provider.MojmapProvider
|
||||
import org.ecorous.esnesnon.nonsense_remapper.provider.ParchmentProvider
|
||||
import org.gradle.api.Project
|
||||
import kotlin.io.path.notExists
|
||||
|
||||
fun Project.minecraft(
|
||||
version: String,
|
||||
parchmentVersion: String = ParchmentProvider.findForMinecraftVersion(version)
|
||||
): Project { // return self to allow for chaining
|
||||
if (VersionChecker.validateVersion(version)) {
|
||||
NonsenseGradlePlugin.minecraftVersion = version
|
||||
NonsenseGradlePlugin.parchmentVersion = parchmentVersion
|
||||
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)
|
||||
println("Downloaded client!")
|
||||
val remappedJar = clientJar.resolveSibling("client-$version-remapped.jar")
|
||||
NonsenseGradlePlugin.remappedGameJarPath = remappedJar
|
||||
println("Time to setup Minecraft!")
|
||||
if (remappedJar.notExists()) {
|
||||
println("Remapping the game...")
|
||||
val data = ProguardParser.read(MojmapProvider.get(version, clientJar.resolveSibling("client-$version.txt")).orElseThrow()).reverse()
|
||||
val paramMappings = ParchmentProvider.getParchment(
|
||||
version, parchmentVersion,
|
||||
NonsenseGradlePlugin.nonsenseCacheDir.resolve("org/parchmentmc/parchment/$version/$parchmentVersion")
|
||||
)
|
||||
NonsenseRemapper.remap(data, clientJar, remappedJar, true, paramMappings)
|
||||
}
|
||||
println("Adding dependencies...")
|
||||
//configurations.getByName("development").extendsFrom(configurations.getByName("implementation"))
|
||||
dependencies.add("implementation","net.minecrell:terminalconsoleappender:1.2.0")
|
||||
VersionChecker.getDependencies(version) {
|
||||
dependencies.add("implementation", it)
|
||||
}
|
||||
dependencies.add("implementation", "net.minecraft:client:$version:remapped")
|
||||
|
||||
println("Generating run configurations...")
|
||||
RunConfigGenerator.generate(this)
|
||||
println("Done!")
|
||||
} else {
|
||||
println("Invalid version! $version")
|
||||
error("Invalid minecraft version provided: $version")
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
fun Project.loader(version: String){
|
||||
dependencies.add("implementation", "org.ecorous.esnesnon:nonsense-loader:$version")
|
||||
// TODO how can we make this version not have to be hard-coded?
|
||||
dependencies.add("annotationProcessor", "net.fabricmc:sponge-mixin:0.13.4+mixin.0.8.5")
|
||||
}
|
|
@ -11,7 +11,7 @@
|
|||
<RegexFilter regex="^Couldn't connect to realms$" onMatch="DENY" onMismatch="NEUTRAL"/>
|
||||
</Filters>
|
||||
<PatternLayout>
|
||||
<LoggerNamePatternSelector defaultPattern="%style{[%d{HH:mm:ss}]}{blue} %highlight{[%t/%level]}{FATAL=red, ERROR=red, WARN=yellow, INFO=green, DEBUG=green, TRACE=blue} %style{(%logger{1})}{cyan} %highlight{%msg%n}{FATAL=red, ERROR=red, WARN=normal, INFO=normal, DEBUG=normal, TRACE=normal}" disableAnsi="${sys:nonsense.log.disableAnsi:-true}">
|
||||
<LoggerNamePatternSelector defaultPattern="%style{[%d{HH:mm:ss}]}{blue} %highlight{[%t/%level]}{FATAL=red, ERROR=red, WARN=yellow, INFO=green, DEBUG=green, TRACE=blue} %style{(%logger{1})}{cyan} %highlight{%msg%n}{FATAL=red, ERROR=red, WARN=normal, INFO=normal, DEBUG=normal, TRACE=normal}" disableAnsi="${sys:frogmc.log.disableAnsi:-true}">
|
||||
<!-- Dont show the logger name for minecraft classes-->
|
||||
<PatternMatch key="net.minecraft.,com.mojang." pattern="%style{[%d{HH:mm:ss}]}{blue} %highlight{[%t/%level]}{FATAL=red, ERROR=red, WARN=yellow, INFO=green, DEBUG=green, TRACE=blue} %style{(Minecraft)}{cyan} %highlight{%msg{nolookups}%n}{FATAL=red, ERROR=red, WARN=normal, INFO=normal, DEBUG=normal, TRACE=normal}"/>
|
||||
</LoggerNamePatternSelector>
|
||||
|
@ -57,12 +57,12 @@
|
|||
</RollingRandomAccessFile>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Logger level="${sys:nonsense.log.level:-info}" name="net.minecraft"/>
|
||||
<Logger level="${sys:frogmc.log.level:-info}" name="net.minecraft"/>
|
||||
<Root level="all">
|
||||
<AppenderRef ref="DebugFile" level="${sys:nonsense.log.debug.level:-debug}"/>
|
||||
<AppenderRef ref="SysOut" level="${sys:nonsense.log.level:-info}"/>
|
||||
<AppenderRef ref="LatestFile" level="${sys:nonsense.log.level:-info}"/>
|
||||
<AppenderRef ref="ServerGuiConsole" level="${sys:nonsense.log.level:-info}"/>
|
||||
<AppenderRef ref="DebugFile" level="${sys:frogmc.log.debug.level:-debug}"/>
|
||||
<AppenderRef ref="SysOut" level="${sys:frogmc.log.level:-info}"/>
|
||||
<AppenderRef ref="LatestFile" level="${sys:frogmc.log.level:-info}"/>
|
||||
<AppenderRef ref="ServerGuiConsole" level="${sys:frogmc.log.level:-info}"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
Loading…
Reference in a new issue