mirror of
https://github.com/MinecraftForge/MinecraftForge.git
synced 2026-11-14 01:04:24 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6f9f0f0cfa |
No files matched your search
+6
-3
@@ -34,9 +34,10 @@
|
||||
/projects/**/run/
|
||||
/projects/**/*.launch
|
||||
/repo/
|
||||
src/generated/resources/**/.cache/
|
||||
src/generated_test/resources/**/.cache/
|
||||
src/generated_mdk/resources/**/.cache/
|
||||
src/*/generated/**/.cache/
|
||||
# Generated by gradle every import
|
||||
**/src/main/resources/META-INF/MANIFEST.MF
|
||||
/fmlloader/src/main/resources/forge_version.json
|
||||
|
||||
#Patch rejects
|
||||
#/patches-/
|
||||
@@ -49,3 +50,5 @@ changelog.txt
|
||||
*.7z
|
||||
/logs/
|
||||
|
||||
/ForgeRoot.ipr
|
||||
/ForgeRoot.iws
|
||||
+16
-1277
File diff suppressed because it is too large.
Load diff
@@ -1,6 +1,6 @@
|
||||
repositories {
|
||||
maven { url = 'https://maven.minecraftforge.net/' }
|
||||
mavenCentral()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package net.minecraftforge.forge.tasks
|
||||
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.ConfigurableFileCollection
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
|
||||
abstract class BundleList extends DefaultTask {
|
||||
@InputFiles abstract ConfigurableFileCollection getConfig()
|
||||
@InputFile abstract RegularFileProperty getServerBundle()
|
||||
@OutputFile abstract RegularFileProperty getOutput()
|
||||
|
||||
BundleList() {
|
||||
config.setFrom(project.configurations.installer)
|
||||
output.convention(project.layout.buildDirectory.file("$name/output.list"))
|
||||
configure {
|
||||
dependsOn(project.tasks.universalJar)
|
||||
inputs.file(project.tasks.universalJar.archiveFile)
|
||||
}
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void run() {
|
||||
def entries = [:] as TreeMap
|
||||
def resolved = project.configurations.installer.resolvedConfiguration.resolvedArtifacts
|
||||
for (var dep : resolved) {
|
||||
def info = Util.getMavenInfoFromDep(dep)
|
||||
//println("$dep.file.sha1\t$info.name\t$info.path")
|
||||
entries.put("$info.art.group:$info.art.name", "$dep.file.sha256\t$info.name\t$info.path")
|
||||
}
|
||||
|
||||
[
|
||||
project.tasks.universalJar
|
||||
].forEach{ packed ->
|
||||
def info = Util.getMavenInfoFromTask(packed)
|
||||
def file = packed.archiveFile.get().asFile
|
||||
entries.put("$info.art.group:$info.art.name:$info.art.classifier", "$file.sha256\t$info.name\t$info.path")
|
||||
}
|
||||
|
||||
try (def zip = new java.util.zip.ZipFile(serverBundle.get().asFile)) {
|
||||
def entry = zip.getEntry('META-INF/libraries.list')
|
||||
def data = zip.getInputStream(entry).text.split('\n')
|
||||
for (def line : data) {
|
||||
def (sha, artifact, path) = line.split('\t')
|
||||
def (group, name, other) = artifact.split(':', 3)
|
||||
//println("Group: $group Name: $name")
|
||||
def key = "$group:$name"
|
||||
if (!entries.containsKey(key))
|
||||
entries.put(key, line)
|
||||
}
|
||||
}
|
||||
|
||||
output.get().asFile.text = entries.values().join('\n')
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
package net.minecraftforge.forge.tasks
|
||||
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import groovy.json.JsonSlurper
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipFile
|
||||
import java.util.zip.ZipOutputStream
|
||||
|
||||
abstract class DevInstallerJar extends DefaultTask {
|
||||
@OutputFile abstract RegularFileProperty getOutput()
|
||||
@InputFile abstract RegularFileProperty getInput()
|
||||
|
||||
DevInstallerJar() {
|
||||
output.convention(project.layout.buildDirectory.file('libs/' + project.name + '-' + project.version + '-installer-dev.jar'))
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
protected void exec() {
|
||||
try (def zos = new ZipOutputStream(output.get().asFile.newDataOutputStream())) {
|
||||
def seen = [] as Set
|
||||
def deps = [:] as java.util.TreeMap
|
||||
try (def base = new ZipFile(input.get().asFile)) {
|
||||
base.entries().each { entry ->
|
||||
zos.putNextEntry(entry)
|
||||
def data = base.getInputStream(entry).bytes
|
||||
zos.write(data)
|
||||
|
||||
if (entry.name == 'install_profile.json' || entry.name == 'version.json') {
|
||||
def json = new JsonSlurper().parse(data)
|
||||
json.libraries.each { lib ->
|
||||
deps.put(lib.name, lib.downloads.artifact)
|
||||
}
|
||||
}
|
||||
|
||||
if (entry.name.startsWith('maven/'))
|
||||
seen.add(entry.name.substring(6))
|
||||
}
|
||||
}
|
||||
|
||||
def resolved = project.configurations.installer.resolvedConfiguration.resolvedArtifacts
|
||||
|
||||
deps.each { name, info ->
|
||||
if (seen.contains(info.path))
|
||||
return
|
||||
|
||||
def art = resolved.find { "${it.moduleVersion.id.group}:${it.moduleVersion.id.name}:${it.moduleVersion.id.version}" == name }
|
||||
|
||||
def file = null
|
||||
if (art != null) {
|
||||
if (!Util.checkExists(info.url))
|
||||
file = art.file
|
||||
} else {
|
||||
// TODO download missing things
|
||||
}
|
||||
|
||||
if (file != null) {
|
||||
//println('maven/' + info.path)
|
||||
zos.putNextEntry(new ZipEntry('maven/' + info.path))
|
||||
zos.write(file.bytes)
|
||||
seen.add(info.path)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package net.minecraftforge.forge.tasks
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.RegularFileProperty
|
||||
import org.gradle.api.provider.Property
|
||||
import org.gradle.api.tasks.*
|
||||
|
||||
abstract class ExtractFile extends DefaultTask {
|
||||
@InputFile abstract RegularFileProperty getInput()
|
||||
@Input abstract Property<String> getTarget()
|
||||
@OutputFile abstract RegularFileProperty getOutput()
|
||||
|
||||
@TaskAction
|
||||
protected void exec() {
|
||||
def zip = new java.util.zip.ZipFile(input.get().asFile);
|
||||
def entry = zip.getEntry(target.get())
|
||||
if (entry == null)
|
||||
throw new IllegalStateException(input.get().asFile.absolutePath + " does not contain " + target.get())
|
||||
def stream = zip.getInputStream(entry)
|
||||
output.get().asFile.withOutputStream { out ->
|
||||
stream.transferTo(out)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,21 +2,26 @@ package net.minecraftforge.forge.tasks
|
||||
|
||||
import org.gradle.api.tasks.bundling.Zip
|
||||
import org.gradle.api.tasks.*
|
||||
import org.gradle.api.provider.SetProperty
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.provider.Property
|
||||
import groovy.json.JsonSlurper
|
||||
|
||||
abstract class InstallerJar extends Zip {
|
||||
@Input @Optional abstract SetProperty<String> getPackedDependencies()
|
||||
@Input @Optional abstract Property<Boolean> getFat()
|
||||
@Input @Optional abstract Property<Boolean> getOffline()
|
||||
|
||||
InstallerJar() {
|
||||
archiveClassifier.set('installer')
|
||||
archiveExtension.set('jar') // Needs to be Zip task to not override Manifest, so set extension
|
||||
destinationDirectory.set(project.layout.buildDirectory.dir('libs'))
|
||||
fat.convention(false)
|
||||
offline.convention(false)
|
||||
|
||||
def installerJson = project.tasks.installerJson
|
||||
def launcherJson = project.tasks.launcherJson
|
||||
def downloadInstaller = project.tasks.downloadInstaller
|
||||
|
||||
dependsOn(installerJson, launcherJson, downloadInstaller)
|
||||
dependsOn(installerJson, launcherJson, downloadInstaller, project.configurations.installer)
|
||||
from(installerJson, launcherJson)
|
||||
|
||||
from(project.rootProject.file('/src/main/resources/url.png'))
|
||||
@@ -24,21 +29,128 @@ abstract class InstallerJar extends Zip {
|
||||
from(project.zipTree(downloadInstaller.output)) {
|
||||
duplicatesStrategy = 'exclude'
|
||||
}
|
||||
|
||||
if (!System.env.TEAMCITY_VERSION) {
|
||||
dependsOn(project.universalJar)
|
||||
from(project.universalJar) {
|
||||
into '/maven/' + project.group.replace('.', '/') + '/' + project.universalJar.archiveBaseName.get() + '/' + project.version + '/'
|
||||
|
||||
if (fat.get() || offline.get()) {
|
||||
def cfg = project.tasks.register(name + "Config", Configure)
|
||||
cfg.get().configure {
|
||||
parent = this
|
||||
dependsOn(project.tasks.installerJson, project.tasks.launcherJson)
|
||||
}
|
||||
|
||||
packedDependencies.get().forEach {
|
||||
def jarTask = project.rootProject.getTasks().findByPath(it)
|
||||
dependsOn(jarTask)
|
||||
from(jarTask) {
|
||||
into '/maven/' + jarTask.project.group.replace('.', '/') + '/' + jarTask.archiveBaseName.get() + '/' + jarTask.project.version + '/'
|
||||
dependsOn(cfg)
|
||||
} else {
|
||||
// Things we ALWAYS bundle, this just the server shim jar, because the installer spec only says to extract the file.
|
||||
// I should make it allow downloads but thats a spec break, and this is just a ~14KB jar
|
||||
[
|
||||
project.tasks.serverShimJar // Server bootstrap executable jar
|
||||
].forEach { packed ->
|
||||
def path = Util.getMavenInfoFromTask(packed).path
|
||||
from(packed) {
|
||||
rename { "maven/$path" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static abstract class Configure extends DefaultTask {
|
||||
public Zip parent
|
||||
private int count = 0;
|
||||
|
||||
@TaskAction
|
||||
protected void exec() {
|
||||
def deps = [:] as java.util.TreeMap
|
||||
|
||||
// Gather all things that need downloading
|
||||
[
|
||||
project.tasks.installerJson,
|
||||
project.tasks.launcherJson
|
||||
].each { task ->
|
||||
def json = task.output.get().asFile.json
|
||||
json.libraries.each { lib ->
|
||||
deps.put(lib.name, lib.downloads.artifact)
|
||||
//println(lib.name)
|
||||
}
|
||||
}
|
||||
|
||||
// First find things we build in this project.
|
||||
[
|
||||
project.tasks.universalJar, // Forge runtime code
|
||||
project.tasks.serverShimJar // Shim jar for dedicated server
|
||||
].forEach { packed ->
|
||||
def name = Util.getMavenInfoFromTask(packed).name
|
||||
def info = deps.remove(name)
|
||||
if (info != null) {
|
||||
println("Adding: $packed.path $name")
|
||||
parent.from(packed) {
|
||||
rename { "maven/$info.path" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find any artifacts from the 'installer' config
|
||||
// This config specifies the runtime files we intend for the interaller to have.
|
||||
// And are typically what we would be developing and testing alongside Forge.
|
||||
// So we may have local modified versions
|
||||
def cfg = project.configurations.installer
|
||||
while (cfg != null) {
|
||||
//println('')
|
||||
def resolved = cfg.resolvedConfiguration.resolvedArtifacts
|
||||
int found = 0
|
||||
for (var dep : resolved) {
|
||||
def name = Util.getMavenInfoFromDep(dep).name
|
||||
def info = deps.remove(name)
|
||||
if (info == null) {
|
||||
//println("Skipping: $name")
|
||||
continue
|
||||
}
|
||||
//println("-$name")
|
||||
found++
|
||||
addFile(dep.file, info)
|
||||
}
|
||||
|
||||
if (deps.isEmpty()) {
|
||||
cfg = null
|
||||
continue
|
||||
}
|
||||
|
||||
// Prevent infinite loops if something fucky happens
|
||||
if (found == 0)
|
||||
throw new IllegalStateException("Failed to find any installer dependencies")
|
||||
|
||||
def seen = [] as Set
|
||||
cfg = project.configurations.detachedConfiguration()
|
||||
cfg.transitive = false
|
||||
for (def key : deps.keySet()) {
|
||||
def (group, artifact, other) = key.split(':', 3)
|
||||
// Only resolve unique group:artifact so we don't get version overrides
|
||||
if (seen.add(group + ':' + artifact)) {
|
||||
//println("+$key")
|
||||
cfg.dependencies.add(project.dependencies.create(key))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void addFile(file, info) {
|
||||
def pack = parent.offline.get() || info.url.isEmpty()
|
||||
|
||||
// If it's a offline jar just always pack
|
||||
if (!pack) {
|
||||
try {
|
||||
def remote = new URL("${info.url}.sha1").getText('UTF-8')
|
||||
pack = !info.sha1.equals(remote)
|
||||
} catch (FileNotFoundException e) {
|
||||
pack = !info.url.startsWith('https://libraries.minecraft.net/')
|
||||
// Oh noes its not there!
|
||||
}
|
||||
}
|
||||
|
||||
if (pack) {
|
||||
println("Adding: $file.absolutePath")
|
||||
parent.from(file) {
|
||||
rename { "maven/$info.path" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@ import java.nio.file.Files
|
||||
abstract class InstallerJson extends DefaultTask {
|
||||
@OutputFile abstract RegularFileProperty getOutput()
|
||||
@InputFiles abstract ConfigurableFileCollection getInput()
|
||||
@Input @Optional abstract SetProperty<String> getPackedDependencies()
|
||||
@Input @Optional final Map<String, Object> libraries = new LinkedHashMap<>()
|
||||
@Input Map<String, Object> json = new LinkedHashMap<>()
|
||||
@InputFile abstract RegularFileProperty getIcon()
|
||||
@@ -23,29 +22,19 @@ abstract class InstallerJson extends DefaultTask {
|
||||
@Input abstract Property<String> getWelcome()
|
||||
|
||||
InstallerJson() {
|
||||
getLauncherJsonName().convention('/version.json')
|
||||
getLogo().convention('/big_logo.png')
|
||||
getMirrors().convention('https://files.minecraftforge.net/mirrors-2.0.json')
|
||||
getWelcome().convention("Welcome to the simple ${project.name.capitalize()} installer.")
|
||||
|
||||
getOutput().convention(project.layout.buildDirectory.file('install_profile.json'))
|
||||
|
||||
['client', 'server'].each { side ->
|
||||
['slim', 'extra'].each { type ->
|
||||
def tsk = project.tasks.getByName("download${side.capitalize()}${type.capitalize()}")
|
||||
dependsOn(tsk)
|
||||
input.from(tsk.output)
|
||||
}
|
||||
def tsk = project.tasks.getByName("create${side.capitalize()}SRG")
|
||||
dependsOn(tsk)
|
||||
input.from(tsk.output)
|
||||
}
|
||||
|
||||
launcherJsonName.convention('/version.json')
|
||||
logo.convention('/big_logo.png')
|
||||
mirrors.convention('https://files.minecraftforge.net/mirrors-2.0.json')
|
||||
welcome.convention("Welcome to the ${project.name.capitalize()} installer.")
|
||||
output.convention(project.layout.buildDirectory.file('libs/install_profile.json'))
|
||||
|
||||
project.afterEvaluate {
|
||||
(packedDependencies.get().collect{ project.rootProject.tasks.findByPath(it) } + [project.universalJar]).forEach {
|
||||
dependsOn(it)
|
||||
input.from it.archiveFile
|
||||
[
|
||||
project.tasks.universalJar,
|
||||
project.tasks.serverShimJar
|
||||
].forEach { packed ->
|
||||
dependsOn(packed)
|
||||
input.from packed.archiveFile
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,17 +42,19 @@ abstract class InstallerJson extends DefaultTask {
|
||||
@TaskAction
|
||||
protected void exec() {
|
||||
def libs = libraries
|
||||
for (def child : packedDependencies.get().collect{ project.rootProject.tasks.findByPath(it) } + [project.universalJar]) {
|
||||
def dep = Util.getMavenDep(child)
|
||||
def path = Util.getMavenPath(child)
|
||||
libs.put(dep.toString(), [
|
||||
name: dep,
|
||||
[
|
||||
project.tasks.universalJar,
|
||||
project.tasks.serverShimJar
|
||||
].forEach { packed ->
|
||||
def info = Util.getMavenInfoFromTask(packed)
|
||||
libs.put(info.name, [
|
||||
name: info.name,
|
||||
downloads: [
|
||||
artifact: [
|
||||
path: path,
|
||||
url: "https://maven.minecraftforge.net/${path}",
|
||||
sha1: child.archiveFile.get().asFile.sha1(),
|
||||
size: child.archiveFile.get().asFile.length()
|
||||
path: info.path,
|
||||
url: "https://maven.minecraftforge.net/$info.path",
|
||||
sha1: packed.archiveFile.get().asFile.sha1(),
|
||||
size: packed.archiveFile.get().asFile.length()
|
||||
]
|
||||
]
|
||||
])
|
||||
|
||||
@@ -16,59 +16,63 @@ abstract class LauncherJson extends DefaultTask {
|
||||
@OutputFile abstract RegularFileProperty getOutput()
|
||||
@InputFiles abstract ConfigurableFileCollection getInput()
|
||||
@Input Map<String, Object> json = new LinkedHashMap<>()
|
||||
@Input @Optional abstract SetProperty<String> getPackedDependencies()
|
||||
|
||||
@Internal final vanilla = project.project(':mcp').file('build/mcp/downloadJson/version.json')
|
||||
@Internal final timestamp = iso8601Now()
|
||||
@Internal final comment = [
|
||||
"Please do not automate the download and installation of Forge.",
|
||||
"Our efforts are supported by ads from the download page.",
|
||||
"If you MUST automate this, please consider supporting the project through https://www.patreon.com/LexManos/"
|
||||
]
|
||||
@Internal final id = "${project.rootProject.ext.MC_VERSION}-${project.name}${project.version.substring(project.rootProject.ext.MC_VERSION.length())}"
|
||||
|
||||
LauncherJson() {
|
||||
getOutput().convention(project.layout.buildDirectory.file('version.json'))
|
||||
output.convention(project.layout.buildDirectory.file('libs/version.json'))
|
||||
|
||||
dependsOn(':fmlloader:jar', 'universalJar')
|
||||
getInput().from(project.tasks.universalJar.archiveFile,
|
||||
project.project(':fmlloader').jar.archiveFile,
|
||||
vanilla)
|
||||
|
||||
project.afterEvaluate {
|
||||
packedDependencies.get().forEach {
|
||||
def jarTask = project.rootProject.tasks.findByPath(it)
|
||||
dependsOn(jarTask)
|
||||
input.from jarTask.archiveFile
|
||||
dependsOn(project.tasks.universalJar)
|
||||
input.from(project.tasks.universalJar.archiveFile)
|
||||
input.from(project.configurations.installer)
|
||||
configure {
|
||||
def mc = project.rootProject.ext.MC_VERSION
|
||||
def forge = project.rootProject.ext.FORGE_VERSION
|
||||
def timestamp = iso8601Now()
|
||||
json.putAll([
|
||||
_comment: [
|
||||
"Please do not automate the download and installation of Forge.",
|
||||
"Our efforts are supported by ads from the download page.",
|
||||
"If you MUST automate this, please consider supporting the project through https://www.patreon.com/LexManos/"
|
||||
],
|
||||
id: "$mc-$project.name$forge",
|
||||
time: timestamp,
|
||||
releaseTime: timestamp,
|
||||
inheritsFrom: mc,
|
||||
type: 'release',
|
||||
logging: [:],
|
||||
mainClass: '',
|
||||
libraries: []
|
||||
] as LinkedHashMap)
|
||||
|
||||
[
|
||||
project.tasks.universalJar
|
||||
].forEach { packed ->
|
||||
dependsOn(packed)
|
||||
input.from packed.archiveFile
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
protected void exec() {
|
||||
if (!json.libraries)
|
||||
json.libraries = []
|
||||
def libs = [:]
|
||||
getArtifacts(project, project.configurations.installer, false).each { key, lib -> libs[key] = lib }
|
||||
getArtifacts(project, project.configurations.moduleonly, false).each { key, lib -> libs[key] = lib }
|
||||
|
||||
packedDependencies.get().collect{ project.rootProject.tasks.findByPath(it) }.forEach {
|
||||
def path = Util.getMavenPath(it)
|
||||
def key = Util.getMavenDep(it)
|
||||
|
||||
libs[key] = [
|
||||
name: key,
|
||||
[
|
||||
project.tasks.universalJar
|
||||
].forEach { packed ->
|
||||
def info = Util.getMavenInfoFromTask(packed)
|
||||
json.libraries.add([
|
||||
name: info.name,
|
||||
downloads: [
|
||||
artifact: [
|
||||
path: path,
|
||||
url: "https://maven.minecraftforge.net/${path}",
|
||||
sha1: it.archiveFile.get().asFile.sha1(),
|
||||
size: it.archiveFile.get().asFile.length()
|
||||
path: info.path,
|
||||
url: "https://maven.minecraftforge.net/$info.path",
|
||||
sha1: packed.archiveFile.get().asFile.sha1(),
|
||||
size: packed.archiveFile.get().asFile.length()
|
||||
]
|
||||
]
|
||||
]
|
||||
])
|
||||
}
|
||||
getArtifacts(project, project.configurations.installer).each { key, lib ->
|
||||
json.libraries.add(lib)
|
||||
}
|
||||
libs.each { key, lib -> json.libraries.add(lib) }
|
||||
Files.writeString(output.get().asFile.toPath(), new JsonBuilder(json).toPrettyString())
|
||||
}
|
||||
}
|
||||
-10
@@ -26,16 +26,6 @@ abstract class SetupCheckJarCompatibility extends DefaultTask {
|
||||
@TaskAction
|
||||
void run() {
|
||||
def inputVersion = inputVersion.get()
|
||||
def fmlLibs = project.configurations.detachedConfiguration(project.PACKED_DEPS.collect {
|
||||
def artifactId = it.split(':')[1]
|
||||
return project.dependencies.create("net.minecraftforge:${artifactId}:${inputVersion}")
|
||||
}.toArray(Dependency[]::new))
|
||||
|
||||
project.tasks.named('checkJarCompatibility') {
|
||||
baseLibraries.from(project.provider {
|
||||
fmlLibs.resolvedConfiguration.lenientConfiguration.files
|
||||
})
|
||||
}
|
||||
|
||||
def baseForgeUserdev = project.layout.buildDirectory.dir(name).map { it.file("forge-${inputVersion}-userdev.jar") }.get().asFile
|
||||
project.rootProject.extensions.download.run {
|
||||
|
||||
@@ -16,25 +16,33 @@ import java.util.zip.ZipInputStream
|
||||
public class Util {
|
||||
static final ASM_LEVEL = Opcodes.ASM9
|
||||
|
||||
static void init() {
|
||||
File.metaClass.sha1 = { ->
|
||||
MessageDigest md = MessageDigest.getInstance('SHA-1')
|
||||
delegate.eachByte 4096, {bytes, size ->
|
||||
md.update(bytes, 0, size)
|
||||
}
|
||||
return md.digest().collect {String.format "%02x", it}.join()
|
||||
}
|
||||
static void init() {
|
||||
File.metaClass.sha1 = { ->
|
||||
MessageDigest md = MessageDigest.getInstance('SHA-1')
|
||||
delegate.eachByte 4096, {bytes, size ->
|
||||
md.update(bytes, 0, size)
|
||||
}
|
||||
return md.digest().collect {String.format "%02x", it}.join()
|
||||
}
|
||||
File.metaClass.getSha1 = { !delegate.exists() ? null : delegate.sha1() }
|
||||
File.metaClass.sha256 = { ->
|
||||
MessageDigest md = MessageDigest.getInstance('SHA-256')
|
||||
delegate.eachByte 4096, {bytes, size ->
|
||||
md.update(bytes, 0, size)
|
||||
}
|
||||
return md.digest().collect {String.format "%02x", it}.join()
|
||||
}
|
||||
File.metaClass.getSha256 = { !delegate.exists() ? null : delegate.sha256() }
|
||||
|
||||
File.metaClass.json = { -> new JsonSlurper().parseText(delegate.text) }
|
||||
File.metaClass.json = { -> new JsonSlurper().parseText(delegate.text) }
|
||||
File.metaClass.getJson = { return delegate.exists() ? new JsonSlurper().parse(delegate) : [:] }
|
||||
File.metaClass.setJson = { json -> delegate.text = new JsonBuilder(json).toPrettyString() }
|
||||
|
||||
Date.metaClass.iso8601 = { ->
|
||||
def format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
|
||||
def result = format.format(delegate)
|
||||
return result[0..21] + ':' + result[22..-1]
|
||||
}
|
||||
Date.metaClass.iso8601 = { ->
|
||||
def format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
|
||||
def result = format.format(delegate)
|
||||
return result[0..21] + ':' + result[22..-1]
|
||||
}
|
||||
|
||||
String.metaClass.rsplit = { String del, int limit = -1 ->
|
||||
def lst = new ArrayList()
|
||||
@@ -47,100 +55,117 @@ public class Util {
|
||||
lst.add(0, tmp)
|
||||
return lst
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] getClasspath(project, libs, artifact) {
|
||||
def ret = []
|
||||
artifactTree(project, artifact).each { key, lib ->
|
||||
libs[lib.name] = lib
|
||||
if (lib.name != artifact)
|
||||
ret.add(lib.name)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
public static def getArtifacts(project, config, classifiers) {
|
||||
def ret = [:]
|
||||
config.resolvedConfiguration.resolvedArtifacts.each {
|
||||
def art = [
|
||||
group: it.moduleVersion.id.group,
|
||||
name: it.moduleVersion.id.name,
|
||||
version: it.moduleVersion.id.version,
|
||||
classifier: it.classifier,
|
||||
extension: it.extension,
|
||||
file: it.file
|
||||
]
|
||||
def key = art.group + ':' + art.name
|
||||
def folder = "${art.group.replace('.', '/')}/${art.name}/${art.version}/"
|
||||
def filename = "${art.name}-${art.version}"
|
||||
if (art.classifier != null)
|
||||
filename += "-${art.classifier}"
|
||||
filename += ".${art.extension}"
|
||||
def path = "${folder}${filename}"
|
||||
def url = "https://libraries.minecraft.net/${path}"
|
||||
if (!checkExists(url)) {
|
||||
url = "https://maven.minecraftforge.net/${path}"
|
||||
}
|
||||
ret[key] = [
|
||||
name: "${art.group}:${art.name}:${art.version}" + (art.classifier == null ? '' : ":${art.classifier}") + (art.extension == 'jar' ? '' : "@${art.extension}"),
|
||||
downloads: [
|
||||
artifact: [
|
||||
path: path,
|
||||
url: url,
|
||||
sha1: sha1(art.file),
|
||||
size: art.file.length()
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
public static def getMavenPath(task) {
|
||||
def classifier = task.archiveClassifier.get()
|
||||
def dep = "${task.project.group}:${task.project.name}:${task.project.version}" + (classifier == '' ? '' : ':' + classifier)
|
||||
return "${task.project.group.replace('.', '/')}/${task.project.name}/${task.project.version}/${task.project.name}-${task.project.version}".toString() + (classifier == '' ? '' : '-' + classifier) + '.jar'
|
||||
}
|
||||
|
||||
public static def getMavenDep(task) {
|
||||
def classifier = task.archiveClassifier.get()
|
||||
return "${task.project.group}:${task.project.name}:${task.project.version}" + (classifier == '' ? '' : ':' + classifier)
|
||||
public static String[] getClasspath(project, libs, artifact) {
|
||||
def ret = []
|
||||
artifactTree(project, artifact).each { key, lib ->
|
||||
libs[lib.name] = lib
|
||||
if (lib.name != artifact)
|
||||
ret.add(lib.name)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
public static def iso8601Now() { new Date().iso8601() }
|
||||
public static def getArtifacts(project, config) {
|
||||
def ret = [:]
|
||||
config.resolvedConfiguration.resolvedArtifacts.each { dep ->
|
||||
def info = getMavenInfoFromDep(dep)
|
||||
def url = "https://libraries.minecraft.net/$info.path"
|
||||
if (!checkExists(url))
|
||||
url = "https://maven.minecraftforge.net/$info.path"
|
||||
|
||||
ret[info.key] = [
|
||||
name: info.name,
|
||||
downloads: [
|
||||
artifact: [
|
||||
path: info.path,
|
||||
url: url,
|
||||
sha1: dep.file.sha1(),
|
||||
size: dep.file.length()
|
||||
]
|
||||
]
|
||||
]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
public static Map getMavenInfoFromDep(dep) {
|
||||
return getMavenInfoFromMap([
|
||||
group: dep.moduleVersion.id.group,
|
||||
name: dep.moduleVersion.id.name,
|
||||
version: dep.moduleVersion.id.version,
|
||||
classifier: dep.classifier,
|
||||
extension: dep.extension
|
||||
])
|
||||
}
|
||||
public static Map getMavenInfoFromTask(task) {
|
||||
return getMavenInfoFromMap([
|
||||
group: task.project.group,
|
||||
name: task.project.name,
|
||||
version: task.project.version,
|
||||
classifier: task.archiveClassifier.get(),
|
||||
extension: task.archiveExtension.get()
|
||||
])
|
||||
}
|
||||
|
||||
private static Map getMavenInfoFromMap(art) {
|
||||
def key = "$art.group:$art.name"
|
||||
def name = "$art.group:$art.name:$art.version"
|
||||
def path = "${art.group.replace('.', '/')}/$art.name/$art.version/$art.name-$art.version"
|
||||
if (art.classifier != null) {
|
||||
name += ":$art.classifier"
|
||||
path += "-$art.classifier"
|
||||
}
|
||||
if (!'jar'.equals(art.extension)) {
|
||||
name += "@$art.extension"
|
||||
path += ".$art.extension"
|
||||
} else {
|
||||
path += ".jar"
|
||||
}
|
||||
return [
|
||||
key: key,
|
||||
name: name,
|
||||
path: path,
|
||||
art: art
|
||||
]
|
||||
}
|
||||
|
||||
public static def sha1(file) {
|
||||
MessageDigest md = MessageDigest.getInstance('SHA-1')
|
||||
file.eachByte 4096, {bytes, size ->
|
||||
md.update(bytes, 0, size)
|
||||
}
|
||||
return md.digest().collect {String.format "%02x", it}.join()
|
||||
}
|
||||
public static def iso8601Now() { new Date().iso8601() }
|
||||
|
||||
private static def artifactTree(project, artifact) {
|
||||
if (!project.ext.has('tree_resolver'))
|
||||
project.ext.tree_resolver = 1
|
||||
def cfg = project.configurations.create('tree_resolver_' + project.ext.tree_resolver++)
|
||||
def dep = project.dependencies.create(artifact)
|
||||
cfg.dependencies.add(dep)
|
||||
def files = cfg.resolve()
|
||||
return getArtifacts(project, cfg, true)
|
||||
}
|
||||
public static def sha1(file) {
|
||||
MessageDigest md = MessageDigest.getInstance('SHA-1')
|
||||
file.eachByte 4096, {bytes, size ->
|
||||
md.update(bytes, 0, size)
|
||||
}
|
||||
return md.digest().collect {String.format "%02x", it}.join()
|
||||
}
|
||||
|
||||
static boolean checkExists(url) {
|
||||
try {
|
||||
def code = new URL(url).openConnection().with {
|
||||
requestMethod = 'HEAD'
|
||||
connect()
|
||||
responseCode
|
||||
}
|
||||
return code == 200
|
||||
} catch (Exception e) {
|
||||
if (e.toString().contains('unable to find valid certification path to requested target'))
|
||||
throw new RuntimeException('Failed to connect to ' + url + ': Missing certificate root authority, try updating java')
|
||||
throw e
|
||||
}
|
||||
}
|
||||
private static def artifactTree(project, artifact, transitive = true) {
|
||||
if (!project.ext.has('tree_resolver'))
|
||||
project.ext.tree_resolver = 1
|
||||
def cfg = project.configurations.create('tree_resolver_' + project.ext.tree_resolver++)
|
||||
cfg.transitive = transitive
|
||||
def dep = project.dependencies.create(artifact)
|
||||
cfg.dependencies.add(dep)
|
||||
def files = cfg.resolve()
|
||||
return getArtifacts(project, cfg)
|
||||
}
|
||||
|
||||
static boolean checkExists(url) {
|
||||
try {
|
||||
def code = new URL(url).openConnection().with {
|
||||
requestMethod = 'HEAD'
|
||||
connect()
|
||||
responseCode
|
||||
}
|
||||
return code == 200
|
||||
} catch (Exception e) {
|
||||
if (e.toString().contains('unable to find valid certification path to requested target'))
|
||||
throw new RuntimeException('Failed to connect to ' + url + ': Missing certificate root authority, try updating java')
|
||||
throw e
|
||||
}
|
||||
}
|
||||
|
||||
static String getLatestForgeVersion(mcVersion) {
|
||||
final json = new JsonSlurper().parseText(new URL('https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json').getText('UTF-8'))
|
||||
|
||||
+18
-4
@@ -1,11 +1,19 @@
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'eclipse'
|
||||
id 'net.minecraftforge.gradle.patcher' version '[6.0.16,6.2)'
|
||||
id 'org.cadixdev.licenser'
|
||||
id 'net.minecraftforge.gradle.patcher'
|
||||
}
|
||||
|
||||
evaluationDependsOn(':mcp')
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven gradleutils.forgeMaven
|
||||
maven gradleutils.minecraftLibsMaven
|
||||
}
|
||||
|
||||
java.toolchain.languageVersion = JavaLanguageVersion.of(JAVA_VERSION)
|
||||
|
||||
dependencies {
|
||||
implementation(libs.forgespi)
|
||||
}
|
||||
@@ -44,8 +52,14 @@ patcher {
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
|
||||
options.warnings = false // Shutup deprecated for removal warnings
|
||||
}
|
||||
|
||||
eclipse.classpath.file.whenMerged {
|
||||
// Disable optional warnings on the minecraft decompiled source code. It just muddies the warning window and hides warnings in our own codebase
|
||||
def src = entries.find { it.path == 'src/main/java' }
|
||||
src.entryAttributes['ignore_optional_problems'] = 'true'
|
||||
}
|
||||
if (src == null) throw new IllegalStateException("You must run `setup` task before importing")
|
||||
src.entryAttributes['ignore_optional_problems'] = 'true'
|
||||
}
|
||||
+1034
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,44 @@
|
||||
apply plugin: 'eclipse'
|
||||
|
||||
eclipse {
|
||||
classpath.file.whenMerged {
|
||||
def minecraft = entries.find { it.path == 'src/main/java' }
|
||||
if (minecraft == null) throw new IllegalStateException("You must run `setup` task before importing")
|
||||
// Disable optional warnings on the minecraft decompiled source code. It just muddies the warning window and hides warnings in our own codebase
|
||||
minecraft.entryAttributes['ignore_optional_problems'] = 'true'
|
||||
|
||||
def mods = [
|
||||
'src/main/java': 'minecraft',
|
||||
'src/main/resources': 'minecraft',
|
||||
'src/test/java': 'minecraft-test',
|
||||
'src/test/resources': 'minecraft-test',
|
||||
|
||||
'main-java': 'forge',
|
||||
'main-resources': 'forge',
|
||||
'generated': 'forge',
|
||||
|
||||
'test-java': 'forge-test',
|
||||
'test-resources': 'forge-test',
|
||||
'test-generated': 'forge-test',
|
||||
]
|
||||
|
||||
entries.forEach { e ->
|
||||
if ('src'.equals(e.kind) && e.hasProperty('output')) {
|
||||
if (mods.containsKey(e.path))
|
||||
e.output = 'bin/' + mods[e.path]
|
||||
//else
|
||||
// throw new IllegalStateException('Unknown path: ' + e.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Filter out the run directory
|
||||
project.resourceFilter {
|
||||
appliesTo = 'FOLDERS'
|
||||
type = 'EXCLUDE_ALL'
|
||||
matcher {
|
||||
id = 'org.eclipse.ui.ide.multiFilter'
|
||||
arguments = '1.0-name-matches-false-true-run'
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import net.minecraftforge.forge.tasks.CleanProperties
|
||||
|
||||
apply plugin: 'eclipse'
|
||||
apply plugin: 'idea'
|
||||
apply plugin: 'net.minecraftforge.gradleutils'
|
||||
|
||||
group = 'net.minecraftforge'
|
||||
version = VERSION
|
||||
println("Version: $version")
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven gradleutils.forgeMaven
|
||||
maven gradleutils.minecraftLibsMaven
|
||||
//mavenLocal()
|
||||
}
|
||||
|
||||
tasks.withType(Javadoc).configureEach {
|
||||
options.tags = [
|
||||
'apiNote:a:<em>API Note:</em>',
|
||||
'implSpec:a:<em>Implementation Requirements:</em>',
|
||||
'implNote:a:<em>Implementation Note:</em>'
|
||||
]
|
||||
options.addStringOption('Xdoclint:all,-missing', '-public')
|
||||
}
|
||||
|
||||
// We need to write the manifest to the binary file so we have properly versioned packaged at dev time.
|
||||
tasks.register('writeManifest') {
|
||||
doLast {
|
||||
if (plugins.findPlugin('net.minecraftforge.gradle.patcher')) // Forge project
|
||||
universalJar.manifest.writeTo(rootProject.file('src/main/resources/META-INF/MANIFEST.MF'))
|
||||
else
|
||||
jar.manifest.writeTo(project.file('src/main/resources/META-INF/MANIFEST.MF'))
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register('generateResources') {
|
||||
dependsOn('writeManifest')
|
||||
}
|
||||
|
||||
// Make sure out manifests get written before compiling the code, IDEA calls this task if you tell it to use the gradle build.
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
dependsOn(generateResources)
|
||||
dependsOn(processResources) // Needed because we merge the output of this with the output of the compile task. And gradle detects downstream tasks using the output without a hard dep
|
||||
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
|
||||
options.warnings = false // Shutup deprecated for removal warnings
|
||||
}
|
||||
|
||||
// Merge the resources and classes into the same directory. We'll need to split them at runtime because
|
||||
// Minecraft and Forge are in the same sourceSet as they are inter dependent.. for now..
|
||||
sourceSets.each {
|
||||
def dir = layout.buildDirectory.dir("classes/java/$it.name")
|
||||
it.output.resourcesDir = dir
|
||||
it.java.destinationDirectory = dir
|
||||
}
|
||||
|
||||
tasks.register('copyEclipseSettings') {
|
||||
doLast {
|
||||
rootProject.fileTree('ide/eclipse/template/.settings/').matching { include '**/*.prefs' }.each { file ->
|
||||
def target = project.file('.settings/' + file.name)
|
||||
def temp = new CleanProperties().load(file)
|
||||
def exst = new CleanProperties().load(target)
|
||||
exst.put('eclipse.preferences.version', '1')
|
||||
temp.forEach(exst::put)
|
||||
exst.store(target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: [Gradle][IntelliJ] Auto trigger these tasks on import.
|
||||
eclipse {
|
||||
// Run everytime eclipse builds the code
|
||||
//autoBuildTasks writeManifest
|
||||
// Run when importing the project
|
||||
synchronizationTasks generateResources, copyEclipseSettings, eclipseClasspath, eclipseProject
|
||||
}
|
||||
+37
-43
@@ -1,12 +1,13 @@
|
||||
import net.minecraftforge.gradleutils.PomUtils
|
||||
|
||||
plugins {
|
||||
id 'com.github.ben-manes.versions'
|
||||
id 'org.javamodularity.moduleplugin' version '1.8.3' apply false
|
||||
id 'org.cadixdev.licenser'
|
||||
id 'java-library'
|
||||
id 'jacoco'
|
||||
id 'maven-publish'
|
||||
id 'org.cadixdev.licenser'
|
||||
id 'net.minecraftforge.gradleutils'
|
||||
}
|
||||
|
||||
dependencyUpdates.rejectVersionIf { isNonStable(it.candidate.version) }
|
||||
apply from: rootProject.file('build_shared.gradle')
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.nulls)
|
||||
@@ -17,57 +18,50 @@ dependencies {
|
||||
implementation(libs.commons.io)
|
||||
}
|
||||
|
||||
task sourcesJar(type: Jar) {
|
||||
archiveClassifier = 'sources'
|
||||
from sourceSets.main.allSource
|
||||
java {
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(JAVA_VERSION)
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
ext {
|
||||
MANIFESTS = [
|
||||
'': [
|
||||
'FMLModType': 'LIBRARY',
|
||||
'Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"),
|
||||
'Git-Commit': GIT_INFO.abbreviatedId,
|
||||
'Git-Branch': GIT_INFO.branch,
|
||||
'Build-Number': "${System.getenv('BUILD_NUMBER')?:0}"
|
||||
] as LinkedHashMap,
|
||||
'net/minecraftforge/fml/loading/': [
|
||||
'Specification-Title': 'FMLLoader',
|
||||
tasks.named('jar', Jar).configure {
|
||||
manifest {
|
||||
attributes([
|
||||
'Automatic-Module-Name': 'net.minecraftforge.fmlcore',
|
||||
'FMLModType': 'LIBRARY'
|
||||
] as LinkedHashMap)
|
||||
attributes([
|
||||
'Specification-Title': 'FML',
|
||||
'Specification-Vendor': 'Forge Development LLC',
|
||||
'Specification-Version': '1',
|
||||
'Implementation-Title': 'FML Loader',
|
||||
'Implementation-Title': 'FML',
|
||||
'Implementation-Version': '1.0',
|
||||
'Implementation-Vendor': 'Forge'
|
||||
] as LinkedHashMap
|
||||
]
|
||||
}
|
||||
|
||||
jar.doFirst {
|
||||
manifest.attributes['Automatic-Module-Name'] = project.name
|
||||
MANIFESTS.each { pkg, values ->
|
||||
if (pkg == '')
|
||||
manifest.attributes(values)
|
||||
else
|
||||
manifest.attributes(values, pkg)
|
||||
'Implementation-Vendor': 'Forge Development LLC'
|
||||
] as LinkedHashMap, 'net/minecraftforge/fml/')
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.compilerArgs << '-Xlint:unchecked'
|
||||
options.compilerArgs << '-Xlint:-unchecked'
|
||||
}
|
||||
|
||||
license {
|
||||
header = rootProject.file('LICENSE-header.txt')
|
||||
|
||||
include 'net/minecraftforge/'
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives jar
|
||||
archives sourcesJar
|
||||
}
|
||||
|
||||
publishing.publications.mavenJava(MavenPublication) {
|
||||
from components.java
|
||||
artifact sourcesJar
|
||||
publishing {
|
||||
publications.register('mavenJava', MavenPublication).configure {
|
||||
from components.java
|
||||
artifactId = 'fmlcore'
|
||||
pom {
|
||||
name = project.name
|
||||
description = 'Modifactions to Minecraft to enable mod developers.'
|
||||
url = 'https://github.com/MinecraftForge/MinecraftForge'
|
||||
PomUtils.setGitHubDetails(pom, 'MinecraftForge')
|
||||
license PomUtils.Licenses.LGPLv2_1
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven gradleutils.publishingForgeMaven
|
||||
}
|
||||
}
|
||||
@@ -5,11 +5,12 @@
|
||||
|
||||
package net.minecraftforge.fml;
|
||||
|
||||
import cpw.mods.modlauncher.util.ServiceLoaderUtils;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.fml.config.IConfigEvent;
|
||||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.ServiceConfigurationError;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -19,10 +20,18 @@ public class Bindings {
|
||||
private final IBindingsProvider provider;
|
||||
|
||||
private Bindings() {
|
||||
final var providers = ServiceLoaderUtils.streamServiceLoader(()->ServiceLoader.load(FMLLoader.getGameLayer(), IBindingsProvider.class), sce->{}).toList();
|
||||
if (providers.size() != 1) {
|
||||
throw new IllegalStateException("Could not find bindings provider");
|
||||
var providers = new ArrayList<IBindingsProvider>(1);
|
||||
for (var itr = ServiceLoader.load(FMLLoader.getGameLayer(), IBindingsProvider.class).iterator(); itr.hasNext(); ) {
|
||||
try {
|
||||
providers.add(itr.next());
|
||||
} catch (ServiceConfigurationError sce) {
|
||||
sce.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
if (providers.size() != 1)
|
||||
throw new IllegalStateException("Could not find bindings provider: " + providers);
|
||||
|
||||
this.provider = providers.get(0);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ package net.minecraftforge.fml;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import net.minecraftforge.fml.loading.FMLConfig;
|
||||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
import net.minecraftforge.forgespi.language.IModInfo;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
@@ -35,81 +34,68 @@ import java.util.zip.GZIPInputStream;
|
||||
|
||||
import static net.minecraftforge.fml.VersionChecker.Status.*;
|
||||
|
||||
public class VersionChecker
|
||||
{
|
||||
public class VersionChecker {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static final int MAX_HTTP_REDIRECTS = Integer.getInteger("http.maxRedirects", 20);
|
||||
private static final int HTTP_TIMEOUT_SECS = Integer.getInteger("http.timeoutSecs", 15);
|
||||
|
||||
public enum Status
|
||||
{
|
||||
public enum Status {
|
||||
PENDING(),
|
||||
FAILED(),
|
||||
UP_TO_DATE(),
|
||||
OUTDATED(3, true),
|
||||
OUTDATED(3, true, true),
|
||||
AHEAD(),
|
||||
BETA(),
|
||||
BETA_OUTDATED(6, true);
|
||||
BETA_OUTDATED(6, true, true);
|
||||
|
||||
final int sheetOffset;
|
||||
final boolean draw, animated;
|
||||
|
||||
Status()
|
||||
{
|
||||
Status() {
|
||||
this(0, false, false);
|
||||
}
|
||||
|
||||
Status(int sheetOffset)
|
||||
{
|
||||
this(sheetOffset, true, false);
|
||||
}
|
||||
|
||||
Status(int sheetOffset, boolean animated)
|
||||
{
|
||||
this(sheetOffset, true, animated);
|
||||
}
|
||||
|
||||
Status(int sheetOffset, boolean draw, boolean animated)
|
||||
{
|
||||
Status(int sheetOffset, boolean draw, boolean animated) {
|
||||
this.sheetOffset = sheetOffset;
|
||||
this.draw = draw;
|
||||
this.animated = animated;
|
||||
}
|
||||
|
||||
public int getSheetOffset()
|
||||
{
|
||||
public int getSheetOffset() {
|
||||
return sheetOffset;
|
||||
}
|
||||
|
||||
public boolean shouldDraw()
|
||||
{
|
||||
public boolean shouldDraw() {
|
||||
return draw;
|
||||
}
|
||||
|
||||
public boolean isAnimated()
|
||||
{
|
||||
public boolean isAnimated() {
|
||||
return animated;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public record CheckResult(VersionChecker.Status status, ComparableVersion target, Map<ComparableVersion, String> changes, String url) {}
|
||||
|
||||
public static void startVersionCheck()
|
||||
{
|
||||
new Thread("Forge Version Check")
|
||||
{
|
||||
public static void startVersionCheck() {
|
||||
new Thread("Forge Version Check") {
|
||||
private HttpClient client;
|
||||
private String mcVersion;
|
||||
private String agent;
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
if (!FMLConfig.getBoolConfigValue(FMLConfig.ConfigValue.VERSION_CHECK))
|
||||
{
|
||||
public void run() {
|
||||
if (!FMLConfig.getBoolConfigValue(FMLConfig.ConfigValue.VERSION_CHECK)) {
|
||||
LOGGER.info("Global Forge version check system disabled, no further processing.");
|
||||
return;
|
||||
}
|
||||
|
||||
var mc = ModList.get().getModContainerById("minecraft");
|
||||
var forge = ModList.get().getModContainerById("forge");
|
||||
this.mcVersion = mc.get().getModInfo().getVersion().toString();
|
||||
|
||||
this.agent = "Java-http-client/" + System.getProperty("java.version")
|
||||
+ " MinecraftForge/" + mcVersion + '-' + forge.get().getModInfo().getVersion();
|
||||
|
||||
client = HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(HTTP_TIMEOUT_SECS)).build();
|
||||
gatherMods().forEach(this::process);
|
||||
}
|
||||
@@ -119,15 +105,9 @@ public class VersionChecker
|
||||
*/
|
||||
private String openUrlString(URL url, IModInfo mod) throws IOException, URISyntaxException, InterruptedException {
|
||||
URL currentUrl = url;
|
||||
var userAgent = this.agent + ' ' + mod.getModId() + '/' + mod.getVersion();
|
||||
|
||||
final StringBuilder sb = new StringBuilder();
|
||||
sb.append("Java-http-client/").append(System.getProperty("java.version")).append(' ');
|
||||
sb.append("MinecraftForge/").append(FMLLoader.versionInfo().mcAndForgeVersion()).append(' ');
|
||||
sb.append(mod.getModId()).append('/').append(mod.getVersion());
|
||||
final String userAgent = sb.toString();
|
||||
|
||||
for (int redirects = 0; redirects < MAX_HTTP_REDIRECTS; redirects++)
|
||||
{
|
||||
for (int redirects = 0; redirects < MAX_HTTP_REDIRECTS; redirects++) {
|
||||
var request = HttpRequest.newBuilder()
|
||||
.uri(currentUrl.toURI())
|
||||
.timeout(Duration.ofSeconds(HTTP_TIMEOUT_SECS))
|
||||
@@ -139,8 +119,7 @@ public class VersionChecker
|
||||
final HttpResponse<InputStream> response = client.send(request, HttpResponse.BodyHandlers.ofInputStream());
|
||||
|
||||
int responseCode = response.statusCode();
|
||||
if (responseCode >= 300 && responseCode <= 399)
|
||||
{
|
||||
if (responseCode >= 300 && responseCode <= 399) {
|
||||
String newLocation = response.headers().firstValue("Location")
|
||||
.orElseThrow(() -> new IOException("Got a 3xx response code but Location header was null while trying to fetch " + url));
|
||||
currentUrl = new URL(currentUrl, newLocation);
|
||||
@@ -150,26 +129,22 @@ public class VersionChecker
|
||||
final boolean isGzipEncoded = response.headers().firstValue("Content-Encoding").orElse("").equals("gzip");
|
||||
|
||||
final String bodyStr;
|
||||
try (InputStream inStream = isGzipEncoded ? new GZIPInputStream(response.body()) : response.body())
|
||||
{
|
||||
try (var bufferedReader = new BufferedReader(new InputStreamReader(inStream)))
|
||||
{
|
||||
bodyStr = bufferedReader.lines().collect(Collectors.joining("\n"));
|
||||
}
|
||||
try (var inStream = isGzipEncoded ? new GZIPInputStream(response.body()) : response.body();
|
||||
var bufferedReader = new BufferedReader(new InputStreamReader(inStream))
|
||||
) {
|
||||
bodyStr = bufferedReader.lines().collect(Collectors.joining("\n"));
|
||||
}
|
||||
return bodyStr;
|
||||
}
|
||||
throw new IOException("Too many redirects while trying to fetch " + url);
|
||||
}
|
||||
|
||||
private void process(IModInfo mod)
|
||||
{
|
||||
private void process(IModInfo mod) {
|
||||
Status status = PENDING;
|
||||
ComparableVersion target = null;
|
||||
Map<ComparableVersion, String> changes = null;
|
||||
String display_url = null;
|
||||
try
|
||||
{
|
||||
try {
|
||||
if (mod.getUpdateURL().isEmpty()) return;
|
||||
URL url = mod.getUpdateURL().get();
|
||||
LOGGER.info("[{}] Starting version check at {}", mod.getModId(), url.toString());
|
||||
@@ -178,54 +153,40 @@ public class VersionChecker
|
||||
|
||||
LOGGER.debug("[{}] Received version check data:\n{}", mod.getModId(), data);
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> json = new Gson().fromJson(data, Map.class);
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, String> promos = (Map<String, String>)json.get("promos");
|
||||
display_url = (String)json.get("homepage");
|
||||
|
||||
var mcVersion = FMLLoader.versionInfo().mcVersion();
|
||||
String rec = promos.get(mcVersion + "-recommended");
|
||||
String lat = promos.get(mcVersion + "-latest");
|
||||
ComparableVersion current = new ComparableVersion(mod.getVersion().toString());
|
||||
|
||||
if (rec != null)
|
||||
{
|
||||
if (rec != null) {
|
||||
ComparableVersion recommended = new ComparableVersion(rec);
|
||||
int diff = recommended.compareTo(current);
|
||||
|
||||
if (diff == 0)
|
||||
status = UP_TO_DATE;
|
||||
else if (diff < 0)
|
||||
{
|
||||
else if (diff < 0) {
|
||||
status = AHEAD;
|
||||
if (lat != null)
|
||||
{
|
||||
if (lat != null) {
|
||||
ComparableVersion latest = new ComparableVersion(lat);
|
||||
if (current.compareTo(latest) < 0)
|
||||
{
|
||||
if (current.compareTo(latest) < 0) {
|
||||
status = OUTDATED;
|
||||
target = latest;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
status = OUTDATED;
|
||||
target = recommended;
|
||||
}
|
||||
}
|
||||
else if (lat != null)
|
||||
{
|
||||
} else if (lat != null) {
|
||||
ComparableVersion latest = new ComparableVersion(lat);
|
||||
if (current.compareTo(latest) < 0)
|
||||
status = BETA_OUTDATED;
|
||||
else
|
||||
status = BETA;
|
||||
status = current.compareTo(latest) < 0 ? BETA_OUTDATED : BETA;
|
||||
target = latest;
|
||||
}
|
||||
else
|
||||
} else
|
||||
status = BETA;
|
||||
|
||||
LOGGER.info("[{}] Found status: {} Current: {} Target: {}", mod.getModId(), status, current, target);
|
||||
@@ -233,27 +194,20 @@ public class VersionChecker
|
||||
changes = new LinkedHashMap<>();
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, String> tmp = (Map<String, String>)json.get(mcVersion);
|
||||
if (tmp != null)
|
||||
{
|
||||
if (tmp != null) {
|
||||
List<ComparableVersion> ordered = new ArrayList<>();
|
||||
for (String key : tmp.keySet())
|
||||
{
|
||||
for (String key : tmp.keySet()) {
|
||||
ComparableVersion ver = new ComparableVersion(key);
|
||||
if (ver.compareTo(current) > 0 && (target == null || ver.compareTo(target) < 1))
|
||||
{
|
||||
ordered.add(ver);
|
||||
}
|
||||
}
|
||||
Collections.sort(ordered);
|
||||
|
||||
for (ComparableVersion ver : ordered)
|
||||
{
|
||||
for (ComparableVersion ver : ordered) {
|
||||
changes.put(ver, tmp.get(ver.toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
} catch (Exception e) {
|
||||
LOGGER.warn("Failed to process update information", e);
|
||||
status = FAILED;
|
||||
}
|
||||
@@ -263,8 +217,7 @@ public class VersionChecker
|
||||
}
|
||||
|
||||
// Gather a list of mods that have opted in to this update system by providing a URL.
|
||||
private static List<IModInfo> gatherMods()
|
||||
{
|
||||
private static List<IModInfo> gatherMods() {
|
||||
List<IModInfo> ret = new LinkedList<>();
|
||||
for (IModInfo info : ModList.get().getMods()) {
|
||||
if (info.getUpdateURL().isPresent())
|
||||
@@ -276,9 +229,7 @@ public class VersionChecker
|
||||
private static Map<IModInfo, CheckResult> results = new ConcurrentHashMap<>();
|
||||
private static final CheckResult PENDING_CHECK = new CheckResult(PENDING, null, null, null);
|
||||
|
||||
public static CheckResult getResult(IModInfo mod)
|
||||
{
|
||||
public static CheckResult getResult(IModInfo mod) {
|
||||
return results.getOrDefault(mod, PENDING_CHECK);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
import net.minecraftforge.gradleutils.PomUtils
|
||||
|
||||
plugins {
|
||||
id 'org.javamodularity.moduleplugin' version '1.8.7' apply false
|
||||
id 'org.cadixdev.licenser'
|
||||
id 'java-library'
|
||||
id 'jacoco'
|
||||
id 'maven-publish'
|
||||
id 'org.cadixdev.licenser'
|
||||
id 'net.minecraftforge.gradleutils'
|
||||
}
|
||||
|
||||
apply from: rootProject.file('build_shared.gradle')
|
||||
|
||||
import org.gradle.internal.os.OperatingSystem
|
||||
switch (OperatingSystem.current()) {
|
||||
case OperatingSystem.LINUX:
|
||||
@@ -18,7 +22,10 @@ switch (OperatingSystem.current()) {
|
||||
break
|
||||
}
|
||||
|
||||
java.withSourcesJar()
|
||||
java {
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(JAVA_VERSION)
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.nulls)
|
||||
@@ -44,12 +51,16 @@ test {
|
||||
jar {
|
||||
manifest {
|
||||
attributes([
|
||||
'Specification-Title': 'FMLEarlyDisplay',
|
||||
'Automatic-Module-Name': 'net.minecraftforge.earlydisplay',
|
||||
'Forge-Module-Layer': 'boot'
|
||||
] as LinkedHashMap)
|
||||
attributes([
|
||||
'Specification-Title': 'FML Early Display',
|
||||
'Specification-Vendor': 'Forge Development LLC',
|
||||
'Specification-Version': '1',
|
||||
'Implementation-Title': 'FML Early Display',
|
||||
'Implementation-Version': '1.0',
|
||||
'Implementation-Vendor': 'Forge'
|
||||
'Implementation-Vendor': 'Forge Development LLC',
|
||||
'Implementation-Version': '1.0'
|
||||
] as LinkedHashMap, 'net/minecraftforge/fml/earlydisplay/')
|
||||
}
|
||||
}
|
||||
@@ -60,9 +71,22 @@ tasks.withType(JavaCompile) {
|
||||
|
||||
license {
|
||||
header = rootProject.file('LICENSE-header.txt')
|
||||
include 'net/minecraftforge/'
|
||||
}
|
||||
|
||||
publishing.publications.mavenJava(MavenPublication) {
|
||||
from components.java
|
||||
publishing {
|
||||
publications.register('mavenJava', MavenPublication).configure {
|
||||
from components.java
|
||||
artifactId = 'fmlearlydisplay'
|
||||
pom {
|
||||
name = project.name
|
||||
description = 'A pretty looking, but prone to erroring screen displayed during Forge loading.'
|
||||
url = 'https://github.com/MinecraftForge/MinecraftForge'
|
||||
PomUtils.setGitHubDetails(pom, 'MinecraftForge')
|
||||
license PomUtils.Licenses.LGPLv2_1
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven gradleutils.publishingForgeMaven
|
||||
}
|
||||
}
|
||||
+13
-11
@@ -114,11 +114,13 @@ public class DisplayWindow implements ImmediateWindowProvider {
|
||||
public String name() {
|
||||
return "fmlearlywindow";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Runnable initialize(String[] arguments) {
|
||||
String mcVersion = FMLLoader.versionInfo().mcVersion();
|
||||
String forgeVersion = FMLLoader.versionInfo().forgeVersion();
|
||||
|
||||
final OptionParser parser = new OptionParser();
|
||||
var mcversionopt = parser.accepts("fml.mcVersion").withRequiredArg().ofType(String.class);
|
||||
var forgeversionopt = parser.accepts("fml.forgeVersion").withRequiredArg().ofType(String.class);
|
||||
var widthopt = parser.accepts("width")
|
||||
.withRequiredArg().ofType(Integer.class)
|
||||
.defaultsTo(FMLConfig.getIntConfigValue(FMLConfig.ConfigValue.EARLY_WINDOW_WIDTH));
|
||||
@@ -133,7 +135,7 @@ public class DisplayWindow implements ImmediateWindowProvider {
|
||||
FMLConfig.updateConfig(FMLConfig.ConfigValue.EARLY_WINDOW_WIDTH, winWidth);
|
||||
FMLConfig.updateConfig(FMLConfig.ConfigValue.EARLY_WINDOW_HEIGHT, winHeight);
|
||||
fbScale = FMLConfig.getIntConfigValue(FMLConfig.ConfigValue.EARLY_WINDOW_FBSCALE);
|
||||
if (System.getenv("FML_EARLY_WINDOW_DARK")!= null) {
|
||||
if (System.getenv("FML_EARLY_WINDOW_DARK") != null) {
|
||||
this.colourScheme = ColourScheme.BLACK;
|
||||
} else {
|
||||
try {
|
||||
@@ -148,10 +150,9 @@ public class DisplayWindow implements ImmediateWindowProvider {
|
||||
}
|
||||
this.maximized = parsed.has(maximizedopt) || FMLConfig.getBoolConfigValue(FMLConfig.ConfigValue.EARLY_WINDOW_MAXIMIZED);
|
||||
|
||||
var forgeVersion = parsed.valueOf(forgeversionopt);
|
||||
StartupNotificationManager.modLoaderConsumer().ifPresent(c->c.accept("Forge loading "+ forgeVersion));
|
||||
StartupNotificationManager.modLoaderConsumer().ifPresent(c->c.accept("Forge loading " + forgeVersion));
|
||||
performanceInfo = new PerformanceInfo();
|
||||
return start(parsed.valueOf(mcversionopt), forgeVersion);
|
||||
return start(mcVersion, forgeVersion);
|
||||
}
|
||||
|
||||
private static final long MINFRAMETIME = TimeUnit.MILLISECONDS.toNanos(10); // This is the FPS cap on the window - note animation is capped at 20FPS via the tickTimer
|
||||
@@ -229,11 +230,11 @@ public class DisplayWindow implements ImmediateWindowProvider {
|
||||
crashElegantly("An error occurred initializing a font for rendering. "+t.getMessage());
|
||||
}
|
||||
this.elements = new ArrayList<>(Arrays.asList(
|
||||
RenderElement.anvil(font),
|
||||
RenderElement.logMessageOverlay(font),
|
||||
RenderElement.forgeVersionOverlay(font, mcVersion+"-"+forgeVersion.split("-")[0]),
|
||||
RenderElement.performanceBar(font),
|
||||
RenderElement.progressBars(font)
|
||||
RenderElement.anvil(font),
|
||||
RenderElement.logMessageOverlay(font),
|
||||
RenderElement.forgeVersionOverlay(font, mcVersion + "-" + forgeVersion),
|
||||
RenderElement.performanceBar(font),
|
||||
RenderElement.progressBars(font)
|
||||
));
|
||||
|
||||
var date = Calendar.getInstance();
|
||||
@@ -285,6 +286,7 @@ public class DisplayWindow implements ImmediateWindowProvider {
|
||||
public Runnable start(@Nullable String mcVersion, final String forgeVersion) {
|
||||
renderScheduler = Executors.newSingleThreadScheduledExecutor(r -> {
|
||||
final var thread = Executors.defaultThreadFactory().newThread(r);
|
||||
thread.setName("EarlyDisplay");
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
});
|
||||
|
||||
+53
-34
@@ -1,12 +1,14 @@
|
||||
import net.minecraftforge.gradleutils.PomUtils
|
||||
|
||||
plugins {
|
||||
id 'com.github.ben-manes.versions'
|
||||
id 'org.javamodularity.moduleplugin' version '1.8.3' apply false
|
||||
id 'org.cadixdev.licenser'
|
||||
id 'java-library'
|
||||
id 'jacoco'
|
||||
id 'maven-publish'
|
||||
id 'org.cadixdev.licenser'
|
||||
id 'net.minecraftforge.gradleutils'
|
||||
}
|
||||
|
||||
dependencyUpdates.rejectVersionIf { isNonStable(it.candidate.version) }
|
||||
apply from: rootProject.file('build_shared.gradle')
|
||||
|
||||
configurations.forEach{ it.transitive = false }
|
||||
|
||||
dependencies {
|
||||
@@ -41,28 +43,25 @@ dependencies {
|
||||
testRuntimeOnly(libs.bootstrap)
|
||||
}
|
||||
|
||||
task sourcesJar(type: Jar) {
|
||||
archiveClassifier = 'sources'
|
||||
from sourceSets.main.allSource
|
||||
java {
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(JAVA_VERSION)
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
ext {
|
||||
MANIFESTS = [
|
||||
'net/minecraftforge/fml/loading/': [
|
||||
'Specification-Title': 'FMLLoader',
|
||||
'Specification-Vendor': 'Forge Development LLC',
|
||||
'Specification-Version': '1',
|
||||
'Implementation-Title': 'FML Loader',
|
||||
'Implementation-Version': '1.0',
|
||||
'Implementation-Vendor': 'Forge'
|
||||
] as LinkedHashMap
|
||||
]
|
||||
}
|
||||
|
||||
jar.doFirst {
|
||||
manifest.attributes['Automatic-Module-Name'] = project.name
|
||||
MANIFESTS.each { pkg, values ->
|
||||
manifest.attributes(values, pkg)
|
||||
tasks.named('jar', Jar).configure {
|
||||
manifest {
|
||||
attributes([
|
||||
'Automatic-Module-Name': 'net.minecraftforge.fmlloader',
|
||||
'Forge-Module-Layer': 'boot'
|
||||
] as LinkedHashMap)
|
||||
attributes([
|
||||
'Specification-Title': 'FMLLoader',
|
||||
'Specification-Vendor': 'Forge Development LLC',
|
||||
'Specification-Version': '1',
|
||||
'Implementation-Title': 'FMLLoader',
|
||||
'Implementation-Vendor': 'Forge Development LLC',
|
||||
'Implementation-Version': FORGE_VERSION
|
||||
] as LinkedHashMap, 'net/minecraftforge/fml/loading/')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,16 +71,36 @@ tasks.withType(JavaCompile) {
|
||||
|
||||
license {
|
||||
header = rootProject.file('LICENSE-header.txt')
|
||||
|
||||
include 'net/minecraftforge/'
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives jar
|
||||
archives sourcesJar
|
||||
publishing {
|
||||
publications.register('mavenJava', MavenPublication).configure {
|
||||
from components.java
|
||||
artifactId = 'fmlloader'
|
||||
pom {
|
||||
name = project.name
|
||||
description = 'Modifactions to Minecraft to enable mod developers.'
|
||||
url = 'https://github.com/MinecraftForge/MinecraftForge'
|
||||
PomUtils.setGitHubDetails(pom, 'MinecraftForge')
|
||||
license PomUtils.Licenses.LGPLv2_1
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven gradleutils.publishingForgeMaven
|
||||
}
|
||||
}
|
||||
|
||||
publishing.publications.mavenJava(MavenPublication) {
|
||||
from components.java
|
||||
artifact sourcesJar
|
||||
}
|
||||
tasks.register('writeForgeVersionJson') {
|
||||
doLast {
|
||||
file('src/main/resources/forge_version.json').json = [
|
||||
'forge': FORGE_VERSION,
|
||||
'mc': MC_VERSION,
|
||||
'mcp': MCP_VERSION
|
||||
] as LinkedHashMap
|
||||
}
|
||||
}
|
||||
|
||||
tasks.named('generateResources').configure {
|
||||
dependsOn('writeForgeVersionJson')
|
||||
}
|
||||
-61
@@ -1,61 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading;
|
||||
|
||||
import cpw.mods.modlauncher.api.NamedPath;
|
||||
import cpw.mods.modlauncher.serviceapi.ITransformerDiscoveryService;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.*;
|
||||
|
||||
public class ClasspathTransformerDiscoverer implements ITransformerDiscoveryService {
|
||||
|
||||
private final List<Path> legacyClasspath = Arrays.stream(System.getProperty("legacyClassPath", "").split(File.pathSeparator)).map(Path::of).toList();
|
||||
|
||||
@Override
|
||||
public List<NamedPath> candidates(Path gameDirectory) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NamedPath> candidates(final Path gameDirectory, final String launchTarget) {
|
||||
if (launchTarget != null && launchTarget.contains("dev")) {
|
||||
this.scan(gameDirectory);
|
||||
}
|
||||
return List.copyOf(found);
|
||||
}
|
||||
|
||||
private final static List<NamedPath> found = new ArrayList<>();
|
||||
|
||||
public static List<Path> allExcluded() {
|
||||
return found.stream().map(np->np.paths()[0]).toList();
|
||||
}
|
||||
|
||||
private void scan(final Path gameDirectory) {
|
||||
try {
|
||||
locateTransformers("META-INF/services/cpw.mods.modlauncher.api.ITransformationService");
|
||||
locateTransformers("META-INF/services/net.minecraftforge.forgespi.locating.IModLocator");
|
||||
} catch (IOException e) {
|
||||
LogManager.getLogger().error("Error during discovery of transform services from the classpath", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void locateTransformers(String resource) throws IOException {
|
||||
final Enumeration<URL> resources = ClassLoader.getSystemClassLoader().getResources(resource);
|
||||
while (resources.hasMoreElements()) {
|
||||
URL url = resources.nextElement();
|
||||
Path path = ClasspathLocatorUtils.findJarPathFor(resource, url.toString(), url);
|
||||
if (legacyClasspath.stream().anyMatch(path::equals) || !Files.exists(path) || Files.isDirectory(path))
|
||||
continue;
|
||||
found.add(new NamedPath(path.toUri().toString(), path));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,6 @@ package net.minecraftforge.fml.loading;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import cpw.mods.modlauncher.Launcher;
|
||||
import cpw.mods.modlauncher.api.*;
|
||||
import cpw.mods.modlauncher.serviceapi.ILaunchPluginService;
|
||||
import cpw.mods.modlauncher.util.ServiceLoaderUtils;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.BackgroundScanHandler;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModDiscoverer;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
|
||||
@@ -24,27 +22,29 @@ import org.slf4j.Logger;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.ServiceConfigurationError;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static net.minecraftforge.fml.loading.LogMarkers.CORE;
|
||||
import static net.minecraftforge.fml.loading.LogMarkers.SCAN;
|
||||
|
||||
public class FMLLoader
|
||||
{
|
||||
public class FMLLoader {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private static AccessTransformerService accessTransformer;
|
||||
private static ModDiscoverer modDiscoverer;
|
||||
private static ICoreModProvider coreModProvider;
|
||||
private static ILaunchPluginService eventBus;
|
||||
private static LanguageLoadingProvider languageLoadingProvider;
|
||||
private static Dist dist;
|
||||
private static String naming;
|
||||
private static LoadingModList loadingModList;
|
||||
private static RuntimeDistCleaner runtimeDistCleaner;
|
||||
private static Path gamePath;
|
||||
private static VersionInfo versionInfo;
|
||||
private static VersionInfo versionInfo = VersionInfo.detect();
|
||||
private static String launchHandlerName;
|
||||
private static CommonLaunchHandler commonLaunchHandler;
|
||||
public static Runnable progressWindowTick;
|
||||
@@ -53,80 +53,74 @@ public class FMLLoader
|
||||
private static boolean production;
|
||||
private static IModuleLayerManager moduleLayerManager;
|
||||
|
||||
static void onInitialLoad(IEnvironment environment, Set<String> otherServices) throws IncompatibleEnvironmentException
|
||||
{
|
||||
final String version = LauncherVersion.getVersion();
|
||||
LOGGER.debug(CORE,"FML {} loading", version);
|
||||
final Package modLauncherPackage = ITransformationService.class.getPackage();
|
||||
LOGGER.debug(CORE,"FML found ModLauncher version : {}", modLauncherPackage.getImplementationVersion());
|
||||
if (!modLauncherPackage.isCompatibleWith("4.0")) {
|
||||
LOGGER.error(CORE, "Found incompatible ModLauncher specification : {}, version {} from {}", modLauncherPackage.getSpecificationVersion(), modLauncherPackage.getImplementationVersion(), modLauncherPackage.getImplementationVendor());
|
||||
throw new IncompatibleEnvironmentException("Incompatible modlauncher found "+modLauncherPackage.getSpecificationVersion());
|
||||
}
|
||||
static void onInitialLoad(IEnvironment env, Set<String> otherServices) throws IncompatibleEnvironmentException {
|
||||
LOGGER.debug(CORE, "Detected version data : {}", versionInfo);
|
||||
LOGGER.debug(CORE, "FML {} loading", LauncherVersion.getVersion());
|
||||
|
||||
accessTransformer = (AccessTransformerService) environment.findLaunchPlugin("accesstransformer").orElseThrow(()-> {
|
||||
LOGGER.error(CORE, "Access Transformer library is missing, we need this to run");
|
||||
return new IncompatibleEnvironmentException("Missing AccessTransformer, cannot run");
|
||||
});
|
||||
|
||||
final Package atPackage = accessTransformer.getClass().getPackage();
|
||||
LOGGER.debug(CORE,"FML found AccessTransformer version : {}", atPackage.getImplementationVersion());
|
||||
if (!atPackage.isCompatibleWith("1.0")) {
|
||||
LOGGER.error(CORE, "Found incompatible AccessTransformer specification : {}, version {} from {}", atPackage.getSpecificationVersion(), atPackage.getImplementationVersion(), atPackage.getImplementationVendor());
|
||||
throw new IncompatibleEnvironmentException("Incompatible accesstransformer found "+atPackage.getSpecificationVersion());
|
||||
}
|
||||
|
||||
eventBus = environment.findLaunchPlugin("eventbus").orElseThrow(()-> {
|
||||
LOGGER.error(CORE, "Event Bus library is missing, we need this to run");
|
||||
return new IncompatibleEnvironmentException("Missing EventBus, cannot run");
|
||||
});
|
||||
|
||||
final Package eventBusPackage = eventBus.getClass().getPackage();
|
||||
LOGGER.debug(CORE,"FML found EventBus version : {}", eventBusPackage.getImplementationVersion());
|
||||
if (!eventBusPackage.isCompatibleWith("1.0")) {
|
||||
LOGGER.error(CORE, "Found incompatible EventBus specification : {}, version {} from {}", eventBusPackage.getSpecificationVersion(), eventBusPackage.getImplementationVersion(), eventBusPackage.getImplementationVendor());
|
||||
throw new IncompatibleEnvironmentException("Incompatible eventbus found "+eventBusPackage.getSpecificationVersion());
|
||||
}
|
||||
|
||||
runtimeDistCleaner = (RuntimeDistCleaner)environment.findLaunchPlugin("runtimedistcleaner").orElseThrow(()-> {
|
||||
LOGGER.error(CORE, "Dist Cleaner is missing, we need this to run");
|
||||
return new IncompatibleEnvironmentException("Missing DistCleaner, cannot run!");
|
||||
});
|
||||
LOGGER.debug(CORE, "Found Runtime Dist Cleaner");
|
||||
|
||||
var coreModProviders = ServiceLoaderUtils.streamWithErrorHandling(ServiceLoader.load(FMLLoader.class.getModule().getLayer(), ICoreModProvider.class), sce -> LOGGER.error(CORE, "Failed to load a coremod library, expect problems", sce)).toList();
|
||||
|
||||
if (coreModProviders.isEmpty()) {
|
||||
LOGGER.error(CORE, "Found no coremod provider. Cannot run");
|
||||
throw new IncompatibleEnvironmentException("No coremod library found");
|
||||
} else if (coreModProviders.size() > 1) {
|
||||
LOGGER.error(CORE, "Found multiple coremod providers : {}. Cannot run", coreModProviders.stream().map(p -> p.getClass().getName()).collect(Collectors.toList()));
|
||||
throw new IncompatibleEnvironmentException("Multiple coremod libraries found");
|
||||
}
|
||||
|
||||
coreModProvider = coreModProviders.get(0);
|
||||
final Package coremodPackage = coreModProvider.getClass().getPackage();
|
||||
LOGGER.debug(CORE,"FML found CoreMod version : {}", coremodPackage.getImplementationVersion());
|
||||
|
||||
|
||||
LOGGER.debug(CORE, "Found ForgeSPI package implementation version {}", Environment.class.getPackage().getImplementationVersion());
|
||||
LOGGER.debug(CORE, "Found ForgeSPI package specification {}", Environment.class.getPackage().getSpecificationVersion());
|
||||
if (Integer.parseInt(Environment.class.getPackage().getSpecificationVersion()) < 2) {
|
||||
LOGGER.error(CORE, "Found an out of date ForgeSPI implementation: {}, loading cannot continue", Environment.class.getPackage().getSpecificationVersion());
|
||||
throw new IncompatibleEnvironmentException("ForgeSPI is out of date, we cannot continue");
|
||||
}
|
||||
checkPackage(ITransformationService.class, "4.0", "ModLauncher");
|
||||
accessTransformer = getPlugin(env, "accesstransformer", "1.0", "AccessTransformer");
|
||||
/*eventBus =*/ getPlugin(env, "eventbus", "1.0", "EventBus");
|
||||
runtimeDistCleaner = getPlugin(env, "runtimedistcleaner", "1.0", "RuntimeDistCleaner");
|
||||
coreModProvider = getSingleService(ICoreModProvider.class, "CoreMod");
|
||||
LOGGER.debug(CORE,"FML found CoreMod version : {}", JarVersionLookupHandler.getInfo(coreModProvider.getClass()).impl().version().orElse("MISSING"));
|
||||
checkPackage(Environment.class, "2.0", "ForgeSPI");
|
||||
|
||||
try {
|
||||
Class.forName("com.electronwill.nightconfig.core.Config", false, environment.getClass().getClassLoader());
|
||||
Class.forName("com.electronwill.nightconfig.toml.TomlFormat", false, environment.getClass().getClassLoader());
|
||||
Class.forName("com.electronwill.nightconfig.core.Config", false, env.getClass().getClassLoader());
|
||||
Class.forName("com.electronwill.nightconfig.toml.TomlFormat", false, env.getClass().getClassLoader());
|
||||
} catch (ClassNotFoundException e) {
|
||||
LOGGER.error(CORE, "Failed to load NightConfig");
|
||||
throw new IncompatibleEnvironmentException("Missing NightConfig");
|
||||
}
|
||||
}
|
||||
|
||||
static void setupLaunchHandler(final IEnvironment environment, final Map<String, Object> arguments)
|
||||
{
|
||||
private static <T> T getPlugin(IEnvironment env, String id, String version, String name) throws IncompatibleEnvironmentException {
|
||||
@SuppressWarnings("unchecked")
|
||||
var plugin = (T)env.findLaunchPlugin(id).orElseThrow(() -> {
|
||||
LOGGER.error(CORE, "{} library is missing, we need this to run", name);
|
||||
return new IncompatibleEnvironmentException("Missing " + name + ", cannot run");
|
||||
});
|
||||
checkPackage(plugin.getClass(), version, name);
|
||||
return plugin;
|
||||
}
|
||||
|
||||
private static void checkPackage(Class<?> cls, String version, String name) throws IncompatibleEnvironmentException {
|
||||
var pkg = cls.getPackage();
|
||||
var info = JarVersionLookupHandler.getInfo(pkg);
|
||||
LOGGER.debug(CORE, "Found {} version: {}", name, info.impl().version().orElse("MISSING"));
|
||||
|
||||
if (!pkg.isCompatibleWith(version)) {
|
||||
LOGGER.error(CORE, "Found incompatible {} specification: {}, version {} from {}", name,
|
||||
info.spec().version().orElse("MISSING"),
|
||||
info.impl().version().orElse("MISSING"),
|
||||
info.impl().vendor().orElse("MISSING")
|
||||
);
|
||||
throw new IncompatibleEnvironmentException("Incompatible " + name + " found " + info.spec().version().orElse("MISSING"));
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> T getSingleService(Class<T> clazz, String name) throws IncompatibleEnvironmentException {
|
||||
var providers = new ArrayList<T>();
|
||||
for (var itr = ServiceLoader.load(FMLLoader.class.getModule().getLayer(), clazz).iterator(); itr.hasNext(); ) {
|
||||
try {
|
||||
providers.add(itr.next());
|
||||
} catch (ServiceConfigurationError e) {
|
||||
LOGGER.error(CORE, "Failed to load a " + name + " library, expect problems", e);
|
||||
}
|
||||
}
|
||||
|
||||
if (providers.isEmpty()) {
|
||||
LOGGER.error(CORE, "Found no {} provider. Cannot run", name);
|
||||
throw new IncompatibleEnvironmentException("No " + name + " library found");
|
||||
} else if (providers.size() > 1) {
|
||||
LOGGER.error(CORE, "Found multiple {} providers: {}. Cannot run", name, providers.stream().map(p -> p.getClass().getName()).toList());
|
||||
throw new IncompatibleEnvironmentException("Multiple " + name + " libraries found");
|
||||
}
|
||||
|
||||
return providers.get(0);
|
||||
}
|
||||
|
||||
static void setupLaunchHandler(final IEnvironment environment, final Map<String, Object> arguments) {
|
||||
final String launchTarget = environment.getProperty(IEnvironment.Keys.LAUNCHTARGET.get()).orElse("MISSING");
|
||||
arguments.put("launchTarget", launchTarget);
|
||||
final Optional<ILaunchHandlerService> launchHandler = environment.findLaunchHandler(launchTarget);
|
||||
@@ -136,7 +130,7 @@ public class FMLLoader
|
||||
throw new RuntimeException("Missing launch handler: " + launchTarget);
|
||||
}
|
||||
|
||||
// TODO: What the fuck is the point of using a service if you require a specific concrete class
|
||||
// TODO: [FML][Loader] What the fuck is the point of using a service if you require a specific concrete class
|
||||
if (!(launchHandler.get() instanceof CommonLaunchHandler)) {
|
||||
LOGGER.error(CORE, "Incompatible Launch handler found - type {}, cannot continue", launchHandler.get().getClass().getName());
|
||||
throw new RuntimeException("Incompatible launch handler found");
|
||||
@@ -149,16 +143,12 @@ public class FMLLoader
|
||||
dist = commonLaunchHandler.getDist();
|
||||
production = commonLaunchHandler.isProduction();
|
||||
|
||||
versionInfo = new VersionInfo(arguments);
|
||||
|
||||
accessTransformer.getExtension().accept(Pair.of(naming, "srg"));
|
||||
|
||||
LOGGER.debug(CORE,"Received command line version data : {}", versionInfo);
|
||||
|
||||
runtimeDistCleaner.getExtension().accept(dist);
|
||||
}
|
||||
public static List<ITransformationService.Resource> beginModScan(final Map<String,?> arguments)
|
||||
{
|
||||
|
||||
public static List<ITransformationService.Resource> beginModScan(final Map<String,?> arguments) {
|
||||
LOGGER.debug(SCAN,"Scanning for Mod Locators");
|
||||
modDiscoverer = new ModDiscoverer(arguments);
|
||||
modValidator = modDiscoverer.discoverMods();
|
||||
@@ -178,8 +168,7 @@ public class FMLLoader
|
||||
return coreModProvider;
|
||||
}
|
||||
|
||||
public static LanguageLoadingProvider getLanguageLoadingProvider()
|
||||
{
|
||||
public static LanguageLoadingProvider getLanguageLoadingProvider() {
|
||||
return languageLoadingProvider;
|
||||
}
|
||||
|
||||
@@ -191,31 +180,26 @@ public class FMLLoader
|
||||
return commonLaunchHandler;
|
||||
}
|
||||
|
||||
public static void addAccessTransformer(Path atPath, ModFile modName)
|
||||
{
|
||||
public static void addAccessTransformer(Path atPath, ModFile modName) {
|
||||
LOGGER.debug(SCAN, "Adding Access Transformer in {}", modName.getFilePath());
|
||||
accessTransformer.offerResource(atPath, modName.getFileName());
|
||||
}
|
||||
|
||||
public static Dist getDist()
|
||||
{
|
||||
public static Dist getDist() {
|
||||
return dist;
|
||||
}
|
||||
|
||||
public static void beforeStart(ModuleLayer gameLayer)
|
||||
{
|
||||
public static void beforeStart(ModuleLayer gameLayer) {
|
||||
ImmediateWindowHandler.acceptGameLayer(gameLayer);
|
||||
ImmediateWindowHandler.updateProgress("Launching minecraft");
|
||||
progressWindowTick.run();
|
||||
}
|
||||
|
||||
public static LoadingModList getLoadingModList()
|
||||
{
|
||||
public static LoadingModList getLoadingModList() {
|
||||
return loadingModList;
|
||||
}
|
||||
|
||||
public static Path getGamePath()
|
||||
{
|
||||
public static Path getGamePath() {
|
||||
return gamePath;
|
||||
}
|
||||
|
||||
@@ -232,7 +216,7 @@ public class FMLLoader
|
||||
}
|
||||
|
||||
public static List<Map<String, String>> modLauncherModList() {
|
||||
return Launcher.INSTANCE.environment().getProperty(IEnvironment.Keys.MODLIST.get()).orElseGet(Collections::emptyList);
|
||||
return Launcher.INSTANCE.environment().getProperty(IEnvironment.Keys.MODLIST.get()).orElse(List.of());
|
||||
}
|
||||
|
||||
public static String launcherHandlerName() {
|
||||
|
||||
@@ -7,7 +7,6 @@ package net.minecraftforge.fml.loading;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import cpw.mods.modlauncher.api.*;
|
||||
import joptsimple.ArgumentAcceptingOptionSpec;
|
||||
import joptsimple.OptionSpecBuilder;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
|
||||
import net.minecraftforge.forgespi.Environment;
|
||||
@@ -15,7 +14,6 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
@@ -24,36 +22,18 @@ import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
import static net.minecraftforge.fml.loading.LogMarkers.CORE;
|
||||
|
||||
public class FMLServiceProvider implements ITransformationService
|
||||
{
|
||||
public class FMLServiceProvider implements ITransformationService {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private ArgumentAcceptingOptionSpec<String> modsOption;
|
||||
private ArgumentAcceptingOptionSpec<String> modListsOption;
|
||||
private ArgumentAcceptingOptionSpec<String> mavenRootsOption;
|
||||
private ArgumentAcceptingOptionSpec<String> forgeOption;
|
||||
private ArgumentAcceptingOptionSpec<String> mcOption;
|
||||
private ArgumentAcceptingOptionSpec<String> forgeGroupOption;
|
||||
private ArgumentAcceptingOptionSpec<String> mcpOption;
|
||||
private ArgumentAcceptingOptionSpec<String> mappingsOption;
|
||||
private List<String> modsArgumentList;
|
||||
private List<String> modListsArgumentList;
|
||||
private List<String> mavenRootsArgumentList;
|
||||
private String targetForgeVersion;
|
||||
private String targetMcVersion;
|
||||
private String targetMcpVersion;
|
||||
private String targetMcpMappings;
|
||||
private String targetForgeGroup;
|
||||
private Map<String, Object> arguments;
|
||||
|
||||
public FMLServiceProvider()
|
||||
{
|
||||
final String markerselection = System.getProperty("forge.logging.markers", "");
|
||||
Arrays.stream(markerselection.split(",")).forEach(marker -> System.setProperty("forge.logging.marker." + marker.toLowerCase(Locale.ROOT), "ACCEPT"));
|
||||
public FMLServiceProvider() {
|
||||
var markers = System.getProperty("forge.logging.markers", "").split(",");
|
||||
for (var marker : markers)
|
||||
System.setProperty("forge.logging.marker." + marker.toLowerCase(Locale.ROOT), "ACCEPT");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
public String name() {
|
||||
return "fml";
|
||||
}
|
||||
|
||||
@@ -66,14 +46,6 @@ public class FMLServiceProvider implements ITransformationService
|
||||
LOGGER.debug(CORE, "Preparing ModFile");
|
||||
environment.computePropertyIfAbsent(Environment.Keys.MODFILEFACTORY.get(), k->ModFile::new);
|
||||
arguments = new HashMap<>();
|
||||
arguments.put("modLists", modListsArgumentList);
|
||||
arguments.put("mods", modsArgumentList);
|
||||
arguments.put("mavenRoots", mavenRootsArgumentList);
|
||||
arguments.put("forgeVersion", targetForgeVersion);
|
||||
arguments.put("forgeGroup", targetForgeGroup);
|
||||
arguments.put("mcVersion", targetMcVersion);
|
||||
arguments.put("mcpVersion", targetMcpVersion);
|
||||
arguments.put("mcpMappings", targetMcpMappings);
|
||||
LOGGER.debug(CORE, "Preparing launch handler");
|
||||
FMLLoader.setupLaunchHandler(environment, arguments);
|
||||
FMLEnvironment.setupInteropEnvironment(environment);
|
||||
@@ -92,46 +64,22 @@ public class FMLServiceProvider implements ITransformationService
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoad(IEnvironment environment, Set<String> otherServices) throws IncompatibleEnvironmentException
|
||||
{
|
||||
// LOGGER.debug("Injecting tracing printstreams for STDOUT/STDERR.");
|
||||
// System.setOut(new TracingPrintStream(LogManager.getLogger("STDOUT"), System.out));
|
||||
// System.setErr(new TracingPrintStream(LogManager.getLogger("STDERR"), System.err));
|
||||
public void onLoad(IEnvironment environment, Set<String> otherServices) throws IncompatibleEnvironmentException {
|
||||
FMLLoader.onInitialLoad(environment, otherServices);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void arguments(BiFunction<String, String, OptionSpecBuilder> argumentBuilder)
|
||||
{
|
||||
forgeOption = argumentBuilder.apply("forgeVersion", "Forge Version number").withRequiredArg().ofType(String.class).required();
|
||||
forgeGroupOption = argumentBuilder.apply("forgeGroup", "Forge Group (for testing)").withRequiredArg().ofType(String.class).defaultsTo("net.minecraftforge");
|
||||
mcOption = argumentBuilder.apply("mcVersion", "Minecraft Version number").withRequiredArg().ofType(String.class).required();
|
||||
mcpOption = argumentBuilder.apply("mcpVersion", "MCP Version number").withRequiredArg().ofType(String.class).required();
|
||||
mappingsOption = argumentBuilder.apply("mcpMappings", "MCP Mappings Channel and Version").withRequiredArg().ofType(String.class);
|
||||
modsOption = argumentBuilder.apply("mods", "List of mods to add").withRequiredArg().ofType(String.class).withValuesSeparatedBy(",");
|
||||
modListsOption = argumentBuilder.apply("modLists", "JSON modlists").withRequiredArg().ofType(String.class).withValuesSeparatedBy(",");
|
||||
mavenRootsOption = argumentBuilder.apply("mavenRoots", "Maven root directories").withRequiredArg().ofType(String.class).withValuesSeparatedBy(",");
|
||||
public void arguments(BiFunction<String, String, OptionSpecBuilder> argumentBuilder) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void argumentValues(OptionResult option)
|
||||
{
|
||||
modsArgumentList = option.values(modsOption);
|
||||
modListsArgumentList = option.values(modListsOption);
|
||||
mavenRootsArgumentList = option.values(mavenRootsOption);
|
||||
targetForgeVersion = option.value(forgeOption);
|
||||
targetForgeGroup = option.value(forgeGroupOption);
|
||||
targetMcVersion = option.value(mcOption);
|
||||
targetMcpVersion = option.value(mcpOption);
|
||||
targetMcpMappings = option.value(mappingsOption);
|
||||
public void argumentValues(OptionResult option) {
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public @NotNull List<ITransformer> transformers()
|
||||
{
|
||||
public @NotNull List<ITransformer> transformers() {
|
||||
LOGGER.debug(CORE, "Loading coremod transformers");
|
||||
return new ArrayList<>(FMLLoader.getCoreModProvider().getCoreModTransformers());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -32,15 +32,16 @@ public class ImmediateWindowHandler {
|
||||
} else {
|
||||
final var providername = FMLConfig.getConfigValue(FMLConfig.ConfigValue.EARLY_WINDOW_PROVIDER);
|
||||
LOGGER.info("Loading ImmediateWindowProvider {}", providername);
|
||||
final var maybeProvider = ServiceLoader.load(ImmediateWindowProvider.class)
|
||||
.stream()
|
||||
.map(ServiceLoader.Provider::get)
|
||||
.filter(p -> Objects.equals(p.name(), providername))
|
||||
.findFirst();
|
||||
provider = maybeProvider.or(() -> {
|
||||
for (var service : ServiceLoader.load(ImmediateWindowProvider.class)) {
|
||||
if (providername.equals(service.name())) {
|
||||
provider = service;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (provider == null) {
|
||||
LOGGER.info("Failed to find ImmediateWindowProvider {}, disabling", providername);
|
||||
return Optional.of(new DummyProvider());
|
||||
}).orElseThrow();
|
||||
provider = new DummyProvider();
|
||||
}
|
||||
}
|
||||
// Only update config if the provider isn't the dummy provider
|
||||
if (!Objects.equals(provider.name(), "dummyprovider"))
|
||||
|
||||
+41
-1
@@ -11,32 +11,72 @@ import java.util.Optional;
|
||||
* Finds Version data from a package, with possible default values
|
||||
*/
|
||||
public class JarVersionLookupHandler {
|
||||
public record Version(Optional<String> title, Optional<String> vendor, Optional<String> version) {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[" +
|
||||
"title=" + title.orElse(null) +
|
||||
", vendor=" + vendor.orElse(null) +
|
||||
", version=" + version.orElse(null) +
|
||||
"]";
|
||||
}
|
||||
}
|
||||
public record Info(Version spec, Version impl) {}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public static Info getInfo(String pkgName) {
|
||||
return getInfo(Package.getPackage(pkgName));
|
||||
}
|
||||
|
||||
public static Info getInfo(Class<?> clazz) {
|
||||
return getInfo(clazz.getPackage());
|
||||
}
|
||||
|
||||
public static Info getInfo(Package pkg) {
|
||||
return new Info(
|
||||
new Version(
|
||||
Optional.ofNullable(pkg.getSpecificationTitle()),
|
||||
Optional.ofNullable(pkg.getSpecificationVendor()),
|
||||
Optional.ofNullable(pkg.getSpecificationVersion())
|
||||
),
|
||||
new Version(
|
||||
Optional.ofNullable(pkg.getImplementationTitle()),
|
||||
Optional.ofNullable(pkg.getImplementationVendor()),
|
||||
Optional.ofNullable(pkg.getImplementationVersion())
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@Deprecated(forRemoval = true, since = "1.20.2")
|
||||
public static Optional<String> getImplementationVersion(final String pkgName) {
|
||||
// Note that with Java 9, you'll probably want the module's version data, hence pulling this out
|
||||
final String pkgVersion = Package.getPackage(pkgName).getImplementationVersion();
|
||||
return Optional.ofNullable(pkgVersion);
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true, since = "1.20.2")
|
||||
public static Optional<String> getSpecificationVersion(final String pkgName) {
|
||||
// Note that with Java 9, you'll probably want the module's version data, hence pulling this out
|
||||
final String pkgVersion = Package.getPackage(pkgName).getSpecificationVersion();
|
||||
return Optional.ofNullable(pkgVersion);
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true, since = "1.20.2")
|
||||
public static Optional<String> getImplementationVersion(final Class<?> clazz) {
|
||||
// With java 9 we'll use the module's version if it exists in preference.
|
||||
final String pkgVersion = clazz.getPackage().getImplementationVersion();
|
||||
return Optional.ofNullable(pkgVersion);
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true, since = "1.20.2")
|
||||
public static Optional<String> getImplementationTitle(final Class<?> clazz) {
|
||||
// With java 9 we'll use the module's version if it exists in preference.
|
||||
final String pkgVersion = clazz.getPackage().getImplementationTitle();
|
||||
return Optional.ofNullable(pkgVersion);
|
||||
}
|
||||
|
||||
|
||||
@Deprecated(forRemoval = true, since = "1.20.2")
|
||||
public static Optional<String> getSpecificationVersion(final Class<?> clazz) {
|
||||
// With java 9 we'll use the module's version if it exists in preference.
|
||||
final String pkgVersion = clazz.getPackage().getSpecificationVersion();
|
||||
|
||||
+81
-106
@@ -7,26 +7,19 @@ package net.minecraftforge.fml.loading;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import cpw.mods.modlauncher.Launcher;
|
||||
import cpw.mods.modlauncher.api.IModuleLayerManager;
|
||||
import cpw.mods.modlauncher.util.ServiceLoaderUtils;
|
||||
import cpw.mods.modlauncher.api.IModuleLayerManager.Layer;
|
||||
import net.minecraftforge.forgespi.language.IModLanguageProvider;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ExplodedDirectoryLocator;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
|
||||
import org.apache.maven.artifact.versioning.ArtifactVersion;
|
||||
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
|
||||
import org.apache.maven.artifact.versioning.VersionRange;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.ServiceConfigurationError;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
@@ -35,115 +28,97 @@ import java.util.stream.Stream;
|
||||
import static net.minecraftforge.fml.loading.LogMarkers.CORE;
|
||||
import static net.minecraftforge.fml.loading.LogMarkers.LOADING;
|
||||
|
||||
public class LanguageLoadingProvider
|
||||
{
|
||||
public class LanguageLoadingProvider {
|
||||
public void forEach(Consumer<IModLanguageProvider> consumer) {
|
||||
providers.forEach(consumer);
|
||||
}
|
||||
|
||||
public <T> Stream<T> applyForEach(Function<IModLanguageProvider, T> function) {
|
||||
return providers.stream().map(function);
|
||||
}
|
||||
|
||||
public void reload() {
|
||||
loadLanguageProviders();
|
||||
}
|
||||
|
||||
/** This doesn't actually do anything with the argument, so just call reload(). Not sure when cpw broke that. */
|
||||
@Deprecated(forRemoval = true, since = "1.20.2")
|
||||
public void addAdditionalLanguages(List<ModFile> modFiles) {
|
||||
reload();
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* INTERNAL SHIT *
|
||||
*==========================================================================*/
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private final List<IModLanguageProvider> languageProviders = new ArrayList<>();
|
||||
private final ServiceLoader<IModLanguageProvider> serviceLoader;
|
||||
private final Map<String, ModLanguageWrapper> languageProviderMap = new HashMap<>();
|
||||
private List<Path> languagePaths = new ArrayList<>();
|
||||
private final List<IModLanguageProvider> providers = new ArrayList<>();
|
||||
private final Map<String, Wrapper> providersByName = new HashMap<>();
|
||||
private static final DefaultArtifactVersion IN_DEV = new DefaultArtifactVersion("0.0-dev");
|
||||
|
||||
public void forEach(final Consumer<IModLanguageProvider> consumer)
|
||||
{
|
||||
languageProviders.forEach(consumer);
|
||||
}
|
||||
|
||||
public <T> Stream<T> applyForEach(final Function<IModLanguageProvider, T> function) {
|
||||
return languageProviders.stream().map(function);
|
||||
}
|
||||
|
||||
private static class ModLanguageWrapper {
|
||||
private final IModLanguageProvider modLanguageProvider;
|
||||
|
||||
private final ArtifactVersion version;
|
||||
public ModLanguageWrapper(IModLanguageProvider modLanguageProvider, ArtifactVersion version)
|
||||
{
|
||||
this.modLanguageProvider = modLanguageProvider;
|
||||
this.version = version;
|
||||
}
|
||||
public ArtifactVersion getVersion()
|
||||
{
|
||||
return version;
|
||||
}
|
||||
|
||||
public IModLanguageProvider getModLanguageProvider()
|
||||
{
|
||||
return modLanguageProvider;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
private record Wrapper(ArtifactVersion version, IModLanguageProvider service) {}
|
||||
|
||||
LanguageLoadingProvider() {
|
||||
var sl = Launcher.INSTANCE.environment().findModuleLayerManager().flatMap(lm->lm.getLayer(IModuleLayerManager.Layer.PLUGIN)).orElseThrow();
|
||||
var sl = Launcher.INSTANCE.environment().findModuleLayerManager().flatMap(lm -> lm.getLayer(Layer.PLUGIN)).orElseThrow();
|
||||
serviceLoader = ServiceLoader.load(sl, IModLanguageProvider.class);
|
||||
loadLanguageProviders();
|
||||
}
|
||||
|
||||
private void loadLanguageProviders() {
|
||||
LOGGER.debug(CORE, "Found {} language providers", ServiceLoaderUtils.streamServiceLoader(()->serviceLoader, sce->LOGGER.error("Problem with language loaders")).count());
|
||||
serviceLoader.forEach(languageProviders::add);
|
||||
ImmediateWindowHandler.updateProgress("Loading language providers");
|
||||
languageProviders.forEach(lp -> {
|
||||
final Path lpPath;
|
||||
try {
|
||||
lpPath = Paths.get(lp.getClass().getProtectionDomain().getCodeSource().getLocation().toURI());
|
||||
} catch (URISyntaxException e) {
|
||||
throw new RuntimeException("Huh?", e);
|
||||
}
|
||||
Optional<String> implementationVersion = JarVersionLookupHandler.getImplementationVersion(lp.getClass());
|
||||
String impl = implementationVersion.orElse(Files.isDirectory(lpPath) ? FMLLoader.versionInfo().forgeVersion().split("\\.")[0] : null);
|
||||
if (impl == null) {
|
||||
LOGGER.error(CORE, "Found unversioned language provider {}", lp.name());
|
||||
throw new RuntimeException("Failed to find implementation version for language provider "+ lp.name());
|
||||
}
|
||||
LOGGER.debug(CORE, "Found language provider {}, version {}", lp.name(), impl);
|
||||
ImmediateWindowHandler.updateProgress("Loaded language provider "+lp.name()+ " " + impl);
|
||||
languageProviderMap.put(lp.name(), new ModLanguageWrapper(lp, new DefaultArtifactVersion(impl)));
|
||||
});
|
||||
}
|
||||
providers.clear();
|
||||
providersByName.clear();
|
||||
|
||||
void addForgeLanguage(final Path forgePath) {
|
||||
if (!languageProviderMap.containsKey("javafml")) {
|
||||
LOGGER.debug(CORE,"Adding forge as a language from {}", forgePath.toString());
|
||||
addLanguagePaths(Stream.of(forgePath));
|
||||
serviceLoader.reload();
|
||||
loadLanguageProviders();
|
||||
} else {
|
||||
LOGGER.debug(CORE, "Skipping adding forge jar - javafml is already present");
|
||||
}
|
||||
}
|
||||
|
||||
private void addLanguagePaths(final Stream<Path> langPaths) {
|
||||
languageProviders.clear();
|
||||
languageProviderMap.clear();
|
||||
// langPaths.peek(languagePaths::add).map(Path::toFile).map(File::toURI).map(rethrowFunction(URI::toURL)).forEach(languageClassLoader::addURL);
|
||||
}
|
||||
|
||||
public void addAdditionalLanguages(List<ModFile> modFiles)
|
||||
{
|
||||
if (modFiles==null) return;
|
||||
Stream<Path> langPaths = modFiles.stream().map(ModFile::getFilePath);
|
||||
addLanguagePaths(langPaths);
|
||||
serviceLoader.reload();
|
||||
loadLanguageProviders();
|
||||
}
|
||||
|
||||
Stream<Path> getLibraries() {
|
||||
return languagePaths.stream();
|
||||
}
|
||||
|
||||
public IModLanguageProvider findLanguage(ModFile mf, String modLoader, VersionRange modLoaderVersion) {
|
||||
final String languageFileName = mf.getProvider() instanceof ExplodedDirectoryLocator ? "in-development" : mf.getFileName();
|
||||
final ModLanguageWrapper mlw = languageProviderMap.get(modLoader);
|
||||
if (mlw == null) {
|
||||
LOGGER.error(LOADING,"Missing language {} version {} wanted by {}", modLoader, modLoaderVersion, languageFileName);
|
||||
throw new EarlyLoadingException("Missing language "+modLoader, null, Collections.singletonList(new EarlyLoadingException.ExceptionData("fml.language.missingversion", modLoader, modLoaderVersion, languageFileName, "null")));
|
||||
}
|
||||
if (!VersionSupportMatrix.testVersionSupportMatrix(modLoaderVersion, modLoader, "languageloader", (llid, range) -> range.containsVersion(mlw.getVersion()))) {
|
||||
LOGGER.error(LOADING,"Missing language {} version {} wanted by {}, found {}", modLoader, modLoaderVersion, languageFileName, mlw.getVersion());
|
||||
throw new EarlyLoadingException("Missing language "+ modLoader + " matching range "+modLoaderVersion + " found "+mlw.getVersion(), null, Collections.singletonList(new EarlyLoadingException.ExceptionData("fml.language.missingversion", modLoader, modLoaderVersion, languageFileName, mlw.getVersion())));
|
||||
for (var itr = serviceLoader.iterator(); itr.hasNext(); ) {
|
||||
try {
|
||||
var srvc = itr.next();
|
||||
providers.add(srvc);
|
||||
} catch (ServiceConfigurationError sce) {
|
||||
LOGGER.error(CORE, "Failed to load IModLauguageProvider Service: " + sce.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
return mlw.getModLanguageProvider();
|
||||
LOGGER.debug(CORE, "Found {} language providers", providers.size());
|
||||
|
||||
ImmediateWindowHandler.updateProgress("Loading language providers");
|
||||
for (var provider : providers) {
|
||||
var info = JarVersionLookupHandler.getInfo(provider.getClass());
|
||||
|
||||
if (info.impl().version().isEmpty() && FMLEnvironment.production) {
|
||||
LOGGER.error(CORE, "Found unversioned language provider {}: {}", provider.name(), provider.getClass().getName());
|
||||
throw new RuntimeException("Failed to find implementation version for language provider " + provider.name() + " of type " + provider.getClass().getName());
|
||||
}
|
||||
|
||||
var version = info.impl().version().map(DefaultArtifactVersion::new).orElse(IN_DEV);
|
||||
|
||||
LOGGER.debug(CORE, "Found language provider {}, version {}", provider.name(), version);
|
||||
ImmediateWindowHandler.updateProgress("Loaded language provider " + provider.name() + " " + version);
|
||||
providersByName.put(provider.name(), new Wrapper(version, provider));
|
||||
}
|
||||
}
|
||||
|
||||
public IModLanguageProvider findLanguage(ModFile mod, String language, VersionRange versionRange) {
|
||||
var consumer = mod.getFileName();
|
||||
var wrapper = providersByName.get(language);
|
||||
|
||||
if (wrapper == null) {
|
||||
LOGGER.error(LOADING, "Missing language {} version {} wanted by {}", language, versionRange, consumer);
|
||||
var data = new EarlyLoadingException.ExceptionData("fml.language.missingversion", language, versionRange, consumer, "null");
|
||||
throw new EarlyLoadingException("Missing language " + language, null, List.of(data));
|
||||
}
|
||||
|
||||
var version = wrapper.version();
|
||||
if (version == IN_DEV) {
|
||||
LOGGER.debug(LOADING, "Language Provider {} version is unknown, but {} was requested by {}. Proceeding as this is development environement", language, versionRange, consumer);
|
||||
return wrapper.service();
|
||||
}
|
||||
|
||||
if (!versionRange.containsVersion(version)) {
|
||||
LOGGER.error(LOADING, "Missing language {} version {} wanted by {}, found {}", language, versionRange, consumer, version);
|
||||
var data = new EarlyLoadingException.ExceptionData("fml.language.missingversion", language, versionRange, consumer, version);
|
||||
throw new EarlyLoadingException("Missing language " + language + " matching range " + versionRange + " found " + version, null, List.of(data));
|
||||
}
|
||||
|
||||
return wrapper.service();
|
||||
}
|
||||
}
|
||||
@@ -8,21 +8,19 @@ package net.minecraftforge.fml.loading;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import static net.minecraftforge.fml.loading.LogMarkers.CORE;
|
||||
|
||||
public class LauncherVersion {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private static final String launcherVersion;
|
||||
|
||||
static {
|
||||
String vers = JarVersionLookupHandler.getImplementationVersion(LauncherVersion.class).orElse(System.getenv("LAUNCHER_VERSION"));
|
||||
if (vers == null) throw new RuntimeException("Missing FMLLauncher version, cannot continue");
|
||||
launcherVersion = vers;
|
||||
LOGGER.debug(CORE, "Found FMLLauncher version {}", launcherVersion);
|
||||
var info = JarVersionLookupHandler.getInfo(LauncherVersion.class);
|
||||
if (info.impl().version().isEmpty())
|
||||
throw new IllegalStateException("Failed to find version for package " + LauncherVersion.class.getPackageName() + " This is an invalid environment");
|
||||
launcherVersion = info.impl().version().get();
|
||||
LOGGER.debug(LogMarkers.CORE, "Found FMLLauncher version {}", launcherVersion);
|
||||
}
|
||||
|
||||
public static String getVersion()
|
||||
{
|
||||
public static String getVersion() {
|
||||
return launcherVersion;
|
||||
}
|
||||
}
|
||||
@@ -5,45 +5,20 @@
|
||||
|
||||
package net.minecraftforge.fml.loading;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class LibraryFinder {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private static Path libsPath;
|
||||
static Path findLibsPath() {
|
||||
if (libsPath == null) {
|
||||
libsPath = Path.of(System.getProperty("libraryDirectory","crazysnowmannonsense/cheezwhizz"));
|
||||
if (!Files.isDirectory(libsPath)) {
|
||||
throw new IllegalStateException("Missing libraryDirectory system property, cannot continue");
|
||||
}
|
||||
libsPath = Path.of(System.getProperty("libraryDirectory", "libraries"));
|
||||
if (!Files.isDirectory(libsPath))
|
||||
throw new IllegalStateException("Library directory: `" + libsPath + "` does not exist. Set it via the `libraryDirectory` system property.");
|
||||
}
|
||||
return libsPath;
|
||||
}
|
||||
|
||||
static Path getForgeLibraryPath(final String mcVersion, final String forgeVersion, final String forgeGroup) {
|
||||
Path forgePath = findLibsPath().resolve(MavenCoordinateResolver.get(forgeGroup, "forge", "", "universal", mcVersion+"-"+forgeVersion));
|
||||
LOGGER.debug(LogMarkers.CORE, "Found forge path {} is {}", forgePath, pathStatus(forgePath));
|
||||
return forgePath;
|
||||
}
|
||||
|
||||
static String pathStatus(final Path path) {
|
||||
return Files.exists(path) ? "present" : "missing";
|
||||
}
|
||||
|
||||
static Path[] getMCPaths(final String mcVersion, final String mcpVersion, final String forgeVersion, final String forgeGroup, final String type) {
|
||||
Path srgMcPath = findLibsPath().resolve(MavenCoordinateResolver.get("net.minecraft", type, "", "srg", mcVersion+"-"+mcpVersion));
|
||||
Path mcExtrasPath = findLibsPath().resolve(MavenCoordinateResolver.get("net.minecraft", type, "", "extra", mcVersion+"-"+mcpVersion));
|
||||
Path patchedBinariesPath = findLibsPath().resolve(MavenCoordinateResolver.get(forgeGroup, "forge", "", type, mcVersion+"-"+forgeVersion));
|
||||
LOGGER.debug(LogMarkers.CORE,"SRG MC at {} is {}", srgMcPath.toString(), pathStatus(srgMcPath));
|
||||
LOGGER.debug(LogMarkers.CORE,"MC Extras at {} is {}", mcExtrasPath.toString(), pathStatus(mcExtrasPath));
|
||||
LOGGER.debug(LogMarkers.CORE,"Forge patches at {} is {}", patchedBinariesPath.toString(), pathStatus(patchedBinariesPath));
|
||||
return new Path[] { patchedBinariesPath, mcExtrasPath, srgMcPath };
|
||||
}
|
||||
|
||||
public static Path findPathForMaven(final String group, final String artifact, final String extension, final String classifier, final String version) {
|
||||
return findLibsPath().resolve(MavenCoordinateResolver.get(group, artifact, extension, classifier, version));
|
||||
}
|
||||
|
||||
@@ -8,8 +8,6 @@ package net.minecraftforge.fml.loading;
|
||||
import com.google.common.graph.GraphBuilder;
|
||||
import com.google.common.graph.MutableGraph;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import cpw.mods.jarhandling.SecureJar;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.MinecraftLocator;
|
||||
import net.minecraftforge.forgespi.language.IModInfo;
|
||||
import net.minecraftforge.fml.loading.EarlyLoadingException.ExceptionData;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
|
||||
@@ -21,10 +19,14 @@ import org.apache.maven.artifact.versioning.ArtifactVersion;
|
||||
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Function;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@@ -150,27 +152,13 @@ public class ModSorter
|
||||
));
|
||||
}
|
||||
|
||||
private void detectSystemMods(final Map<String, List<ModFile>> modFilesByFirstId)
|
||||
{
|
||||
private void detectSystemMods(final Map<String, List<ModFile>> modFilesByFirstId) {
|
||||
// Capture system mods (ex. MC, Forge) here, so we can keep them for later
|
||||
final Set<String> systemMods = new HashSet<>();
|
||||
// The minecraft mod is always a system mod
|
||||
systemMods.add("minecraft");
|
||||
// Find mod file from MinecraftLocator to define the system mods
|
||||
modFiles.stream()
|
||||
.filter(modFile -> modFile.getProvider().getClass() == MinecraftLocator.class)
|
||||
.map(ModFile::getSecureJar)
|
||||
.map(SecureJar::moduleDataProvider)
|
||||
.map(SecureJar.ModuleDataProvider::getManifest)
|
||||
.map(Manifest::getMainAttributes)
|
||||
.map(mf -> mf.getValue("FML-System-Mods"))
|
||||
.filter(Objects::nonNull)
|
||||
.findFirst()
|
||||
.ifPresent(value -> systemMods.addAll(Arrays.asList(value.split(","))));
|
||||
var systemMods = List.of("minecraft", "forge");
|
||||
LOGGER.debug("Configured system mods: {}", systemMods);
|
||||
|
||||
this.systemMods = new ArrayList<>();
|
||||
for (String systemMod : systemMods) {
|
||||
for (var systemMod : systemMods) {
|
||||
var container = modFilesByFirstId.get(systemMod);
|
||||
if (container != null && !container.isEmpty()) {
|
||||
LOGGER.debug("Found system mod: {}", systemMod);
|
||||
|
||||
@@ -5,42 +5,37 @@
|
||||
|
||||
package net.minecraftforge.fml.loading;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
|
||||
import org.apache.commons.lang3.text.StrLookup;
|
||||
import org.apache.commons.lang3.text.StrSubstitutor;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class StringSubstitutor
|
||||
{
|
||||
private static final Map<String,String> globals = ImmutableMap.of(
|
||||
"mcVersion", FMLLoader.versionInfo().mcVersion(),
|
||||
"forgeVersion", FMLLoader.versionInfo().forgeVersion()
|
||||
);
|
||||
import org.apache.commons.lang3.text.StrLookup;
|
||||
import org.apache.commons.lang3.text.StrSubstitutor;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class StringSubstitutor {
|
||||
public static String replace(final String in, final ModFile file) {
|
||||
return new StrSubstitutor(getStringLookup(file)).replace(in);
|
||||
}
|
||||
|
||||
private static StrLookup<String> getStringLookup(final ModFile file) {
|
||||
return new StrLookup<String>()
|
||||
{
|
||||
var globals = Map.of(
|
||||
"mcVersion", FMLLoader.versionInfo().mcVersion(),
|
||||
"forgeVersion", FMLLoader.versionInfo().forgeVersion()
|
||||
);
|
||||
return new StrLookup<String>() {
|
||||
@Override
|
||||
public String lookup(String key)
|
||||
{
|
||||
final String[] parts = key.split("\\.");
|
||||
if (parts.length == 1) return key;
|
||||
final String pfx = parts[0];
|
||||
public String lookup(String key) {
|
||||
var parts = key.split("\\.");
|
||||
if (parts.length == 1)
|
||||
return key;
|
||||
var pfx = parts[0];
|
||||
|
||||
if ("global".equals(pfx))
|
||||
{
|
||||
return globals.get(parts[1]);
|
||||
}
|
||||
else if ("file".equals(pfx) && file != null)
|
||||
{
|
||||
return String.valueOf(file.getSubstitutionMap().get().get(parts[1]));
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -5,18 +5,45 @@
|
||||
|
||||
package net.minecraftforge.fml.loading;
|
||||
|
||||
import java.util.Map;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
/** TODO: [FML][Loader] Remove VersionInfo - we shouldn't need to base any decisions on forge or mc version info at the loader level. So once I get a proper replacement i'm gunna kill this. */
|
||||
public record VersionInfo(String forgeVersion, String mcVersion, String mcpVersion, String forgeGroup) {
|
||||
VersionInfo(Map<String, ?> arguments) {
|
||||
this((String) arguments.get("forgeVersion"), (String) arguments.get("mcVersion"), (String) arguments.get("mcpVersion"), (String) arguments.get("forgeGroup"));
|
||||
}
|
||||
|
||||
public String mcAndForgeVersion() {
|
||||
return mcVersion+"-"+forgeVersion;
|
||||
return mcVersion + "-" + forgeVersion;
|
||||
}
|
||||
|
||||
public String mcAndMCPVersion() {
|
||||
return mcVersion + "-" + mcpVersion;
|
||||
}
|
||||
|
||||
/*==========================================================================*
|
||||
* INTERNAL SHIT *
|
||||
*==========================================================================*/
|
||||
private static Gson GSON = new GsonBuilder().create();
|
||||
private static <T> T readJson(String path, Class<T> type) {
|
||||
try (var is = VersionInfo.class.getResourceAsStream(path)) {
|
||||
if (is == null)
|
||||
return null;
|
||||
|
||||
try (var reader = new InputStreamReader(is)) {
|
||||
return GSON.fromJson(reader, type);
|
||||
}
|
||||
} catch (IOException | JsonSyntaxException e) {
|
||||
throw new IllegalStateException("Failed to parse version info from " + path , e);
|
||||
}
|
||||
}
|
||||
|
||||
static VersionInfo detect() {
|
||||
var forge = readJson("/forge_version.json", JSON.class);
|
||||
if (forge == null)
|
||||
throw new IllegalStateException("Failed to find /forge_version.json This is a critical issue");
|
||||
return new VersionInfo(forge.forge(), forge.mc(), forge.mcp(), "net.minecraftforge");
|
||||
}
|
||||
private record JSON(String forge, String mcp, String mc) {}
|
||||
}
|
||||
@@ -18,7 +18,7 @@ import java.util.function.BiPredicate;
|
||||
public class VersionSupportMatrix {
|
||||
private static final HashMap<String, List<ArtifactVersion>> overrideVersions = new HashMap<>();
|
||||
static {
|
||||
final ArtifactVersion version = new DefaultArtifactVersion(FMLLoader.versionInfo().mcVersion());
|
||||
final ArtifactVersion version = new DefaultArtifactVersion(FMLLoader.versionInfo().mcVersion());
|
||||
if (MavenVersionAdapter.createFromVersionSpec("[1.19.2]").containsVersion(version)) {
|
||||
// 1.19.2 is Compatible with 1.19.1
|
||||
add("languageloader.javafml", "42");
|
||||
@@ -31,7 +31,7 @@ public class VersionSupportMatrix {
|
||||
}
|
||||
public static <T> boolean testVersionSupportMatrix(VersionRange declaredRange, String lookupId, String type, BiPredicate<String, VersionRange> standardLookup) {
|
||||
if (standardLookup.test(lookupId, declaredRange)) return true;
|
||||
List<ArtifactVersion> custom = overrideVersions.get(type +"." +lookupId);
|
||||
List<ArtifactVersion> custom = overrideVersions.get(type + "." + lookupId);
|
||||
return custom == null ? false : custom.stream().anyMatch(declaredRange::containsVersion);
|
||||
}
|
||||
}
|
||||
-52
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraftforge.forgespi.locating.IDependencyLocator;
|
||||
import net.minecraftforge.forgespi.locating.IModFile;
|
||||
import net.minecraftforge.forgespi.locating.ModFileLoadingException;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Optional;
|
||||
|
||||
public abstract class AbstractJarFileDependencyLocator extends AbstractJarFileModProvider implements IDependencyLocator
|
||||
{
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
protected Optional<InputStream> loadResourceFromModFile(final IModFile modFile, final Path path) {
|
||||
try {
|
||||
return Optional.of(Files.newInputStream(modFile.findResource(path.toString())));
|
||||
}
|
||||
catch (final FileNotFoundException e) {
|
||||
LOGGER.debug("Failed to load resource {} from {}, it does not contain dependency information.", path, modFile.getFileName());
|
||||
return Optional.empty();
|
||||
}
|
||||
catch (final Exception e) {
|
||||
LOGGER.error("Failed to load resource {} from mod {}, cause {}", path, modFile.getFileName(), e);
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
protected Optional<IModFile> loadModFileFrom(final IModFile file, final Path path) {
|
||||
try {
|
||||
final Path pathInModFile = file.findResource(path.toString());
|
||||
return Optional.of(createMod(pathInModFile).file());
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOGGER.error("Failed to load mod file {} from {}", path, file.getFileName());
|
||||
throw new ModFileLoadingException("Failed to load mod file "+file.getFileName());
|
||||
}
|
||||
}
|
||||
|
||||
protected String identifyMod(final IModFile modFile) {
|
||||
return modFile.getFileName();
|
||||
}
|
||||
}
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import net.minecraftforge.forgespi.locating.IModLocator;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public abstract class AbstractJarFileModLocator extends AbstractJarFileModProvider implements IModLocator
|
||||
{
|
||||
@Override
|
||||
public List<IModLocator.ModFileOrException> scanMods()
|
||||
{
|
||||
return scanCandidates().map(this::createMod).toList();
|
||||
}
|
||||
|
||||
public abstract Stream<Path> scanCandidates();
|
||||
}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import cpw.mods.jarhandling.SecureJar;
|
||||
import net.minecraftforge.fml.loading.LogMarkers;
|
||||
import net.minecraftforge.forgespi.locating.IModFile;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public abstract class AbstractJarFileModProvider extends AbstractModProvider
|
||||
{
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
@Override
|
||||
public void scanFile(final IModFile file, final Consumer<Path> pathConsumer) {
|
||||
LOGGER.debug(LogMarkers.SCAN,"Scan started: {}", file);
|
||||
final Function<Path, SecureJar.Status> status = p->file.getSecureJar().verifyPath(p);
|
||||
try (Stream<Path> files = Files.find(file.getSecureJar().getRootPath(), Integer.MAX_VALUE, (p, a) -> p.getNameCount() > 0 && p.getFileName().toString().endsWith(".class"))) {
|
||||
file.setSecurityStatus(files.peek(pathConsumer).map(status).reduce((s1, s2)-> SecureJar.Status.values()[Math.min(s1.ordinal(), s2.ordinal())]).orElse(SecureJar.Status.INVALID));
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
LOGGER.debug(LogMarkers.SCAN,"Scan finished: {}", file);
|
||||
}
|
||||
}
|
||||
+53
-11
@@ -16,26 +16,28 @@ import net.minecraftforge.forgespi.locating.IModFile;
|
||||
import net.minecraftforge.forgespi.locating.IModLocator;
|
||||
import net.minecraftforge.forgespi.locating.IModProvider;
|
||||
import net.minecraftforge.forgespi.locating.ModFileLoadingException;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.jar.Manifest;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public abstract class AbstractModProvider implements IModProvider {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
protected static final String MODS_TOML = "META-INF/mods.toml";
|
||||
protected static final String MANIFEST = "META-INF/MANIFEST.MF";
|
||||
|
||||
protected IModLocator.ModFileOrException createMod(Path... path) {
|
||||
protected IModLocator.ModFileOrException createMod(Path path) {
|
||||
var mjm = new ModJarMetadata();
|
||||
var sj = SecureJar.from(
|
||||
Manifest::new,
|
||||
jar -> jar.moduleDataProvider().findFile(MODS_TOML).isPresent() ? mjm : JarMetadata.from(jar, path),
|
||||
path
|
||||
);
|
||||
@@ -49,18 +51,18 @@ public abstract class AbstractModProvider implements IModProvider {
|
||||
LOGGER.debug(LogMarkers.SCAN, "Found {} mod of type {}: {}", MODS_TOML, type, path);
|
||||
mod = new ModFile(sj, this, ModFileParser::modsTomlParser);
|
||||
} else if (type != null) {
|
||||
LOGGER.debug(LogMarkers.SCAN, "Found {} mod of type {}: {}", MANIFEST, type, path);
|
||||
LOGGER.debug(LogMarkers.SCAN, "Found {} mod of type {}: {}", JarFile.MANIFEST_NAME, type, path);
|
||||
mod = new ModFile(sj, this, this::manifestParser, type);
|
||||
} else
|
||||
return new IModLocator.ModFileOrException(null, new ModFileLoadingException("Invalid mod file found " + Arrays.toString(path)));
|
||||
return new IModLocator.ModFileOrException(null, new ModFileLoadingException("Invalid mod file found " + path));
|
||||
|
||||
mjm.setModFile(mod);
|
||||
return new IModLocator.ModFileOrException(mod, null);
|
||||
}
|
||||
|
||||
protected IModFileInfo manifestParser(final IModFile mod) {
|
||||
Function<String, Optional<String>> cfg = name -> Optional.ofNullable(mod.getSecureJar().moduleDataProvider().getManifest().getMainAttributes().getValue(name));
|
||||
var license = cfg.apply("LICENSE").orElse("");
|
||||
var mf = mod.getSecureJar().moduleDataProvider().getManifest().getMainAttributes();
|
||||
var license = mf.getValue("LICENSE");
|
||||
var dummy = new IConfigurable() {
|
||||
@Override
|
||||
public <T> Optional<T> getConfigElement(String... key) {
|
||||
@@ -72,7 +74,7 @@ public abstract class AbstractModProvider implements IModProvider {
|
||||
}
|
||||
};
|
||||
|
||||
return new DefaultModFileInfo(mod, license, dummy);
|
||||
return new DefaultModFileInfo(mod, license == null ? "" : license, dummy);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -80,10 +82,50 @@ public abstract class AbstractModProvider implements IModProvider {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initArguments(final Map<String, ?> arguments) {
|
||||
}
|
||||
|
||||
protected String getDefaultJarModType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scanFile(IModFile file, Consumer<Path> pathConsumer) {
|
||||
LOGGER.debug(LogMarkers.SCAN, "Scan started: {}", file);
|
||||
var jar = file.getSecureJar();
|
||||
var root = jar.getRootPath();
|
||||
Consumer<Path> consumer = pathConsumer;
|
||||
|
||||
//TODO: [SecureJar] Actually redesign this to have a proper validation.
|
||||
var holder = new Holder<SecureJar.Status>();
|
||||
holder.value = SecureJar.Status.NONE;
|
||||
if (jar.hasSecurityData()) {
|
||||
consumer = path -> {
|
||||
pathConsumer.accept(path);
|
||||
var status = jar.verifyPath(path);
|
||||
if (status.ordinal() < holder.value.ordinal())
|
||||
holder.value = status;
|
||||
};
|
||||
}
|
||||
|
||||
try {
|
||||
Files.walk(root)
|
||||
.filter(p -> p.toString().endsWith(".class"))
|
||||
.forEach(consumer);
|
||||
|
||||
file.setSecurityStatus(holder.value);
|
||||
} catch(IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
LOGGER.debug(LogMarkers.SCAN, "Scan finished: {}", file);
|
||||
}
|
||||
|
||||
private static class Holder<T> {
|
||||
T value;
|
||||
}
|
||||
|
||||
private record DefaultModFileInfo(IModFile mod, String license, IConfigurable configurable) implements IModFileInfo, IConfigurable {
|
||||
@Override public <T> Optional<T> getConfigElement(final String... strings) { return Optional.empty(); }
|
||||
@Override public List<? extends IConfigurable> getConfigList(final String... strings) { return null; }
|
||||
|
||||
+78
-37
@@ -5,68 +5,109 @@
|
||||
|
||||
package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import net.minecraftforge.fml.loading.ClasspathLocatorUtils;
|
||||
import com.mojang.logging.LogUtils;
|
||||
|
||||
import net.minecraftforge.fml.loading.LogMarkers;
|
||||
import net.minecraftforge.forgespi.locating.IModLocator;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
public class ClasspathLocator extends AbstractJarFileModLocator {
|
||||
@ApiStatus.Internal
|
||||
public class ClasspathLocator extends AbstractModProvider implements IModLocator {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private final List<Path> legacyClasspath = Arrays.stream(System.getProperty("legacyClassPath", "").split(File.pathSeparator)).map(Path::of).toList();
|
||||
private boolean enabled = false;
|
||||
private static final Attributes.Name MOD_TYPE = new Attributes.Name("FMLModType");
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "userdev classpath";
|
||||
return "classpath";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Path> scanCandidates() {
|
||||
if (!enabled)
|
||||
return Stream.of();
|
||||
public List<ModFileOrException> scanMods() {
|
||||
//var cl = ClassLoader.getSystemClassLoader();
|
||||
var cl = getClass().getClassLoader();
|
||||
|
||||
try {
|
||||
var claimed = new ArrayList<>(legacyClasspath);
|
||||
var paths = Stream.<Path>builder();
|
||||
// Find 'minecraft' during dev time this is also Forge. So skip it.
|
||||
// Can return null for dedicated server as Minecraft isn't on the classpath
|
||||
// On the client it doesn't matter as it's the vanilla jar so wouldnt have mods.toml in it.
|
||||
var minecraft = getPathFromResource(cl, "net/minecraft/obfuscate/DontObfuscate.class");
|
||||
|
||||
findPaths(claimed, MODS_TOML).forEach(paths::add);
|
||||
findPaths(claimed, MANIFEST).forEach(paths::add);
|
||||
|
||||
return paths.build();
|
||||
} catch (IOException e) {
|
||||
LOGGER.error(LogMarkers.SCAN, "Error trying to find resources", e);
|
||||
throw new RuntimeException(e);
|
||||
var claimed = new HashSet<Path>();
|
||||
var tomls = getUrls(cl, MODS_TOML);
|
||||
for (var url : tomls) {
|
||||
var path = getPath(url, MODS_TOML);
|
||||
if (!path.equals(minecraft))
|
||||
claimed.add(path);
|
||||
}
|
||||
}
|
||||
|
||||
private List<Path> findPaths(List<Path> claimed, String resource) throws IOException {
|
||||
var ret = new ArrayList<Path>();
|
||||
final Enumeration<URL> resources = ClassLoader.getSystemClassLoader().getResources(resource);
|
||||
while (resources.hasMoreElements()) {
|
||||
URL url = resources.nextElement();
|
||||
Path path = ClasspathLocatorUtils.findJarPathFor(resource, resource, url);
|
||||
if (claimed.stream().anyMatch(path::equals) || !Files.exists(path) || Files.isDirectory(path))
|
||||
var manifests = getUrls(cl, JarFile.MANIFEST_NAME);
|
||||
for (var url : manifests) {
|
||||
var path = getPath(url, JarFile.MANIFEST_NAME);
|
||||
if (claimed.contains(path))
|
||||
continue;
|
||||
ret.add(path);
|
||||
|
||||
try (var is = url.openStream()) {
|
||||
var mf = new Manifest(is);
|
||||
var type = mf.getMainAttributes().getValue(MOD_TYPE);
|
||||
if (type != null)
|
||||
claimed.add(path);
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn(LogMarkers.SCAN, "Error reading manifest from: " + url, e);
|
||||
}
|
||||
}
|
||||
|
||||
var ret = new ArrayList<ModFileOrException>();
|
||||
for (var path : claimed)
|
||||
ret.add(createMod(path));
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initArguments(Map<String, ?> arguments) {
|
||||
var launchTarget = (String) arguments.get("launchTarget");
|
||||
enabled = launchTarget != null && launchTarget.contains("dev");
|
||||
private List<URL> getUrls(ClassLoader cl, String resource) {
|
||||
try {
|
||||
var lst = Collections.list(cl.getResources(resource));
|
||||
if (LOGGER.isDebugEnabled(LogMarkers.SCAN)) {
|
||||
Collections.sort(lst, (a, b) -> a.toString().compareTo(b.toString()));
|
||||
LOGGER.debug(LogMarkers.SCAN, "Scanning Classloader: {} for {}", cl, resource);
|
||||
for (var url : lst)
|
||||
LOGGER.debug(LogMarkers.SCAN, "\t{}", url);
|
||||
}
|
||||
return lst;
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn(LogMarkers.SCAN, "Error finding resources for: " + resource, e);
|
||||
return List.of();
|
||||
}
|
||||
}
|
||||
|
||||
private static Path getPath(URL url, String resource) {
|
||||
var str = url.toString();
|
||||
int len = resource.length();
|
||||
if ("jar".equalsIgnoreCase(url.getProtocol())) {
|
||||
str = url.getFile();
|
||||
len += 2;
|
||||
}
|
||||
str = str.substring(0, str.length() - len);
|
||||
var path = Paths.get(URI.create(str));
|
||||
return path;
|
||||
}
|
||||
|
||||
private static Path getPathFromResource(ClassLoader cl, String resource) {
|
||||
var url = cl.getResource(resource);
|
||||
if (url == null)
|
||||
return null;
|
||||
return getPath(url, resource);
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,9 @@ import java.io.Reader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public class CoreModFile implements ICoreModFile {
|
||||
private final Path internalPath;
|
||||
private final ModFile file;
|
||||
|
||||
-78
@@ -1,78 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraftforge.fml.loading.LogMarkers;
|
||||
import net.minecraftforge.forgespi.locating.IModFile;
|
||||
import net.minecraftforge.forgespi.locating.IModLocator;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ExplodedDirectoryLocator implements IModLocator {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
public record ExplodedMod(String modid, List<Path> paths) {}
|
||||
|
||||
private final List<ExplodedMod> explodedMods = new ArrayList<>();
|
||||
private final Map<ExplodedMod, IModFile> mods = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public List<IModLocator.ModFileOrException> scanMods() {
|
||||
explodedMods.forEach(explodedMod ->
|
||||
ModJarMetadata.buildFile(this,
|
||||
jar->jar.moduleDataProvider().findFile("/META-INF/mods.toml").isPresent(),
|
||||
null,
|
||||
explodedMod.paths().toArray(Path[]::new))
|
||||
.ifPresentOrElse(f->mods.put(explodedMod, f), () -> LOGGER.warn(LogMarkers.LOADING, "Failed to find exploded resource mods.toml in directory {}", explodedMod.paths().get(0).toString())));
|
||||
return mods.values().stream().map(mf->new IModLocator.ModFileOrException(mf, null)).toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "exploded directory";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scanFile(final IModFile file, final Consumer<Path> pathConsumer) {
|
||||
LOGGER.debug(LogMarkers.SCAN,"Scanning exploded directory {}", file.getFilePath().toString());
|
||||
try (Stream<Path> files = Files.find(file.getSecureJar().getRootPath(), Integer.MAX_VALUE, (p, a) -> p.getNameCount() > 0 && p.getFileName().toString().endsWith(".class"))) {
|
||||
files.forEach(pathConsumer);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
LOGGER.debug(LogMarkers.SCAN,"Exploded directory scan complete {}", file.getFilePath().toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "{ExplodedDir locator}";
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void initArguments(final Map<String, ?> arguments) {
|
||||
final var explodedTargets = ((Map<String, List<ExplodedMod>>) arguments).get("explodedTargets");
|
||||
if (explodedTargets != null && !explodedTargets.isEmpty()) {
|
||||
explodedMods.addAll(explodedTargets);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValid(final IModFile modFile) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
+60
-52
@@ -10,17 +10,21 @@ import com.google.common.collect.Lists;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraftforge.fml.loading.EarlyLoadingException;
|
||||
import net.minecraftforge.forgespi.language.IModInfo;
|
||||
import net.minecraftforge.forgespi.locating.IDependencyLocator;
|
||||
import net.minecraftforge.forgespi.locating.IModFile;
|
||||
import net.minecraftforge.forgespi.locating.ModFileLoadingException;
|
||||
import net.minecraftforge.jarjar.selection.JarSelector;
|
||||
import org.apache.maven.artifact.versioning.ArtifactVersion;
|
||||
import org.apache.maven.artifact.versioning.VersionRange;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
@@ -30,26 +34,35 @@ import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class JarInJarDependencyLocator extends AbstractJarFileDependencyLocator
|
||||
{
|
||||
@ApiStatus.Internal
|
||||
public class JarInJarDependencyLocator extends AbstractModProvider implements IDependencyLocator {
|
||||
private static final String COLOR_CODE = "\u00a7";
|
||||
private static final String RESET = COLOR_CODE + "r";
|
||||
private static final String YELLOW = COLOR_CODE + "e";
|
||||
private static final String RED = COLOR_CODE + "4";
|
||||
private static final String GREEN = COLOR_CODE + "2";
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
@Override
|
||||
public String name()
|
||||
{
|
||||
public String name() {
|
||||
return "JarInJar";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<IModFile> scanMods(final Iterable<IModFile> loadedMods)
|
||||
{
|
||||
public List<IModFile> scanMods(Iterable<IModFile> loadedMods) {
|
||||
final List<IModFile> sources = Lists.newArrayList();
|
||||
loadedMods.forEach(sources::add);
|
||||
|
||||
final List<IModFile> dependenciesToLoad = JarSelector.detectAndSelect(sources, this::loadResourceFromModFile, this::loadModFileFrom, this::identifyMod, this::exception);
|
||||
var dependenciesToLoad = JarSelector.detectAndSelect(
|
||||
sources,
|
||||
this::loadResourceFromModFile,
|
||||
this::loadModFileFrom,
|
||||
this::identifyMod,
|
||||
this::exception
|
||||
);
|
||||
|
||||
if (dependenciesToLoad.isEmpty())
|
||||
{
|
||||
if (dependenciesToLoad.isEmpty()) {
|
||||
LOGGER.info("No dependencies to load found. Skipping!");
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@@ -59,43 +72,27 @@ public class JarInJarDependencyLocator extends AbstractJarFileDependencyLocator
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initArguments(final Map<String, ?> arguments)
|
||||
{
|
||||
// NO-OP, for now
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getDefaultJarModType()
|
||||
{
|
||||
protected String getDefaultJarModType() {
|
||||
return IModFile.Type.GAMELIBRARY.name();
|
||||
}
|
||||
|
||||
@SuppressWarnings("resource")
|
||||
@Override
|
||||
protected Optional<IModFile> loadModFileFrom(final IModFile file, final Path path)
|
||||
{
|
||||
try
|
||||
{
|
||||
protected Optional<IModFile> loadModFileFrom(IModFile file, Path path) {
|
||||
try {
|
||||
final Path pathInModFile = file.findResource(path.toString());
|
||||
final URI filePathUri = new URI("jij:" + (pathInModFile.toAbsolutePath().toUri().getRawSchemeSpecificPart())).normalize();
|
||||
final Map<String, ?> outerFsArgs = ImmutableMap.of("packagePath", pathInModFile);
|
||||
final FileSystem zipFS = FileSystems.newFileSystem(filePathUri, outerFsArgs);
|
||||
final Path pathInFS = zipFS.getPath("/");
|
||||
return Optional.of(createMod(pathInFS).file());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Failed to load mod file {} from {}", path, file.getFileName());
|
||||
final RuntimeException exception = new ModFileLoadingException("Failed to load mod file " + file.getFileName());
|
||||
exception.initCause(e);
|
||||
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
protected EarlyLoadingException exception(Collection<JarSelector.ResolutionFailureInformation<IModFile>> failedDependencies)
|
||||
{
|
||||
|
||||
protected EarlyLoadingException exception(Collection<JarSelector.ResolutionFailureInformation<IModFile>> failedDependencies) {
|
||||
final List<EarlyLoadingException.ExceptionData> errors = failedDependencies.stream()
|
||||
.filter(entry -> !entry.sources().isEmpty()) //Should never be the case, but just to be sure
|
||||
.map(this::buildExceptionData)
|
||||
@@ -105,8 +102,7 @@ public class JarInJarDependencyLocator extends AbstractJarFileDependencyLocator
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private EarlyLoadingException.ExceptionData buildExceptionData(final JarSelector.ResolutionFailureInformation<IModFile> entry)
|
||||
{
|
||||
private EarlyLoadingException.ExceptionData buildExceptionData(JarSelector.ResolutionFailureInformation<IModFile> entry) {
|
||||
return new EarlyLoadingException.ExceptionData(
|
||||
getErrorTranslationKey(entry),
|
||||
entry.identifier().group() + ":" + entry.identifier().artifact(),
|
||||
@@ -119,16 +115,14 @@ public class JarInJarDependencyLocator extends AbstractJarFileDependencyLocator
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String getErrorTranslationKey(final JarSelector.ResolutionFailureInformation<IModFile> entry)
|
||||
{
|
||||
private String getErrorTranslationKey(JarSelector.ResolutionFailureInformation<IModFile> entry) {
|
||||
return entry.failureReason() == JarSelector.FailureReason.VERSION_RESOLUTION_FAILED ?
|
||||
"fml.dependencyloading.conflictingdependencies" :
|
||||
"fml.dependencyloading.mismatchedcontaineddependencies";
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private Stream<ModWithVersionRange> getModWithVersionRangeStream(final JarSelector.SourceWithRequestedVersionRange<IModFile> file)
|
||||
{
|
||||
private Stream<ModWithVersionRange> getModWithVersionRangeStream(JarSelector.SourceWithRequestedVersionRange<IModFile> file) {
|
||||
return file.sources()
|
||||
.stream()
|
||||
.map(IModFile::getModFileInfo)
|
||||
@@ -136,23 +130,37 @@ public class JarInJarDependencyLocator extends AbstractJarFileDependencyLocator
|
||||
.map(modInfo -> new ModWithVersionRange(modInfo, file.requestedVersionRange(), file.includedVersion()));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private String formatError(final ModWithVersionRange modWithVersionRange)
|
||||
{
|
||||
return "\u00a7e" + modWithVersionRange.modInfo().getModId() + "\u00a7r - \u00a74" + modWithVersionRange.versionRange().toString() + "\u00a74 - \u00a72" + modWithVersionRange.artifactVersion().toString() + "\u00a72";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String identifyMod(final IModFile modFile)
|
||||
{
|
||||
if (modFile.getModFileInfo() == null || modFile.getModInfos().isEmpty())
|
||||
{
|
||||
return modFile.getFileName();
|
||||
protected Optional<InputStream> loadResourceFromModFile(IModFile modFile, Path path) {
|
||||
try {
|
||||
var pathInModFile = modFile.findResource(path.toString());
|
||||
if (!Files.exists(pathInModFile)) {
|
||||
LOGGER.debug("Failed to load resource {} from {}, it does not contain dependency information.", path, modFile.getFileName());
|
||||
return Optional.empty();
|
||||
}
|
||||
return Optional.of(Files.newInputStream(pathInModFile));
|
||||
} catch (Exception e) {
|
||||
LOGGER.error("Failed to load resource {} from mod {}, cause {}", path, modFile.getFileName(), e);
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
return modFile.getModInfos().stream().map(IModInfo::getModId).collect(Collectors.joining());
|
||||
}
|
||||
|
||||
private record ModWithVersionRange(IModInfo modInfo, VersionRange versionRange, ArtifactVersion artifactVersion)
|
||||
{}
|
||||
@NotNull
|
||||
private String formatError(ModWithVersionRange modWithVersionRange){
|
||||
return YELLOW + modWithVersionRange.modInfo().getModId() + RESET + " - " +
|
||||
RED + modWithVersionRange.versionRange().toString() + RESET + " - " +
|
||||
GREEN + modWithVersionRange.artifactVersion().toString() + RESET;
|
||||
}
|
||||
|
||||
protected String identifyMod(IModFile modFile) {
|
||||
if (modFile.getModFileInfo() != null && !modFile.getModInfos().isEmpty())
|
||||
return modFile.getModInfos().stream().map(IModInfo::getModId).collect(Collectors.joining());
|
||||
|
||||
var module = modFile.getSecureJar().moduleDataProvider().name();
|
||||
if (module != null && !module.isEmpty())
|
||||
return module;
|
||||
|
||||
return modFile.getFileName();
|
||||
}
|
||||
|
||||
private record ModWithVersionRange(IModInfo modInfo, VersionRange versionRange, ArtifactVersion artifactVersion) {}
|
||||
}
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import net.minecraftforge.fml.loading.FMLPaths;
|
||||
import net.minecraftforge.fml.loading.MavenCoordinateResolver;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class MavenDirectoryLocator extends AbstractJarFileModLocator
|
||||
{
|
||||
private List<Path> modCoords;
|
||||
|
||||
@Override
|
||||
public Stream<Path> scanCandidates() {
|
||||
return modCoords.stream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "maven libs";
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "{Maven Directory locator for mods "+this.modCoords+"}";
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void initArguments(final Map<String, ?> arguments) {
|
||||
final List<String> mavenRoots = (List<String>) arguments.get("mavenRoots");
|
||||
final List<Path> mavenRootPaths = mavenRoots.stream().map(n -> FMLPaths.GAMEDIR.get().resolve(n)).collect(Collectors.toList());
|
||||
final List<String> mods = (List<String>) arguments.get("mods");
|
||||
final List<String> listedMods = ModListHandler.processModLists((List<String>) arguments.get("modLists"), mavenRootPaths);
|
||||
|
||||
List<Path> localModCoords = Stream.concat(mods.stream(),listedMods.stream()).map(MavenCoordinateResolver::get).collect(Collectors.toList());
|
||||
// find the modCoords path in each supplied maven path, and turn it into a mod file. (skips not found files)
|
||||
|
||||
this.modCoords = localModCoords.stream().map(mc -> mavenRootPaths.stream().map(root -> root.resolve(mc)).filter(path -> Files.exists(path)).findFirst().orElseThrow(() -> new IllegalArgumentException("Failed to locate requested mod coordinate " + mc))).collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
+16
-76
@@ -6,62 +6,36 @@
|
||||
package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import com.electronwill.nightconfig.core.Config;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import cpw.mods.jarhandling.SecureJar;
|
||||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
import net.minecraftforge.fml.loading.LogMarkers;
|
||||
import net.minecraftforge.forgespi.language.IModFileInfo;
|
||||
import net.minecraftforge.forgespi.locating.IModFile;
|
||||
import net.minecraftforge.forgespi.locating.IModLocator;
|
||||
import net.minecraftforge.forgespi.locating.ModFileFactory;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class MinecraftLocator extends AbstractModProvider implements IModLocator
|
||||
{
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public class MinecraftLocator extends AbstractModProvider implements IModLocator {
|
||||
@Override
|
||||
public List<IModLocator.ModFileOrException> scanMods() {
|
||||
final var launchHandler = FMLLoader.getLaunchHandler();
|
||||
var baseMC = launchHandler.getMinecraftPaths();
|
||||
var mcjar = ModJarMetadata.buildFile(j->ModFileFactory.FACTORY.build(j, this, this::buildMinecraftTOML), j->true, baseMC.minecraftFilter(), baseMC.minecraftPaths().toArray(Path[]::new)).orElseThrow();
|
||||
var artifacts = baseMC.otherArtifacts().stream()
|
||||
.map(SecureJar::from)
|
||||
.map(sj -> new ModFile(sj, this, ModFileParser::modsTomlParser))
|
||||
.collect(Collectors.<IModFile>toList());
|
||||
var othermods = baseMC.otherModPaths().stream()
|
||||
.map(p -> createMod(p.toArray(Path[]::new)))
|
||||
.filter(Objects::nonNull);
|
||||
artifacts.add(mcjar);
|
||||
var minecraft = FMLLoader.getLaunchHandler().getMinecraftPaths();
|
||||
|
||||
return Stream.concat(artifacts.stream().map(f -> new ModFileOrException(f, null)), othermods).toList();
|
||||
// Minecraft itself.
|
||||
var meta = new ModJarMetadata();
|
||||
var mcjar = SecureJar.from(
|
||||
jar -> meta,
|
||||
minecraft.toArray(Path[]::new)
|
||||
);
|
||||
var mc = ModFileFactory.FACTORY.build(mcjar, this, this::buildMinecraftTOML);
|
||||
meta.setModFile(mc);
|
||||
|
||||
return List.of(new ModFileOrException(mc, null));
|
||||
}
|
||||
|
||||
private IModFileInfo buildMinecraftTOML(final IModFile iModFile) {
|
||||
final ModFile modFile = (ModFile) iModFile;
|
||||
/*
|
||||
final Path mcmodtoml = modFile.findResource("META-INF", "minecraftmod.toml");
|
||||
if (Files.notExists(mcmodtoml)) {
|
||||
LOGGER.fatal(LOADING, "Mod file {} is missing minecraftmod.toml file", modFile.getFilePath());
|
||||
return null;
|
||||
}
|
||||
|
||||
final FileConfig mcmodstomlfile = FileConfig.builder(mcmodtoml).build();
|
||||
mcmodstomlfile.load();
|
||||
mcmodstomlfile.close();
|
||||
*/
|
||||
|
||||
|
||||
// We haven't changed this in years, and I can't be asked right now to special case this one file in the path.
|
||||
final var conf = Config.inMemory();
|
||||
conf.set("modLoader", "minecraft");
|
||||
@@ -76,47 +50,13 @@ public class MinecraftLocator extends AbstractModProvider implements IModLocator
|
||||
mods.set("authors", "MCP: Searge,ProfMobius,IngisKahn,Fesh0r,ZeuX,R4wk,LexManos,Bspkrs");
|
||||
mods.set("description", "Minecraft, decompiled and deobfuscated with MCP technology");
|
||||
conf.set("mods", List.of(mods));
|
||||
/*
|
||||
conf.putAll(mcmodstomlfile);
|
||||
|
||||
final var extralangs = Stream.<IModFileInfo.LanguageSpec>builder();
|
||||
final Path forgemodtoml = modFile.findResource("META-INF", "mods.toml");
|
||||
if (Files.notExists(forgemodtoml)) {
|
||||
LOGGER.info("No forge mods.toml file found, not loading forge mod");
|
||||
} else {
|
||||
final FileConfig forgemodstomlfile = FileConfig.builder(forgemodtoml).build();
|
||||
forgemodstomlfile.load();
|
||||
forgemodstomlfile.close();
|
||||
conf.putAll(forgemodstomlfile);
|
||||
conf.<List<Object>>get("mods").add(0, mcmodstomlfile.<List<Object>>get("mods").get(0)); // Add MC as a sub-mod
|
||||
extralangs.add(new IModFileInfo.LanguageSpec(mcmodstomlfile.get("modLoader"), MavenVersionAdapter.createFromVersionSpec(mcmodstomlfile.get("loaderVersion"))));
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
final NightConfigWrapper configWrapper = new NightConfigWrapper(conf);
|
||||
//final ModFileInfo modFileInfo = new ModFileInfo(modFile, configWrapper, extralangs.build().toList());
|
||||
return new ModFileInfo(modFile, configWrapper, configWrapper::setFile, List.of());
|
||||
var configWrapper = new NightConfigWrapper(conf);
|
||||
return new ModFileInfo((ModFile)iModFile, configWrapper, configWrapper::setFile, List.of());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "minecraft";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scanFile(final IModFile modFile, final Consumer<Path> pathConsumer) {
|
||||
LOGGER.debug(LogMarkers.SCAN, "Scan started: {}", modFile);
|
||||
try (Stream<Path> files = Files.find(modFile.getSecureJar().getRootPath(), Integer.MAX_VALUE, (p, a) -> p.getNameCount() > 0 && p.getFileName().toString().endsWith(".class"))) {
|
||||
files.forEach(pathConsumer);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
LOGGER.debug(LogMarkers.SCAN, "Scan finished: {}", modFile);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initArguments(final Map<String, ?> arguments) {
|
||||
// no op
|
||||
}
|
||||
}
|
||||
+32
-62
@@ -7,65 +7,42 @@ package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraftforge.forgespi.language.ModFileScanData;
|
||||
import net.minecraftforge.forgespi.language.ModFileScanData.EnumData;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
|
||||
public class ModAnnotation
|
||||
{
|
||||
public static ModFileScanData.AnnotationData fromModAnnotation(final Type clazz, final ModAnnotation annotation) {
|
||||
@ApiStatus.Internal
|
||||
class ModAnnotation {
|
||||
public static ModFileScanData.AnnotationData fromModAnnotation(Type clazz, ModAnnotation annotation) {
|
||||
return new ModFileScanData.AnnotationData(annotation.asmType, annotation.type, clazz, annotation.member, annotation.values);
|
||||
}
|
||||
|
||||
public static class EnumHolder
|
||||
{
|
||||
private final String desc;
|
||||
private final String value;
|
||||
|
||||
public EnumHolder(String desc, String value)
|
||||
{
|
||||
this.desc = desc;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getDesc()
|
||||
{
|
||||
return desc;
|
||||
}
|
||||
|
||||
public String getValue()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
private final ElementType type;
|
||||
private final Type asmType;
|
||||
private final String member;
|
||||
private final Map<String,Object> values = Maps.newHashMap();
|
||||
private final Map<String,Object> values = new HashMap<>();
|
||||
|
||||
private ArrayList<Object> arrayList;
|
||||
private String arrayName;
|
||||
public ModAnnotation(ElementType type, Type asmType, String member)
|
||||
{
|
||||
public ModAnnotation(ElementType type, Type asmType, String member) {
|
||||
this.type = type;
|
||||
this.asmType = asmType;
|
||||
this.member = member;
|
||||
}
|
||||
|
||||
public ModAnnotation(Type asmType, ModAnnotation parent)
|
||||
{
|
||||
this.type = parent.type;
|
||||
this.asmType = asmType;
|
||||
this.member = parent.member;
|
||||
|
||||
public ModAnnotation(Type asmType, ModAnnotation parent) {
|
||||
this(parent.type, asmType, parent.member);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
public String toString() {
|
||||
return MoreObjects.toStringHelper("Annotation")
|
||||
.add("type",type)
|
||||
.add("name",asmType.getClassName())
|
||||
@@ -74,52 +51,45 @@ public class ModAnnotation
|
||||
.toString();
|
||||
}
|
||||
|
||||
public ElementType getType()
|
||||
{
|
||||
public ElementType getType() {
|
||||
return type;
|
||||
}
|
||||
public Type getASMType()
|
||||
{
|
||||
|
||||
public Type getASMType() {
|
||||
return asmType;
|
||||
}
|
||||
public String getMember()
|
||||
{
|
||||
|
||||
public String getMember() {
|
||||
return member;
|
||||
}
|
||||
public Map<String, Object> getValues()
|
||||
{
|
||||
|
||||
public Map<String, Object> getValues() {
|
||||
return values;
|
||||
}
|
||||
public void addArray(String name)
|
||||
{
|
||||
this.arrayList = Lists.newArrayList();
|
||||
|
||||
public void addArray(String name) {
|
||||
this.arrayList = new ArrayList<>();
|
||||
this.arrayName = name;
|
||||
}
|
||||
public void addProperty(String key, Object value)
|
||||
{
|
||||
|
||||
public void addProperty(String key, Object value) {
|
||||
if (this.arrayList != null)
|
||||
{
|
||||
arrayList.add(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
values.put(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void addEnumProperty(String key, String enumName, String value)
|
||||
{
|
||||
addProperty(key, new EnumHolder(enumName, value));
|
||||
public void addEnumProperty(String key, String enumName, String value) {
|
||||
addProperty(key, new EnumData(Type.getType(enumName), value));
|
||||
}
|
||||
|
||||
public void endArray()
|
||||
{
|
||||
public void endArray() {
|
||||
values.put(arrayName, arrayList);
|
||||
arrayList = null;
|
||||
}
|
||||
public ModAnnotation addChildAnnotation(String name, String desc)
|
||||
{
|
||||
ModAnnotation child = new ModAnnotation(Type.getType(desc), this);
|
||||
|
||||
public ModAnnotation addChildAnnotation(String name, String desc) {
|
||||
var child = new ModAnnotation(Type.getType(desc), this);
|
||||
addProperty(name, child.getValues());
|
||||
return child;
|
||||
}
|
||||
|
||||
-67
@@ -1,67 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
public class ModAnnotationVisitor extends AnnotationVisitor {
|
||||
private final ModAnnotation annotation;
|
||||
private LinkedList<ModAnnotation> annotations;
|
||||
private boolean array;
|
||||
private boolean isSubAnnotation;
|
||||
|
||||
public ModAnnotationVisitor(LinkedList<ModAnnotation> annotations, ModAnnotation annotation) {
|
||||
super(Opcodes.ASM9);
|
||||
this.annotations = annotations;
|
||||
this.annotation = annotation;
|
||||
}
|
||||
|
||||
public ModAnnotationVisitor(LinkedList<ModAnnotation> annotations, ModAnnotation annotation, String name) {
|
||||
this(annotations, annotation);
|
||||
this.array = true;
|
||||
annotation.addArray(name);
|
||||
}
|
||||
|
||||
public ModAnnotationVisitor(LinkedList<ModAnnotation> annotations, ModAnnotation annotation, boolean isSubAnnotation) {
|
||||
this(annotations, annotation);
|
||||
this.isSubAnnotation = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(String key, Object value) {
|
||||
annotation.addProperty(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnum(String name, String desc, String value) {
|
||||
annotation.addEnumProperty(name, desc, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitArray(String name) {
|
||||
return new ModAnnotationVisitor(annotations, annotation, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String name, String desc) {
|
||||
var ma = annotations.getFirst();
|
||||
var childAnnotation = ma.addChildAnnotation(name, desc);
|
||||
annotations.addFirst(childAnnotation);
|
||||
return new ModAnnotationVisitor(annotations, childAnnotation, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
if (array)
|
||||
annotation.endArray();
|
||||
|
||||
if (isSubAnnotation)
|
||||
annotations.addLast(annotations.removeFirst());
|
||||
}
|
||||
}
|
||||
+77
-6
@@ -6,6 +6,8 @@
|
||||
package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import net.minecraftforge.forgespi.language.ModFileScanData;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
@@ -19,13 +21,14 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ModClassVisitor extends ClassVisitor {
|
||||
@ApiStatus.Internal
|
||||
class ModClassVisitor extends ClassVisitor {
|
||||
private Type asmType;
|
||||
private Type asmSuperType;
|
||||
private Set<Type> interfaces;
|
||||
private final LinkedList<ModAnnotation> annotations = new LinkedList<>();
|
||||
|
||||
public ModClassVisitor() {
|
||||
ModClassVisitor() {
|
||||
super(Opcodes.ASM9);
|
||||
}
|
||||
|
||||
@@ -38,19 +41,33 @@ public class ModClassVisitor extends ClassVisitor {
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(final String annotationName, final boolean runtimeVisible) {
|
||||
ModAnnotation ann = new ModAnnotation(ElementType.TYPE, Type.getType(annotationName), this.asmType.getClassName());
|
||||
var ann = new ModAnnotation(ElementType.TYPE, Type.getType(annotationName), this.asmType.getClassName());
|
||||
annotations.addFirst(ann);
|
||||
return new ModAnnotationVisitor(annotations, ann);
|
||||
return new ModAnnotationVisitor(ann);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
|
||||
return new ModFieldVisitor(name, annotations);
|
||||
return new FieldVisitor(Opcodes.ASM9) {
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String annotationName, boolean runtimeVisible) {
|
||||
var ann = new ModAnnotation(ElementType.FIELD, Type.getType(annotationName), name);
|
||||
annotations.addFirst(ann);
|
||||
return new ModAnnotationVisitor(ann);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
return new ModMethodVisitor(name, desc, annotations);
|
||||
return new MethodVisitor(Opcodes.ASM9) {
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String annotationName, boolean runtimeVisible) {
|
||||
ModAnnotation ann = new ModAnnotation(ElementType.METHOD, Type.getType(annotationName), name + desc);
|
||||
annotations.addFirst(ann);
|
||||
return new ModAnnotationVisitor(ann);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public void buildData(final Set<ModFileScanData.ClassData> classes, final Set<ModFileScanData.AnnotationData> baked) {
|
||||
@@ -60,4 +77,58 @@ public class ModClassVisitor extends ClassVisitor {
|
||||
baked.add(ModAnnotation.fromModAnnotation(this.asmType, a));
|
||||
}
|
||||
}
|
||||
|
||||
private class ModAnnotationVisitor extends AnnotationVisitor {
|
||||
private final ModAnnotation annotation;
|
||||
private boolean array;
|
||||
private boolean isSubAnnotation;
|
||||
|
||||
public ModAnnotationVisitor(ModAnnotation annotation) {
|
||||
super(Opcodes.ASM9);
|
||||
this.annotation = annotation;
|
||||
}
|
||||
|
||||
public ModAnnotationVisitor(ModAnnotation annotation, String name) {
|
||||
this(annotation);
|
||||
this.array = true;
|
||||
annotation.addArray(name);
|
||||
}
|
||||
|
||||
public ModAnnotationVisitor(ModAnnotation annotation, boolean isSubAnnotation) {
|
||||
this(annotation);
|
||||
this.isSubAnnotation = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(String key, Object value) {
|
||||
annotation.addProperty(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnum(String name, String desc, String value) {
|
||||
annotation.addEnumProperty(name, desc, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitArray(String name) {
|
||||
return new ModAnnotationVisitor(annotation, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String name, String desc) {
|
||||
var ma = annotations.getFirst();
|
||||
var childAnnotation = ma.addChildAnnotation(name, desc);
|
||||
annotations.addFirst(childAnnotation);
|
||||
return new ModAnnotationVisitor(childAnnotation, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
if (array)
|
||||
annotation.endArray();
|
||||
|
||||
if (isSubAnnotation)
|
||||
annotations.addLast(annotations.removeFirst());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,8 @@ import net.minecraftforge.forgespi.language.IModFileInfo;
|
||||
import net.minecraftforge.forgespi.locating.IDependencyLocator;
|
||||
import net.minecraftforge.forgespi.locating.IModFile;
|
||||
import net.minecraftforge.forgespi.locating.IModLocator;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -30,6 +32,7 @@ import java.util.Objects;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public class ModDiscoverer {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private final ServiceLoader<IModLocator> modLocators;
|
||||
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.util.LinkedList;
|
||||
|
||||
public class ModFieldVisitor extends FieldVisitor
|
||||
{
|
||||
private final LinkedList<ModAnnotation> annotations;
|
||||
private final String fieldName;
|
||||
|
||||
public ModFieldVisitor(String name, final LinkedList<ModAnnotation> annotations)
|
||||
{
|
||||
super(Opcodes.ASM9);
|
||||
this.fieldName = name;
|
||||
this.annotations = annotations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String annotationName, boolean runtimeVisible)
|
||||
{
|
||||
ModAnnotation ann = new ModAnnotation(ElementType.FIELD, Type.getType(annotationName), fieldName);
|
||||
annotations.addFirst(ann);
|
||||
return new ModAnnotationVisitor(annotations, ann);
|
||||
}
|
||||
}
|
||||
@@ -23,6 +23,8 @@ import org.slf4j.Logger;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -35,16 +37,7 @@ import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
public class ModFile implements IModFile {
|
||||
// Mods either must have a mods.toml or a manifest. We can no longer just put any jar on the classpath.
|
||||
@Deprecated(forRemoval = true, since = "1.18")
|
||||
public static final Manifest DEFAULTMANIFEST;
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
static {
|
||||
DEFAULTMANIFEST = new Manifest();
|
||||
DEFAULTMANIFEST.getMainAttributes().putValue("FMLModType", "MOD");
|
||||
}
|
||||
|
||||
private final String jarVersion;
|
||||
private final ModFileFactory.ModFileInfoParser parser;
|
||||
private Map<String, Object> fileProperties;
|
||||
@@ -176,9 +169,13 @@ public class ModFile implements IModFile {
|
||||
}
|
||||
|
||||
public void identifyLanguage() {
|
||||
this.loaders = this.modFileInfo.requiredLanguageLoaders().stream()
|
||||
.map(spec-> FMLLoader.getLanguageLoadingProvider().findLanguage(this, spec.languageName(), spec.acceptedVersions()))
|
||||
.toList();
|
||||
var lst = new ArrayList<IModLanguageProvider>(this.modFileInfo.requiredLanguageLoaders().size());
|
||||
var services = FMLLoader.getLanguageLoadingProvider();
|
||||
for (var spec : this.modFileInfo.requiredLanguageLoaders()) {
|
||||
var service = services.findLanguage(this, spec.languageName(), spec.acceptedVersions());
|
||||
lst.add(service);
|
||||
}
|
||||
this.loaders = Collections.unmodifiableList(lst);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -15,6 +15,7 @@ import net.minecraftforge.forgespi.locating.ForgeFeature;
|
||||
import org.apache.maven.artifact.versioning.ArtifactVersion;
|
||||
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
|
||||
import org.apache.maven.artifact.versioning.VersionRange;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.net.URL;
|
||||
@@ -24,8 +25,8 @@ import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ModInfo implements IModInfo, IConfigurable
|
||||
{
|
||||
@ApiStatus.Internal
|
||||
public class ModInfo implements IModInfo, IConfigurable {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private static final DefaultArtifactVersion DEFAULT_VERSION = new DefaultArtifactVersion("1");
|
||||
private static final Pattern VALID_MODID = Pattern.compile("^[a-z][a-z0-9_]{1,63}$");
|
||||
|
||||
+3
-34
@@ -6,48 +6,17 @@
|
||||
package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import cpw.mods.jarhandling.JarMetadata;
|
||||
import cpw.mods.jarhandling.SecureJar;
|
||||
import net.minecraftforge.forgespi.locating.IModFile;
|
||||
import net.minecraftforge.forgespi.locating.IModLocator;
|
||||
|
||||
import java.lang.module.ModuleDescriptor;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public final class ModJarMetadata implements JarMetadata {
|
||||
private IModFile modFile;
|
||||
private ModuleDescriptor descriptor;
|
||||
|
||||
// TODO: Remove helper functions to cleanup api
|
||||
@Deprecated(forRemoval = true, since="1.18")
|
||||
static Optional<IModFile> buildFile(IModLocator locator, Predicate<SecureJar> jarTest, BiPredicate<String, String> filter, Path... files) {
|
||||
return buildFile(j->new ModFile(j, locator, ModFileParser::modsTomlParser), jarTest, filter, files);
|
||||
}
|
||||
|
||||
// TODO: Remove helper functions to cleanup api
|
||||
@Deprecated(forRemoval = true, since="1.18")
|
||||
static IModFile buildFile(IModLocator locator, Path... files) {
|
||||
return buildFile(locator, j->true, null, files).orElseThrow(()->new IllegalArgumentException("Failed to find valid JAR file"));
|
||||
}
|
||||
|
||||
// TODO: Remove helper functions to cleanup api
|
||||
@Deprecated(forRemoval = true, since="1.18")
|
||||
static Optional<IModFile> buildFile(Function<SecureJar, IModFile> mfConstructor, Predicate<SecureJar> jarTest, BiPredicate<String, String> filter, Path... files) {
|
||||
var mjm = new ModJarMetadata();
|
||||
var sj = SecureJar.from(()->ModFile.DEFAULTMANIFEST, j->mjm, filter, files);
|
||||
if (jarTest.test(sj)) {
|
||||
var mf = mfConstructor.apply(sj);
|
||||
mjm.setModFile(mf);
|
||||
return Optional.of(mf);
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
ModJarMetadata() {
|
||||
}
|
||||
|
||||
|
||||
-93
@@ -1,93 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraftforge.fml.loading.FMLPaths;
|
||||
import net.minecraftforge.fml.loading.LogMarkers;
|
||||
import net.minecraftforge.fml.loading.MavenCoordinateResolver;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ModListHandler {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
/**
|
||||
* Reads the modList paths specified, and searches each maven root for mods matching. Returns a list of mods
|
||||
* found.
|
||||
*
|
||||
* @param modListPaths Paths to search for mod file lists
|
||||
* @param mavenRootPaths Roots to look for mods listed
|
||||
* @return list of found mod coordinates
|
||||
*/
|
||||
public static List<String> processModLists(final List<String> modListPaths, final List<Path> mavenRootPaths) {
|
||||
final List<String> modCoordinates = modListPaths.stream().map(ModListHandler::transformPathToList).
|
||||
flatMap(Collection::stream).
|
||||
collect(Collectors.toList());
|
||||
|
||||
List<Pair<Path,String>> localModCoords = modCoordinates.stream().map(mc->Pair.of(MavenCoordinateResolver.get(mc), mc)).collect(Collectors.toList());
|
||||
final List<Pair<Path, String>> foundCoordinates = localModCoords.stream().
|
||||
map(mc -> mavenRootPaths.stream().
|
||||
map(root -> Pair.of(root.resolve(mc.getLeft()), mc.getRight())).
|
||||
filter(path -> Files.exists(path.getLeft())).
|
||||
findFirst().
|
||||
orElseGet(()->{
|
||||
LOGGER.warn(LogMarkers.CORE, "Failed to find coordinate {}", mc);
|
||||
return null;
|
||||
})).
|
||||
filter(Objects::nonNull).
|
||||
collect(Collectors.toList());
|
||||
|
||||
final List<String> found = foundCoordinates.stream().map(Pair::getRight).collect(Collectors.toList());
|
||||
LOGGER.debug(LogMarkers.CORE, "Found mod coordinates from lists: {}", found);
|
||||
return found;
|
||||
}
|
||||
|
||||
private static List<String> transformPathToList(final String path) {
|
||||
LOGGER.debug(LogMarkers.CORE, "Reading mod list {}", path);
|
||||
Path filePath = FMLPaths.GAMEDIR.get().resolve(path);
|
||||
if (!Files.exists(filePath)) {
|
||||
LOGGER.warn(LogMarkers.CORE, "Failed to find modlist file at {}", filePath);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
String extension = fileExtension(filePath);
|
||||
if (Objects.equals("list",extension)) {
|
||||
return readListFile(filePath).stream().filter(s -> !s.isEmpty()).collect(Collectors.toList());
|
||||
} else {
|
||||
LOGGER.warn(LogMarkers.CORE, "Failed to read unknown file list type {} for file {}", extension, filePath);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
private static String fileExtension(final Path path) {
|
||||
String fileName = path.getFileName().toString();
|
||||
int idx = fileName.lastIndexOf('.');
|
||||
return idx > -1 ? fileName.substring(idx+1) : "";
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple list file, ending in ".list" with one mod coordinate per line
|
||||
* @param filePath path
|
||||
* @return list
|
||||
*/
|
||||
private static List<String> readListFile(final Path filePath) {
|
||||
try {
|
||||
return Files.readAllLines(filePath);
|
||||
} catch (IOException e) {
|
||||
LOGGER.warn(LogMarkers.CORE, "Failed to read file list {}", filePath, e);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
-38
@@ -1,38 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.util.LinkedList;
|
||||
|
||||
public class ModMethodVisitor extends MethodVisitor {
|
||||
|
||||
private final LinkedList<ModAnnotation> annotations;
|
||||
private String methodName;
|
||||
private String methodDescriptor;
|
||||
|
||||
public ModMethodVisitor(String name, String desc, final LinkedList<ModAnnotation> annotations)
|
||||
{
|
||||
super(Opcodes.ASM9);
|
||||
this.methodName = name;
|
||||
this.methodDescriptor = desc;
|
||||
this.annotations = annotations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String annotationName, boolean runtimeVisible)
|
||||
{
|
||||
ModAnnotation ann = new ModAnnotation(ElementType.METHOD, Type.getType(annotationName), methodName+methodDescriptor);
|
||||
annotations.addFirst(ann);
|
||||
return new ModAnnotationVisitor(annotations, ann);
|
||||
}
|
||||
|
||||
}
|
||||
+21
-21
@@ -9,33 +9,28 @@ import com.mojang.logging.LogUtils;
|
||||
import net.minecraftforge.fml.loading.FMLPaths;
|
||||
import net.minecraftforge.fml.loading.LogMarkers;
|
||||
import net.minecraftforge.fml.loading.ModDirTransformerDiscoverer;
|
||||
import net.minecraftforge.fml.loading.StringUtils;
|
||||
import net.minecraftforge.forgespi.locating.IModLocator;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Comparator;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static cpw.mods.modlauncher.api.LamdbaExceptionUtils.uncheck;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Support loading mods located in JAR files in the mods folder
|
||||
*/
|
||||
public class ModsFolderLocator extends AbstractJarFileModLocator
|
||||
{
|
||||
public class ModsFolderLocator extends AbstractModProvider implements IModLocator {
|
||||
private static final String SUFFIX = ".jar";
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private final Path modFolder;
|
||||
private final String customName;
|
||||
|
||||
public ModsFolderLocator() {
|
||||
this(FMLPaths.MODSDIR.get());
|
||||
}
|
||||
|
||||
ModsFolderLocator(Path modFolder) {
|
||||
this(modFolder, "mods folder");
|
||||
this(FMLPaths.MODSDIR.get(), "mods folder");
|
||||
}
|
||||
|
||||
ModsFolderLocator(Path modFolder, String name) {
|
||||
@@ -44,13 +39,22 @@ public class ModsFolderLocator extends AbstractJarFileModLocator
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Path> scanCandidates() {
|
||||
LOGGER.debug(LogMarkers.SCAN,"Scanning mods dir {} for mods", this.modFolder);
|
||||
public List<IModLocator.ModFileOrException> scanMods() {
|
||||
LOGGER.debug(LogMarkers.SCAN, "Scanning mods dir {} for mods", this.modFolder);
|
||||
var excluded = ModDirTransformerDiscoverer.allExcluded();
|
||||
try (var stream = Files.list(this.modFolder)) {
|
||||
return stream.filter(path -> !excluded.contains(path) && path.getFileName().toString().toLowerCase(Locale.ROOT).endsWith(SUFFIX))
|
||||
.sorted(Comparator.comparing(path -> path.getFileName().toString().toLowerCase(Locale.ROOT)))
|
||||
.map(this::createMod)
|
||||
.toList();
|
||||
} catch (IOException e) {
|
||||
return sneak(e);
|
||||
}
|
||||
}
|
||||
|
||||
return uncheck(()-> Files.list(this.modFolder))
|
||||
.filter(p-> !excluded.contains(p) && StringUtils.toLowerCase(p.getFileName().toString()).endsWith(SUFFIX))
|
||||
.sorted(Comparator.comparing(path-> StringUtils.toLowerCase(path.getFileName().toString())));
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <E extends Throwable, R> R sneak(Throwable e) throws E {
|
||||
throw (E)e;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -62,8 +66,4 @@ public class ModsFolderLocator extends AbstractJarFileModLocator
|
||||
public String toString() {
|
||||
return "{"+customName+" locator at "+this.modFolder+"}";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initArguments(final Map<String, ?> arguments) {
|
||||
}
|
||||
}
|
||||
+4
-1
@@ -15,9 +15,12 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
|
||||
public class NightConfigWrapper implements IConfigurable {
|
||||
@ApiStatus.Internal
|
||||
class NightConfigWrapper implements IConfigurable {
|
||||
private final UnmodifiableConfig config;
|
||||
private IModFileInfo file;
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ import com.mojang.logging.LogUtils;
|
||||
import net.minecraftforge.fml.loading.LogMarkers;
|
||||
import net.minecraftforge.forgespi.language.IModLanguageProvider;
|
||||
import net.minecraftforge.forgespi.language.ModFileScanData;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
@@ -18,7 +20,8 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
public class Scanner {
|
||||
@ApiStatus.Internal
|
||||
class Scanner {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private final ModFile fileToScan;
|
||||
|
||||
|
||||
+47
-98
@@ -7,15 +7,9 @@ package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import cpw.mods.jarhandling.SecureJar;
|
||||
import cpw.mods.modlauncher.api.ServiceRunner;
|
||||
import net.minecraftforge.fml.loading.LogMarkers;
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
@@ -63,55 +57,6 @@ abstract class CommonDevLaunchHandler extends CommonLaunchHandler {
|
||||
return args.getArguments();
|
||||
}
|
||||
|
||||
protected List<Path> getLibraries(String[] classpath) {
|
||||
// TODO: These should all be FMLModType: LIBRARY, find them that way?
|
||||
// Or at the very least include version info
|
||||
var fmlcore = findJarOnClasspath(classpath, "fmlcore");
|
||||
var javafmllang = findJarOnClasspath(classpath, "javafmllanguage");
|
||||
var lowcodelang = findJarOnClasspath(classpath, "lowcodelanguage");
|
||||
var mclang = findJarOnClasspath(classpath, "mclanguage");
|
||||
return List.of(fmlcore, javafmllang, lowcodelang, mclang);
|
||||
}
|
||||
|
||||
protected BiPredicate<String, String> getMcFilter(Path extra, List<Path> minecraft) {
|
||||
var packages = getPackages(); // Pulled out so it is passed to the lambda as value
|
||||
var extraPath = extra.toString().replace('\\', '/');
|
||||
|
||||
// We serve everything, except for things in the forge packages.
|
||||
BiPredicate<String, String> mcFilter = (path, base) -> {
|
||||
if (base.equals(extraPath) ||
|
||||
path.endsWith("/")) return true;
|
||||
for (var pkg : packages)
|
||||
if (path.startsWith(pkg)) return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
return mcFilter;
|
||||
}
|
||||
|
||||
protected Path getForgeMod(List<Path> minecraft) {
|
||||
var packages = getPackages(); // Pulled out so it is passed to the lambda as value
|
||||
// We need to separate out our resources/code so that we can show up as a different data pack.
|
||||
var modJar = SecureJar.from((path, base) -> {
|
||||
if (!path.endsWith(".class")) return true;
|
||||
for (var pkg : packages)
|
||||
if (path.startsWith(pkg)) return true;
|
||||
return false;
|
||||
}, minecraft.stream().distinct().toArray(Path[]::new));
|
||||
|
||||
//modJar.getPackages().stream().sorted().forEach(System.out::println);
|
||||
return modJar.getRootPath();
|
||||
}
|
||||
|
||||
private static String[] getPackages() {
|
||||
return new String[] {
|
||||
"net/minecraftforge/",
|
||||
"META-INF/services/",
|
||||
"META-INF/coremods.json",
|
||||
"META-INF/mods.toml"
|
||||
};
|
||||
}
|
||||
|
||||
private static String getRandomNumbers(int length) {
|
||||
// Generate a time-based random number, to mimic how n.m.client.Main works
|
||||
return Long.toString(System.nanoTime() % (int) Math.pow(10, length));
|
||||
@@ -122,48 +67,11 @@ abstract class CommonDevLaunchHandler extends CommonLaunchHandler {
|
||||
return super.makeService(preLaunch(arguments, gameLayer), gameLayer);
|
||||
}
|
||||
|
||||
// TODO: Get rid of this string based classpath crap when I re-write mod loader
|
||||
protected static Map<String, List<Path>> getModClasses() {
|
||||
var modClasses = Optional.ofNullable(System.getenv("MOD_CLASSES")).orElse("");
|
||||
LOGGER.debug(LogMarkers.CORE, "Got mod coordinates {} from env", modClasses);
|
||||
|
||||
/*
|
||||
* So MOD_CLASSES at the end of the day is a Map<ModId, List<Path>> but because we want to shove that into a single string.
|
||||
* And pass it in as an environment variable.. instead od using json, or a file path, or anything sane, cpw did hacky string stuff
|
||||
* Basically what we want to do is split the string by File.pathSeperator to get each key-value pair.
|
||||
* Then we find the magic string "%%" and split on that. Using the first half as the mod id and the second as the path.
|
||||
* BUT the modID is optional, and if not specified {why wouldn't it be?} it uses "defaultmodid"
|
||||
* Anyways, it was stream hell, but now it's simpler to debug/understand.
|
||||
*/
|
||||
Map<String, List<Path>> ret = new HashMap<>();
|
||||
var entries = modClasses.split(File.pathSeparator);
|
||||
for (var entry : entries) {
|
||||
var modid = "defaultmodid";
|
||||
var path = entry;
|
||||
|
||||
int idx = entry.indexOf("%%");
|
||||
if (idx != -1) {
|
||||
modid = entry.substring(0, idx);
|
||||
path = entry.substring(idx + 2);
|
||||
}
|
||||
|
||||
var p = Paths.get(path);
|
||||
var lst = ret.computeIfAbsent(modid, k -> new ArrayList<>());
|
||||
if (!lst.contains(p))
|
||||
lst.add(p);
|
||||
}
|
||||
|
||||
LOGGER.debug(LogMarkers.CORE, "Found supplied mod coordinates:");
|
||||
ret.forEach((k, v) -> {
|
||||
if (v.size() == 1)
|
||||
LOGGER.debug(LogMarkers.CORE, "\t{}: {}", k, v.get(0));
|
||||
else {
|
||||
LOGGER.debug(LogMarkers.CORE, "\t{}:", k);
|
||||
v.forEach(e -> LOGGER.debug("\t\t{}", e));
|
||||
}
|
||||
});
|
||||
|
||||
return ret;
|
||||
protected static String[] findClassPath() {
|
||||
var classpath = System.getProperty("legacyClassPath");
|
||||
if (classpath == null)
|
||||
classpath = System.getProperty("java.class.path");
|
||||
return classpath.split(File.pathSeparator);
|
||||
}
|
||||
|
||||
protected static Path findJarOnClasspath(String[] classpath, String match) {
|
||||
@@ -181,8 +89,49 @@ abstract class CommonDevLaunchHandler extends CommonLaunchHandler {
|
||||
}
|
||||
|
||||
if (ret == null)
|
||||
new IllegalStateException("Could not find " + match + " in classpath");
|
||||
throw new IllegalStateException("Could not find " + match + " in classpath");
|
||||
|
||||
return Paths.get(ret);
|
||||
}
|
||||
|
||||
// TODO: [Forge][UFS][DEV] Make Forge and MC seperate sources at dev time so we don't have to filter
|
||||
protected static Path getMinecraftOnly(Path extra, Path forge) {
|
||||
var packages = getPackages(); // Pulled out so it is passed to the lambda as value
|
||||
var extraPath = extra.toString().replace('\\', '/');
|
||||
|
||||
// We serve everything, except for things in the forge packages.
|
||||
BiPredicate<String, String> mcFilter = (path, base) -> {
|
||||
if (base.equals(extraPath) ||
|
||||
path.endsWith("/")) return true;
|
||||
for (var pkg : packages)
|
||||
if (path.startsWith(pkg)) return false;
|
||||
return true;
|
||||
};
|
||||
var fs = UnionHelper.newFileSystem(mcFilter, new Path[] { forge, extra });
|
||||
return fs.getRootDirectories().iterator().next();
|
||||
}
|
||||
|
||||
// TODO: [Forge][UFS][DEV] Make Forge and MC seperate sources at dev time so we don't have to filter
|
||||
protected static Path getForgeOnly(Path forge) {
|
||||
var packages = getPackages(); // Pulled out so it is passed to the lambda as value
|
||||
// We need to separate out our resources/code so that we can show up as a different data pack.
|
||||
var modJar = SecureJar.from((path, base) -> {
|
||||
if (!path.endsWith(".class")) return true;
|
||||
for (var pkg : packages)
|
||||
if (path.startsWith(pkg)) return true;
|
||||
return false;
|
||||
}, new Path[] { forge });
|
||||
|
||||
//modJar.getPackages().stream().sorted().forEach(System.out::println);
|
||||
return modJar.getRootPath();
|
||||
}
|
||||
|
||||
private static String[] getPackages() {
|
||||
return new String[] {
|
||||
"net/minecraftforge/",
|
||||
"META-INF/services/",
|
||||
"META-INF/coremods.json",
|
||||
"META-INF/mods.toml"
|
||||
};
|
||||
}
|
||||
}
|
||||
+4
-12
@@ -20,7 +20,6 @@ import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.function.BiPredicate;
|
||||
|
||||
/**
|
||||
* This is required by FMLLoader because ILaunchHandlerService doesn't have the context we need.
|
||||
@@ -29,16 +28,9 @@ import java.util.function.BiPredicate;
|
||||
* So until that happens, guess this is public api.
|
||||
*/
|
||||
public abstract class CommonLaunchHandler implements ILaunchHandlerService {
|
||||
public record LocatedPaths(
|
||||
List<Path> minecraftPaths,
|
||||
BiPredicate<String, String> minecraftFilter,
|
||||
List<List<Path>> otherModPaths,
|
||||
List<Path> otherArtifacts
|
||||
) {}
|
||||
|
||||
protected static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
private final LaunchType type;
|
||||
protected final LaunchType type;
|
||||
private final String prefix;
|
||||
|
||||
protected CommonLaunchHandler(LaunchType type, String prefix) {
|
||||
@@ -52,7 +44,7 @@ public abstract class CommonLaunchHandler implements ILaunchHandlerService {
|
||||
public boolean isProduction() { return false; }
|
||||
public abstract String getNaming();
|
||||
|
||||
public abstract LocatedPaths getMinecraftPaths();
|
||||
public abstract List<Path> getMinecraftPaths();
|
||||
|
||||
@Override
|
||||
public void configureTransformationClassLoader(final ITransformingClassLoaderBuilder builder) {
|
||||
@@ -60,7 +52,7 @@ public abstract class CommonLaunchHandler implements ILaunchHandlerService {
|
||||
|
||||
protected String[] preLaunch(String[] arguments, ModuleLayer layer) {
|
||||
URI uri;
|
||||
try (var reader = layer.configuration().findModule("fmlloader").orElseThrow().reference().open()) {
|
||||
try (var reader = layer.configuration().findModule("net.minecraftforge.fmlloader").orElseThrow().reference().open()) {
|
||||
uri = reader.find("log4j2.xml").orElseThrow();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
@@ -89,7 +81,7 @@ public abstract class CommonLaunchHandler implements ILaunchHandlerService {
|
||||
protected static final LaunchType SERVER_GAMETEST = new LaunchType("server_gametest", "forge", "net.minecraftforge.gametest.GameTestMain", Dist.DEDICATED_SERVER, false);
|
||||
|
||||
protected void runTarget(String module, String target, final String[] arguments, final ModuleLayer layer) throws Throwable {
|
||||
var mod = layer.findModule(module).orElseThrow();
|
||||
var mod = layer.findModule(module).orElse(null);
|
||||
if (mod == null) throw new IllegalStateException("Could not find module " + module);
|
||||
var cls = Class.forName(mod, target);
|
||||
if (cls == null) throw new IllegalStateException("Could not find class " + target + " in module " + module);
|
||||
|
||||
+26
-243
@@ -4,269 +4,52 @@
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.nio.file.spi.FileSystemProvider;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import com.electronwill.nightconfig.core.Config;
|
||||
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 com.google.common.jimfs.Jimfs;
|
||||
|
||||
import cpw.mods.niofs.union.UnionFileSystemProvider;
|
||||
|
||||
@ApiStatus.Internal
|
||||
abstract class ForgeDevLaunchHandler extends CommonDevLaunchHandler {
|
||||
private static final String MODS_TOML = "META-INF/mods.toml";
|
||||
private static final String PACK_META = "pack.mcmeta";
|
||||
private static final UnionFileSystemProvider UFSP = (UnionFileSystemProvider)FileSystemProvider.installedProviders().stream().filter(fsp->fsp.getScheme().equals("union")).findFirst().orElseThrow(()->new IllegalStateException("Couldn't find UnionFileSystemProvider"));
|
||||
|
||||
private ForgeDevLaunchHandler(LaunchType type) {
|
||||
super(type, "forge_dev_");
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocatedPaths getMinecraftPaths() {
|
||||
// Our exploded directories in development include all mods and minecraft itself.
|
||||
var mods = getModClasses();
|
||||
public List<Path> getMinecraftPaths() {
|
||||
|
||||
// The extra jar is on the classpath, so try and pull it out of the legacy classpath
|
||||
var legacyCP = Objects.requireNonNull(System.getProperty("legacyClassPath"), "Missing legacyClassPath, cannot find client-extra").split(File.pathSeparator);
|
||||
var legacyCP = findClassPath();
|
||||
var extra = findJarOnClasspath(legacyCP, "client-extra");
|
||||
// Minecraft is an exploded directory, so find it.
|
||||
var minecraft = getPathFromResource("net/minecraft/client/Minecraft.class");
|
||||
var forge = getPathFromResource("net/minecraftforge/common/MinecraftForge.class");
|
||||
|
||||
// If both Forge and MC are in the same folder, then we are in intellij or gradle
|
||||
// So we have to create a filtered jar
|
||||
if (!forge.equals(minecraft))
|
||||
return List.of(minecraft, extra);
|
||||
|
||||
// Minecraft is extra jar {resources} + our exploded directories in dev
|
||||
var minecraft = mods.remove("minecraft");
|
||||
if (minecraft == null)
|
||||
throw new IllegalStateException("Could not find 'minecraft' mod paths.");
|
||||
var mcFilter = getMcFilter(extra, minecraft);
|
||||
|
||||
// The MC code/Patcher edits are in exploded directories
|
||||
var modstream = Stream.<List<Path>>builder();
|
||||
var forge = this.getForgeMod(minecraft);
|
||||
modstream.add(List.of(forge));
|
||||
|
||||
// Explode our test mods into a framework that treats them all as separate jars.
|
||||
var tests = mods.remove("tests");
|
||||
if (tests != null)
|
||||
explodeTestMods(tests).forEach(modstream::add);
|
||||
|
||||
mods.values().forEach(modstream::add);
|
||||
|
||||
return new LocatedPaths(
|
||||
Stream.concat(minecraft.stream(), List.of(extra).stream()).toList(),
|
||||
mcFilter,
|
||||
modstream.build().toList(),
|
||||
getLibraries(legacyCP)
|
||||
);
|
||||
var filtered = CommonDevLaunchHandler.getMinecraftOnly(extra, minecraft);
|
||||
return List.of(filtered);
|
||||
}
|
||||
|
||||
private List<List<Path>> explodeTestMods(List<Path> paths) {
|
||||
var mod = new ArrayList<List<Path>>();
|
||||
var memory = Jimfs.newFileSystem();
|
||||
static Path getPathFromResource(String resource) {
|
||||
var cl = ForgeDevLaunchHandler.class.getClassLoader();
|
||||
var url = cl.getResource(resource);
|
||||
if (url == null)
|
||||
throw new IllegalStateException("Could not find " + resource + " in classloader " + cl);
|
||||
|
||||
// First lets find all class files, that have the @Mod annotation and map packages to modids.
|
||||
var packages = findTestModPackages(paths);
|
||||
for (var entry : packages.entrySet()) {
|
||||
var pkg = entry.getKey();
|
||||
var modids = entry.getValue();
|
||||
|
||||
var resourcePaths = new LinkedHashSet<Path>();
|
||||
// Jump down into the package for resources
|
||||
paths.stream()
|
||||
.<Path>mapMulti((p, c) -> modids.forEach(id -> c.accept(p.resolve(id))))
|
||||
.filter(Files::exists).forEach(resourcePaths::add);
|
||||
|
||||
var root = memory.getPath(modids.iterator().next()); // use the first modid as our root
|
||||
buildModsToml(resourcePaths, modids, root);
|
||||
buildPackMeta(resourcePaths, root);
|
||||
|
||||
if (Files.exists(root))
|
||||
resourcePaths.add(root);
|
||||
|
||||
// We want just the class files from the root of the input paths. So make a new union with a filter.
|
||||
var classes = UFSP.newFileSystem(
|
||||
(path, base) -> {
|
||||
if (path.endsWith("/")) {
|
||||
if (path.startsWith("/"))
|
||||
path = path.substring(1);
|
||||
return pkg.startsWith(path) || path.startsWith(pkg);
|
||||
}
|
||||
return path.endsWith(".class") && path.startsWith(pkg);
|
||||
}, paths.toArray(Path[]::new));
|
||||
// And we want the resources, and none of the classes, just in case things get kinda fucky and somehow a package overlaps a sub package {shading?}
|
||||
var resources = UFSP.newFileSystem((path, base) -> !path.endsWith(".class"), resourcePaths.toArray(Path[]::new));
|
||||
|
||||
mod.add(List.of(classes.getRoot(), resources.getRoot()));
|
||||
var str = url.toString();
|
||||
int len = resource.length();
|
||||
if ("jar".equalsIgnoreCase(url.getProtocol())) {
|
||||
str = url.getFile();
|
||||
len += 2;
|
||||
}
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
// Find all the @Mods so we can generate tomls, and so we can pick packages. Right now it doesn't allow mods in parent directories,
|
||||
// I could make it merge all the way up, but I think this would be fine.
|
||||
private Map<String, Set<String>> findTestModPackages(List<Path> paths) {
|
||||
var mods = new HashMap<String, Set<String>>();
|
||||
for (var path : paths) {
|
||||
try (var files = Files.walk(path)) {
|
||||
var classes = files
|
||||
.filter(Files::isRegularFile)
|
||||
.filter(p -> p.getFileName().toString().endsWith(".class"))
|
||||
.toList();
|
||||
|
||||
for (var cls : classes) {
|
||||
try (var is = Files.newInputStream(cls)) {
|
||||
var reader = new ClassReader(is);
|
||||
reader.accept(new ClassVisitor(Opcodes.ASM9) {
|
||||
private String clsName;
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||
this.clsName = name;
|
||||
super.visit(version, access, name, signature, superName, interfaces);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String name, boolean runtime) {
|
||||
if (!"Lnet/minecraftforge/fml/common/Mod;".equals(name))
|
||||
return super.visitAnnotation(name, runtime);
|
||||
|
||||
return new AnnotationVisitor(Opcodes.ASM9) {
|
||||
@Override
|
||||
public void visit(String key, Object value) {
|
||||
if ("value".equals(key)) {
|
||||
int idx = clsName.lastIndexOf('/');
|
||||
var pkg = clsName.substring(0, idx);
|
||||
mods.computeIfAbsent(pkg, k -> new HashSet<>()).add((String)value);
|
||||
|
||||
idx = pkg.lastIndexOf('/');
|
||||
while (idx != -1) {
|
||||
pkg = pkg.substring(0, idx);
|
||||
idx = pkg.lastIndexOf('/');
|
||||
if (mods.containsKey(pkg))
|
||||
throw new IllegalStateException("Invalid ForgeDev test mod layout, conflicting packages: " + pkg + " for " + clsName);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
sneak(e);
|
||||
}
|
||||
}
|
||||
return mods;
|
||||
}
|
||||
|
||||
// Builds or update the mods.toml file for all @Mods in this package
|
||||
private void buildModsToml(Set<Path> resources, Set<String> modids, Path root) {
|
||||
var toml = resources.stream()
|
||||
.map(p -> p.resolve(MODS_TOML))
|
||||
.filter(Files::exists)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
Config cfg = null;
|
||||
|
||||
if (toml != null)
|
||||
cfg = new TomlParser().parse(toml, FileNotFoundAction.READ_NOTHING);
|
||||
else
|
||||
cfg = Config.inMemory();
|
||||
|
||||
boolean modified = false;
|
||||
var defaults = Map.of(
|
||||
"modLoader", "javafml",
|
||||
"loaderVersion", "[0,)",
|
||||
"license", "Doesnt fucking matter"
|
||||
);
|
||||
|
||||
for (var key : defaults.keySet()) {
|
||||
if (!cfg.contains(key)) {
|
||||
cfg.set(key, defaults.get(key));
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
var modlist = new ArrayList<Config>();
|
||||
if (cfg.contains("mods")) {
|
||||
@SuppressWarnings("unchecked")
|
||||
var existing = ((List<Config>)cfg.get("mods"));
|
||||
modlist.addAll(existing);
|
||||
}
|
||||
|
||||
for (var modid : modids) {
|
||||
if (!modlist.stream().anyMatch(c -> modid.equals(c.get("modId")))) {
|
||||
modified = true;
|
||||
var tmp = Config.inMemory();
|
||||
tmp.set("modId", modid);
|
||||
modlist.add(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
cfg.set("mods", modlist);
|
||||
|
||||
if (modified) {
|
||||
var target = root.resolve(MODS_TOML);
|
||||
try {
|
||||
Files.createDirectories(target.getParent());
|
||||
new TomlWriter().write(cfg, target, WritingMode.REPLACE);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to create in memory toml: " + target, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is optional, it just hides a warning screen when starting up. I should probably remove this once I restructure how data gen for test mods work and make it generate there.
|
||||
private void buildPackMeta(Set<Path> paths, Path root) {
|
||||
var existing = paths.stream()
|
||||
.map(p -> p.resolve(PACK_META))
|
||||
.filter(Files::exists)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
if (existing != null)
|
||||
return;
|
||||
var target = root.resolve(PACK_META);
|
||||
try {
|
||||
Files.createDirectories(target.getParent());
|
||||
Files.writeString(target,
|
||||
"""
|
||||
{
|
||||
"pack": {
|
||||
"description": "doesn't matter",
|
||||
"pack_format": 18
|
||||
}
|
||||
}
|
||||
""", StandardCharsets.UTF_8, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to make in memory pack.mcmeta: " + target, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <E extends Throwable, R> R sneak(Throwable e) throws E {
|
||||
throw (E)e;
|
||||
str = str.substring(0, str.length() - len);
|
||||
var path = Paths.get(URI.create(str));
|
||||
return path;
|
||||
}
|
||||
|
||||
public static class Client extends ForgeDevLaunchHandler {
|
||||
|
||||
@@ -0,0 +1,276 @@
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.objectweb.asm.AnnotationVisitor;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import com.electronwill.nightconfig.core.Config;
|
||||
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 com.google.common.jimfs.Jimfs;
|
||||
|
||||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.AbstractModProvider;
|
||||
import net.minecraftforge.forgespi.locating.IModLocator;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public class ForgeDevLocator extends AbstractModProvider implements IModLocator {
|
||||
private static final String PACK_META = "pack.mcmeta";
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "forge_dev_locator";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ModFileOrException> scanMods() {
|
||||
var handler = FMLLoader.getLaunchHandler();
|
||||
|
||||
if (!(handler instanceof ForgeDevLaunchHandler))
|
||||
return List.of();
|
||||
|
||||
var mods = getMods();
|
||||
var ret = new ArrayList<ModFileOrException>();
|
||||
for (var path : mods) {
|
||||
var mod = createMod(path);
|
||||
if (mod != null)
|
||||
ret.add(mod);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private List<Path> getMods() {
|
||||
// Forge is an exploded directory as well
|
||||
var minecraft = ForgeDevLaunchHandler.getPathFromResource("net/minecraft/client/Minecraft.class");
|
||||
var forge = ForgeDevLaunchHandler.getPathFromResource("net/minecraftforge/common/MinecraftForge.class");
|
||||
if (minecraft.equals(forge)) {
|
||||
// If both Forge and MC are in the same folder, then we are in intellij or gradle
|
||||
// So we have to create a filtered jar
|
||||
forge = CommonDevLaunchHandler.getForgeOnly(forge);
|
||||
}
|
||||
var ret = new ArrayList<Path>();
|
||||
ret.add(forge);
|
||||
|
||||
var isTest = Boolean.getBoolean("forgedev.enableTestMods");
|
||||
if (!isTest)
|
||||
return ret;
|
||||
|
||||
var test = ForgeDevLaunchHandler.getPathFromResource("net/minecraftforge/test/BaseTestMod.class");
|
||||
// Explode our test mods into a framework that treats them all as separate jars.
|
||||
var tests = explodeTestMods(test);
|
||||
ret.addAll(tests);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
private List<Path> explodeTestMods(Path path) {
|
||||
var mod = new ArrayList<Path>();
|
||||
var memory = Jimfs.newFileSystem();
|
||||
|
||||
// First lets find all class files, that have the @Mod annotation and map packages to modids.
|
||||
var packages = findTestModPackages(path);
|
||||
for (var entry : packages.entrySet()) {
|
||||
var pkg = entry.getKey();
|
||||
var modids = entry.getValue();
|
||||
|
||||
// Find resource directories for every mod in this group
|
||||
var resourcePaths = new LinkedHashSet<Path>();
|
||||
for (var modid : modids) {
|
||||
var rsc = path.resolve(modid);
|
||||
if (Files.exists(rsc))
|
||||
resourcePaths.add(rsc);
|
||||
}
|
||||
|
||||
var root = memory.getPath(modids.iterator().next()); // use the first modid as our root
|
||||
buildModsToml(resourcePaths, modids, root);
|
||||
buildPackMeta(resourcePaths, root);
|
||||
|
||||
if (Files.exists(root))
|
||||
resourcePaths.add(root);
|
||||
|
||||
// We want just the class files from the root of the input paths. So make a new union with a filter.
|
||||
var classes = UnionHelper.newFileSystem(
|
||||
(name, base) -> {
|
||||
if (name.endsWith("/")) {
|
||||
if (name.startsWith("/"))
|
||||
name = name.substring(1);
|
||||
return pkg.startsWith(name) || name.startsWith(pkg);
|
||||
}
|
||||
return name.endsWith(".class") && name.startsWith(pkg);
|
||||
}, new Path[] { path }
|
||||
);
|
||||
|
||||
// And we want the resources, and none of the classes, just in case things get kinda fucky and somehow a package overlaps a sub package {shading?}
|
||||
var resources = UnionHelper.newFileSystem((name, base) -> !name.endsWith(".class"), resourcePaths.toArray(Path[]::new));
|
||||
|
||||
// Union of unions, Yay!
|
||||
var union = UnionHelper.newFileSystem(null, new Path[] {
|
||||
classes.getRootDirectories().iterator().next(),
|
||||
resources.getRootDirectories().iterator().next()
|
||||
});
|
||||
mod.add(union.getRootDirectories().iterator().next());
|
||||
}
|
||||
|
||||
return mod;
|
||||
}
|
||||
|
||||
// Find all the @Mods so we can generate tomls, and so we can pick packages. Right now it doesn't allow mods in parent directories,
|
||||
// I could make it merge all the way up, but I think this would be fine.
|
||||
private Map<String, Set<String>> findTestModPackages(Path path) {
|
||||
var mods = new HashMap<String, Set<String>>();
|
||||
try (var files = Files.walk(path)) {
|
||||
var classes = files
|
||||
.filter(Files::isRegularFile)
|
||||
.filter(p -> p.getFileName().toString().endsWith(".class"))
|
||||
.toList();
|
||||
|
||||
for (var cls : classes) {
|
||||
try (var is = Files.newInputStream(cls)) {
|
||||
var reader = new ClassReader(is);
|
||||
reader.accept(new ClassVisitor(Opcodes.ASM9) {
|
||||
private String clsName;
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||
this.clsName = name;
|
||||
super.visit(version, access, name, signature, superName, interfaces);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String name, boolean runtime) {
|
||||
if (!"Lnet/minecraftforge/fml/common/Mod;".equals(name))
|
||||
return super.visitAnnotation(name, runtime);
|
||||
|
||||
return new AnnotationVisitor(Opcodes.ASM9) {
|
||||
@Override
|
||||
public void visit(String key, Object value) {
|
||||
if ("value".equals(key)) {
|
||||
int idx = clsName.lastIndexOf('/');
|
||||
var pkg = clsName.substring(0, idx);
|
||||
mods.computeIfAbsent(pkg, k -> new HashSet<>()).add((String)value);
|
||||
|
||||
idx = pkg.lastIndexOf('/');
|
||||
while (idx != -1) {
|
||||
pkg = pkg.substring(0, idx);
|
||||
idx = pkg.lastIndexOf('/');
|
||||
if (mods.containsKey(pkg))
|
||||
throw new IllegalStateException("Invalid ForgeDev test mod layout, conflicting packages: " + pkg + " for " + clsName);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
sneak(e);
|
||||
}
|
||||
return mods;
|
||||
}
|
||||
|
||||
// Builds or update the mods.toml file for all @Mods in this package
|
||||
private void buildModsToml(Set<Path> resources, Set<String> modids, Path root) {
|
||||
var toml = resources.stream()
|
||||
.map(p -> p.resolve(MODS_TOML))
|
||||
.filter(Files::exists)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
Config cfg = null;
|
||||
|
||||
if (toml != null)
|
||||
cfg = new TomlParser().parse(toml, FileNotFoundAction.READ_NOTHING);
|
||||
else
|
||||
cfg = Config.inMemory();
|
||||
|
||||
boolean modified = false;
|
||||
var defaults = Map.of(
|
||||
"modLoader", "javafml",
|
||||
"loaderVersion", "[0,)",
|
||||
"license", "Doesnt fucking matter"
|
||||
);
|
||||
|
||||
for (var key : defaults.keySet()) {
|
||||
if (!cfg.contains(key)) {
|
||||
cfg.set(key, defaults.get(key));
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
|
||||
var modlist = new ArrayList<Config>();
|
||||
if (cfg.contains("mods")) {
|
||||
@SuppressWarnings("unchecked")
|
||||
var existing = ((List<Config>)cfg.get("mods"));
|
||||
modlist.addAll(existing);
|
||||
}
|
||||
|
||||
for (var modid : modids) {
|
||||
if (!modlist.stream().anyMatch(c -> modid.equals(c.get("modId")))) {
|
||||
modified = true;
|
||||
var tmp = Config.inMemory();
|
||||
tmp.set("modId", modid);
|
||||
modlist.add(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
cfg.set("mods", modlist);
|
||||
|
||||
if (modified) {
|
||||
var target = root.resolve(MODS_TOML);
|
||||
try {
|
||||
Files.createDirectories(target.getParent());
|
||||
new TomlWriter().write(cfg, target, WritingMode.REPLACE);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to create in memory toml: " + target, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This is optional, it just hides a warning screen when starting up. I should probably remove this once I restructure how data gen for test mods work and make it generate there.
|
||||
private void buildPackMeta(Set<Path> paths, Path root) {
|
||||
var existing = paths.stream()
|
||||
.map(p -> p.resolve(PACK_META))
|
||||
.filter(Files::exists)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
|
||||
if (existing != null)
|
||||
return;
|
||||
var target = root.resolve(PACK_META);
|
||||
try {
|
||||
Files.createDirectories(target.getParent());
|
||||
Files.writeString(target,
|
||||
"""
|
||||
{
|
||||
"pack": {
|
||||
"description": "doesn't matter",
|
||||
"pack_format": 18
|
||||
}
|
||||
}
|
||||
""", StandardCharsets.UTF_8, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING, StandardOpenOption.CREATE);
|
||||
} catch (IOException e) {
|
||||
throw new IllegalStateException("Failed to make in memory pack.mcmeta: " + target, e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <E extends Throwable, R> R sneak(Throwable e) throws E {
|
||||
throw (E)e;
|
||||
}
|
||||
}
|
||||
+4
-61
@@ -7,14 +7,10 @@ package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.function.BiPredicate;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import cpw.mods.jarhandling.SecureJar;
|
||||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
import net.minecraftforge.fml.loading.LibraryFinder;
|
||||
import net.minecraftforge.fml.loading.VersionInfo;
|
||||
|
||||
@ApiStatus.Internal
|
||||
abstract class ForgeProdLaunchHandler extends CommonLaunchHandler {
|
||||
@@ -25,75 +21,22 @@ abstract class ForgeProdLaunchHandler extends CommonLaunchHandler {
|
||||
@Override public String getNaming() { return "srg"; }
|
||||
@Override public boolean isProduction() { return true; }
|
||||
|
||||
protected Path mvn(VersionInfo info, String artifact) {
|
||||
return mvn(info, artifact, "");
|
||||
}
|
||||
protected Path mvn(VersionInfo info, String artifact, String classifier) {
|
||||
return mvn(info.forgeGroup(), artifact, classifier, info.mcAndForgeVersion());
|
||||
}
|
||||
protected Path mvn(String group, String artifact, String classifier, String version) {
|
||||
return LibraryFinder.findPathForMaven(group, artifact, "", classifier, version);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocatedPaths getMinecraftPaths() {
|
||||
public List<Path> getMinecraftPaths() {
|
||||
var vers = FMLLoader.versionInfo();
|
||||
|
||||
// Minecraft
|
||||
var mc = getMCPaths(vers);
|
||||
|
||||
// mods (Forge)
|
||||
var forgejar = mvn(vers, "forge", "universal");
|
||||
|
||||
// Libraries
|
||||
var fmlcore = mvn(vers, "fmlcore");
|
||||
var javafmllang = mvn(vers, "javafmllanguage");
|
||||
var lowcodelang = mvn(vers, "lowcodelanguage");
|
||||
var mclang = mvn(vers, "mclanguage");
|
||||
|
||||
return new LocatedPaths(
|
||||
mc,
|
||||
null,
|
||||
List.of(List.of(forgejar)),
|
||||
List.of(fmlcore, javafmllang, lowcodelang, mclang)
|
||||
);
|
||||
var mc = LibraryFinder.findPathForMaven(vers.forgeGroup(), "forge", "", this.type.name(), vers.mcAndForgeVersion());
|
||||
return List.of(mc);
|
||||
}
|
||||
|
||||
protected abstract List<Path> getMCPaths(VersionInfo vers);
|
||||
|
||||
public static class Client extends ForgeProdLaunchHandler {
|
||||
public Client() {
|
||||
super(CLIENT);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Path> getMCPaths(VersionInfo vers) {
|
||||
var mc = mvn("net.minecraft", "client", "srg", vers.mcAndMCPVersion());
|
||||
var mcextra = mvn("net.minecraft", "client", "extra", vers.mcAndMCPVersion());
|
||||
var patches = mvn(vers, "forge", "client");
|
||||
return List.of(mc, mcextra, patches);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Server extends ForgeProdLaunchHandler {
|
||||
public Server() {
|
||||
super(SERVER);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Path> getMCPaths(VersionInfo vers) {
|
||||
var mc = mvn("net.minecraft", "server", "srg", vers.mcAndMCPVersion());
|
||||
var mcextra = mvn("net.minecraft", "server", "extra", vers.mcAndMCPVersion());
|
||||
var patches = mvn(vers, "forge", "server");
|
||||
|
||||
// TODO: Get rid of this by filtering the file at install time.
|
||||
// We only want it for it's resources. So filter everything else out.
|
||||
BiPredicate<String, String> filter = (path, base) -> {
|
||||
return path.equals("META-INF/versions/") || // This is required because it bypasses our filter for the manifest, and it's a multi-release jar.
|
||||
(!path.endsWith(".class") && !path.startsWith("META-INF/"));
|
||||
};
|
||||
mcextra = SecureJar.from(filter, mcextra).getRootPath();
|
||||
|
||||
return List.of(mc, mcextra, patches);
|
||||
}
|
||||
}
|
||||
}
|
||||
+7
-22
@@ -6,14 +6,13 @@
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import java.util.function.BiPredicate;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
import cpw.mods.jarhandling.SecureJar;
|
||||
|
||||
@ApiStatus.Internal
|
||||
abstract class ForgeUserdevLaunchHandler extends CommonDevLaunchHandler {
|
||||
private ForgeUserdevLaunchHandler(LaunchType type) {
|
||||
@@ -21,8 +20,8 @@ abstract class ForgeUserdevLaunchHandler extends CommonDevLaunchHandler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocatedPaths getMinecraftPaths() {
|
||||
var legacyCP = Objects.requireNonNull(System.getProperty("legacyClassPath"), "Missing legacyClassPath, cannot find userdev jars").split(File.pathSeparator);
|
||||
public List<Path> getMinecraftPaths() {
|
||||
var legacyCP = findClassPath();
|
||||
var vers = FMLLoader.versionInfo();
|
||||
|
||||
// Minecraft is extra jar {resources} + forge jar {patches}
|
||||
@@ -30,22 +29,8 @@ abstract class ForgeUserdevLaunchHandler extends CommonDevLaunchHandler {
|
||||
var extra = findJarOnClasspath(legacyCP, "client-extra");
|
||||
var forge = findJarOnClasspath(legacyCP, "forge-" + vers.mcAndForgeVersion());
|
||||
// We need to filter the forge jar to just MC code
|
||||
var filter = getMcFilter(extra, List.of(forge));
|
||||
|
||||
// Mods is forge classes + anything from MOD_CLASSES
|
||||
var modstream = Stream.<List<Path>>builder();
|
||||
// We want the forge as a seperate mod jar.
|
||||
var forgemod = this.getForgeMod(List.of(forge));
|
||||
modstream.add(List.of(forgemod));
|
||||
// Mod code is in exploded directories
|
||||
getModClasses().forEach((modid, paths) -> modstream.add(paths));
|
||||
|
||||
return new LocatedPaths(
|
||||
List.of(forge, extra),
|
||||
filter,
|
||||
modstream.build().toList(),
|
||||
getLibraries(legacyCP)
|
||||
);
|
||||
var minecraft = CommonDevLaunchHandler.getMinecraftOnly(extra, forge);
|
||||
return List.of(minecraft);
|
||||
}
|
||||
|
||||
public static class Client extends ForgeUserdevLaunchHandler {
|
||||
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import java.util.List;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.AbstractModProvider;
|
||||
import net.minecraftforge.forgespi.locating.IModLocator;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public class ForgeUserdevLocator extends AbstractModProvider implements IModLocator {
|
||||
@Override
|
||||
public String name() {
|
||||
return "forge_userdev_locator";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<ModFileOrException> scanMods() {
|
||||
var handler = FMLLoader.getLaunchHandler();
|
||||
|
||||
if (!(handler instanceof ForgeUserdevLaunchHandler))
|
||||
return List.of();
|
||||
var forge = ForgeDevLaunchHandler.getPathFromResource("net/minecraftforge/common/MinecraftForge.class");
|
||||
var filtered = ForgeUserdevLaunchHandler.getForgeOnly(forge);
|
||||
var mod = createMod(filtered);
|
||||
return List.of(mod);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.spi.FileSystemProvider;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.function.BiPredicate;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
@ApiStatus.Internal
|
||||
final class UnionHelper {
|
||||
private static final FileSystemProvider UFSP = getUnionFileSystemProvider();
|
||||
private static FileSystemProvider getUnionFileSystemProvider() {
|
||||
for (var provider : FileSystemProvider.installedProviders()) {
|
||||
if ("union".equals(provider.getScheme()))
|
||||
return provider;
|
||||
}
|
||||
throw new IllegalStateException("Missing UnionFileSystemProvider");
|
||||
};
|
||||
|
||||
static FileSystem newFileSystem(BiPredicate<String, String> filter, Path[] paths) {
|
||||
try {
|
||||
if (paths == null || paths.length == 0)
|
||||
throw new IllegalArgumentException("Must contain atleast one path");
|
||||
|
||||
if (filter == null && paths.length == 1)
|
||||
return FileSystems.newFileSystem(paths[0]);
|
||||
|
||||
var map = new HashMap<String, Object>();
|
||||
if (filter != null)
|
||||
map.put("filter", filter);
|
||||
|
||||
var lst = new ArrayList<>(Arrays.asList(paths));
|
||||
var base = lst.remove(0);
|
||||
map.put("additional", lst);
|
||||
return UFSP.newFileSystem(base, map);
|
||||
} catch (IOException e) {
|
||||
return sneak(e);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <E extends Throwable, R> R sneak(Throwable e) throws E {
|
||||
throw (E)e;
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -1,2 +1 @@
|
||||
net.minecraftforge.fml.loading.ModDirTransformerDiscoverer
|
||||
net.minecraftforge.fml.loading.ClasspathTransformerDiscoverer
|
||||
net.minecraftforge.fml.loading.ModDirTransformerDiscoverer
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
net.minecraftforge.fml.loading.moddiscovery.ModsFolderLocator
|
||||
net.minecraftforge.fml.loading.moddiscovery.MavenDirectoryLocator
|
||||
net.minecraftforge.fml.loading.moddiscovery.ExplodedDirectoryLocator
|
||||
net.minecraftforge.fml.loading.moddiscovery.MinecraftLocator
|
||||
net.minecraftforge.fml.loading.moddiscovery.ClasspathLocator
|
||||
net.minecraftforge.fml.loading.targets.ForgeDevLocator
|
||||
net.minecraftforge.fml.loading.targets.ForgeUserdevLocator
|
||||
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="warn" shutdownHook="disable">
|
||||
<Configuration status="warn" packages="net.minecrell.terminalconsole,net.minecrell.terminalconsole.util,com.mojang.util,cpw.mods.modlauncher.log,net.minecraftforge.fml.loading.log4j" shutdownHook="disable">
|
||||
<filters>
|
||||
<ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="NEUTRAL"/>
|
||||
<MarkerFilter marker="NETWORK_PACKETS" onMatch="${sys:forge.logging.marker.networking:-DENY}" onMismatch="NEUTRAL"/>
|
||||
|
||||
+8
-1
@@ -1,4 +1,11 @@
|
||||
# Sets default memory used for gradle commands. Can be overridden by user or command line properties.
|
||||
# This is required to provide enough memory for the Minecraft decompilation process.
|
||||
org.gradle.jvmargs=-Xmx3G
|
||||
org.gradle.daemon=false
|
||||
org.gradle.daemon=false
|
||||
|
||||
JAVA_VERSION=17
|
||||
MC_VERSION=1.20.2
|
||||
MC_NEXT_VERSION=1.21
|
||||
MCP_VERSION=20230921.100330
|
||||
MAPPING_CHANNEL=official
|
||||
MAPPING_VERSION=1.20.2
|
||||
@@ -1,12 +1,13 @@
|
||||
import net.minecraftforge.gradleutils.PomUtils
|
||||
|
||||
plugins {
|
||||
id 'com.github.ben-manes.versions'
|
||||
id 'org.javamodularity.moduleplugin' version '1.8.3' apply false
|
||||
id 'org.cadixdev.licenser'
|
||||
id 'java-library'
|
||||
id 'jacoco'
|
||||
id 'maven-publish'
|
||||
id 'org.cadixdev.licenser'
|
||||
id 'net.minecraftforge.gradleutils'
|
||||
}
|
||||
|
||||
dependencyUpdates.rejectVersionIf { isNonStable(it.candidate.version) }
|
||||
apply from: rootProject.file('build_shared.gradle')
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.nulls)
|
||||
@@ -14,38 +15,25 @@ dependencies {
|
||||
implementation(project(':fmlcore'))
|
||||
}
|
||||
|
||||
task sourcesJar(type: Jar) {
|
||||
archiveClassifier = 'sources'
|
||||
from sourceSets.main.allSource
|
||||
java {
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(JAVA_VERSION)
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
ext {
|
||||
MANIFESTS = [
|
||||
'': [
|
||||
'FMLModType': 'LANGPROVIDER',
|
||||
'Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"),
|
||||
'Git-Commit': GIT_INFO.abbreviatedId,
|
||||
'Git-Branch': GIT_INFO.branch,
|
||||
'Build-Number': "${System.getenv('BUILD_NUMBER')?:0}",
|
||||
] as LinkedHashMap,
|
||||
'net/minecraftforge/fml/loading/': [
|
||||
'Specification-Title': 'FMLLoader',
|
||||
'Specification-Vendor': 'Forge Development LLC',
|
||||
'Specification-Version': '1',
|
||||
'Implementation-Title': 'FML Loader',
|
||||
'Implementation-Version': '1.0',
|
||||
'Implementation-Vendor': 'Forge'
|
||||
] as LinkedHashMap
|
||||
]
|
||||
}
|
||||
|
||||
jar.doFirst {
|
||||
manifest.attributes['Automatic-Module-Name'] = project.name
|
||||
MANIFESTS.each { pkg, values ->
|
||||
if (pkg == '')
|
||||
manifest.attributes(values)
|
||||
else
|
||||
manifest.attributes(values, pkg)
|
||||
tasks.named('jar', Jar).configure {
|
||||
manifest {
|
||||
attributes([
|
||||
'Automatic-Module-Name': 'net.minecraftforge.javafmlmod',
|
||||
'FMLModType': 'LANGPROVIDER'
|
||||
] as LinkedHashMap)
|
||||
attributes([
|
||||
'Specification-Title': 'JavaFMLMod',
|
||||
'Specification-Vendor': 'Forge Development LLC',
|
||||
'Specification-Version': '1',
|
||||
'Implementation-Title': 'JavaFMLMod',
|
||||
'Implementation-Vendor': 'Forge Development LLC',
|
||||
'Implementation-Version': FORGE_VERSION
|
||||
] as LinkedHashMap, 'net/minecraftforge/fml/javafmlmod/')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,16 +43,22 @@ tasks.withType(JavaCompile) {
|
||||
|
||||
license {
|
||||
header = rootProject.file('LICENSE-header.txt')
|
||||
|
||||
include 'net/minecraftforge/'
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives jar
|
||||
archives sourcesJar
|
||||
}
|
||||
|
||||
publishing.publications.mavenJava(MavenPublication) {
|
||||
from components.java
|
||||
artifact sourcesJar
|
||||
publishing {
|
||||
publications.register('mavenJava', MavenPublication).configure {
|
||||
from components.java
|
||||
artifactId = 'javafmllanguage'
|
||||
pom {
|
||||
name = project.name
|
||||
description = 'Language provider for Minecraft Forge that provides basic java mod functionality'
|
||||
url = 'https://github.com/MinecraftForge/MinecraftForge'
|
||||
PomUtils.setGitHubDetails(pom, 'MinecraftForge')
|
||||
license PomUtils.Licenses.LGPLv2_1
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven gradleutils.publishingForgeMaven
|
||||
}
|
||||
}
|
||||
+46
-39
@@ -6,67 +6,74 @@
|
||||
package net.minecraftforge.fml.javafmlmod;
|
||||
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.fml.Logging;
|
||||
import net.minecraftforge.fml.ModContainer;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
|
||||
import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus;
|
||||
import net.minecraftforge.forgespi.language.ModFileScanData;
|
||||
import net.minecraftforge.forgespi.language.ModFileScanData.AnnotationData;
|
||||
import net.minecraftforge.forgespi.language.ModFileScanData.EnumData;
|
||||
import net.minecraftforge.fml.loading.FMLEnvironment;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModAnnotation;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static net.minecraftforge.fml.Logging.LOADING;
|
||||
|
||||
/**
|
||||
* Automatic eventbus subscriber - reads {@link net.minecraftforge.fml.common.Mod.EventBusSubscriber}
|
||||
* annotations and passes the class instances to the {@link net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus}
|
||||
* Automatic eventbus subscriber - reads {@link EventBusSubscriber}
|
||||
* annotations and passes the class instances to the {@link nBus}
|
||||
* defined by the annotation. Defaults to {@code MinecraftForge#EVENT_BUS}
|
||||
*/
|
||||
public class AutomaticEventSubscriber
|
||||
{
|
||||
public class AutomaticEventSubscriber {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static final Type AUTO_SUBSCRIBER = Type.getType(Mod.EventBusSubscriber.class);
|
||||
private static final Type AUTO_SUBSCRIBER = Type.getType(EventBusSubscriber.class);
|
||||
private static final Type MOD_TYPE = Type.getType(Mod.class);
|
||||
public static void inject(final ModContainer mod, final ModFileScanData scanData, final ClassLoader loader)
|
||||
{
|
||||
if (scanData == null) return;
|
||||
LOGGER.debug(LOADING,"Attempting to inject @EventBusSubscriber classes into the eventbus for {}", mod.getModId());
|
||||
List<ModFileScanData.AnnotationData> ebsTargets = scanData.getAnnotations().stream().
|
||||
filter(annotationData -> AUTO_SUBSCRIBER.equals(annotationData.annotationType())).
|
||||
collect(Collectors.toList());
|
||||
Map<String, String> modids = scanData.getAnnotations().stream().
|
||||
filter(annotationData -> MOD_TYPE.equals(annotationData.annotationType())).
|
||||
collect(Collectors.toMap(a -> a.clazz().getClassName(), a -> (String)a.annotationData().get("value")));
|
||||
|
||||
ebsTargets.forEach(ad -> {
|
||||
@SuppressWarnings("unchecked")
|
||||
final List<ModAnnotation.EnumHolder> sidesValue = (List<ModAnnotation.EnumHolder>)ad.annotationData().
|
||||
getOrDefault("value", Arrays.asList(new ModAnnotation.EnumHolder(null, "CLIENT"), new ModAnnotation.EnumHolder(null, "DEDICATED_SERVER")));
|
||||
final EnumSet<Dist> sides = sidesValue.stream().map(eh -> Dist.valueOf(eh.getValue())).
|
||||
collect(Collectors.toCollection(() -> EnumSet.noneOf(Dist.class)));
|
||||
final String modId = (String)ad.annotationData().getOrDefault("modid", modids.getOrDefault(ad.clazz().getClassName(), mod.getModId()));
|
||||
final ModAnnotation.EnumHolder busTargetHolder = (ModAnnotation.EnumHolder)ad.annotationData().getOrDefault("bus", new ModAnnotation.EnumHolder(null, "FORGE"));
|
||||
final Mod.EventBusSubscriber.Bus busTarget = Mod.EventBusSubscriber.Bus.valueOf(busTargetHolder.getValue());
|
||||
public static void inject(ModContainer mod, ModFileScanData scanData, ClassLoader loader) {
|
||||
if (scanData == null) return;
|
||||
LOGGER.debug(Logging.LOADING, "Attempting to inject @EventBusSubscriber classes into the eventbus for {}", mod.getModId());
|
||||
|
||||
var targets = scanData.getAnnotations().stream()
|
||||
.filter(data -> AUTO_SUBSCRIBER.equals(data.annotationType()))
|
||||
.toList();
|
||||
|
||||
var modids = scanData.getAnnotations().stream()
|
||||
.filter(data -> MOD_TYPE.equals(data.annotationType()))
|
||||
.collect(Collectors.toMap(a -> a.clazz().getClassName(), a -> (String)a.annotationData().get("value")));
|
||||
|
||||
var defaultSides = List.of(new EnumData(null, "CLIENT"), new EnumData(null, "DEDICATED_SERVER"));
|
||||
var defaultBus = new EnumData(null, "FORGE");
|
||||
|
||||
for (var data : targets) {
|
||||
var modId = modids.getOrDefault(data.clazz().getClassName(), mod.getModId());
|
||||
modId = value(data, "modid", modId);
|
||||
|
||||
var sidesValue = value(data, "value", defaultSides);
|
||||
var sides = sidesValue.stream()
|
||||
.map(EnumData::value)
|
||||
.map(Dist::valueOf)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
var busName = value(data, "bus", defaultBus).value();
|
||||
var busTarget = Bus.valueOf(busName);
|
||||
if (Objects.equals(mod.getModId(), modId) && sides.contains(FMLEnvironment.dist)) {
|
||||
try
|
||||
{
|
||||
LOGGER.debug(LOADING, "Auto-subscribing {} to {}", ad.clazz().getClassName(), busTarget);
|
||||
busTarget.bus().get().register(Class.forName(ad.clazz().getClassName(), true, loader));
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
LOGGER.fatal(LOADING, "Failed to load mod class {} for @EventBusSubscriber annotation", ad.clazz(), e);
|
||||
try {
|
||||
LOGGER.debug(Logging.LOADING, "Auto-subscribing {} to {}", data.clazz().getClassName(), busTarget);
|
||||
busTarget.bus().get().register(Class.forName(data.clazz().getClassName(), true, loader));
|
||||
} catch (ClassNotFoundException e) {
|
||||
LOGGER.fatal(Logging.LOADING, "Failed to load mod class {} for @EventBusSubscriber annotation", data.clazz(), e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <R> R value(AnnotationData data, String key, R value) {
|
||||
return (R)data.annotationData().getOrDefault(key, value);
|
||||
}
|
||||
}
|
||||
+20
-28
@@ -25,8 +25,7 @@ import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public class FMLModContainer extends ModContainer
|
||||
{
|
||||
public class FMLModContainer extends ModContainer {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static final Marker LOADING = MarkerManager.getMarker("LOADING");
|
||||
private final ModFileScanData scanResults;
|
||||
@@ -34,8 +33,7 @@ public class FMLModContainer extends ModContainer
|
||||
private Object modInstance;
|
||||
private final Class<?> modClass;
|
||||
|
||||
public FMLModContainer(IModInfo info, String className, ModFileScanData modFileScanResults, ModuleLayer gameLayer)
|
||||
{
|
||||
public FMLModContainer(IModInfo info, String className, ModFileScanData modFileScanResults, ModuleLayer gameLayer) {
|
||||
super(info);
|
||||
LOGGER.debug(LOADING,"Creating FMLModContainer instance for {}", className);
|
||||
this.scanResults = modFileScanResults;
|
||||
@@ -44,66 +42,60 @@ public class FMLModContainer extends ModContainer
|
||||
this.configHandler = Optional.of(ce->this.eventBus.post(ce.self()));
|
||||
final FMLJavaModLoadingContext contextExtension = new FMLJavaModLoadingContext(this);
|
||||
this.contextExtension = () -> contextExtension;
|
||||
try
|
||||
{
|
||||
var layer = gameLayer.findModule(info.getOwningFile().moduleName()).orElseThrow();
|
||||
modClass = Class.forName(layer, className);
|
||||
|
||||
|
||||
try {
|
||||
var moduleName = info.getOwningFile().moduleName();
|
||||
var module = gameLayer.findModule(moduleName)
|
||||
.orElseThrow(() -> new IllegalStateException("Failed to find " + moduleName + " in " + gameLayer));
|
||||
modClass = Class.forName(module, className);
|
||||
LOGGER.trace(LOADING,"Loaded modclass {} with {}", modClass.getName(), modClass.getClassLoader());
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
} catch (Throwable e) {
|
||||
LOGGER.error(LOADING, "Failed to load class {}", className, e);
|
||||
throw new ModLoadingException(info, ModLoadingStage.CONSTRUCT, "fml.modloading.failedtoloadmodclass", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void onEventFailed(IEventBus iEventBus, Event event, IEventListener[] iEventListeners, int i, Throwable throwable)
|
||||
{
|
||||
private void onEventFailed(IEventBus iEventBus, Event event, IEventListener[] iEventListeners, int i, Throwable throwable) {
|
||||
LOGGER.error(new EventBusErrorMessage(event, i, iEventListeners, throwable));
|
||||
}
|
||||
|
||||
private void constructMod()
|
||||
{
|
||||
try
|
||||
{
|
||||
private void constructMod() {
|
||||
try {
|
||||
LOGGER.trace(LOADING, "Loading mod instance {} of type {}", getModId(), modClass.getName());
|
||||
this.modInstance = modClass.getDeclaredConstructor().newInstance();
|
||||
LOGGER.trace(LOADING, "Loaded mod instance {} of type {}", getModId(), modClass.getName());
|
||||
}
|
||||
catch (Throwable e)
|
||||
{
|
||||
} catch (Throwable e) {
|
||||
// When a mod constructor throws an exception, it's wrapped in an InvocationTargetException which hides the
|
||||
// actual exception from the mod loading error screen.
|
||||
if (e instanceof InvocationTargetException wrapped)
|
||||
e = Objects.requireNonNullElse(wrapped.getCause(), e); // unwrap the exception
|
||||
|
||||
LOGGER.error(LOADING,"Failed to create mod instance. ModID: {}, class {}", getModId(), modClass.getName(), e);
|
||||
LOGGER.error(LOADING, "Failed to create mod instance. ModID: {}, class {}", getModId(), modClass.getName(), e);
|
||||
throw new ModLoadingException(modInfo, ModLoadingStage.CONSTRUCT, "fml.modloading.failedtoloadmod", e, modClass);
|
||||
}
|
||||
|
||||
try {
|
||||
LOGGER.trace(LOADING, "Injecting Automatic event subscribers for {}", getModId());
|
||||
AutomaticEventSubscriber.inject(this, this.scanResults, this.modClass.getClassLoader());
|
||||
LOGGER.trace(LOADING, "Completed Automatic event subscribers for {}", getModId());
|
||||
} catch (Throwable e) {
|
||||
LOGGER.error(LOADING,"Failed to register automatic subscribers. ModID: {}, class {}", getModId(), modClass.getName(), e);
|
||||
LOGGER.error(LOADING, "Failed to register automatic subscribers. ModID: {}, class {}", getModId(), modClass.getName(), e);
|
||||
throw new ModLoadingException(modInfo, ModLoadingStage.CONSTRUCT, "fml.modloading.failedtoloadmod", e, modClass);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Object mod)
|
||||
{
|
||||
public boolean matches(Object mod) {
|
||||
return mod == modInstance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getMod()
|
||||
{
|
||||
public Object getMod() {
|
||||
return modInstance;
|
||||
}
|
||||
|
||||
public IEventBus getEventBus()
|
||||
{
|
||||
public IEventBus getEventBus() {
|
||||
return this.eventBus;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,18 @@
|
||||
import net.minecraftforge.gradleutils.PomUtils
|
||||
|
||||
plugins {
|
||||
id 'org.javamodularity.moduleplugin' version '1.8.3' apply false
|
||||
id 'org.cadixdev.licenser'
|
||||
id 'java-library'
|
||||
id 'jacoco'
|
||||
id 'maven-publish'
|
||||
id 'org.cadixdev.licenser'
|
||||
id 'net.minecraftforge.gradleutils'
|
||||
}
|
||||
|
||||
java.withSourcesJar()
|
||||
apply from: rootProject.file('build_shared.gradle')
|
||||
|
||||
java {
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(JAVA_VERSION)
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.nulls)
|
||||
@@ -17,16 +24,16 @@ dependencies {
|
||||
tasks.named('jar', Jar).configure {
|
||||
manifest {
|
||||
attributes([
|
||||
'Automatic-Module-Name': project.name.toLowerCase(),
|
||||
'Automatic-Module-Name': 'net.minecraftforge.lowcodemod',
|
||||
'FMLModType': 'LANGPROVIDER'
|
||||
] as java.util.LinkedHashMap)
|
||||
] as LinkedHashMap)
|
||||
attributes([
|
||||
'Specification-Title': 'LowCodeMod',
|
||||
'Specification-Vendor': 'Forge Development LLC',
|
||||
'Specification-Version': '1.0',
|
||||
'Implementation-Title': project.name,
|
||||
'Implementation-Version': FORGE_VERSION.split('\\.')[0], //project.version, TODO: Split out into a real versioned system, for now use the default that net.minecraftforge.fml.loading.LanguageLoadingProvider.loadLanguageProviders() used
|
||||
'Implementation-Vendor': 'Forge Development LLC'
|
||||
'Specification-Title': 'LowCodeMod',
|
||||
'Specification-Vendor': 'Forge Development LLC',
|
||||
'Specification-Version': '1.0',
|
||||
'Implementation-Title': project.name,
|
||||
'Implementation-Vendor': 'Forge Development LLC',
|
||||
'Implementation-Version': FORGE_VERSION.split('\\.')[0]
|
||||
] as java.util.LinkedHashMap, 'net/minecraftforge/fml/lowcodemod/')
|
||||
}
|
||||
}
|
||||
@@ -37,9 +44,22 @@ tasks.withType(JavaCompile) {
|
||||
|
||||
license {
|
||||
header = rootProject.file('LICENSE-header.txt')
|
||||
include 'net/minecraftforge/'
|
||||
}
|
||||
|
||||
publishing.publications.mavenJava(MavenPublication) {
|
||||
from components.java
|
||||
publishing {
|
||||
publications.register('mavenJava', MavenPublication).configure {
|
||||
from components.java
|
||||
artifactId = 'lowcodelanguage'
|
||||
pom {
|
||||
name = project.name
|
||||
description = 'Language provider for Minecraft Forge that loads resource packs as mods'
|
||||
url = 'https://github.com/MinecraftForge/MinecraftForge'
|
||||
PomUtils.setGitHubDetails(pom, 'MinecraftForge')
|
||||
license PomUtils.Licenses.LGPLv2_1
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven gradleutils.publishingForgeMaven
|
||||
}
|
||||
}
|
||||
+32
-13
@@ -1,9 +1,14 @@
|
||||
import net.minecraftforge.gradleutils.PomUtils
|
||||
|
||||
plugins {
|
||||
id 'org.cadixdev.licenser'
|
||||
id 'java-library'
|
||||
id 'jacoco'
|
||||
id 'maven-publish'
|
||||
id 'org.cadixdev.licenser'
|
||||
id 'net.minecraftforge.gradleutils'
|
||||
}
|
||||
|
||||
apply from: rootProject.file('build_shared.gradle')
|
||||
|
||||
dependencies {
|
||||
compileOnly(libs.nulls)
|
||||
implementation(project(':fmlloader'))
|
||||
@@ -11,22 +16,23 @@ dependencies {
|
||||
}
|
||||
|
||||
java {
|
||||
toolchain.languageVersion = JavaLanguageVersion.of(JAVA_VERSION)
|
||||
withSourcesJar()
|
||||
}
|
||||
|
||||
tasks.named('jar', Jar).configure {
|
||||
manifest {
|
||||
attributes([
|
||||
'Automatic-Module-Name': project.name.toLowerCase(),
|
||||
'FMLModType': 'LANGPROVIDER',
|
||||
'Automatic-Module-Name': 'net.minecraftforge.mclanguageprovider',
|
||||
'FMLModType': 'LANGPROVIDER'
|
||||
] as LinkedHashMap)
|
||||
attributes([
|
||||
'Specification-Title': 'MCLanguage',
|
||||
'Specification-Vendor': 'Forge Development LLC',
|
||||
'Specification-Version': '1',
|
||||
'Implementation-Title': 'MCLanguage',
|
||||
'Implementation-Version': '1.0',
|
||||
'Implementation-Vendor': 'Forge Development LLC',
|
||||
'Specification-Title': 'MCLanguage',
|
||||
'Specification-Vendor': 'Forge Development LLC',
|
||||
'Specification-Version': '1',
|
||||
'Implementation-Title': 'MCLanguage',
|
||||
'Implementation-Vendor': 'Forge Development LLC',
|
||||
'Implementation-Version': '1.0'
|
||||
] as LinkedHashMap, 'net/minecraftforge/fml/mclanguageprovider/')
|
||||
}
|
||||
}
|
||||
@@ -37,9 +43,22 @@ tasks.withType(JavaCompile) {
|
||||
|
||||
license {
|
||||
header = rootProject.file('LICENSE-header.txt')
|
||||
include 'net/minecraftforge/'
|
||||
}
|
||||
|
||||
publishing.publications.mavenJava(MavenPublication) {
|
||||
from components.java
|
||||
publishing {
|
||||
publications.register('mavenJava', MavenPublication).configure {
|
||||
from components.java
|
||||
artifactId = 'mclanguage'
|
||||
pom {
|
||||
name = project.name
|
||||
description = 'Language provider for Minecraft Forge that provides Minecraft Itself'
|
||||
url = 'https://github.com/MinecraftForge/MinecraftForge'
|
||||
PomUtils.setGitHubDetails(pom, 'MinecraftForge')
|
||||
license PomUtils.Licenses.LGPLv2_1
|
||||
}
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven gradleutils.publishingForgeMaven
|
||||
}
|
||||
}
|
||||
+23
-13
@@ -73,12 +73,6 @@ minecraft {
|
||||
// You can set various levels here.
|
||||
// Please read: https://stackoverflow.com/questions/2031163/when-to-use-the-different-log-levels
|
||||
property 'forge.logging.console.level', 'debug'
|
||||
|
||||
mods {
|
||||
"${mod_id}" {
|
||||
source sourceSets.main
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
client {
|
||||
@@ -169,13 +163,12 @@ tasks.named('processResources', ProcessResources).configure {
|
||||
tasks.named('jar', Jar).configure {
|
||||
manifest {
|
||||
attributes([
|
||||
'Specification-Title' : mod_id,
|
||||
'Specification-Vendor' : mod_authors,
|
||||
'Specification-Version' : '1', // We are version 1 of ourselves
|
||||
'Implementation-Title' : project.name,
|
||||
'Implementation-Version' : project.jar.archiveVersion,
|
||||
'Implementation-Vendor' : mod_authors,
|
||||
'Implementation-Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ")
|
||||
'Specification-Title' : mod_id,
|
||||
'Specification-Vendor' : mod_authors,
|
||||
'Specification-Version' : '1', // We are version 1 of ourselves
|
||||
'Implementation-Title' : project.name,
|
||||
'Implementation-Version' : project.jar.archiveVersion,
|
||||
'Implementation-Vendor' : mod_authors
|
||||
])
|
||||
}
|
||||
|
||||
@@ -205,3 +198,20 @@ publishing {
|
||||
tasks.withType(JavaCompile).configureEach {
|
||||
options.encoding = 'UTF-8' // Use the UTF-8 charset for Java compilation
|
||||
}
|
||||
|
||||
eclipse {
|
||||
// Run everytime eclipse builds the code
|
||||
//autoBuildTasks genEclipseRuns
|
||||
// Run when importing the project
|
||||
synchronizationTasks 'genEclipseRuns'
|
||||
}
|
||||
|
||||
// Merge the resources and classes into the same directory.
|
||||
// This is done because java expects modules to be in a single directory.
|
||||
// And if we have it in multiple we have to do performance intensive hacks like having the UnionFileSystem
|
||||
// This will eventually be migrated to ForgeGradle so modders don't need to manually do it. But that is later.
|
||||
sourceSets.each {
|
||||
def dir = layout.buildDirectory.dir("sourcesSets/$it.name")
|
||||
it.output.resourcesDir = dir
|
||||
it.java.destinationDirectory = dir
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
-p @MODULE_PATH@
|
||||
--add-modules @MODULES@
|
||||
--add-exports jdk.naming.dns/com.sun.jndi.dns=java.naming
|
||||
-Djava.net.preferIPv6Addresses=system
|
||||
-DignoreList=@IGNORE_LIST@
|
||||
-DlibraryDirectory=libraries
|
||||
-DlegacyClassPath=@CLASS_PATH@
|
||||
net.minecraftforge.bootstrap.BootstrapLauncher
|
||||
--launchTarget @TASK@
|
||||
--fml.forgeVersion @FORGE_VERSION@
|
||||
--fml.mcVersion @MC_VERSION@
|
||||
--fml.forgeGroup @FORGE_GROUP@
|
||||
--fml.mcpVersion @MCP_VERSION@
|
||||
@@ -1,7 +0,0 @@
|
||||
@echo off
|
||||
REM Forge requires a configured set of both JVM and program arguments.
|
||||
REM Add custom JVM arguments to the user_jvm_args.txt
|
||||
REM Add custom program arguments {such as nogui} to this file in the next line before the %* or
|
||||
REM pass them to this script directly
|
||||
java @user_jvm_args.txt @libraries/@MAVEN_PATH@/win_args.txt %*
|
||||
pause
|
||||
@@ -1,6 +0,0 @@
|
||||
#!/usr/bin/env sh
|
||||
# Forge requires a configured set of both JVM and program arguments.
|
||||
# Add custom JVM arguments to the user_jvm_args.txt
|
||||
# Add custom program arguments {such as nogui} to this file in the next line before the "$@" or
|
||||
# pass them to this script directly
|
||||
java @user_jvm_args.txt @libraries/@MAVEN_PATH@/unix_args.txt "$@"
|
||||
@@ -1,9 +0,0 @@
|
||||
# Xmx and Xms set the maximum and minimum RAM usage, respectively.
|
||||
# They can take any number, followed by an M or a G.
|
||||
# M means Megabyte, G means Gigabyte.
|
||||
# For example, to set the maximum to 3GB: -Xmx3G
|
||||
# To set the minimum to 2.5GB: -Xms2500M
|
||||
|
||||
# A good default for a modded server is 4GB.
|
||||
# Uncomment the next line to set it.
|
||||
# -Xmx4G
|
||||
+23
-20
@@ -3,7 +3,7 @@ import org.gradle.internal.os.OperatingSystem
|
||||
pluginManagement {
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
mavenLocal()
|
||||
//mavenLocal()
|
||||
maven { url = 'https://maven.minecraftforge.net/' }
|
||||
}
|
||||
}
|
||||
@@ -25,10 +25,12 @@ plugins {
|
||||
dependencyResolutionManagement {
|
||||
versionCatalogs {
|
||||
libs {
|
||||
library('forgespi', 'net.minecraftforge:forgespi:7.1.0') // Needs modlauncher
|
||||
library('bootstrap', 'net.minecraftforge:bootstrap:1.2.0') // Needs modlauncher
|
||||
library('modlauncher', 'net.minecraftforge:modlauncher:10.1.1') // Needs securemodules
|
||||
library('securemodules', 'net.minecraftforge:securemodules:2.2.3') // Needs unsafe
|
||||
library('forgespi', 'net.minecraftforge:forgespi:7.1.1') // Needs modlauncher
|
||||
library('bootstrap', 'net.minecraftforge:bootstrap:2.0.0') // Needs modlauncher
|
||||
library('bootstrap-api', 'net.minecraftforge:bootstrap-api:2.0.0')
|
||||
library('bootstrap-shim', 'net.minecraftforge:bootstrap-shim:2.0.0')
|
||||
library('modlauncher', 'net.minecraftforge:modlauncher:10.1.2') // Needs securemodules
|
||||
library('securemodules', 'net.minecraftforge:securemodules:2.2.7') // Needs unsafe
|
||||
library('unsafe', 'net.minecraftforge:unsafe:0.9.2')
|
||||
library('accesstransformers', 'net.minecraftforge:accesstransformers:8.1.1')
|
||||
library('antlr-runtime', 'org.antlr:antlr4-runtime:4.9.1') // Needed by Access Transformer because parsing a simple text file was too complicated
|
||||
@@ -37,16 +39,16 @@ dependencyResolutionManagement {
|
||||
library('eventbus', 'net.minecraftforge:eventbus:6.2.0')
|
||||
library('typetools', 'net.jodah:typetools:0.6.3') // Needed by EventBus because of lambdas
|
||||
library('mergetool-api', 'net.minecraftforge:mergetool-api:1.0')
|
||||
|
||||
|
||||
library('mixin', 'org.spongepowered:mixin:0.8.5')
|
||||
library('nulls', 'org.jetbrains:annotations:23.0.0') // Got to have our null annotations!
|
||||
library('slf4j-api', 'org.slf4j:slf4j-api:1.8.0-beta4')
|
||||
library('slf4j-api', 'org.slf4j:slf4j-api:2.0.7')
|
||||
library('maven-artifact', 'org.apache.maven:maven-artifact:3.8.5')
|
||||
|
||||
|
||||
// Google's InMemory File System. Used by ForgeDev Tests for now, but could be useful for a lot of things.
|
||||
library('jimfs', 'com.google.jimfs:jimfs:1.3.0')
|
||||
bundle('jimfs', ['guava', 'failureaccess'])
|
||||
|
||||
|
||||
// ASM it's used for so many of our hacks
|
||||
version('asm', '9.6')
|
||||
library('asm', 'org.ow2.asm', 'asm' ).versionRef('asm')
|
||||
@@ -55,27 +57,27 @@ dependencyResolutionManagement {
|
||||
library('asm-commons', 'org.ow2.asm', 'asm-commons' ).versionRef('asm')
|
||||
library('asm-analysis', 'org.ow2.asm', 'asm-analysis').versionRef('asm')
|
||||
bundle('asm', ['asm', 'asm-tree', 'asm-util', 'asm-commons', 'asm-analysis'])
|
||||
|
||||
|
||||
// Terminal Console Appender.. essentually make pretty colors in the console, but it's been a PITA
|
||||
library('terminalconsoleappender', 'net.minecrell:terminalconsoleappender:1.2.0')
|
||||
version('jline', '3.12.1')
|
||||
library('jline-reader', 'org.jline', 'jline-reader' ).versionRef('jline')
|
||||
library('jline-terminal', 'org.jline', 'jline-terminal').versionRef('jline')
|
||||
bundle('terminalconsoleappender', ['terminalconsoleappender', 'jline-reader', 'jline-terminal'])
|
||||
|
||||
|
||||
// The core of our configuration system, it has many flaws, but it works for the most part
|
||||
version('night-config', '3.6.4')
|
||||
library('night-config-toml', 'com.electronwill.night-config', 'toml').versionRef('night-config')
|
||||
library('night-config-core', 'com.electronwill.night-config', 'core').versionRef('night-config')
|
||||
bundle('night-config', ['night-config-toml', 'night-config-core'])
|
||||
|
||||
|
||||
// Jar in Jar FileSystem
|
||||
version('jarjar', '0.3.19')
|
||||
library('jarjar-fs', 'net.minecraftforge', 'JarJarFileSystems').versionRef('jarjar')
|
||||
library('jarjar-meta', 'net.minecraftforge', 'JarJarMetadata' ).versionRef('jarjar')
|
||||
library('jarjar-selector', 'net.minecraftforge', 'JarJarSelector' ).versionRef('jarjar')
|
||||
bundle('jarjar', ['jarjar-fs', 'jarjar-selector', 'jarjar-meta'])
|
||||
|
||||
|
||||
// These are libraries shipped by the MC launcher, try and keep them in sync with the manifest
|
||||
// but honestly if we don't it just means we ship them as normal libraries by adding them to the installer config in the forge project
|
||||
library('gson', 'com.google.code.gson:gson:2.10.1')
|
||||
@@ -88,8 +90,8 @@ dependencyResolutionManagement {
|
||||
library('mojang-logging', 'com.mojang:logging:1.1.1')
|
||||
library('jopt-simple', 'net.sf.jopt-simple:jopt-simple:5.0.4')
|
||||
library('commons-io', 'commons-io:commons-io:2.13.0')
|
||||
|
||||
version('lwjgl', '3.3.1')
|
||||
|
||||
version('lwjgl', '3.3.2')
|
||||
library('lwjgl', 'org.lwjgl', 'lwjgl' ).versionRef('lwjgl')
|
||||
library('lwjgl-glfw', 'org.lwjgl', 'lwjgl-glfw' ).versionRef('lwjgl')
|
||||
library('lwjgl-opengl', 'org.lwjgl', 'lwjgl-opengl').versionRef('lwjgl')
|
||||
@@ -110,13 +112,14 @@ include 'lowcodelanguage'
|
||||
include 'fmlearlydisplay'
|
||||
|
||||
include ':mcp'
|
||||
include ':forge'
|
||||
|
||||
project(":mcp").projectDir = file("projects/mcp")
|
||||
|
||||
include ':forge'
|
||||
project(":forge").projectDir = file("projects/forge")
|
||||
project(':forge').buildFileName = '../../build_forge.gradle'
|
||||
|
||||
if (!System.env.TEAMCITY_VERSION) {
|
||||
include ':clean'
|
||||
project(':clean').projectDir = file('projects/clean')
|
||||
project(':clean').buildFileName = '../../build_clean.gradle'
|
||||
include ':clean'
|
||||
project(':clean').projectDir = file('projects/clean')
|
||||
project(':clean').buildFileName = '../../build_clean.gradle'
|
||||
}
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 KiB |
Binary file not shown.
@@ -1,69 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="warn" packages="net.minecrell.terminalconsole,net.minecrell.terminalconsole.util,com.mojang.util,cpw.mods.modlauncher.log,net.minecraftforge.fml.loading.log4j" shutdownHook="disable">
|
||||
<filters>
|
||||
<ThresholdFilter level="WARN" onMatch="ACCEPT" onMismatch="NEUTRAL"/>
|
||||
<MarkerFilter marker="NETWORK_PACKETS" onMatch="${sys:forge.logging.marker.networking:-DENY}" onMismatch="NEUTRAL"/>
|
||||
<MarkerFilter marker="CLASSLOADING" onMatch="${sys:forge.logging.marker.classloading:-DENY}" onMismatch="NEUTRAL"/>
|
||||
<MarkerFilter marker="LAUNCHPLUGIN" onMatch="${sys:forge.logging.marker.launchplugin:-DENY}" onMismatch="NEUTRAL"/>
|
||||
<MarkerFilter marker="CLASSDUMP" onMatch="${sys:forge.logging.marker.classdump:-DENY}" onMismatch="NEUTRAL"/>
|
||||
<MarkerFilter marker="AXFORM" onMatch="${sys:forge.logging.marker.axform:-DENY}" onMismatch="NEUTRAL"/>
|
||||
<MarkerFilter marker="EVENTBUS" onMatch="${sys:forge.logging.marker.eventbus:-DENY}" onMismatch="NEUTRAL"/>
|
||||
<MarkerFilter marker="DISTXFORM" onMatch="${sys:forge.logging.marker.distxform:-DENY}" onMismatch="NEUTRAL"/>
|
||||
<MarkerFilter marker="SCAN" onMatch="${sys:forge.logging.marker.scan:-DENY}" onMismatch="NEUTRAL"/>
|
||||
<MarkerFilter marker="REGISTRIES" onMatch="${sys:forge.logging.marker.registries:-DENY}" onMismatch="NEUTRAL"/>
|
||||
<MarkerFilter marker="REGISTRYDUMP" onMatch="${sys:forge.logging.marker.registrydump:-DENY}" onMismatch="NEUTRAL"/>
|
||||
<MarkerFilter marker="SPLASH" onMatch="${sys:forge.logging.marker.splash:-DENY}" onMismatch="NEUTRAL"/>
|
||||
<MarkerFilter marker="RESOURCE-CACHE" onMatch="${sys:forge.logging.marker.resource.cache:-DENY}" onMismatch="NEUTRAL"/>
|
||||
<MarkerFilter marker="FORGEMOD" onMatch="${sys:forge.logging.marker.forgemod:-ACCEPT}" onMismatch="NEUTRAL"/>
|
||||
<MarkerFilter marker="LOADING" onMatch="${sys:forge.logging.marker.loading:-ACCEPT}" onMismatch="NEUTRAL"/>
|
||||
<MarkerFilter marker="CORE" onMatch="${sys:forge.logging.marker.core:-ACCEPT}" onMismatch="NEUTRAL"/>
|
||||
</filters>
|
||||
<Appenders>
|
||||
<TerminalConsole name="Console">
|
||||
<PatternLayout>
|
||||
<LoggerNamePatternSelector defaultPattern="%highlightForge{[%d{HH:mm:ss}] [%t/%level] [%c{2.}/%markerSimpleName]: %minecraftFormatting{%msg{nolookup}}%n%tEx}">
|
||||
<!-- don't include the full logger name for Mojang's logs since they use full class names and it's very verbose -->
|
||||
<PatternMatch key="net.minecraft." pattern="%highlightForge{[%d{HH:mm:ss}] [%t/%level] [minecraft/%logger{1}]: %minecraftFormatting{%msg{nolookup}}%n%tEx}"/>
|
||||
<PatternMatch key="com.mojang." pattern="%highlightForge{[%d{HH:mm:ss}] [%t/%level] [mojang/%logger{1}]: %minecraftFormatting{%msg{nolookup}}%n%tEx}"/>
|
||||
</LoggerNamePatternSelector>
|
||||
</PatternLayout>
|
||||
</TerminalConsole>
|
||||
<Queue name="ServerGuiConsole" ignoreExceptions="true">
|
||||
<PatternLayout>
|
||||
<LoggerNamePatternSelector defaultPattern="[%d{HH:mm:ss}] [%t/%level] [%c{2.}/%markerSimpleName]: %minecraftFormatting{%msg{nolookup}}{strip}%n">
|
||||
<!-- don't include the full logger name for Mojang's logs since they use full class names and it's very verbose -->
|
||||
<PatternMatch key="net.minecraft." pattern="[%d{HH:mm:ss}] [%t/%level] [minecraft/%logger{1}]: %minecraftFormatting{%msg{nolookup}}{strip}%n"/>
|
||||
<PatternMatch key="com.mojang." pattern="[%d{HH:mm:ss}] [%t/%level] [mojang/%logger{1}]: %minecraftFormatting{%msg{nolookup}}{strip}%n"/>
|
||||
</LoggerNamePatternSelector>
|
||||
</PatternLayout>
|
||||
</Queue>
|
||||
<RollingRandomAccessFile name="File" fileName="logs/latest.log" filePattern="logs/%d{yyyy-MM-dd}-%i.log.gz">
|
||||
<PatternLayout pattern="[%d{ddMMMyyyy HH:mm:ss.SSS}] [%t/%level] [%logger/%markerSimpleName]: %minecraftFormatting{%msg{nolookup}}{strip}%n%xEx"/>
|
||||
<Policies>
|
||||
<TimeBasedTriggeringPolicy/>
|
||||
<OnStartupTriggeringPolicy/>
|
||||
</Policies>
|
||||
<DefaultRolloverStrategy max="99" fileIndex="min"/>
|
||||
</RollingRandomAccessFile>
|
||||
<RollingRandomAccessFile name="DebugFile" fileName="logs/debug.log" filePattern="logs/debug-%i.log.gz">
|
||||
<PatternLayout pattern="[%d{ddMMMyyyy HH:mm:ss.SSS}] [%t/%level] [%logger/%markerSimpleName]: %minecraftFormatting{%msg{nolookup}}{strip}%n%xEx"/>
|
||||
<Policies>
|
||||
<OnStartupTriggeringPolicy/>
|
||||
<SizeBasedTriggeringPolicy size="200MB"/>
|
||||
</Policies>
|
||||
<DefaultRolloverStrategy max="5" fileIndex="min"/>
|
||||
</RollingRandomAccessFile>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<!-- make sure mojang's logging is set to 'info' so that their LOGGER.isDebugEnabled() behavior isn't active -->
|
||||
<Logger level="${sys:forge.logging.mojang.level:-info}" name="com.mojang"/>
|
||||
<Logger level="${sys:forge.logging.mojang.level:-info}" name="net.minecraft"/>
|
||||
<Logger level="${sys:forge.logging.classtransformer.level:-info}" name="cpw.mods.modlauncher.ClassTransformer"/>
|
||||
<Root level="all">
|
||||
<AppenderRef ref="Console" level="${sys:forge.logging.console.level:-info}"/>
|
||||
<AppenderRef ref="ServerGuiConsole" level="${sys:forge.logging.console.level:-info}"/>
|
||||
<AppenderRef ref="File" level="${sys:forge.logging.file.level:-info}"/>
|
||||
<AppenderRef ref="DebugFile" level="${sys:forge.logging.debugFile.level:-debug}"/>
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
@@ -1,140 +0,0 @@
|
||||
/*
|
||||
* Minecraft Forge
|
||||
* Copyright (c) 2016-2021.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation version 2.1
|
||||
* of the License.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.test;
|
||||
|
||||
import com.google.common.graph.GraphBuilder;
|
||||
import com.google.common.graph.MutableGraph;
|
||||
import net.minecraftforge.fml.loading.toposort.CyclePresentException;
|
||||
import net.minecraftforge.fml.loading.toposort.StronglyConnectedComponentDetector;
|
||||
import net.minecraftforge.fml.loading.toposort.TopologicalSort;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.contains;
|
||||
import static org.hamcrest.Matchers.containsInAnyOrder;
|
||||
|
||||
/**
|
||||
* Tests for topological sort.
|
||||
*/
|
||||
public class TopologicalSortTests {
|
||||
|
||||
@Test
|
||||
@DisplayName("strongly connected components")
|
||||
@SuppressWarnings("unchecked")
|
||||
void testScc() {
|
||||
MutableGraph<Integer> graph = GraphBuilder.directed().build();
|
||||
graph.putEdge(2, 4);
|
||||
graph.putEdge(4, 7);
|
||||
graph.putEdge(4, 6);
|
||||
graph.putEdge(6, 1);
|
||||
graph.putEdge(3, 4);
|
||||
graph.putEdge(1, 4);
|
||||
graph.putEdge(3, 1);
|
||||
Set<Set<Integer>> components = new StronglyConnectedComponentDetector<>(graph).getComponents();
|
||||
assertThat(components, containsInAnyOrder(contains(2), contains(3), contains(7), containsInAnyOrder(1, 4, 6)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("strongly connected components 2")
|
||||
@SuppressWarnings("unchecked")
|
||||
void testScc2() {
|
||||
MutableGraph<Integer> graph = GraphBuilder.directed().build();
|
||||
graph.putEdge(2, 4);
|
||||
graph.putEdge(4, 8);
|
||||
graph.putEdge(8, 2);
|
||||
graph.putEdge(2, 1);
|
||||
graph.putEdge(2, 3);
|
||||
graph.putEdge(1, 9);
|
||||
graph.putEdge(9, 7);
|
||||
graph.putEdge(7, 5);
|
||||
graph.putEdge(5, 1);
|
||||
graph.putEdge(5, 3);
|
||||
graph.putEdge(3, 6);
|
||||
graph.putEdge(6, 5);
|
||||
graph.putEdge(9, 3);
|
||||
Set<Set<Integer>> components = new StronglyConnectedComponentDetector<>(graph).getComponents();
|
||||
assertThat(components, containsInAnyOrder(containsInAnyOrder(1, 3, 5, 6, 7, 9), containsInAnyOrder(2, 4, 8)));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("basic sort")
|
||||
void testBasicSort() {
|
||||
MutableGraph<Integer> graph = GraphBuilder.directed().build();
|
||||
graph.putEdge(2, 4);
|
||||
graph.putEdge(4, 7);
|
||||
graph.putEdge(4, 6);
|
||||
graph.putEdge(3, 4);
|
||||
graph.putEdge(1, 4);
|
||||
graph.putEdge(3, 1);
|
||||
List<Integer> list = TopologicalSort.topologicalSort(graph, Comparator.naturalOrder());
|
||||
assertThat(list, contains(2, 3, 1, 4, 6, 7));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("basic sort 2")
|
||||
void testBasicSort2() {
|
||||
MutableGraph<Integer> graph = GraphBuilder.directed().build();
|
||||
graph.putEdge(7, 6);
|
||||
graph.putEdge(8, 6);
|
||||
graph.putEdge(8, 2);
|
||||
graph.putEdge(12, 2);
|
||||
graph.putEdge(2, 6);
|
||||
graph.putEdge(6, 1);
|
||||
graph.putEdge(2, 4);
|
||||
graph.putEdge(2, 1);
|
||||
graph.putEdge(1, 4);
|
||||
graph.putEdge(1, 3);
|
||||
graph.putEdge(4, 5);
|
||||
graph.putEdge(1, 5);
|
||||
graph.putEdge(5, 3);
|
||||
graph.putEdge(1, 10);
|
||||
graph.putEdge(3, 11);
|
||||
graph.putEdge(5, 9);
|
||||
graph.putEdge(11, 9);
|
||||
graph.putEdge(11, 13);
|
||||
graph.putEdge(10, 13);
|
||||
List<Integer> list = TopologicalSort.topologicalSort(graph, Collections.reverseOrder());
|
||||
assertThat(list, contains(12, 8, 7, 2, 6, 1, 10, 4, 5, 3, 11, 13, 9));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("loop sort")
|
||||
void testLoopSort() {
|
||||
MutableGraph<Integer> graph = GraphBuilder.directed().build();
|
||||
graph.putEdge(2, 4);
|
||||
graph.putEdge(4, 7);
|
||||
graph.putEdge(4, 6);
|
||||
graph.putEdge(6, 1);
|
||||
graph.putEdge(3, 4);
|
||||
graph.putEdge(1, 4);
|
||||
graph.putEdge(3, 1);
|
||||
CyclePresentException ex = Assertions.assertThrows(CyclePresentException.class, () -> {
|
||||
TopologicalSort.topologicalSort(graph, Comparator.naturalOrder());
|
||||
});
|
||||
assertThat(ex.getCycles(), contains(containsInAnyOrder(1, 4, 6)));
|
||||
}
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loaded 100 of 670 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user