From 519399f856100af53b24d2231dadf29b5fdbb751 Mon Sep 17 00:00:00 2001 From: SizableShrimp Date: Thu, 18 May 2023 03:52:41 -0500 Subject: [PATCH] [1.18.x] Make mixins work with JarJar (#9506) Backport of #8900 Update ASM to 9.5 Update JarJar to 0.3.19 Update SecureJarHandler to 1.0.8 --- build.gradle | 20 ++-- buildSrc/build.gradle | 9 +- .../forge/tasks/TeamcityRequests.groovy | 74 +++++++++++++ .../minecraftforge/forge/tasks/Util.groovy | 7 +- fmlloader/build.gradle | 4 +- .../JarInJarDependencyLocator.java | 99 +++++++++++++----- server_files/args.txt | 1 + .../META-INF/jarjar/ForgeJiJTestMod-1.5.jar | Bin 0 -> 20171 bytes .../resources/META-INF/jarjar/metadata.json | 6 +- 9 files changed, 174 insertions(+), 46 deletions(-) create mode 100644 buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/TeamcityRequests.groovy create mode 100644 src/test/resources/META-INF/jarjar/ForgeJiJTestMod-1.5.jar diff --git a/build.gradle b/build.gradle index 930895dcc0..fc1b671c0d 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'net.minecraftforge.gradle:ForgeGradle:[5.1.39,)' + classpath 'net.minecraftforge.gradle:ForgeGradle:[5.1.39,5.2.0)' } } @@ -59,23 +59,22 @@ ext { COREMODS_VERSION = '5.0.1' EVENTBUS_VERSION = '5.0.3' MODLAUNCHER_VERSION = '9.1.3' - SECUREJARHANDLER_VERSION = '1.0.3' + SECUREJARHANDLER_VERSION = '1.0.8' BOOTSTRAPLAUNCHER_VERSION = '1.0.0' - ASM_VERSION = '9.2' + ASM_VERSION = '9.5' INSTALLER_VERSION = '2.1.+' MIXIN_VERSION = '0.8.5' - JARJAR_VERSION = '0.3.0' + JARJAR_VERSION = '0.3.19' GIT_INFO = gradleutils.gitInfo VERSION = gradleutils.getFilteredMCTagOffsetBranchVersion(true, '[0-9]', MC_VERSION) // FML_VERSION = gradleutils.getFilteredMCTagOffsetBranchVersion(true, 'FML', MC_VERSION) FORGE_VERSION = VERSION.substring(MC_VERSION.length() + 1) - SPECIAL_SOURCE = 'net.md-5:SpecialSource:1.10.0' BINPATCH_TOOL = 'net.minecraftforge:binarypatcher:1.0.12:fatjar' INSTALLER_TOOLS = 'net.minecraftforge:installertools:1.3.0' JAR_SPLITTER = 'net.minecraftforge:jarsplitter:1.1.4' - FART = 'net.minecraftforge:ForgeAutoRenamingTool:0.1.22:all' + FART = 'net.minecraftforge:ForgeAutoRenamingTool:0.1.25:all' MIN_TAG_FOR_CHANGELOG = "39.0" LAST_RB = GIT_INFO.tag.rsplit('.', 1)[1] as int >= 1 ? GIT_INFO.tag + '.0' : null } @@ -258,6 +257,7 @@ def sharedDeps = { moduleonly "org.ow2.asm:asm-util:${ASM_VERSION}" moduleonly "org.ow2.asm:asm-analysis:${ASM_VERSION}" moduleonly "cpw.mods:bootstraplauncher:${BOOTSTRAPLAUNCHER_VERSION}" + moduleonly "net.minecraftforge:JarJarFileSystems:${JARJAR_VERSION}" installer "cpw.mods:securejarhandler:${SECUREJARHANDLER_VERSION}" installer "org.ow2.asm:asm:${ASM_VERSION}" @@ -281,7 +281,8 @@ def sharedDeps = { installer 'org.jline:jline-terminal:3.12.+' //Dep of TerminaalConsoleeAppender installer "org.spongepowered:mixin:${MIXIN_VERSION}" installer 'org.openjdk.nashorn:nashorn-core:15.3' - installer "net.minecraftforge:JarJar:${JARJAR_VERSION}" + installer "net.minecraftforge:JarJarSelector:${JARJAR_VERSION}" + installer "net.minecraftforge:JarJarMetadata:${JARJAR_VERSION}" /* installer 'org.lwjgl:lwjgl:3.2.2' @@ -450,6 +451,7 @@ def sharedFmlonlyForge = { Project prj -> run.jvmArgs '--add-modules', 'ALL-MODULE-PATH' // Additions to these JVM module args should be mirrored to server_files/args.txt and other similar blocks in the buildscript run.jvmArgs '--add-opens', 'java.base/java.util.jar=cpw.mods.securejarhandler' + run.jvmArgs '--add-opens', 'java.base/java.lang.invoke=cpw.mods.securejarhandler' run.jvmArgs '--add-exports', 'java.base/sun.security.util=cpw.mods.securejarhandler' run.jvmArgs '--add-exports', 'jdk.naming.dns/com.sun.jndi.dns=java.naming' } @@ -640,6 +642,7 @@ project(':fmlonly') { '--add-modules', 'ALL-MODULE-PATH', // Additions to these JVM module args should be mirrored to server_files/args.txt and other similar blocks in the buildscript '--add-opens', 'java.base/java.util.jar=cpw.mods.securejarhandler', + '--add-opens', 'java.base/java.lang.invoke=cpw.mods.securejarhandler', '--add-exports', 'java.base/sun.security.util=cpw.mods.securejarhandler', '--add-exports', 'jdk.naming.dns/com.sun.jndi.dns=java.naming' ] @@ -1200,7 +1203,7 @@ project(':forge') { } def baseForgeVersionProperty = project.objects.property(String) - baseForgeVersionProperty.set(project.provider { getLatestForgeVersion(MC_VERSION) }.map { MC_VERSION + '-' + it }) + baseForgeVersionProperty.set(project.provider { TeamcityRequests.attemptFindBase(rootDir) ?: getLatestForgeVersion(MC_VERSION) }) baseForgeVersionProperty.finalizeValueOnRead() def jarCompatibilityTaskSetup = { task -> task.group = 'jar compatibility' @@ -1277,6 +1280,7 @@ project(':forge') { '--add-modules', 'ALL-MODULE-PATH', // Additions to these JVM module args should be mirrored to server_files/args.txt and other similar blocks in the buildscript '--add-opens', 'java.base/java.util.jar=cpw.mods.securejarhandler', + '--add-opens', 'java.base/java.lang.invoke=cpw.mods.securejarhandler', '--add-exports', 'java.base/sun.security.util=cpw.mods.securejarhandler', '--add-exports', 'jdk.naming.dns/com.sun.jndi.dns=java.naming' ] diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index 19adbbe7a5..110556af31 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -1,11 +1,12 @@ repositories { maven { url = 'https://maven.minecraftforge.net/' } - mavenCentral() + mavenCentral() } dependencies { - implementation 'org.ow2.asm:asm:9.1' - implementation 'org.ow2.asm:asm-tree:9.1' - implementation 'net.minecraftforge:srgutils:0.4.+' + implementation 'org.ow2.asm:asm:9.5' + implementation 'org.ow2.asm:asm-tree:9.5' + implementation 'net.minecraftforge:srgutils:0.5.+' implementation 'commons-io:commons-io:2.8.0' implementation 'com.google.code.gson:gson:2.10' + implementation 'org.eclipse.jgit:org.eclipse.jgit:6.5.0.202303070854-r' } \ No newline at end of file diff --git a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/TeamcityRequests.groovy b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/TeamcityRequests.groovy new file mode 100644 index 0000000000..d6b73c32fc --- /dev/null +++ b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/TeamcityRequests.groovy @@ -0,0 +1,74 @@ +package net.minecraftforge.forge.tasks + +import com.google.gson.Gson +import com.google.gson.reflect.TypeToken +import groovy.transform.CompileStatic +import org.eclipse.jgit.api.Git + +import javax.annotation.Nullable +import java.util.stream.Collectors + +@CompileStatic +class TeamcityRequests { + public static final Gson GSON = new Gson() + + @Nullable + static T jsonRequest(TypeToken clazz, String url) throws Exception { + final HttpURLConnection conn = (HttpURLConnection) URI.create(url).toURL().openConnection() + conn.setRequestProperty('Accept', 'application/json') + conn.setReadTimeout(5 * 1000) + conn.setConnectTimeout(5 * 1000) + conn.connect() + + if (conn.responseCode !== 200) { + return null + } + + try (final InputStream is = conn.getInputStream()) { + GSON.fromJson(new InputStreamReader(is), clazz.getType()) + } + } + + @Nullable + static Map buildsByCommit() throws IOException { + final Map builds = [:] + jsonRequest(new TypeToken() {}, "https://teamcity.minecraftforge.net/guestAuth/app/rest/builds?fields=build:(revisions,number)&locator=buildType:(id:MinecraftForge_MinecraftForge_MinecraftForge_MinecraftForge__Build),count:300,status:SUCCESS") + ?.build?.forEach { + it.revisions.revision.each { rev -> builds[rev.version] = it } + } + return builds + } + + @Nullable + static String attemptFindBase(File gitPath) { + try (final git = Git.open(gitPath)) { + final Map buildByCommit = buildsByCommit() + for (final commit in git.log().setMaxCount(100).call()) { + // Find the first commit which was built on CI + final build = buildByCommit[commit.id.name] + if (build) { + return build.number + } + } + } catch (Exception ignored) {} + return null + } + + static final class Builds { + public List build + } + + static final class Build { + public String number + public Revisions revisions + } + + static final class Revisions { + public int count + public List revision + } + + static final class Revision { + public String version + } +} \ No newline at end of file diff --git a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/Util.groovy b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/Util.groovy index 76b171b7d1..a98526aff1 100644 --- a/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/Util.groovy +++ b/buildSrc/src/main/groovy/net/minecraftforge/forge/tasks/Util.groovy @@ -143,8 +143,9 @@ public class Util { } } - public static String getLatestForgeVersion(mcVersion) { - def json = new JsonSlurper().parseText(new URL("https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json").getText("UTF-8")) - return json.promos["$mcVersion-latest"] + static String getLatestForgeVersion(mcVersion) { + final json = new JsonSlurper().parseText(new URL('https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json').getText('UTF-8')) + final ver = json.promos["$mcVersion-latest"] + ver === null ? null : (mcVersion + '-' + ver) } } \ No newline at end of file diff --git a/fmlloader/build.gradle b/fmlloader/build.gradle index 7249d4cc99..f461c8f150 100644 --- a/fmlloader/build.gradle +++ b/fmlloader/build.gradle @@ -27,7 +27,9 @@ dependencies { api('com.electronwill.night-config:toml:3.6.4') api('cpw.mods:modlauncher:9.0.+') api('net.minecraftforge:coremods:5.0.+') - api "net.minecraftforge:JarJar:${JARJAR_VERSION}" + api "net.minecraftforge:JarJarSelector:${JARJAR_VERSION}" + api "net.minecraftforge:JarJarMetadata:${JARJAR_VERSION}" + implementation("cpw.mods:securejarhandler:${SECUREJARHANDLER_VERSION}") implementation('org.apache.logging.log4j:log4j-core:2.17.0') annotationProcessor('org.apache.logging.log4j:log4j-core:2.17.0') diff --git a/fmlloader/src/main/java/net/minecraftforge/fml/loading/moddiscovery/JarInJarDependencyLocator.java b/fmlloader/src/main/java/net/minecraftforge/fml/loading/moddiscovery/JarInJarDependencyLocator.java index 8cf7a832e2..3b81f03715 100644 --- a/fmlloader/src/main/java/net/minecraftforge/fml/loading/moddiscovery/JarInJarDependencyLocator.java +++ b/fmlloader/src/main/java/net/minecraftforge/fml/loading/moddiscovery/JarInJarDependencyLocator.java @@ -5,6 +5,7 @@ package net.minecraftforge.fml.loading.moddiscovery; +import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import net.minecraftforge.fml.loading.EarlyLoadingException; import net.minecraftforge.forgespi.language.IModInfo; @@ -16,10 +17,15 @@ import org.apache.maven.artifact.versioning.ArtifactVersion; import org.apache.maven.artifact.versioning.VersionRange; import org.jetbrains.annotations.NotNull; +import java.net.URI; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; +import java.nio.file.Path; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; @@ -34,20 +40,16 @@ public class JarInJarDependencyLocator extends AbstractJarFileDependencyLocator } @Override - public List scanMods(final Iterable loadedMods) { + public List scanMods(final Iterable loadedMods) + { final List sources = Lists.newArrayList(); loadedMods.forEach(sources::add); - final List dependenciesToLoad = JarSelector.detectAndSelect( - sources, - this::loadResourceFromModFile, - this::loadModFileFrom, - this::identifyMod, - this::exception - ); + final List dependenciesToLoad = JarSelector.detectAndSelect(sources, this::loadResourceFromModFile, this::loadModFileFrom, this::identifyMod, this::exception); - if (dependenciesToLoad.isEmpty()) { + if (dependenciesToLoad.isEmpty()) + { LOGGER.info("No dependencies to load found. Skipping!"); return Collections.emptyList(); } @@ -68,46 +70,87 @@ public class JarInJarDependencyLocator extends AbstractJarFileDependencyLocator return IModFile.Type.GAMELIBRARY.name(); } + @SuppressWarnings("resource") + @Override + protected Optional loadModFileFrom(final IModFile file, final Path path) + { + try + { + final Path pathInModFile = file.findResource(path.toString()); + final URI filePathUri = new URI("jij:" + (pathInModFile.toAbsolutePath().toUri().getRawSchemeSpecificPart())).normalize(); + final Map outerFsArgs = ImmutableMap.of("packagePath", pathInModFile); + final FileSystem zipFS = FileSystems.newFileSystem(filePathUri, outerFsArgs); + final Path pathInFS = zipFS.getPath("/"); + return createMod(pathInFS); + } + catch (Exception e) + { + LOGGER.error("Failed to load mod file {} from {}", path, file.getFileName()); + + throw new RuntimeException("Failed to load mod file " + file.getFileName(), e); + } + } + protected EarlyLoadingException exception(Collection> failedDependencies) { final List errors = failedDependencies.stream() - .filter(entry -> !entry.sources().isEmpty()) //Should never be the case, but just to be sure - .map(entry -> new EarlyLoadingException.ExceptionData( - getErrorTranslationKey(entry), - entry.identifier().group() + ":" + entry.identifier().artifact(), - entry.sources() - .stream() - .flatMap(this::getModWithVersionRangeStream) - .map(this::formatError) - .collect(Collectors.joining(", ")))).toList(); + .filter(entry -> !entry.sources().isEmpty()) //Should never be the case, but just to be sure + .map(this::buildExceptionData) + .toList(); return new EarlyLoadingException(failedDependencies.size() + " Dependency restrictions were not met.", null, errors); } + @NotNull + private EarlyLoadingException.ExceptionData buildExceptionData(final JarSelector.ResolutionFailureInformation entry) + { + return new EarlyLoadingException.ExceptionData( + getErrorTranslationKey(entry), + entry.identifier().group() + ":" + entry.identifier().artifact(), + entry.sources() + .stream() + .flatMap(this::getModWithVersionRangeStream) + .map(this::formatError) + .collect(Collectors.joining(", ")) + ); + } + @NotNull private String getErrorTranslationKey(final JarSelector.ResolutionFailureInformation entry) { - return entry.failureReason() == JarSelector.FailureReason.VERSION_RESOLUTION_FAILED - ? "fml.dependencyloading.conflictingdependencies" - : "fml.dependencyloading.mismatchedcontaineddependencies"; + return entry.failureReason() == JarSelector.FailureReason.VERSION_RESOLUTION_FAILED ? + "fml.dependencyloading.conflictingdependencies" : + "fml.dependencyloading.mismatchedcontaineddependencies"; } @NotNull private Stream getModWithVersionRangeStream(final JarSelector.SourceWithRequestedVersionRange file) { - return file.sources().stream().map(IModFile::getModFileInfo) - .flatMap(modFileInfo -> modFileInfo.getMods().stream()) - .map(modInfo -> new ModWithVersionRange(modInfo, file.requestedVersionRange(), file.includedVersion())); + return file.sources() + .stream() + .map(IModFile::getModFileInfo) + .flatMap(modFileInfo -> modFileInfo.getMods().stream()) + .map(modInfo -> new ModWithVersionRange(modInfo, file.requestedVersionRange(), file.includedVersion())); } @NotNull private String formatError(final ModWithVersionRange modWithVersionRange) { - return "\u00a7e" + modWithVersionRange.modInfo().getModId() + "\u00a7r - \u00a74" - + modWithVersionRange.versionRange().toString() + "\u00a74 - \u00a72" - + modWithVersionRange.artifactVersion().toString() + "\u00a72"; + return "\u00a7e" + modWithVersionRange.modInfo().getModId() + "\u00a7r - \u00a74" + modWithVersionRange.versionRange().toString() + "\u00a74 - \u00a72" + modWithVersionRange.artifactVersion().toString() + "\u00a72"; } - private record ModWithVersionRange(IModInfo modInfo, VersionRange versionRange, ArtifactVersion artifactVersion) {} + @Override + protected String identifyMod(final IModFile modFile) + { + if (modFile.getModFileInfo() == null || modFile.getModInfos().isEmpty()) + { + return modFile.getFileName(); + } + + return modFile.getModInfos().stream().map(IModInfo::getModId).collect(Collectors.joining()); + } + + private record ModWithVersionRange(IModInfo modInfo, VersionRange versionRange, ArtifactVersion artifactVersion) + {} } diff --git a/server_files/args.txt b/server_files/args.txt index 6dd9824f33..84df94b0e5 100644 --- a/server_files/args.txt +++ b/server_files/args.txt @@ -1,6 +1,7 @@ -p @MODULE_PATH@ --add-modules @MODULES@ --add-opens java.base/java.util.jar=cpw.mods.securejarhandler +--add-opens java.base/java.lang.invoke=cpw.mods.securejarhandler --add-exports java.base/sun.security.util=cpw.mods.securejarhandler --add-exports jdk.naming.dns/com.sun.jndi.dns=java.naming -Djava.net.preferIPv6Addresses=system diff --git a/src/test/resources/META-INF/jarjar/ForgeJiJTestMod-1.5.jar b/src/test/resources/META-INF/jarjar/ForgeJiJTestMod-1.5.jar new file mode 100644 index 0000000000000000000000000000000000000000..be2cb6620a65a5420f623a39b28efd4bf29dfaf3 GIT binary patch literal 20171 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 gmn^Co3qm57R1mbaXfFVORl