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

@ -91,9 +91,19 @@ project(':forge') {
compileJava.sourceCompatibility = compileJava.targetCompatibility = sourceCompatibility = targetCompatibility = '1.8' // Need this here so eclipse task generates correctly.
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 super.commonLibPaths(new Path[] {getForgePath(), patchedBinariesPath, srgMcPath});
return LibraryFinder.commonLibPaths(new Path[] {FMLLoader.getForgePath()});
}
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();