From 8085b50b3dde94d6963ea7afe0052c3c25df4058 Mon Sep 17 00:00:00 2001 From: Gravita Date: Sun, 11 Jul 2021 15:35:51 +0700 Subject: [PATCH] [FEATURE] MakeProfileHelper --- .../command/hash/DownloadClientCommand.java | 9 +- .../command/hash/MakeProfileCommand.java | 11 +- .../command/hash/SaveProfilesCommand.java | 142 +--------- .../helper/MakeProfileHelper.java | 250 ++++++++++++++++++ 4 files changed, 265 insertions(+), 147 deletions(-) create mode 100644 LaunchServer/src/main/java/pro/gravit/launchserver/helper/MakeProfileHelper.java diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/DownloadClientCommand.java b/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/DownloadClientCommand.java index e7d0b444..f5307730 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/DownloadClientCommand.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/DownloadClientCommand.java @@ -7,6 +7,7 @@ import pro.gravit.launcher.profiles.ClientProfile; import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.command.Command; +import pro.gravit.launchserver.helper.MakeProfileHelper; import pro.gravit.utils.command.CommandException; import pro.gravit.utils.helper.IOHelper; @@ -70,11 +71,11 @@ public void invoke(String... args) throws IOException, CommandException { if (version.compareTo(ClientProfile.Version.MC164) <= 0) { logger.warn("Minecraft 1.6.4 and below not supported. Use at your own risk"); } - SaveProfilesCommand.MakeProfileOption[] options = SaveProfilesCommand.getMakeProfileOptionsFromDir(clientDir, version); - for (SaveProfilesCommand.MakeProfileOption option : options) { - logger.debug("Detected option {}", option); + MakeProfileHelper.MakeProfileOption[] options = MakeProfileHelper.getMakeProfileOptionsFromDir(clientDir, version); + for (MakeProfileHelper.MakeProfileOption option : options) { + logger.debug("Detected option {}", option.getClass().getSimpleName()); } - client = SaveProfilesCommand.makeProfile(version, dirName, options); + client = MakeProfileHelper.makeProfile(version, dirName, options); } catch (Throwable e) { isMirrorClientDownload = true; } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/MakeProfileCommand.java b/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/MakeProfileCommand.java index c96b3e9a..f97b8967 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/MakeProfileCommand.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/MakeProfileCommand.java @@ -6,6 +6,7 @@ import pro.gravit.launcher.profiles.ClientProfile; import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.command.Command; +import pro.gravit.launchserver.helper.MakeProfileHelper; import pro.gravit.utils.helper.IOHelper; import java.io.Writer; @@ -30,14 +31,14 @@ public String getUsageDescription() { @Override public void invoke(String... args) throws Exception { verifyArgs(args, 3); - ClientProfile.Version version = ClientProfile.Version.byName(args[2]); - SaveProfilesCommand.MakeProfileOption[] options = SaveProfilesCommand.getMakeProfileOptionsFromDir(server.updatesDir.resolve(args[2]), version); - for (SaveProfilesCommand.MakeProfileOption option : options) { + ClientProfile.Version version = ClientProfile.Version.byName(args[1]); + MakeProfileHelper.MakeProfileOption[] options = MakeProfileHelper.getMakeProfileOptionsFromDir(server.updatesDir.resolve(args[2]), version); + for (MakeProfileHelper.MakeProfileOption option : options) { logger.info("Detected option {}", option); } - ClientProfile profile = SaveProfilesCommand.makeProfile(ClientProfile.Version.byName(args[1]), args[0], options); + ClientProfile profile = MakeProfileHelper.makeProfile(ClientProfile.Version.byName(args[1]), args[0], options); try (Writer writer = IOHelper.newWriter(server.profilesDir.resolve(args[0].concat(".json")))) { - Launcher.gsonManager.gson.toJson(profile, writer); + Launcher.gsonManager.configGson.toJson(profile, writer); } logger.info("Profile {} created", args[0]); server.syncProfilesDir(); diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/SaveProfilesCommand.java b/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/SaveProfilesCommand.java index cc907467..b8ba4656 100644 --- a/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/SaveProfilesCommand.java +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/command/hash/SaveProfilesCommand.java @@ -4,22 +4,22 @@ import org.apache.logging.log4j.Logger; import pro.gravit.launcher.Launcher; import pro.gravit.launcher.profiles.ClientProfile; -import pro.gravit.launcher.profiles.ClientProfileBuilder; import pro.gravit.launcher.profiles.optional.OptionalFile; import pro.gravit.launcher.profiles.optional.OptionalTrigger; import pro.gravit.launcher.profiles.optional.actions.*; -import pro.gravit.launcher.profiles.optional.triggers.OSTrigger; import pro.gravit.launchserver.LaunchServer; import pro.gravit.launchserver.command.Command; import pro.gravit.utils.helper.IOHelper; -import pro.gravit.utils.helper.JVMHelper; import java.io.IOException; import java.io.Reader; import java.io.Writer; import java.nio.file.Files; import java.nio.file.Path; -import java.util.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.UUID; public class SaveProfilesCommand extends Command { private transient final Logger logger = LogManager.getLogger(); @@ -28,137 +28,6 @@ public SaveProfilesCommand(LaunchServer server) { super(server); } - public static ClientProfile makeProfile(ClientProfile.Version version, String title, MakeProfileOption... options) { - ClientProfileBuilder builder = new ClientProfileBuilder(); - builder.setVersion(version.name); - builder.setDir(title); - builder.setAssetDir("asset" + version.name); - builder.setAssetIndex(version.name); - builder.setInfo("Информация о сервере"); - builder.setTitle(title); - builder.setUuid(UUID.randomUUID()); - builder.setMainClass(getMainClassByVersion(version, options)); - builder.setServers(List.of(new ClientProfile.ServerProfile(title, "localhost", 25535))); - // ------------ - builder.setUpdateVerify(List.of("libraries", "natives", "minecraft.jar", "forge.jar", "liteloader.jar", "mods")); - builder.setClassPath(List.of("libraries", "minecraft.jar", "forge.jar", "liteloader.jar")); - builder.setUpdate(List.of("servers.dat")); - List jvmArgs = new ArrayList<>(4); - Set optionals = new HashSet<>(); - jvmArgs.add("-XX:+DisableAttachMechanism"); - // Official Mojang launcher java arguments - jvmArgs.add("-XX:+UseG1GC"); - jvmArgs.add("-XX:+UnlockExperimentalVMOptions"); - jvmArgs.add("-XX:G1NewSizePercent=20"); - jvmArgs.add("-XX:MaxGCPauseMillis=50"); - jvmArgs.add("-XX:G1HeapRegionSize=32M"); - // ----------- - if (version.compareTo(ClientProfile.Version.MC1122) > 0) { - jvmArgs.add("-Djava.library.path=natives"); - if (optionContains(options, MakeProfileOption.FORGE)) { - builder.setClassLoaderConfig(ClientProfile.ClassLoaderConfig.AGENT); - } - OptionalFile optionalMacOs = new OptionalFile(); - optionalMacOs.name = "MacOSArgs"; - optionalMacOs.visible = false; - optionalMacOs.actions = new ArrayList<>(1); - optionalMacOs.actions.add(new OptionalActionJvmArgs(List.of("-XstartOnFirstThread"))); - optionalMacOs.triggersList = List.of(new OSTrigger(JVMHelper.OS.MACOSX)); - optionals.add(optionalMacOs); - } - if (optionContains(options, MakeProfileOption.LWJGLMAC)) { - OptionalFile optionalMac = new OptionalFile(); - optionalMac.name = "MacLwjgl"; - optionalMac.visible = false; - optionalMac.actions.add(new OptionalActionFile(Map.of( - "libraries/libraries/org/lwjgl/lwjgl/3.2.1", "", - "libraries/libraries/org/lwjgl/lwjgl-glfw/3.2.1", "", - "libraries/libraries/org/lwjgl/lwjgl-openal/3.2.1", "", - "libraries/libraries/org/lwjgl/lwjgl-stb/3.2.1", "", - "libraries/libraries/org/lwjgl/lwjgl-tinyfd/3.2.1", "", - "libraries/libraries/org/lwjgl/lwjgl-opengl/3.2.1", "", - "libraries/libraries/org/lwjgl/lwjgl-jemalloc/3.2.1", "" - ))); - optionalMac.triggersList = List.of(new OSTrigger(JVMHelper.OS.MACOSX)); - optionals.add(optionalMac); - OptionalFile optionalOther = new OptionalFile(); - optionalOther.name = "NonMacLwjgl"; - optionalOther.visible = false; - optionalOther.actions.add(new OptionalActionFile(Map.of( - "libraries/libraries/org/lwjgl/lwjgl/3.2.2", "", - "libraries/libraries/org/lwjgl/lwjgl-glfw/3.2.2", "", - "libraries/libraries/org/lwjgl/lwjgl-openal/3.2.2", "", - "libraries/libraries/org/lwjgl/lwjgl-stb/3.2.2", "", - "libraries/libraries/org/lwjgl/lwjgl-tinyfd/3.2.2", "", - "libraries/libraries/org/lwjgl/lwjgl-opengl/3.2.2", "", - "libraries/libraries/org/lwjgl/lwjgl-jemalloc/3.2.2", "" - ))); - OSTrigger nonMacTrigger = new OSTrigger(JVMHelper.OS.MACOSX); - nonMacTrigger.inverted = true; - optionalOther.triggersList = List.of(nonMacTrigger); - optionals.add(optionalOther); - } - if (version.compareTo(ClientProfile.Version.MC117) >= 0) { - builder.setMinJavaVersion(16); - builder.setRecommendJavaVersion(16); - } - jvmArgs.add("-Dfml.ignorePatchDiscrepancies=true"); - jvmArgs.add("-Dfml.ignoreInvalidMinecraftCertificates=true"); - builder.setJvmArgs(jvmArgs); - builder.setUpdateOptional(optionals); - List clientArgs = new ArrayList<>(); - if (optionContains(options, MakeProfileOption.LAUNCHWRAPPER)) { - if (optionContains(options, MakeProfileOption.LITELOADER)) { - clientArgs.add("--tweakClass"); - clientArgs.add("com.mumfrey.liteloader.launch.LiteLoaderTweaker"); - } - if (optionContains(options, MakeProfileOption.FORGE)) { - clientArgs.add("--tweakClass"); - if (version.compareTo(ClientProfile.Version.MC1710) > 0) { - clientArgs.add("net.minecraftforge.fml.common.launcher.FMLTweaker"); - } else { - clientArgs.add("cpw.mods.fml.common.launcher.FMLTweaker"); - } - if (version.compareTo(ClientProfile.Version.MC1122) <= 0) { - builder.setMinJavaVersion(8); - builder.setRecommendJavaVersion(8); - builder.setMaxJavaVersion(8); - } - } - } - builder.setClientArgs(clientArgs); - - return builder.createClientProfile(); - } - - private static boolean optionContains(MakeProfileOption[] options, MakeProfileOption option) { - return Arrays.stream(options).anyMatch(e -> e == option); - } - - public static String getMainClassByVersion(ClientProfile.Version version, MakeProfileOption... options) { - if (optionContains(options, MakeProfileOption.LAUNCHWRAPPER)) { - return "net.minecraft.launchwrapper.Launch"; - } - return "net.minecraft.client.main.Main"; - } - - public static MakeProfileOption[] getMakeProfileOptionsFromDir(Path dir, ClientProfile.Version version) { - List options = new ArrayList<>(2); - if (Files.exists(dir.resolve("forge.jar"))) { - options.add(MakeProfileOption.FORGE); - } - if (Files.exists(dir.resolve("liteloader.jar"))) { - options.add(MakeProfileOption.LITELOADER); - } - if (Files.exists(dir.resolve("libraries/org/lwjgl/lwjgl/3.2.2")) && Files.exists(dir.resolve("libraries/org/lwjgl/lwjgl/3.2.1"))) { - options.add(MakeProfileOption.LWJGLMAC); - } - if (version.compareTo(ClientProfile.Version.MC1122) <= 0) { - options.add(MakeProfileOption.LAUNCHWRAPPER); - } - return options.toArray(new MakeProfileOption[0]); - } - @SuppressWarnings("deprecation") public static void saveProfile(ClientProfile profile, Path path) throws IOException { if (profile.getUUID() == null) profile.setUUID(UUID.randomUUID()); @@ -241,7 +110,4 @@ public void invoke(String... args) throws Exception { } } - public enum MakeProfileOption { - LAUNCHWRAPPER, VANILLA, FORGE, FABRIC, LITELOADER, LWJGLMAC - } } diff --git a/LaunchServer/src/main/java/pro/gravit/launchserver/helper/MakeProfileHelper.java b/LaunchServer/src/main/java/pro/gravit/launchserver/helper/MakeProfileHelper.java new file mode 100644 index 00000000..6f155399 --- /dev/null +++ b/LaunchServer/src/main/java/pro/gravit/launchserver/helper/MakeProfileHelper.java @@ -0,0 +1,250 @@ +package pro.gravit.launchserver.helper; + +import pro.gravit.launcher.profiles.ClientProfile; +import pro.gravit.launcher.profiles.ClientProfileBuilder; +import pro.gravit.launcher.profiles.optional.OptionalFile; +import pro.gravit.launcher.profiles.optional.actions.OptionalActionFile; +import pro.gravit.launcher.profiles.optional.actions.OptionalActionJvmArgs; +import pro.gravit.launcher.profiles.optional.triggers.OSTrigger; +import pro.gravit.utils.helper.JVMHelper; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.*; + +public class MakeProfileHelper { + public static ClientProfile makeProfile(ClientProfile.Version version, String title, MakeProfileOption... options) { + ClientProfileBuilder builder = new ClientProfileBuilder(); + builder.setVersion(version.name); + builder.setDir(title); + builder.setAssetDir("asset" + version.name); + builder.setAssetIndex(version.name); + builder.setInfo("Информация о сервере"); + builder.setTitle(title); + builder.setUuid(UUID.randomUUID()); + builder.setMainClass(getMainClassByVersion(version, options)); + builder.setServers(List.of(new ClientProfile.ServerProfile(title, "localhost", 25535))); + // ------------ + builder.setUpdateVerify(List.of("libraries", "natives", "mods", "minecraft.jar", "forge.jar", "liteloader.jar")); + { + List classPath = new ArrayList<>(5); + classPath.add("libraries"); + classPath.add("minecraft.jar"); + if (version.compareTo(ClientProfile.Version.MC1122) <= 0) { + findOption(options, MakeProfileOptionForge.class).ifPresent(e -> classPath.add("forge.jar")); + findOption(options, MakeProfileOptionLiteLoader.class).ifPresent(e -> classPath.add("liteloader.jar")); + } + builder.setClassPath(classPath); + } + builder.setUpdate(List.of("servers.dat")); + List jvmArgs = new ArrayList<>(4); + Set optionals = new HashSet<>(); + jvmArgs.add("-XX:+DisableAttachMechanism"); + // Official Mojang launcher java arguments + jvmArgs.add("-XX:+UseG1GC"); + jvmArgs.add("-XX:+UnlockExperimentalVMOptions"); + jvmArgs.add("-XX:G1NewSizePercent=20"); + jvmArgs.add("-XX:MaxGCPauseMillis=50"); + jvmArgs.add("-XX:G1HeapRegionSize=32M"); + // ----------- + Optional forge = findOption(options, MakeProfileOptionForge.class); + if (version.compareTo(ClientProfile.Version.MC1122) > 0) { + jvmArgs.add("-Djava.library.path=natives"); + OptionalFile optionalMacOs = new OptionalFile(); + optionalMacOs.name = "MacOSArgs"; + optionalMacOs.visible = false; + optionalMacOs.actions = new ArrayList<>(1); + optionalMacOs.actions.add(new OptionalActionJvmArgs(List.of("-XstartOnFirstThread"))); + optionalMacOs.triggersList = List.of(new OSTrigger(JVMHelper.OS.MACOSX)); + optionals.add(optionalMacOs); + } + if (findOption(options, MakeProfileOptionLwjgl.class).isPresent()) { + OptionalFile optionalMac = new OptionalFile(); + optionalMac.name = "MacLwjgl"; + optionalMac.visible = false; + optionalMac.actions = new ArrayList<>(1); + optionalMac.actions.add(new OptionalActionFile(Map.of( + "libraries/org/lwjgl/lwjgl/3.2.1", "", + "libraries/org/lwjgl/lwjgl-glfw/3.2.1", "", + "libraries/org/lwjgl/lwjgl-openal/3.2.1", "", + "libraries/org/lwjgl/lwjgl-stb/3.2.1", "", + "libraries/org/lwjgl/lwjgl-tinyfd/3.2.1", "", + "libraries/org/lwjgl/lwjgl-opengl/3.2.1", "", + "libraries/org/lwjgl/lwjgl-jemalloc/3.2.1", "" + ))); + optionalMac.triggersList = List.of(new OSTrigger(JVMHelper.OS.MACOSX)); + optionals.add(optionalMac); + OptionalFile optionalOther = new OptionalFile(); + optionalOther.name = "NonMacLwjgl"; + optionalOther.visible = false; + optionalOther.actions = new ArrayList<>(1); + optionalOther.actions.add(new OptionalActionFile(Map.of( + "libraries/org/lwjgl/lwjgl/3.2.2", "", + "libraries/org/lwjgl/lwjgl-glfw/3.2.2", "", + "libraries/org/lwjgl/lwjgl-openal/3.2.2", "", + "libraries/org/lwjgl/lwjgl-stb/3.2.2", "", + "libraries/org/lwjgl/lwjgl-tinyfd/3.2.2", "", + "libraries/org/lwjgl/lwjgl-opengl/3.2.2", "", + "libraries/org/lwjgl/lwjgl-jemalloc/3.2.2", "" + ))); + OSTrigger nonMacTrigger = new OSTrigger(JVMHelper.OS.MACOSX); + nonMacTrigger.inverted = true; + optionalOther.triggersList = List.of(nonMacTrigger); + optionals.add(optionalOther); + } + if (version.compareTo(ClientProfile.Version.MC117) >= 0) { + builder.setMinJavaVersion(16); + builder.setRecommendJavaVersion(16); + } + jvmArgs.add("-Dfml.ignorePatchDiscrepancies=true"); + jvmArgs.add("-Dfml.ignoreInvalidMinecraftCertificates=true"); + builder.setJvmArgs(jvmArgs); + builder.setUpdateOptional(optionals); + List clientArgs = new ArrayList<>(); + if (findOption(options, MakeProfileOptionLaunchWrapper.class).isPresent()) { + if (findOption(options, MakeProfileOptionLiteLoader.class).isPresent()) { + clientArgs.add("--tweakClass"); + clientArgs.add("com.mumfrey.liteloader.launch.LiteLoaderTweaker"); + } + if (forge.isPresent()) { + clientArgs.add("--tweakClass"); + if (version.compareTo(ClientProfile.Version.MC1710) > 0) { + clientArgs.add("net.minecraftforge.fml.common.launcher.FMLTweaker"); + } else { + clientArgs.add("cpw.mods.fml.common.launcher.FMLTweaker"); + } + if (version.compareTo(ClientProfile.Version.MC1122) <= 0) { + builder.setMinJavaVersion(8); + builder.setRecommendJavaVersion(8); + builder.setMaxJavaVersion(8); + } + } + } else if (version.compareTo(ClientProfile.Version.MC1122) > 0) { + if (forge.isPresent()) { + clientArgs.addAll(forge.get().makeClientArgs()); + builder.setClassLoaderConfig(ClientProfile.ClassLoaderConfig.AGENT); + if (version.compareTo(ClientProfile.Version.MC1165) <= 0) { + builder.setMaxJavaVersion(15); + } + } + } + builder.setClientArgs(clientArgs); + + return builder.createClientProfile(); + } + + @SuppressWarnings("unchecked") + private static Optional findOption(MakeProfileOption[] options, Class clazz) { + return (Optional) Arrays.stream(options).filter((o) -> clazz.isAssignableFrom(o.getClass())).findFirst(); + } + + public static String getMainClassByVersion(ClientProfile.Version version, MakeProfileOption... options) { + if (findOption(options, MakeProfileOptionLaunchWrapper.class).isPresent()) { + return "net.minecraft.launchwrapper.Launch"; + } + if (findOption(options, MakeProfileOptionForge.class).isPresent() && version.compareTo(ClientProfile.Version.MC1122) > 0) { + return "cpw.mods.modlauncher.Launcher"; + } + if (findOption(options, MakeProfileOptionFabric.class).isPresent()) { + return "net.fabricmc.loader.launch.knot.KnotClient"; + } + return "net.minecraft.client.main.Main"; + } + + public static MakeProfileOption[] getMakeProfileOptionsFromDir(Path dir, ClientProfile.Version version) throws IOException { + List options = new ArrayList<>(2); + if (version.compareTo(ClientProfile.Version.MC1122) <= 0) { + if (Files.exists(dir.resolve("forge.jar"))) { + options.add(new MakeProfileOptionForge()); + } + } else { + if (Files.exists(dir.resolve("libraries/net/minecraftforge/forge"))) { + options.add(new MakeProfileOptionForge(dir)); + } + if (Files.exists(dir.resolve("libraries/net/fabricmc/fabric-loader"))) { + options.add(new MakeProfileOptionFabric()); + } + } + if (Files.exists(dir.resolve("liteloader.jar"))) { + options.add(new MakeProfileOptionLiteLoader()); + } + if (Files.exists(dir.resolve("libraries/org/lwjgl/lwjgl/3.2.2")) && Files.exists(dir.resolve("libraries/org/lwjgl/lwjgl/3.2.1"))) { + options.add(new MakeProfileOptionLwjgl()); + } + if (version.compareTo(ClientProfile.Version.MC1122) <= 0) { + options.add(new MakeProfileOptionLaunchWrapper()); + } + return options.toArray(new MakeProfileOption[0]); + } + + public interface MakeProfileOption { + } + + public static class MakeProfileOptionForge implements MakeProfileOption { + public String launchTarget; + public String forgeVersion; + public String forgeGroup; + public String minecraftVersion; + public String mcpVersion; + + public List makeClientArgs() { + if (launchTarget == null) return List.of(); + return List.of("--launchTarget", launchTarget, "--fml.forgeVersion", forgeVersion, "--fml.mcVersion", minecraftVersion, "--fml.forgeGroup", forgeGroup, "--fml.mcpVersion", mcpVersion); + } + + public MakeProfileOptionForge() { + + } + + public MakeProfileOptionForge(String launchTarget, String forgeVersion, String forgeGroup, String minecraftVersion, String mcpVersion) { + this.launchTarget = launchTarget; + this.forgeVersion = forgeVersion; + this.forgeGroup = forgeGroup; + this.minecraftVersion = minecraftVersion; + this.mcpVersion = mcpVersion; + } + + public MakeProfileOptionForge(Path clientDir) throws IOException { + Path libraries = clientDir.resolve("libraries"); + if (!Files.exists(libraries)) { + throw new IOException("libraries not found"); + } + Path forgePath = findFirstDir(libraries.resolve("net/minecraftforge/forge")); + if (forgePath == null) { + throw new IOException("forge not found"); + } + String[] forgeFullVersion = forgePath.getFileName().toString().split("-"); + minecraftVersion = forgeFullVersion[0]; + forgeVersion = forgeFullVersion[1]; + launchTarget = "fmlclient"; + forgeGroup = "net.minecraftforge"; + Path minecraftPath = findFirstDir(libraries.resolve("net/minecraft/client")); + if (minecraftPath == null) { + throw new IOException("mcp not found"); + } + String[] minecraftFullVersion = minecraftPath.getFileName().toString().split("-"); + mcpVersion = minecraftFullVersion[1]; + } + } + + private static Path findFirstDir(Path path) throws IOException { + return Files.list(path).findFirst().orElse(null); + } + + public static class MakeProfileOptionLaunchWrapper implements MakeProfileOption { + + } + + public static class MakeProfileOptionFabric implements MakeProfileOption { + + } + + public static class MakeProfileOptionLiteLoader implements MakeProfileOption { + + } + + public static class MakeProfileOptionLwjgl implements MakeProfileOption { + + } +}