Finish up classloading system. Use ModJARURL to locate resources
and enable ModLauncher to use those instead. This allows all mods to load within the scope of the game classloader, removing weird conflicts and class discovery problems.
This commit is contained in:
parent
6baddc7e26
commit
fd9c83b65f
47
build.gradle
47
build.gradle
|
@ -126,6 +126,7 @@ project(':forge') {
|
|||
mavenLocal()
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
ext {
|
||||
SPEC_VERSION = '24.0' // This is overwritten by git tag, but here so dev time doesnt explode
|
||||
// The new versioning sceme is <MCVersion>-<ForgeMC>.<RB>.<CommitsSinceRB>
|
||||
|
@ -133,11 +134,30 @@ project(':forge') {
|
|||
// Essentially, the same as the old, except dropping the first number, and the builds are no longer unique.
|
||||
MCP_ARTIFACT = project(':mcp').mcp.config
|
||||
}
|
||||
|
||||
def getVersion = {
|
||||
//TAG-offset-hash
|
||||
def raw = grgit.describe(longDescr: true)
|
||||
def desc = (raw == null ? '0.0-0-unknown' : grgit.describe(longDescr: true)).split('-') as List
|
||||
def hash = desc.remove(desc.size() - 1)
|
||||
def offset = desc.remove(desc.size() - 1)
|
||||
def tag = desc.join('-')
|
||||
def branch = grgit.branch.current().name
|
||||
if (branch in ['master', 'HEAD', MC_VERSION, MC_VERSION + '.0'])
|
||||
branch = null
|
||||
if (branch != null && branch.endsWith('.x') && MC_VERSION.startsWith(branch.substring(0, branch.length() - 2))) //1.13.x
|
||||
branch = null
|
||||
SPEC_VERSION = tag
|
||||
return "${MC_VERSION}-${tag}.${offset}${t -> if (branch != null) t << '-' + branch}".toString() //Bake the response instead of making it dynamic
|
||||
}
|
||||
|
||||
version = getVersion()
|
||||
|
||||
patcher {
|
||||
parent = project(':clean')
|
||||
patches = file("$rootDir/patches/minecraft")
|
||||
patchedSrc = file('src/main/java')
|
||||
accessTransformer = file("$rootDir/src/main/resources/forge_at.cfg")
|
||||
accessTransformer = file("$rootDir/src/main/resources/META-INF/accesstransformer.cfg")
|
||||
exc = file("$rootDir/src/main/resources/forge.exc")
|
||||
srgPatches = true
|
||||
clientRun {
|
||||
|
@ -153,9 +173,10 @@ project(':forge') {
|
|||
'org.lwjgl.system.SharedLibraryExtractDirectory': 'lwjgl_dll',
|
||||
'mc.version': MC_VERSION,
|
||||
'mcp.version': MCP_VERSION,
|
||||
'forge.version': "${project.version.substring(MC_VERSION.length() + 1)}".toString(),
|
||||
'forge.version': project.version.substring(MC_VERSION.length() + 1),
|
||||
'forge.spec': SPEC_VERSION,
|
||||
'forge.group': project.group
|
||||
'forge.group': project.group,
|
||||
'fmllauncher.version': SPEC_VERSION
|
||||
]
|
||||
}
|
||||
serverRun {
|
||||
|
@ -173,24 +194,6 @@ project(':forge') {
|
|||
}
|
||||
}
|
||||
|
||||
def getVersion = {
|
||||
//TAG-offset-hash
|
||||
def raw = grgit.describe(longDescr: true)
|
||||
def desc = (raw == null ? '0.0-0-unknown' : grgit.describe(longDescr: true)).split('-') as List
|
||||
def hash = desc.remove(desc.size() - 1)
|
||||
def offset = desc.remove(desc.size() - 1)
|
||||
def tag = desc.join('-')
|
||||
def branch = grgit.branch.current().name
|
||||
if (branch in ['master', 'HEAD', MC_VERSION, MC_VERSION + '.0'])
|
||||
branch = null
|
||||
if (branch != null && branch.endsWith('.x') && MC_VERSION.startsWith(branch.substring(0, branch.length() - 2))) //1.13.x
|
||||
branch = null
|
||||
SPEC_VERSION = tag
|
||||
return "${MC_VERSION}-${tag}.${offset}${t -> if (branch != null) t << '-' + branch}".toString() //Bake the response instead of making it dynamic
|
||||
}
|
||||
|
||||
version = getVersion()
|
||||
|
||||
ext {
|
||||
MANIFESTS = [
|
||||
'/': [
|
||||
|
@ -251,7 +254,7 @@ project(':forge') {
|
|||
installer 'org.ow2.asm:asm:6.2'
|
||||
installer 'org.ow2.asm:asm-commons:6.2'
|
||||
installer 'org.ow2.asm:asm-tree:6.2'
|
||||
installer 'cpw.mods:modlauncher:0.2.0'
|
||||
installer 'cpw.mods:modlauncher:0.3.0'
|
||||
installer 'net.minecraftforge:accesstransformers:0.10+:shadowed'
|
||||
installer 'net.minecraftforge:eventbus:0.1+:service'
|
||||
installer 'net.minecraftforge:forgespi:0.1+'
|
||||
|
|
|
@ -71,7 +71,7 @@ public class FMLClientLaunchProvider extends FMLCommonLaunchHandler implements I
|
|||
final String forgeVersion = (String) arguments.get("forgeVersion");
|
||||
final String mcVersion = (String) arguments.get("mcVersion");
|
||||
final String forgeGroup = (String) arguments.get("forgeGroup");
|
||||
mods.add(forgeGroup+":forge::universal:"+mcVersion+"-"+forgeVersion);
|
||||
mods.add(forgeGroup+":forge:universal:"+mcVersion+"-"+forgeVersion);
|
||||
// generics are gross yea?
|
||||
((Map)arguments).put("mavenRoots", mavenRoots);
|
||||
((Map)arguments).put("mods", mods);
|
||||
|
|
|
@ -30,6 +30,7 @@ import org.apache.logging.log4j.Logger;
|
|||
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.Callable;
|
||||
|
@ -87,8 +88,8 @@ public class FMLDevClientLaunchProvider extends FMLCommonLaunchHandler implement
|
|||
{
|
||||
// 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));
|
||||
((Map<String, List<Pair<Path,List<Path>>>>) arguments).computeIfAbsent("explodedTargets", a->new ArrayList<>()).
|
||||
add(Pair.of(forgemodstoml, Collections.singletonList(compiledClasses)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -29,9 +29,7 @@ import org.apache.logging.log4j.LogManager;
|
|||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import static net.minecraftforge.fml.loading.LogMarkers.CORE;
|
||||
|
@ -86,8 +84,8 @@ public class FMLDevServerLaunchProvider extends FMLCommonLaunchHandler implement
|
|||
{
|
||||
// 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));
|
||||
((Map<String, List<Pair<Path,List<Path>>>>) arguments).computeIfAbsent("explodedTargets", a->new ArrayList<>()).
|
||||
add(Pair.of(forgemodstoml, Collections.singletonList(compiledClasses)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package net.minecraftforge.fml.loading;
|
||||
|
||||
import cpw.mods.modlauncher.TransformingClassLoader;
|
||||
import cpw.mods.modlauncher.api.IEnvironment;
|
||||
import cpw.mods.modlauncher.api.ILaunchHandlerService;
|
||||
import cpw.mods.modlauncher.api.ITransformationService;
|
||||
|
@ -56,7 +57,7 @@ public class FMLLoader
|
|||
private static LanguageLoadingProvider languageLoadingProvider;
|
||||
private static Dist dist;
|
||||
private static LoadingModList loadingModList;
|
||||
private static ClassLoader launchClassLoader;
|
||||
private static TransformingClassLoader launchClassLoader;
|
||||
private static RuntimeDistCleaner runtimeDistCleaner;
|
||||
private static Path gamePath;
|
||||
private static Path forgePath;
|
||||
|
@ -207,7 +208,7 @@ public class FMLLoader
|
|||
|
||||
public static void beforeStart(ITransformingClassLoader launchClassLoader)
|
||||
{
|
||||
FMLLoader.launchClassLoader = launchClassLoader.getInstance();
|
||||
FMLLoader.launchClassLoader = (TransformingClassLoader) launchClassLoader.getInstance();
|
||||
}
|
||||
|
||||
|
||||
|
@ -217,7 +218,7 @@ public class FMLLoader
|
|||
|
||||
}
|
||||
|
||||
public static ClassLoader getLaunchClassLoader()
|
||||
public static TransformingClassLoader getLaunchClassLoader()
|
||||
{
|
||||
return launchClassLoader;
|
||||
}
|
||||
|
|
|
@ -69,7 +69,7 @@ public class FMLServerLaunchProvider extends FMLCommonLaunchHandler implements I
|
|||
final String forgeVersion = (String) arguments.get("forgeVersion");
|
||||
final String mcVersion = (String) arguments.get("mcVersion");
|
||||
final String forgeGroup = (String) arguments.get("forgeGroup");
|
||||
mods.add(forgeGroup+":forge::universal:"+mcVersion+"-"+forgeVersion);
|
||||
mods.add(forgeGroup+":forge:universal:"+mcVersion+"-"+forgeVersion);
|
||||
// generics are gross yea?
|
||||
((Map)arguments).put("mavenRoots", mavenRoots);
|
||||
((Map)arguments).put("mods", mods);
|
||||
|
|
|
@ -49,22 +49,27 @@ public class LibraryFinder {
|
|||
|
||||
static Path findJarPathFor(final String className, final String jarName) {
|
||||
final URL resource = LibraryFinder.class.getClassLoader().getResource(className);
|
||||
return findJarPathFor(className, jarName, resource);
|
||||
}
|
||||
|
||||
public static Path findJarPathFor(final String resourceName, final String jarName, final URL resource) {
|
||||
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())));
|
||||
path = Paths.get(new URI("file://"+uri.getRawSchemeSpecificPart().substring(0, uri.getRawSchemeSpecificPart().length()-resourceName.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);
|
||||
LOGGER.error(CORE, "Failed to find JAR for class {} - {}", resourceName, jarName);
|
||||
throw new RuntimeException("Unable to locate "+resourceName+" - "+jarName, e);
|
||||
}
|
||||
}
|
||||
static Path[] commonLibPaths(Path[] extras) {
|
||||
|
||||
public static Path[] commonLibPaths(Path[] extras) {
|
||||
final Path realms = findJarPathFor("com/mojang/realmsclient/RealmsVersion.class", "realms");
|
||||
return ObjectArrays.concat(extras, realms);
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ import net.minecraftforge.fml.loading.moddiscovery.ModFile;
|
|||
import net.minecraftforge.fml.loading.moddiscovery.ModFileInfo;
|
||||
import net.minecraftforge.fml.loading.moddiscovery.ModInfo;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
|
@ -102,6 +104,20 @@ public class LoadingModList
|
|||
return null;
|
||||
}
|
||||
|
||||
public URL findURLForResource(final String resourceName) {
|
||||
for (ModFileInfo mf : modFiles) {
|
||||
final Path resource = mf.getFile().findResource(resourceName);
|
||||
if (Files.exists(resource)) {
|
||||
try {
|
||||
return new URL("modjar://"+mf.getMods().get(0).getModId()+"/"+resourceName);
|
||||
} catch (MalformedURLException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public ModFileInfo getModFileById(String modid)
|
||||
{
|
||||
return this.fileById.get(modid);
|
||||
|
|
|
@ -25,7 +25,8 @@ import java.nio.file.Paths;
|
|||
/**
|
||||
* Convert a maven coordinate into a Path.
|
||||
*
|
||||
* {@code <groupId>:<artifactId>[:<extension>[:<classifier>]]:<version>}, must not be {@code null}.
|
||||
* This is gradle standard not maven standard coordinate formatting
|
||||
* {@code <groupId>:<artifactId>[:<classifier>]:<version>[@extension]}, must not be {@code null}.
|
||||
*/
|
||||
|
||||
public class MavenCoordinateResolver {
|
||||
|
@ -33,9 +34,10 @@ public class MavenCoordinateResolver {
|
|||
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];
|
||||
final String classifier = parts.length > 3 ? parts[2] : "";
|
||||
final String[] versext = parts[parts.length-1].split("@");
|
||||
final String version = versext[0];
|
||||
final String extension = versext.length > 1 ? versext[1] : "";
|
||||
return get(groupId, artifactId, extension, classifier, version);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,29 +25,43 @@ import java.net.URL;
|
|||
import java.net.URLConnection;
|
||||
import java.net.URLStreamHandler;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class ModJarURLHandler extends URLStreamHandler
|
||||
{
|
||||
// modjar:///modid!path/to/file
|
||||
// modjar://modid/path/to/file
|
||||
@Override
|
||||
protected URLConnection openConnection(URL url) throws IOException {
|
||||
protected URLConnection openConnection(URL url) {
|
||||
return new URLConnection(url) {
|
||||
private Path resource;
|
||||
private String modpath;
|
||||
private String modid;
|
||||
|
||||
@Override
|
||||
public void connect() throws IOException
|
||||
public void connect()
|
||||
{
|
||||
final String path = url.getPath();
|
||||
final String[] parts = path.split("!");
|
||||
if (parts.length!=2) throw new IOException("Illegal URL format "+path);
|
||||
modid = parts[0];
|
||||
modpath = parts[1];
|
||||
if (resource == null) {
|
||||
modid = url.getHost();
|
||||
// trim first char
|
||||
modpath = url.getPath().substring(1);
|
||||
resource = FMLLoader.getLoadingModList().getModFileById(modid).getFile().findResource(modpath);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException
|
||||
{
|
||||
return Files.newInputStream(FMLLoader.getLoadingModList().getModFileById(modid).getFile().findResource(modpath));
|
||||
connect();
|
||||
return Files.newInputStream(resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getContentLengthLong() {
|
||||
try {
|
||||
connect();
|
||||
return Files.size(resource);
|
||||
} catch (IOException e) {
|
||||
return -1L;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -36,8 +36,8 @@ 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;
|
||||
private final List<Pair<Path,List<Path>>> rootDirs;
|
||||
private final Map<ModFile, Pair<Path,List<Path>>> mods;
|
||||
|
||||
public ExplodedDirectoryLocator() {
|
||||
this.rootDirs = new ArrayList<>();
|
||||
|
@ -49,7 +49,7 @@ public class ExplodedDirectoryLocator implements IModLocator {
|
|||
final Path modstoml = Paths.get("META-INF", "mods.toml");
|
||||
// Collect all the mods.toml files found
|
||||
rootDirs.forEach(pathPathPair -> {
|
||||
Path resources = pathPathPair.getRight();
|
||||
Path resources = pathPathPair.getLeft();
|
||||
Path modtoml = resources.resolve(modstoml);
|
||||
if (Files.exists(modtoml)) {
|
||||
ModFile mf = new ModFile(pathPathPair.getLeft(), this);
|
||||
|
@ -73,25 +73,29 @@ public class ExplodedDirectoryLocator implements IModLocator {
|
|||
}
|
||||
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);
|
||||
Path found = mods.get(modFile).getLeft().resolve(target);
|
||||
if (Files.exists(found)) return found;
|
||||
// then try left path (classes)
|
||||
return mods.get(modFile).getLeft().resolve(target);
|
||||
return mods.get(modFile).getRight().stream().map(p->p.resolve(target)).findFirst().orElse(found.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());
|
||||
LOGGER.debug(SCAN,"Scanning exploded directory {}", modFile.getFilePath().toString());
|
||||
final Pair<Path, List<Path>> pathPathPair = mods.get(modFile);
|
||||
// classes are in the right branch of the pair
|
||||
pathPathPair.getRight().forEach(path->scanIndividualPath(path, pathConsumer));
|
||||
LOGGER.debug(SCAN,"Exploded directory scan complete {}", pathPathPair.getLeft().toString());
|
||||
}
|
||||
|
||||
private void scanIndividualPath(final Path path, Consumer<Path> pathConsumer) {
|
||||
LOGGER.debug(SCAN, "Scanning exploded target {}", path.toString());
|
||||
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) {
|
||||
LOGGER.info("Exception scanning {}", path, e);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
|
@ -107,7 +111,7 @@ public class ExplodedDirectoryLocator implements IModLocator {
|
|||
@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");
|
||||
final List<Pair<Path, List<Path>>> explodedTargets = ((Map<String, List<Pair<Path, List<Path>>>>) arguments).get("explodedTargets");
|
||||
if (explodedTargets != null && !explodedTargets.isEmpty()) {
|
||||
rootDirs.addAll(explodedTargets);
|
||||
}
|
||||
|
|
|
@ -49,12 +49,18 @@ public class LaunchTesting
|
|||
logcontext.getConfiguration().addFilter(eventbusFilter);
|
||||
logcontext.getConfiguration().addFilter(distxformFilter);
|
||||
logcontext.updateLoggers();
|
||||
File invsorter = new File("/home/cpw/projects/minecraft/inventorysorter/classes");
|
||||
if (invsorter.exists()) {
|
||||
System.setProperty("fml.explodedDir", "/home/cpw/projects/minecraft/inventorysorter/classes"); //TODO: Move this to a example included in our tests, not a random location...
|
||||
}
|
||||
|
||||
String assets = System.getenv().getOrDefault("assetDirectory", "assets");
|
||||
String target = System.getenv().get("target");
|
||||
String[] launchArgs = new String[]{
|
||||
"--gameDir", ".",
|
||||
"--launchTarget", target,
|
||||
"--fml.forgeVersion", System.getProperty("forge.version"),
|
||||
"--fml.mcpVersion", System.getProperty("mcp.version"),
|
||||
"--fml.mcVersion", System.getProperty("mc.version"),
|
||||
"--fml.forgeGroup", System.getProperty("forge.group")
|
||||
};
|
||||
|
||||
|
||||
if (target == null) {
|
||||
throw new IllegalArgumentException("Environment variable target must be set.");
|
||||
|
@ -62,23 +68,15 @@ public class LaunchTesting
|
|||
|
||||
if (Objects.equals(target,"fmldevclient")) {
|
||||
hackNatives();
|
||||
Launcher.main("--launchTarget", target,
|
||||
"--gameDir", ".",
|
||||
"--accessToken", "blah",
|
||||
"--version", "FMLDev",
|
||||
"--assetIndex", "1.13",
|
||||
"--assetsDir", assets,
|
||||
"--userProperties", "{}",
|
||||
"--fml.forgeVersion", "24.0.0",
|
||||
"--fml.mcpVersion", "2018.11.30",
|
||||
"--fml.mcVersion", "1.13");
|
||||
} else if (Objects.equals(target, "fmldevserver")) {
|
||||
String[] launchargs = ObjectArrays.concat(new String[] {"--launchTarget", target,
|
||||
"--gameDir", ".", "--fml.forgeVersion", "24.0.0",
|
||||
"--fml.mcpVersion", "2018.11.30",
|
||||
"--fml.mcVersion", "1.13"}, args, String.class);
|
||||
Launcher.main(launchargs);
|
||||
launchArgs = ObjectArrays.concat(launchArgs, new String[] {
|
||||
"--accessToken", "blah",
|
||||
"--version", "FMLDev",
|
||||
"--assetIndex", "1.13",
|
||||
"--assetsDir", assets,
|
||||
"--userProperties", "{}"
|
||||
}, String.class);
|
||||
}
|
||||
Launcher.main(launchArgs);
|
||||
Thread.sleep(10000);
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
|
||||
package net.minecraftforge.fml;
|
||||
|
||||
import cpw.mods.modlauncher.TransformingClassLoader;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.client.event.ModelRegistryEvent;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
|
@ -40,12 +41,13 @@ import java.util.stream.Collectors;
|
|||
import java.util.stream.Stream;
|
||||
|
||||
import static net.minecraftforge.fml.Logging.CORE;
|
||||
import static net.minecraftforge.fml.Logging.LOADING;
|
||||
|
||||
public class ModLoader
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private static ModLoader INSTANCE;
|
||||
private final ClassLoader launchClassLoader;
|
||||
private final TransformingClassLoader launchClassLoader;
|
||||
private final LoadingModList loadingModList;
|
||||
private final ModLoadingClassLoader modClassLoader;
|
||||
|
||||
|
@ -112,6 +114,7 @@ public class ModLoader
|
|||
{
|
||||
final Map<String, IModInfo> modInfoMap = modFile.getModFileInfo().getMods().stream().collect(Collectors.toMap(IModInfo::getModId, Function.identity()));
|
||||
|
||||
LOGGER.debug(LOADING, "ModContainer is {}", ModContainer.class.getClassLoader());
|
||||
return modFile.getScanResult().getTargets().entrySet().stream().
|
||||
map(e-> {
|
||||
try {
|
||||
|
|
|
@ -19,20 +19,18 @@
|
|||
|
||||
package net.minecraftforge.fml;
|
||||
|
||||
import cpw.mods.modlauncher.TransformingClassLoader;
|
||||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
import net.minecraftforge.fml.loading.ModJarURLHandler;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
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;
|
||||
|
||||
public class ModLoadingClassLoader extends SecureClassLoader
|
||||
public class ModLoadingClassLoader extends TransformingClassLoader
|
||||
{
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
|
@ -41,62 +39,44 @@ public class ModLoadingClassLoader extends SecureClassLoader
|
|||
}
|
||||
|
||||
private final Predicate<String> classLoadingPredicate;
|
||||
private final TransformingClassLoader tcl;
|
||||
|
||||
protected ModLoadingClassLoader(final ClassLoader parent) {
|
||||
super(parent);
|
||||
protected ModLoadingClassLoader(final TransformingClassLoader parent) {
|
||||
super(parent, path->FMLLoader.getLoadingModList().findURLForResource(path));
|
||||
this.tcl = parent;
|
||||
this.classLoadingPredicate = FMLLoader.getClassLoaderExclusions();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected URL locateResource(final String path) {
|
||||
return FMLLoader.getLoadingModList().findURLForResource(path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getResource(String name)
|
||||
{
|
||||
return super.getResource(name);
|
||||
return locateResource(name);
|
||||
}
|
||||
|
||||
/*
|
||||
@Override
|
||||
protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException
|
||||
{
|
||||
synchronized (getClassLoadingLock(name)) {
|
||||
Class<?> existingClass = tcl.getLoadedClass(name);
|
||||
if (existingClass != null) return existingClass;
|
||||
|
||||
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);
|
||||
if (classResource != null) {
|
||||
try {
|
||||
final byte[] bytes = Files.readAllBytes(classResource);
|
||||
return defineClass(name, bytes, 0, bytes.length);
|
||||
LOGGER.debug(LOADING, "Loading class {}", name);
|
||||
if (!classLoadingPredicate.test(name)) {
|
||||
LOGGER.debug(LOADING, "Delegating to parent {}", name);
|
||||
return tcl.loadClass(name);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new ClassNotFoundException("Failed to load class file " + classResource + " for "+ className, e);
|
||||
}
|
||||
} else if(getParent() != null) {
|
||||
getParent().loadClass(name);
|
||||
return tcl.loadClass(name, this::locateResource);
|
||||
}
|
||||
throw new ClassNotFoundException("Failed to find class file "+ className);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected URL findResource(String name)
|
||||
{
|
||||
return super.findResource(name);
|
||||
return locateResource(name);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,8 +35,7 @@ import java.util.function.Function;
|
|||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static net.minecraftforge.fml.Logging.LOADING;
|
||||
import static net.minecraftforge.fml.Logging.SCAN;
|
||||
import static net.minecraftforge.fml.Logging.*;
|
||||
|
||||
public class FMLJavaModLanguageProvider implements IModLanguageProvider
|
||||
{
|
||||
|
@ -68,13 +67,14 @@ public class FMLJavaModLanguageProvider implements IModLanguageProvider
|
|||
// in the classloader of the game - the context classloader is appropriate here.
|
||||
try
|
||||
{
|
||||
final Constructor<?> constructor = Class.forName("net.minecraftforge.fml.javafmlmod.FMLModContainer", true, Thread.currentThread().getContextClassLoader()).
|
||||
getConstructor(IModInfo.class, String.class, ClassLoader.class, ModFileScanData.class);
|
||||
final Class<?> fmlContainer = Class.forName("net.minecraftforge.fml.javafmlmod.FMLModContainer", true, Thread.currentThread().getContextClassLoader());
|
||||
LOGGER.debug(LOADING, "Loading FMLModContainer from classloader {} - got {}", Thread.currentThread().getContextClassLoader(), fmlContainer.getClassLoader());
|
||||
final Constructor<?> constructor = fmlContainer.getConstructor(IModInfo.class, String.class, ClassLoader.class, ModFileScanData.class);
|
||||
return (T)constructor.newInstance(info, className, modClassLoader, modFileScanResults);
|
||||
}
|
||||
catch (NoSuchMethodException | ClassNotFoundException | InstantiationException | IllegalAccessException | InvocationTargetException e)
|
||||
{
|
||||
LOGGER.error(LOADING,"Unable to load FMLModContainer, wut?", e);
|
||||
LOGGER.fatal(LOADING,"Unable to load FMLModContainer, wut?", e);
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package net.minecraftforge.userdev;
|
||||
|
||||
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.util.concurrent.Callable;
|
||||
|
||||
import static net.minecraftforge.fml.loading.LogMarkers.CORE;
|
||||
|
||||
public class FMLUserdevClientLaunchProvider extends FMLUserdevLaunchProvider implements ILaunchHandlerService {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
@Override
|
||||
public Dist getDist() {
|
||||
return Dist.CLIENT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "fmluserdevclient";
|
||||
}
|
||||
|
||||
@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;
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
package net.minecraftforge.userdev;
|
||||
|
||||
import com.google.common.collect.ObjectArrays;
|
||||
import com.google.common.collect.Streams;
|
||||
import cpw.mods.modlauncher.api.IEnvironment;
|
||||
import net.minecraftforge.fml.loading.FMLCommonLaunchHandler;
|
||||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
import net.minecraftforge.fml.loading.LibraryFinder;
|
||||
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.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static net.minecraftforge.fml.loading.LogMarkers.CORE;
|
||||
|
||||
public abstract class FMLUserdevLaunchProvider extends FMLCommonLaunchHandler {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
private Path forgeJar;
|
||||
private Path mcJars;
|
||||
|
||||
@Override
|
||||
public Path getForgePath(final String mcVersion, final String forgeVersion, final String forgeGroup) {
|
||||
final URL forgePath = getClass().getClassLoader().getResource("net/minecraftforge/versions/forge/ForgeVersion.class");
|
||||
if (forgePath == null) {
|
||||
LOGGER.fatal(CORE, "Unable to locate forge on the classpath");
|
||||
throw new RuntimeException("Unable to locate forge on the classpath");
|
||||
}
|
||||
forgeJar = LibraryFinder.findJarPathFor("ForgeVersion.class","forge", forgePath);
|
||||
return forgeJar;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void setup(final IEnvironment environment, final Map<String, ?> arguments) {
|
||||
final List<String> mavenRoots = new ArrayList<>((List<String>) arguments.get("mavenRoots"));
|
||||
final List<String> mods = new ArrayList<>((List<String>) arguments.get("mods"));
|
||||
final String forgeVersion = (String) arguments.get("forgeVersion");
|
||||
final String mcVersion = (String) arguments.get("mcVersion");
|
||||
final String mcpVersion = (String) arguments.get("mcpVersion");
|
||||
final String forgeGroup = (String) arguments.get("forgeGroup");
|
||||
final String userdevVersion = mcVersion+"-"+forgeVersion+"_mapped_snapshot_"+mcpVersion;
|
||||
int dirs = forgeGroup.split("\\.").length + 2;
|
||||
Path fjroot = forgeJar;
|
||||
do {
|
||||
fjroot = fjroot.getParent();
|
||||
} while (dirs-- > 0);
|
||||
final String fjpath = fjroot.toString();
|
||||
LOGGER.info(CORE, "Injecting forge as mod {} from maven path {}", userdevVersion, fjpath);
|
||||
mavenRoots.add(fjpath);
|
||||
mods.add(forgeGroup+":userdev:"+userdevVersion);
|
||||
|
||||
try {
|
||||
final Enumeration<URL> resources = ClassLoader.getSystemClassLoader().getResources("META-INF/mods.toml");
|
||||
final ArrayList<URL> modstoml = Collections.list(resources);
|
||||
modstoml.stream().filter(u-> !u.getPath().contains("!"));
|
||||
|
||||
} catch (IOException e) {
|
||||
LOGGER.fatal("Error trying to find resources", e);
|
||||
throw new RuntimeException("wha?", e);
|
||||
}
|
||||
|
||||
LOGGER.fatal(CORE, "Got mod coordinates {} from env", System.getenv("MOD_CLASSES"));
|
||||
|
||||
final List<Path> modClasses = Arrays.stream(System.getenv("MOD_CLASSES").split(File.pathSeparator)).
|
||||
map(Paths::get).collect(Collectors.toList());
|
||||
|
||||
LOGGER.fatal(CORE, "Processed mod coordinates [{}]", modClasses.stream().map(Object::toString).collect(Collectors.joining(",")));
|
||||
|
||||
((Map<String, List<Pair<Path,List<Path>>>>) arguments).computeIfAbsent("explodedTargets", a->new ArrayList<>()).
|
||||
add(Pair.of(modClasses.get(0), modClasses.subList(1, modClasses.size())));
|
||||
|
||||
|
||||
// generics are gross yea?
|
||||
((Map)arguments).put("mavenRoots", mavenRoots);
|
||||
((Map)arguments).put("mods", mods);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void validatePaths(final Path forgePath, final Path[] mcPaths, final String forgeVersion, final String mcVersion, final String mcpVersion) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public Path[] getMCPaths(final String mcVersion, final String mcpVersion, final String forgeVersion, final String forgeGroup) {
|
||||
final URL mcDataPath = getClass().getClassLoader().getResource("assets/minecraft/lang/en_us.json");
|
||||
if (mcDataPath == null) {
|
||||
LOGGER.fatal(CORE, "Unable to locate minecraft data on the classpath");
|
||||
throw new RuntimeException("Unable to locate minecraft data on the classpath");
|
||||
}
|
||||
mcJars = LibraryFinder.findJarPathFor("en_us.json","mcdata", mcDataPath);
|
||||
return new Path[] {mcJars};
|
||||
}
|
||||
|
||||
public Path[] identifyTransformationTargets()
|
||||
{
|
||||
return LibraryFinder.commonLibPaths(ObjectArrays.concat(FMLLoader.getForgePath(), FMLLoader.getMCPaths()));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package net.minecraftforge.userdev;
|
||||
|
||||
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.util.concurrent.Callable;
|
||||
|
||||
import static net.minecraftforge.fml.loading.LogMarkers.CORE;
|
||||
|
||||
public class FMLUserdevServerLaunchProvider extends FMLUserdevLaunchProvider implements ILaunchHandlerService {
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
@Override
|
||||
public Dist getDist() {
|
||||
return Dist.DEDICATED_SERVER;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String name() {
|
||||
return "fmluserdevserver";
|
||||
}
|
||||
|
||||
@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());
|
||||
Thread.currentThread().setContextClassLoader(launchClassLoader.getInstance());
|
||||
Class.forName("net.minecraft.server.MinecraftServer", true, launchClassLoader.getInstance()).getMethod("main", String[].class).invoke(null, (Object)arguments);
|
||||
return null;
|
||||
};
|
||||
}
|
||||
}
|
|
@ -57,25 +57,33 @@ public class UserdevLauncher
|
|||
throw new IllegalArgumentException("Environment variable 'assets' must be set to a valid path.");
|
||||
}
|
||||
if (target == null) {
|
||||
throw new IllegalArgumentException("Environment variable 'target' must be set to 'fmldevclient' or 'fmldevserver'.");
|
||||
throw new IllegalArgumentException("Environment variable 'target' must be set to 'fmluserdevclient' or 'fmluserdevserver'.");
|
||||
}
|
||||
|
||||
if (Objects.equals(target,"fmldevclient")) {
|
||||
String[] launchArgs = new String[]{
|
||||
"--gameDir", ".",
|
||||
"--launchTarget", target,
|
||||
"--fml.forgeVersion", System.getenv("FORGE_VERSION"),
|
||||
"--fml.mcpVersion", System.getenv("MCP_VERSION"),
|
||||
"--fml.mcVersion", System.getenv("MC_VERSION"),
|
||||
"--fml.forgeGroup", System.getenv("FORGE_GROUP")
|
||||
};
|
||||
|
||||
if (Objects.equals(target,"fmluserdevclient")) {
|
||||
hackNatives();
|
||||
Launcher.main("--launchTarget", target,
|
||||
"--gameDir", ".",
|
||||
launchArgs = ObjectArrays.concat(launchArgs, new String[] {
|
||||
"--accessToken", "blah",
|
||||
"--version", "FMLDev",
|
||||
"--assetIndex", "1.13",
|
||||
"--assetsDir", assets,
|
||||
"--userProperties", "{}");
|
||||
} else if (Objects.equals(target, "fmldevserver")) {
|
||||
String[] launchargs = ObjectArrays.concat(new String[] {"--launchTarget", target,
|
||||
"--gameDir", "."}, args, String.class);
|
||||
Launcher.main(launchargs);
|
||||
"--userProperties", "{}"
|
||||
}, String.class);
|
||||
} else if (Objects.equals(target, "fmluserdevserver")) {
|
||||
// we're good
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown value for 'target' property: " + target);
|
||||
}
|
||||
Launcher.main(launchArgs);
|
||||
Thread.sleep(10000);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
net.minecraftforge.userdev.FMLUserdevClientLaunchProvider
|
||||
net.minecraftforge.userdev.FMLUserdevServerLaunchProvider
|
|
@ -1 +0,0 @@
|
|||
net.minecraftforge.userdev.ClasspathLocator
|
Loading…
Reference in New Issue