From fd9c83b65fa1fa2a17fc23bd06149fd79909eb9d Mon Sep 17 00:00:00 2001 From: cpw Date: Fri, 4 Jan 2019 11:44:57 -0500 Subject: [PATCH] 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. --- build.gradle | 47 ++++---- .../fml/loading/FMLClientLaunchProvider.java | 2 +- .../loading/FMLDevClientLaunchProvider.java | 5 +- .../loading/FMLDevServerLaunchProvider.java | 8 +- .../minecraftforge/fml/loading/FMLLoader.java | 7 +- .../fml/loading/FMLServerLaunchProvider.java | 2 +- .../fml/loading/LibraryFinder.java | 13 ++- .../fml/loading/LoadingModList.java | 16 +++ .../fml/loading/MavenCoordinateResolver.java | 10 +- .../fml/loading/ModJarURLHandler.java | 32 ++++-- .../ExplodedDirectoryLocator.java | 34 +++--- .../net/minecraftforge/fml/LaunchTesting.java | 38 +++---- .../net/minecraftforge/fml/ModLoader.java | 5 +- .../fml/ModLoadingClassLoader.java | 64 ++++------- .../FMLJavaModLanguageProvider.java | 10 +- .../FMLUserdevClientLaunchProvider.java | 37 ++++++ .../userdev/FMLUserdevLaunchProvider.java | 106 ++++++++++++++++++ .../FMLUserdevServerLaunchProvider.java | 39 +++++++ .../userdev/UserdevLauncher.java | 26 +++-- ...mods.modlauncher.api.ILaunchHandlerService | 2 + ...forge.fml.loading.moddiscovery.IModLocator | 1 - 21 files changed, 360 insertions(+), 144 deletions(-) create mode 100644 src/userdev/java/net/minecraftforge/userdev/FMLUserdevClientLaunchProvider.java create mode 100644 src/userdev/java/net/minecraftforge/userdev/FMLUserdevLaunchProvider.java create mode 100644 src/userdev/java/net/minecraftforge/userdev/FMLUserdevServerLaunchProvider.java create mode 100644 src/userdev/resources/META-INF/services/cpw.mods.modlauncher.api.ILaunchHandlerService delete mode 100644 src/userdev/resources/META-INF/services/net.minecraftforge.fml.loading.moddiscovery.IModLocator diff --git a/build.gradle b/build.gradle index 12ed83a0f..7a07eec2a 100644 --- a/build.gradle +++ b/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 -.. @@ -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+' diff --git a/src/fmllauncher/java/net/minecraftforge/fml/loading/FMLClientLaunchProvider.java b/src/fmllauncher/java/net/minecraftforge/fml/loading/FMLClientLaunchProvider.java index 4bc3d0444..5b8355fe7 100644 --- a/src/fmllauncher/java/net/minecraftforge/fml/loading/FMLClientLaunchProvider.java +++ b/src/fmllauncher/java/net/minecraftforge/fml/loading/FMLClientLaunchProvider.java @@ -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); diff --git a/src/fmllauncher/java/net/minecraftforge/fml/loading/FMLDevClientLaunchProvider.java b/src/fmllauncher/java/net/minecraftforge/fml/loading/FMLDevClientLaunchProvider.java index c227d8d95..350bd12b7 100644 --- a/src/fmllauncher/java/net/minecraftforge/fml/loading/FMLDevClientLaunchProvider.java +++ b/src/fmllauncher/java/net/minecraftforge/fml/loading/FMLDevClientLaunchProvider.java @@ -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>>) arguments).computeIfAbsent("explodedTargets", a->new ArrayList<>()). - add(Pair.of(compiledClasses, forgemodstoml)); + ((Map>>>) arguments).computeIfAbsent("explodedTargets", a->new ArrayList<>()). + add(Pair.of(forgemodstoml, Collections.singletonList(compiledClasses))); } @Override diff --git a/src/fmllauncher/java/net/minecraftforge/fml/loading/FMLDevServerLaunchProvider.java b/src/fmllauncher/java/net/minecraftforge/fml/loading/FMLDevServerLaunchProvider.java index d93ab0d68..c8080780a 100644 --- a/src/fmllauncher/java/net/minecraftforge/fml/loading/FMLDevServerLaunchProvider.java +++ b/src/fmllauncher/java/net/minecraftforge/fml/loading/FMLDevServerLaunchProvider.java @@ -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>>) arguments).computeIfAbsent("explodedTargets", a->new ArrayList<>()). - add(Pair.of(compiledClasses, forgemodstoml)); + ((Map>>>) arguments).computeIfAbsent("explodedTargets", a->new ArrayList<>()). + add(Pair.of(forgemodstoml, Collections.singletonList(compiledClasses))); } @Override diff --git a/src/fmllauncher/java/net/minecraftforge/fml/loading/FMLLoader.java b/src/fmllauncher/java/net/minecraftforge/fml/loading/FMLLoader.java index 4ee097cb5..3b9afa5ec 100644 --- a/src/fmllauncher/java/net/minecraftforge/fml/loading/FMLLoader.java +++ b/src/fmllauncher/java/net/minecraftforge/fml/loading/FMLLoader.java @@ -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; } diff --git a/src/fmllauncher/java/net/minecraftforge/fml/loading/FMLServerLaunchProvider.java b/src/fmllauncher/java/net/minecraftforge/fml/loading/FMLServerLaunchProvider.java index ca5e9800e..329187b50 100644 --- a/src/fmllauncher/java/net/minecraftforge/fml/loading/FMLServerLaunchProvider.java +++ b/src/fmllauncher/java/net/minecraftforge/fml/loading/FMLServerLaunchProvider.java @@ -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); diff --git a/src/fmllauncher/java/net/minecraftforge/fml/loading/LibraryFinder.java b/src/fmllauncher/java/net/minecraftforge/fml/loading/LibraryFinder.java index 635a72cc6..083b7515d 100644 --- a/src/fmllauncher/java/net/minecraftforge/fml/loading/LibraryFinder.java +++ b/src/fmllauncher/java/net/minecraftforge/fml/loading/LibraryFinder.java @@ -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); } diff --git a/src/fmllauncher/java/net/minecraftforge/fml/loading/LoadingModList.java b/src/fmllauncher/java/net/minecraftforge/fml/loading/LoadingModList.java index 80be27a93..7e65c7d9b 100644 --- a/src/fmllauncher/java/net/minecraftforge/fml/loading/LoadingModList.java +++ b/src/fmllauncher/java/net/minecraftforge/fml/loading/LoadingModList.java @@ -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); diff --git a/src/fmllauncher/java/net/minecraftforge/fml/loading/MavenCoordinateResolver.java b/src/fmllauncher/java/net/minecraftforge/fml/loading/MavenCoordinateResolver.java index 3db254545..c76d2d743 100644 --- a/src/fmllauncher/java/net/minecraftforge/fml/loading/MavenCoordinateResolver.java +++ b/src/fmllauncher/java/net/minecraftforge/fml/loading/MavenCoordinateResolver.java @@ -25,7 +25,8 @@ import java.nio.file.Paths; /** * Convert a maven coordinate into a Path. * - * {@code :[:[:]]:}, must not be {@code null}. + * This is gradle standard not maven standard coordinate formatting + * {@code :[:]:[@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); } diff --git a/src/fmllauncher/java/net/minecraftforge/fml/loading/ModJarURLHandler.java b/src/fmllauncher/java/net/minecraftforge/fml/loading/ModJarURLHandler.java index f77a322a6..528874705 100644 --- a/src/fmllauncher/java/net/minecraftforge/fml/loading/ModJarURLHandler.java +++ b/src/fmllauncher/java/net/minecraftforge/fml/loading/ModJarURLHandler.java @@ -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; + } } }; } diff --git a/src/fmllauncher/java/net/minecraftforge/fml/loading/moddiscovery/ExplodedDirectoryLocator.java b/src/fmllauncher/java/net/minecraftforge/fml/loading/moddiscovery/ExplodedDirectoryLocator.java index c7d81c4b9..075ead4b1 100644 --- a/src/fmllauncher/java/net/minecraftforge/fml/loading/moddiscovery/ExplodedDirectoryLocator.java +++ b/src/fmllauncher/java/net/minecraftforge/fml/loading/moddiscovery/ExplodedDirectoryLocator.java @@ -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> rootDirs; - private final Map> mods; + private final List>> rootDirs; + private final Map>> 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 pathConsumer) { - LOGGER.debug(SCAN,"Scanning directory {}", modFile.getFilePath().toString()); - final Pair pathPathPair = mods.get(modFile); - // classes are in the left branch of the pair - try (Stream 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> 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 pathConsumer) { + LOGGER.debug(SCAN, "Scanning exploded target {}", path.toString()); + try (Stream 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 arguments) { - final List> explodedTargets = ((Map>>) arguments).get("explodedTargets"); + final List>> explodedTargets = ((Map>>>) arguments).get("explodedTargets"); if (explodedTargets != null && !explodedTargets.isEmpty()) { rootDirs.addAll(explodedTargets); } diff --git a/src/main/java/net/minecraftforge/fml/LaunchTesting.java b/src/main/java/net/minecraftforge/fml/LaunchTesting.java index 58f0ea578..c2ce7d210 100644 --- a/src/main/java/net/minecraftforge/fml/LaunchTesting.java +++ b/src/main/java/net/minecraftforge/fml/LaunchTesting.java @@ -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); } diff --git a/src/main/java/net/minecraftforge/fml/ModLoader.java b/src/main/java/net/minecraftforge/fml/ModLoader.java index be8acc853..559f09028 100644 --- a/src/main/java/net/minecraftforge/fml/ModLoader.java +++ b/src/main/java/net/minecraftforge/fml/ModLoader.java @@ -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 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 { diff --git a/src/main/java/net/minecraftforge/fml/ModLoadingClassLoader.java b/src/main/java/net/minecraftforge/fml/ModLoadingClassLoader.java index 6215dfa8c..81150f9e7 100644 --- a/src/main/java/net/minecraftforge/fml/ModLoadingClassLoader.java +++ b/src/main/java/net/minecraftforge/fml/ModLoadingClassLoader.java @@ -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 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); } } diff --git a/src/main/java/net/minecraftforge/fml/javafmlmod/FMLJavaModLanguageProvider.java b/src/main/java/net/minecraftforge/fml/javafmlmod/FMLJavaModLanguageProvider.java index 91df8665f..151b40e2b 100644 --- a/src/main/java/net/minecraftforge/fml/javafmlmod/FMLJavaModLanguageProvider.java +++ b/src/main/java/net/minecraftforge/fml/javafmlmod/FMLJavaModLanguageProvider.java @@ -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); } } diff --git a/src/userdev/java/net/minecraftforge/userdev/FMLUserdevClientLaunchProvider.java b/src/userdev/java/net/minecraftforge/userdev/FMLUserdevClientLaunchProvider.java new file mode 100644 index 000000000..a61e4fef0 --- /dev/null +++ b/src/userdev/java/net/minecraftforge/userdev/FMLUserdevClientLaunchProvider.java @@ -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 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; + }; + } +} diff --git a/src/userdev/java/net/minecraftforge/userdev/FMLUserdevLaunchProvider.java b/src/userdev/java/net/minecraftforge/userdev/FMLUserdevLaunchProvider.java new file mode 100644 index 000000000..5bd3fa1b6 --- /dev/null +++ b/src/userdev/java/net/minecraftforge/userdev/FMLUserdevLaunchProvider.java @@ -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 arguments) { + final List mavenRoots = new ArrayList<>((List) arguments.get("mavenRoots")); + final List mods = new ArrayList<>((List) 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 resources = ClassLoader.getSystemClassLoader().getResources("META-INF/mods.toml"); + final ArrayList 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 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>>>) 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())); + } + +} diff --git a/src/userdev/java/net/minecraftforge/userdev/FMLUserdevServerLaunchProvider.java b/src/userdev/java/net/minecraftforge/userdev/FMLUserdevServerLaunchProvider.java new file mode 100644 index 000000000..1e48f37c0 --- /dev/null +++ b/src/userdev/java/net/minecraftforge/userdev/FMLUserdevServerLaunchProvider.java @@ -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 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; + }; + } +} diff --git a/src/userdev/java/net/minecraftforge/userdev/UserdevLauncher.java b/src/userdev/java/net/minecraftforge/userdev/UserdevLauncher.java index 53061facf..fe4b3578b 100644 --- a/src/userdev/java/net/minecraftforge/userdev/UserdevLauncher.java +++ b/src/userdev/java/net/minecraftforge/userdev/UserdevLauncher.java @@ -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); } diff --git a/src/userdev/resources/META-INF/services/cpw.mods.modlauncher.api.ILaunchHandlerService b/src/userdev/resources/META-INF/services/cpw.mods.modlauncher.api.ILaunchHandlerService new file mode 100644 index 000000000..3c70f3f7e --- /dev/null +++ b/src/userdev/resources/META-INF/services/cpw.mods.modlauncher.api.ILaunchHandlerService @@ -0,0 +1,2 @@ +net.minecraftforge.userdev.FMLUserdevClientLaunchProvider +net.minecraftforge.userdev.FMLUserdevServerLaunchProvider \ No newline at end of file diff --git a/src/userdev/resources/META-INF/services/net.minecraftforge.fml.loading.moddiscovery.IModLocator b/src/userdev/resources/META-INF/services/net.minecraftforge.fml.loading.moddiscovery.IModLocator deleted file mode 100644 index fc6e5d3cc..000000000 --- a/src/userdev/resources/META-INF/services/net.minecraftforge.fml.loading.moddiscovery.IModLocator +++ /dev/null @@ -1 +0,0 @@ -net.minecraftforge.userdev.ClasspathLocator \ No newline at end of file