separated fmllauncher code from main forge mod code. This enables much

stronger classloader separation between the two sides. Forge now
loads as a regular mod.

Still needs a bunch of debugging, but structure looks good and game loads
in forge dev.
This commit is contained in:
cpw 2018-12-31 16:33:54 -05:00
parent 558e1d2fb0
commit 48846bc0ba
72 changed files with 693 additions and 755 deletions

View file

@ -93,7 +93,17 @@ project(':forge') {
group = 'net.minecraftforge.test' //TODO: remove test when we finish patches and want users to find it
sourceSets {
fmllauncher {
java {
srcDir "$rootDir/src/fmllauncher/java"
}
resources {
srcDir "$rootDir/src/fmllauncher/resources"
}
}
main {
compileClasspath += sourceSets.fmllauncher.runtimeClasspath
runtimeClasspath += sourceSets.fmllauncher.runtimeClasspath
java {
srcDir "$rootDir/src/main/java"
}
@ -210,6 +220,21 @@ project(':forge') {
installer 'java3d:vecmath:1.5.2'
installer 'org.apache.logging.log4j:log4j-api:2.11.1'
installer 'org.apache.logging.log4j:log4j-core:2.11.1'
fmllauncherImplementation 'org.ow2.asm:asm:6.2'
fmllauncherImplementation 'org.ow2.asm:asm-commons:6.2'
fmllauncherImplementation 'org.ow2.asm:asm-tree:6.2'
fmllauncherImplementation 'cpw.mods:modlauncher:0.1.0'
fmllauncherImplementation 'net.minecraftforge:accesstransformers:0.10+:shadowed'
fmllauncherImplementation 'net.minecraftforge:eventbus:0.1+:service'
fmllauncherImplementation 'net.minecraftforge:forgespi:0.1+'
fmllauncherImplementation 'net.minecraftforge:coremods:0.1+'
fmllauncherImplementation 'com.electronwill.night-config:core:3.4.0'
fmllauncherImplementation 'com.electronwill.night-config:toml:3.4.0'
fmllauncherImplementation 'org.apache.maven:maven-artifact:3.5.3'
fmllauncherImplementation 'org.apache.logging.log4j:log4j-api:2.11.1'
fmllauncherImplementation 'org.apache.logging.log4j:log4j-core:2.11.1'
fmllauncherImplementation 'com.google.guava:guava:21.0'
fmllauncherImplementation 'com.google.code.gson:gson:2.8.0'
}
task runclient(type: JavaExec, dependsOn: [":forge:downloadAssets", ":forge:extractNatives"]) {
@ -644,6 +669,37 @@ project(':forge') {
'Implementation-Version': MCP_VERSION,
'Implementation-Vendor': 'Forge'
] as LinkedHashMap, 'net/minecraftforge/versions/mcp/')
// manifest entry for FML @Mod
manifest.attributes([
'Specification-Title': 'Mod Language Provider',
'Specification-Vendor': 'Forge Development LLC',
'Specification-Version': '1',
'Implementation-Title': 'FML Java Mod',
'Implementation-Version': SPEC_VERSION,
'Implementation-Vendor': 'Forge'
] as LinkedHashMap, 'net/minecraftforge/fml/javafmlmod/')
}
}
task fmllauncherJar(type: Jar) {
from sourceSets.fmllauncher.output
doFirst {
manifest.attributes([
'Specification-Title' : 'Forge',
'Specification-Vendor' : 'Forge Development LLC',
'Specification-Version' : SPEC_VERSION,
'Implementation-Title' : project.group,
'Implementation-Version': project.version.substring(MC_VERSION.length() + 1),
'Implementation-Vendor' : 'Forge Development LLC'
] as LinkedHashMap, 'net/minecraftforge/versions/forge/')
manifest.attributes([
'Specification-Title' : 'Minecraft',
'Specification-Vendor' : 'Mojang',
'Specification-Version' : MC_VERSION,
'Implementation-Title' : 'MCP',
'Implementation-Version': MCP_VERSION,
'Implementation-Vendor' : 'Forge'
] as LinkedHashMap, 'net/minecraftforge/versions/mcp/')
}
}
@ -811,6 +867,7 @@ project(':forge') {
artifact makeMdk
artifact userdevJar
artifact sourcesJar
artifact fmllauncherJar
pom {
name = 'forge'

View file

@ -0,0 +1,9 @@
package net.minecraftforge.fml.language;
public interface ILifecycleEvent<R extends ILifecycleEvent<?>> {
@SuppressWarnings("unchecked")
default R concrete() {
return (R) this;
}
}

View file

@ -19,9 +19,8 @@
package net.minecraftforge.fml.language;
import net.minecraftforge.fml.LifecycleEventProvider;
import java.util.function.Consumer;
import java.util.function.Supplier;
/**
* Loaded as a ServiceLoader, from the classpath. ExtensionPoint are loaded from
@ -35,8 +34,7 @@ public interface IModLanguageProvider
Consumer<ModFileScanData> getFileVisitor();
void preLifecycleEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent);
void postLifecycleEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent);
<R extends ILifecycleEvent<R>> void consumeLifecycleEvent(Supplier<R> consumeEvent);
interface IModLanguageLoader {
<T> T loadMod(IModInfo info, ClassLoader modClassLoader, ModFileScanData modFileScanResults);

View file

@ -19,7 +19,6 @@
package net.minecraftforge.fml.loading;
import java.util.Arrays;
import java.util.List;
/**

View file

@ -19,6 +19,7 @@
package net.minecraftforge.fml.loading;
import com.google.common.collect.ObjectArrays;
import cpw.mods.modlauncher.api.IEnvironment;
import cpw.mods.modlauncher.api.ILaunchHandlerService;
import cpw.mods.modlauncher.api.ITransformingClassLoader;
@ -26,38 +27,33 @@ import net.minecraftforge.api.distmarker.Dist;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.lang.reflect.Field;
import java.net.URISyntaxException;
import java.io.File;
import java.nio.file.Files;
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.concurrent.Callable;
import static net.minecraftforge.fml.Logging.CORE;
public class FMLDevClientLaunchProvider extends FMLCommonLaunchHandler implements ILaunchHandlerService
public class FMLClientLaunchProvider extends FMLCommonLaunchHandler implements ILaunchHandlerService
{
private static final Logger LOGGER = LogManager.getLogger();
@Override
public String name()
{
return "fmldevclient";
return "fmlclient";
}
@Override
public Path[] identifyTransformationTargets()
{
return super.commonLibPaths(new Path[] {getForgePath()});
return LibraryFinder.commonLibPaths(ObjectArrays.concat(FMLLoader.getForgePath(), FMLLoader.getMCPaths()));
}
@Override
public Callable<Void> launchService(String[] arguments, ITransformingClassLoader launchClassLoader)
{
return () -> {
LOGGER.debug(CORE, "Launching minecraft in {} with arguments {}", launchClassLoader, arguments);
super.beforeStart(launchClassLoader);
launchClassLoader.addTargetPackageFilter(getPackagePredicate());
Class.forName("net.minecraft.client.main.Main", true, launchClassLoader.getInstance()).getMethod("main", String[].class).invoke(null, (Object)arguments);
@ -66,9 +62,7 @@ public class FMLDevClientLaunchProvider extends FMLCommonLaunchHandler implement
}
@Override
public void setup(IEnvironment environment)
{
LOGGER.debug(CORE, "No jar creation necessary. Launch is dev environment");
public void setup(final IEnvironment environment, final Map<String, ?> arguments) {
}
@Override

View file

@ -26,17 +26,12 @@ import net.minecraftforge.api.distmarker.Dist;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
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.function.Predicate;
import static net.minecraftforge.fml.Logging.CORE;
public abstract class FMLCommonLaunchHandler
{
private static final Logger LOGGER = LogManager.getLogger();
@ -49,57 +44,27 @@ public abstract class FMLCommonLaunchHandler
"net.minecraftforge.eventbus.", "net.minecraftforge.api."
);
private Path forgePath;
protected Predicate<String> getPackagePredicate() {
return cn -> SKIPPACKAGES.stream().noneMatch(cn::startsWith);
}
public void setup(final IEnvironment environment)
public Path getForgePath(final String mcVersion, final String forgeVersion) {
return LibraryFinder.getForgeLibraryPath(mcVersion, forgeVersion);
}
public Path[] getMCPaths(final String mcVersion, final String forgeVersion) {
return LibraryFinder.getMCPaths(mcVersion, forgeVersion, getDist().isClient() ? "client" : "server");
}
public void setup(final IEnvironment environment, final Map<String, ?> arguments)
{
}
Path findLibsPath() {
final Path asm = findJarPathFor("org/objectweb/asm/Opcodes.class", "asm");
// go up SIX parents to find the libs dir
final Path libs = asm.getParent().getParent().getParent().getParent().getParent().getParent();
LOGGER.debug(CORE, "Found probable library path {}", libs);
return libs;
}
Path findJarPathFor(final String className, final String jarName) {
final URL resource = getClass().getClassLoader().getResource(className);
try {
Path path;
final URI uri = resource.toURI();
if (uri.getRawSchemeSpecificPart().contains("!")) {
path = Paths.get(new URI(uri.getRawSchemeSpecificPart().split("!")[0]));
} else {
path = Paths.get(new URI("file://"+uri.getRawSchemeSpecificPart().substring(0, uri.getRawSchemeSpecificPart().length()-className.length())));
}
LOGGER.debug(CORE, "Found JAR {} at path {}", jarName, path.toString());
return path;
} catch (URISyntaxException e) {
LOGGER.error(CORE, "Failed to find JAR for class {} - {}", className, jarName);
throw new RuntimeException("Unable to locate "+className+" - "+jarName, e);
}
}
Path[] commonLibPaths(Path[] extras) {
final Path realms = findJarPathFor("com/mojang/realmsclient/RealmsVersion.class", "realms");
return ObjectArrays.concat(extras, realms);
}
Path getForgePath() {
if (forgePath == null) {
forgePath = findJarPathFor("net/minecraftforge/versions/forge/ForgeVersion.class", "forge");
LOGGER.debug(CORE, "Found forge path {}", forgePath);
}
return forgePath;
}
public abstract Dist getDist();
protected void beforeStart(ITransformingClassLoader launchClassLoader)
{
FMLLoader.beforeStart(launchClassLoader, getForgePath());
FMLLoader.beforeStart(launchClassLoader);
}
}

View file

@ -23,14 +23,13 @@ import com.electronwill.nightconfig.core.ConfigSpec;
import com.electronwill.nightconfig.core.file.CommentedFileConfig;
import com.electronwill.nightconfig.core.io.WritingMode;
import net.minecraftforge.api.distmarker.Dist;
import org.apache.commons.lang3.StringUtils;
import org.apache.logging.log4j.LogManager;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.stream.Collectors;
import static net.minecraftforge.fml.Logging.CORE;
import static net.minecraftforge.fml.loading.LogMarkers.CORE;
public class FMLConfig
{
@ -41,11 +40,6 @@ public class FMLConfig
configSpec.defineInList("side", Dist.CLIENT.name(), Arrays.stream(Dist.values()).map(Enum::name).collect(Collectors.toList()));
configSpec.defineInRange("maxframerate", 60, 10, 120);
configSpec.defineInRange("minframerate", 60, 10, 120);
/* Tests that we know work and shouldn't be done in runtime.
configSpec.defineInList(Arrays.asList("tasty","flavour"), Dist.CLIENT.name(), Arrays.stream(Dist.values()).map(Enum::name).collect(Collectors.toList()));
configSpec.defineInList(Arrays.asList("tasty","teaser"), Dist.CLIENT.name(), Arrays.stream(Dist.values()).map(Enum::name).collect(Collectors.toList()));
configSpec.define("longstring", StringUtils.repeat("AAAA", 10000), s->s!=null && ((String)s).length()>0);
*/
}
private CommentedFileConfig configData;

View file

@ -23,48 +23,52 @@ import cpw.mods.modlauncher.api.IEnvironment;
import cpw.mods.modlauncher.api.ILaunchHandlerService;
import cpw.mods.modlauncher.api.ITransformingClassLoader;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.relauncher.libraries.LibraryManager;
import net.minecraftforge.versions.forge.ForgeVersion;
import net.minecraftforge.versions.mcp.MCPVersion;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.File;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Callable;
public class FMLClientLaunchProvider extends FMLCommonLaunchHandler implements ILaunchHandlerService
import static net.minecraftforge.fml.loading.LogMarkers.CORE;
public class FMLDevClientLaunchProvider extends FMLCommonLaunchHandler implements ILaunchHandlerService
{
private static final Logger LOGGER = LogManager.getLogger();
private Path compiledClasses;
@Override
public String name()
{
return "fmlclient";
return "fmldevclient";
}
@Override
public Path[] identifyTransformationTargets()
{
Path libsPath = findLibsPath();
Path patchedBinariesPath = libsPath.resolve(Paths.get(ForgeVersion.getGroup().replace('.', File.separatorChar), "forge", MCPVersion.getMCVersion() + "-" + ForgeVersion.getVersion(), "forge-" + MCPVersion.getMCVersion() + "-" + ForgeVersion.getVersion() + "-client.jar"));
Path srgMcPath = libsPath.resolve(Paths.get("net","minecraft", "client", MCPVersion.getMCPandMCVersion(), "client-"+MCPVersion.getMCPandMCVersion()+"-srg.jar"));
LOGGER.info("SRG MC at {} is {}", srgMcPath.toString(), Files.exists(srgMcPath) ? "present" : "missing");
LOGGER.info("Forge patches at {} is {}", patchedBinariesPath.toString(), Files.exists(patchedBinariesPath) ? "present" : "missing");
LOGGER.info("Forge at {} is {}", getForgePath().toString(), Files.exists(getForgePath()) ? "present" : "missing");
if (!(Files.exists(srgMcPath) && Files.exists(patchedBinariesPath) && Files.exists(getForgePath()))) {
throw new RuntimeException("Failed to find patched jars");
return LibraryFinder.commonLibPaths(new Path[] {FMLLoader.getForgePath()});
}
return super.commonLibPaths(new Path[] {getForgePath(), patchedBinariesPath, srgMcPath});
public Path getForgePath(final String mcVersion, final String forgeVersion) {
// In forge dev, we just find the path for ForgeVersion for everything
compiledClasses = LibraryFinder.findJarPathFor("net/minecraftforge/versions/forge/ForgeVersion.class", "forge");
return compiledClasses;
}
public Path[] getMCPaths(final String mcVersion, final String forgeVersion) {
// In forge dev, we just find the path for ForgeVersion for everything
return new Path[] { compiledClasses, compiledClasses };
}
@Override
public Callable<Void> launchService(String[] arguments, ITransformingClassLoader launchClassLoader)
{
return () -> {
LOGGER.debug(CORE, "Launching minecraft in {} with arguments {}", launchClassLoader, arguments);
super.beforeStart(launchClassLoader);
launchClassLoader.addTargetPackageFilter(getPackagePredicate());
Class.forName("net.minecraft.client.main.Main", true, launchClassLoader.getInstance()).getMethod("main", String[].class).invoke(null, (Object)arguments);
@ -72,8 +76,15 @@ public class FMLClientLaunchProvider extends FMLCommonLaunchHandler implements I
};
}
@SuppressWarnings("unchecked")
@Override
public void setup(final IEnvironment environment) {
public void setup(IEnvironment environment, final Map<String, ?> arguments)
{
LOGGER.debug(CORE, "No jar creation necessary. Launch is dev environment");
// we're injecting forge into the exploded dir finder
final Path forgemodstoml = LibraryFinder.findJarPathFor("META-INF/mods.toml", "forgemodstoml");
((Map<String, List<Pair<Path,Path>>>) arguments).computeIfAbsent("explodedTargets", a->new ArrayList<>()).
add(Pair.of(compiledClasses, forgemodstoml));
}
@Override

View file

@ -26,16 +26,11 @@ import net.minecraftforge.api.distmarker.Dist;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.lang.reflect.Field;
import java.net.URISyntaxException;
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.concurrent.Callable;
import java.util.function.Predicate;
import static net.minecraftforge.fml.Logging.CORE;
import static net.minecraftforge.fml.loading.LogMarkers.CORE;
public class FMLDevServerLaunchProvider extends FMLCommonLaunchHandler implements ILaunchHandlerService
{
@ -50,7 +45,7 @@ public class FMLDevServerLaunchProvider extends FMLCommonLaunchHandler implement
@Override
public Path[] identifyTransformationTargets()
{
return super.commonLibPaths(new Path[] { getForgePath() });
return LibraryFinder.commonLibPaths(new Path[] { FMLLoader.getForgePath() });
}
@Override
@ -67,7 +62,7 @@ public class FMLDevServerLaunchProvider extends FMLCommonLaunchHandler implement
}
@Override
public void setup(IEnvironment environment)
public void setup(IEnvironment environment, final Map<String, ?> arguments)
{
LOGGER.debug(CORE, "No jar creation necessary. Launch is dev environment");
}

View file

@ -26,7 +26,6 @@ import cpw.mods.modlauncher.api.ITransformingClassLoader;
import cpw.mods.modlauncher.api.IncompatibleEnvironmentException;
import cpw.mods.modlauncher.serviceapi.ILaunchPluginService;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.versions.forge.ForgeVersion;
import net.minecraftforge.fml.loading.moddiscovery.BackgroundScanHandler;
import net.minecraftforge.fml.loading.moddiscovery.ModDiscoverer;
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
@ -36,16 +35,15 @@ import org.apache.logging.log4j.Logger;
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.Optional;
import java.util.ServiceLoader;
import java.util.Set;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import static net.minecraftforge.fml.Logging.CORE;
import static net.minecraftforge.fml.Logging.SCAN;
import static net.minecraftforge.fml.loading.LogMarkers.CORE;
import static net.minecraftforge.fml.loading.LogMarkers.SCAN;
public class FMLLoader
{
@ -61,10 +59,14 @@ public class FMLLoader
private static RuntimeDistCleaner runtimeDistCleaner;
private static Path gamePath;
private static Path forgePath;
private static Path[] mcPaths;
static String forgeVersion;
static String mcVersion;
private static Predicate<String> classLoaderExclusions;
static void onInitialLoad(IEnvironment environment, Set<String> otherServices) throws IncompatibleEnvironmentException
{
final String version = ForgeVersion.getVersion();
final String version = LauncherVersion.getVersion();
LOGGER.debug(CORE,"FML {} loading", version);
final Package modLauncherPackage = ITransformationService.class.getPackage();
LOGGER.debug(CORE,"FML found ModLauncher version : {}", modLauncherPackage.getImplementationVersion());
@ -110,11 +112,9 @@ public class FMLLoader
coreModProvider = coreModProviders.get(0);
final Package coremodPackage = coreModProvider.getClass().getPackage();
LOGGER.debug(CORE,"FML found CoreMod version : {}", coremodPackage.getImplementationVersion());
languageLoadingProvider = new LanguageLoadingProvider();
}
static void setupLaunchHandler(final IEnvironment environment)
static void setupLaunchHandler(final IEnvironment environment, final Map<String, ?> arguments)
{
final String launchTarget = environment.getProperty(IEnvironment.Keys.LAUNCHTARGET.get()).orElse("MISSING");
final Optional<ILaunchHandlerService> launchHandler = environment.findLaunchHandler(launchTarget);
@ -131,14 +131,36 @@ public class FMLLoader
gamePath = environment.getProperty(IEnvironment.Keys.GAMEDIR.get()).orElse(Paths.get(".").toAbsolutePath());
FMLCommonLaunchHandler commonLaunchHandler = (FMLCommonLaunchHandler)launchHandler.get();
commonLaunchHandler.setup(environment);
dist = commonLaunchHandler.getDist();
mcVersion = (String) arguments.get("mcVersion");
forgeVersion = (String) arguments.get("forgeVersion");
forgePath = commonLaunchHandler.getForgePath(mcVersion, forgeVersion);
mcPaths = commonLaunchHandler.getMCPaths(mcVersion, forgeVersion);
if (!Files.exists(forgePath)) {
LOGGER.fatal(CORE, "Failed to find forge version {} for MC {} at {}", forgeVersion, mcVersion, forgePath);
throw new RuntimeException("Missing forge!");
}
if (!Files.exists(mcPaths[0])) {
LOGGER.fatal(CORE, "Failed to find deobfuscated Minecraft version {} at {}", mcVersion, mcPaths[0]);
throw new RuntimeException("Missing deobfuscated minecraft!");
}
if (!Files.exists(mcPaths[1])) {
LOGGER.fatal(CORE, "Failed to find forge patches for Minecraft version {} at {}", mcVersion, mcPaths[1]);
throw new RuntimeException("Missing forge patches!");
}
commonLaunchHandler.setup(environment, arguments);
classLoaderExclusions = commonLaunchHandler.getPackagePredicate();
languageLoadingProvider = new LanguageLoadingProvider();
runtimeDistCleaner.getExtension().accept(dist);
}
public static void beginModScan()
public static void beginModScan(final Map<String,?> arguments)
{
LOGGER.debug(SCAN,"Scanning for Mod Locators");
modDiscoverer = new ModDiscoverer();
modDiscoverer = new ModDiscoverer(arguments);
final BackgroundScanHandler backgroundScanHandler = modDiscoverer.discoverMods();
loadingModList = backgroundScanHandler.getLoadingModList();
}
@ -152,6 +174,10 @@ public class FMLLoader
return languageLoadingProvider;
}
static ModDiscoverer getModDiscoverer() {
return modDiscoverer;
}
public static void loadAccessTransformer()
{
final URL resource = FMLLoader.class.getClassLoader().getResource("forge_at.cfg");
@ -181,10 +207,9 @@ public class FMLLoader
return dist;
}
public static void beforeStart(ITransformingClassLoader launchClassLoader, Path forgePath)
public static void beforeStart(ITransformingClassLoader launchClassLoader)
{
FMLLoader.launchClassLoader = launchClassLoader.getInstance();
FMLLoader.forgePath = forgePath;
}
@ -207,4 +232,12 @@ public class FMLLoader
public static Path getForgePath() {
return forgePath;
}
public static Path[] getMCPaths() {
return mcPaths;
}
public static Predicate<String> getClassLoaderExclusions() {
return classLoaderExclusions;
}
}

View file

@ -20,17 +20,14 @@
package net.minecraftforge.fml.loading;
import cpw.mods.modlauncher.api.IEnvironment;
import net.minecraftforge.fml.FileUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import static net.minecraftforge.fml.Logging.CORE;
import static net.minecraftforge.fml.loading.LogMarkers.CORE;
public enum FMLPaths
{

View file

@ -19,19 +19,16 @@
package net.minecraftforge.fml.loading;
import com.google.common.collect.ObjectArrays;
import cpw.mods.modlauncher.api.IEnvironment;
import cpw.mods.modlauncher.api.ILaunchHandlerService;
import cpw.mods.modlauncher.api.ITransformingClassLoader;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.versions.forge.ForgeVersion;
import net.minecraftforge.versions.mcp.MCPVersion;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Map;
import java.util.concurrent.Callable;
public class FMLServerLaunchProvider extends FMLCommonLaunchHandler implements ILaunchHandlerService
@ -47,16 +44,7 @@ public class FMLServerLaunchProvider extends FMLCommonLaunchHandler implements I
@Override
public Path[] identifyTransformationTargets()
{
Path libsPath = findLibsPath();
Path patchedBinariesPath = libsPath.resolve(Paths.get(ForgeVersion.getGroup().replace('.', File.separatorChar), "forge", MCPVersion.getMCVersion() + "-" + ForgeVersion.getVersion(), "forge-" + MCPVersion.getMCVersion() + "-" + ForgeVersion.getVersion() + "-server.jar"));
Path srgMcPath = libsPath.resolve(Paths.get("net","minecraft", "server", MCPVersion.getMCPandMCVersion(), "server-"+MCPVersion.getMCPandMCVersion()+"-srg.jar"));
LOGGER.info("SRG MC at {} is {}", srgMcPath.toString(), Files.exists(srgMcPath) ? "present" : "missing");
LOGGER.info("Forge patches at {} is {}", patchedBinariesPath.toString(), Files.exists(patchedBinariesPath) ? "present" : "missing");
LOGGER.info("Forge at {} is {}", getForgePath().toString(), Files.exists(getForgePath()) ? "present" : "missing");
if (!(Files.exists(srgMcPath) && Files.exists(patchedBinariesPath) && Files.exists(getForgePath()))) {
throw new RuntimeException("Failed to find patched jars");
}
return new Path[] {getForgePath(), patchedBinariesPath, srgMcPath};
return ObjectArrays.concat(FMLLoader.getForgePath(), FMLLoader.getMCPaths());
}
@Override
@ -71,7 +59,7 @@ public class FMLServerLaunchProvider extends FMLCommonLaunchHandler implements I
}
@Override
public void setup(final IEnvironment environment) {
public void setup(final IEnvironment environment, final Map<String, ?> arguments) {
}
@Override

View file

@ -29,12 +29,10 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.*;
import java.util.function.BiFunction;
import static net.minecraftforge.fml.Logging.CORE;
import static net.minecraftforge.fml.loading.LogMarkers.CORE;
public class FMLServiceProvider implements ITransformationService
{
@ -42,8 +40,14 @@ public class FMLServiceProvider implements ITransformationService
private static final Logger LOGGER = LogManager.getLogger();
private ArgumentAcceptingOptionSpec<String> modsOption;
private ArgumentAcceptingOptionSpec<String> modListsOption;
private ArgumentAcceptingOptionSpec<String> mavenRootsOption;
private ArgumentAcceptingOptionSpec<String> forgeOption;
private ArgumentAcceptingOptionSpec<String> mcOption;
private List<String> modsArgumentList;
private List<String> modListsArgumentList;
private List<String> mavenRootsArgumentList;
private String targetForgeVersion;
private String targetMcVersion;
@Override
public String name()
@ -58,10 +62,16 @@ public class FMLServiceProvider implements ITransformationService
FMLPaths.setup(environment);
LOGGER.debug(CORE,"Loading configuration");
FMLConfig.load();
final Map<String, Object> arguments = new HashMap<>();
arguments.put("modLists", modListsArgumentList);
arguments.put("mods", modsArgumentList);
arguments.put("mavenRoots", mavenRootsArgumentList);
arguments.put("forgeVersion", targetForgeVersion);
arguments.put("mcVersion", targetMcVersion);
LOGGER.debug(CORE, "Preparing launch handler");
FMLLoader.setupLaunchHandler(environment);
FMLLoader.setupLaunchHandler(environment, arguments);
LOGGER.debug(CORE,"Initiating mod scan");
FMLLoader.beginModScan();
FMLLoader.beginModScan(arguments);
LOGGER.debug(CORE, "Loading access transformers");
FMLLoader.loadAccessTransformer();
}
@ -75,8 +85,11 @@ public class FMLServiceProvider implements ITransformationService
@Override
public void arguments(BiFunction<String, String, OptionSpecBuilder> argumentBuilder)
{
forgeOption = argumentBuilder.apply("forgeVersion", "Forge Version number").withRequiredArg().ofType(String.class).required();
mcOption = argumentBuilder.apply("mcVersion", "Minecraft Version number").withRequiredArg().ofType(String.class).required();
modsOption = argumentBuilder.apply("mods", "List of mods to add").withRequiredArg().ofType(String.class).withValuesSeparatedBy(",");
modListsOption = argumentBuilder.apply("modLists", "JSON modlists").withRequiredArg().ofType(String.class).withValuesSeparatedBy(",");
mavenRootsOption = argumentBuilder.apply("mavenRoots", "Maven root directories").withRequiredArg().ofType(String.class).withValuesSeparatedBy(",");
}
@Override
@ -84,6 +97,9 @@ public class FMLServiceProvider implements ITransformationService
{
modsArgumentList = option.values(modsOption);
modListsArgumentList = option.values(modListsOption);
mavenRootsArgumentList = option.values(mavenRootsOption);
targetForgeVersion = option.value(forgeOption);
targetMcVersion = option.value(mcOption);
}
@Nonnull

View file

@ -17,7 +17,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.fml;
package net.minecraftforge.fml.loading;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -27,7 +27,7 @@ import java.nio.file.FileAlreadyExistsException;
import java.nio.file.Files;
import java.nio.file.Path;
import static net.minecraftforge.fml.Logging.CORE;
import static net.minecraftforge.fml.loading.LogMarkers.CORE;
public class FileUtils
{

View file

@ -0,0 +1,39 @@
package net.minecraftforge.fml.loading;
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();
return Optional.ofNullable(pkgVersion);
}
public static Optional<String> getSpecificationVersion(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).getSpecificationVersion();
return Optional.ofNullable(pkgVersion);
}
public static Optional<String> getImplementationVersion(final Class<?> clazz) {
// With java 9 we'll use the module's version if it exists in preference.
final String pkgVersion = clazz.getPackage().getImplementationVersion();
return Optional.ofNullable(pkgVersion);
}
public static Optional<String> getImplementationTitle(final Class<?> clazz) {
// With java 9 we'll use the module's version if it exists in preference.
final String pkgVersion = clazz.getPackage().getImplementationTitle();
return Optional.ofNullable(pkgVersion);
}
public static Optional<String> getSpecificationVersion(final Class<?> clazz) {
// With java 9 we'll use the module's version if it exists in preference.
final String pkgVersion = clazz.getPackage().getSpecificationVersion();
return Optional.ofNullable(pkgVersion);
}
}

View file

@ -17,7 +17,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.fml;
package net.minecraftforge.fml.loading;
import java.util.function.BiConsumer;
import java.util.function.Function;

View file

@ -19,7 +19,6 @@
package net.minecraftforge.fml.loading;
import net.minecraftforge.versions.forge.ForgeVersion;
import net.minecraftforge.fml.language.IModLanguageProvider;
import net.minecraftforge.fml.loading.moddiscovery.ExplodedDirectoryLocator;
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
@ -29,12 +28,16 @@ import org.apache.maven.artifact.versioning.ArtifactVersion;
import org.apache.maven.artifact.versioning.DefaultArtifactVersion;
import org.apache.maven.artifact.versioning.VersionRange;
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.*;
import java.util.function.Consumer;
import java.util.stream.Stream;
import static net.minecraftforge.fml.Logging.CORE;
import static net.minecraftforge.fml.loading.LogMarkers.CORE;
public class LanguageLoadingProvider
{
@ -73,13 +76,20 @@ public class LanguageLoadingProvider
serviceLoader.forEach(languageProviders::add);
languageProviders.forEach(lp -> {
final Package pkg = lp.getClass().getPackage();
String implementationVersion = pkg.getImplementationVersion();
if (implementationVersion == null) {
implementationVersion = ForgeVersion.getSpec();
final Path lpPath;
try {
lpPath = Paths.get(lp.getClass().getProtectionDomain().getCodeSource().getLocation().toURI());
} catch (URISyntaxException e) {
throw new RuntimeException("Huh?", e);
}
LOGGER.debug(CORE, "Found system classpath language provider {}, version {}", lp.name(), implementationVersion);
languageProviderMap.put(lp.name(), new ModLanguageWrapper(lp, new DefaultArtifactVersion(implementationVersion)));
Optional<String> implementationVersion = JarVersionLookupHandler.getImplementationVersion(lp.getClass());
String impl = implementationVersion.orElse(Files.isDirectory(lpPath) ? FMLLoader.forgeVersion.split("\\.")[0] : null);
if (impl == null) {
LOGGER.fatal(CORE, "Found unversioned system classpath language provider {}", lp.name());
throw new RuntimeException("Failed to find implementation version for language provider "+ lp.name());
}
LOGGER.debug(CORE, "Found system classpath language provider {}, version {}", lp.name(), impl);
languageProviderMap.put(lp.name(), new ModLanguageWrapper(lp, new DefaultArtifactVersion(impl)));
});
}

View file

@ -0,0 +1,23 @@
package net.minecraftforge.fml.loading;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import static net.minecraftforge.fml.loading.LogMarkers.CORE;
public class LauncherVersion {
private static final Logger LOGGER = LogManager.getLogger();
private static final String launcherVersion;
static {
String vers = JarVersionLookupHandler.getImplementationVersion(LauncherVersion.class).orElse(System.getProperty("fmllauncher.version"));
if (vers == null) throw new RuntimeException("Missing FMLLauncher version, cannot continue");
launcherVersion = vers;
LOGGER.info(CORE, "Found FMLLauncher version {}", launcherVersion);
}
public static String getVersion()
{
return launcherVersion;
}
}

View file

@ -0,0 +1,69 @@
package net.minecraftforge.fml.loading;
import com.google.common.collect.ObjectArrays;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.File;
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 static net.minecraftforge.fml.loading.LogMarkers.CORE;
public class LibraryFinder {
private static final Logger LOGGER = LogManager.getLogger();
private static Path libsPath;
static Path findLibsPath() {
if (libsPath == null) {
final Path asm = findJarPathFor("org/objectweb/asm/Opcodes.class", "asm");
// go up SIX parents to find the libs dir
final Path libs = asm.getParent().getParent().getParent().getParent().getParent().getParent();
LOGGER.debug(CORE, "Found probable library path {}", libs);
libsPath = libs;
}
return libsPath;
}
static Path findJarPathFor(final String className, final String jarName) {
final URL resource = LibraryFinder.class.getClassLoader().getResource(className);
try {
Path path;
final URI uri = resource.toURI();
if (uri.getRawSchemeSpecificPart().contains("!")) {
path = Paths.get(new URI(uri.getRawSchemeSpecificPart().split("!")[0]));
} else {
path = Paths.get(new URI("file://"+uri.getRawSchemeSpecificPart().substring(0, uri.getRawSchemeSpecificPart().length()-className.length())));
}
LOGGER.debug(CORE, "Found JAR {} at path {}", jarName, path.toString());
return path;
} catch (NullPointerException | URISyntaxException e) {
LOGGER.error(CORE, "Failed to find JAR for class {} - {}", className, jarName);
throw new RuntimeException("Unable to locate "+className+" - "+jarName, e);
}
}
static Path[] commonLibPaths(Path[] extras) {
final Path realms = findJarPathFor("com/mojang/realmsclient/RealmsVersion.class", "realms");
return ObjectArrays.concat(extras, realms);
}
static Path getForgeLibraryPath(final String mcVersion, final String forgeVersion) {
Path forgePath = findLibsPath().resolve(MavenCoordinateResolver.get("net.minecraftforge", "forge", "", "", mcVersion+"-"+forgeVersion));
LOGGER.debug(CORE, "Found forge path {} is {}", forgePath, pathStatus(forgePath));
return forgePath;
}
static String pathStatus(final Path path) {
return Files.exists(path) ? "present" : "missing";
}
static Path[] getMCPaths(final String mcVersion, final String forgeVersion, final String type) {
Path srgMcPath = findLibsPath().resolve(MavenCoordinateResolver.get("net.minecraft", type, "", "srg", mcVersion));
Path patchedBinariesPath = findLibsPath().resolve(MavenCoordinateResolver.get("net.minecraftforge", "forge", "", type, mcVersion+"-"+forgeVersion));
LOGGER.info("SRG MC at {} is {}", srgMcPath.toString(), pathStatus(srgMcPath));
LOGGER.info("Forge patches at {} is {}", patchedBinariesPath.toString(), pathStatus(patchedBinariesPath));
return new Path[] { srgMcPath, patchedBinariesPath };
}
}

View file

@ -20,9 +20,6 @@
package net.minecraftforge.fml.loading;
import com.google.common.collect.Streams;
import net.minecraftforge.fml.LifecycleEventProvider;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.ModLoadingStage;
import net.minecraftforge.fml.loading.moddiscovery.BackgroundScanHandler;
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo;
@ -34,7 +31,6 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
/**

View file

@ -0,0 +1,12 @@
package net.minecraftforge.fml.loading;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
public class LogMarkers {
public static final Marker CORE = MarkerManager.getMarker("CORE");
public static final Marker LOADING = MarkerManager.getMarker("LOADING");
public static final Marker SCAN = MarkerManager.getMarker("SCAN");
public static final Marker SPLASH = MarkerManager.getMarker("SPLASH");
}

View file

@ -0,0 +1,37 @@
package net.minecraftforge.fml.loading;
import java.nio.file.Path;
import java.nio.file.Paths;
/**
* Convert a maven coordinate into a Path.
*
* {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}.
*/
public class MavenCoordinateResolver {
public static Path get(final String coordinate) {
final String[] parts = coordinate.split(":");
final String groupId = parts[0];
final String artifactId = parts[1];
final String extension = parts.length > 3 ? parts[2] : "";
final String classifier = parts.length > 4 ? parts[3] : "";
final String version = parts[parts.length-1];
return get(groupId, artifactId, extension, classifier, version);
}
public static Path get(final String groupId, final String artifactId, final String extension, final String classifier, final String version)
{
final String fileName = artifactId + "-" + version +
(!classifier.isEmpty() ? "-"+ classifier : "") +
(!extension.isEmpty() ? "." + extension : ".jar");
String[] groups = groupId.split("\\.");
Path result = Paths.get(groups[0]);
for (int i = 1; i < groups.length; i++) {
result = result.resolve(groups[i]);
}
return result.resolve(artifactId).resolve(version).resolve(fileName);
}
}

View file

@ -24,7 +24,7 @@ import org.apache.logging.log4j.Logger;
import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
import org.apache.maven.artifact.versioning.VersionRange;
import static net.minecraftforge.fml.Logging.CORE;
import static net.minecraftforge.fml.loading.LogMarkers.CORE;
public final class MavenVersionAdapter {
private static final Logger LOGGER = LogManager.getLogger();

View file

@ -19,8 +19,7 @@
package net.minecraftforge.fml.loading;
import net.minecraftforge.fml.Java9BackportUtils;
import net.minecraftforge.fml.common.toposort.TopologicalSort;
import net.minecraftforge.fml.loading.toposort.TopologicalSort;
import net.minecraftforge.fml.language.IModInfo;
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo;
@ -36,7 +35,7 @@ import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static net.minecraftforge.fml.Logging.LOADING;
import static net.minecraftforge.fml.loading.LogMarkers.LOADING;
public class ModSorter
{

View file

@ -20,7 +20,6 @@
package net.minecraftforge.fml.loading;
import com.google.common.collect.ImmutableMap;
import net.minecraftforge.versions.forge.ForgeVersion;
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
import org.apache.commons.lang3.text.StrLookup;
import org.apache.commons.lang3.text.StrSubstitutor;
@ -32,9 +31,9 @@ import java.util.Map;
public class StringSubstitutor
{
private static final Map<String,String> globals = ImmutableMap.of(
"mcVersion", ForgeVersion.mcVersion,
"forgeVersion", ForgeVersion.getVersion(),
"mcpVersion", ForgeVersion.mcpVersion);
"mcVersion", FMLLoader.forgeVersion,
"forgeVersion", FMLLoader.mcVersion
);
public static String replace(final String in, final ModFile file) {
return new StrSubstitutor(getStringLookup(file)).replace(in);

View file

@ -25,7 +25,6 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
/**
* Created by cpw on 05/06/17.

View file

@ -0,0 +1,71 @@
package net.minecraftforge.fml.loading.moddiscovery;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.stream.Stream;
import java.util.zip.ZipError;
import static net.minecraftforge.fml.loading.LogMarkers.SCAN;
public class AbstractJarFileLocator {
private static final Logger LOGGER = LogManager.getLogger();
protected final Map<ModFile, FileSystem> modJars;
public AbstractJarFileLocator() {
this.modJars = new HashMap<>();
}
protected FileSystem createFileSystem(ModFile modFile) {
try {
return FileSystems.newFileSystem(modFile.getFilePath(), modFile.getClass().getClassLoader());
} catch (ZipError | IOException e) {
LOGGER.debug(SCAN,"Ignoring invalid JAR file {}", modFile.getFilePath());
return null;
}
}
public Path findPath(final ModFile modFile, final String... path) {
if (path.length < 1) {
throw new IllegalArgumentException("Missing path");
}
return modJars.get(modFile).getPath(path[0], Arrays.copyOfRange(path, 1, path.length));
}
public void scanFile(final ModFile file, final Consumer<Path> pathConsumer) {
LOGGER.debug(SCAN,"Scan started: {}", file);
FileSystem fs = modJars.get(file);
fs.getRootDirectories().forEach(path -> {
try (Stream<Path> files = Files.find(path, Integer.MAX_VALUE, (p, a) -> p.getNameCount() > 0 && p.getFileName().toString().endsWith(".class"))) {
files.forEach(pathConsumer);
} catch (IOException e) {
e.printStackTrace();
}
});
LOGGER.debug(SCAN,"Scan finished: {}", file);
}
public Optional<Manifest> findManifest(final Path file)
{
try (JarFile jf = new JarFile(file.toFile()))
{
return Optional.ofNullable(jf.getManifest());
}
catch (IOException e)
{
return Optional.empty();
}
}
}

View file

@ -31,7 +31,7 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import static net.minecraftforge.fml.Logging.SCAN;
import static net.minecraftforge.fml.loading.LogMarkers.SCAN;
public class BackgroundScanHandler
{

View file

@ -0,0 +1,113 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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.moddiscovery;
import net.minecraftforge.fml.loading.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.nio.file.*;
import java.util.*;
import java.util.function.Consumer;
import java.util.jar.Manifest;
import java.util.stream.Stream;
import static net.minecraftforge.fml.loading.LogMarkers.LOADING;
import static net.minecraftforge.fml.loading.LogMarkers.SCAN;
public class ExplodedDirectoryLocator implements IModLocator {
private static final Logger LOGGER = LogManager.getLogger();
private final List<Pair<Path,Path>> rootDirs;
private final Map<ModFile, Pair<Path,Path>> mods;
public ExplodedDirectoryLocator() {
this.rootDirs = new ArrayList<>();
this.mods = new HashMap<>();
}
@Override
public List<ModFile> scanMods() {
final Path modstoml = Paths.get("META-INF", "mods.toml");
// Collect all the mods.toml files found
rootDirs.forEach(pathPathPair -> {
Path resources = pathPathPair.getRight();
Path modtoml = resources.resolve(modstoml);
if (Files.exists(modtoml)) {
ModFile mf = new ModFile(pathPathPair.getLeft(), this);
mods.put(mf, pathPathPair);
} else {
LOGGER.warn(LOADING, "Failed to find exploded resource mods.toml in directory {}", resources.toString());
}
});
return new ArrayList<>(mods.keySet());
}
@Override
public String name() {
return "exploded directory";
}
@Override
public Path findPath(final ModFile modFile, final String... path) {
if (path.length < 1) {
throw new IllegalArgumentException("Missing path");
}
final Path target = Paths.get(path[0], Arrays.copyOfRange(path, 1, path.length));
// try right path first (resources)
Path found = mods.get(modFile).getRight().resolve(target);
if (Files.exists(found)) return found;
// then try left path (classes)
return mods.get(modFile).getLeft().resolve(target);
}
@Override
public void scanFile(final ModFile modFile, final Consumer<Path> pathConsumer) {
LOGGER.debug(SCAN,"Scanning directory {}", modFile.getFilePath().toString());
final Pair<Path, Path> pathPathPair = mods.get(modFile);
// classes are in the left branch of the pair
try (Stream<Path> files = Files.find(pathPathPair.getLeft(), Integer.MAX_VALUE, (p, a) -> p.getNameCount() > 0 && p.getFileName().toString().endsWith(".class"))) {
files.forEach(pathConsumer);
} catch (IOException e) {
e.printStackTrace();
}
LOGGER.debug(SCAN,"Directory scan complete {}", pathPathPair.getLeft());
}
@Override
public String toString()
{
return "{ExplodedDir locator}";
}
@Override
public Optional<Manifest> findManifest(Path file)
{
return Optional.empty();
}
@SuppressWarnings("unchecked")
@Override
public void initArguments(final Map<String, ?> arguments) {
final List<Pair<Path, Path>> explodedTargets = ((Map<String, List<Pair<Path, Path>>>) arguments).get("explodedTargets");
rootDirs.addAll(explodedTargets);
}
}

View file

@ -21,6 +21,7 @@ package net.minecraftforge.fml.loading.moddiscovery;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.jar.Manifest;
@ -39,4 +40,6 @@ public interface IModLocator {
void scanFile(final ModFile modFile, Consumer<Path> pathConsumer);
Optional<Manifest> findManifest(Path file);
void initArguments(Map<String, ?> arguments);
}

View file

@ -0,0 +1,43 @@
package net.minecraftforge.fml.loading.moddiscovery;
import net.minecraftforge.fml.loading.FMLPaths;
import net.minecraftforge.fml.loading.MavenCoordinateResolver;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class MavenDirectoryLocator extends AbstractJarFileLocator implements IModLocator {
private List<Path> modCoords;
@Override
public List<ModFile> scanMods() {
return modCoords.stream().
map(mc -> new ModFile(mc, this)).
peek(f->modJars.compute(f, (mf, fs)->createFileSystem(mf))).
collect(Collectors.toList());
}
@Override
public String name() {
return "maven libs";
}
public String toString() {
return "{Maven Directory locator for mods "+this.modCoords+"}";
}
@SuppressWarnings("unchecked")
@Override
public void initArguments(final Map<String, ?> arguments) {
final List<String> mavenRoots = (List<String>) arguments.get("mavenRoots");
final List<Path> mavenRootPaths = mavenRoots.stream().map(n -> FMLPaths.GAMEDIR.get().resolve(n)).collect(Collectors.toList());
final List<String> mods = (List<String>) arguments.get("mods");
List<Path> localModCoords = mods.stream().map(MavenCoordinateResolver::get).collect(Collectors.toList());
// find the modCoords path in each supplied maven path, and turn it into a mod file. (skips not found files)
this.modCoords = localModCoords.stream().map(mc -> mavenRootPaths.stream().map(root -> root.resolve(mc)).filter(path -> Files.exists(path)).findFirst().orElseThrow(() -> new IllegalArgumentException("Failed to locate requested mod coordinate " + mc))).collect(Collectors.toList());
}
}

View file

@ -26,6 +26,7 @@ import net.minecraftforge.fml.loading.ModSorter;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.nio.file.Path;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
@ -34,16 +35,18 @@ import java.util.Map;
import java.util.ServiceLoader;
import java.util.stream.Collectors;
import static net.minecraftforge.fml.Logging.SCAN;
import static net.minecraftforge.fml.loading.LogMarkers.SCAN;
public class ModDiscoverer {
private static final Logger LOGGER = LogManager.getLogger();
private final ServiceLoader<IModLocator> locators;
private final List<IModLocator> locatorList;
public ModDiscoverer() {
public ModDiscoverer(Map<String, ?> arguments) {
locators = ServiceLoader.load(IModLocator.class);
locatorList = ServiceLoaderStreamUtils.toList(this.locators);
locatorList.forEach(l->l.initArguments(arguments));
LOGGER.debug(SCAN,"Found Mod Locators : {}", ()->locatorList.stream().map(iModLocator -> "("+iModLocator.name() + ":" + iModLocator.getClass().getPackage().getImplementationVersion()+")").collect(Collectors.joining(",")));
}
@ -79,4 +82,8 @@ public class ModDiscoverer {
loadingModList.addForScanning(backgroundScanHandler);
return backgroundScanHandler;
}
public void addExplodedTarget(final Path compiledClasses, final Path forgemodstoml) {
}
}

View file

@ -41,8 +41,8 @@ import java.util.function.Supplier;
import java.util.jar.Attributes;
import java.util.jar.Manifest;
import static net.minecraftforge.fml.Logging.LOADING;
import static net.minecraftforge.fml.Logging.SCAN;
import static net.minecraftforge.fml.loading.LogMarkers.LOADING;
import static net.minecraftforge.fml.loading.LogMarkers.SCAN;
public class ModFile
{

View file

@ -35,7 +35,7 @@ import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import static net.minecraftforge.fml.Logging.LOADING;
import static net.minecraftforge.fml.loading.LogMarkers.LOADING;
public class ModFileParser {

View file

@ -24,42 +24,31 @@ import net.minecraftforge.fml.loading.FMLPaths;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.zip.ZipError;
import static cpw.mods.modlauncher.api.LamdbaExceptionUtils.uncheck;
import static net.minecraftforge.fml.Logging.SCAN;
import static net.minecraftforge.fml.loading.LogMarkers.SCAN;
/**
* Support loading mods located in JAR files in the mods folder
*/
public class ModsFolderLocator implements IModLocator {
public class ModsFolderLocator extends AbstractJarFileLocator implements IModLocator {
private static final String SUFFIX = ".jar";
private static final Logger LOGGER = LogManager.getLogger();
private final Path modFolder;
private final Map<ModFile, FileSystem> modJars;
public ModsFolderLocator() {
this(FMLPaths.MODSDIR.get());
}
ModsFolderLocator(Path modFolder) {
super();
this.modFolder = modFolder;
this.modJars = new HashMap<>();
}
@Override
@ -78,52 +67,13 @@ public class ModsFolderLocator implements IModLocator {
return "mods folder";
}
private FileSystem createFileSystem(ModFile modFile) {
try {
return FileSystems.newFileSystem(modFile.getFilePath(), modFile.getClass().getClassLoader());
} catch (ZipError | IOException e) {
LOGGER.debug(SCAN,"Ignoring invalid JAR file {}", modFile.getFilePath());
return null;
}
}
@Override
public Path findPath(final ModFile modFile, final String... path) {
if (path.length < 1) {
throw new IllegalArgumentException("Missing path");
}
return modJars.get(modFile).getPath(path[0], Arrays.copyOfRange(path, 1, path.length));
}
@Override
public void scanFile(final ModFile file, final Consumer<Path> pathConsumer) {
LOGGER.debug(SCAN,"Scan started: {}", file);
FileSystem fs = modJars.get(file);
fs.getRootDirectories().forEach(path -> {
try (Stream<Path> files = Files.find(path, Integer.MAX_VALUE, (p, a) -> p.getNameCount() > 0 && p.getFileName().toString().endsWith(".class"))) {
files.forEach(pathConsumer);
} catch (IOException e) {
e.printStackTrace();
}
});
LOGGER.debug(SCAN,"Scan finished: {}", file);
}
@Override
public String toString() {
return "{ModJarsFolder locator at "+this.modFolder+"}";
}
@Override
public Optional<Manifest> findManifest(final Path file)
{
try (JarFile jf = new JarFile(file.toFile()))
{
return Optional.ofNullable(jf.getManifest());
}
catch (IOException e)
{
return Optional.empty();
}
public void initArguments(final Map<String, ?> arguments) {
}
}

View file

@ -29,7 +29,7 @@ import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import static net.minecraftforge.fml.Logging.SCAN;
import static net.minecraftforge.fml.loading.LogMarkers.SCAN;
public class Scanner {
private static final Logger LOGGER = LogManager.getLogger();

View file

@ -17,7 +17,7 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package net.minecraftforge.fml.common.toposort;
package net.minecraftforge.fml.loading.toposort;
import com.google.common.collect.Sets;
import net.minecraftforge.fml.loading.EarlyLoadingException;

View file

@ -0,0 +1 @@
net.minecraftforge.fml.loading.RuntimeDistCleaner

View file

@ -0,0 +1,3 @@
net.minecraftforge.fml.loading.moddiscovery.ModsFolderLocator
net.minecraftforge.fml.loading.moddiscovery.MavenDirectoryLocator
net.minecraftforge.fml.loading.moddiscovery.ExplodedDirectoryLocator

View file

@ -68,7 +68,9 @@ public class LaunchTesting
"--version", "FMLDev",
"--assetIndex", "1.13",
"--assetsDir", assets,
"--userProperties", "{}");
"--userProperties", "{}",
"--fml.forgeVersion", "24.0.0",
"--fml.mcVersion", "1.13");
} else if (Objects.equals(target, "fmldevserver")) {
String[] launchargs = ObjectArrays.concat(new String[] {"--launchTarget", target,
"--gameDir", "."}, args, String.class);

View file

@ -21,6 +21,7 @@ package net.minecraftforge.fml;
import net.minecraftforge.fml.common.event.ModLifecycleEvent;
import net.minecraftforge.fml.javafmlmod.FMLModContainer;
import net.minecraftforge.fml.language.ILifecycleEvent;
import java.util.List;
import java.util.function.Consumer;
@ -46,7 +47,7 @@ public enum LifecycleEventProvider
}
public static class LifecycleEvent {
public static class LifecycleEvent implements ILifecycleEvent<LifecycleEvent> {
private final ModLoadingStage stage;
public LifecycleEvent(ModLoadingStage stage)

View file

@ -92,7 +92,7 @@ public class ModList
}
public void dispatchLifeCycleEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent, final Consumer<List<ModLoadingException>> errorHandler) {
FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.preLifecycleEvent(lifecycleEvent));
FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.consumeLifecycleEvent(()->lifecycleEvent));
DeferredWorkQueue.deferredWorkQueue.clear();
try
{
@ -105,7 +105,7 @@ public class ModList
LOGGER.debug(LOADING, "Dispatching synchronous work, {} jobs", DeferredWorkQueue.deferredWorkQueue.size());
DeferredWorkQueue.deferredWorkQueue.forEach(FutureTask::run);
LOGGER.debug(LOADING, "Synchronous work queue complete");
FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.postLifecycleEvent(lifecycleEvent));
FMLLoader.getLanguageLoadingProvider().forEach(lp->lp.consumeLifecycleEvent(()->lifecycleEvent));
}
public void setLoadedMods(final List<ModContainer> modContainers)

View file

@ -28,6 +28,7 @@ import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.SecureClassLoader;
import java.util.function.Predicate;
import static net.minecraftforge.fml.Logging.LOADING;
@ -39,8 +40,11 @@ public class ModLoadingClassLoader extends SecureClassLoader
ClassLoader.registerAsParallelCapable();
}
private final Predicate<String> classLoadingPredicate;
protected ModLoadingClassLoader(final ClassLoader parent) {
super(parent);
this.classLoadingPredicate = FMLLoader.getClassLoaderExclusions();
}
@Override
@ -49,21 +53,29 @@ public class ModLoadingClassLoader extends SecureClassLoader
return super.getResource(name);
}
/*
@Override
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
{
final String className = name.replace('.', '/').concat(".class");
final Path classResource = FMLLoader.getLoadingModList().findResource(className);
if (classResource != null)
{
return findClass(name);
}
return super.loadClass(name, resolve);
}
*/
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException
{
if (!classLoadingPredicate.test(name)) {
LOGGER.debug(LOADING, "Delegating to parent {}", name);
return getParent().loadClass(name);
}
LOGGER.debug(LOADING, "Loading class {}", name);
final String className = name.replace('.','/').concat(".class");
final Path classResource = FMLLoader.getLoadingModList().findResource(className);

View file

@ -1,63 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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.client.gui;
import net.minecraft.client.gui.GuiScreen;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.common.toposort.ModSortingException;
import net.minecraftforge.fml.common.toposort.ModSortingException.SortingExceptionData;
public class GuiSortingProblem extends GuiScreen {
private SortingExceptionData<ModContainer> failedList;
public GuiSortingProblem(ModSortingException modSorting)
{
this.failedList = modSorting.getExceptionData();
}
@Override
public void initGui()
{
super.initGui();
}
@Override
public void render(int mouseX, int mouseY, float partialTicks)
{
this.drawDefaultBackground();
int offset = Math.max(85 - (failedList.getVisitedNodes().size() + 3) * 10, 10);
this.drawCenteredString(this.fontRenderer, "Forge Mod Loader has found a problem with your minecraft installation", this.width / 2, offset, 0xFFFFFF);
offset+=10;
this.drawCenteredString(this.fontRenderer, "A mod sorting cycle was detected and loading cannot continue", this.width / 2, offset, 0xFFFFFF);
offset+=10;
this.drawCenteredString(this.fontRenderer, String.format("The first mod in the cycle is %s", failedList.getFirstBadNode()), this.width / 2, offset, 0xFFFFFF);
offset+=10;
this.drawCenteredString(this.fontRenderer, "The remainder of the cycle involves these mods", this.width / 2, offset, 0xFFFFFF);
offset+=5;
for (ModContainer mc : failedList.getVisitedNodes())
{/* TODO Mod dependencies
offset+=10;
this.drawCenteredString(this.fontRenderer, String.format("%s : before: %s, after: %s", mc.toString(), mc.getDependants(), mc.getDependencies()), this.width / 2, offset, 0xEEEEEE);
*/}
offset+=20;
this.drawCenteredString(this.fontRenderer, "The file 'ForgeModLoader-client-0.log' contains more information", this.width / 2, offset, 0xFFFFFF);
}
}

View file

@ -1,93 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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.common.toposort;
import java.util.Set;
import net.minecraft.client.gui.GuiScreen;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.client.IDisplayableError;
import net.minecraftforge.fml.client.gui.GuiSortingProblem;
import net.minecraftforge.fml.common.EnhancedRuntimeException;
import net.minecraftforge.fml.common.EnhancedRuntimeException.WrappedPrintStream;
public class ModSortingException extends EnhancedRuntimeException implements IDisplayableError
{
private static final long serialVersionUID = 1L;
public class SortingExceptionData<T>
{
public SortingExceptionData(T node, Set<T> visitedNodes)
{
this.firstBadNode = node;
this.visitedNodes = visitedNodes;
}
private T firstBadNode;
private Set<T> visitedNodes;
public T getFirstBadNode()
{
return firstBadNode;
}
public Set<T> getVisitedNodes()
{
return visitedNodes;
}
}
private SortingExceptionData<?> sortingExceptionData;
public <T> ModSortingException(String string, T node, Set<T> visitedNodes)
{
super(string);
this.sortingExceptionData = new SortingExceptionData<>(node, visitedNodes);
}
@SuppressWarnings("unchecked")
public <T> SortingExceptionData<T> getExceptionData()
{
return (SortingExceptionData<T>) sortingExceptionData;
}
@Override
protected void printStackTrace(WrappedPrintStream stream)
{
SortingExceptionData<ModContainer> exceptionData = getExceptionData();
stream.println("A dependency cycle was detected in the input mod set so an ordering cannot be determined");
stream.println("The first mod in the cycle is " + exceptionData.getFirstBadNode());
stream.println("The mod cycle involves:");
for (ModContainer mc : exceptionData.getVisitedNodes())
{/* TODO Mod dependencies
stream.println(String.format("\t%s : before: %s, after: %s", mc.toString(), mc.getDependants(), mc.getDependencies()));
*/}
}
@Override
@OnlyIn(Dist.CLIENT)
public GuiScreen createGui()
{
return new GuiSortingProblem(this);
}
}

View file

@ -19,7 +19,7 @@
package net.minecraftforge.fml.javafmlmod;
import net.minecraftforge.fml.LifecycleEventProvider;
import net.minecraftforge.fml.language.ILifecycleEvent;
import net.minecraftforge.fml.language.IModLanguageProvider;
import net.minecraftforge.fml.language.IModInfo;
import net.minecraftforge.fml.language.ModFileScanData;
@ -32,6 +32,7 @@ import java.lang.reflect.InvocationTargetException;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import static net.minecraftforge.fml.Logging.LOADING;
@ -100,12 +101,7 @@ public class FMLJavaModLanguageProvider implements IModLanguageProvider
}
@Override
public void preLifecycleEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent)
{
}
public <R extends ILifecycleEvent<R>> void consumeLifecycleEvent(final Supplier<R> consumeEvent) {
@Override
public void postLifecycleEvent(LifecycleEventProvider.LifecycleEvent lifecycleEvent)
{
}
}

View file

@ -1,101 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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.moddiscovery;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.jar.Manifest;
import java.util.stream.Stream;
import static net.minecraftforge.fml.Logging.LOADING;
import static net.minecraftforge.fml.Logging.SCAN;
public class ExplodedDirectoryLocator implements IModLocator {
private static final String DIR = System.getProperty("fml.explodedDir", "modclasses");
private static final Logger LOGGER = LogManager.getLogger();
private final Path rootDir;
public ExplodedDirectoryLocator() {
this.rootDir = FileSystems.getDefault().getPath(DIR);
if (!Files.exists(this.rootDir)) {
LOGGER.debug(LOADING,"Creating directory {}" + this.rootDir);
try
{
Files.createDirectory(this.rootDir);
}
catch (IOException e)
{
LOGGER.error(LOADING,"Error creating {}", this.rootDir, e);
throw new RuntimeException(e);
}
}
}
@Override
public List<ModFile> scanMods() {
return Collections.singletonList(new ModFile(rootDir, this));
}
@Override
public String name() {
return "exploded directory";
}
@Override
public Path findPath(final ModFile modFile, final String... path) {
if (path.length < 1) {
throw new IllegalArgumentException("Missing path");
}
return rootDir.resolve(FileSystems.getDefault().getPath(path[0], Arrays.copyOfRange(path, 1, path.length)));
}
@Override
public void scanFile(final ModFile modFile, final Consumer<Path> pathConsumer) {
LOGGER.debug(SCAN,"Scanning directory {}", rootDir);
try (Stream<Path> files = Files.find(rootDir, Integer.MAX_VALUE, (p, a) -> p.getNameCount() > 0 && p.getFileName().toString().endsWith(".class"))) {
files.forEach(pathConsumer);
} catch (IOException e) {
e.printStackTrace();
}
LOGGER.debug(SCAN,"Directory scan complete {}", rootDir);
}
@Override
public String toString()
{
return "{ExplodedDir locator at "+this.rootDir+"}";
}
@Override
public Optional<Manifest> findManifest(Path file)
{
return Optional.empty();
}
}

View file

@ -1,244 +0,0 @@
/*
* Minecraft Forge
* Copyright (c) 2016-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.moddiscovery;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.fml.loading.FMLEnvironment;
import net.minecraftforge.fml.loading.moddiscovery.IModLocator;
import net.minecraftforge.fml.loading.moddiscovery.ModFile;
import net.minecraftforge.versions.forge.ForgeVersion;
import net.minecraftforge.versions.mcp.MCPVersion;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.*;
import java.util.*;
import java.util.function.Consumer;
import java.util.jar.Manifest;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import static net.minecraftforge.fml.Logging.SCAN;
/*
* Locate Forge in the classpath, this works similar to ClasspathLocator.
* Forge/Minecraft are in the following formats:
* Runtime:
* %lib%/net/minecraftforge/forge/{ver}/forge-{ver}.jar or ./forge-{ver}-universal.jar - Client is in libs, Server is in root
* %lib%/net/minecraftforge/forge/{ver}/forge-{ver}-{client|server}.jar - Forge, Vanilla classes, Patched
* %lib%/net/minecraft/{client|server}/{mcver}/{client|server}-{mcver}-srg.jar - Vanilla classes, SRG, Unpatched
*
* UserDev:
* Forge and vanilla is in a single jar file, patched, on the classpath.
*
* Eclipse:
* bin/{sourceset}/
*
* Intellij: TODO: Verify, I don't use intellij
* out/production/resources/../classes/ - Resources are a separate folder so we have to navigate to classes
*
* Gradle:
* build/classes/{language}/{sourceset}
* build/resources/{sourceset}
*
*/
public class ForgeLocator implements IModLocator
{
private static final Logger LOGGER = LogManager.getLogger();
private static final String FORGE_META = "forgemod.toml";
private static final String MAIN_SOURCESET = "main";
private static final String LIB_RESOURCE = "org/objectweb/asm/Opcodes.class";
private static final String LIB_PATH = "org/ow2/asm/asm".replace('/', File.separatorChar);
private List<Path> roots = new ArrayList<>();
private boolean filterClasses = false;
public ForgeLocator() { }
@Override
public List<ModFile> scanMods()
{
ClassLoader loader = Thread.currentThread().getContextClassLoader();
URL url = loader.getResource(FORGE_META);
if (url == null)
throw new IllegalStateException("Could not locate \"" + FORGE_META + "\", Forge is correupt/not on classpath!");
File base = getRoot(url, FORGE_META);
if (base == null)
throw new IllegalStateException("Could not determine root for Forge resource: " + url);
url = loader.getResource(LIB_RESOURCE);
if (url == null)
throw new IllegalStateException("Could not locate \"" + LIB_RESOURCE + "\" to locate libraries directory.");
File libs = getRoot(url, LIB_RESOURCE);
//If In maven style the libraries folder we need to drop the file name, and version folder
if (libs != null && libs.getParentFile().getParentFile().getAbsolutePath().endsWith(LIB_PATH))
{
int count = 2 + LIB_PATH.split(Pattern.quote("" + File.separatorChar)).length;
for (int x = 0; x < count; x++)
libs = libs.getParentFile();
}
else
{
libs = null;
}
LOGGER.info("Forge Locator: ");
LOGGER.info(" Forge: " + base);
LOGGER.info(" Libraries: " + libs);
Path path = base.toPath();
if (base.isDirectory())
{ // Development workspace.
int count = path.getNameCount();
//Eclipse: /bin/{sourceset}/
if (count >= 2 && MAIN_SOURCESET.equals(path.getName(count - 1).toString()) && "bin".equals(path.getName(count - 2).toString()))
{
roots.add(path);
roots.add(path.getParent().resolve("userdev")); //Do I need this?
} //Intellij: /out/production/resources/ & /out/production/classes/
else if (count >= 1 && "resources".equals(path.getName(count - 1).toString()))
{
roots.add(path.getParent().resolve("classes"));
roots.add(path);
} //Gradle: build/resources/{sourceset} & build/classes/{language}/{sourceset}
else if (count >= 2 && MAIN_SOURCESET.equals(path.getName(count - 1).toString()) && "resources".equals(path.getName(count - 2).toString()))
{
Path classes = path.getParent().getParent().resolve("classes");
roots.add(classes.resolve(MAIN_SOURCESET));
roots.add(classes.resolve("userdev"));
roots.add(path);
roots.add(path.getParent().resolve("userdev"));
}
filterClasses = true;
}
else if (base.isFile())
{
if (libs == null) //User dev workspace.
{
roots.add(path);
}
else //Standard install
{
String dist = FMLEnvironment.dist == Dist.CLIENT ? "client" : "server";
String version = MCPVersion.getMCVersion() + "-" + ForgeVersion.getVersion();
roots.add(path); // universal
roots.add(libs.toPath().resolve(Paths.get(ForgeVersion.getGroup().replace('.', File.separatorChar), "forge", version, "forge-" + version + "-" + dist + ".jar"))); // Patched classes
roots.add(libs.toPath().resolve(Paths.get("net", "minecraft", dist, MCPVersion.getMCPandMCVersion(), dist + "-" + MCPVersion.getMCPandMCVersion() + "-srg.jar"))); //Vanilla jar in srg names
}
}
else
throw new IllegalStateException("Forge path is neither File or Directory... " + base);
if (roots.isEmpty())
return Collections.emptyList();
roots.forEach(r -> LOGGER.info(" Root: " + r));
return Collections.singletonList(new ModFile(roots.get(0), this));
}
protected File getRoot(URL url, String resource)
{
String path = url.getPath();
if ("jar".equals(url.getProtocol()))
{
int idx = path.indexOf("!/");
path = path.substring(0, idx);
}
else
{
//Strip resource path off the end
path = path.substring(0, path.length() - resource.length());
}
if (path.startsWith("file:"))
path = path.substring(5);
return new File(path).getAbsoluteFile();
}
@Override
public String name()
{
return "forge locator";
}
@Override
public Path findPath(final ModFile modFile, String... path)
{
if (path.length < 1)
throw new IllegalArgumentException("Missing path");
if (path.length == 2 && "META-INF".equals(path[0]) && "mods.toml".equals(path[1]))
path = FORGE_META.split("/");
if (filterClasses && path[path.length - 1].endsWith(".class"))
path[path.length - 1] = path[path.length - 1] + "THIS_FILE_WILL_NEVER_EXITS_DIRTY_HAX";
String[] tail = Arrays.copyOfRange(path, 1, path.length);
for (Path root : roots)
{
Path target = root.resolve(root.getFileSystem().getPath(path[0], tail));
if (Files.exists(target))
return target;
}
return modFile.getFilePath().resolve(modFile.getFilePath().getFileSystem().getPath(path[0], tail));
}
@Override
public void scanFile(final ModFile modFile, final Consumer<Path> pathConsumer)
{
final Set<String> visited = new HashSet<>();
roots.stream().forEach(root ->
{
LOGGER.debug(SCAN,"Scanning Forge: " + root);
try (Stream<Path> files = Files.find(root, Integer.MAX_VALUE, (p, a) -> p.getNameCount() > 0 && p.getFileName().toString().endsWith(".class")))
{
files.forEach(f -> {
String relative = root.relativize(f).toString(); //TODO: Test jared files...
if (visited.add(relative))
pathConsumer.accept(Paths.get(relative));
});
}
catch (IOException e)
{
e.printStackTrace();
}
});
LOGGER.debug(SCAN,"Forge scan complete");
}
@Override
public String toString()
{
return "{Forge locator}";
}
@Override
public Optional<Manifest> findManifest(Path file)
{
return Optional.empty();
}
}

View file

@ -37,13 +37,11 @@ import java.util.stream.Collectors;
public class ResourcePackLoader
{
private static Map<ModFile, ModFileResourcePack> modResourcePacks;
private static AbstractResourcePack forgePack;
private static ResourcePackList<?> resourcePackList;
public static IResourcePack getResourcePackFor(String modId)
{
if (modId == "forge") return forgePack;
else return modResourcePacks.get(ModList.get().getModFileById(modId).getFile());
return modResourcePacks.get(ModList.get().getModFileById(modId).getFile());
}
@SuppressWarnings("unchecked")
@ -56,9 +54,6 @@ public class ResourcePackLoader
modResourcePacks = ModList.get().getModFiles().stream().
map(mf -> new ModFileResourcePack(mf.getFile())).
collect(Collectors.toMap(ModFileResourcePack::getModFile, Function.identity()));
forgePack = Files.isDirectory(FMLLoader.getForgePath()) ?
new ForgeFolderPack(FMLLoader.getForgePath().toFile()) :
new ForgeFilePack(FMLLoader.getForgePath().toFile());
resourcePacks.addPackFinder(new ModPackFinder());
}
@ -111,7 +106,6 @@ public class ResourcePackLoader
@Override
public <T extends ResourcePackInfo> void addPackInfosToMap(Map<String, T> packList, ResourcePackInfo.IFactory<T> factory)
{
packList.put("forge", ResourcePackInfo.func_195793_a("forge", true, ()->forgePack, factory, ResourcePackInfo.Priority.BOTTOM));
for (Entry<ModFile, ModFileResourcePack> e : modResourcePacks.entrySet())
{
String name = "modfile/" + e.getKey().getFileName();

View file

@ -1,4 +1,3 @@
net.minecraftforge.fml.loading.RuntimeDistCleaner
net.minecraftforge.common.asm.RuntimeEnumExtender
net.minecraftforge.common.asm.ObjectHolderDefinalize
net.minecraftforge.common.asm.CapabilityInjectDefinalize

View file

@ -1,2 +0,0 @@
net.minecraftforge.fml.loading.moddiscovery.ModsFolderLocator
net.minecraftforge.fml.loading.moddiscovery.ForgeLocator

View file

@ -148,4 +148,11 @@ public class ClasspathLocator implements IModLocator
{
return Optional.empty();
}
@Override
public void initArguments(final Map<String, ?> arguments) {
}
}