diff --git a/build.gradle b/build.gradle index faa4ce8a..e7147e1b 100644 --- a/build.gradle +++ b/build.gradle @@ -9,6 +9,8 @@ changelog { disableAutomaticPublicationRegistration() } +logger.lifecycle("FML version {}", gradleutils.getTagOffsetVersion()) + allprojects { apply plugin: 'java-library' apply plugin: 'jacoco' diff --git a/gradle.properties b/gradle.properties index b2792257..0de44f1f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ accesstransformers_version=9.0.0 coremods_version=6.0.0 eventbus_version=7.0.16 modlauncher_version=10.0.9 -securejarhandler_version=2.1.10 +securejarhandler_version=2.1.23 bootstraplauncher_version=1.1.2 asm_version=9.5 mixin_version=0.8.5 diff --git a/loader/src/main/java/net/neoforged/fml/loading/ModDirTransformerDiscoverer.java b/loader/src/main/java/net/neoforged/fml/loading/ModDirTransformerDiscoverer.java index fc599e97..0609959d 100644 --- a/loader/src/main/java/net/neoforged/fml/loading/ModDirTransformerDiscoverer.java +++ b/loader/src/main/java/net/neoforged/fml/loading/ModDirTransformerDiscoverer.java @@ -6,6 +6,8 @@ package net.neoforged.fml.loading; import com.mojang.logging.LogUtils; +import cpw.mods.jarhandling.JarContentsBuilder; +import cpw.mods.jarhandling.JarMetadata; import cpw.mods.jarhandling.SecureJar; import cpw.mods.modlauncher.api.LamdbaExceptionUtils; import cpw.mods.modlauncher.api.NamedPath; @@ -14,7 +16,6 @@ import java.io.IOException; import java.io.UncheckedIOException; -import java.lang.module.ModuleDescriptor; import java.nio.file.AccessDeniedException; import java.nio.file.FileAlreadyExistsException; import java.nio.file.Files; @@ -81,22 +82,26 @@ private static void scan(final Path gameDirectory) { // Skip if the mods dir doesn't exist yet. return; } - try (var walk = Files.walk(modsDir, 1)){ - walk.forEach(ModDirTransformerDiscoverer::visitFile); + try (var walk = Files.walk(modsDir, 1)) { + // Collect to list first, and then parallel stream it. + // Before JDK 19, Files.walk streams are not parallelized efficiently for small numbers of elements. + // See https://bugs.openjdk.org/browse/JDK-8280915. + walk.toList().parallelStream() + .filter(ModDirTransformerDiscoverer::shouldLoadInServiceLayer) + .forEachOrdered(p -> found.add(new NamedPath(p.getFileName().toString(), p))); } catch (IOException | IllegalStateException ioe) { LOGGER.error("Error during early discovery", ioe); } } - private static void visitFile(Path path) { - if (!Files.isRegularFile(path)) return; - if (!path.toString().endsWith(".jar")) return; - if (LamdbaExceptionUtils.uncheck(() -> Files.size(path)) == 0) return; + private static boolean shouldLoadInServiceLayer(Path path) { + if (!Files.isRegularFile(path)) return false; + if (!path.toString().endsWith(".jar")) return false; + if (LamdbaExceptionUtils.uncheck(() -> Files.size(path)) == 0) return false; - SecureJar jar = SecureJar.from(path); - jar.moduleDataProvider().descriptor().provides().stream() - .map(ModuleDescriptor.Provides::service) - .filter(SERVICES::contains) - .forEach(s -> found.add(new NamedPath(s, path))); + JarMetadata metadata = JarMetadata.from(new JarContentsBuilder().paths(path).build()); + return metadata.providers().stream() + .map(SecureJar.Provider::serviceName) + .anyMatch(SERVICES::contains); } } diff --git a/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/AbstractModProvider.java b/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/AbstractModProvider.java index f49d185d..3af0316d 100644 --- a/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/AbstractModProvider.java +++ b/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/AbstractModProvider.java @@ -6,7 +6,7 @@ package net.neoforged.fml.loading.moddiscovery; import com.mojang.logging.LogUtils; -import cpw.mods.jarhandling.JarMetadata; +import cpw.mods.jarhandling.JarContentsBuilder; import cpw.mods.jarhandling.SecureJar; import net.neoforged.fml.loading.LogMarkers; import net.neoforged.neoforgespi.language.IConfigurable; @@ -25,7 +25,6 @@ import java.util.Map; import java.util.Optional; import java.util.function.Function; -import java.util.jar.Manifest; public abstract class AbstractModProvider implements IModProvider { @@ -34,30 +33,27 @@ public abstract class AbstractModProvider implements IModProvider protected static final String MANIFEST = "META-INF/MANIFEST.MF"; 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), - null, - path - ); + var jarContents = new JarContentsBuilder() + .paths(path) + .build(); IModFile mod; - var type = sj.moduleDataProvider().getManifest().getMainAttributes().getValue(ModFile.TYPE); + var type = jarContents.getManifest().getMainAttributes().getValue(ModFile.TYPE); if (type == null) { type = getDefaultJarModType(); } - if (sj.moduleDataProvider().findFile(MODS_TOML).isPresent()) { + if (jarContents.findFile(MODS_TOML).isPresent()) { LOGGER.debug(LogMarkers.SCAN, "Found {} mod of type {}: {}", MODS_TOML, type, path); - mod = new ModFile(sj, this, ModFileParser::modsTomlParser); + var mjm = new ModJarMetadata(jarContents); + mod = new ModFile(SecureJar.from(jarContents, mjm), this, ModFileParser::modsTomlParser); + mjm.setModFile(mod); } else if (type != null) { LOGGER.debug(LogMarkers.SCAN, "Found {} mod of type {}: {}", MANIFEST, type, path); - mod = new ModFile(sj, this, this::manifestParser, type); + mod = new ModFile(SecureJar.from(jarContents), this, this::manifestParser, type); } else { return new IModLocator.ModFileOrException(null, new ModFileLoadingException("Invalid mod file found "+ Arrays.toString(path))); } - mjm.setModFile(mod); return new IModLocator.ModFileOrException(mod, null); } diff --git a/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/ExplodedDirectoryLocator.java b/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/ExplodedDirectoryLocator.java index b994008f..0888dc80 100644 --- a/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/ExplodedDirectoryLocator.java +++ b/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/ExplodedDirectoryLocator.java @@ -6,6 +6,8 @@ package net.neoforged.fml.loading.moddiscovery; import com.mojang.logging.LogUtils; +import cpw.mods.jarhandling.JarContentsBuilder; +import cpw.mods.jarhandling.SecureJar; import net.neoforged.fml.loading.LogMarkers; import net.neoforged.neoforgespi.locating.IModFile; import net.neoforged.neoforgespi.locating.IModLocator; @@ -31,12 +33,17 @@ public record ExplodedMod(String modid, List paths) {} @Override public List scanMods() { - explodedMods.forEach(explodedMod -> - ModJarMetadata.buildFile(this, - jar->jar.moduleDataProvider().findFile("/META-INF/mods.toml").isPresent(), - null, - explodedMod.paths().toArray(Path[]::new)) - .ifPresentOrElse(f->mods.put(explodedMod, f), () -> LOGGER.warn(LogMarkers.LOADING, "Failed to find exploded resource mods.toml in directory {}", explodedMod.paths().get(0).toString()))); + explodedMods.forEach(explodedMod -> { + var jarContents = new JarContentsBuilder().paths(explodedMod.paths().toArray(Path[]::new)).build(); + if (jarContents.findFile(AbstractModProvider.MODS_TOML).isPresent()) { + var mjm = new ModJarMetadata(jarContents); + var mf = new ModFile(SecureJar.from(jarContents, mjm), this, ModFileParser::modsTomlParser); + mjm.setModFile(mf); + mods.put(explodedMod, mf); + } else { + 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(); } diff --git a/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/MinecraftLocator.java b/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/MinecraftLocator.java index a506458d..c5cd1c9e 100644 --- a/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/MinecraftLocator.java +++ b/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/MinecraftLocator.java @@ -7,6 +7,7 @@ import com.electronwill.nightconfig.core.Config; import com.mojang.logging.LogUtils; +import cpw.mods.jarhandling.JarContentsBuilder; import cpw.mods.jarhandling.SecureJar; import net.neoforged.fml.loading.FMLLoader; import net.neoforged.fml.loading.LogMarkers; @@ -34,7 +35,14 @@ public class MinecraftLocator extends AbstractModProvider implements IModLocator public List scanMods() { final var launchHandler = FMLLoader.getLaunchHandler(); var baseMC = launchHandler.getMinecraftPaths(); - var mcjar = ModJarMetadata.buildFile(j->ModFileFactory.FACTORY.build(j, this, this::buildMinecraftTOML), j->true, baseMC.minecraftFilter(), baseMC.minecraftPaths().toArray(Path[]::new)).orElseThrow(); + var mcJarContents = new JarContentsBuilder() + .paths(baseMC.minecraftPaths().toArray(Path[]::new)) + .pathFilter(baseMC.minecraftFilter()) + .build(); + var mcJarMetadata = new ModJarMetadata(mcJarContents); + var mcSecureJar = SecureJar.from(mcJarContents, mcJarMetadata); + var mcjar = ModFileFactory.FACTORY.build(mcSecureJar, this, this::buildMinecraftTOML); + mcJarMetadata.setModFile(mcjar); var artifacts = baseMC.otherArtifacts().stream() .map(SecureJar::from) .map(sj -> new ModFile(sj, this, ModFileParser::modsTomlParser)) diff --git a/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/ModFile.java b/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/ModFile.java index 78d2a53c..8d410d71 100644 --- a/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/ModFile.java +++ b/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/ModFile.java @@ -35,16 +35,8 @@ import java.util.jar.Manifest; public class ModFile implements IModFile { - // Mods either must have a mods.toml or a manifest. We can no longer just put any jar on the classpath. - @Deprecated(forRemoval = true, since = "1.18") - public static final Manifest DEFAULTMANIFEST; private static final Logger LOGGER = LogUtils.getLogger(); - static { - DEFAULTMANIFEST = new Manifest(); - DEFAULTMANIFEST.getMainAttributes().putValue("FMLModType", "MOD"); - } - private final String jarVersion; private final ModFileFactory.ModFileInfoParser parser; private Map fileProperties; diff --git a/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/ModJarMetadata.java b/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/ModJarMetadata.java index ac04b7f9..2eb443b1 100644 --- a/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/ModJarMetadata.java +++ b/loader/src/main/java/net/neoforged/fml/loading/moddiscovery/ModJarMetadata.java @@ -5,50 +5,20 @@ package net.neoforged.fml.loading.moddiscovery; +import cpw.mods.jarhandling.JarContents; import cpw.mods.jarhandling.JarMetadata; -import cpw.mods.jarhandling.SecureJar; +import cpw.mods.jarhandling.LazyJarMetadata; import net.neoforged.neoforgespi.locating.IModFile; -import net.neoforged.neoforgespi.locating.IModLocator; import java.lang.module.ModuleDescriptor; -import java.nio.file.Path; import java.util.Objects; -import java.util.Optional; -import java.util.function.BiPredicate; -import java.util.function.Function; -import java.util.function.Predicate; -public final class ModJarMetadata implements JarMetadata { +public final class ModJarMetadata extends LazyJarMetadata implements JarMetadata { + private final JarContents jarContents; private IModFile modFile; - private ModuleDescriptor descriptor; - // TODO: Remove helper functions to cleanup api - @Deprecated(forRemoval = true, since="1.18") - static Optional buildFile(IModLocator locator, Predicate jarTest, BiPredicate filter, Path... files) { - return buildFile(j->new ModFile(j, locator, ModFileParser::modsTomlParser), jarTest, filter, files); - } - - // TODO: Remove helper functions to cleanup api - @Deprecated(forRemoval = true, since="1.18") - static IModFile buildFile(IModLocator locator, Path... files) { - return buildFile(locator, j->true, null, files).orElseThrow(()->new IllegalArgumentException("Failed to find valid JAR file")); - } - - // TODO: Remove helper functions to cleanup api - @Deprecated(forRemoval = true, since="1.18") - static Optional buildFile(Function mfConstructor, Predicate jarTest, BiPredicate filter, Path... files) { - var mjm = new ModJarMetadata(); - var sj = SecureJar.from(()->ModFile.DEFAULTMANIFEST, j->mjm, filter, files); - if (jarTest.test(sj)) { - var mf = mfConstructor.apply(sj); - mjm.setModFile(mf); - return Optional.of(mf); - } else { - return Optional.empty(); - } - } - - ModJarMetadata() { + ModJarMetadata(JarContents jarContents) { + this.jarContents = jarContents; } public void setModFile(IModFile file) { @@ -66,17 +36,15 @@ public String version() { } @Override - public ModuleDescriptor descriptor() { - if (descriptor != null) return descriptor; + public ModuleDescriptor computeDescriptor() { var bld = ModuleDescriptor.newAutomaticModule(name()) .version(version()) - .packages(modFile.getSecureJar().getPackages()); - modFile.getSecureJar().getProviders().stream() + .packages(jarContents.getPackagesExcluding("assets", "data")); + jarContents.getMetaInfServices().stream() .filter(p -> !p.providers().isEmpty()) .forEach(p -> bld.provides(p.serviceName(), p.providers())); modFile.getModFileInfo().usesServices().forEach(bld::uses); - descriptor = bld.build(); - return descriptor; + return bld.build(); } public IModFile modFile() { diff --git a/loader/src/main/java/net/neoforged/fml/loading/targets/CommonDevLaunchHandler.java b/loader/src/main/java/net/neoforged/fml/loading/targets/CommonDevLaunchHandler.java index be3431e1..55bc5cfb 100644 --- a/loader/src/main/java/net/neoforged/fml/loading/targets/CommonDevLaunchHandler.java +++ b/loader/src/main/java/net/neoforged/fml/loading/targets/CommonDevLaunchHandler.java @@ -5,7 +5,9 @@ package net.neoforged.fml.loading.targets; +import cpw.mods.jarhandling.JarContentsBuilder; import cpw.mods.jarhandling.SecureJar; +import cpw.mods.niofs.union.UnionPathFilter; import net.neoforged.fml.loading.FileUtils; import java.nio.file.Path; @@ -13,7 +15,6 @@ import java.util.Arrays; import java.util.List; import java.util.Optional; -import java.util.function.BiPredicate; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Stream; @@ -88,12 +89,12 @@ protected static Path findJarOnClasspath(String[] classpath, String match) { .orElseThrow(() -> new IllegalStateException("Could not find " + match + " in classpath")); } - protected BiPredicate getMcFilter(Path extra, List minecraft, Stream.Builder> mods) { + protected UnionPathFilter getMcFilter(Path extra, List minecraft, Stream.Builder> mods) { final var packages = getExcludedPrefixes(); final var extraPath = extra.toString().replace('\\', '/'); // We serve everything, except for things in the forge packages. - BiPredicate mcFilter = (path, base) -> { + UnionPathFilter mcFilter = (path, base) -> { if (base.equals(extraPath) || path.endsWith("/")) return true; for (var pkg : packages) @@ -102,12 +103,15 @@ protected BiPredicate getMcFilter(Path extra, List minecra }; // We need to separate out our resources/code so that we can show up as a different data pack. - var modJar = SecureJar.from((path, base) -> { - if (!path.endsWith(".class")) return true; - for (var pkg : packages) - if (path.startsWith(pkg)) return true; - return false; - }, minecraft.stream().distinct().toArray(Path[]::new)); + var modJar = SecureJar.from(new JarContentsBuilder() + .pathFilter((path, base) -> { + if (!path.endsWith(".class")) return true; + for (var pkg : packages) + if (path.startsWith(pkg)) return true; + return false; + }) + .paths(minecraft.stream().distinct().toArray(Path[]::new)) + .build()); //modJar.getPackages().stream().sorted().forEach(System.out::println); mods.add(List.of(modJar.getRootPath())); diff --git a/loader/src/main/java/net/neoforged/fml/loading/targets/CommonLaunchHandler.java b/loader/src/main/java/net/neoforged/fml/loading/targets/CommonLaunchHandler.java index cb6ac309..ecdf5416 100644 --- a/loader/src/main/java/net/neoforged/fml/loading/targets/CommonLaunchHandler.java +++ b/loader/src/main/java/net/neoforged/fml/loading/targets/CommonLaunchHandler.java @@ -9,6 +9,7 @@ import cpw.mods.modlauncher.api.ILaunchHandlerService; import cpw.mods.modlauncher.api.ITransformingClassLoaderBuilder; import cpw.mods.modlauncher.api.ServiceRunner; +import cpw.mods.niofs.union.UnionPathFilter; import net.neoforged.fml.loading.FMLLoader; import net.neoforged.fml.loading.FileUtils; import net.neoforged.fml.loading.LogMarkers; @@ -29,11 +30,10 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; -import java.util.function.BiPredicate; import java.util.stream.Collectors; public abstract class CommonLaunchHandler implements ILaunchHandlerService { - public record LocatedPaths(List minecraftPaths, BiPredicate minecraftFilter, List> otherModPaths, List otherArtifacts) {} + public record LocatedPaths(List minecraftPaths, UnionPathFilter minecraftFilter, List> otherModPaths, List otherArtifacts) {} protected static final Logger LOGGER = LogUtils.getLogger(); diff --git a/loader/src/main/java/net/neoforged/fml/loading/targets/CommonServerLaunchHandler.java b/loader/src/main/java/net/neoforged/fml/loading/targets/CommonServerLaunchHandler.java index 17739c25..b2fe86d0 100644 --- a/loader/src/main/java/net/neoforged/fml/loading/targets/CommonServerLaunchHandler.java +++ b/loader/src/main/java/net/neoforged/fml/loading/targets/CommonServerLaunchHandler.java @@ -5,6 +5,7 @@ package net.neoforged.fml.loading.targets; +import cpw.mods.jarhandling.JarContentsBuilder; import cpw.mods.jarhandling.SecureJar; import net.neoforged.fml.loading.FMLLoader; import net.neoforged.fml.loading.LibraryFinder; @@ -13,7 +14,6 @@ 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 { @@ -31,28 +31,23 @@ public LocatedPaths getMinecraftPaths() { final var vers = FMLLoader.versionInfo(); var mc = LibraryFinder.findPathForMaven("net.minecraft", "server", "", "srg", vers.mcAndNeoFormVersion()); var mcextra = LibraryFinder.findPathForMaven("net.minecraft", "server", "", "extra", vers.mcAndNeoFormVersion()); - 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 filter = (path, base) -> true; - BiPredicate nullFilter = filter; + var mcextra_filtered = SecureJar.from(new JarContentsBuilder() + // We only want it for its resources. So filter everything else out. + .pathFilter((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/")); + }) + .paths(mcextra) + .build()); var mcstream = Stream.builder().add(mc).add(mcextra_filtered.getRootPath()); var modstream = Stream.>builder(); - filter = processMCStream(vers, mcstream, filter, modstream); + processMCStream(vers, mcstream, modstream); - // use this hack instead of setting filter to null initially for backwards compatibility if anything overrides - // processMCStream with a custom filter - if (filter == nullFilter) - filter = null; - - return new LocatedPaths(mcstream.build().toList(), filter, modstream.build().toList(), this.getFmlPaths(this.getLegacyClasspath())); + return new LocatedPaths(mcstream.build().toList(), null, modstream.build().toList(), this.getFmlPaths(this.getLegacyClasspath())); } - protected abstract BiPredicate processMCStream(VersionInfo versionInfo, Stream.Builder mc, BiPredicate filter, Stream.Builder> mods); + protected abstract void processMCStream(VersionInfo versionInfo, Stream.Builder mc, Stream.Builder> mods); } diff --git a/loader/src/main/java/net/neoforged/fml/loading/targets/FMLServerLaunchHandler.java b/loader/src/main/java/net/neoforged/fml/loading/targets/FMLServerLaunchHandler.java index 35902b00..512b1de2 100644 --- a/loader/src/main/java/net/neoforged/fml/loading/targets/FMLServerLaunchHandler.java +++ b/loader/src/main/java/net/neoforged/fml/loading/targets/FMLServerLaunchHandler.java @@ -10,16 +10,14 @@ import java.nio.file.Path; import java.util.List; -import java.util.function.BiPredicate; import java.util.stream.Stream; public class FMLServerLaunchHandler extends CommonServerLaunchHandler { @Override public String name() { return "fmlserver"; } @Override - protected BiPredicate processMCStream(VersionInfo versionInfo, Stream.Builder mc, BiPredicate filter, Stream.Builder> mods) { + protected void processMCStream(VersionInfo versionInfo, Stream.Builder mc, Stream.Builder> mods) { var fmlonly = LibraryFinder.findPathForMaven("net.neoforged.fml", "fmlonly", "", "universal", versionInfo.mcAndFmlVersion()); mods.add(List.of(fmlonly)); - return filter; } } diff --git a/loader/src/main/java/net/neoforged/fml/loading/targets/ForgeServerLaunchHandler.java b/loader/src/main/java/net/neoforged/fml/loading/targets/ForgeServerLaunchHandler.java index 861ac989..f3599b88 100644 --- a/loader/src/main/java/net/neoforged/fml/loading/targets/ForgeServerLaunchHandler.java +++ b/loader/src/main/java/net/neoforged/fml/loading/targets/ForgeServerLaunchHandler.java @@ -10,18 +10,16 @@ import java.nio.file.Path; import java.util.List; -import java.util.function.BiPredicate; import java.util.stream.Stream; public class ForgeServerLaunchHandler extends CommonServerLaunchHandler { @Override public String name() { return "forgeserver"; } @Override - protected BiPredicate processMCStream(VersionInfo versionInfo, Stream.Builder mc, BiPredicate filter, Stream.Builder> mods) { + protected void processMCStream(VersionInfo versionInfo, Stream.Builder mc, Stream.Builder> mods) { var forgepatches = LibraryFinder.findPathForMaven("net.neoforged", "neoforge", "", "server", versionInfo.neoForgeVersion()); var forgejar = LibraryFinder.findPathForMaven("net.neoforged", "neoforge", "", "universal", versionInfo.neoForgeVersion()); mc.add(forgepatches); mods.add(List.of(forgejar)); - return filter; } }