Compare commits

...
64 Commits
Author SHA1 Message Date
LexManos 16defac82e 1.20.2 RB 1 - https://forums.minecraftforge.net/topic/139824-forge-481-minecraft-1202/ 2023-12-06 10:21:59 -08:00
J-RAP 16483d4ea1 Implemented ClientPauseEvent/Hook (#9782) 2023-11-30 17:55:34 -08:00
J.T. McQuigg 28909812bb Datagen addOptionalTag/s methods that allow passing the TagKey itself instead of passing the location (#9807) 2023-11-29 19:27:54 -08:00
LexManos e5580b84b7 Deprecate EntityRenderersEvent.AddLayers functions that hard case to LivingEntityRenderers as the backing maps do not guarantee that type. Closes #9683 2023-11-28 20:55:20 -08:00
LexManos 607ca5beea Fix TagLoader error not printing tag name correctly. Closes #9693 2023-11-28 19:31:20 -08:00
LexManos 744906e33a Fix LoadingErrorScreen inner headers are not centered. Closes #9687 2023-11-28 19:26:56 -08:00
LexManos c1557ec3c2 Rework KeyModifiers system to properly allow keybinds to be triggered when multiple modifiers are pressed.
Fix setting keybinds whel using keyboard inputs to select the menu. Closes #9793
2023-11-28 19:10:07 -08:00
LexManos 4ac1a3dedf Fix KeyModifiers not being properly taken into account. Closes #9806 2023-11-28 16:16:00 -08:00
Paint_Ninjaandembeddedt a26f952212 Don't turn off VSync when rendering from Minecraft context (#9800)
Co-authored-by: embeddedt <[email protected]>
2023-11-26 16:57:01 -08:00
Paint_Ninjaandembeddedt 83ee68c0b0 [1.20.x] Fix rare crash with early display window, fixes #9673 (#9798)
Co-Authored-By: embeddedt <[email protected]>
2023-11-26 16:23:46 -08:00
DBotThePony a5ff932125 Move Chunk Watch/Unwatch events to fire after sync packets have been sent to the client (#9790)
This makes sure that modders who send extra chunk data will have clients who know about the chunk.
2023-11-17 08:45:55 -08:00
LexManos 0d820eddd4 Add more sanitization to the ChannelListManager because other projects like to impose arbitrary critical restrictions. Closes #9789 #9772 2023-11-15 09:09:18 -08:00
LexManos 83c651ce39 Fix tag loading being unordered. Closes #9774 2023-11-13 08:50:16 -08:00
Lupicus 7cd88944d4 Fix texture UV values for obj models. Closes #9737 (#9767) 2023-11-10 15:56:38 -08:00
Paint_Ninja dff52ecd71 Gradle 8.4 (#9778) 2023-11-10 11:51:08 -08:00
DBotThePony da34d97d9d Make Ingredient check for invalidation on getItems(), invalidate ingredients on tags update (#9688) 2023-11-07 21:53:27 -08:00
Paint_Ninja a27405b8d1 Improve mod description formatting in mods screen (#9768) 2023-11-02 10:53:17 -07:00
LexManos 7a443276a5 Fix lowcode implementation version, now that it's actually labeling the correct package. 2023-10-24 23:13:26 -07:00
LexManos e34090c809 Buildscript cleanup and organization.
Updated to use newer libraries that reduce the command line arguments needed to run the game.
Fix issues in the SecureModule's classloader that caused resources to not be seen when they are on parent module layers.
2023-10-24 16:24:13 -07:00
LexManos 72e8ae252c Fix @GameTestGenerator not functioning correctly. Closes #9748 2023-10-23 11:37:51 -07:00
Paint_Ninja 347c480bdc Optimise registries by using fastutils collections to minimize boxing. (#9749) 2023-10-14 10:14:03 -07:00
Paint_Ninja e0b9474621 Minor MDK changes (#9750) 2023-10-14 09:38:49 -07:00
Paint_Ninja 8ad1e899a3 Improve mod loading error message for errors inside mod constructors (#9751) 2023-10-14 09:38:29 -07:00
The_Dead_2 3b6bb5edbc Add a CrashReportAnalyser that tries to identify the mod that crashed the game (#9732) 2023-10-14 09:38:14 -07:00
LexManos c4e7d694c1 Fix potential threading issue when registering CriteriaTriggers. Closes #9745 2023-10-12 22:31:35 -07:00
LexManos e00de1a326 Move to a cache based crowdin solution instead of hitting the api every build. 2023-10-12 22:30:48 -07:00
LexManos e9ee999558 Fix Melon/Pumpkin stems having wrong plat type causing them to remain after trampling farmland. Fixes #9495 2023-10-11 22:54:55 -07:00
LexManos a1aacf25fe Fix loot tables not having their ids set. 2023-10-10 23:41:21 -07:00
LexManos 0c2f3e5523 Make horses and llama's fire LivingFallEvent. Fixes #9743 2023-10-09 11:33:39 -07:00
LexManos a6978cffbf Fix advancement screen being too dark. Fixes #9727 2023-10-09 11:14:52 -07:00
LexManos 2332ecf984 Fix ConditionalCodec having an inverted test. Closes #9742
Remove clean project on CI.
Bump FG version to fix transitive issue on module path.
2023-10-08 13:13:17 -07:00
LexManos 9cd73c84a6 More buildscript work
Fixed ignoreList for client and server.
Lazy configured a few more tasks
Fixed early loading screen hardcoded names.. why?
2023-10-07 12:41:16 -07:00
LexManos ab544a78d3 Bump guava to module path for jimfs. Fixes dedicated server. 2023-10-07 01:01:12 -07:00
LexManos 5b0abf97c8 Revive custom ingredient types. 2023-10-06 21:14:04 -07:00
LexManos 587b684035 Revive ConditionalRecipe and ConditionalAdvancement.
Also directly add conditional support to all vanilla recipe data generators.
2023-10-06 21:01:03 -07:00
LexManos 90dafb0df0 Rewrite our GameTest hooks to be sane.
Add game test to GlobalLootModifierTest which validates the smelting modifiers.
Removed FakePlayer system, it's broken and needs a major rewrite.
2023-10-06 21:01:02 -07:00
LexManos aff05a4cf3 Cleanup FMLLoader and Launch Handlers.
Cleanup some of the build.gradle
Start work on restructuring test mods. They are now treated as their own jars, as well as having their mods.toml auto-generated. No more mods.toml conflicts!
Add pattern matching support to Data run modlist.
2023-10-06 21:01:01 -07:00
DavidQF555 1ddac54256 Fix Slot Index for Inventory Ticking Items (#9736) 2023-09-29 00:14:56 -07:00
LexManos fcf9088aa5 Make default Entity.getAddPacket automatically use Forge's enhanced spawn packet if nessasary.
This spawn packet needs to be re-designed, it is effectively only there to hook into creating the new entity instance. This will be re-evaluated during the cleanup in 1.21+
2023-09-28 16:31:18 -07:00
Paint_Ninjaandembeddedt 374dbc7a4b Optimise path filter in mod loading (#9710)
Co-authored-by: embeddedt <[email protected]>
2023-09-28 14:26:43 -07:00
Jonathan Colmenares d25ba7adc8 Fix ignitedByLava making blocks permanently flammable (#9735) Fixes #9730 2023-09-27 23:11:48 -07:00
LexManos 05c88ae33a Some BackgroundScanHandler code cleanup. Closes #9713 2023-09-27 23:08:50 -07:00
LexManos e2d564d32a Fix missed patch causing new onInventoryTick method to not be called. 2023-09-27 16:38:29 -07:00
Lubyanoy Ivan 225092130e Fixed PlayerDestroyItemEvent not triggering for Fishing Rods (#9729) 2023-09-26 10:45:34 -07:00
RealMangoRage eefca22aa3 Fix migration error causing nether and end portals to not function properly. 2023-09-24 09:57:15 -07:00
Paint_Ninja 19bbb88e52 Optimise ForgeRegistry#validateContent by removing optimization that ModLauncher broke. (#9715) 2023-09-24 09:56:35 -07:00
Martijn van den Brand 18886e5128 Fix double message encoding in Channel#toVanillaPacket #9721 (#9722)
Fix OpenContainer message not resetting reader index of additional data.
2023-09-23 12:12:46 -07:00
LexManos 47dfa83cff Make CriteriaTriggers#register public again. Closes #9723 2023-09-23 12:04:50 -07:00
LexManos 0940c532f2 Fix Hunger bar rendering incorrectly. Fixes #9725 2023-09-23 12:04:49 -07:00
LexManos d80f106140 Send known channels to the server when they send us theirs. 2023-09-21 16:25:51 -07:00
LexManos 516b35ce57 1.20.2 Release 2023-09-21 11:28:37 -07:00
LexManos be60ddbfca Enable ForgeLootTableProvider. 2023-09-20 17:46:22 -07:00
LexManos af6e848227 Global Loot Modifiers 2023-09-20 15:24:46 -07:00
LexManos dd139115ed Move old tests. 2023-09-20 15:24:15 -07:00
LexManos 145df60bec 1.20.2-rc2 2023-09-18 10:53:24 -07:00
LexManos f7cca8fb88 1.20.2-rc1 2023-09-15 12:51:23 -07:00
LexManos 1f59b3f9fe 1.20.2-pre4 2023-09-13 15:15:42 -07:00
LexManos 084effdadf 1.20.2-pre3 2023-09-12 12:34:59 -07:00
LexManos 40a7998281 Use official mappings for in-repo patches, will be converted to SRG during build. 2023-09-11 14:47:34 -07:00
LexManos 4786fa6f89 1.20.2-pre2 2023-09-11 12:47:19 -07:00
LexManos 9291b865c7 Fix debug overlay text not rendering. 2023-09-11 11:36:45 -07:00
LexManos 52eca96fdc Make SimpleChannel use a varInt for the discriminator allowing essentially unlimited packets. 2023-09-10 20:56:04 -07:00
LexManos 4649f7af36 Rewrite networking. 2023-09-10 20:27:15 -07:00
LexManos 3edefbe33e 1.20.2-pre1 2023-09-08 10:56:10 -07:00
1698 changed files with 23685 additions and 31409 deletions

No files matched your search

+3
View File
@@ -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
View File
@@ -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-/
-15
View File
@@ -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({
Vendored
-125
View File
@@ -1,125 +0,0 @@
@Library('forge-shared-library')_
pipeline {
options {
disableConcurrentBuilds()
}
agent {
docker {
image 'gradle:7-jdk16'
//args '-v forgegc:/home/gradle/.gradle_cache/'
}
}
environment {
GRADLE_ARGS = '--no-daemon --console=plain' // No daemon for now as FG3 kinda derps. //'-Dorg.gradle.daemon.idletimeout=5000'
DISCORD_WEBHOOK = credentials('forge-discord-jenkins-webhook')
DISCORD_PREFIX = "Job: Forge Branch: ${BRANCH_NAME} Build: #${BUILD_NUMBER}"
JENKINS_HEAD = 'https://wiki.jenkins-ci.org/download/attachments/2916393/headshot.png'
}
stages {
stage('notify_start') {
when {
not {
changeRequest()
}
}
steps {
discordSend(
title: "${DISCORD_PREFIX} Started",
successful: true,
result: 'ABORTED', //White border
thumbnail: JENKINS_HEAD,
webhookURL: DISCORD_WEBHOOK
)
}
}
/*
stage('pull_cache') {
steps {
sh 'cp -r /home/gradle/.gradle_cache/jdks/ /home/gradle/.gradle/jdks/'
sh 'cp -r /home/gradle/.gradle_cache/wrapper/ /home/gradle/.gradle/wrapper/'
sh 'cp -r /home/gradle/.gradle_cache/caches/ /home/gradle/.gradle/caches/'
}
}
*/
stage('setup') {
steps {
withGradle {
sh './gradlew ${GRADLE_ARGS} --refresh-dependencies --continue setup'
}
script {
env.MYVERSION = sh(returnStdout: true, script: './gradlew :forge:properties -q | grep "^version:" | cut -d" " -f2').trim()
env.FMLONLY_VERSION = sh(returnStdout: true, script: './gradlew :fmlonly:properties -q | grep "^version:" | cut -d" " -f2').trim()
}
}
}
stage('changelog') {
when {
not {
changeRequest()
}
}
steps {
writeChangelog(currentBuild, 'build/changelog.txt')
}
}
stage('publish') {
when {
not {
changeRequest()
}
}
environment {
CROWDIN = credentials('forge-crowdin')
KEYSTORE = credentials('forge-jenkins-keystore-old')
KEYSTORE_KEYPASS = credentials('forge-jenkins-keystore-old-keypass')
KEYSTORE_STOREPASS = credentials('forge-jenkins-keystore-old-keypass')
}
steps {
withCredentials([usernamePassword(credentialsId: 'maven-forge-user', usernameVariable: 'MAVEN_USER', passwordVariable: 'MAVEN_PASSWORD')]) {
withGradle {
sh './gradlew ${GRADLE_ARGS} publish -PkeystoreKeyPass=${KEYSTORE_KEYPASS} -PkeystoreStorePass=${KEYSTORE_STOREPASS} -Pkeystore=${KEYSTORE} -PcrowdinKey=${CROWDIN}'
}
}
}
post {
success {
build job: 'filegenerator', parameters: [string(name: 'COMMAND', value: "promote net.minecraftforge:forge ${env.MYVERSION} latest")], propagate: false, wait: false
build job: 'filegenerator', parameters: [string(name: 'COMMAND', value: "promote net.minecraftforge:fmlonly ${env.FMLONLY_VERSION} latest")], propagate: false, wait: false
}
}
}
stage('test_publish_pr') { //Publish to local repo to test full process, but don't include credentials so it can't sign/publish to maven
when {
changeRequest()
}
environment {
CROWDIN = credentials('forge-crowdin')
}
steps {
withGradle {
sh './gradlew ${GRADLE_ARGS} publish -PcrowdinKey=${CROWDIN}'
}
}
}
}
post {
always {
script {
archiveArtifacts artifacts: 'projects/forge/build/libs/**/*.*', fingerprint: true, onlyIfSuccessful: true, allowEmptyArchive: true
if (env.CHANGE_ID == null) { // This is unset for non-PRs
discordSend(
title: "${DISCORD_PREFIX} Finished ${currentBuild.currentResult}",
description: '```\n' + getChanges(currentBuild) + '\n```',
successful: currentBuild.resultIsBetterOrEqualTo("SUCCESS"),
result: currentBuild.currentResult,
thumbnail: JENKINS_HEAD,
webhookURL: DISCORD_WEBHOOK
)
}
}
}
}
}
+305 -800
View File
File diff suppressed because it is too large. Load diff
@@ -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'
+51
View File
@@ -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'
}
+3 -3
View File
@@ -2,8 +2,8 @@
MinecraftForge
=============
[![Stable Release](https://img.shields.io/badge/dynamic/json?url=https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json&label=Stable&prefix=1.20.1&query=$.promos["1.20.1-recommended"]&color=brightgreen&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtc3RhciBmYS13LTE4IiBhcmlhLWhpZGRlbj0idHJ1ZSIgZGF0YS1pY29uPSJzdGFyIiBkYXRhLXByZWZpeD0iZmFzIiBmb2N1c2FibGU9ImZhbHNlIiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCA1NzYgNTEyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNMjU5LjMgMTcuOEwxOTQgMTUwLjIgNDcuOSAxNzEuNWMtMjYuMiAzLjgtMzYuNyAzNi4xLTE3LjcgNTQuNmwxMDUuNyAxMDMtMjUgMTQ1LjVjLTQuNSAyNi4zIDIzLjIgNDYgNDYuNCAzMy43TDI4OCA0MzkuNmwxMzAuNyA2OC43YzIzLjIgMTIuMiA1MC45LTcuNCA0Ni40LTMzLjdsLTI1LTE0NS41IDEwNS43LTEwM2MxOS0xOC41IDguNS01MC44LTE3LjctNTQuNkwzODIgMTUwLjIgMzE2LjcgMTcuOGMtMTEuNy0yMy42LTQ1LjYtMjMuOS01Ny40IDB6IiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K)][Download]
[![Latest Release](https://img.shields.io/badge/dynamic/json?url=https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json&label=Latest&prefix=1.20.1-&query=$.promos["1.20.1-latest"]&color=blue&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtYnVnIGZhLXctMTYiIGFyaWEtaGlkZGVuPSJ0cnVlIiBkYXRhLWljb249ImJ1ZyIgZGF0YS1wcmVmaXg9ImZhcyIgZm9jdXNhYmxlPSJmYWxzZSIgcm9sZT0iaW1nIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTUxMS45ODggMjg4LjljLS40NzggMTcuNDMtMTUuMjE3IDMxLjEtMzIuNjUzIDMxLjFINDI0djE2YzAgMjEuODY0LTQuODgyIDQyLjU4NC0xMy42IDYxLjE0NWw2MC4yMjggNjAuMjI4YzEyLjQ5NiAxMi40OTcgMTIuNDk2IDMyLjc1OCAwIDQ1LjI1NS0xMi40OTggMTIuNDk3LTMyLjc1OSAxMi40OTYtNDUuMjU2IDBsLTU0LjczNi01NC43MzZDMzQ1Ljg4NiA0NjcuOTY1IDMxNC4zNTEgNDgwIDI4MCA0ODBWMjM2YzAtNi42MjctNS4zNzMtMTItMTItMTJoLTI0Yy02LjYyNyAwLTEyIDUuMzczLTEyIDEydjI0NGMtMzQuMzUxIDAtNjUuODg2LTEyLjAzNS05MC42MzYtMzIuMTA4bC01NC43MzYgNTQuNzM2Yy0xMi40OTggMTIuNDk3LTMyLjc1OSAxMi40OTYtNDUuMjU2IDAtMTIuNDk2LTEyLjQ5Ny0xMi40OTYtMzIuNzU4IDAtNDUuMjU1bDYwLjIyOC02MC4yMjhDOTIuODgyIDM3OC41ODQgODggMzU3Ljg2NCA4OCAzMzZ2LTE2SDMyLjY2NkMxNS4yMyAzMjAgLjQ5MSAzMDYuMzMuMDEzIDI4OC45LS40ODQgMjcwLjgxNiAxNC4wMjggMjU2IDMyIDI1Nmg1NnYtNTguNzQ1bC00Ni42MjgtNDYuNjI4Yy0xMi40OTYtMTIuNDk3LTEyLjQ5Ni0zMi43NTggMC00NS4yNTUgMTIuNDk4LTEyLjQ5NyAzMi43NTgtMTIuNDk3IDQ1LjI1NiAwTDE0MS4yNTUgMTYwaDIyOS40ODlsNTQuNjI3LTU0LjYyN2MxMi40OTgtMTIuNDk3IDMyLjc1OC0xMi40OTcgNDUuMjU2IDAgMTIuNDk2IDEyLjQ5NyAxMi40OTYgMzIuNzU4IDAgNDUuMjU1TDQyNCAxOTcuMjU1VjI1Nmg1NmMxNy45NzIgMCAzMi40ODQgMTQuODE2IDMxLjk4OCAzMi45ek0yNTcgMGMtNjEuODU2IDAtMTEyIDUwLjE0NC0xMTIgMTEyaDIyNEMzNjkgNTAuMTQ0IDMxOC44NTYgMCAyNTcgMHoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=
[![Stable Release](https://img.shields.io/badge/dynamic/json?url=https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json&label=Stable&prefix=1.20.2&query=$.promos["1.20.2-recommended"]&color=brightgreen&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtc3RhciBmYS13LTE4IiBhcmlhLWhpZGRlbj0idHJ1ZSIgZGF0YS1pY29uPSJzdGFyIiBkYXRhLXByZWZpeD0iZmFzIiBmb2N1c2FibGU9ImZhbHNlIiByb2xlPSJpbWciIHZpZXdCb3g9IjAgMCA1NzYgNTEyIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPgo8cGF0aCBkPSJNMjU5LjMgMTcuOEwxOTQgMTUwLjIgNDcuOSAxNzEuNWMtMjYuMiAzLjgtMzYuNyAzNi4xLTE3LjcgNTQuNmwxMDUuNyAxMDMtMjUgMTQ1LjVjLTQuNSAyNi4zIDIzLjIgNDYgNDYuNCAzMy43TDI4OCA0MzkuNmwxMzAuNyA2OC43YzIzLjIgMTIuMiA1MC45LTcuNCA0Ni40LTMzLjdsLTI1LTE0NS41IDEwNS43LTEwM2MxOS0xOC41IDguNS01MC44LTE3LjctNTQuNkwzODIgMTUwLjIgMzE2LjcgMTcuOGMtMTEuNy0yMy42LTQ1LjYtMjMuOS01Ny40IDB6IiBmaWxsPSJ3aGl0ZSIvPgo8L3N2Zz4K)][Download]
[![Latest Release](https://img.shields.io/badge/dynamic/json?url=https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json&label=Latest&prefix=1.20.2-&query=$.promos["1.20.2-latest"]&color=blue&logo=data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz4KPHN2ZyBjbGFzcz0ic3ZnLWlubGluZS0tZmEgZmEtYnVnIGZhLXctMTYiIGFyaWEtaGlkZGVuPSJ0cnVlIiBkYXRhLWljb249ImJ1ZyIgZGF0YS1wcmVmaXg9ImZhcyIgZm9jdXNhYmxlPSJmYWxzZSIgcm9sZT0iaW1nIiB2aWV3Qm94PSIwIDAgNTEyIDUxMiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj4KPHBhdGggZD0iTTUxMS45ODggMjg4LjljLS40NzggMTcuNDMtMTUuMjE3IDMxLjEtMzIuNjUzIDMxLjFINDI0djE2YzAgMjEuODY0LTQuODgyIDQyLjU4NC0xMy42IDYxLjE0NWw2MC4yMjggNjAuMjI4YzEyLjQ5NiAxMi40OTcgMTIuNDk2IDMyLjc1OCAwIDQ1LjI1NS0xMi40OTggMTIuNDk3LTMyLjc1OSAxMi40OTYtNDUuMjU2IDBsLTU0LjczNi01NC43MzZDMzQ1Ljg4NiA0NjcuOTY1IDMxNC4zNTEgNDgwIDI4MCA0ODBWMjM2YzAtNi42MjctNS4zNzMtMTItMTItMTJoLTI0Yy02LjYyNyAwLTEyIDUuMzczLTEyIDEydjI0NGMtMzQuMzUxIDAtNjUuODg2LTEyLjAzNS05MC42MzYtMzIuMTA4bC01NC43MzYgNTQuNzM2Yy0xMi40OTggMTIuNDk3LTMyLjc1OSAxMi40OTYtNDUuMjU2IDAtMTIuNDk2LTEyLjQ5Ny0xMi40OTYtMzIuNzU4IDAtNDUuMjU1bDYwLjIyOC02MC4yMjhDOTIuODgyIDM3OC41ODQgODggMzU3Ljg2NCA4OCAzMzZ2LTE2SDMyLjY2NkMxNS4yMyAzMjAgLjQ5MSAzMDYuMzMuMDEzIDI4OC45LS40ODQgMjcwLjgxNiAxNC4wMjggMjU2IDMyIDI1Nmg1NnYtNTguNzQ1bC00Ni42MjgtNDYuNjI4Yy0xMi40OTYtMTIuNDk3LTEyLjQ5Ni0zMi43NTggMC00NS4yNTUgMTIuNDk4LTEyLjQ5NyAzMi43NTgtMTIuNDk3IDQ1LjI1NiAwTDE0MS4yNTUgMTYwaDIyOS40ODlsNTQuNjI3LTU0LjYyN2MxMi40OTgtMTIuNDk3IDMyLjc1OC0xMi40OTcgNDUuMjU2IDAgMTIuNDk2IDEyLjQ5NyAxMi40OTYgMzIuNzU4IDAgNDUuMjU1TDQyNCAxOTcuMjU1VjI1Nmg1NmMxNy45NzIgMCAzMi40ODQgMTQuODE2IDMxLjk4OCAzMi45ek0yNTcgMGMtNjEuODU2IDAtMTEyIDUwLjE0NC0xMTIgMTEyaDIyNEMzNjkgNTAuMTQ0IDMxOC44NTYgMCAyNTcgMHoiIGZpbGw9IndoaXRlIi8+Cjwvc3ZnPgo=
)][Download] [![Discord](https://img.shields.io/discord/313125603924639766.svg?color=%237289da&label=Discord&logo=discord&logoColor=%237289da)][Discord] [![Support](https://img.shields.io/badge/Patreon-Support-orange.svg?logo=Patreon)][Patreon]
Forge is a free, open-source modding API all of your favourite mods use!
@@ -73,4 +73,4 @@ Please ensure you have a valid email address associated with your GitHub account
[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/MinecraftForge
[Patreon]: https://www.patreon.com/LexManos
+5 -6
View File
@@ -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) {
@@ -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,6 +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";
public static final String IGNORESERVERONLY = "SERVER_ONLY";
}
}
+9 -34
View File
@@ -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/')
}
}
@@ -40,11 +40,13 @@ import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.StringJoiner;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
@@ -86,6 +88,7 @@ public class DisplayWindow implements ImmediateWindowProvider {
private int framecount;
private EarlyFramebuffer framebuffer;
private ScheduledFuture<?> windowTick;
private ScheduledFuture<?> initializationFuture;
private PerformanceInfo performanceInfo;
private ScheduledFuture<?> performanceTick;
@@ -264,7 +267,6 @@ public class DisplayWindow implements ImmediateWindowProvider {
public void render(int alpha) {
var currentVAO = glGetInteger(GL_VERTEX_ARRAY_BINDING);
var currentFB = glGetInteger(GL_READ_FRAMEBUFFER_BINDING);
glfwSwapInterval(0);
glViewport(0, 0, this.context.scaledWidth(), this.context.scaledHeight());
RenderElement.globalAlpha = alpha;
framebuffer.activate();
@@ -287,7 +289,7 @@ public class DisplayWindow implements ImmediateWindowProvider {
return thread;
});
initWindow(mcVersion);
renderScheduler.schedule(() -> initRender(mcVersion, forgeVersion), 1, TimeUnit.MILLISECONDS);
this.initializationFuture = renderScheduler.schedule(() -> initRender(mcVersion, forgeVersion), 1, TimeUnit.MILLISECONDS);
return this::periodicTick;
}
@@ -518,6 +520,16 @@ public class DisplayWindow implements ImmediateWindowProvider {
* @return the Window we own.
*/
public long setupMinecraftWindow(final IntSupplier width, final IntSupplier height, final Supplier<String> title, final LongSupplier monitorSupplier) {
// wait for the window to actually be initialized
try {
this.initializationFuture.get(30, TimeUnit.SECONDS);
} catch (InterruptedException | ExecutionException e) {
throw new RuntimeException(e);
} catch (TimeoutException e) {
Thread.dumpStack();
crashElegantly("We seem to be having trouble initializing the window, waited for 30 seconds");
}
// we have to spin wait for the window ticker
ImmediateWindowHandler.updateProgress("Initializing Game Graphics");
while (!this.windowTick.isDone()) {
+27 -37
View File
@@ -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',
@@ -56,6 +56,7 @@ public class ObjectHolderDefinalize implements ILaunchPluginService {
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();
@@ -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
@@ -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;
@@ -42,7 +42,6 @@ public class FMLConfig
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) {
@@ -52,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;
@@ -137,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;
@@ -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 -> {}
}
}
@@ -27,8 +27,7 @@ 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";
@@ -36,26 +35,24 @@ public abstract class AbstractModProvider implements IModProvider
protected IModLocator.ModFileOrException createMod(Path... path) {
var mjm = new ModJarMetadata();
var sj = SecureJar.from(
Manifest::new,
jar -> jar.moduleDataProvider().findFile(MODS_TOML).isPresent() ? mjm : JarMetadata.from(jar, path),
(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) {
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);
@@ -88,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() + ")"; }
}
}
@@ -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)
@@ -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);
}
}
}
@@ -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();
@@ -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;
}
@@ -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());
}
}
@@ -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));
}
}
}
@@ -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);
}
@@ -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();
@@ -80,7 +80,8 @@ public class ModInfo implements IModInfo, IConfigurable
this.displayName = config.<String>getConfigElement("displayName")
.orElse(this.modId);
this.description = config.<String>getConfigElement("description")
.orElse("MISSING DESCRIPTION");
.orElse("MISSING DESCRIPTION")
.replace("\r\n", "\n").stripIndent();
this.logoFile = Optional.ofNullable(config.<String>getConfigElement("logoFile")
.orElseGet(() -> ownFile.flatMap(mf -> mf.<String>getConfigElement("logoFile"))
.orElse(null)));
@@ -205,7 +206,7 @@ public class ModInfo implements IModInfo, IConfigurable
private final boolean mandatory;
private final Ordering ordering;
private final DependencySide side;
private Optional<URL> referralUrl;
private final Optional<URL> referralUrl;
public ModVersion(final IModInfo owner, final IConfigurable config) {
this.owner = owner;
@@ -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,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
@@ -9,7 +9,6 @@ import com.mojang.logging.LogUtils;
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;
@@ -23,7 +22,7 @@ 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 LoadingModList loadingModList;
@@ -31,7 +30,7 @@ public class ModValidator {
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.candidatePlugins = lst(modFiles.get(IModFile.Type.LANGPROVIDER));
@@ -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<>();
@@ -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);
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
@@ -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);
}
@@ -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);
}
}
@@ -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));
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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;
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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));
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
}
@@ -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);
}
}
@@ -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;
}
}
@@ -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);
}
}
@@ -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);
}
}
}
@@ -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");
}
}
@@ -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
View File
@@ -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;
}
},
}
}
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip
networkTimeout=10000
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+6 -5
View File
@@ -85,9 +85,6 @@ done
APP_BASE_NAME=${0##*/}
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -144,7 +141,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
case $MAX_FD in #(
max*)
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC3045
MAX_FD=$( ulimit -H -n ) ||
warn "Could not query maximum file descriptor limit"
esac
@@ -152,7 +149,7 @@ if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
'' | soft) :;; #(
*)
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
# shellcheck disable=SC3045
# shellcheck disable=SC3045
ulimit -n "$MAX_FD" ||
warn "Could not set maximum file descriptor limit to $MAX_FD"
esac
@@ -197,6 +194,10 @@ if "$cygwin" || "$msys" ; then
done
fi
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command;
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
# shell script including quotes and variable substitutions, so put them in
@@ -0,0 +1,19 @@
activeContentFilterList=*.makefile,makefile,*.Makefile,Makefile,Makefile.*,*.mk,MANIFEST.MF,.project,*.yml
addNewLine=true
convertActionOnSaave=AnyEdit.CnvrtTabToSpaces
eclipse.preferences.version=1
encoding/<project>=UTF-8
fixLineDelimiters=false
ignoreBlankLinesWhenTrimming=false
inActiveContentFilterList=
javaTabWidthForJava=true
org.eclipse.jdt.ui.editor.tab.width=2
projectPropsEnabled=true
removeTrailingSpaces=true
replaceAllSpaces=false
replaceAllTabs=false
saveAndAddLine=false
saveAndConvert=true
saveAndFixLineDelimiters=false
saveAndTrim=true
useModulo4Tabs=false
@@ -0,0 +1,2 @@
eclipse.preferences.version=1
encoding/<project>=UTF-8
@@ -0,0 +1,2 @@
eclipse.preferences.version=1
line.separator=\n
+3 -4
View File
@@ -2,15 +2,14 @@ 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)
implementation(project(':fmlloader'))
implementation(project(':fmlcore'))
}
@@ -21,6 +21,8 @@ import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
import java.lang.reflect.InvocationTargetException;
import java.util.Objects;
import java.util.Optional;
public class FMLModContainer extends ModContainer
@@ -70,6 +72,11 @@ public class FMLModContainer extends ModContainer
}
catch (Throwable e)
{
// When a mod constructor throws an exception, it's wrapped in an InvocationTargetException which hides the
// actual exception from the mod loading error screen.
if (e instanceof InvocationTargetException wrapped)
e = Objects.requireNonNullElse(wrapped.getCause(), e); // unwrap the exception
LOGGER.error(LOADING,"Failed to create mod instance. ModID: {}, class {}", getModId(), modClass.getName(), e);
throw new ModLoadingException(modInfo, ModLoadingStage.CONSTRUCT, "fml.modloading.failedtoloadmod", e, modClass);
}
+16 -33
View File
@@ -1,50 +1,33 @@
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) }
java.withSourcesJar()
dependencies {
compileOnly("org.jetbrains:annotations:${JETBRAINS_ANNOTATIONS_VERSION}")
compileOnly(libs.nulls)
implementation(project(':fmlloader'))
implementation(project(':fmlcore'))
}
ext {
MANIFESTS = [
'': [
'FMLModType': 'LANGPROVIDER',
'Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"),
'Git-Commit': GIT_INFO.abbreviatedId,
'Git-Branch': GIT_INFO.branch,
'Build-Number': "${System.getenv('BUILD_NUMBER')?:0}",
] as LinkedHashMap,
'net/minecraftforge/fml/loading/': [
'Specification-Title': 'FMLLoader',
tasks.named('jar', Jar).configure {
manifest {
attributes([
'Automatic-Module-Name': project.name.toLowerCase(),
'FMLModType': 'LANGPROVIDER'
] as java.util.LinkedHashMap)
attributes([
'Specification-Title': 'LowCodeMod',
'Specification-Vendor': 'Forge Development LLC',
'Specification-Version': '1',
'Implementation-Title': 'FML Loader',
'Implementation-Version': '1.0',
'Implementation-Vendor': 'Forge'
] as LinkedHashMap
]
}
jar.doFirst {
manifest.attributes['Automatic-Module-Name'] = project.name
MANIFESTS.each { pkg, values ->
if (pkg == '')
manifest.attributes(values)
else
manifest.attributes(values, pkg)
'Specification-Version': '1.0',
'Implementation-Title': project.name,
'Implementation-Version': FORGE_VERSION.split('\\.')[0], //project.version, TODO: Split out into a real versioned system, for now use the default that net.minecraftforge.fml.loading.LanguageLoadingProvider.loadLanguageProviders() used
'Implementation-Vendor': 'Forge Development LLC'
] as java.util.LinkedHashMap, 'net/minecraftforge/fml/lowcodemod/')
}
}
+15 -41
View File
@@ -1,52 +1,33 @@
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)
implementation(project(':fmlloader'))
implementation(project(':fmlcore'))
}
task sourcesJar(type: Jar) {
archiveClassifier = 'sources'
from sourceSets.main.allSource
java {
withSourcesJar()
}
ext {
MANIFESTS = [
'': [
tasks.named('jar', Jar).configure {
manifest {
attributes([
'Automatic-Module-Name': project.name.toLowerCase(),
'FMLModType': 'LANGPROVIDER',
'Timestamp': new Date().format("yyyy-MM-dd'T'HH:mm:ssZ"),
'Git-Commit': GIT_INFO.abbreviatedId,
'Git-Branch': GIT_INFO.branch,
'Build-Number': "${System.getenv('BUILD_NUMBER')?:0}",
] as LinkedHashMap,
'net/minecraftforge/fml/mclanguageprovider/': [
'Specification-Title': 'FMLLoader',
] as LinkedHashMap)
attributes([
'Specification-Title': 'MCLanguage',
'Specification-Vendor': 'Forge Development LLC',
'Specification-Version': '1',
'Implementation-Title': 'FML Loader',
'Implementation-Title': 'MCLanguage',
'Implementation-Version': '1.0',
'Implementation-Vendor': 'Forge'
] as LinkedHashMap
]
}
jar.doFirst {
manifest.attributes['Automatic-Module-Name'] = project.name
MANIFESTS.each { pkg, values ->
if (pkg == '')
manifest.attributes(values)
else
manifest.attributes(values, pkg)
'Implementation-Vendor': 'Forge Development LLC',
] as LinkedHashMap, 'net/minecraftforge/fml/mclanguageprovider/')
}
}
@@ -56,16 +37,9 @@ tasks.withType(JavaCompile) {
license {
header = rootProject.file('LICENSE-header.txt')
include 'net/minecraftforge/'
}
artifacts {
archives jar
archives sourcesJar
}
publishing.publications.mavenJava(MavenPublication) {
from components.java
artifact sourcesJar
}
+7 -7
View File
@@ -16,19 +16,19 @@ Step 1: Open your command-line and browse to the folder where you extracted the
Step 2: You're left with a choice.
If you prefer to use Eclipse:
1. Run the following command: `gradlew genEclipseRuns` (`./gradlew genEclipseRuns` if you are on Mac/Linux)
1. Run the following command: `./gradlew genEclipseRuns`
2. Open Eclipse, Import > Existing Gradle Project > Select Folder
or run `gradlew eclipse` to generate the project.
If you prefer to use IntelliJ:
1. Open IDEA, and import project.
2. Select your build.gradle file and have it import.
3. Run the following command: `gradlew genIntellijRuns` (`./gradlew genIntellijRuns` if you are on Mac/Linux)
3. Run the following command: `./gradlew genIntellijRuns`
4. Refresh the Gradle Project in IDEA if required.
If at any point you are missing libraries in your IDE, or you've run into problems you can
run `gradlew --refresh-dependencies` to refresh the local cache. `gradlew clean` to reset everything
{this does not affect your code} and then start the process again.
(this does not affect your code) and then start the process again.
Mapping Names:
=============================
@@ -40,7 +40,7 @@ https://github.com/MinecraftForge/MCPConfig/blob/master/Mojang.md
Additional Resources:
=========================
Community Documentation: https://mcforge.readthedocs.io/en/latest/gettingstarted/
LexManos' Install Video: https://www.youtube.com/watch?v=8VEdtQLuLO0
Forge Forum: https://forums.minecraftforge.net/
Forge Discord: https://discord.gg/UvedJ9m
Community Documentation: https://docs.minecraftforge.net/en/latest/gettingstarted/
LexManos' Install Video: https://youtu.be/8VEdtQLuLO0
Forge Forums: https://forums.minecraftforge.net/
Forge Discord: https://discord.minecraftforge.net/
+1 -1
View File
@@ -2,7 +2,7 @@ plugins {
id 'eclipse'
id 'idea'
id 'maven-publish'
id 'net.minecraftforge.gradle' version '[6.0,6.2)'
id 'net.minecraftforge.gradle' version '[6.0.16,6.2)'
}
version = mod_version
+1 -1
View File
@@ -9,5 +9,5 @@ pluginManagement {
}
plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.5.0'
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.7.0'
}
Loaded 100 of 1698 files, more files were not shown because too many files have changed in this diff. Show more