diff --git a/src/main/java/net/minecraftforge/fml/LaunchTesting.java b/src/main/java/net/minecraftforge/fml/LaunchTesting.java index c34b865a37..020c5e7499 100644 --- a/src/main/java/net/minecraftforge/fml/LaunchTesting.java +++ b/src/main/java/net/minecraftforge/fml/LaunchTesting.java @@ -25,9 +25,10 @@ import org.apache.logging.log4j.core.config.Configurator; public class LaunchTesting { - public static void main(String... args) + public static void main(String... args) throws InterruptedException { Configurator.setRootLevel(Level.DEBUG); Launcher.main("--launchTarget", "fml","--gameDir", "projects/run"); + Thread.sleep(10000); } } diff --git a/src/main/java/net/minecraftforge/fml/loading/FMLJavaModLanguageProvider.java b/src/main/java/net/minecraftforge/fml/loading/FMLJavaModLanguageProvider.java new file mode 100644 index 0000000000..fe23461e66 --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/loading/FMLJavaModLanguageProvider.java @@ -0,0 +1,86 @@ +/* + * Minecraft Forge + * Copyright (c) 2018. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.fml.loading; + +import net.minecraftforge.fml.common.ModContainer; +import net.minecraftforge.fml.loading.moddiscovery.ModFile; +import net.minecraftforge.fml.loading.moddiscovery.ModInfo; +import net.minecraftforge.fml.loading.moddiscovery.ScanResult; +import org.objectweb.asm.Type; + +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static net.minecraftforge.fml.Logging.SCAN; +import static net.minecraftforge.fml.Logging.fmlLog; + +public class FMLJavaModLanguageProvider implements IModLanguageProvider +{ + private static class FMLModTarget implements IModLanguageProvider.IModLanguageLoader { + private final String className; + private final String modId; + + private FMLModTarget(String className, String modId) + { + this.className = className; + this.modId = modId; + } + + public String getModId() + { + return modId; + } + + @Override + public ModContainer loadMod(ModFile file, ClassLoader modClassLoader) + { + return null; + } + } + + public static final Type MODANNOTATION = Type.getType("Lnet/minecraftforge/fml/common/Mod;"); + + @Override + public String name() + { + return "javafml"; + } + + @Override + public Consumer getFileVisitor() { + return scanResult -> { + final Map modTargetMap = scanResult.getAnnotations().stream() + .filter(ad -> ad.getAnnotationType().equals(MODANNOTATION)) + .peek(ad -> fmlLog.debug(SCAN, "Found @Mod class {} with id {}", ad.getClassType().getClassName(), ad.getAnnotationData().get("modid"))) + .map(ad -> new FMLModTarget(ad.getClassType().getClassName(), (String)ad.getAnnotationData().get("modid"))) + .collect(Collectors.toMap(FMLModTarget::getModId, Function.identity())); + modTargetMap.forEach((key, value) -> scanResult.getFile().claimLanguage(key, value)); + }; + } + + @Override + public List buildModContainers(List modFiles, ClassLoader modClassLoader) + { + return null; + } +} diff --git a/src/main/java/net/minecraftforge/fml/loading/FMLLoader.java b/src/main/java/net/minecraftforge/fml/loading/FMLLoader.java index 5aecb99907..0255021420 100644 --- a/src/main/java/net/minecraftforge/fml/loading/FMLLoader.java +++ b/src/main/java/net/minecraftforge/fml/loading/FMLLoader.java @@ -28,25 +28,20 @@ import net.minecraftforge.fml.loading.moddiscovery.ModDiscoverer; import net.minecraftforge.forgespi.ICoreModProvider; import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; import java.util.ServiceLoader; import java.util.Set; import java.util.stream.Collectors; import static net.minecraftforge.fml.Logging.CORE; -import static net.minecraftforge.fml.Logging.LOADING; import static net.minecraftforge.fml.Logging.SCAN; import static net.minecraftforge.fml.Logging.fmlLog; public class FMLLoader { - private static ILaunchPluginService accessTransformer; private static ModDiscoverer modDiscoverer; - private static ICoreModProvider coreMod; + private static ICoreModProvider coreModProvider; + private static LanguageLoadingProvider languageLoadingProvider; static void onInitialLoad(IEnvironment environment, Set otherServices) throws IncompatibleEnvironmentException { @@ -79,9 +74,11 @@ public class FMLLoader throw new IncompatibleEnvironmentException("Multiple coremod libraries found"); } - coreMod = coreModProviders.get(0); - final Package coremodPackage = coreMod.getClass().getPackage(); + coreModProvider = coreModProviders.get(0); + final Package coremodPackage = coreModProvider.getClass().getPackage(); fmlLog.debug(CORE,"FML found CoreMod version : {}", coremodPackage.getImplementationVersion()); + + languageLoadingProvider = new LanguageLoadingProvider(); } public static void load() @@ -90,4 +87,13 @@ public class FMLLoader modDiscoverer = new ModDiscoverer(); modDiscoverer.discoverMods(); } + + public static ICoreModProvider getCoreModProvider() { + return coreModProvider; + } + + public static LanguageLoadingProvider getLanguageLoadingProvider() + { + return languageLoadingProvider; + } } diff --git a/src/main/java/net/minecraftforge/fml/loading/FMLServiceProvider.java b/src/main/java/net/minecraftforge/fml/loading/FMLServiceProvider.java index 3ceec55c0a..0d1c070bab 100644 --- a/src/main/java/net/minecraftforge/fml/loading/FMLServiceProvider.java +++ b/src/main/java/net/minecraftforge/fml/loading/FMLServiceProvider.java @@ -25,14 +25,17 @@ import cpw.mods.modlauncher.api.ITransformer; import cpw.mods.modlauncher.api.IncompatibleEnvironmentException; import joptsimple.ArgumentAcceptingOptionSpec; import joptsimple.OptionSpecBuilder; +import net.minecraftforge.coremod.CoreModEngine; import net.minecraftforge.fml.FMLConfig; import net.minecraftforge.fml.common.FMLPaths; import javax.annotation.Nonnull; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; import java.util.function.BiFunction; +import java.util.stream.Collectors; import static net.minecraftforge.fml.Logging.CORE; import static net.minecraftforge.fml.Logging.fmlLog; @@ -86,7 +89,8 @@ public class FMLServiceProvider implements ITransformationService @Override public List transformers() { - return Collections.emptyList(); + fmlLog.debug(CORE, "Loading coremod transformers"); + return new ArrayList<>(FMLLoader.getCoreModProvider().getCoreModTransformers()); } } diff --git a/src/main/java/net/minecraftforge/fml/loading/IModLanguageProvider.java b/src/main/java/net/minecraftforge/fml/loading/IModLanguageProvider.java new file mode 100644 index 0000000000..cceb903340 --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/loading/IModLanguageProvider.java @@ -0,0 +1,47 @@ +/* + * Minecraft Forge + * Copyright (c) 2018. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.fml.loading; + +import net.minecraftforge.fml.common.ModContainer; +import net.minecraftforge.fml.loading.moddiscovery.ModFile; +import net.minecraftforge.fml.loading.moddiscovery.ModInfo; +import net.minecraftforge.fml.loading.moddiscovery.ScanResult; + +import java.util.List; +import java.util.function.Consumer; + +/** + * Loaded as a ServiceLoader, from the classpath. Extensions are loaded from + * the mods directory, with the FMLType META-INF of LANGPROVIDER. + * + * Version data is read from the manifest's implementation version. + */ +public interface IModLanguageProvider +{ + String name(); + + Consumer getFileVisitor(); + + interface IModLanguageLoader { + ModContainer loadMod(ModFile file, ClassLoader modClassLoader); + } + + List buildModContainers(List modFiles, ClassLoader modClassLoader); +} diff --git a/src/main/java/net/minecraftforge/fml/loading/LanguageLoadingProvider.java b/src/main/java/net/minecraftforge/fml/loading/LanguageLoadingProvider.java new file mode 100644 index 0000000000..5b7c7f9d12 --- /dev/null +++ b/src/main/java/net/minecraftforge/fml/loading/LanguageLoadingProvider.java @@ -0,0 +1,64 @@ +/* + * Minecraft Forge + * Copyright (c) 2018. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +package net.minecraftforge.fml.loading; + +import net.minecraftforge.fml.loading.moddiscovery.ModFile; + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.ServiceLoader; +import java.util.stream.Stream; + +import static net.minecraftforge.fml.Logging.CORE; +import static net.minecraftforge.fml.Logging.fmlLog; + +public class LanguageLoadingProvider +{ + private final List languageProviders = new ArrayList<>(); + private final ServiceLoader serviceLoader; + private final Map languageProviderMap = new HashMap<>(); + + LanguageLoadingProvider() { + serviceLoader = ServiceLoader.load(IModLanguageProvider.class); + serviceLoader.forEach(languageProviders::add); + + languageProviders.forEach(lp -> { + final Package pkg = lp.getClass().getPackage(); + fmlLog.debug(CORE, "Found system classpath language provider {}, version {}", lp.name(), pkg.getImplementationVersion()); + }); + + languageProviders.forEach(lp->languageProviderMap.put(lp.name(), lp)); + } + + public void addAdditionalLanguages(List modFiles) + { + if (modFiles==null) return; + Stream langPaths = modFiles.stream().map(ModFile::getFilePath); + serviceLoader.reload(); + } + + public IModLanguageProvider getLanguage(String name) + { + return languageProviderMap.get(name); + } +} diff --git a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/BackgroundScanHandler.java b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/BackgroundScanHandler.java index 93694a4496..42a754f995 100644 --- a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/BackgroundScanHandler.java +++ b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/BackgroundScanHandler.java @@ -19,11 +19,14 @@ package net.minecraftforge.fml.loading.moddiscovery; +import com.google.common.util.concurrent.MoreExecutors; + import java.util.ArrayList; import java.util.List; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import static net.minecraftforge.fml.Logging.SCAN; @@ -37,7 +40,11 @@ public class BackgroundScanHandler private final List allFiles; public BackgroundScanHandler() { - modContentScanner = Executors.newCachedThreadPool(); + modContentScanner = Executors.newSingleThreadExecutor(r -> { + final Thread thread = Executors.defaultThreadFactory().newThread(r); + thread.setDaemon(true); + return thread; + }); scannedFiles = new ArrayList<>(); pendingFiles = new ArrayList<>(); allFiles = new ArrayList<>(); diff --git a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModDiscoverer.java b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModDiscoverer.java index 27dc4f028f..de71a15a58 100644 --- a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModDiscoverer.java +++ b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModDiscoverer.java @@ -20,6 +20,8 @@ package net.minecraftforge.fml.loading.moddiscovery; import cpw.mods.modlauncher.ServiceLoaderStreamUtils; +import net.minecraftforge.fml.loading.FMLLoader; +import net.minecraftforge.fml.loading.LanguageLoadingProvider; import java.util.Collection; import java.util.List; @@ -54,12 +56,12 @@ public class ModDiscoverer { .peek(mf -> fmlLog.debug(SCAN,"Found mod file {} of type {} with locator {}", mf.getFileName(), mf.getType(), mf.getLocator())) .collect(Collectors.groupingBy(ModFile::getType)); - //ModLanguageProvider.loadAdditionalLanguages(modFiles.get(ModFile.Type.LANGPROVIDER)); + FMLLoader.getLanguageLoadingProvider().addAdditionalLanguages(modFiles.get(ModFile.Type.LANGPROVIDER)); BackgroundScanHandler backgroundScanHandler = new BackgroundScanHandler(); final List mods = modFiles.get(ModFile.Type.MOD); mods.forEach(ModFile::identifyMods); fmlLog.debug(SCAN,"Found {} mod files with {} mods", mods::size, ()->mods.stream().mapToInt(mf -> mf.getModInfos().size()).sum()); - //mods.stream().map(ModFile::getCoreMods).flatMap(List::stream).forEach(ServiceProviders.getCoreModProvider()::addCoreMod); + mods.stream().map(ModFile::getCoreMods).flatMap(List::stream).forEach(FMLLoader.getCoreModProvider()::addCoreMod); mods.forEach(backgroundScanHandler::submitForScanning); return backgroundScanHandler; } diff --git a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModFile.java b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModFile.java index 87330353bb..8f57bae9fa 100644 --- a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModFile.java +++ b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModFile.java @@ -19,14 +19,21 @@ package net.minecraftforge.fml.loading.moddiscovery; +import net.minecraftforge.fml.loading.IModLanguageProvider; + +import javax.swing.text.html.Option; import java.nio.file.Path; import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; +import java.util.function.Function; import java.util.jar.Attributes; import java.util.jar.Manifest; +import java.util.stream.Collectors; import static net.minecraftforge.fml.Logging.LOADING; import static net.minecraftforge.fml.Logging.SCAN; @@ -40,6 +47,11 @@ public class ModFile DEFAULTMANIFEST.getMainAttributes().putValue("FMLModType", "MOD"); } + public void claimLanguage(String modId, IModLanguageProvider.IModLanguageLoader loader) + { + this.modInfoMap.get(modId).setLoader(loader); + } + public enum Type { MOD, LIBRARY, LANGPROVIDER } @@ -47,6 +59,7 @@ public class ModFile private final Type modFileType; private final Manifest manifest; private List modInfos; + private Map modInfoMap; private final IModLocator locator; private ScanResult fileScanResult; private CompletableFuture futureScanResult; @@ -60,7 +73,8 @@ public class ModFile manifest = locator.findManifest(file).orElse(DEFAULTMANIFEST); if (manifest != DEFAULTMANIFEST) fmlLog.debug(SCAN,"Mod file {} has a manifest", file); else fmlLog.debug(SCAN,"Mod file {} is missing a manifest", file); - modFileType = Type.valueOf(manifest.getMainAttributes().getValue(TYPE)); + final Optional value = Optional.ofNullable(manifest.getMainAttributes().getValue(TYPE)); + modFileType = Type.valueOf(value.orElse("MOD")); } public Type getType() { @@ -78,6 +92,7 @@ public class ModFile public void identifyMods() { this.modInfos = ModFileParser.readModList(this); this.modInfos.forEach(mi-> fmlLog.debug(LOADING,"Found mod {} for language {}", mi.getModId(), mi.getModLoader())); + this.modInfoMap = this.modInfos.stream().collect(Collectors.toMap(ModInfo::getModId, Function.identity())); this.coreMods = ModFileParser.getCoreMods(this); this.coreMods.forEach(mi-> fmlLog.debug(LOADING,"Found coremod {}", mi.getPath())); } diff --git a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModInfo.java b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModInfo.java index 2a3c1dea0f..7910600c8c 100644 --- a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModInfo.java +++ b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ModInfo.java @@ -20,6 +20,10 @@ package net.minecraftforge.fml.loading.moddiscovery; import net.minecraftforge.fml.common.versioning.ArtifactVersion; +import net.minecraftforge.fml.loading.IModLanguageProvider; + +import java.net.URL; +import java.util.List; public class ModInfo { private final ModFile owningFile; @@ -27,11 +31,12 @@ public class ModInfo { private final ArtifactVersion version; private final String displayName; private final String description; - private final java.net.URL updateJSONURL; + private final URL updateJSONURL; private final String modLoader; - private final java.util.List dependencies; + private final List dependencies; + private IModLanguageProvider.IModLanguageLoader loader; - public ModInfo(final ModFile owningFile, final String modLoader, final String modId, final String displayName, final ArtifactVersion version, final String description, final java.net.URL updateJSONURL, final java.util.List dependencies) { + public ModInfo(final ModFile owningFile, final String modLoader, final String modId, final String displayName, final ArtifactVersion version, final String description, final URL updateJSONURL, final List dependencies) { this.owningFile = owningFile; this.modLoader = modLoader; this.modId = modId; @@ -58,6 +63,11 @@ public class ModInfo { return version; } + public void setLoader(IModLanguageProvider.IModLanguageLoader loader) + { + this.loader = loader; + } + public enum Ordering { BEFORE, AFTER, NONE; } diff --git a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ScanResult.java b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ScanResult.java index f35f6269ca..cd75eaa907 100644 --- a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ScanResult.java +++ b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/ScanResult.java @@ -22,27 +22,33 @@ package net.minecraftforge.fml.loading.moddiscovery; import org.objectweb.asm.Type; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Set; public class ScanResult { private final ModFile file; - private final java.util.List annotations = new java.util.ArrayList<>(); - private final java.util.List classes = new java.util.ArrayList<>(); + private final List annotations = new ArrayList<>(); + private final List classes = new ArrayList<>(); public ScanResult(final ModFile file) { this.file = file; } + public ModFile getFile() { + return file; + } + public static boolean interestingAnnotations(final ModAnnotation annotation) { return true; } - public java.util.List getClasses() { + public List getClasses() { return classes; } - public java.util.List getAnnotations() { + public List getAnnotations() { return annotations; } diff --git a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/Scanner.java b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/Scanner.java index 0770426dbb..92fdab37d1 100644 --- a/src/main/java/net/minecraftforge/fml/loading/moddiscovery/Scanner.java +++ b/src/main/java/net/minecraftforge/fml/loading/moddiscovery/Scanner.java @@ -19,8 +19,19 @@ package net.minecraftforge.fml.loading.moddiscovery; +import net.minecraftforge.fml.loading.FMLLoader; +import net.minecraftforge.fml.loading.IModLanguageProvider; +import net.minecraftforge.fml.loading.LanguageLoadingProvider; import org.objectweb.asm.ClassReader; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Set; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +import static net.minecraftforge.fml.Logging.LOADING; import static net.minecraftforge.fml.Logging.SCAN; import static net.minecraftforge.fml.Logging.fmlLog; @@ -34,17 +45,20 @@ public class Scanner { public ScanResult scan() { ScanResult result = new ScanResult(fileToScan); fileToScan.scanFile(p -> fileVisitor(p, result)); + final Set modLoaders = fileToScan.getModInfos().stream().map(ModInfo::getModLoader).map(FMLLoader.getLanguageLoadingProvider()::getLanguage).collect(Collectors.toSet()); + modLoaders.stream().peek(ml-> fmlLog.debug(SCAN, "Scanning {} with language loader {}", fileToScan.getFilePath(), ml.name())) + .map(IModLanguageProvider::getFileVisitor).forEach(c->c.accept(result)); return result; } - private void fileVisitor(final java.nio.file.Path path, final ScanResult result) { + private void fileVisitor(final Path path, final ScanResult result) { try { fmlLog.debug(SCAN,"Scanning {} path {}", fileToScan, path); ModClassVisitor mcv = new ModClassVisitor(); - org.objectweb.asm.ClassReader cr = new ClassReader(java.nio.file.Files.newInputStream(path)); + ClassReader cr = new ClassReader(Files.newInputStream(path)); cr.accept(mcv, 0); mcv.buildData(result.getClasses(), result.getAnnotations()); - } catch (java.io.IOException e) { + } catch (IOException e) { // mark path bad } } diff --git a/src/main/resources/META-INF/services/net.minecraftforge.fml.loading.IModLanguageProvider b/src/main/resources/META-INF/services/net.minecraftforge.fml.loading.IModLanguageProvider new file mode 100644 index 0000000000..dc3f90283f --- /dev/null +++ b/src/main/resources/META-INF/services/net.minecraftforge.fml.loading.IModLanguageProvider @@ -0,0 +1 @@ +net.minecraftforge.fml.loading.FMLJavaModLanguageProvider \ No newline at end of file