Skip to content

Commit

Permalink
separated fmllauncher code from main forge mod code. This enables much
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
cpw committed Dec 31, 2018
1 parent 558e1d2 commit 48846bc
Show file tree
Hide file tree
Showing 72 changed files with 692 additions and 754 deletions.
59 changes: 58 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
Expand Down Expand Up @@ -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"]) {
Expand Down Expand Up @@ -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/')
}
}

Expand Down Expand Up @@ -811,6 +867,7 @@ project(':forge') {
artifact makeMdk
artifact userdevJar
artifact sourcesJar
artifact fmllauncherJar

pom {
name = 'forge'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package net.minecraftforge.fml.language;

public interface ILifecycleEvent<R extends ILifecycleEvent<?>> {
@SuppressWarnings("unchecked")
default R concrete() {
return (R) this;
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@

package net.minecraftforge.fml.loading;

import java.util.Arrays;
import java.util.List;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,45 +19,41 @@

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 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);
Expand All @@ -66,9 +62,7 @@ public Callable<Void> launchService(String[] arguments, ITransformingClassLoader
}

@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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,12 @@
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();
Expand All @@ -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);
}

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);
public Path[] getMCPaths(final String mcVersion, final String forgeVersion) {
return LibraryFinder.getMCPaths(mcVersion, forgeVersion, getDist().isClient() ? "client" : "server");
}

Path getForgePath() {
if (forgePath == null) {
forgePath = findJarPathFor("net/minecraftforge/versions/forge/ForgeVersion.class", "forge");
LOGGER.debug(CORE, "Found forge path {}", forgePath);
}
return forgePath;
public void setup(final IEnvironment environment, final Map<String, ?> arguments)
{

}

public abstract Dist getDist();

protected void beforeStart(ITransformingClassLoader launchClassLoader)
{
FMLLoader.beforeStart(launchClassLoader, getForgePath());
FMLLoader.beforeStart(launchClassLoader);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,13 @@
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
{
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,57 +23,68 @@
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);
return null;
};
}

@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
Expand Down
Loading

0 comments on commit 48846bc

Please sign in to comment.