diff --git a/src/main/java/net/fabricmc/loom/configuration/ide/RunConfigSettings.java b/src/main/java/net/fabricmc/loom/configuration/ide/RunConfigSettings.java index 64498e730..60b7e2c74 100644 --- a/src/main/java/net/fabricmc/loom/configuration/ide/RunConfigSettings.java +++ b/src/main/java/net/fabricmc/loom/configuration/ide/RunConfigSettings.java @@ -403,6 +403,30 @@ public void data() { forgeTemplate("data"); } + /** + * Configure run config with the default data options. + * + *

This method can only be used on NeoForge. + */ + @ApiStatus.Experimental + public void clientData() { + ModPlatform.assertForgeLike(getExtension(), () -> "RunConfigSettings.clientData() is only usable on NeoForge."); + environment("dataClient"); + forgeTemplate("dataClient"); + } + + /** + * Configure run config with the default data options. + * + *

This method can only be used on NeoForge. + */ + @ApiStatus.Experimental + public void serverData() { + ModPlatform.assertForgeLike(getExtension(), () -> "RunConfigSettings.serverData() is only usable on NeoForge."); + environment("dataServer"); + forgeTemplate("dataServer"); + } + /** * Applies a Forge run config template to these settings. * diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeRunTemplate.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeRunTemplate.java index 624a2630a..ce01ce1c5 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeRunTemplate.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/ForgeRunTemplate.java @@ -66,7 +66,13 @@ public record ForgeRunTemplate( public static final Codec> MAP_CODEC = Codec.unboundedMap(Codec.STRING, CODEC) .xmap( map -> { - final Map newMap = new HashMap<>(map); + final Map newMap = new HashMap<>(); + + // TODO: Remove this hack once we patch DLI to support clientData as env + for (Map.Entry entry : map.entrySet()) { + String name = entry.getKey().replaceAll("clientData", "dataClient").replaceAll("serverData", "dataServer"); + newMap.put(name, entry.getValue()); + } // Iterate through all templates and fill in empty names. // The NeoForge format doesn't include the name property, so we'll use the map keys diff --git a/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java b/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java index f69f7f0bd..b4c347683 100644 --- a/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java +++ b/src/main/java/net/fabricmc/loom/configuration/providers/forge/MinecraftPatchedProvider.java @@ -56,6 +56,7 @@ import dev.architectury.loom.util.MappingOption; import dev.architectury.loom.util.TempFiles; import org.gradle.api.Project; +import org.gradle.api.file.FileCollection; import org.gradle.api.logging.LogLevel; import org.gradle.api.logging.Logger; import org.objectweb.asm.ClassReader; @@ -460,8 +461,9 @@ private void patchJars() throws Exception { private void patchJars(Path clean, Path output, Path patches) { ForgeToolExecutor.exec(project, spec -> { UserdevConfig.BinaryPatcherConfig config = getExtension().getForgeUserdevProvider().getConfig().binpatcher(); - spec.classpath(DependencyDownloader.download(project, config.dependency())); - spec.getMainClass().set("net.minecraftforge.binarypatcher.ConsoleTool"); + final FileCollection download = DependencyDownloader.download(project, config.dependency()); + spec.classpath(download); + spec.getMainClass().set(getMainClass(download)); for (String arg : config.args()) { String actual = switch (arg) { @@ -475,6 +477,45 @@ private void patchJars(Path clean, Path output, Path patches) { }); } + private static String getMainClass(final Iterable files) { + String mainClass = null; + IOException ex = null; + + for (File file : files) { + if (file.getName().endsWith(".jar")) { + try (FileSystemUtil.Delegate fs = FileSystemUtil.getJarFileSystem(file.toPath())) { + final Path mfPath = fs.getPath("META-INF/MANIFEST.MF"); + + if (Files.exists(mfPath)) { + try (InputStream in = Files.newInputStream(mfPath)) { + mainClass = new Manifest(in).getMainAttributes().getValue("Main-Class"); + } + } + } catch (final IOException e) { + if (ex == null) { + ex = e; + } else { + ex.addSuppressed(e); + } + } + + if (mainClass != null) { + break; + } + } + } + + if (mainClass == null) { + if (ex != null) { + throw new UncheckedIOException(ex); + } else { + throw new RuntimeException("Failed to find main class"); + } + } + + return mainClass; + } + private void walkFileSystems(Path source, Path target, Predicate filter, Function> toWalk, FsPathConsumer action) throws IOException { try (FileSystemUtil.Delegate sourceFs = FileSystemUtil.getJarFileSystem(source, false);