mirror of
https://github.com/MinecraftForge/MinecraftForge.git
synced 2026-11-14 01:04:24 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16defac82e | ||
|
|
16483d4ea1 | ||
|
|
28909812bb | ||
|
|
e5580b84b7 | ||
|
|
607ca5beea | ||
|
|
744906e33a | ||
|
|
c1557ec3c2 | ||
|
|
4ac1a3dedf | ||
|
|
a26f952212 | ||
|
|
83ee68c0b0 | ||
|
|
a5ff932125 | ||
|
|
0d820eddd4 | ||
|
|
83c651ce39 | ||
|
|
7cd88944d4 | ||
|
|
dff52ecd71 | ||
|
|
da34d97d9d | ||
|
|
a27405b8d1 | ||
|
|
7a443276a5 | ||
|
|
e34090c809 | ||
|
|
72e8ae252c | ||
|
|
347c480bdc | ||
|
|
e0b9474621 | ||
|
|
8ad1e899a3 | ||
|
|
3b6bb5edbc | ||
|
|
c4e7d694c1 | ||
|
|
e00de1a326 | ||
|
|
e9ee999558 | ||
|
|
a1aacf25fe | ||
|
|
0c2f3e5523 | ||
|
|
a6978cffbf | ||
|
|
2332ecf984 | ||
|
|
9cd73c84a6 | ||
|
|
ab544a78d3 | ||
|
|
5b0abf97c8 | ||
|
|
587b684035 | ||
|
|
90dafb0df0 | ||
|
|
aff05a4cf3 | ||
|
|
1ddac54256 | ||
|
|
fcf9088aa5 | ||
|
|
374dbc7a4b | ||
|
|
d25ba7adc8 | ||
|
|
05c88ae33a | ||
|
|
e2d564d32a | ||
|
|
225092130e | ||
|
|
eefca22aa3 | ||
|
|
19bbb88e52 | ||
|
|
18886e5128 | ||
|
|
47dfa83cff | ||
|
|
0940c532f2 | ||
|
|
d80f106140 | ||
|
|
516b35ce57 | ||
|
|
be60ddbfca | ||
|
|
af6e848227 | ||
|
|
dd139115ed | ||
|
|
145df60bec | ||
|
|
f7cca8fb88 | ||
|
|
1f59b3f9fe | ||
|
|
084effdadf | ||
|
|
40a7998281 | ||
|
|
4786fa6f89 | ||
|
|
9291b865c7 | ||
|
|
52eca96fdc | ||
|
|
4649f7af36 | ||
|
|
3edefbe33e |
No files matched your search
@@ -21,3 +21,6 @@ src/generated/**/*.json text eol=lf
|
||||
src/generated_test/**/.cache/cache text eol=lf
|
||||
src/generated_test/**/*.json text eol=lf
|
||||
|
||||
# We force eclipse to use unix line endings
|
||||
ide/eclipse/template/.settings/*.prefs text eol=lf
|
||||
|
||||
+6
-4
@@ -13,6 +13,9 @@
|
||||
/classes
|
||||
*.iml
|
||||
|
||||
# We however WANT the ide folder as it has tempaltes/configs for developing Forge.
|
||||
!ide/**
|
||||
|
||||
#gradle
|
||||
/build
|
||||
/.gradle
|
||||
@@ -23,7 +26,6 @@
|
||||
/projects/mcp/
|
||||
/projects/clean/
|
||||
/projects/forge/
|
||||
/projects/fmlonly/
|
||||
#/patches/*
|
||||
|
||||
#occupational hazards
|
||||
@@ -32,9 +34,9 @@
|
||||
/projects/**/run/
|
||||
/projects/**/*.launch
|
||||
/repo/
|
||||
src/generated/resources/.cache/
|
||||
src/generated_test/resources/.cache/
|
||||
src/generated_mdk/resources/.cache/
|
||||
src/generated/resources/**/.cache/
|
||||
src/generated_test/resources/**/.cache/
|
||||
src/generated_mdk/resources/**/.cache/
|
||||
|
||||
#Patch rejects
|
||||
#/patches-/
|
||||
|
||||
Vendored
+2
-17
@@ -36,8 +36,8 @@ project {
|
||||
buildType(PullRequestCompatibility)
|
||||
|
||||
params {
|
||||
text("docker_jdk_version", "21", label = "Gradle version", description = "The version of the JDK to use during execution of tasks in a JDK.", display = ParameterDisplay.HIDDEN, allowEmpty = false)
|
||||
text("docker_gradle_version", "8.8", label = "Gradle version", description = "The version of Gradle to use during execution of Gradle tasks.", display = ParameterDisplay.HIDDEN, allowEmpty = false)
|
||||
text("docker_jdk_version", "17", label = "Gradle version", description = "The version of the JDK to use during execution of tasks in a JDK.", display = ParameterDisplay.HIDDEN, allowEmpty = false)
|
||||
text("docker_gradle_version", "7.5.1", label = "Gradle version", description = "The version of Gradle to use during execution of Gradle tasks.", display = ParameterDisplay.HIDDEN, allowEmpty = false)
|
||||
text("git_main_branch", "1.20.x", label = "Git Main Branch", description = "The git main or default branch to use in VCS operations.", display = ParameterDisplay.HIDDEN, allowEmpty = false)
|
||||
text("git_branch_spec", """
|
||||
+:refs/heads/(main*)
|
||||
@@ -47,8 +47,6 @@ project {
|
||||
""".trimIndent(), label = "The branch specification of the repository", description = "By default all main branches are build by the configuration. Modify this value to adapt the branches build.", display = ParameterDisplay.HIDDEN, allowEmpty = true)
|
||||
text("github_repository_name", "MinecraftForge", label = "The github repository name. Used to connect to it in VCS Roots.", description = "This is the repository slug on github. So for example `MinecraftForge` or `MinecraftForge`. It is interpolated into the global VCS Roots.", display = ParameterDisplay.HIDDEN, allowEmpty = false)
|
||||
text("env.PUBLISHED_JAVA_ARTIFACT_ID", "forge", label = "Published artifact id", description = "The maven coordinate artifact id that has been published by this build. Can not be empty.", allowEmpty = false)
|
||||
text("env.PUBLISHED_JAVA_FML_ARTIFACT_ID", "fmlonly", label = "Published fmlonly artifact id", description = "The maven coordinate artifact id for fml only that has been published by this build. Can not be empty.", allowEmpty = false)
|
||||
text("env.PUBLISHED_JAVA_FML_ARTIFACT_VERSION", "0.0.0-SNAPSHOT", label = "Published fmlonly artifact version", description = "The version for fml only that has been published by this build. Can not be empty.", allowEmpty = false)
|
||||
text("env.PUBLISHED_JAVA_GROUP", "net.minecraftforge", label = "Published group", description = "The maven coordinate group that has been published by this build. Can not be empty.", allowEmpty = false)
|
||||
//These are references and not actually keys
|
||||
password("env.CROWDIN_KEY", "credentialsJSON:a3102dbe-805d-4177-9f54-3d2c2eb08fd5", display = ParameterDisplay.HIDDEN)
|
||||
@@ -76,19 +74,6 @@ object Build : BuildType({
|
||||
id("MinecraftForge_MinecraftForge__Build")
|
||||
name = "Build"
|
||||
description = "Builds and Publishes the main branches of the project."
|
||||
|
||||
features {
|
||||
feature {
|
||||
id = "trigger_fml_only_files_generator"
|
||||
type = "triggerBuildFeature"
|
||||
param("triggers", "MinecraftForge_FilesGenerator_GeneratePages")
|
||||
param("parameters", """
|
||||
env.PUBLISHED_JAVA_FML_ARTIFACT_ID~env.PUBLISHED_JAVA_ARTIFACT_ID
|
||||
env.PUBLISHED_JAVA_FML_ARTIFACT_VERSION~env.PUBLISHED_JAVA_ARTIFACT_VERSION
|
||||
env.PUBLISHED_JAVA_GROUP
|
||||
""".trimIndent())
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
object BuildSecondaryBranches : BuildType({
|
||||
|
||||
+297
-819
File diff suppressed because it is too large.
Load diff
+1
-1
@@ -27,6 +27,6 @@ abstract class BytecodePredicateFinder extends BytecodeFinder {
|
||||
@Internal
|
||||
@Override
|
||||
protected Object getData() {
|
||||
return matches.stream().sorted().toList() ?: {throw new RuntimeException('Failed to find any targets, please ensure that method names and descriptors are correct.')}()
|
||||
return matches.toSorted(Comparator.naturalOrder()) ?: {throw new RuntimeException('Failed to find any targets, please ensure that method names and descriptors are correct.')}()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package net.minecraftforge.forge.tasks;
|
||||
|
||||
import java.util.Properties;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Eclipse config files are literally just java properties, with the header cleaned up.
|
||||
* https://github.com/eclipse/buildship/blob/5b2c7fca7fa86cd74d71b3c099c7b1559eba038e/org.eclipse.buildship.core/src/main/java/org/eclipse/buildship/core/internal/configuration/PreferenceStore.java#L238
|
||||
*
|
||||
* This does the same thing, as well as sorting alphabetically.
|
||||
* It also ignores all comments. We can add them latter if someone cares.
|
||||
*/
|
||||
public class CleanProperties extends Properties {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private static final String LINE_SEP = System.getProperty("line.separator");
|
||||
private static final String UNIX_LINE_SEP = "\n";
|
||||
private static final Charset ENCODING = StandardCharsets.UTF_8;
|
||||
|
||||
public CleanProperties load(File input) throws IOException {
|
||||
if (input.exists()) {
|
||||
try (Reader is = new InputStreamReader(new FileInputStream(input), ENCODING)) {
|
||||
super.load(is);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void store(File out) throws IOException {
|
||||
if (!out.getParentFile().exists())
|
||||
out.getParentFile().mkdirs();
|
||||
|
||||
try (OutputStream os = new FileOutputStream(out)) {
|
||||
store(os, null);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized Enumeration<Object> keys() {
|
||||
Set<Object> ret = new TreeSet<>();
|
||||
for (Enumeration<?> e = super.keys(); e.hasMoreElements();)
|
||||
ret.add(e.nextElement());
|
||||
return Collections.enumeration(ret);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Map.Entry<Object, Object>> entrySet() {
|
||||
Set<Map.Entry<Object, Object>> ret = new TreeSet<>((l, r) -> ((String)l.getKey()).compareTo((String)r.getKey()));
|
||||
ret.addAll(super.entrySet());
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(OutputStream out, String comments) throws IOException {
|
||||
out.write(clean().getBytes(ENCODING));
|
||||
out.flush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(Writer out, String comments) throws IOException {
|
||||
out.write(clean());
|
||||
out.flush();
|
||||
}
|
||||
|
||||
private String clean() throws IOException {
|
||||
ByteArrayOutputStream tmp = new ByteArrayOutputStream();
|
||||
try {
|
||||
super.store(tmp, null);
|
||||
} finally {
|
||||
tmp.close();
|
||||
}
|
||||
|
||||
String ret = tmp.toString(ENCODING).replace(LINE_SEP, UNIX_LINE_SEP);
|
||||
ret = ret.substring(ret.indexOf(UNIX_LINE_SEP) + 1);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
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.Input
|
||||
import org.gradle.api.tasks.Optional
|
||||
import org.gradle.api.tasks.OutputFile
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipInputStream
|
||||
import java.util.zip.ZipOutputStream
|
||||
|
||||
abstract class Crowdin extends DefaultTask {
|
||||
@Input abstract Property<String> getId()
|
||||
@Input @Optional abstract Property<String> getKey()
|
||||
@Input boolean json = true
|
||||
@OutputFile abstract RegularFileProperty getOutput()
|
||||
@OutputFile abstract RegularFileProperty getExport()
|
||||
|
||||
Crowdin() {
|
||||
outputs.upToDateWhen{ false }
|
||||
id.convention('minecraft-forge')
|
||||
output.convention(project.layout.buildDirectory.dir(name).map {it.file("output.zip") })
|
||||
export.convention(project.layout.buildDirectory.dir(name).map {it.file("export.json") })
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
def run() {
|
||||
File outputFile = output.get().asFile
|
||||
File exportFile = export.get().asFile
|
||||
if (outputFile.exists())
|
||||
outputFile.delete()
|
||||
|
||||
if (!key.isPresent())
|
||||
return
|
||||
String key = this.key.get()
|
||||
String id = this.id.get()
|
||||
|
||||
// Force an export
|
||||
new URL("https://api.crowdin.com/api/project/${id}/export?key=${key}").withInputStream { i ->
|
||||
exportFile.withOutputStream { it << i }
|
||||
}
|
||||
|
||||
if (!exportFile.text.contains('success')) {
|
||||
throw new RuntimeException("Crowdin export failed, see ${exportFile} for more info")
|
||||
}
|
||||
|
||||
new URL("https://api.crowdin.com/api/project/${id}/download/all.zip?key=${key}").withInputStream { i ->
|
||||
new ZipInputStream(i).withCloseable { zin ->
|
||||
outputFile.withOutputStream { out ->
|
||||
new ZipOutputStream(out).withCloseable { zout ->
|
||||
ZipEntry zein
|
||||
while ((zein = zin.nextEntry) != null) {
|
||||
if (zein.isDirectory()) {
|
||||
zout.putNextEntry(new ZipEntry(zein.name))
|
||||
} else {
|
||||
// 1.13+ uses json
|
||||
if (zein.name.endsWith('.json') == json) {
|
||||
ZipEntry zeout = new ZipEntry(json ? zein.name.toLowerCase() : zein.name)
|
||||
zeout.time = 1
|
||||
zout.putNextEntry(zeout)
|
||||
zout << zin
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
package net.minecraftforge.forge.tasks
|
||||
|
||||
import org.gradle.api.DefaultTask
|
||||
import org.gradle.api.file.DirectoryProperty
|
||||
import org.gradle.api.tasks.InputDirectory
|
||||
import org.gradle.api.tasks.TaskAction
|
||||
|
||||
import java.nio.file.Files
|
||||
|
||||
abstract class FixPatchImports extends DefaultTask {
|
||||
|
||||
@InputDirectory abstract DirectoryProperty getClean()
|
||||
@InputDirectory abstract DirectoryProperty getPatched()
|
||||
|
||||
@TaskAction
|
||||
void run() {
|
||||
final patchedPath = getPatched().get().asFile.toPath()
|
||||
final cleanPath = getClean().get().asFile.toPath()
|
||||
|
||||
try (final var stream = Files.find(patchedPath, Integer.MAX_VALUE, (path, attr) -> path.toString().endsWith('.java'))) {
|
||||
final itr = stream.iterator()
|
||||
while (itr.hasNext()) {
|
||||
final next = itr.next()
|
||||
final clean = cleanPath.resolve(patchedPath.relativize(next).toString())
|
||||
|
||||
final patchedLines = Files.readAllLines(next)
|
||||
final cleanLines = Files.readAllLines(clean)
|
||||
|
||||
final patchedImport = patchedLines.findLastIndexOf(2) { it.startsWith('import ') }
|
||||
final cleanImport = cleanLines.findLastIndexOf(2) { it.startsWith('import ') }
|
||||
|
||||
if (cleanImport !== patchedImport) {
|
||||
patchedLines.removeIf { it.startsWith('import ') }
|
||||
patchedLines.addAll(2, cleanLines.subList(2, cleanImport + 1))
|
||||
Files.write(next, patchedLines)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -127,7 +127,7 @@ public class Util {
|
||||
return getArtifacts(project, cfg, true)
|
||||
}
|
||||
|
||||
private static boolean checkExists(url) {
|
||||
static boolean checkExists(url) {
|
||||
try {
|
||||
def code = new URL(url).openConnection().with {
|
||||
requestMethod = 'HEAD'
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
plugins {
|
||||
id 'java-library'
|
||||
id 'eclipse'
|
||||
id 'net.minecraftforge.gradle.patcher' version '[6.0.16,6.2)'
|
||||
}
|
||||
|
||||
evaluationDependsOn(':mcp')
|
||||
|
||||
dependencies {
|
||||
implementation(libs.forgespi)
|
||||
}
|
||||
|
||||
patcher {
|
||||
parent = project(':mcp')
|
||||
mcVersion = MC_VERSION
|
||||
patchedSrc = file('src/main/java')
|
||||
|
||||
mappings channel: MAPPING_CHANNEL, version: MAPPING_VERSION
|
||||
|
||||
runs {
|
||||
clean_client {
|
||||
client true
|
||||
taskName 'clean_client'
|
||||
ideaModule "${rootProject.name}.${project.name}.main"
|
||||
|
||||
main 'net.minecraft.client.main.Main'
|
||||
workingDirectory project.file('run/client')
|
||||
|
||||
args '--gameDir', '.'
|
||||
args '--version', MC_VERSION
|
||||
args '--assetsDir', downloadAssets.output
|
||||
args '--assetIndex', '{asset_index}'
|
||||
args '--accessToken', '0'
|
||||
}
|
||||
|
||||
clean_server {
|
||||
client false
|
||||
taskName 'clean_server'
|
||||
ideaModule "${rootProject.name}.${project.name}.main"
|
||||
|
||||
main 'net.minecraft.server.Main'
|
||||
workingDirectory project.file('run/server')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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'
|
||||
}
|
||||
+11
-30
@@ -2,15 +2,16 @@
|
||||
|
||||
MinecraftForge
|
||||
=============
|
||||
[][Download]
|
||||
[][Download]
|
||||
[][Download] [][Discord] [][Patreon]
|
||||
|
||||
Forge is a free, open-source modding API all of your favourite mods use!
|
||||
|
||||
| Version | Support |
|
||||
|--------------|-----------------------------|
|
||||
| All versions | [Supported][Support-Policy] |
|
||||
| Version | Support |
|
||||
|---------| ------------- |
|
||||
| 1.20.x | Active |
|
||||
| 1.19.x | Legacy |
|
||||
|
||||
* [Download]
|
||||
* [Forum]
|
||||
@@ -26,23 +27,10 @@ You can download the installer for the *Recommended Build* or the
|
||||
*Latest build* there. Latest builds may have newer features but may be
|
||||
more unstable as a result. The installer will attempt to install Forge
|
||||
into your vanilla launcher environment, where you can then create a new
|
||||
profile using that version and play the game!
|
||||
profile using that version and play the game!
|
||||
|
||||
For support and questions, visit [the Support Forum][Support-Forum] or [the Forge Discord server][Discord].
|
||||
|
||||
## Required Java Versions
|
||||
|
||||
We require the version of Java that Minecraft requires, which is currently:
|
||||
|
||||
| Minecraft | Java |
|
||||
|---------------|-------------------|
|
||||
| 1.20.6+ | [21][Adoptium-21] |
|
||||
| 1.18 - 1.20.4 | [17][Adoptium-17] |
|
||||
| 1.17.1 | [16][Adoptium-16] |
|
||||
| 1.16.5- | [8][Adoptium-8] |
|
||||
|
||||
**If you are on Windows and have difficulty using Java to launch JAR files, consider using [Jarfix by Johann Löfflmann][Jarfix].**
|
||||
|
||||
# Creating Mods
|
||||
|
||||
[See the "Getting Started" section in the Forge Documentation][Getting-Started].
|
||||
@@ -75,21 +63,14 @@ Please ensure you have a valid email address associated with your GitHub account
|
||||
|
||||
[Download]: https://files.minecraftforge.net/
|
||||
|
||||
[Adoptium-21]: https://adoptium.net/temurin/releases/?version=21
|
||||
[Adoptium-17]: https://adoptium.net/temurin/releases/?version=17
|
||||
[Adoptium-16]: https://adoptium.net/temurin/releases/?version=16
|
||||
[Adoptium-8]: https://adoptium.net/temurin/releases/?version=8
|
||||
[Jarfix]: https://johann.loefflmann.net/en/software/jarfix/index.html
|
||||
|
||||
[Forum]: https://forums.minecraftforge.net/
|
||||
[Support-Forum]: https://forums.minecraftforge.net/forum/18-support-bug-reports/
|
||||
[Support-Policy]: https://forums.minecraftforge.net/topic/144690-new-tiered-support-policy
|
||||
|
||||
[Discord]: https://discord.minecraftforge.net/
|
||||
|
||||
[Documentation]: https://docs.minecraftforge.net/en/1.20.1/
|
||||
[Getting-Started]: https://docs.minecraftforge.net/en/1.20.1/gettingstarted/
|
||||
[ForgeDev]: https://docs.minecraftforge.net/en/1.20.1/forgedev/
|
||||
[Pull-Requests]: https://docs.minecraftforge.net/en/1.20.1/forgedev/#making-changes-and-pull-requests
|
||||
[Documentation]: https://docs.minecraftforge.net/
|
||||
[Getting-Started]: https://docs.minecraftforge.net/en/latest/gettingstarted/
|
||||
[ForgeDev]: https://docs.minecraftforge.net/en/latest/forgedev/
|
||||
[Pull-Requests]: https://docs.minecraftforge.net/en/latest/forgedev/#making-changes-and-pull-requests
|
||||
|
||||
[Patreon]: https://www.patreon.com/LexManos
|
||||
@@ -2,20 +2,19 @@ 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'
|
||||
}
|
||||
apply plugin: 'java-library'
|
||||
apply plugin: 'jacoco'
|
||||
apply plugin: 'org.javamodularity.moduleplugin'
|
||||
|
||||
dependencyUpdates.rejectVersionIf { isNonStable(it.candidate.version) }
|
||||
|
||||
dependencies {
|
||||
compileOnly("org.jetbrains:annotations:${JETBRAINS_ANNOTATIONS_VERSION}")
|
||||
compileOnly(libs.nulls)
|
||||
|
||||
api("net.minecraftforge:eventbus:${EVENTBUS_VERSION}")
|
||||
api(libs.eventbus)
|
||||
|
||||
implementation(project(':fmlloader'))
|
||||
implementation("commons-io:commons-io:${COMMONS_IO_VERSION}")
|
||||
implementation(libs.commons.io)
|
||||
}
|
||||
|
||||
task sourcesJar(type: Jar) {
|
||||
|
||||
@@ -5,39 +5,36 @@
|
||||
|
||||
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.ServiceConfigurationError;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Used to allow access to certain things from the game layer
|
||||
*/
|
||||
public class Bindings {
|
||||
private Bindings() {}
|
||||
private static final Bindings INSTANCE = new Bindings();
|
||||
|
||||
/**
|
||||
* The provider of the bindings
|
||||
* @implNote May also throw a {@link ServiceConfigurationError}
|
||||
*/
|
||||
private static final IBindingsProvider PROVIDER = ServiceLoader.load(FMLLoader.getGameLayer(), IBindingsProvider.class)
|
||||
.findFirst().orElseThrow(() -> new IllegalStateException("Could not find bindings provider"));
|
||||
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");
|
||||
}
|
||||
this.provider = providers.get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A supplier of net.minecraftforge.common.MinecraftForge#EVENT_BUS
|
||||
*/
|
||||
public static Supplier<IEventBus> getForgeBus() {
|
||||
return PROVIDER.getForgeBusSupplier();
|
||||
return INSTANCE.provider.getForgeBusSupplier();
|
||||
}
|
||||
|
||||
public static Supplier<I18NParser> getMessageParser() {
|
||||
return PROVIDER.getMessageParser();
|
||||
return INSTANCE.provider.getMessageParser();
|
||||
}
|
||||
|
||||
public static Supplier<IConfigEvent.ConfigConfig> getConfigConfiguration() {
|
||||
return PROVIDER.getConfigConfiguration();
|
||||
return INSTANCE.provider.getConfigConfiguration();
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@ import java.util.function.Supplier;
|
||||
* @param <T> the type of the record which is held by the extension point
|
||||
*/
|
||||
@SuppressWarnings("unused") // Type parameter T
|
||||
// TODO: Complete re-write, this hasnt actually be used in a generic way by mods and we could make it a lot simpler.
|
||||
public interface IExtensionPoint<T extends Record>
|
||||
{
|
||||
/**
|
||||
@@ -40,7 +41,7 @@ public interface IExtensionPoint<T extends Record>
|
||||
* where {@code true} means it is from the server and {@code false} means it is from the world save. The return
|
||||
* value of the predicate determines whether the remote version is "compatible" for the purposes of the display test.</p>
|
||||
*
|
||||
* <p>The local compatibility version may be of the value {@link net.minecraftforge.network.NetworkConstants#IGNORESERVERONLY},
|
||||
* <p>The local compatibility version may be of the value {@link DisplayTest#IGNORESERVERONLY},
|
||||
* in which case clients will ignore the mod's presence if it is present on the server but not on the client.
|
||||
* However, the remote version test predicate must still accept this value as a remote version in order to display
|
||||
* as compatible if the mod is present on the client.</p>
|
||||
@@ -94,25 +95,6 @@ public interface IExtensionPoint<T extends Record>
|
||||
*/
|
||||
@SuppressWarnings("JavadocReference") // reference to NetworkConstants, ForgeHooksClient
|
||||
record DisplayTest(Supplier<String> suppliedVersion, BiPredicate<String, Boolean> remoteVersionTest) implements IExtensionPoint<DisplayTest> {
|
||||
public static final String IGNORESERVERONLY = "OHNOES\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31\uD83D\uDE31";
|
||||
|
||||
/**
|
||||
* Ignores any version information coming from the server - use for server only mods
|
||||
*/
|
||||
public static final Supplier<DisplayTest> IGNORE_SERVER_VERSION = () -> new DisplayTest(IGNORESERVERONLY, (remoteVersion, isFromServer) -> true);
|
||||
|
||||
/**
|
||||
* Ignores all information and provides no information
|
||||
*/
|
||||
public static final Supplier<DisplayTest> IGNORE_ALL_VERSION = () -> new DisplayTest("", (remoteVersion, isFromServer) -> true);
|
||||
|
||||
/**
|
||||
* An optional alternative to {@link #DisplayTest(Supplier, BiPredicate)} which accepts a constant version string
|
||||
* instead of a {@link Supplier}.
|
||||
* <p>Internally, the provided version string is wrapped in a Supplier for you.</p>
|
||||
*/
|
||||
public DisplayTest(String version, BiPredicate<String, Boolean> remoteVersionTest) {
|
||||
this(() -> version, remoteVersionTest);
|
||||
}
|
||||
public static final String IGNORESERVERONLY = "SERVER_ONLY";
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,6 @@ import net.minecraftforge.eventbus.api.Event;
|
||||
import net.minecraftforge.fml.config.IConfigEvent;
|
||||
import net.minecraftforge.fml.config.ModConfig;
|
||||
import net.minecraftforge.fml.event.IModBusEvent;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo;
|
||||
import net.minecraftforge.fml.loading.progress.ProgressMeter;
|
||||
import net.minecraftforge.forgespi.language.IModInfo;
|
||||
|
||||
@@ -22,7 +21,6 @@ import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
@@ -60,16 +58,15 @@ public abstract class ModContainer
|
||||
this.modInfo = info;
|
||||
this.modLoadingStage = ModLoadingStage.CONSTRUCT;
|
||||
|
||||
final String displayTestString = info.getConfig().<String>getConfigElement("displayTest")
|
||||
.orElseGet(() -> (Boolean) info.getOwningFile().getFileProperties().getOrDefault(ModFileInfo.CLIENT_SIDE_ONLY_PROP, Boolean.FALSE) ? "IGNORE_ALL_VERSION" : "MATCH_VERSION");
|
||||
final String displayTestString = info.getConfig().<String>getConfigElement("displayTest").orElse("MATCH_VERSION"); // missing defaults to DEFAULT type
|
||||
Supplier<IExtensionPoint.DisplayTest> displayTestSupplier = switch (displayTestString) {
|
||||
case "MATCH_VERSION" -> // default displaytest checks for version string match
|
||||
() -> new IExtensionPoint.DisplayTest(() -> this.modInfo.getVersion().toString(),
|
||||
(incoming, isNetwork) -> Objects.equals(incoming, this.modInfo.getVersion().toString()));
|
||||
case "IGNORE_SERVER_VERSION" -> // Ignores any version information coming from the server - use for server only mods
|
||||
IExtensionPoint.DisplayTest.IGNORE_SERVER_VERSION;
|
||||
() -> new IExtensionPoint.DisplayTest(() -> IExtensionPoint.DisplayTest.IGNORESERVERONLY, (incoming, isNetwork) -> true);
|
||||
case "IGNORE_ALL_VERSION" -> // Ignores all information and provides no information
|
||||
IExtensionPoint.DisplayTest.IGNORE_ALL_VERSION;
|
||||
() -> new IExtensionPoint.DisplayTest(() -> "", (incoming, isNetwork) -> true);
|
||||
case "NONE" -> null; // NO display test at all - use this if you're going to do your own display test
|
||||
default -> // any other value throws an exception
|
||||
throw new IllegalArgumentException("Invalid displayTest value supplied in mods.toml");
|
||||
@@ -148,22 +145,6 @@ public abstract class ModContainer
|
||||
extensionPoints.put(point, extension);
|
||||
}
|
||||
|
||||
public void registerDisplayTest(IExtensionPoint.DisplayTest displayTest) {
|
||||
registerExtensionPoint(IExtensionPoint.DisplayTest.class, () -> displayTest);
|
||||
}
|
||||
|
||||
public void registerDisplayTest(Supplier<IExtensionPoint.DisplayTest> displayTest) {
|
||||
registerExtensionPoint(IExtensionPoint.DisplayTest.class, displayTest);
|
||||
}
|
||||
|
||||
public void registerDisplayTest(String version, BiPredicate<String, Boolean> remoteVersionTest) {
|
||||
registerDisplayTest(new IExtensionPoint.DisplayTest(version, remoteVersionTest));
|
||||
}
|
||||
|
||||
public void registerDisplayTest(Supplier<String> suppliedVersion, BiPredicate<String, Boolean> remoteVersionTest) {
|
||||
registerDisplayTest(new IExtensionPoint.DisplayTest(suppliedVersion, remoteVersionTest));
|
||||
}
|
||||
|
||||
public void addConfig(final ModConfig modConfig) {
|
||||
configs.put(modConfig.getType(), modConfig);
|
||||
}
|
||||
|
||||
@@ -8,54 +8,38 @@ package net.minecraftforge.fml;
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraftforge.fml.config.IConfigSpec;
|
||||
import net.minecraftforge.fml.config.ModConfig;
|
||||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ModLoadingContext
|
||||
{
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private static final ThreadLocal<ModLoadingContext> context = ThreadLocal.withInitial(ModLoadingContext::new);
|
||||
private ModContainer activeContainer;
|
||||
private Object languageExtension;
|
||||
private ModLoadingStage stage;
|
||||
|
||||
/**
|
||||
* @deprecated Use the context provided by your language loader in your mod's constructor.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.21.1")
|
||||
public static ModLoadingContext get() {
|
||||
return context.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Going to be moved to ForgeHooks for internal use.
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.21.1")
|
||||
private ModContainer activeContainer;
|
||||
|
||||
public void setActiveContainer(final ModContainer container) {
|
||||
this.activeContainer = container;
|
||||
this.languageExtension = container == null ? null : container.contextExtension.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Going to be moved to ForgeHooks for internal use.
|
||||
* @deprecated Override/Use {@link ModLoadingContext#getContainer()}
|
||||
*/
|
||||
@Deprecated(forRemoval = true, since = "1.21.1")
|
||||
public ModContainer getActiveContainer() {
|
||||
return activeContainer == null ? ModList.get().getModContainerById("minecraft").orElseThrow(()->new RuntimeException("Where is minecraft???!")) : activeContainer;
|
||||
}
|
||||
|
||||
@Deprecated(forRemoval = true, since = "1.21.1")
|
||||
public String getActiveNamespace() {
|
||||
return activeContainer == null ? "minecraft" : activeContainer.getNamespace();
|
||||
}
|
||||
|
||||
public ModContainer getContainer() {
|
||||
return getActiveContainer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register an {@link IExtensionPoint} with the mod container.
|
||||
* @param point The extension point to register
|
||||
@@ -63,67 +47,29 @@ public class ModLoadingContext
|
||||
* @param <T> The type signature of the extension operator
|
||||
*/
|
||||
public <T extends Record & IExtensionPoint<T>> void registerExtensionPoint(Class<? extends IExtensionPoint<T>> point, Supplier<T> extension) {
|
||||
getContainer().registerExtensionPoint(point, extension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a {@link IExtensionPoint.DisplayTest} with the mod container.
|
||||
* <p>A shorthand for registering a DisplayTest with {@link #registerExtensionPoint(Class, Supplier)}.</p>
|
||||
* @param displayTest The {@link IExtensionPoint.DisplayTest} to register
|
||||
*/
|
||||
public void registerDisplayTest(IExtensionPoint.DisplayTest displayTest) {
|
||||
getContainer().registerDisplayTest(() -> displayTest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a {@link IExtensionPoint.DisplayTest} with the mod container.
|
||||
* <p>A shorthand for registering a DisplayTest supplier with {@link #registerExtensionPoint(Class, Supplier)}.</p>
|
||||
* @param displayTest The {@link Supplier<IExtensionPoint.DisplayTest>} to register
|
||||
*/
|
||||
public void registerDisplayTest(Supplier<IExtensionPoint.DisplayTest> displayTest) {
|
||||
getContainer().registerDisplayTest(displayTest);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a {@link IExtensionPoint.DisplayTest} with the mod container.
|
||||
* <p>A shorthand for registering a DisplayTest with {@link #registerExtensionPoint(Class, Supplier)} that also
|
||||
* creates the DisplayTest instance for you using the provided parameters.</p>
|
||||
* @see IExtensionPoint.DisplayTest#DisplayTest(String, BiPredicate)
|
||||
*/
|
||||
public void registerDisplayTest(String version, BiPredicate<String, Boolean> remoteVersionTest) {
|
||||
getContainer().registerDisplayTest(new IExtensionPoint.DisplayTest(version, remoteVersionTest));
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a {@link IExtensionPoint.DisplayTest} with the mod container.
|
||||
* <p>A shorthand for registering a DisplayTest with {@link #registerExtensionPoint(Class, Supplier)} that also
|
||||
* creates the DisplayTest instance for you using the provided parameters.</p>
|
||||
* @see IExtensionPoint.DisplayTest#DisplayTest(Supplier, BiPredicate)
|
||||
*/
|
||||
public void registerDisplayTest(Supplier<String> suppliedVersion, BiPredicate<String, Boolean> remoteVersionTest) {
|
||||
getContainer().registerDisplayTest(new IExtensionPoint.DisplayTest(suppliedVersion, remoteVersionTest));
|
||||
getActiveContainer().registerExtensionPoint(point, extension);
|
||||
}
|
||||
|
||||
public void registerConfig(ModConfig.Type type, IConfigSpec<?> spec) {
|
||||
if (spec.isEmpty())
|
||||
{
|
||||
// This handles the case where a mod tries to register a config, without any options configured inside it.
|
||||
LOGGER.debug("Attempted to register an empty config for type {} on mod {}", type, getContainer().getModId());
|
||||
LOGGER.debug("Attempted to register an empty config for type {} on mod {}", type, getActiveContainer().getModId());
|
||||
return;
|
||||
}
|
||||
|
||||
getContainer().addConfig(new ModConfig(type, spec, getContainer()));
|
||||
getActiveContainer().addConfig(new ModConfig(type, spec, getActiveContainer()));
|
||||
}
|
||||
|
||||
public void registerConfig(ModConfig.Type type, IConfigSpec<?> spec, String fileName) {
|
||||
if (spec.isEmpty())
|
||||
{
|
||||
// This handles the case where a mod tries to register a config, without any options configured inside it.
|
||||
LOGGER.debug("Attempted to register an empty config for type {} on mod {} using file name {}", type, getContainer().getModId(), fileName);
|
||||
LOGGER.debug("Attempted to register an empty config for type {} on mod {} using file name {}", type, getActiveContainer().getModId(), fileName);
|
||||
return;
|
||||
}
|
||||
|
||||
getContainer().addConfig(new ModConfig(type, spec, getContainer(), fileName));
|
||||
getActiveContainer().addConfig(new ModConfig(type, spec, getActiveContainer(), fileName));
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -51,8 +51,8 @@ public class VersionChecker
|
||||
BETA(),
|
||||
BETA_OUTDATED(6, true);
|
||||
|
||||
private final int sheetOffset;
|
||||
private final boolean draw, animated;
|
||||
final int sheetOffset;
|
||||
final boolean draw, animated;
|
||||
|
||||
Status()
|
||||
{
|
||||
@@ -91,9 +91,6 @@ public class VersionChecker
|
||||
return animated;
|
||||
}
|
||||
|
||||
public boolean isOutdated() {
|
||||
return this == OUTDATED || this == BETA_OUTDATED;
|
||||
}
|
||||
}
|
||||
|
||||
public record CheckResult(VersionChecker.Status status, ComparableVersion target, Map<ComparableVersion, String> changes, String url) {}
|
||||
@@ -260,7 +257,7 @@ public class VersionChecker
|
||||
LOGGER.warn("Failed to process update information", e);
|
||||
status = FAILED;
|
||||
}
|
||||
RESULTS.put(mod, new CheckResult(status, target, changes, display_url));
|
||||
results.put(mod, new CheckResult(status, target, changes, display_url));
|
||||
}
|
||||
}.start();
|
||||
}
|
||||
@@ -276,12 +273,12 @@ public class VersionChecker
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static final Map<IModInfo, CheckResult> RESULTS = new ConcurrentHashMap<>();
|
||||
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)
|
||||
{
|
||||
return RESULTS.getOrDefault(mod, PENDING_CHECK);
|
||||
return results.getOrDefault(mod, PENDING_CHECK);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,14 +1,11 @@
|
||||
plugins {
|
||||
id 'com.github.ben-manes.versions'
|
||||
id 'org.javamodularity.moduleplugin' version '1.8.7' apply false
|
||||
id 'org.cadixdev.licenser'
|
||||
id 'java-library'
|
||||
id 'jacoco'
|
||||
}
|
||||
apply plugin: 'java-library'
|
||||
apply plugin: 'jacoco'
|
||||
apply plugin: 'org.javamodularity.moduleplugin'
|
||||
|
||||
import org.gradle.internal.os.OperatingSystem
|
||||
|
||||
switch (OperatingSystem.current()) {
|
||||
case OperatingSystem.LINUX:
|
||||
project.ext.lwjglNatives = "natives-linux"
|
||||
@@ -21,22 +18,15 @@ switch (OperatingSystem.current()) {
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
dependencyUpdates.rejectVersionIf { isNonStable(it.candidate.version) }
|
||||
|
||||
java.withSourcesJar()
|
||||
|
||||
dependencies {
|
||||
compileOnly('org.jetbrains:annotations:23.0.0')
|
||||
compileOnly(libs.nulls)
|
||||
implementation(project(':fmlloader'))
|
||||
implementation(project(':fmlcore'))
|
||||
implementation('org.lwjgl:lwjgl:3.3.1')
|
||||
implementation('org.lwjgl:lwjgl-glfw:3.3.1')
|
||||
implementation('org.lwjgl:lwjgl-opengl:3.3.1')
|
||||
implementation('org.lwjgl:lwjgl-stb:3.3.1')
|
||||
implementation('org.lwjgl:lwjgl-tinyfd:3.3.1')
|
||||
implementation(libs.bundles.lwjgl)
|
||||
implementation('org.slf4j:slf4j-api:1.8.0-beta4')
|
||||
implementation("net.sf.jopt-simple:jopt-simple:${JOPT_SIMPLE_VERSION}")
|
||||
implementation(libs.jopt.simple)
|
||||
testImplementation('org.junit.jupiter:junit-jupiter-api:5.8.2')
|
||||
testImplementation('org.powermock:powermock-core:2.0.9')
|
||||
testRuntimeOnly('org.junit.jupiter:junit-jupiter-engine:5.8.2')
|
||||
@@ -51,31 +41,16 @@ test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
|
||||
ext {
|
||||
MANIFESTS = [
|
||||
'': [
|
||||
'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/earlydisplay/': [
|
||||
jar {
|
||||
manifest {
|
||||
attributes([
|
||||
'Specification-Title': 'FMLEarlyDisplay',
|
||||
'Specification-Vendor': 'Forge Development LLC',
|
||||
'Specification-Version': '1',
|
||||
'Implementation-Title': 'FML Early Display',
|
||||
'Implementation-Version': '1.0',
|
||||
'Implementation-Vendor': 'Forge'
|
||||
] as LinkedHashMap
|
||||
]
|
||||
}
|
||||
|
||||
jar.doFirst {
|
||||
MANIFESTS.each { pkg, values ->
|
||||
if (pkg == '')
|
||||
manifest.attributes(values)
|
||||
else
|
||||
manifest.attributes(values, pkg)
|
||||
] as LinkedHashMap, 'net/minecraftforge/fml/earlydisplay/')
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+39
-55
@@ -14,7 +14,9 @@ import net.minecraftforge.fml.loading.ImmediateWindowProvider;
|
||||
import net.minecraftforge.fml.loading.progress.StartupNotificationManager;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.lwjgl.PointerBuffer;
|
||||
import org.lwjgl.glfw.GLFWImage;
|
||||
import org.lwjgl.glfw.GLFWVidMode;
|
||||
import org.lwjgl.stb.STBImage;
|
||||
import org.lwjgl.system.MemoryStack;
|
||||
import org.lwjgl.system.MemoryUtil;
|
||||
import org.lwjgl.util.tinyfd.TinyFileDialogs;
|
||||
@@ -28,8 +30,9 @@ import java.io.InputStreamReader;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
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.Calendar;
|
||||
@@ -47,6 +50,7 @@ import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.IntConsumer;
|
||||
import java.util.function.IntSupplier;
|
||||
import java.util.function.LongSupplier;
|
||||
@@ -76,7 +80,7 @@ public class DisplayWindow implements ImmediateWindowProvider {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger("EARLYDISPLAY");
|
||||
private final AtomicBoolean animationTimerTrigger = new AtomicBoolean(true);
|
||||
|
||||
private ColourScheme colourScheme = ColourScheme.RED;
|
||||
private ColourScheme colourScheme;
|
||||
private ElementShader elementShader;
|
||||
|
||||
private RenderElement.DisplayContext context;
|
||||
@@ -133,17 +137,13 @@ public class DisplayWindow implements ImmediateWindowProvider {
|
||||
this.colourScheme = ColourScheme.BLACK;
|
||||
} else {
|
||||
try {
|
||||
// check the options file for the colour scheme
|
||||
var optionLines = Files.readAllLines(FMLPaths.GAMEDIR.get().resolve(Path.of("options.txt")));
|
||||
var keyName = "darkMojangStudiosBackground:";
|
||||
for (String line : optionLines) {
|
||||
if (line.startsWith(keyName)) {
|
||||
this.colourScheme = line.startsWith("true", keyName.length()) ? ColourScheme.BLACK : ColourScheme.RED;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
this.colourScheme = ColourScheme.RED; // fallback to red colourScheme
|
||||
var optionLines = Files.readAllLines(FMLPaths.GAMEDIR.get().resolve(Paths.get("options.txt")));
|
||||
var options = optionLines.stream().map(l -> l.split(":")).filter(a -> a.length == 2).collect(Collectors.toMap(a -> a[0], a -> a[1]));
|
||||
var colourScheme = Boolean.parseBoolean(options.getOrDefault("darkMojangStudiosBackground", "false"));
|
||||
this.colourScheme = colourScheme ? ColourScheme.BLACK : ColourScheme.RED;
|
||||
} catch (IOException ioe) {
|
||||
// No options
|
||||
this.colourScheme = ColourScheme.RED; // default to red colourscheme
|
||||
}
|
||||
}
|
||||
this.maximized = parsed.has(maximizedopt) || FMLConfig.getBoolConfigValue(FMLConfig.ConfigValue.EARLY_WINDOW_MAXIMIZED);
|
||||
@@ -228,7 +228,7 @@ public class DisplayWindow implements ImmediateWindowProvider {
|
||||
LOGGER.error("Crash during font initialization", t);
|
||||
crashElegantly("An error occurred initializing a font for rendering. "+t.getMessage());
|
||||
}
|
||||
this.elements = new ArrayList<>(List.of(
|
||||
this.elements = new ArrayList<>(Arrays.asList(
|
||||
RenderElement.anvil(font),
|
||||
RenderElement.logMessageOverlay(font),
|
||||
RenderElement.forgeVersionOverlay(font, mcVersion+"-"+forgeVersion.split("-")[0]),
|
||||
@@ -362,15 +362,14 @@ public class DisplayWindow implements ImmediateWindowProvider {
|
||||
glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_NATIVE_CONTEXT_API);
|
||||
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
|
||||
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
|
||||
|
||||
String vanillaWindowTitle = "Minecraft* ";
|
||||
if (mcVersion != null) vanillaWindowTitle += mcVersion;
|
||||
|
||||
// this emulates what we would get without early progress window
|
||||
// as vanilla never sets these, so GLFW uses the first window title
|
||||
// set them explicitly to avoid it using "FML early loading progress" as the class
|
||||
glfwWindowHintString(GLFW_X11_CLASS_NAME, vanillaWindowTitle);
|
||||
glfwWindowHintString(GLFW_X11_INSTANCE_NAME, vanillaWindowTitle);
|
||||
if (mcVersion != null) {
|
||||
// this emulates what we would get without early progress window
|
||||
// as vanilla never sets these, so GLFW uses the first window title
|
||||
// set them explicitly to avoid it using "FML early loading progress" as the class
|
||||
String vanillaWindowTitle = "Minecraft* " + mcVersion;
|
||||
glfwWindowHintString(GLFW_X11_CLASS_NAME, vanillaWindowTitle);
|
||||
glfwWindowHintString(GLFW_X11_INSTANCE_NAME, vanillaWindowTitle);
|
||||
}
|
||||
|
||||
long primaryMonitor = glfwGetPrimaryMonitor();
|
||||
if (primaryMonitor == 0) {
|
||||
@@ -392,8 +391,7 @@ public class DisplayWindow implements ImmediateWindowProvider {
|
||||
}, 10, TimeUnit.SECONDS);
|
||||
int versidx = 0;
|
||||
var skipVersions = FMLConfig.<String>getListConfigValue(FMLConfig.ConfigValue.EARLY_WINDOW_SKIP_GL_VERSIONS);
|
||||
boolean showHelpLog = FMLConfig.getBoolConfigValue(FMLConfig.ConfigValue.EARLY_WINDOW_LOG_HELP_MSG);
|
||||
final String[] lastGLError = new String[GL_VERSIONS.length];
|
||||
final String[] lastGLError=new String[GL_VERSIONS.length];
|
||||
do {
|
||||
final var glVersionToTry = GL_VERSIONS[versidx][0] + "." + GL_VERSIONS[versidx][1];
|
||||
if (skipVersions.contains(glVersionToTry)) {
|
||||
@@ -402,23 +400,11 @@ public class DisplayWindow implements ImmediateWindowProvider {
|
||||
continue;
|
||||
}
|
||||
LOGGER.info("Trying GL version " + glVersionToTry);
|
||||
if (showHelpLog && versidx == 0) {
|
||||
LOGGER.info("""
|
||||
If this message is the only thing at the bottom of your log before a crash, you probably have a driver issue.
|
||||
|
||||
Possible solutions:
|
||||
A) Make sure Minecraft is set to prefer high performance graphics in the OS and/or driver control panel
|
||||
B) Check for driver updates on the graphics brand's website
|
||||
C) Try reinstalling your graphics drivers
|
||||
D) If still not working after trying all of the above, ask for further help on the Forge forums or Discord
|
||||
|
||||
You can safely ignore this message if the game starts up successfully.""");
|
||||
}
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, GL_VERSIONS[versidx][0]); // we try our versions one at a time
|
||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, GL_VERSIONS[versidx][1]);
|
||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
||||
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
|
||||
window = glfwCreateWindow(winWidth, winHeight, vanillaWindowTitle, 0L, 0L);
|
||||
window = glfwCreateWindow(winWidth, winHeight, "Minecraft: Forge Loading...", 0L, 0L);
|
||||
var erridx = versidx;
|
||||
handleLastGLFWError((error, description) -> lastGLError[erridx] = String.format("Trying %d.%d: GLFW error: [0x%X]%s", GL_VERSIONS[erridx][0], GL_VERSIONS[erridx][1], error, description));
|
||||
if (lastGLError[versidx] != null) {
|
||||
@@ -446,9 +432,6 @@ public class DisplayWindow implements ImmediateWindowProvider {
|
||||
this.glVersion = gotVersion;
|
||||
this.window = window;
|
||||
|
||||
if (showHelpLog)
|
||||
FMLConfig.updateConfig(FMLConfig.ConfigValue.EARLY_WINDOW_LOG_HELP_MSG, false);
|
||||
|
||||
int[] x = new int[1];
|
||||
int[] y = new int[1];
|
||||
glfwGetMonitorPos(primaryMonitor, x, y);
|
||||
@@ -466,19 +449,19 @@ public class DisplayWindow implements ImmediateWindowProvider {
|
||||
glfwSetWindowPos(window, (vidmode.width() - this.winWidth) / 2 + monitorX, (vidmode.height() - this.winHeight) / 2 + monitorY);
|
||||
|
||||
// Attempt setting the icon
|
||||
// int[] channels = new int[1];
|
||||
// try (var glfwImgBuffer = GLFWImage.create(MemoryUtil.getAllocator().malloc(GLFWImage.SIZEOF), 1)) {
|
||||
// final ByteBuffer imgBuffer;
|
||||
// try (GLFWImage glfwImages = GLFWImage.malloc()) {
|
||||
// imgBuffer = STBHelper.loadImageFromClasspath("forge_logo.png", 20000, x, y, channels);
|
||||
// glfwImgBuffer.put(glfwImages.set(x[0], y[0], imgBuffer));
|
||||
// glfwSetWindowIcon(window, glfwImgBuffer);
|
||||
// STBImage.stbi_image_free(imgBuffer);
|
||||
// }
|
||||
// } catch (NullPointerException e) {
|
||||
// System.err.println("Failed to load forge logo");
|
||||
// }
|
||||
// handleLastGLFWError((error, description) -> LOGGER.debug(String.format("Suppressing GLFW icon error: [0x%X]%s", error, description)));
|
||||
int[] channels = new int[1];
|
||||
try (var glfwImgBuffer = GLFWImage.create(MemoryUtil.getAllocator().malloc(GLFWImage.SIZEOF), 1)) {
|
||||
final ByteBuffer imgBuffer;
|
||||
try (GLFWImage glfwImages = GLFWImage.malloc()) {
|
||||
imgBuffer = STBHelper.loadImageFromClasspath("forge_logo.png", 20000, x, y, channels);
|
||||
glfwImgBuffer.put(glfwImages.set(x[0], y[0], imgBuffer));
|
||||
glfwSetWindowIcon(window, glfwImgBuffer);
|
||||
STBImage.stbi_image_free(imgBuffer);
|
||||
}
|
||||
} catch (NullPointerException e) {
|
||||
System.err.println("Failed to load forge logo");
|
||||
}
|
||||
handleLastGLFWError((error, description) -> LOGGER.debug(String.format("Suppressing GLFW icon error: [0x%X]%s", error, description)));
|
||||
|
||||
glfwSetFramebufferSizeCallback(window, this::fbResize);
|
||||
glfwSetWindowPosCallback(window, this::winMove);
|
||||
@@ -613,7 +596,8 @@ public class DisplayWindow implements ImmediateWindowProvider {
|
||||
var fm = layer.findModule("forge").orElseThrow();
|
||||
getClass().getModule().addReads(fm);
|
||||
var clz = FMLLoader.getGameLayer().findModule("forge").map(l->Class.forName(l, "net.minecraftforge.client.loading.ForgeLoadingOverlay")).orElseThrow();
|
||||
loadingOverlay = Arrays.stream(clz.getMethods()).filter(m-> Modifier.isStatic(m.getModifiers()) && m.getName().equals("newInstance")).findFirst().orElseThrow();
|
||||
var methods = Arrays.stream(clz.getMethods()).filter(m-> Modifier.isStatic(m.getModifiers())).collect(Collectors.toMap(Method::getName, Function.identity()));
|
||||
loadingOverlay = methods.get("newInstance");
|
||||
}
|
||||
|
||||
public int getFramebufferTextureId() {
|
||||
|
||||
+7
-16
@@ -6,44 +6,35 @@
|
||||
package net.minecraftforge.fml.earlydisplay;
|
||||
|
||||
import com.sun.management.OperatingSystemMXBean;
|
||||
import net.minecraftforge.fml.loading.FMLConfig;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.MemoryMXBean;
|
||||
import java.lang.management.MemoryUsage;
|
||||
|
||||
public class PerformanceInfo {
|
||||
private static final MemoryMXBean MEMORY_BEAN = ManagementFactory.getMemoryMXBean();
|
||||
|
||||
private final boolean showCPUUsage;
|
||||
private final OperatingSystemMXBean osBean;
|
||||
|
||||
private final MemoryMXBean memoryBean;
|
||||
float memory;
|
||||
private String text;
|
||||
|
||||
PerformanceInfo() {
|
||||
showCPUUsage = FMLConfig.getBoolConfigValue(FMLConfig.ConfigValue.EARLY_WINDOW_SHOW_CPU);
|
||||
osBean = showCPUUsage ? ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class) : null;
|
||||
osBean = ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class);
|
||||
memoryBean = ManagementFactory.getMemoryMXBean();
|
||||
}
|
||||
|
||||
void update() {
|
||||
final MemoryUsage heapusage = MEMORY_BEAN.getHeapMemoryUsage();
|
||||
final MemoryUsage heapusage = memoryBean.getHeapMemoryUsage();
|
||||
memory = (float) heapusage.getUsed() / heapusage.getMax();
|
||||
|
||||
if (!showCPUUsage) {
|
||||
text = "Memory: %d/%dMB (%.1f%%)".formatted(heapusage.getUsed() >>> 20, heapusage.getMax() >>> 20, memory * 100f);
|
||||
return;
|
||||
}
|
||||
|
||||
var cpuLoad = osBean.getProcessCpuLoad();
|
||||
String cpuText;
|
||||
if (cpuLoad == -1) {
|
||||
cpuText = "*CPU: %.1f%%".formatted(osBean.getCpuLoad() * 100f);
|
||||
cpuText = String.format("*CPU: %.1f%%", osBean.getCpuLoad() * 100f);
|
||||
} else {
|
||||
cpuText = "CPU: %.1f%%".formatted(cpuLoad * 100f);
|
||||
cpuText = String.format("CPU: %.1f%%", cpuLoad * 100f);
|
||||
}
|
||||
|
||||
text = "Memory: %d/%dMB (%.1f%%) %s".formatted(heapusage.getUsed() >>> 20, heapusage.getMax() >>> 20, memory * 100f, cpuText);
|
||||
text = String.format("Heap: %d/%d MB (%.1f%%) OffHeap: %d MB %s", heapusage.getUsed() >> 20, heapusage.getMax() >> 20, memory * 100.0, memoryBean.getNonHeapMemoryUsage().getUsed() >> 20, cpuText);
|
||||
}
|
||||
|
||||
String text() {
|
||||
|
||||
+2
-8
@@ -34,14 +34,11 @@ public class RenderElement {
|
||||
};
|
||||
}
|
||||
}
|
||||
@FunctionalInterface
|
||||
interface TextureRenderer {
|
||||
void accept(SimpleBufferBuilder bb, DisplayContext context, int[] size, int frame);
|
||||
}
|
||||
@FunctionalInterface
|
||||
interface Initializer extends Supplier<Renderer> {}
|
||||
|
||||
@FunctionalInterface
|
||||
interface TextGenerator {
|
||||
void accept(SimpleBufferBuilder bb, SimpleFont fh, DisplayContext ctx);
|
||||
}
|
||||
@@ -204,17 +201,14 @@ public class RenderElement {
|
||||
bar.then(label).accept(buffer, context, frameNumber);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface ColourFunction {
|
||||
int colour(int frame);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface ProgressDisplay {
|
||||
float[] progress(int frame);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
interface BarPosition {
|
||||
int[] location(DisplayContext context);
|
||||
}
|
||||
@@ -286,7 +280,7 @@ public class RenderElement {
|
||||
if (num < min) {
|
||||
return min;
|
||||
} else {
|
||||
return Math.min(num, max);
|
||||
return num > max ? max : num;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,7 +288,7 @@ public class RenderElement {
|
||||
if (num < min) {
|
||||
return min;
|
||||
} else {
|
||||
return Math.min(num, max);
|
||||
return num > max ? max : num;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+28
-38
@@ -2,47 +2,43 @@ 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'
|
||||
}
|
||||
apply plugin: 'java-library'
|
||||
apply plugin: 'jacoco'
|
||||
apply plugin: 'org.javamodularity.moduleplugin'
|
||||
|
||||
dependencyUpdates.rejectVersionIf { isNonStable(it.candidate.version) }
|
||||
configurations.forEach{ it.transitive = false }
|
||||
|
||||
dependencies {
|
||||
compileOnly("org.jetbrains:annotations:${JETBRAINS_ANNOTATIONS_VERSION}")
|
||||
compileOnly(libs.nulls)
|
||||
|
||||
api("org.ow2.asm:asm:${ASM_VERSION}")
|
||||
api("org.ow2.asm:asm-tree:${ASM_VERSION}")
|
||||
api("org.ow2.asm:asm-commons:${ASM_VERSION}")
|
||||
api("net.minecraftforge:forgespi:${SPI_VERSION}")
|
||||
api("net.minecraftforge:mergetool:${MERGETOOL_VERSION}:api")
|
||||
api("org.apache.logging.log4j:log4j-api:${LOG4J_VERSION}")
|
||||
api("org.slf4j:slf4j-api:${SLF4J_API_VERSION}")
|
||||
api("com.google.guava:guava:${GUAVA_VERSION}")
|
||||
api("com.google.code.gson:gson:${GSON_VERSION}")
|
||||
api("org.apache.maven:maven-artifact:${APACHE_MAVEN_ARTIFACT_VERSION}")
|
||||
api("org.apache.commons:commons-lang3:${APACHE_COMMONS_LANG3_VERSION}")
|
||||
api("com.electronwill.night-config:core:${NIGHTCONFIG_VERSION}")
|
||||
api("com.electronwill.night-config:toml:${NIGHTCONFIG_VERSION}")
|
||||
api("cpw.mods:modlauncher:${MODLAUNCHER_VERSION}")
|
||||
api("net.minecraftforge:coremods:${COREMODS_VERSION}")
|
||||
api("com.mojang:logging:${MOJANG_LOGGING_VERSION}")
|
||||
api "net.minecraftforge:JarJarSelector:${JARJAR_VERSION}"
|
||||
api "net.minecraftforge:JarJarMetadata:${JARJAR_VERSION}"
|
||||
api(libs.bundles.asm) // Needed by all the black magic
|
||||
api(libs.forgespi)
|
||||
api(libs.mergetool.api)
|
||||
api(libs.log4j.api)
|
||||
api(libs.slf4j.api)
|
||||
api(libs.guava)
|
||||
api(libs.gson)
|
||||
api(libs.maven.artifact)
|
||||
api(libs.apache.commons)
|
||||
api(libs.bundles.night.config)
|
||||
api(libs.modlauncher)
|
||||
api(libs.coremods)
|
||||
api(libs.mojang.logging)
|
||||
api(libs.jarjar.selector)
|
||||
api(libs.jarjar.meta)
|
||||
|
||||
implementation("net.sf.jopt-simple:jopt-simple:${JOPT_SIMPLE_VERSION}")
|
||||
implementation("cpw.mods:securejarhandler:${SECUREJARHANDLER_VERSION}")
|
||||
implementation("net.minecraftforge:accesstransformers:${ACCESSTRANSFORMERS_VERSION}")
|
||||
implementation("net.minecrell:terminalconsoleappender:${TERMINALCONSOLEAPPENDER_VERSION}")
|
||||
// implementation("org.jline:jline:${JLINE_VERSION}")
|
||||
implementation(libs.jopt.simple)
|
||||
implementation(libs.securemodules)
|
||||
implementation(libs.accesstransformers)
|
||||
implementation(libs.terminalconsoleappender)
|
||||
implementation(libs.jimfs)
|
||||
|
||||
implementation("org.apache.logging.log4j:log4j-core:${LOG4J_VERSION}")
|
||||
annotationProcessor("org.apache.logging.log4j:log4j-core:${LOG4J_VERSION}")
|
||||
implementation(libs.log4j.core)
|
||||
annotationProcessor(libs.log4j.core)
|
||||
|
||||
testCompileOnly("org.jetbrains:annotations:${JETBRAINS_ANNOTATIONS_VERSION}")
|
||||
testRuntimeOnly("cpw.mods:bootstraplauncher:${BOOTSTRAPLAUNCHER_VERSION}")
|
||||
testCompileOnly(libs.nulls)
|
||||
testRuntimeOnly(libs.bootstrap)
|
||||
}
|
||||
|
||||
task sourcesJar(type: Jar) {
|
||||
@@ -52,12 +48,6 @@ task sourcesJar(type: Jar) {
|
||||
|
||||
ext {
|
||||
MANIFESTS = [
|
||||
'/': [
|
||||
'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',
|
||||
@@ -77,7 +67,7 @@ jar.doFirst {
|
||||
}
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
options.compilerArgs << '-Xlint:-unchecked'
|
||||
options.compilerArgs << '-Xlint:unchecked'
|
||||
}
|
||||
|
||||
license {
|
||||
|
||||
+6
-13
@@ -36,9 +36,9 @@ import cpw.mods.modlauncher.serviceapi.ILaunchPluginService;
|
||||
*/
|
||||
public class CapabilityTokenSubclass implements ILaunchPluginService {
|
||||
|
||||
private static final String FUNC_NAME = "getType";
|
||||
private static final String FUNC_DESC = "()Ljava/lang/String;";
|
||||
private static final String CAP_INJECT = "net/minecraftforge/common/capabilities/CapabilityToken"; //Don't directly reference this to prevent class loading.
|
||||
private final String FUNC_NAME = "getType";
|
||||
private final String FUNC_DESC = "()Ljava/lang/String;";
|
||||
private final String CAP_INJECT = "net/minecraftforge/common/capabilities/CapabilityToken"; //Don't directly reference this to prevent class loading.
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
@@ -51,14 +51,7 @@ public class CapabilityTokenSubclass implements ILaunchPluginService {
|
||||
@Override
|
||||
public EnumSet<Phase> handlesClass(Type classType, boolean isEmpty)
|
||||
{
|
||||
if (isEmpty)
|
||||
return NAY;
|
||||
|
||||
String internalName = classType.getInternalName();
|
||||
if (internalName.startsWith("net/minecraft/") || internalName.startsWith("com/mojang/"))
|
||||
return NAY;
|
||||
|
||||
return YAY;
|
||||
return isEmpty ? NAY : YAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -82,7 +75,7 @@ public class CapabilityTokenSubclass implements ILaunchPluginService {
|
||||
SignatureReader reader = new SignatureReader(classNode.signature); // Having a node version of this would probably be useful.
|
||||
reader.accept(new SignatureVisitor(Opcodes.ASM9)
|
||||
{
|
||||
final Deque<String> stack = new ArrayDeque<>();
|
||||
Deque<String> stack = new ArrayDeque<>();
|
||||
|
||||
@Override
|
||||
public void visitClassType(final String name)
|
||||
@@ -120,7 +113,7 @@ public class CapabilityTokenSubclass implements ILaunchPluginService {
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Holder {
|
||||
private static class Holder {
|
||||
String value;
|
||||
}
|
||||
|
||||
|
||||
+10
-30
@@ -8,7 +8,6 @@ package net.minecraftforge.fml.common.asm;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -36,11 +35,7 @@ public class ObjectHolderDefinalize implements ILaunchPluginService {
|
||||
new VanillaObjectHolderData("net.minecraft.core.particles.ParticleTypes", "particle_type", "net.minecraft.core.particles.ParticleType"),
|
||||
new VanillaObjectHolderData("net.minecraft.sounds.SoundEvents", "sound_event", "net.minecraft.sounds.SoundEvent")
|
||||
).collect(Collectors.toMap(VanillaObjectHolderData::holderClass, Function.identity()));
|
||||
private static final Set<String> VANILLA_OBJECT_HOLDER_CLASSES = VANILLA_OBJECT_HOLDERS.keySet().stream()
|
||||
.map(it -> it.replace('.', '/'))
|
||||
.collect(Collectors.toUnmodifiableSet());
|
||||
private static final String OBJECT_HOLDER = "Lnet/minecraftforge/registries/ObjectHolder;"; //Don't directly reference this to prevent class loading.
|
||||
private static final int PUBLIC_STATIC_FINAL_FLAGS = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
|
||||
private final String OBJECT_HOLDER = "Lnet/minecraftforge/registries/ObjectHolder;"; //Don't directly reference this to prevent class loading.
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
@@ -51,33 +46,17 @@ public class ObjectHolderDefinalize implements ILaunchPluginService {
|
||||
private static final EnumSet<Phase> NAY = EnumSet.noneOf(Phase.class);
|
||||
|
||||
@Override
|
||||
public EnumSet<Phase> handlesClass(Type classType, boolean isEmpty) {
|
||||
if (isEmpty)
|
||||
return NAY;
|
||||
|
||||
// Let's skip processing classes that definitely don't have object holders
|
||||
String internalName = classType.getInternalName();
|
||||
|
||||
// Forge classes that aren't in the debug package (that package is used for tests)
|
||||
if (internalName.startsWith("net/minecraftforge/") && !internalName.substring(18).contains("debug")
|
||||
&& !internalName.equals("net/minecraftforge/common/crafting/ConditionalRecipe"))
|
||||
return NAY;
|
||||
|
||||
// Vanilla classes that don't have object holders
|
||||
if (internalName.startsWith("com/mojang/"))
|
||||
return NAY;
|
||||
|
||||
// ...except specific ones we added to the VANILLA_OBJECT_HOLDERS map
|
||||
if (internalName.startsWith("net/minecraft/") && !VANILLA_OBJECT_HOLDER_CLASSES.contains(internalName))
|
||||
return NAY;
|
||||
|
||||
return YAY;
|
||||
public EnumSet<Phase> handlesClass(Type classType, boolean isEmpty)
|
||||
{
|
||||
return isEmpty ? NAY : YAY;
|
||||
}
|
||||
|
||||
private boolean hasHolder(List<AnnotationNode> lst) {
|
||||
private boolean hasHolder(List<AnnotationNode> lst)
|
||||
{
|
||||
return lst != null && lst.stream().anyMatch(n -> n.desc.equals(OBJECT_HOLDER));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
private String getValue(List<AnnotationNode> lst)
|
||||
{
|
||||
AnnotationNode ann = lst.stream().filter(n -> n.desc.equals(OBJECT_HOLDER)).findFirst().get();
|
||||
@@ -97,9 +76,10 @@ public class ObjectHolderDefinalize implements ILaunchPluginService {
|
||||
{
|
||||
final AtomicBoolean changes = new AtomicBoolean();
|
||||
//Must be public static finals, and non-array objects
|
||||
final int flags = Opcodes.ACC_PUBLIC | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL;
|
||||
|
||||
//Fix Annotated Fields before injecting from class level
|
||||
classNode.fields.stream().filter(f -> ((f.access & PUBLIC_STATIC_FINAL_FLAGS) == PUBLIC_STATIC_FINAL_FLAGS) && f.desc.startsWith("L") && hasHolder(f.visibleAnnotations)).forEach(f ->
|
||||
classNode.fields.stream().filter(f -> ((f.access & flags) == flags) && f.desc.startsWith("L") && hasHolder(f.visibleAnnotations)).forEach(f ->
|
||||
{
|
||||
int prev = f.access;
|
||||
f.access &= ~Opcodes.ACC_FINAL; //Strip final
|
||||
@@ -109,7 +89,7 @@ public class ObjectHolderDefinalize implements ILaunchPluginService {
|
||||
|
||||
if (VANILLA_OBJECT_HOLDERS.containsKey(classType.getClassName())) //Class level, de-finalize all fields and add @ObjectHolder to them!
|
||||
{
|
||||
classNode.fields.stream().filter(f -> ((f.access & PUBLIC_STATIC_FINAL_FLAGS) == PUBLIC_STATIC_FINAL_FLAGS) && f.desc.startsWith("L")).forEach(f ->
|
||||
classNode.fields.stream().filter(f -> ((f.access & flags) == flags) && f.desc.startsWith("L")).forEach(f ->
|
||||
{
|
||||
int prev = f.access;
|
||||
f.access &= ~Opcodes.ACC_FINAL;
|
||||
|
||||
+23
-30
@@ -20,8 +20,6 @@ import org.objectweb.asm.tree.MethodNode;
|
||||
|
||||
import cpw.mods.modlauncher.serviceapi.ILaunchPluginService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.Marker;
|
||||
import org.slf4j.MarkerFactory;
|
||||
|
||||
/**
|
||||
* Modifies specified enums to allow runtime extension by making the $VALUES field non-final and
|
||||
@@ -30,16 +28,15 @@ import org.slf4j.MarkerFactory;
|
||||
public class RuntimeEnumExtender implements ILaunchPluginService {
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private static final Type STRING = Type.getType(String.class);
|
||||
private static final Type ENUM = Type.getType(Enum.class);
|
||||
private static final Type MARKER_IFACE = Type.getType("Lnet/minecraftforge/common/IExtensibleEnum;");
|
||||
private static final Type ARRAY_UTILS = Type.getType("Lorg/apache/commons/lang3/ArrayUtils;"); //Don't directly reference this to prevent class loading.
|
||||
private static final String ADD_DESC = Type.getMethodDescriptor(Type.getType(Object[].class), Type.getType(Object[].class), Type.getType(Object.class));
|
||||
private static final Type UNSAFE_HACKS = Type.getType("Lnet/minecraftforge/fml/unsafe/UnsafeHacks;"); //Again, not direct reference to prevent class loading.
|
||||
private static final String CLEAN_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Class.class));
|
||||
private static final String NAME_DESC = Type.getMethodDescriptor(STRING);
|
||||
private static final String EQUALS_DESC = Type.getMethodDescriptor(Type.BOOLEAN_TYPE, STRING);
|
||||
private static final int FLAGS = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC;
|
||||
private final Type STRING = Type.getType(String.class);
|
||||
private final Type ENUM = Type.getType(Enum.class);
|
||||
private final Type MARKER_IFACE = Type.getType("Lnet/minecraftforge/common/IExtensibleEnum;");
|
||||
private final Type ARRAY_UTILS = Type.getType("Lorg/apache/commons/lang3/ArrayUtils;"); //Don't directly reference this to prevent class loading.
|
||||
private final String ADD_DESC = Type.getMethodDescriptor(Type.getType(Object[].class), Type.getType(Object[].class), Type.getType(Object.class));
|
||||
private final Type UNSAFE_HACKS = Type.getType("Lnet/minecraftforge/fml/unsafe/UnsafeHacks;"); //Again, not direct reference to prevent class loading.
|
||||
private final String CLEAN_DESC = Type.getMethodDescriptor(Type.VOID_TYPE, Type.getType(Class.class));
|
||||
private final String NAME_DESC = Type.getMethodDescriptor(STRING);
|
||||
private final String EQUALS_DESC = Type.getMethodDescriptor(Type.BOOLEAN_TYPE, STRING);
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
@@ -50,15 +47,9 @@ public class RuntimeEnumExtender implements ILaunchPluginService {
|
||||
private static final EnumSet<Phase> NAY = EnumSet.noneOf(Phase.class);
|
||||
|
||||
@Override
|
||||
public EnumSet<Phase> handlesClass(Type classType, boolean isEmpty) {
|
||||
if (isEmpty)
|
||||
return NAY;
|
||||
|
||||
String internalName = classType.getInternalName();
|
||||
if (internalName.startsWith("net/minecraftforge/") || internalName.startsWith("com/mojang/"))
|
||||
return NAY;
|
||||
|
||||
return YAY;
|
||||
public EnumSet<Phase> handlesClass(Type classType, boolean isEmpty)
|
||||
{
|
||||
return isEmpty ? NAY : YAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -67,24 +58,25 @@ public class RuntimeEnumExtender implements ILaunchPluginService {
|
||||
if ((classNode.access & Opcodes.ACC_ENUM) == 0)
|
||||
return ComputeFlags.NO_REWRITE;
|
||||
|
||||
if (!classNode.interfaces.contains(MARKER_IFACE.getInternalName()))
|
||||
return ComputeFlags.NO_REWRITE;
|
||||
|
||||
Type array = Type.getType("[" + classType.getDescriptor());
|
||||
String arrayDesc = array.getDescriptor();
|
||||
final int flags = Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC;
|
||||
|
||||
FieldNode values = classNode.fields.stream().filter(f -> f.desc.equals(arrayDesc) && ((f.access & FLAGS) == FLAGS)).findFirst().orElse(null);
|
||||
FieldNode values = classNode.fields.stream().filter(f -> f.desc.contentEquals(array.getDescriptor()) && ((f.access & flags) == flags)).findFirst().orElse(null);
|
||||
|
||||
if (!classNode.interfaces.contains(MARKER_IFACE.getInternalName())) {
|
||||
return ComputeFlags.NO_REWRITE;
|
||||
}
|
||||
|
||||
//Static methods named "create" with first argument as a string
|
||||
List<MethodNode> candidates = classNode.methods.stream()
|
||||
.filter(m -> ((m.access & Opcodes.ACC_STATIC) != 0) && m.name.equals("create"))
|
||||
.toList();
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (candidates.isEmpty()) {
|
||||
throw new IllegalStateException("IExtensibleEnum has no candidate factory methods: " + classType.getClassName());
|
||||
}
|
||||
|
||||
for (var mtd : candidates)
|
||||
candidates.forEach(mtd ->
|
||||
{
|
||||
Type[] args = Type.getArgumentTypes(mtd.desc);
|
||||
if (args.length == 0 || !args[0].equals(STRING)) {
|
||||
@@ -116,7 +108,8 @@ public class RuntimeEnumExtender implements ILaunchPluginService {
|
||||
Type[] ctrArgs = new Type[args.length + 1];
|
||||
ctrArgs[0] = STRING;
|
||||
ctrArgs[1] = Type.INT_TYPE;
|
||||
System.arraycopy(args, 1, ctrArgs, 2, args.length - 1);
|
||||
for (int x = 1; x < args.length; x++)
|
||||
ctrArgs[1 + x] = args[x];
|
||||
|
||||
String desc = Type.getMethodDescriptor(Type.VOID_TYPE, ctrArgs);
|
||||
|
||||
@@ -238,7 +231,7 @@ public class RuntimeEnumExtender implements ILaunchPluginService {
|
||||
ins.load(vars, classType);
|
||||
ins.areturn(classType);
|
||||
}
|
||||
}
|
||||
});
|
||||
return ComputeFlags.COMPUTE_FRAMES;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,6 @@
|
||||
|
||||
package net.minecraftforge.fml.loading;
|
||||
|
||||
import net.minecraftforge.fml.loading.progress.StartupNotificationManager;
|
||||
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@@ -14,6 +14,8 @@ import java.util.List;
|
||||
* or server.
|
||||
*/
|
||||
public class EarlyLoadingException extends RuntimeException {
|
||||
private static final long serialVersionUID = 2302192216172508006L;
|
||||
|
||||
public static class ExceptionData {
|
||||
private final IModInfo modInfo;
|
||||
private final String i18message;
|
||||
|
||||
@@ -36,15 +36,12 @@ public class FMLConfig
|
||||
EARLY_WINDOW_FBSCALE("earlyWindowFBScale", 1, "Early window framebuffer scale"),
|
||||
EARLY_WINDOW_MAXIMIZED("earlyWindowMaximized", Boolean.FALSE, "Early window starts maximized"),
|
||||
EARLY_WINDOW_SKIP_GL_VERSIONS("earlyWindowSkipGLVersions", List.of(), "Skip specific GL versions, may help with buggy graphics card drivers"),
|
||||
EARLY_WINDOW_SQUIR("earlyWindowSquir", Boolean.FALSE, "Squir?"),
|
||||
EARLY_WINDOW_SHOW_CPU("earlyWindowShowCPU", Boolean.FALSE, "Whether to show CPU usage stats in early window"),
|
||||
EARLY_WINDOW_LOG_HELP_MSG("earlyWindowLogHelpMessage", Boolean.TRUE, "Whether to log a help message on first attempt, to aid troubleshooting. This setting should automatically disable itself after a successful launch"),
|
||||
EARLY_WINDOW_SQUIR("earlyWindowSquir", Boolean.FALSE, "Squir?")
|
||||
;
|
||||
|
||||
private final String entry;
|
||||
private final Object defaultValue;
|
||||
private final String comment;
|
||||
private final Class<?> valueType;
|
||||
private final Function<Object, Object> entryFunction;
|
||||
|
||||
ConfigValue(final String entry, final Object defaultValue, final String comment) {
|
||||
@@ -54,7 +51,6 @@ public class FMLConfig
|
||||
this.entry = entry;
|
||||
this.defaultValue = defaultValue;
|
||||
this.comment = comment;
|
||||
this.valueType = defaultValue.getClass();
|
||||
this.entryFunction = entryFunction;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ import net.minecraftforge.fml.loading.moddiscovery.ModFile;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModValidator;
|
||||
import net.minecraftforge.accesstransformer.service.AccessTransformerService;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.fml.loading.progress.StartupNotificationManager;
|
||||
import net.minecraftforge.fml.loading.targets.CommonLaunchHandler;
|
||||
import net.minecraftforge.forgespi.Environment;
|
||||
import net.minecraftforge.forgespi.coremod.ICoreModProvider;
|
||||
@@ -65,9 +64,6 @@ public class FMLLoader
|
||||
throw new IncompatibleEnvironmentException("Incompatible modlauncher found "+modLauncherPackage.getSpecificationVersion());
|
||||
}
|
||||
|
||||
// Allows us to communicate properties with other services through ModLauncher
|
||||
setupBlackboardKeys();
|
||||
|
||||
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");
|
||||
@@ -129,12 +125,6 @@ public class FMLLoader
|
||||
}
|
||||
}
|
||||
|
||||
private static void setupBlackboardKeys() {
|
||||
LOGGER.debug(CORE, "Requesting CoreMods to not apply the fix for ASMAPI.findFirstInstructionBefore by default");
|
||||
var blackboardKey = TypesafeMap.Key.getOrCreate(Launcher.INSTANCE.blackboard(), "coremods.use_old_findFirstInstructionBefore", Boolean.class);
|
||||
Launcher.INSTANCE.blackboard().<Boolean>computeIfAbsent(blackboardKey, k -> true);
|
||||
}
|
||||
|
||||
static void setupLaunchHandler(final IEnvironment environment, final Map<String, Object> arguments)
|
||||
{
|
||||
final String launchTarget = environment.getProperty(IEnvironment.Keys.LAUNCHTARGET.get()).orElse("MISSING");
|
||||
@@ -146,6 +136,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
|
||||
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");
|
||||
|
||||
@@ -126,6 +126,7 @@ public class FMLServiceProvider implements ITransformationService
|
||||
targetMcpMappings = option.value(mappingsOption);
|
||||
}
|
||||
|
||||
@SuppressWarnings("rawtypes")
|
||||
@Override
|
||||
public @NotNull List<ITransformer> transformers()
|
||||
{
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class FileUtils
|
||||
{
|
||||
|
||||
public static String fileExtension(final Path path) {
|
||||
String fileName = path.getFileName().toString();
|
||||
int idx = fileName.lastIndexOf('.');
|
||||
if (idx > -1) {
|
||||
return fileName.substring(idx+1);
|
||||
} else {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean matchFileName(String path, String... matches) {
|
||||
// Extract file name from path
|
||||
String name = path.substring(Math.min(path.lastIndexOf(File.separatorChar) + 1, path.length()));
|
||||
// Check if it contains any of the desired keywords
|
||||
for (String match : matches) {
|
||||
if (name.contains(match)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@ public class ImmediateWindowHandler {
|
||||
|
||||
private static ProgressMeter earlyProgress;
|
||||
public static void load(final String launchTarget, final String[] arguments) {
|
||||
if (!List.of("forgeclient", "forgeclientuserdev", "forgeclientdev").contains(launchTarget)) {
|
||||
if (!launchTarget.contains("client")) {
|
||||
provider = new DummyProvider();
|
||||
LOGGER.info("ImmediateWindowProvider not loading because launch target is {}", launchTarget);
|
||||
} else if (!FMLConfig.getBoolConfigValue(FMLConfig.ConfigValue.EARLY_WINDOW_CONTROL)) {
|
||||
|
||||
@@ -11,6 +11,7 @@ import java.util.Optional;
|
||||
* Finds Version data from a package, with possible default values
|
||||
*/
|
||||
public class JarVersionLookupHandler {
|
||||
|
||||
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();
|
||||
|
||||
@@ -9,7 +9,6 @@ import com.mojang.logging.LogUtils;
|
||||
import cpw.mods.modlauncher.Launcher;
|
||||
import cpw.mods.modlauncher.api.IModuleLayerManager;
|
||||
import cpw.mods.modlauncher.util.ServiceLoaderUtils;
|
||||
import net.minecraftforge.fml.loading.progress.StartupNotificationManager;
|
||||
import net.minecraftforge.forgespi.language.IModLanguageProvider;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ExplodedDirectoryLocator;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
|
||||
|
||||
-3
@@ -7,7 +7,6 @@ package net.minecraftforge.fml.loading;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import cpw.mods.jarhandling.SecureJar;
|
||||
import cpw.mods.modlauncher.Launcher;
|
||||
import cpw.mods.modlauncher.api.LamdbaExceptionUtils;
|
||||
import cpw.mods.modlauncher.api.NamedPath;
|
||||
import cpw.mods.modlauncher.serviceapi.ITransformerDiscoveryService;
|
||||
@@ -19,8 +18,6 @@ import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.zip.ZipFile;
|
||||
import java.util.Set;
|
||||
|
||||
public class ModDirTransformerDiscoverer implements ITransformerDiscoveryService {
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import cpw.mods.modlauncher.api.LamdbaExceptionUtils;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLStreamHandler;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import static net.minecraftforge.fml.loading.LogMarkers.CORE;
|
||||
|
||||
public class ModJarURLHandler extends URLStreamHandler
|
||||
{
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
// modjar://modid/path/to/file
|
||||
@Override
|
||||
protected URLConnection openConnection(URL url) {
|
||||
return new ModJarURLConnection(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int hashCode(final URL u) {
|
||||
return Objects.hash(u.getHost(), u.getFile());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean equals(final URL u1, final URL u2) {
|
||||
return Objects.equals(u1.getProtocol(), u2.getProtocol())
|
||||
&& Objects.equals(u1.getHost(), u2.getHost())
|
||||
&& Objects.equals(u1.getFile(), u2.getFile());
|
||||
}
|
||||
|
||||
static class ModJarURLConnection extends URLConnection {
|
||||
private Path resource;
|
||||
private String modpath;
|
||||
private String modid;
|
||||
private Optional<Manifest> manifest;
|
||||
|
||||
public ModJarURLConnection(final URL url) {
|
||||
super(url);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void connect()
|
||||
{
|
||||
// if (resource == null) {
|
||||
// modid = url.getHost();
|
||||
// // trim first char
|
||||
// modpath = url.getPath().substring(1);
|
||||
// resource = FMLLoader.getLoadingModList().getModFileById(modid).getFile().findResource(modpath);
|
||||
// manifest = FMLLoader.getLoadingModList().getModFileById(modid).getManifest();
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException
|
||||
{
|
||||
connect();
|
||||
LOGGER.trace(CORE, "Loading modjar URL {} got resource {} {}", url, resource, resource != null ? Files.exists(resource) : "missing");
|
||||
return Files.newInputStream(resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getContentLengthLong() {
|
||||
try {
|
||||
connect();
|
||||
return Files.size(resource);
|
||||
} catch (IOException e) {
|
||||
return -1L;
|
||||
}
|
||||
}
|
||||
|
||||
// Used to cache protectiondomains by "top level object" aka the modid
|
||||
@Override
|
||||
public URL getURL() {
|
||||
return LamdbaExceptionUtils.uncheck(()->new URL("modjar://"+modid));
|
||||
}
|
||||
|
||||
public Optional<Manifest> getManifest() {
|
||||
return manifest;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,7 +10,6 @@ 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.IModFileInfo;
|
||||
import net.minecraftforge.forgespi.language.IModInfo;
|
||||
import net.minecraftforge.fml.loading.EarlyLoadingException.ExceptionData;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
|
||||
@@ -72,7 +71,6 @@ public class ModSorter
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
private void sort()
|
||||
{
|
||||
// lambdas are identity based, so sorting them is impossible unless you hold reference to them
|
||||
@@ -121,7 +119,6 @@ public class ModSorter
|
||||
.collect(toList());
|
||||
}
|
||||
|
||||
@SuppressWarnings("UnstableApiUsage")
|
||||
private void addDependency(MutableGraph<ModFileInfo> topoGraph, IModInfo.ModVersion dep)
|
||||
{
|
||||
final ModFileInfo self = (ModFileInfo)dep.getOwner().getOwningFile();
|
||||
@@ -133,6 +130,7 @@ public class ModSorter
|
||||
switch (dep.getOrdering()) {
|
||||
case BEFORE -> topoGraph.putEdge(self, target);
|
||||
case AFTER -> topoGraph.putEdge(target, self);
|
||||
default -> {}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ public class RuntimeDistCleaner implements ILaunchPluginService
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static boolean remove(final List<AnnotationNode> anns, final String side)
|
||||
private boolean remove(final List<AnnotationNode> anns, final String side)
|
||||
{
|
||||
return unpack(anns).stream().
|
||||
filter(ann->Objects.equals(ann.desc, ONLYIN)).
|
||||
@@ -169,15 +169,9 @@ public class RuntimeDistCleaner implements ILaunchPluginService
|
||||
private static final EnumSet<Phase> NAY = EnumSet.noneOf(Phase.class);
|
||||
|
||||
@Override
|
||||
public EnumSet<Phase> handlesClass(Type classType, boolean isEmpty) {
|
||||
if (isEmpty)
|
||||
return NAY;
|
||||
|
||||
String internalName = classType.getInternalName();
|
||||
if (internalName.startsWith("net/minecraftforge/"))
|
||||
return NAY;
|
||||
|
||||
return YAY;
|
||||
public EnumSet<Phase> handlesClass(Type classType, boolean isEmpty)
|
||||
{
|
||||
return isEmpty ? NAY : YAY;
|
||||
}
|
||||
|
||||
private static class LambdaGatherer extends MethodVisitor {
|
||||
|
||||
@@ -18,15 +18,12 @@ 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());
|
||||
// if (MavenVersionAdapter.createFromVersionSpec("[1.19.2]").containsVersion(version)) {
|
||||
// // 1.19.2 is Compatible with 1.19.1
|
||||
// add("languageloader.javafml", "42");
|
||||
// add("mod.minecraft", "1.19.1");
|
||||
// add("mod.forge", "42.0.9");
|
||||
// }
|
||||
if (FMLLoader.versionInfo().mcVersion().equals("1.20.1")) {
|
||||
add("mod.forge", "47.1.79");
|
||||
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");
|
||||
add("mod.minecraft", "1.19.1");
|
||||
add("mod.forge", "42.0.9");
|
||||
}
|
||||
}
|
||||
private static void add(String key, String value) {
|
||||
|
||||
+13
-38
@@ -27,40 +27,32 @@ import java.util.Optional;
|
||||
import java.util.function.Function;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
public abstract class AbstractModProvider implements IModProvider
|
||||
{
|
||||
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) {
|
||||
return this.createMod(getDefaultJarModType(), path);
|
||||
}
|
||||
|
||||
// FORGE: Backporting artifact, the defaultType must be the first parameter since path is varargs
|
||||
protected IModLocator.ModFileOrException createMod(String defaultType, Path... path) {
|
||||
var mjm = new ModJarMetadata();
|
||||
var sj = SecureJar.from(
|
||||
Manifest::new,
|
||||
jar -> jar.moduleDataProvider().findFile(MODS_TOML).isPresent() ? mjm : JarMetadata.from(jar, path),
|
||||
(root, p) -> true,
|
||||
path
|
||||
Manifest::new,
|
||||
jar -> jar.moduleDataProvider().findFile(MODS_TOML).isPresent() ? mjm : JarMetadata.from(jar, path),
|
||||
path
|
||||
);
|
||||
|
||||
IModFile mod;
|
||||
var type = sj.moduleDataProvider().getManifest().getMainAttributes().getValue(ModFile.TYPE);
|
||||
if (type == null) {
|
||||
type = defaultType;
|
||||
}
|
||||
if (type == null)
|
||||
type = getDefaultJarModType();
|
||||
|
||||
if (sj.moduleDataProvider().findFile(MODS_TOML).isPresent()) {
|
||||
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);
|
||||
mod = new ModFile(sj, this, this::manifestParser, type);
|
||||
} else {
|
||||
return new IModLocator.ModFileOrException(null, new ModFileLoadingException("Invalid mod file found "+ Arrays.toString(path)));
|
||||
}
|
||||
} else
|
||||
return new IModLocator.ModFileOrException(null, new ModFileLoadingException("Invalid mod file found " + Arrays.toString(path)));
|
||||
|
||||
mjm.setModFile(mod);
|
||||
return new IModLocator.ModFileOrException(mod, null);
|
||||
@@ -93,36 +85,19 @@ public abstract class AbstractModProvider implements IModProvider
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@Override public <T> Optional<T> getConfigElement(final String... strings) { return Optional.empty(); }
|
||||
@Override public List<? extends IConfigurable> getConfigList(final String... strings) { return null; }
|
||||
@Override public List<IModInfo> getMods() { return Collections.emptyList(); }
|
||||
@Override public List<LanguageSpec> requiredLanguageLoaders() { return Collections.emptyList(); }
|
||||
@Override public boolean showAsResourcePack() { return false; }
|
||||
@Override public Map<String, Object> getFileProperties() { return Collections.emptyMap(); }
|
||||
@Override
|
||||
public String getLicense() { return license; }
|
||||
|
||||
@Override public String getLicense() { return license; }
|
||||
@Override public IModFile getFile() { return mod; }
|
||||
@Override public IConfigurable getConfig() { return configurable; }
|
||||
|
||||
// These Should never be called as it's only called from ModJarMetadata.version and we bypass that
|
||||
@Override public String moduleName() { return mod.getSecureJar().name(); }
|
||||
@Override public String versionString() { return null; }
|
||||
@Override public List<String> usesServices() { return null; }
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "IModFileInfo(" + mod.getFilePath() + ")";
|
||||
}
|
||||
@Override public String toString() { return "IModFileInfo(" + mod.getFilePath() + ")"; }
|
||||
}
|
||||
}
|
||||
-12
@@ -9,13 +9,11 @@ import com.mojang.logging.LogUtils;
|
||||
import net.minecraftforge.fml.loading.ImmediateWindowHandler;
|
||||
import net.minecraftforge.fml.loading.LoadingModList;
|
||||
import net.minecraftforge.fml.loading.LogMarkers;
|
||||
import net.minecraftforge.fml.loading.progress.StartupNotificationManager;
|
||||
import net.minecraftforge.forgespi.language.ModFileScanData;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
@@ -32,9 +30,6 @@ public class BackgroundScanHandler
|
||||
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private final ExecutorService modContentScanner;
|
||||
private final List<ModFile> pendingFiles;
|
||||
private final List<ModFile> scannedFiles;
|
||||
private final List<ModFile> allFiles;
|
||||
private final List<ModFile> modFiles;
|
||||
private ScanStatus status;
|
||||
private LoadingModList loadingModList;
|
||||
@@ -46,9 +41,6 @@ public class BackgroundScanHandler
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
});
|
||||
scannedFiles = new ArrayList<>();
|
||||
pendingFiles = new ArrayList<>();
|
||||
allFiles = new ArrayList<>();
|
||||
status = ScanStatus.NOT_STARTED;
|
||||
}
|
||||
|
||||
@@ -63,8 +55,6 @@ public class BackgroundScanHandler
|
||||
}
|
||||
status = ScanStatus.RUNNING;
|
||||
ImmediateWindowHandler.updateProgress("Scanning mod candidates");
|
||||
allFiles.add(file);
|
||||
pendingFiles.add(file);
|
||||
final CompletableFuture<ModFileScanData> future = CompletableFuture.supplyAsync(file::compileContent, modContentScanner)
|
||||
.whenComplete(file::setScanResult)
|
||||
.whenComplete((r,t)-> this.addCompletedFile(file,r,t));
|
||||
@@ -76,8 +66,6 @@ public class BackgroundScanHandler
|
||||
status = ScanStatus.ERRORED;
|
||||
LOGGER.error(LogMarkers.SCAN,"An error occurred scanning file {}", file, throwable);
|
||||
}
|
||||
pendingFiles.remove(file);
|
||||
scannedFiles.add(file);
|
||||
}
|
||||
|
||||
public void setLoadingModList(LoadingModList loadingModList)
|
||||
|
||||
+1
-23
@@ -12,12 +12,9 @@ import org.slf4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
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;
|
||||
@@ -25,8 +22,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ClasspathLocator extends AbstractJarFileModLocator
|
||||
{
|
||||
public class ClasspathLocator extends AbstractJarFileModLocator {
|
||||
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;
|
||||
@@ -73,22 +69,4 @@ public class ClasspathLocator extends AbstractJarFileModLocator
|
||||
var launchTarget = (String) arguments.get("launchTarget");
|
||||
enabled = launchTarget != null && launchTarget.contains("dev");
|
||||
}
|
||||
|
||||
private Path findJarPathFor(final String resourceName, final String jarName, final URL resource) {
|
||||
try {
|
||||
Path path;
|
||||
final URI uri = resource.toURI();
|
||||
if (uri.getScheme().equals("jar") && uri.getRawSchemeSpecificPart().contains("!/")) {
|
||||
int lastExcl = uri.getRawSchemeSpecificPart().lastIndexOf("!/");
|
||||
path = Paths.get(new URI(uri.getRawSchemeSpecificPart().substring(0, lastExcl)));
|
||||
} else {
|
||||
path = Paths.get(new URI("file://"+uri.getRawSchemeSpecificPart().substring(0, uri.getRawSchemeSpecificPart().length()-resourceName.length())));
|
||||
}
|
||||
//LOGGER.debug(CORE, "Found JAR {} at path {}", jarName, path.toString());
|
||||
return path;
|
||||
} catch (NullPointerException | URISyntaxException e) {
|
||||
LOGGER.error(LogMarkers.SCAN, "Failed to find JAR for class {} - {}", resourceName, jarName);
|
||||
throw new RuntimeException("Unable to locate "+resourceName+" - "+jarName, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -34,7 +34,7 @@ public class ExplodedDirectoryLocator implements IModLocator {
|
||||
explodedMods.forEach(explodedMod ->
|
||||
ModJarMetadata.buildFile(this,
|
||||
jar->jar.moduleDataProvider().findFile("/META-INF/mods.toml").isPresent(),
|
||||
(a,b) -> true,
|
||||
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();
|
||||
|
||||
+3
-4
@@ -11,12 +11,11 @@ import net.minecraftforge.forgespi.locating.ModFileLoadingException;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
|
||||
public class InvalidModFileException extends ModFileLoadingException
|
||||
{
|
||||
public class InvalidModFileException extends ModFileLoadingException {
|
||||
private static final long serialVersionUID = 1230464325917450374L;
|
||||
private final IModFileInfo modFileInfo;
|
||||
|
||||
public InvalidModFileException(String message, IModFileInfo modFileInfo)
|
||||
{
|
||||
public InvalidModFileException(String message, IModFileInfo modFileInfo) {
|
||||
super(String.format(Locale.ROOT, "%s (%s)", message, Optional.ofNullable(modFileInfo).map(mf->mf.getFile().getFileName()).orElse("MISSING FILE NAME")));
|
||||
this.modFileInfo = modFileInfo;
|
||||
}
|
||||
|
||||
+1
-8
@@ -81,14 +81,7 @@ public class JarInJarDependencyLocator extends AbstractJarFileDependencyLocator
|
||||
final Map<String, ?> outerFsArgs = ImmutableMap.of("packagePath", pathInModFile);
|
||||
final FileSystem zipFS = FileSystems.newFileSystem(filePathUri, outerFsArgs);
|
||||
final Path pathInFS = zipFS.getPath("/");
|
||||
final IModFile.Type parentType = file.getType();
|
||||
final String modType;
|
||||
if (parentType == IModFile.Type.LIBRARY || parentType == IModFile.Type.LANGPROVIDER) {
|
||||
modType = IModFile.Type.LIBRARY.name();
|
||||
} else {
|
||||
modType = IModFile.Type.GAMELIBRARY.name();
|
||||
}
|
||||
return Optional.of(createMod(modType, pathInFS).file());
|
||||
return Optional.of(createMod(pathInFS).file());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
+15
-29
@@ -10,72 +10,58 @@ import org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.util.LinkedList;
|
||||
|
||||
public class ModAnnotationVisitor extends AnnotationVisitor
|
||||
{
|
||||
public class ModAnnotationVisitor extends AnnotationVisitor {
|
||||
private final ModAnnotation annotation;
|
||||
private LinkedList<ModAnnotation> annotations;
|
||||
private boolean array;
|
||||
private String name;
|
||||
private boolean isSubAnnotation;
|
||||
|
||||
public ModAnnotationVisitor(LinkedList<ModAnnotation> annotations, ModAnnotation annotation)
|
||||
{
|
||||
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)
|
||||
{
|
||||
public ModAnnotationVisitor(LinkedList<ModAnnotation> annotations, ModAnnotation annotation, String name) {
|
||||
this(annotations, annotation);
|
||||
this.array = true;
|
||||
this.name = name;
|
||||
annotation.addArray(name);
|
||||
}
|
||||
|
||||
public ModAnnotationVisitor(LinkedList<ModAnnotation> annotations, ModAnnotation annotation, boolean isSubAnnotation)
|
||||
{
|
||||
public ModAnnotationVisitor(LinkedList<ModAnnotation> annotations, ModAnnotation annotation, boolean isSubAnnotation) {
|
||||
this(annotations, annotation);
|
||||
this.isSubAnnotation = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(String key, Object value)
|
||||
{
|
||||
public void visit(String key, Object value) {
|
||||
annotation.addProperty(key, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnum(String name, String desc, String value)
|
||||
{
|
||||
public void visitEnum(String name, String desc, String value) {
|
||||
annotation.addEnumProperty(name, desc, value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitArray(String name)
|
||||
{
|
||||
public AnnotationVisitor visitArray(String name) {
|
||||
return new ModAnnotationVisitor(annotations, annotation, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String name, String desc)
|
||||
{
|
||||
ModAnnotation ma = annotations.getFirst();
|
||||
final ModAnnotation childAnnotation = ma.addChildAnnotation(name, desc);
|
||||
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);
|
||||
return new ModAnnotationVisitor(annotations, childAnnotation, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd()
|
||||
{
|
||||
public void visitEnd() {
|
||||
if (array)
|
||||
{
|
||||
annotation.endArray();
|
||||
}
|
||||
|
||||
if (isSubAnnotation)
|
||||
{
|
||||
ModAnnotation child = annotations.removeFirst();
|
||||
annotations.addLast(child);
|
||||
}
|
||||
annotations.addLast(annotations.removeFirst());
|
||||
}
|
||||
}
|
||||
+13
-22
@@ -15,58 +15,49 @@ import org.objectweb.asm.Type;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ModClassVisitor extends ClassVisitor
|
||||
{
|
||||
public class ModClassVisitor extends ClassVisitor {
|
||||
private Type asmType;
|
||||
private Type asmSuperType;
|
||||
private Set<Type> interfaces;
|
||||
private final LinkedList<ModAnnotation> annotations = new LinkedList<>();
|
||||
public ModClassVisitor()
|
||||
{
|
||||
|
||||
public ModClassVisitor() {
|
||||
super(Opcodes.ASM9);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces)
|
||||
{
|
||||
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
|
||||
this.asmType = Type.getObjectType(name);
|
||||
this.asmSuperType = superName != null && superName.length() > 0 ? Type.getObjectType(superName) : null;
|
||||
this.asmSuperType = superName != null && !superName.isEmpty() ? Type.getObjectType(superName) : null;
|
||||
this.interfaces = Stream.of(interfaces).map(Type::getObjectType).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(final String annotationName, final boolean runtimeVisible)
|
||||
{
|
||||
public AnnotationVisitor visitAnnotation(final String annotationName, final boolean runtimeVisible) {
|
||||
ModAnnotation ann = new ModAnnotation(ElementType.TYPE, Type.getType(annotationName), this.asmType.getClassName());
|
||||
annotations.addFirst(ann);
|
||||
return new ModAnnotationVisitor(annotations, ann);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value)
|
||||
{
|
||||
public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
|
||||
return new ModFieldVisitor(name, annotations);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions)
|
||||
{
|
||||
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
|
||||
return new ModMethodVisitor(name, desc, annotations);
|
||||
}
|
||||
|
||||
public void buildData(final Set<ModFileScanData.ClassData> classes, final Set<ModFileScanData.AnnotationData> annotations) {
|
||||
public void buildData(final Set<ModFileScanData.ClassData> classes, final Set<ModFileScanData.AnnotationData> baked) {
|
||||
classes.add(new ModFileScanData.ClassData(this.asmType, this.asmSuperType, this.interfaces));
|
||||
final List<ModFileScanData.AnnotationData> collect = this.annotations.stream().
|
||||
filter(ma->ModFileScanData.interestingAnnotations().test(ma.getASMType())).
|
||||
map(a -> ModAnnotation.fromModAnnotation(this.asmType, a)).collect(Collectors.toList());
|
||||
annotations.addAll(collect);
|
||||
for (var a : this.annotations) {
|
||||
//if (ModFileScanData.interestingAnnotations().test(a.getASMType())) // Always true, so who cares. Left as a note to remove this in SPI later.
|
||||
baked.add(ModAnnotation.fromModAnnotation(this.asmType, a));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+1
-20
@@ -12,7 +12,6 @@ import cpw.mods.modlauncher.Launcher;
|
||||
import cpw.mods.modlauncher.api.IModuleLayerManager;
|
||||
import cpw.mods.modlauncher.util.ServiceLoaderUtils;
|
||||
import net.minecraftforge.fml.loading.EarlyLoadingException;
|
||||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
import net.minecraftforge.fml.loading.ImmediateWindowHandler;
|
||||
import net.minecraftforge.fml.loading.LogMarkers;
|
||||
import net.minecraftforge.fml.loading.UniqueModListBuilder;
|
||||
@@ -22,7 +21,6 @@ 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.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -69,7 +67,6 @@ public class ModDiscoverer {
|
||||
List<EarlyLoadingException.ExceptionData> discoveryErrorData = new ArrayList<>();
|
||||
boolean successfullyLoadedMods = true;
|
||||
List<IModFileInfo> brokenFiles = new ArrayList<>();
|
||||
boolean distIsDedicatedServer = FMLLoader.getDist().isDedicatedServer();
|
||||
ImmediateWindowHandler.updateProgress("Discovering mod files");
|
||||
//Loop all mod locators to get the prime mods to load from.
|
||||
for (IModLocator locator : modLocatorList) {
|
||||
@@ -88,24 +85,8 @@ public class ModDiscoverer {
|
||||
if (!badModFiles.isEmpty()) {
|
||||
LOGGER.error(LogMarkers.SCAN, "Locator {} returned {} files which is are not ModFile instances! They will be skipped!", locator, badModFiles.size());
|
||||
brokenFiles.addAll(badModFiles.stream().map(IModFile::getModFileInfo).toList());
|
||||
locatedFiles.removeAll(badModFiles);
|
||||
}
|
||||
|
||||
if (distIsDedicatedServer) {
|
||||
var clientOnlyModFiles = locatedFiles.stream()
|
||||
.filter(file -> {
|
||||
// some mod files can have null infos, like javafml, mclanguage, lowcode, and fmlcore
|
||||
@Nullable var info = file.getModFileInfo();
|
||||
|
||||
return info != null && (Boolean) info.getFileProperties().getOrDefault(ModFileInfo.CLIENT_SIDE_ONLY_PROP, Boolean.FALSE);
|
||||
})
|
||||
.toList();
|
||||
if (!clientOnlyModFiles.isEmpty()) {
|
||||
LOGGER.warn(LogMarkers.SCAN, "Locator {} returned {} files which are client-side-only mods, but we're on a dedicated server. They will be skipped!", locator, clientOnlyModFiles.size());
|
||||
locatedFiles.removeAll(clientOnlyModFiles);
|
||||
}
|
||||
}
|
||||
|
||||
locatedFiles.removeAll(badModFiles);
|
||||
LOGGER.debug(LogMarkers.SCAN, "Locator {} found {} valid mod files", locator, locatedFiles.size());
|
||||
handleLocatedFiles(loadedFiles, locatedFiles);
|
||||
} catch (InvalidModFileException imfe) {
|
||||
|
||||
@@ -10,7 +10,6 @@ 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.fml.loading.progress.StartupNotificationManager;
|
||||
import net.minecraftforge.forgespi.language.IModFileInfo;
|
||||
import net.minecraftforge.forgespi.language.IModInfo;
|
||||
import net.minecraftforge.forgespi.language.IModLanguageProvider;
|
||||
@@ -207,8 +206,11 @@ public class ModFile implements IModFile {
|
||||
this.securityStatus = status;
|
||||
}
|
||||
|
||||
public ArtifactVersion getJarVersion()
|
||||
{
|
||||
public SecureJar.Status getSecurityStatus() {
|
||||
return this.securityStatus;
|
||||
}
|
||||
|
||||
public ArtifactVersion getJarVersion() {
|
||||
return new DefaultArtifactVersion(this.jarVersion);
|
||||
}
|
||||
|
||||
|
||||
+2
-20
@@ -29,7 +29,6 @@ import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -51,10 +50,6 @@ public class ModFileInfo implements IModFileInfo, IConfigurable
|
||||
private final String license;
|
||||
private final List<String> usesServices;
|
||||
|
||||
public static final String CLIENT_SIDE_ONLY_PROP = "__FORGE_clientSideOnly";
|
||||
|
||||
private static final Map<String, Object> CLIENT_SIDE_ONLY_MAP = Map.of(CLIENT_SIDE_ONLY_PROP, Boolean.TRUE);
|
||||
|
||||
ModFileInfo(final ModFile modFile, final IConfigurable config, Consumer<IModFileInfo> configFileConsumer)
|
||||
{
|
||||
this.modFile = modFile;
|
||||
@@ -73,23 +68,10 @@ public class ModFileInfo implements IModFileInfo, IConfigurable
|
||||
.orElse("");
|
||||
this.showAsResourcePack = config.<Boolean>getConfigElement("showAsResourcePack")
|
||||
.orElse(false);
|
||||
boolean clientSideOnly = config.<Boolean>getConfigElement("clientSideOnly")
|
||||
.orElse(false);
|
||||
this.usesServices = config.<List<String>>getConfigElement("services")
|
||||
.orElse(List.of());
|
||||
|
||||
var maybeProps = config.<Map<String, Object>>getConfigElement("properties");
|
||||
if (clientSideOnly) {
|
||||
if (maybeProps.isPresent()) {
|
||||
this.properties = new HashMap<>(maybeProps.get());
|
||||
this.properties.put(CLIENT_SIDE_ONLY_PROP, Boolean.TRUE);
|
||||
} else {
|
||||
this.properties = CLIENT_SIDE_ONLY_MAP;
|
||||
}
|
||||
} else {
|
||||
this.properties = maybeProps.orElse(Collections.emptyMap());
|
||||
}
|
||||
|
||||
this.properties = config.<Map<String, Object>>getConfigElement("properties")
|
||||
.orElse(Collections.emptyMap());
|
||||
this.modFile.setFileProperties(this.properties);
|
||||
this.issueURL = config.<String>getConfigElement("issueTrackerURL")
|
||||
.map(StringUtils::toURL)
|
||||
|
||||
@@ -22,7 +22,6 @@ import java.nio.file.Path;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ModFileParser {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
+1
-1
@@ -31,7 +31,7 @@ public final class ModJarMetadata implements JarMetadata {
|
||||
// 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, (a,b) -> true, files).orElseThrow(()->new IllegalArgumentException("Failed to find valid JAR file"));
|
||||
return buildFile(locator, j->true, null, files).orElseThrow(()->new IllegalArgumentException("Failed to find valid JAR file"));
|
||||
}
|
||||
|
||||
// TODO: Remove helper functions to cleanup api
|
||||
|
||||
+7
-2
@@ -7,7 +7,6 @@ package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import net.minecraftforge.fml.loading.FMLPaths;
|
||||
import net.minecraftforge.fml.loading.FileUtils;
|
||||
import net.minecraftforge.fml.loading.LogMarkers;
|
||||
import net.minecraftforge.fml.loading.MavenCoordinateResolver;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
@@ -63,7 +62,7 @@ public class ModListHandler {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
String extension = FileUtils.fileExtension(filePath);
|
||||
String extension = fileExtension(filePath);
|
||||
if (Objects.equals("list",extension)) {
|
||||
return readListFile(filePath).stream().filter(s -> !s.isEmpty()).collect(Collectors.toList());
|
||||
} else {
|
||||
@@ -72,6 +71,12 @@ public class ModListHandler {
|
||||
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
|
||||
|
||||
+6
-29
@@ -6,11 +6,9 @@
|
||||
package net.minecraftforge.fml.loading.moddiscovery;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import cpw.mods.jarhandling.SecureJar;
|
||||
import cpw.mods.modlauncher.api.IModuleLayerManager;
|
||||
import cpw.mods.modlauncher.api.ITransformationService;
|
||||
import net.minecraftforge.fml.loading.*;
|
||||
import net.minecraftforge.fml.loading.progress.StartupNotificationManager;
|
||||
import net.minecraftforge.forgespi.language.IModFileInfo;
|
||||
import net.minecraftforge.forgespi.locating.IModFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -24,20 +22,17 @@ import java.util.stream.Collectors;
|
||||
|
||||
public class ModValidator {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private final Map<IModFile.Type, List<ModFile>> modFiles;
|
||||
//private final Map<IModFile.Type, List<ModFile>> modFiles;
|
||||
private final List<ModFile> candidatePlugins;
|
||||
private final List<ModFile> candidateMods;
|
||||
private final List<ModFile> gameLibraries;
|
||||
private LoadingModList loadingModList;
|
||||
private final List<IModFile> brokenFiles;
|
||||
private List<IModFile> brokenFiles;
|
||||
private final List<EarlyLoadingException.ExceptionData> discoveryErrorData;
|
||||
|
||||
public ModValidator(final Map<IModFile.Type, List<ModFile>> modFiles, final List<IModFileInfo> brokenFiles, final List<EarlyLoadingException.ExceptionData> discoveryErrorData) {
|
||||
this.modFiles = modFiles;
|
||||
//this.modFiles = modFiles;
|
||||
this.candidateMods = lst(modFiles.get(IModFile.Type.MOD));
|
||||
this.candidateMods.addAll(lst(modFiles.get(IModFile.Type.GAMELIBRARY)));
|
||||
this.gameLibraries = lst(modFiles.get(IModFile.Type.GAMELIBRARY));
|
||||
this.candidateMods.addAll(this.gameLibraries);
|
||||
this.candidatePlugins = lst(modFiles.get(IModFile.Type.LANGPROVIDER));
|
||||
this.candidatePlugins.addAll(lst(modFiles.get(IModFile.Type.LIBRARY)));
|
||||
this.discoveryErrorData = discoveryErrorData;
|
||||
@@ -59,7 +54,8 @@ public class ModValidator {
|
||||
@NotNull
|
||||
private List<ModFile> validateFiles(final List<ModFile> mods) {
|
||||
final List<ModFile> brokenFiles = new ArrayList<>();
|
||||
for (Iterator<ModFile> iterator = mods.iterator(); iterator.hasNext();) {
|
||||
for (Iterator<ModFile> iterator = mods.iterator(); iterator.hasNext(); )
|
||||
{
|
||||
ModFile modFile = iterator.next();
|
||||
if (!modFile.getProvider().isValid(modFile) || !modFile.identifyMods()) {
|
||||
LOGGER.warn(LogMarkers.SCAN, "File {} has been ignored - it is invalid", modFile.getFilePath());
|
||||
@@ -75,26 +71,7 @@ public class ModValidator {
|
||||
}
|
||||
|
||||
public ITransformationService.Resource getModResources() {
|
||||
var mods = new ArrayList<SecureJar>();
|
||||
// Add only the valid mods that we will be attempting to load.
|
||||
// If any detectable error happens during the sorting process {missing deps, duplicates,
|
||||
// This helps prevent coremods/mixins from screwing up us displaying the error screens that the sorting/validation is trying to display.
|
||||
// This won't fix them all as they are still loaded and will still apply but it might help
|
||||
for (var info : this.loadingModList.getModFiles())
|
||||
mods.add(info.getFile().getSecureJar());
|
||||
|
||||
// Add any game libraries, this *may* be duplicates, depending on the state of the sorting. But until I get around to re-writing
|
||||
// that clusterfuck of a system, have this simple de-duplication. Order of resource is important, but i'm unsure if duplication is
|
||||
// so might as well waste a few cycles on checking. Still faster then converting from a set to a list.
|
||||
// Ideally we would explicitly list out which mods to load {forge and MC being the only ones} but some coremods could require extra things.
|
||||
// So try game libraries as well
|
||||
for (var lib : this.gameLibraries) {
|
||||
var jar = lib.getSecureJar();
|
||||
if (!mods.contains(jar))
|
||||
mods.add(jar);
|
||||
}
|
||||
|
||||
return new ITransformationService.Resource(IModuleLayerManager.Layer.GAME, mods);
|
||||
return new ITransformationService.Resource(IModuleLayerManager.Layer.GAME, this.candidateMods.stream().map(IModFile::getSecureJar).toList());
|
||||
}
|
||||
|
||||
private List<EarlyLoadingException.ExceptionData> validateLanguages() {
|
||||
|
||||
@@ -45,7 +45,7 @@ public class Scanner {
|
||||
try (InputStream in = Files.newInputStream(path)){
|
||||
ModClassVisitor mcv = new ModClassVisitor();
|
||||
ClassReader cr = new ClassReader(in);
|
||||
cr.accept(mcv, ClassReader.SKIP_CODE | ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
|
||||
cr.accept(mcv, 0);
|
||||
mcv.buildData(result.getClasses(), result.getAnnotations());
|
||||
} catch (IOException | IllegalArgumentException e) {
|
||||
// mark path bad
|
||||
|
||||
@@ -10,7 +10,7 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
public final class ProgressMeter {
|
||||
private final String name;
|
||||
private final int steps;
|
||||
private final AtomicInteger current;
|
||||
private AtomicInteger current;
|
||||
private Message label;
|
||||
|
||||
public ProgressMeter(String name, int steps, int current, Message label) {
|
||||
|
||||
-2
@@ -10,8 +10,6 @@ import com.google.common.base.CharMatcher;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
public class StartupNotificationManager {
|
||||
private static volatile EnumMap<Message.MessageType, List<Message>> messages = new EnumMap<>(Message.MessageType.class);
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -19,6 +21,7 @@ import java.util.function.Supplier;
|
||||
* A class that attempts to parse command line arguments into key value pairs to allow addition and editing.
|
||||
* Can not use JOptSimple as that doesn't parse out the values for keys unless the spec says it has a value.
|
||||
*/
|
||||
@ApiStatus.Internal
|
||||
class ArgumentList {
|
||||
private static final Logger LOGGER = LogUtils.getLogger();
|
||||
private List<Supplier<String[]>> entries = new ArrayList<>();
|
||||
|
||||
-47
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import cpw.mods.modlauncher.api.ServiceRunner;
|
||||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
import net.minecraftforge.fml.loading.LibraryFinder;
|
||||
import net.minecraftforge.fml.loading.VersionInfo;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public abstract class CommonClientLaunchHandler extends CommonLaunchHandler {
|
||||
@Override public Dist getDist() { return Dist.CLIENT; }
|
||||
@Override public String getNaming() { return "srg"; }
|
||||
@Override public boolean isProduction() { return true; }
|
||||
|
||||
@Override
|
||||
protected ServiceRunner makeService(final String[] arguments, final ModuleLayer gameLayer) {
|
||||
return ()->clientService(arguments, gameLayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocatedPaths getMinecraftPaths() {
|
||||
final var vers = FMLLoader.versionInfo();
|
||||
var mc = LibraryFinder.findPathForMaven("net.minecraft", "client", "", "srg", vers.mcAndMCPVersion());
|
||||
var mcextra = LibraryFinder.findPathForMaven("net.minecraft", "client", "", "extra", vers.mcAndMCPVersion());
|
||||
var mcstream = Stream.<Path>builder().add(mc).add(mcextra);
|
||||
var modstream = Stream.<List<Path>>builder();
|
||||
|
||||
processMCStream(vers, mcstream, modstream);
|
||||
|
||||
var fmlcore = LibraryFinder.findPathForMaven(vers.forgeGroup(), "fmlcore", "", "", vers.mcAndForgeVersion());
|
||||
var javafmllang = LibraryFinder.findPathForMaven(vers.forgeGroup(), "javafmllanguage", "", "", vers.mcAndForgeVersion());
|
||||
var lowcodelang = LibraryFinder.findPathForMaven(vers.forgeGroup(), "lowcodelanguage", "", "", vers.mcAndForgeVersion());
|
||||
var mclang = LibraryFinder.findPathForMaven(vers.forgeGroup(), "mclanguage", "", "", vers.mcAndForgeVersion());
|
||||
|
||||
return new LocatedPaths(mcstream.build().toList(), (a,b) -> true, modstream.build().toList(), List.of(fmlcore, javafmllang, lowcodelang, mclang));
|
||||
}
|
||||
|
||||
protected abstract void processMCStream(VersionInfo versionInfo, Stream.Builder<Path> mc, Stream.Builder<List<Path>> mods);
|
||||
}
|
||||
+103
-54
@@ -7,46 +7,30 @@ package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import cpw.mods.jarhandling.SecureJar;
|
||||
import cpw.mods.modlauncher.api.ServiceRunner;
|
||||
import net.minecraftforge.fml.loading.FileUtils;
|
||||
|
||||
import net.minecraftforge.fml.loading.LogMarkers;
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public abstract class CommonDevLaunchHandler extends CommonLaunchHandler {
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
@ApiStatus.Internal
|
||||
abstract class CommonDevLaunchHandler extends CommonLaunchHandler {
|
||||
protected CommonDevLaunchHandler(LaunchType type, String prefix) {
|
||||
super(type, prefix);
|
||||
}
|
||||
|
||||
@Override public String getNaming() { return "mcp"; }
|
||||
@Override public boolean isProduction() { return false; }
|
||||
|
||||
@Override
|
||||
public LocatedPaths getMinecraftPaths() {
|
||||
// Minecraft is extra jar {resources} + our exploded directories in dev
|
||||
final var mcstream = Stream.<Path>builder();
|
||||
|
||||
// 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 extra = findJarOnClasspath(legacyCP, "client-extra");
|
||||
|
||||
// The MC code/Patcher edits are in exploded directories
|
||||
final var modstream = Stream.<List<Path>>builder();
|
||||
final var mods = getModClasses();
|
||||
final var minecraft = mods.remove("minecraft");
|
||||
if (minecraft == null)
|
||||
throw new IllegalStateException("Could not find 'minecraft' mod paths.");
|
||||
minecraft.stream().distinct().forEach(mcstream::add);
|
||||
mods.values().forEach(modstream::add);
|
||||
|
||||
mcstream.add(extra);
|
||||
var mcFilter = getMcFilter(extra, minecraft, modstream);
|
||||
return new LocatedPaths(mcstream.build().toList(), mcFilter, modstream.build().toList(), getFmlStuff(legacyCP));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String[] preLaunch(String[] arguments, ModuleLayer layer) {
|
||||
super.preLaunch(arguments, layer);
|
||||
@@ -79,34 +63,34 @@ public abstract class CommonDevLaunchHandler extends CommonLaunchHandler {
|
||||
return args.getArguments();
|
||||
}
|
||||
|
||||
protected List<Path> getFmlStuff(String[] classpath) {
|
||||
// We also want the FML things, fmlcore, javafmllanguage, mclanguage, I don't like hard coding these, but hey whatever works for now.
|
||||
return Arrays.stream(classpath)
|
||||
.filter(e -> FileUtils.matchFileName(e, "fmlcore", "javafmllanguage", "lowcodelanguage", "mclanguage"))
|
||||
.map(Paths::get)
|
||||
.toList();
|
||||
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 static Path findJarOnClasspath(String[] classpath, String match) {
|
||||
return Paths.get(Arrays.stream(classpath)
|
||||
.filter(e -> FileUtils.matchFileName(e, match))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new IllegalStateException("Could not find " + match + " in classpath")));
|
||||
}
|
||||
|
||||
protected BiPredicate<String, String> getMcFilter(Path extra, List<Path> minecraft, Stream.Builder<List<Path>> mods) {
|
||||
final var packages = getPackages();
|
||||
final var extraPath = extra.toString().replace('\\', '/');
|
||||
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;
|
||||
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;
|
||||
@@ -114,14 +98,18 @@ public abstract class CommonDevLaunchHandler extends CommonLaunchHandler {
|
||||
if (path.startsWith(pkg)) return true;
|
||||
return false;
|
||||
}, minecraft.stream().distinct().toArray(Path[]::new));
|
||||
//modJar.getPackages().stream().sorted().forEach(System.out::println);
|
||||
mods.add(List.of(modJar.getRootPath()));
|
||||
|
||||
return mcFilter;
|
||||
//modJar.getPackages().stream().sorted().forEach(System.out::println);
|
||||
return modJar.getRootPath();
|
||||
}
|
||||
|
||||
protected String[] getPackages() {
|
||||
return new String[]{ "net/minecraftforge/", "META-INF/services/", "META-INF/coremods.json", "META-INF/mods.toml" };
|
||||
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) {
|
||||
@@ -131,9 +119,70 @@ public abstract class CommonDevLaunchHandler extends CommonLaunchHandler {
|
||||
|
||||
@Override
|
||||
protected ServiceRunner makeService(final String[] arguments, final ModuleLayer gameLayer) {
|
||||
var args = preLaunch(arguments, gameLayer);
|
||||
return ()->devService(args, gameLayer);
|
||||
return super.makeService(preLaunch(arguments, gameLayer), gameLayer);
|
||||
}
|
||||
|
||||
abstract void devService(final String[] arguments, final ModuleLayer gameLayer) throws Throwable;
|
||||
// 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 Path findJarOnClasspath(String[] classpath, String match) {
|
||||
String ret = null;
|
||||
|
||||
for (var entry : classpath) {
|
||||
int idx = entry.lastIndexOf(File.separatorChar);
|
||||
if (idx == -1) continue;
|
||||
|
||||
var name = entry.substring(idx + 1);
|
||||
if (name.startsWith(match)) {
|
||||
ret = entry;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ret == null)
|
||||
new IllegalStateException("Could not find " + match + " in classpath");
|
||||
|
||||
return Paths.get(ret);
|
||||
}
|
||||
}
|
||||
+38
-51
@@ -10,49 +10,52 @@ import cpw.mods.modlauncher.api.ILaunchHandlerService;
|
||||
import cpw.mods.modlauncher.api.ITransformingClassLoaderBuilder;
|
||||
import cpw.mods.modlauncher.api.ServiceRunner;
|
||||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
import net.minecraftforge.fml.loading.LogMarkers;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import org.apache.logging.log4j.core.LoggerContext;
|
||||
import org.apache.logging.log4j.core.config.ConfigurationFactory;
|
||||
import org.apache.logging.log4j.core.config.ConfigurationSource;
|
||||
import org.apache.logging.log4j.core.config.Configurator;
|
||||
import org.slf4j.Logger;
|
||||
import sun.misc.Unsafe;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* This is required by FMLLoader because ILaunchHandlerService doesn't have the context we need.
|
||||
* I need to clean this up to make proper api. But that involves editing ModLauncher itself which i'm not gunna do right now.
|
||||
*
|
||||
* 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) {}
|
||||
public record LocatedPaths(
|
||||
List<Path> minecraftPaths,
|
||||
BiPredicate<String, String> minecraftFilter,
|
||||
List<List<Path>> otherModPaths,
|
||||
List<Path> otherArtifacts
|
||||
) {}
|
||||
|
||||
protected static final Logger LOGGER = LogUtils.getLogger();
|
||||
|
||||
public abstract Dist getDist();
|
||||
private final LaunchType type;
|
||||
private final String prefix;
|
||||
|
||||
protected CommonLaunchHandler(LaunchType type, String prefix) {
|
||||
this.type = type;
|
||||
this.prefix = prefix;
|
||||
}
|
||||
|
||||
@Override public String name() { return this.prefix + this.type.name(); }
|
||||
public Dist getDist() { return this.type.dist(); }
|
||||
public boolean isData() { return this.type.data(); }
|
||||
public boolean isProduction() { return false; }
|
||||
public abstract String getNaming();
|
||||
|
||||
public boolean isProduction() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean isData() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public abstract LocatedPaths getMinecraftPaths();
|
||||
|
||||
@Override
|
||||
public void configureTransformationClassLoader(final ITransformingClassLoaderBuilder builder) {
|
||||
|
||||
}
|
||||
|
||||
protected String[] preLaunch(String[] arguments, ModuleLayer layer) {
|
||||
@@ -69,45 +72,29 @@ public abstract class CommonLaunchHandler implements ILaunchHandlerService {
|
||||
return arguments;
|
||||
}
|
||||
|
||||
protected final Map<String, List<Path>> getModClasses() {
|
||||
final String modClasses = Optional.ofNullable(System.getenv("MOD_CLASSES")).orElse("");
|
||||
LOGGER.debug(LogMarkers.CORE, "Got mod coordinates {} from env", modClasses);
|
||||
|
||||
record ExplodedModPath(String modid, Path path) {}
|
||||
// "a/b/;c/d/;" -> "modid%%c:\fish\pepper;modid%%c:\fish2\pepper2\;modid2%%c:\fishy\bums;modid2%%c:\hmm"
|
||||
final var modClassPaths = Arrays.stream(modClasses.split(File.pathSeparator))
|
||||
.map(inp -> inp.split("%%", 2))
|
||||
.map(splitString -> new ExplodedModPath(splitString.length == 1 ? "defaultmodid" : splitString[0], Paths.get(splitString[splitString.length - 1])))
|
||||
.collect(Collectors.groupingBy(ExplodedModPath::modid, Collectors.mapping(ExplodedModPath::path, Collectors.toList())));
|
||||
|
||||
LOGGER.debug(LogMarkers.CORE, "Found supplied mod coordinates [{}]", modClassPaths);
|
||||
|
||||
//final var explodedTargets = ((Map<String, List<ExplodedDirectoryLocator.ExplodedMod>>)arguments).computeIfAbsent("explodedTargets", a -> new ArrayList<>());
|
||||
//modClassPaths.forEach((modlabel,paths) -> explodedTargets.add(new ExplodedDirectoryLocator.ExplodedMod(modlabel, paths)));
|
||||
return modClassPaths;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServiceRunner launchService(final String[] arguments, final ModuleLayer gameLayer) {
|
||||
FMLLoader.beforeStart(gameLayer);
|
||||
return makeService(arguments, gameLayer);
|
||||
}
|
||||
|
||||
protected abstract ServiceRunner makeService(final String[] arguments, final ModuleLayer gameLayer);
|
||||
|
||||
protected void clientService(final String[] arguments, final ModuleLayer layer) throws Throwable {
|
||||
runTarget("net.minecraft.client.main.Main", arguments, layer);
|
||||
protected ServiceRunner makeService(final String[] arguments, final ModuleLayer gameLayer) {
|
||||
return () -> runTarget(this.type.module(), this.type.main(), arguments, gameLayer);
|
||||
}
|
||||
|
||||
protected void serverService(final String[] arguments, final ModuleLayer layer) throws Throwable {
|
||||
runTarget("net.minecraft.server.Main", arguments, layer);
|
||||
}
|
||||
protected record LaunchType(String name, String module, String main, Dist dist, boolean data) {};
|
||||
protected static final LaunchType CLIENT = new LaunchType("client", "minecraft", "net.minecraft.client.main.Main", Dist.CLIENT, false);
|
||||
protected static final LaunchType DATA = new LaunchType("data", "minecraft", "net.minecraft.data.Main", Dist.CLIENT, true);
|
||||
protected static final LaunchType SERVER = new LaunchType("server", "minecraft", "net.minecraft.server.Main", Dist.DEDICATED_SERVER, false);
|
||||
protected static final LaunchType SERVER_GAMETEST = new LaunchType("server_gametest", "forge", "net.minecraftforge.gametest.GameTestMain", Dist.DEDICATED_SERVER, false);
|
||||
|
||||
protected void dataService(final String[] arguments, final ModuleLayer layer) throws Throwable {
|
||||
runTarget("net.minecraft.data.Main", arguments, layer);
|
||||
}
|
||||
|
||||
protected void runTarget(final String target, final String[] arguments, final ModuleLayer layer) throws Throwable {
|
||||
Class.forName(layer.findModule("minecraft").orElseThrow(),target).getMethod("main", String[].class).invoke(null, (Object)arguments);
|
||||
protected void runTarget(String module, String target, final String[] arguments, final ModuleLayer layer) throws Throwable {
|
||||
var mod = layer.findModule(module).orElseThrow();
|
||||
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);
|
||||
var mtd = cls.getMethod("main", String[].class);
|
||||
if (mtd == null) throw new IllegalStateException("Class " + target + " in module " + module + " does not have a main(String[]) method");
|
||||
mtd.invoke(null, (Object)arguments);
|
||||
}
|
||||
}
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import cpw.mods.jarhandling.SecureJar;
|
||||
import cpw.mods.modlauncher.api.ServiceRunner;
|
||||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
import net.minecraftforge.fml.loading.LibraryFinder;
|
||||
import net.minecraftforge.fml.loading.VersionInfo;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public abstract class CommonServerLaunchHandler extends CommonLaunchHandler {
|
||||
@Override public Dist getDist() { return Dist.DEDICATED_SERVER; }
|
||||
@Override public String getNaming() { return "srg"; }
|
||||
@Override public boolean isProduction() { return true; }
|
||||
|
||||
@Override
|
||||
protected ServiceRunner makeService(String[] arguments, ModuleLayer layer) {
|
||||
return () -> serverService(arguments, layer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LocatedPaths getMinecraftPaths() {
|
||||
final var vers = FMLLoader.versionInfo();
|
||||
var mc = LibraryFinder.findPathForMaven("net.minecraft", "server", "", "srg", vers.mcAndMCPVersion());
|
||||
var mcextra = LibraryFinder.findPathForMaven("net.minecraft", "server", "", "extra", vers.mcAndMCPVersion());
|
||||
var mcextra_filtered = SecureJar.from( // We only want it for it's resources. So filter everything else out.
|
||||
(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
|
||||
);
|
||||
BiPredicate<String, String> filter = (path, base) -> true;
|
||||
|
||||
var mcstream = Stream.<Path>builder().add(mc).add(mcextra_filtered.getRootPath());
|
||||
var modstream = Stream.<List<Path>>builder();
|
||||
|
||||
filter = processMCStream(vers, mcstream, filter, modstream);
|
||||
|
||||
var fmlcore = LibraryFinder.findPathForMaven(vers.forgeGroup(), "fmlcore", "", "", vers.mcAndForgeVersion());
|
||||
var javafmllang = LibraryFinder.findPathForMaven(vers.forgeGroup(), "javafmllanguage", "", "", vers.mcAndForgeVersion());
|
||||
var lowcodelang = LibraryFinder.findPathForMaven(vers.forgeGroup(), "lowcodelanguage", "", "", vers.mcAndForgeVersion());
|
||||
var mclang = LibraryFinder.findPathForMaven(vers.forgeGroup(), "mclanguage", "", "", vers.mcAndForgeVersion());
|
||||
|
||||
return new LocatedPaths(mcstream.build().toList(), filter, modstream.build().toList(), List.of(fmlcore, javafmllang, lowcodelang, mclang));
|
||||
}
|
||||
|
||||
protected abstract BiPredicate<String, String> processMCStream(VersionInfo versionInfo, Stream.Builder<Path> mc, BiPredicate<String, String> filter, Stream.Builder<List<Path>> mods);
|
||||
}
|
||||
-42
@@ -1,42 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
import net.minecraftforge.fml.loading.VersionInfo;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public abstract class CommonUserdevLaunchHandler extends CommonDevLaunchHandler {
|
||||
@Override
|
||||
public LocatedPaths getMinecraftPaths() {
|
||||
final var vers = FMLLoader.versionInfo();
|
||||
|
||||
// Minecraft is extra jar {resources} + forge jar {patches}
|
||||
final var mcstream = Stream.<Path>builder();
|
||||
// Mod code is in exploded directories
|
||||
final var modstream = Stream.<List<Path>>builder();
|
||||
|
||||
// The MC extra and forge jars are on the classpath, so try and pull them out
|
||||
var legacyCP = Objects.requireNonNull(System.getProperty("legacyClassPath"), "Missing legacyClassPath, cannot find userdev jars").split(File.pathSeparator);
|
||||
var extra = findJarOnClasspath(legacyCP, "client-extra");
|
||||
|
||||
processStreams(legacyCP, vers, mcstream, modstream);
|
||||
getModClasses().forEach((modid, paths) -> modstream.add(paths));
|
||||
|
||||
var minecraft = mcstream.build().collect(Collectors.toList());
|
||||
var mcFilter = getMcFilter(extra, minecraft, modstream);
|
||||
minecraft.add(extra); // Add extra late so the filter is made correctly
|
||||
return new LocatedPaths(minecraft, mcFilter, modstream.build().toList(), getFmlStuff(legacyCP));
|
||||
}
|
||||
|
||||
protected abstract void processStreams(String[] classpath, VersionInfo versionInfo, Stream.Builder<Path> mc, Stream.Builder<List<Path>> mods);
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
|
||||
public class FMLClientDevLaunchHandler extends CommonDevLaunchHandler {
|
||||
@Override public String name() { return "fmlclientdev"; }
|
||||
@Override public Dist getDist() { return Dist.CLIENT; }
|
||||
|
||||
@Override
|
||||
public void devService(String[] arguments, ModuleLayer layer) throws Throwable {
|
||||
clientService(arguments, layer);
|
||||
}
|
||||
}
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import net.minecraftforge.fml.loading.LibraryFinder;
|
||||
import net.minecraftforge.fml.loading.VersionInfo;
|
||||
|
||||
public class FMLClientLaunchHandler extends CommonClientLaunchHandler {
|
||||
@Override public String name() { return "fmlclient"; }
|
||||
|
||||
@Override
|
||||
protected void processMCStream(VersionInfo versionInfo, Stream.Builder<Path> mc, Stream.Builder<List<Path>> mods) {
|
||||
var fmlonly = LibraryFinder.findPathForMaven(versionInfo.forgeGroup(), "fmlonly", "", "universal", versionInfo.mcAndForgeVersion());
|
||||
mods.add(List.of(fmlonly));
|
||||
}
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
|
||||
public class FMLClientUserdevLaunchHandler extends FMLUserdevLaunchHandler {
|
||||
@Override
|
||||
public String name() { return "fmlclientuserdev"; }
|
||||
|
||||
@Override
|
||||
public Dist getDist() { return Dist.CLIENT; }
|
||||
|
||||
@Override
|
||||
protected void devService(String[] arguments, ModuleLayer layer) throws Throwable {
|
||||
clientService(arguments, layer);
|
||||
}
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
|
||||
public class FMLDataUserdevLaunchHandler extends FMLUserdevLaunchHandler {
|
||||
@Override
|
||||
public String name() { return "fmldatauserdev"; }
|
||||
|
||||
@Override
|
||||
public Dist getDist() { return Dist.CLIENT; }
|
||||
|
||||
@Override
|
||||
public boolean isData() { return true; }
|
||||
|
||||
@Override
|
||||
public void devService(String[] arguments, ModuleLayer layer) throws Throwable {
|
||||
dataService(arguments, layer);
|
||||
}
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
|
||||
public class FMLServerDevLaunchHandler extends CommonDevLaunchHandler {
|
||||
@Override public String name() { return "fmlserverdev"; }
|
||||
@Override public Dist getDist() { return Dist.DEDICATED_SERVER; }
|
||||
|
||||
@Override
|
||||
public void devService(String[] arguments, ModuleLayer layer) throws Throwable {
|
||||
serverService(arguments, layer);
|
||||
}
|
||||
}
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import cpw.mods.modlauncher.api.ILaunchHandlerService;
|
||||
import net.minecraftforge.fml.loading.LibraryFinder;
|
||||
import net.minecraftforge.fml.loading.VersionInfo;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class FMLServerLaunchHandler extends CommonServerLaunchHandler implements ILaunchHandlerService {
|
||||
@Override public String name() { return "fmlserver"; }
|
||||
|
||||
@Override
|
||||
protected BiPredicate<String, String> processMCStream(VersionInfo versionInfo, Stream.Builder<Path> mc, BiPredicate<String, String> filter, Stream.Builder<List<Path>> mods) {
|
||||
var fmlonly = LibraryFinder.findPathForMaven(versionInfo.forgeGroup(), "fmlonly", "", "universal", versionInfo.mcAndForgeVersion());
|
||||
mods.add(List.of(fmlonly));
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
|
||||
public class FMLServerUserdevLaunchHandler extends FMLUserdevLaunchHandler {
|
||||
@Override public String name() { return "fmlserveruserdev"; }
|
||||
@Override public Dist getDist() { return Dist.DEDICATED_SERVER; }
|
||||
|
||||
@Override
|
||||
public void devService(String[] arguments, ModuleLayer layer) throws Throwable {
|
||||
serverService(arguments, layer);
|
||||
}
|
||||
}
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import net.minecraftforge.fml.loading.VersionInfo;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public abstract class FMLUserdevLaunchHandler extends CommonUserdevLaunchHandler {
|
||||
@Override
|
||||
protected void processStreams(String[] classpath, VersionInfo versionInfo, Stream.Builder<Path> mc, Stream.Builder<List<Path>> mods) {
|
||||
var fmlonly = findJarOnClasspath(classpath, "fmlonly-" + versionInfo.mcAndForgeVersion());
|
||||
mc.add(fmlonly);
|
||||
}
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
|
||||
public class ForgeClientDevLaunchHandler extends CommonDevLaunchHandler {
|
||||
@Override public String name() { return "forgeclientdev"; }
|
||||
@Override public Dist getDist() { return Dist.CLIENT; }
|
||||
|
||||
@Override
|
||||
public void devService(String[] arguments, ModuleLayer layer) throws Throwable {
|
||||
clientService(arguments, layer);
|
||||
}
|
||||
}
|
||||
-25
@@ -1,25 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import net.minecraftforge.fml.loading.LibraryFinder;
|
||||
import net.minecraftforge.fml.loading.VersionInfo;
|
||||
|
||||
public class ForgeClientLaunchHandler extends CommonClientLaunchHandler {
|
||||
@Override public String name() { return "forgeclient"; }
|
||||
|
||||
@Override
|
||||
protected void processMCStream(VersionInfo versionInfo, Stream.Builder<Path> mc, Stream.Builder<List<Path>> mods) {
|
||||
var forgepatches = LibraryFinder.findPathForMaven(versionInfo.forgeGroup(), "forge", "", "client", versionInfo.mcAndForgeVersion());
|
||||
var forgejar = LibraryFinder.findPathForMaven(versionInfo.forgeGroup(), "forge", "", "universal", versionInfo.mcAndForgeVersion());
|
||||
mc.add(forgepatches);
|
||||
mods.add(List.of(forgejar));
|
||||
}
|
||||
}
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
|
||||
public class ForgeClientUserdevLaunchHandler extends ForgeUserdevLaunchHandler {
|
||||
@Override
|
||||
public String name() { return "forgeclientuserdev"; }
|
||||
|
||||
@Override
|
||||
public Dist getDist() { return Dist.CLIENT; }
|
||||
|
||||
@Override
|
||||
public void devService(String[] arguments, ModuleLayer layer) throws Throwable {
|
||||
clientService(arguments, layer);
|
||||
}
|
||||
}
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
|
||||
public class ForgeDataDevLaunchHandler extends CommonDevLaunchHandler {
|
||||
@Override public String name() { return "forgedatadev"; }
|
||||
@Override public Dist getDist() { return Dist.CLIENT; }
|
||||
@Override public boolean isData() { return true; }
|
||||
|
||||
@Override
|
||||
public void devService(String[] arguments, ModuleLayer layer) throws Throwable {
|
||||
Class.forName(layer.findModule("minecraft").orElseThrow(), "net.minecraft.data.Main").getMethod("main", String[].class).invoke(null, (Object) arguments);
|
||||
}
|
||||
}
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
|
||||
public class ForgeDataUserdevLaunchHandler extends ForgeUserdevLaunchHandler {
|
||||
@Override
|
||||
public String name() { return "forgedatauserdev"; }
|
||||
|
||||
@Override
|
||||
public Dist getDist() { return Dist.CLIENT; }
|
||||
|
||||
@Override
|
||||
public boolean isData() { return true; }
|
||||
|
||||
@Override
|
||||
public void devService(String[] arguments, ModuleLayer layer) throws Throwable {
|
||||
dataService(arguments, layer);
|
||||
}
|
||||
}
|
||||
+295
@@ -0,0 +1,295 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
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.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.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();
|
||||
|
||||
// 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 extra = findJarOnClasspath(legacyCP, "client-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)
|
||||
);
|
||||
}
|
||||
|
||||
private List<List<Path>> explodeTestMods(List<Path> paths) {
|
||||
var mod = new ArrayList<List<Path>>();
|
||||
var memory = Jimfs.newFileSystem();
|
||||
|
||||
// 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()));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public static class Client extends ForgeDevLaunchHandler {
|
||||
public Client() {
|
||||
super(CLIENT);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Data extends ForgeDevLaunchHandler {
|
||||
public Data() {
|
||||
super(DATA);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Server extends ForgeDevLaunchHandler {
|
||||
public Server() {
|
||||
super(SERVER);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ServerGameTest extends ForgeDevLaunchHandler {
|
||||
public ServerGameTest() {
|
||||
super(SERVER_GAMETEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
|
||||
public class ForgeGametestDevLaunchHandler extends CommonDevLaunchHandler {
|
||||
@Override public String name() { return "forgegametestserverdev"; }
|
||||
@Override public Dist getDist() { return Dist.DEDICATED_SERVER; }
|
||||
|
||||
@Override
|
||||
public void devService(String[] arguments, ModuleLayer layer) throws Throwable {
|
||||
Class.forName(layer.findModule("forge").orElseThrow(), "net.minecraftforge.gametest.GameTestMain").getMethod("main", String[].class).invoke(null, (Object)arguments);
|
||||
}
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
|
||||
public class ForgeGametestUserdevLaunchHandler extends ForgeUserdevLaunchHandler {
|
||||
@Override public String name() { return "forgegametestserveruserdev"; }
|
||||
@Override public Dist getDist() { return Dist.DEDICATED_SERVER; }
|
||||
|
||||
@Override
|
||||
public void devService(String[] arguments, ModuleLayer layer) throws Throwable {
|
||||
Class.forName(layer.findModule("forge").orElseThrow(), "net.minecraftforge.gametest.GameTestMain").getMethod("main", String[].class).invoke(null, (Object)arguments);
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
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 {
|
||||
protected ForgeProdLaunchHandler(LaunchType type) {
|
||||
super(type, "forge_");
|
||||
}
|
||||
|
||||
@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() {
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
|
||||
public class ForgeServerDevLaunchHandler extends CommonDevLaunchHandler {
|
||||
@Override public String name() { return "forgeserverdev"; }
|
||||
@Override public Dist getDist() { return Dist.DEDICATED_SERVER; }
|
||||
|
||||
@Override
|
||||
public void devService(String[] arguments, ModuleLayer layer) throws Throwable {
|
||||
serverService(arguments, layer);
|
||||
}
|
||||
}
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import cpw.mods.modlauncher.api.ILaunchHandlerService;
|
||||
import net.minecraftforge.fml.loading.LibraryFinder;
|
||||
import net.minecraftforge.fml.loading.VersionInfo;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.function.BiPredicate;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class ForgeServerLaunchHandler extends CommonServerLaunchHandler implements ILaunchHandlerService {
|
||||
@Override public String name() { return "forgeserver"; }
|
||||
|
||||
@Override
|
||||
protected BiPredicate<String, String> processMCStream(VersionInfo versionInfo, Stream.Builder<Path> mc, BiPredicate<String, String> filter, Stream.Builder<List<Path>> mods) {
|
||||
var forgepatches = LibraryFinder.findPathForMaven(versionInfo.forgeGroup(), "forge", "", "server", versionInfo.mcAndForgeVersion());
|
||||
var forgejar = LibraryFinder.findPathForMaven(versionInfo.forgeGroup(), "forge", "", "universal", versionInfo.mcAndForgeVersion());
|
||||
mc.add(forgepatches);
|
||||
mods.add(List.of(forgejar));
|
||||
return filter;
|
||||
}
|
||||
}
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
|
||||
public class ForgeServerUserdevLaunchHandler extends ForgeUserdevLaunchHandler {
|
||||
@Override public String name() { return "forgeserveruserdev"; }
|
||||
@Override public Dist getDist() { return Dist.DEDICATED_SERVER; }
|
||||
|
||||
@Override
|
||||
public void devService(String[] arguments, ModuleLayer layer) throws Throwable {
|
||||
serverService(arguments, layer);
|
||||
}
|
||||
}
|
||||
+60
-6
@@ -5,16 +5,70 @@
|
||||
|
||||
package net.minecraftforge.fml.loading.targets;
|
||||
|
||||
import net.minecraftforge.fml.loading.VersionInfo;
|
||||
|
||||
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;
|
||||
|
||||
public abstract class ForgeUserdevLaunchHandler extends CommonUserdevLaunchHandler {
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
@ApiStatus.Internal
|
||||
abstract class ForgeUserdevLaunchHandler extends CommonDevLaunchHandler {
|
||||
private ForgeUserdevLaunchHandler(LaunchType type) {
|
||||
super(type, "forge_userdev_");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void processStreams(String[] classpath, VersionInfo versionInfo, Stream.Builder<Path> mc, Stream.Builder<List<Path>> mods) {
|
||||
var forge = findJarOnClasspath(classpath, "forge-" + versionInfo.mcAndForgeVersion());
|
||||
mc.add(forge);
|
||||
public LocatedPaths getMinecraftPaths() {
|
||||
var legacyCP = Objects.requireNonNull(System.getProperty("legacyClassPath"), "Missing legacyClassPath, cannot find userdev jars").split(File.pathSeparator);
|
||||
var vers = FMLLoader.versionInfo();
|
||||
|
||||
// Minecraft is extra jar {resources} + forge jar {patches}
|
||||
// The MC extra and forge jars are on the classpath, so try and pull them out
|
||||
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)
|
||||
);
|
||||
}
|
||||
|
||||
public static class Client extends ForgeUserdevLaunchHandler {
|
||||
public Client() {
|
||||
super(CLIENT);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Data extends ForgeUserdevLaunchHandler {
|
||||
public Data() {
|
||||
super(DATA);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Server extends ForgeUserdevLaunchHandler {
|
||||
public Server() {
|
||||
super(SERVER);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ServerGameTest extends ForgeUserdevLaunchHandler {
|
||||
public ServerGameTest() {
|
||||
super(SERVER_GAMETEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
@@ -11,6 +11,7 @@ import java.util.Set;
|
||||
* An exception thrown for graphs with cycles as an argument for topological sort.
|
||||
*/
|
||||
public final class CyclePresentException extends IllegalArgumentException {
|
||||
private static final long serialVersionUID = -2609660010927054667L;
|
||||
private final Set<Set<?>> cycles;
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) Forge Development LLC and contributors
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fml.server;
|
||||
|
||||
import cpw.mods.modlauncher.InvalidLauncherSetupException;
|
||||
import cpw.mods.modlauncher.Launcher;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URL;
|
||||
import java.security.CodeSource;
|
||||
import java.security.ProtectionDomain;
|
||||
import java.util.Optional;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
public class ServerMain {
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
Class.forName("cpw.mods.modlauncher.Launcher", false, ClassLoader.getSystemClassLoader());
|
||||
Class.forName("net.minecraftforge.forgespi.Environment", false, ClassLoader.getSystemClassLoader());
|
||||
} catch (ClassNotFoundException cnfe) {
|
||||
System.err.println("FATAL ERROR, You need to run the installer. The libraries required to launch a server are missing");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
final String launchArgs = Optional.ofNullable(ServerMain.class.getProtectionDomain()).
|
||||
map(ProtectionDomain::getCodeSource).
|
||||
map(CodeSource::getLocation).
|
||||
map(ServerMain::urlToManifest).
|
||||
map(Manifest::getMainAttributes).
|
||||
map(a -> a.getValue("ServerLaunchArgs")).
|
||||
orElseThrow(ServerMain::throwMissingManifest);
|
||||
String[] defaultargs = launchArgs.split(" ");
|
||||
String[] result = new String[args.length + defaultargs.length];
|
||||
System.arraycopy(defaultargs, 0, result, 0, defaultargs.length);
|
||||
System.arraycopy(args, 0, result, defaultargs.length, args.length);
|
||||
// separate class, so the exception can resolve
|
||||
new Runner().runLauncher(result);
|
||||
}
|
||||
|
||||
private static class Runner {
|
||||
private void runLauncher(final String[] result) {
|
||||
try {
|
||||
Launcher.main(result);
|
||||
} catch (InvalidLauncherSetupException e) {
|
||||
System.err.println("The server is missing critical libraries and cannot load. Please run the installer to correct this");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
private static Manifest urlToManifest(URL url) {
|
||||
try {
|
||||
return new JarFile(new File(url.toURI())).getManifest();
|
||||
} catch (URISyntaxException | IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static RuntimeException throwMissingManifest() {
|
||||
System.err.println("This is not being run from a valid JAR file, essential data is missing.");
|
||||
return new RuntimeException("Missing the manifest");
|
||||
}
|
||||
}
|
||||
Whitespace-only changes.
+14
-17
@@ -1,18 +1,15 @@
|
||||
net.minecraftforge.fml.loading.targets.FMLClientLaunchHandler
|
||||
net.minecraftforge.fml.loading.targets.FMLClientDevLaunchHandler
|
||||
net.minecraftforge.fml.loading.targets.FMLClientUserdevLaunchHandler
|
||||
net.minecraftforge.fml.loading.targets.FMLServerLaunchHandler
|
||||
net.minecraftforge.fml.loading.targets.FMLServerDevLaunchHandler
|
||||
net.minecraftforge.fml.loading.targets.FMLServerUserdevLaunchHandler
|
||||
net.minecraftforge.fml.loading.targets.FMLDataUserdevLaunchHandler
|
||||
# Production
|
||||
net.minecraftforge.fml.loading.targets.ForgeProdLaunchHandler$Client
|
||||
net.minecraftforge.fml.loading.targets.ForgeProdLaunchHandler$Server
|
||||
|
||||
net.minecraftforge.fml.loading.targets.ForgeClientLaunchHandler
|
||||
net.minecraftforge.fml.loading.targets.ForgeClientDevLaunchHandler
|
||||
net.minecraftforge.fml.loading.targets.ForgeClientUserdevLaunchHandler
|
||||
net.minecraftforge.fml.loading.targets.ForgeServerLaunchHandler
|
||||
net.minecraftforge.fml.loading.targets.ForgeServerDevLaunchHandler
|
||||
net.minecraftforge.fml.loading.targets.ForgeServerUserdevLaunchHandler
|
||||
net.minecraftforge.fml.loading.targets.ForgeDataDevLaunchHandler
|
||||
net.minecraftforge.fml.loading.targets.ForgeDataUserdevLaunchHandler
|
||||
net.minecraftforge.fml.loading.targets.ForgeGametestDevLaunchHandler
|
||||
net.minecraftforge.fml.loading.targets.ForgeGametestUserdevLaunchHandler
|
||||
# Mod Developers
|
||||
net.minecraftforge.fml.loading.targets.ForgeUserdevLaunchHandler$Client
|
||||
net.minecraftforge.fml.loading.targets.ForgeUserdevLaunchHandler$Data
|
||||
net.minecraftforge.fml.loading.targets.ForgeUserdevLaunchHandler$Server
|
||||
net.minecraftforge.fml.loading.targets.ForgeUserdevLaunchHandler$ServerGameTest
|
||||
|
||||
#Working on Forge itself
|
||||
net.minecraftforge.fml.loading.targets.ForgeDevLaunchHandler$Client
|
||||
net.minecraftforge.fml.loading.targets.ForgeDevLaunchHandler$Data
|
||||
net.minecraftforge.fml.loading.targets.ForgeDevLaunchHandler$Server
|
||||
net.minecraftforge.fml.loading.targets.ForgeDevLaunchHandler$ServerGameTest
|
||||
@@ -1 +0,0 @@
|
||||
../../src/fmlcommon/
|
||||
@@ -1,47 +0,0 @@
|
||||
/*
|
||||
* Minecraft Forge - Forge Development LLC
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fmlonly;
|
||||
|
||||
import net.minecraftforge.eventbus.api.BusBuilder;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.fml.I18NParser;
|
||||
import net.minecraftforge.fml.IBindingsProvider;
|
||||
import net.minecraftforge.fml.config.IConfigEvent;
|
||||
import net.minecraftforge.fml.event.config.ModConfigEvent;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class FMLOnlyBindings implements IBindingsProvider {
|
||||
private static final IEventBus DUMMYFORGEBUS = BusBuilder.builder().build();
|
||||
|
||||
@Override
|
||||
public Supplier<IEventBus> getForgeBusSupplier() {
|
||||
return ()->DUMMYFORGEBUS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<I18NParser> getMessageParser() {
|
||||
return ()->new I18NParser() {
|
||||
@Override
|
||||
public String parseMessage(final String i18nMessage, final Object... args) {
|
||||
return i18nMessage + Arrays.stream(args).map(Objects::toString).collect(Collectors.joining(",", "[", "]"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String stripControlCodes(final String toStrip) {
|
||||
return toStrip;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Supplier<IConfigEvent.ConfigConfig> getConfigConfiguration() {
|
||||
return ()->new IConfigEvent.ConfigConfig(ModConfigEvent.Loading::new, ModConfigEvent.Reloading::new, ModConfigEvent.Unloading::new);
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
/*
|
||||
* Minecraft Forge - Forge Development LLC
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fmlonly;
|
||||
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
|
||||
@Mod("fmlonly")
|
||||
public class FMLOnlyMod {}
|
||||
@@ -1,81 +0,0 @@
|
||||
/*
|
||||
* Minecraft Forge - Forge Development LLC
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fmlonlyclient;
|
||||
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.resources.ClientPackSource;
|
||||
import net.minecraft.server.packs.repository.PackRepository;
|
||||
import net.minecraft.server.packs.resources.PreparableReloadListener;
|
||||
import net.minecraft.server.packs.resources.ReloadableResourceManager;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.fml.LoadingFailedException;
|
||||
import net.minecraftforge.fml.ModLoader;
|
||||
import net.minecraftforge.fml.ModWorkManager;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
public class ClientModLoader
|
||||
{
|
||||
private static boolean loading;
|
||||
private static Minecraft mc;
|
||||
private static boolean loadingComplete;
|
||||
private static LoadingFailedException error;
|
||||
|
||||
public static void begin(final Minecraft minecraft, final PackRepository defaultResourcePacks, final ReloadableResourceManager mcResourceManager)
|
||||
{
|
||||
// force log4j to shutdown logging in a shutdown hook. This is because we disable default shutdown hook so the server properly logs it's shutdown
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(LogManager::shutdown));
|
||||
loading = true;
|
||||
ClientModLoader.mc = minecraft;
|
||||
createRunnableWithCatch(()->ModLoader.get().gatherAndInitializeMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), ()->{})).run();
|
||||
mcResourceManager.registerReloadListener(ClientModLoader::onResourceReload);
|
||||
}
|
||||
|
||||
private static CompletableFuture<Void> onResourceReload(final PreparableReloadListener.PreparationBarrier stage, final ResourceManager resourceManager, final ProfilerFiller prepareProfiler, final ProfilerFiller executeProfiler, final Executor asyncExecutor, final Executor syncExecutor) {
|
||||
return CompletableFuture.runAsync(createRunnableWithCatch(() -> startModLoading(ModWorkManager.wrappedExecutor(syncExecutor), asyncExecutor)), ModWorkManager.parallelExecutor())
|
||||
.thenCompose(stage::wait)
|
||||
.thenRunAsync(() -> finishModLoading(ModWorkManager.wrappedExecutor(syncExecutor), asyncExecutor), ModWorkManager.parallelExecutor());
|
||||
}
|
||||
|
||||
private static Runnable createRunnableWithCatch(Runnable r) {
|
||||
return ()-> {
|
||||
if (loadingComplete) return;
|
||||
try {
|
||||
r.run();
|
||||
} catch (LoadingFailedException e) {
|
||||
if (error == null) error = e;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static void startModLoading(ModWorkManager.DrivenExecutor syncExecutor, Executor parallelExecutor) {
|
||||
if (error!=null) throw error;
|
||||
createRunnableWithCatch(() -> ModLoader.get().loadMods(syncExecutor, parallelExecutor, ()->{})).run();
|
||||
}
|
||||
|
||||
private static void finishModLoading(ModWorkManager.DrivenExecutor syncExecutor, Executor parallelExecutor)
|
||||
{
|
||||
if (error!=null) throw error;
|
||||
createRunnableWithCatch(() -> ModLoader.get().finishMods(syncExecutor, parallelExecutor, ()->{})).run();
|
||||
loading = false;
|
||||
loadingComplete = true;
|
||||
// reload game settings on main thread
|
||||
syncExecutor.execute(()->mc.options.load());
|
||||
}
|
||||
|
||||
public static boolean isLoading()
|
||||
{
|
||||
return loading;
|
||||
}
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
/*
|
||||
* Minecraft Forge - Forge Development LLC
|
||||
* SPDX-License-Identifier: LGPL-2.1-only
|
||||
*/
|
||||
|
||||
package net.minecraftforge.fmlonlyserver;
|
||||
|
||||
import joptsimple.OptionParser;
|
||||
import net.minecraftforge.fml.LoadingFailedException;
|
||||
import net.minecraftforge.fml.ModLoader;
|
||||
import net.minecraftforge.fml.ModLoadingWarning;
|
||||
import net.minecraftforge.fml.ModWorkManager;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import static net.minecraftforge.fml.Logging.LOADING;
|
||||
|
||||
public class ServerModLoader
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static boolean hasErrors = false;
|
||||
|
||||
public static void addOptions(OptionParser parser)
|
||||
{
|
||||
// We consume this argument as we use it in the launcher and the client side.
|
||||
parser.accepts("gameDir").withRequiredArg().ofType(File.class).defaultsTo(new File("."));
|
||||
}
|
||||
|
||||
public static void load()
|
||||
{
|
||||
try
|
||||
{
|
||||
ModLoader.get().gatherAndInitializeMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), () -> {});
|
||||
ModLoader.get().loadMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), () -> {});
|
||||
ModLoader.get().finishMods(ModWorkManager.syncExecutor(), ModWorkManager.parallelExecutor(), () -> {});
|
||||
} catch (LoadingFailedException error)
|
||||
{
|
||||
ServerModLoader.hasErrors = true;
|
||||
throw error;
|
||||
}
|
||||
List<ModLoadingWarning> warnings = ModLoader.get().getWarnings();
|
||||
if (!warnings.isEmpty())
|
||||
{
|
||||
LOGGER.warn(LOADING, "Mods loaded with {} warnings", warnings.size());
|
||||
warnings.forEach(warning -> LOGGER.warn(LOADING, warning.formatToString()));
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean hasErrors()
|
||||
{
|
||||
return ServerModLoader.hasErrors;
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
{
|
||||
"load_fml_client": "coremods/load_fml_client.js",
|
||||
"load_fml_server": "coremods/load_fml_server.js"
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
modLoader="javafml"
|
||||
loaderVersion="[24,]"
|
||||
issueTrackerURL="https://www.minecraftforge.net/"
|
||||
license="LGPL v2.1"
|
||||
|
||||
[[mods]]
|
||||
modId="fmlonly"
|
||||
# We use the global forge version
|
||||
version="${global.forgeVersion}"
|
||||
updateJSONURL="https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json"
|
||||
displayName="Forge"
|
||||
credits="Anyone who has contributed on Github and supports our development"
|
||||
authors="LexManos,cpw"
|
||||
description='''
|
||||
Forge, a broad compatibility API.
|
||||
'''
|
||||
@@ -1 +0,0 @@
|
||||
net.minecraftforge.fmlonly.FMLOnlyBindings
|
||||
@@ -1 +0,0 @@
|
||||
net.minecraftforge.fml.core.ModStateProvider
|
||||
@@ -1,36 +0,0 @@
|
||||
var ASMAPI = Java.type('net.minecraftforge.coremod.api.ASMAPI')
|
||||
var Opcodes = Java.type('org.objectweb.asm.Opcodes')
|
||||
|
||||
function initializeCoreMod() {
|
||||
return {
|
||||
'minecraft': {
|
||||
'target': {
|
||||
'type': 'METHOD',
|
||||
'class': 'net.minecraft.client.Minecraft',
|
||||
'methodName': '<init>',
|
||||
'methodDesc': '(Lnet/minecraft/client/main/GameConfig;)V'
|
||||
},
|
||||
'transformer': function(methodNode) {
|
||||
//print(ASMAPI.methodNodeToString(methodNode))
|
||||
var meth = ASMAPI.getMethodNode();
|
||||
var resourcePackRepository = ASMAPI.mapField("f_91038_")
|
||||
var resourceManager = ASMAPI.mapField("f_91036_")
|
||||
|
||||
meth.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
meth.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
meth.visitFieldInsn(Opcodes.GETFIELD, "net/minecraft/client/Minecraft", resourcePackRepository, "Lnet/minecraft/server/packs/repository/PackRepository;");
|
||||
meth.visitVarInsn(Opcodes.ALOAD, 0);
|
||||
meth.visitFieldInsn(Opcodes.GETFIELD, "net/minecraft/client/Minecraft", resourceManager, "Lnet/minecraft/server/packs/resources/ReloadableResourceManager;");
|
||||
meth.visitMethodInsn(Opcodes.INVOKESTATIC, "net/minecraftforge/fmlonlyclient/ClientModLoader", "begin", "(Lnet/minecraft/client/Minecraft;Lnet/minecraft/server/packs/repository/PackRepository;Lnet/minecraft/server/packs/resources/ReloadableResourceManager;)V", false)
|
||||
for (var i=0; i<methodNode.instructions.size(); i++) {
|
||||
var ain = methodNode.instructions.get(i);
|
||||
if (ain.getOpcode() == Opcodes.PUTFIELD && ain.name.equals(resourceManager)) {
|
||||
methodNode.instructions.insert(ain, meth.instructions);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return methodNode;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
var ASMAPI = Java.type('net.minecraftforge.coremod.api.ASMAPI')
|
||||
var Opcodes = Java.type('org.objectweb.asm.Opcodes')
|
||||
|
||||
function initializeCoreMod() {
|
||||
return {
|
||||
'minecraft': {
|
||||
'target': {
|
||||
'type': 'METHOD',
|
||||
'class': 'net.minecraft.server.Main',
|
||||
'methodName': 'main',
|
||||
'methodDesc': '([Ljava/lang/String;)V'
|
||||
},
|
||||
'transformer': function(methodNode) {
|
||||
var meth = ASMAPI.getMethodNode();
|
||||
meth.visitMethodInsn(Opcodes.INVOKESTATIC, 'net/minecraftforge/fmlonlyserver/ServerModLoader', 'load', '()V', false)
|
||||
|
||||
var methOptions = ASMAPI.getMethodNode();
|
||||
methOptions.visitIntInsn(Opcodes.ALOAD, 1);
|
||||
methOptions.visitMethodInsn(Opcodes.INVOKESTATIC, 'net/minecraftforge/fmlonlyserver/ServerModLoader', 'addOptions', '(Ljoptsimple/OptionParser;)V', false)
|
||||
|
||||
for (var i=0; i<methodNode.instructions.size(); i++) {
|
||||
var ain = methodNode.instructions.get(i);
|
||||
if (ain.getOpcode() == Opcodes.INVOKESPECIAL && ain.owner == 'joptsimple/OptionParser' && ain.name == '<init>' && ain.desc == '()V') {
|
||||
// Inject after the STORE
|
||||
methodNode.instructions.insert(methodNode.instructions.get(i + 1), methOptions.instructions);
|
||||
} else if (ain.getOpcode() == Opcodes.INVOKESTATIC && ain.owner == 'net/minecraft/Util' && ain.name == ASMAPI.mapMethod('m_137584_') /* startTimerHackThread */ && ain.desc == '()V') {
|
||||
methodNode.instructions.insert(ain, meth.instructions);
|
||||
break; // By this point we've already reached the first injection point so break
|
||||
}
|
||||
}
|
||||
return methodNode;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
Loaded 100 of 1740 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user