Compare commits

..

No commits in common. "461688d5f6db8aede303301ea3201356336df30c" and "17c4cf3a5656c60a19df8993bc5166f2b54991a7" have entirely different histories.

23 changed files with 299 additions and 815 deletions

View file

@ -18,6 +18,6 @@ jobs:
- name: Build
run: |
chmod +x ./gradlew
./gradlew publishAllPublicationsToFrogMCSnapshotsMavenRepository \
-PFrogMCSnapshotsMavenUsername=${{ secrets.MAVEN_PUSH_USER }} \
-PFrogMCSnapshotsMavenPassword=${{ secrets.MAVEN_PUSH_TOKEN }}
./gradlew publishAllPublicationsToEsnesnonSnapshotsMavenRepository \
-PEsnesnonSnapshotsMavenUsername=${{ secrets.MAVEN_PUSH_USER }} \
-PEsnesnonSnapshotsMavenPassword=${{ secrets.MAVEN_PUSH_TOKEN }}

1
.gitignore vendored
View file

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

View file

@ -1,50 +1,41 @@
plugins {
kotlin("jvm") version "2.0.0"
kotlin("jvm") version "1.9.23"
`java-gradle-plugin`
`maven-publish`
}
group = "dev.frogmc"
group = "org.ecorous.esnesnon"
version = "0.0.1-SNAPSHOT"
repositories {
maven {
name = "FrogMC Maven Releases"
url = uri("https://maven.frogmc.dev/releases")
name = "Esnesnos Maven Releases"
url = uri("https://maven-esnesnon.ecorous.org/releases")
}
maven {
name = "FrogMC Maven Snapshots"
url = uri("https://maven.frogmc.dev/snapshots")
name = "Esnesnos Maven Snapshots"
url = uri("https://maven-esnesnon.ecorous.org/snapshots")
}
mavenCentral()
}
dependencies {
implementation("org.ecorous.frogmc:thyroxine:1.0.0-SNAPSHOT")
implementation("org.ow2.asm:asm:9.7")
implementation("org.ecorous.esnesnon:nonsense-remapper:1.0.0-SNAPSHOT")
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("phytotelma") {
id = "dev.frogmc.phytotelma"
implementationClass = "dev.frogmc.phytotelma.PhytotelmaPlugin"
create("nonsense-gradle") {
id = "org.ecorous.esnesnon.nonsense-gradle"
implementationClass = "org.ecorous.esnesnon.gradle.NonsenseGradlePlugin"
}
}
}
tasks.jar {
manifest {
attributes["Implementation-Version"] = version
}
}
tasks.test {
useJUnitPlatform()
}
@ -59,8 +50,8 @@ publishing {
repositories {
maven {
name = "FrogMCSnapshotsMaven"
url = uri("https://maven.frogmc.dev/snapshots")
name = "EsnesnonSnapshotsMaven"
url = uri("https://maven-esnesnon.ecorous.org/snapshots")
credentials(PasswordCredentials::class)
authentication {
create<BasicAuthentication>("basic")
@ -68,8 +59,8 @@ publishing {
}
maven {
name = "FrogMCReleasesMaven"
url = uri("https://maven.frogmc.dev/releases")
name = "EsnesnonReleasesMaven"
url = uri("https://maven-esnesnon.ecorous.org/releases")
credentials(PasswordCredentials::class)
authentication {
create<BasicAuthentication>("basic")

View file

@ -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.8-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

View file

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

View file

@ -1,191 +0,0 @@
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
}
}

View file

@ -1,296 +0,0 @@
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)
}
}

View file

@ -1,97 +0,0 @@
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())
}

View file

@ -1,136 +0,0 @@
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
}
}

View file

@ -0,0 +1,148 @@
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
}
}

View file

@ -1,8 +1,8 @@
package dev.frogmc.phytotelma
package org.ecorous.esnesnon.gradle
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import dev.frogmc.phytotelma.common.CachingHttpClient
import org.ecorous.esnesnon.gradle.common.CachingHttpClient
import java.net.URI
import java.nio.file.Files
import java.nio.file.Path
@ -17,16 +17,18 @@ object VersionChecker {
fetchVersionData(version)
val clientData = fetchClientDownload(version)
// download client data
val downloadDirectory = PhytotelmaPlugin.globalCacheDir.resolve("net/minecraft/client/$version/")
val downloadDirectory = NonsenseGradlePlugin.nonsenseCacheDir.resolve("net/minecraft/client/$version/")
println("Directory: "+downloadDirectory.absolutePathString())
val downloadFile = downloadDirectory.resolve("client-$version.jar")
if (downloadFile.exists()) {
println("Client already downloaded! Assuming it's valid. FIXME: Add checksum validation.")
println("Client already downloaded to $downloadFile. 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
}

View file

@ -1,6 +1,6 @@
package dev.frogmc.phytotelma.common
package org.ecorous.esnesnon.gradle.common
import dev.frogmc.phytotelma.PhytotelmaPlugin
import org.ecorous.esnesnon.gradle.NonsenseGradlePlugin
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 = PhytotelmaPlugin.globalCacheDir.resolve("httpCache")
val path = NonsenseGradlePlugin.nonsenseCacheDir.resolve("httpCache")
.resolve(uri.host+uri.rawPath) // Use rawPath to ensure ASCII compat (since this will be a filesystem dir)
path.createParentDirectories()
return path

View file

@ -1,4 +1,4 @@
package dev.frogmc.phytotelma.common
package org.ecorous.esnesnon.gradle.common
enum class Env(val id: String, val pascalName: String) {
SERVER("server", "Server"),

View file

@ -1,4 +1,4 @@
package dev.frogmc.phytotelma.common
package org.ecorous.esnesnon.gradle.common
fun sanitizeXmlValue(value: String): String {
return value.replace("&", "&amp;").replace("\"", "&quot;")

View file

@ -0,0 +1,61 @@
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")
}

View file

@ -1,10 +1,10 @@
package dev.frogmc.phytotelma.run
package org.ecorous.esnesnon.gradle.run
import com.google.gson.Gson
import com.google.gson.JsonObject
import dev.frogmc.phytotelma.PhytotelmaPlugin
import dev.frogmc.phytotelma.VersionChecker
import dev.frogmc.phytotelma.common.CachingHttpClient
import org.ecorous.esnesnon.gradle.NonsenseGradlePlugin
import org.ecorous.esnesnon.gradle.VersionChecker
import org.ecorous.esnesnon.gradle.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 = PhytotelmaPlugin.minecraftVersion
val path = PhytotelmaPlugin.globalCacheDir.resolve("assets")
val version = NonsenseGradlePlugin.minecraftVersion
val path = NonsenseGradlePlugin.nonsenseCacheDir.resolve("assets")
val id = VersionChecker.downloadAssetIndex(version, path.resolve("indexes"))
val index = Gson().fromJson(

View file

@ -1,4 +1,4 @@
package dev.frogmc.phytotelma.run
package org.ecorous.esnesnon.gradle.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;
}

View file

@ -1,10 +1,10 @@
package dev.frogmc.phytotelma.run
package org.ecorous.esnesnon.gradle.run
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.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 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/phytotelma/log4j.xml"
private const val LOG4J_CONFIG_PATH = ".gradle/nonsense-gradle/log4j.xml"
private const val ASSET_DIR = "assets"
private val ADAPTERS = arrayOf(EclipseAdapter(), IdeaAdapter())
@ -27,12 +27,15 @@ object RunConfigGenerator {
}
}
val assetPath = PhytotelmaPlugin.globalCacheDir.resolve(ASSET_DIR).absolute()
val assetPath = NonsenseGradlePlugin.nonsenseCacheDir.resolve(ASSET_DIR).absolute()
val assetIndexPath = assetPath.resolve("indexes")
if (assetIndexPath.notExists()) {
assetIndexPath.createDirectories()
}
val indexId = VersionChecker.downloadAssetIndex(PhytotelmaPlugin.minecraftVersion, assetIndexPath)
val indexId = VersionChecker.downloadAssetIndex(NonsenseGradlePlugin.minecraftVersion, assetIndexPath)
val runConfigPath = project.rootDir.resolve(".idea").resolve("runConfigurations")
runConfigPath.mkdirs()
for (adapter in ADAPTERS) {
if (!adapter.shouldGenerate())
@ -42,21 +45,21 @@ object RunConfigGenerator {
adapter.generate(
project,
"Minecraft ${env.pascalName}",
"dev.frogmc.frogloader.impl.launch.${env.id}.Frog${env.pascalName}",
"org.ecorous.esnesnon.nonsense.loader.impl.launch.${env.id}.Nonsense${env.pascalName}",
mutableListOf(
"-Xmx${project.properties.getOrDefault("frogmc.gameHeap", "2048M")}",
"-Dfrogmc.development=true",
"-Dfrogmc.plugin.minecraft.gameJar=${PhytotelmaPlugin.remappedGameJarPath}",
"-Xmx${project.properties.getOrDefault("nonsense.gameHeap", "2048M")}",
"-Dnonsense.development=true",
"-Dnonsense.plugin.minecraft.gameJar=${NonsenseGradlePlugin.remappedGameJarPath}",
"-Dlog4j2.configurationFile=$log4jPath",
"-Dlog4j2.formatMsgLookups=true"
).apply {
if (project.gradle.startParameter.consoleOutput != ConsoleOutput.Plain){
add("-Dfrogmc.log.disableAnsi=false")
add("-Dnonsense.log.disableAnsi=false")
}
}.toTypedArray(), if (env == Env.CLIENT) {
arrayOf(
"--assetsDir", assetPath.toString(),
"--version", "FrogMC",
"--version", "Nonsense",
"--assetIndex", indexId,
"--accessToken", "0"
)

View file

@ -1,8 +1,8 @@
package dev.frogmc.phytotelma.run.adapter
package org.ecorous.esnesnon.gradle.run.adapter
import dev.frogmc.phytotelma.common.formatXmlList
import dev.frogmc.phytotelma.common.sanitizeXmlValue
import dev.frogmc.phytotelma.run.RunConfigAdapter
import org.ecorous.esnesnon.gradle.common.formatXmlList
import org.ecorous.esnesnon.gradle.common.sanitizeXmlValue
import org.ecorous.esnesnon.gradle.run.RunConfigAdapter
import org.gradle.api.Project
import org.gradle.plugins.ide.eclipse.model.EclipseModel
import java.nio.charset.StandardCharsets

View file

@ -1,8 +1,8 @@
package dev.frogmc.phytotelma.run.adapter
package org.ecorous.esnesnon.gradle.run.adapter
import dev.frogmc.phytotelma.common.formatXmlList
import dev.frogmc.phytotelma.common.sanitizeXmlValue
import dev.frogmc.phytotelma.run.RunConfigAdapter
import org.ecorous.esnesnon.gradle.common.formatXmlList
import org.ecorous.esnesnon.gradle.common.sanitizeXmlValue
import org.ecorous.esnesnon.gradle.run.RunConfigAdapter
import org.gradle.api.Project
import org.gradle.api.plugins.JavaPluginExtension
import org.gradle.api.tasks.SourceSet

View file

@ -1,8 +1,8 @@
package dev.frogmc.phytotelma.run.task
package org.ecorous.esnesnon.gradle.run.task
import dev.frogmc.phytotelma.PhytotelmaPlugin
import dev.frogmc.phytotelma.VersionChecker
import dev.frogmc.phytotelma.common.Env
import org.ecorous.esnesnon.gradle.NonsenseGradlePlugin
import org.ecorous.esnesnon.gradle.VersionChecker
import org.ecorous.esnesnon.gradle.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 = "frogmc"
val assetPath = PhytotelmaPlugin.globalCacheDir.resolve("assets").absolute()
group = "nonsense"
val assetPath = NonsenseGradlePlugin.nonsenseCacheDir.resolve("assets").absolute()
val assetIndexPath = assetPath.resolve("indexes")
if (assetIndexPath.notExists()) {
assetIndexPath.createDirectories()
}
val indexId = VersionChecker.downloadAssetIndex(PhytotelmaPlugin.minecraftVersion, assetIndexPath)
val log4jPath = project.rootDir.resolve(".gradle/phytotelma/log4j.xml").toPath().absolute()
val indexId = VersionChecker.downloadAssetIndex(NonsenseGradlePlugin.minecraftVersion, assetIndexPath)
val log4jPath = project.rootDir.resolve(".gradle/nonsense-gradle/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", "FrogMC",
"--version", "Nonsense",
"--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("frogmc.gameHeap", "2048M")}",
"-Dfrogmc.development=true",
"-Dfrogmc.plugin.minecraft.gameJar=${PhytotelmaPlugin.remappedGameJarPath}",
"-Xmx${project.properties.getOrDefault("nonsense.gameHeap", "2048M")}",
"-Dnonsense.development=true",
"-Dnonsense.plugin.minecraft.gameJar=${NonsenseGradlePlugin.remappedGameJarPath}",
"-Dlog4j2.configurationFile=$log4jPath",
"-Dlog4j2.formatMsgNoLookups=true",
writeArgFile()
)
if (project.gradle.startParameter.consoleOutput != ConsoleOutput.Plain){
jvmArguments.add("-Dfrogmc.log.disableAnsi=false")
jvmArguments.add("-Dnonsense.log.disableAnsi=false")
}
mainClass.set("dev.frogmc.frogloader.impl.launch.${env.id}.Frog${env.pascalName}")
mainClass.set("org.ecorous.esnesnon.nonsense.loader.impl.launch.${env.id}.Nonsense${env.pascalName}")
}
private fun writeArgFile(): String {
val file = Files.createTempFile("phytotelma-classpath", ".args")
val file = Files.createTempFile("nonsense-classpath", ".args")
val content = "-classpath " + classpath.files.stream()
.map { it.absolutePath }

View file

@ -1,25 +1,25 @@
package dev.frogmc.phytotelma.vineflower
package org.ecorous.esnesnon.gradle.vineflower
import net.fabricmc.fernflower.api.IFabricJavadocProvider
import org.ecorous.frogmc.thyroxine.api.data.Parchment
import org.ecorous.esnesnon.nonsense_remapper.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" }
}
}

View file

@ -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:frogmc.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:nonsense.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:frogmc.log.level:-info}" name="net.minecraft"/>
<Logger level="${sys:nonsense.log.level:-info}" name="net.minecraft"/>
<Root level="all">
<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}"/>
<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}"/>
</Root>
</Loggers>
</Configuration>