From 76570ddbe583b1fc5b203cb2302b8ebfd7b9303b Mon Sep 17 00:00:00 2001 From: Gravita <12893402+gravit0@users.noreply.github.com> Date: Sun, 4 May 2025 19:02:46 +0700 Subject: [PATCH 1/9] [FEATURE] Support directories in classpath in ServerWrapper --- .../gravit/launcher/server/ServerWrapper.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java b/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java index aaa393e0..d6fb2002 100644 --- a/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java +++ b/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java @@ -25,15 +25,18 @@ import pro.gravit.utils.launch.*; import java.io.File; +import java.io.IOException; import java.lang.invoke.MethodHandle; import java.lang.invoke.MethodHandles; import java.lang.invoke.MethodType; import java.lang.reflect.Type; +import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; public class ServerWrapper extends JsonConfigurable { public static final Path configFile = Paths.get(System.getProperty("serverwrapper.configFile", "ServerWrapperConfig.json")); @@ -209,7 +212,19 @@ public void run(String... args) throws Throwable { LaunchOptions options = new LaunchOptions(); options.enableHacks = config.enableHacks; options.moduleConf = config.moduleConf; - classLoaderControl = launch.init(config.classpath.stream().map(Paths::get).collect(Collectors.toCollection(ArrayList::new)), config.nativesDir, options); + classLoaderControl = launch.init(config.classpath.stream() + .map(Paths::get) + .flatMap(p -> { + if(!Files.isDirectory(p)) { + return Stream.of(p); + } + try { + return Files.walk(p).filter(e -> e.getFileName().toString().endsWith(".jar")); + } catch (IOException e) { + throw new RuntimeException(e); + } + }) + .collect(Collectors.toCollection(ArrayList::new)), config.nativesDir, options); if(ServerAgent.isAgentStarted()) { ClientService.instrumentation = ServerAgent.inst; } From b41e8db336d547a5c9151721e6f66c720382924b Mon Sep 17 00:00:00 2001 From: Gravita <12893402+gravit0@users.noreply.github.com> Date: Sun, 4 May 2025 19:38:34 +0700 Subject: [PATCH 2/9] [FEATURE] Add ServerWrapperInlineInitializer --- .../gravit/launcher/server/ServerWrapper.java | 56 +++++++++++-------- .../ServerWrapperInlineInitializer.java | 9 +++ 2 files changed, 42 insertions(+), 23 deletions(-) create mode 100644 ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapperInlineInitializer.java diff --git a/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java b/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java index d6fb2002..24389d21 100644 --- a/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java +++ b/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java @@ -103,31 +103,20 @@ public void getProfiles() throws Exception { } } - public void run(String... args) throws Throwable { + + public void initialize() throws Exception { initGson(); AuthRequest.registerProviders(); GetAvailabilityAuthRequest.registerProviders(); OptionalAction.registerProviders(); OptionalTrigger.registerProviders(); - if (args.length > 0 && args[0].equalsIgnoreCase("setup") && !disableSetup) { - LogHelper.debug("Read ServerWrapperConfig.json"); - loadConfig(); - ServerWrapperSetup setup = new ServerWrapperSetup(); - setup.run(); - System.exit(0); - } - if (args.length > 1 && args[0].equalsIgnoreCase("installAuthlib") && !disableSetup) { - LogHelper.debug("Read ServerWrapperConfig.json"); - loadConfig(); - InstallAuthlib command = new InstallAuthlib(); - command. run(args[1]); - System.exit(0); - } LogHelper.debug("Read ServerWrapperConfig.json"); loadConfig(); + } + + public void connect() throws Exception { config.applyEnv(); updateLauncherConfig(); - Launcher.applyLauncherEnv(Objects.requireNonNullElse(config.env, LauncherConfig.LauncherEnvironment.STD)); StdWebSocketService service = StdWebSocketService.initWebSockets(config.address).get(); service.reconnectCallback = () -> { @@ -139,11 +128,6 @@ public void run(String... args) throws Throwable { LogHelper.error(e); } }; - if(config.properties != null) { - for(Map.Entry e : config.properties.entrySet()) { - System.setProperty(e.getKey(), e.getValue()); - } - } Request.setRequestService(service); if (config.logFile != null) LogHelper.addOutput(IOHelper.newWriter(Paths.get(config.logFile), true)); { @@ -156,6 +140,34 @@ public void run(String... args) throws Throwable { if(config.encodedServerEcPublicKey != null) { KeyService.serverEcPublicKey = SecurityHelper.toPublicECDSAKey(config.encodedServerEcPublicKey); } + ClientService.nativePath = config.nativesDir; + ConfigService.serverName = config.serverName; + } + + public void run(String... args) throws Throwable { + initialize(); + if (args.length > 0 && args[0].equalsIgnoreCase("setup") && !disableSetup) { + ServerWrapperSetup setup = new ServerWrapperSetup(); + setup.run(); + System.exit(0); + } + if (args.length > 1 && args[0].equalsIgnoreCase("installAuthlib") && !disableSetup) { + InstallAuthlib command = new InstallAuthlib(); + command. run(args[1]); + System.exit(0); + } + connect(); + if(config.properties != null) { + for(Map.Entry e : config.properties.entrySet()) { + System.setProperty(e.getKey(), e.getValue()); + } + } + if(config.encodedServerRsaPublicKey != null) { + KeyService.serverRsaPublicKey = SecurityHelper.toPublicRSAKey(config.encodedServerRsaPublicKey); + } + if(config.encodedServerEcPublicKey != null) { + KeyService.serverEcPublicKey = SecurityHelper.toPublicECDSAKey(config.encodedServerEcPublicKey); + } String classname = (config.mainclass == null || config.mainclass.isEmpty()) ? args[0] : config.mainclass; if (classname.isEmpty()) { LogHelper.error("MainClass not found. Please set MainClass for ServerWrapper.json or first commandline argument"); @@ -185,8 +197,6 @@ public void run(String... args) throws Throwable { System.arraycopy(args, 1, real_args, 0, args.length - 1); } else real_args = args; Launch launch; - ClientService.nativePath = config.nativesDir; - ConfigService.serverName = config.serverName; if(config.loadNatives != null) { for(String e : config.loadNatives) { System.load(Paths.get(config.nativesDir).resolve(ClientService.findLibrary(e)).toAbsolutePath().toString()); diff --git a/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapperInlineInitializer.java b/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapperInlineInitializer.java new file mode 100644 index 00000000..178797cb --- /dev/null +++ b/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapperInlineInitializer.java @@ -0,0 +1,9 @@ +package pro.gravit.launcher.server; + +public class ServerWrapperInlineInitializer { + public static void initialize() throws Exception { + ServerWrapper.wrapper = new ServerWrapper(ServerWrapper.Config.class, ServerWrapper.configFile); + ServerWrapper.wrapper.initialize(); + ServerWrapper.wrapper.connect(); + } +} From 2fea94071b6260d32124c1f30834166e4084aac1 Mon Sep 17 00:00:00 2001 From: Gravita <12893402+gravit0@users.noreply.github.com> Date: Sun, 4 May 2025 21:12:57 +0700 Subject: [PATCH 3/9] [FEATURE] Support runCompatClasses in ServerWrapperInlineInitializer --- .../gravit/launcher/server/ServerWrapper.java | 24 +++++++++++-------- .../ServerWrapperInlineInitializer.java | 3 ++- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java b/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java index 24389d21..6319a3b3 100644 --- a/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java +++ b/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java @@ -142,6 +142,19 @@ public void connect() throws Exception { } ClientService.nativePath = config.nativesDir; ConfigService.serverName = config.serverName; + if(config.configServiceSettings != null) { + config.configServiceSettings.apply(); + } + } + + public void runCompatClasses() throws Throwable { + if(config.compatClasses != null) { + for (String e : config.compatClasses) { + Class clazz = classLoaderControl == null ? Class.forName(e) : classLoaderControl.getClass(e); + MethodHandle runMethod = MethodHandles.lookup().findStatic(clazz, "run", MethodType.methodType(void.class, ClassLoaderControl.class)); + runMethod.invoke(classLoaderControl); + } + } } public void run(String... args) throws Throwable { @@ -240,18 +253,9 @@ public void run(String... args) throws Throwable { } ClientService.classLoaderControl = classLoaderControl; ClientService.baseURLs = classLoaderControl.getURLs(); - if(config.configServiceSettings != null) { - config.configServiceSettings.apply(); - } LogHelper.info("Start Minecraft Server"); try { - if(config.compatClasses != null) { - for (String e : config.compatClasses) { - Class clazz = classLoaderControl.getClass(e); - MethodHandle runMethod = MethodHandles.lookup().findStatic(clazz, "run", MethodType.methodType(void.class, ClassLoaderControl.class)); - runMethod.invoke(classLoaderControl); - } - } + runCompatClasses(); LogHelper.debug("Invoke main method %s with %s", classname, launch.getClass().getName()); launch.launch(config.mainclass, config.mainmodule, Arrays.asList(real_args)); } catch (Throwable e) { diff --git a/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapperInlineInitializer.java b/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapperInlineInitializer.java index 178797cb..121f7a2f 100644 --- a/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapperInlineInitializer.java +++ b/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapperInlineInitializer.java @@ -1,9 +1,10 @@ package pro.gravit.launcher.server; public class ServerWrapperInlineInitializer { - public static void initialize() throws Exception { + public static void run() throws Throwable { ServerWrapper.wrapper = new ServerWrapper(ServerWrapper.Config.class, ServerWrapper.configFile); ServerWrapper.wrapper.initialize(); ServerWrapper.wrapper.connect(); + ServerWrapper.wrapper.runCompatClasses(); } } From 880957fa9bb5079961b7d1388282ef58470839d3 Mon Sep 17 00:00:00 2001 From: Gravita <12893402+gravit0@users.noreply.github.com> Date: Sun, 4 May 2025 21:15:44 +0700 Subject: [PATCH 4/9] [FEATURE] Support inline initialization in DebugMain --- .../launcher/runtime/debug/DebugMain.java | 17 ++++++++++++----- .../debug/DebugMainInlineInitializer.java | 7 +++++++ 2 files changed, 19 insertions(+), 5 deletions(-) create mode 100644 Launcher/src/main/java/pro/gravit/launcher/runtime/debug/DebugMainInlineInitializer.java diff --git a/Launcher/src/main/java/pro/gravit/launcher/runtime/debug/DebugMain.java b/Launcher/src/main/java/pro/gravit/launcher/runtime/debug/DebugMain.java index 1d3ee6c3..cecb2f28 100644 --- a/Launcher/src/main/java/pro/gravit/launcher/runtime/debug/DebugMain.java +++ b/Launcher/src/main/java/pro/gravit/launcher/runtime/debug/DebugMain.java @@ -28,6 +28,7 @@ public class DebugMain { public static String webSocketURL = System.getProperty("launcherdebug.websocket", "ws://localhost:9274/api"); public static String projectName = System.getProperty("launcherdebug.projectname", "Minecraft"); public static String unlockSecret = System.getProperty("launcherdebug.unlocksecret", ""); + public static boolean disableConsole = Boolean.getBoolean("launcherdebug.disableConsole"); public static boolean offlineMode = Boolean.getBoolean("launcherdebug.offlinemode"); public static boolean disableAutoRefresh = Boolean.getBoolean("launcherdebug.disableautorefresh"); public static String[] moduleClasses = System.getProperty("launcherdebug.modules", "").split(","); @@ -37,6 +38,14 @@ public class DebugMain { public static void main(String[] args) throws Throwable { LogHelper.printVersion("Launcher"); LogHelper.printLicense("Launcher"); + initialize(); + LogHelper.debug("Initialization LauncherEngine"); + LauncherEngine instance = LauncherEngine.newInstance(false, ClientRuntimeProvider.class); + instance.start(args); + LauncherEngine.exitLauncher(0); + } + + public static void initialize() throws Exception { IS_DEBUG.set(true); LogHelper.info("Launcher start in DEBUG mode (Only for developers)"); LogHelper.debug("Initialization LauncherConfig"); @@ -56,7 +65,9 @@ public static void main(String[] args) throws Throwable { } LauncherEngine.modulesManager.initModules(null); LauncherEngine.initGson(LauncherEngine.modulesManager); - ConsoleManager.initConsole(); + if(!disableConsole) { + ConsoleManager.initConsole(); + } LauncherEngine.modulesManager.invokeEvent(new PreConfigPhase()); RequestService service; if (offlineMode) { @@ -72,10 +83,6 @@ public static void main(String[] args) throws Throwable { if(!disableAutoRefresh) { Request.startAutoRefresh(); } - LogHelper.debug("Initialization LauncherEngine"); - LauncherEngine instance = LauncherEngine.newInstance(false, ClientRuntimeProvider.class); - instance.start(args); - LauncherEngine.exitLauncher(0); } @SuppressWarnings("unchecked") diff --git a/Launcher/src/main/java/pro/gravit/launcher/runtime/debug/DebugMainInlineInitializer.java b/Launcher/src/main/java/pro/gravit/launcher/runtime/debug/DebugMainInlineInitializer.java new file mode 100644 index 00000000..2686344f --- /dev/null +++ b/Launcher/src/main/java/pro/gravit/launcher/runtime/debug/DebugMainInlineInitializer.java @@ -0,0 +1,7 @@ +package pro.gravit.launcher.runtime.debug; + +public class DebugMainInlineInitializer { + public static void run() throws Exception { + DebugMain.initialize(); + } +} From 90f74aaf25a02f410934d48e2a2b46ac347a1f2f Mon Sep 17 00:00:00 2001 From: Gravita <12893402+gravit0@users.noreply.github.com> Date: Sun, 4 May 2025 21:54:51 +0700 Subject: [PATCH 5/9] [FEATURE] Build ServerWrapper inline jar --- .github/workflows/push.yml | 1 + ServerWrapper/build.gradle | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index aec14a00..ec012898 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -39,6 +39,7 @@ jobs: cp LaunchServer.jar ../../../artifacts/LaunchServer.jar cd ../../.. cp ServerWrapper/build/libs/ServerWrapper.jar artifacts/ServerWrapper.jar + cp ServerWrapper/build/libs/ServerWrapper-inline.jar artifacts/ServerWrapperInline.jar cp LauncherAuthlib/build/libs/LauncherAuthlib.jar artifacts/LauncherAuthlib.jar || true cp modules/*_module/build/libs/*.jar artifacts/modules || true cp modules/*_lmodule/build/libs/*.jar artifacts/modules || true diff --git a/ServerWrapper/build.gradle b/ServerWrapper/build.gradle index 68a4f2bc..a84b4353 100644 --- a/ServerWrapper/build.gradle +++ b/ServerWrapper/build.gradle @@ -48,6 +48,21 @@ pack project(':LauncherAPI') exclude 'module-info.class' } +tasks.register('inlinejar', Jar) { + dependsOn configurations.runtimeClasspath + from { + configurations.runtimeClasspath.filter {! (it.name =~ /gson.*\.jar/ || it.name =~ /error_prone_annotations.*\.jar/)}.collect { it.isDirectory() ? it : zipTree(it) } + } + from { + sourceSets.main.output + } + archiveClassifier.set('inline') + manifest.attributes("Main-Class": mainClassName, + "Automatic-Module-Name": "ServerWrapper" + ) + duplicatesStrategy = DuplicatesStrategy.EXCLUDE +} + publishing { publications { serverwrapperapi(MavenPublication) { @@ -94,4 +109,4 @@ pack project(':LauncherAPI') sign publishing.publications.serverwrapperapi } -assemble.dependsOn tasks.shadowJar +assemble.dependsOn tasks.shadowJar, tasks.inlinejar From 911ca1e69ffc7cefe7e2987548285dcf87f2528b Mon Sep 17 00:00:00 2001 From: Gravita <12893402+gravit0@users.noreply.github.com> Date: Sun, 4 May 2025 22:03:28 +0700 Subject: [PATCH 6/9] [FIX] Duplicate classpath and modulepath entities --- .../client/ClientLauncherEntryPoint.java | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/LauncherClient/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java b/LauncherClient/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java index 65dcb764..d38100b3 100644 --- a/LauncherClient/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java +++ b/LauncherClient/src/main/java/pro/gravit/launcher/client/ClientLauncherEntryPoint.java @@ -116,15 +116,6 @@ private static void realMain(String[] args) throws Throwable { // Verify ClientLauncher sign and classpath LogHelper.debug("Verifying ClientLauncher sign and classpath"); - Set ignoredPath = new HashSet<>(); - List classpath = resolveClassPath(ignoredPath, clientDir, params.actions, params.profile) - .collect(Collectors.toCollection(ArrayList::new)); - if(LogHelper.isDevEnabled()) { - for(var e : classpath) { - LogHelper.dev("Classpath entry %s", e); - } - } - List classpathURLs = classpath.stream().map(IOHelper::toURL).toList(); // Start client with WatchService monitoring RequestService service; if (params.offlineMode) { @@ -158,6 +149,18 @@ private static void realMain(String[] args) throws Throwable { System.load(Paths.get(params.nativesDir).resolve(ClientService.findLibrary(e)).toAbsolutePath().toString()); } } + Set ignoredPath = new HashSet<>(); + if(options.moduleConf != null && options.moduleConf.modulePath != null) { + List resolvedModulePath = resolveClassPath(ignoredPath, clientDir, null, params.profile).toList(); + } + List classpath = resolveClassPath(ignoredPath, clientDir, params.actions, params.profile) + .collect(Collectors.toCollection(ArrayList::new)); + if(LogHelper.isDevEnabled()) { + for(var e : classpath) { + LogHelper.dev("Classpath entry %s", e); + } + } + List classpathURLs = classpath.stream().map(IOHelper::toURL).toList(); if (classLoaderConfig == ClientProfile.ClassLoaderConfig.LAUNCHER || classLoaderConfig == ClientProfile.ClassLoaderConfig.MODULE) { if(JVMHelper.JVM_VERSION <= 11) { launch = new LegacyLaunch(); @@ -274,9 +277,11 @@ private static Stream resolveClassPathStream(Set ignorePaths, Path c public static Stream resolveClassPath(Set ignorePaths, Path clientDir, Set actions, ClientProfile profile) throws IOException { Stream result = resolveClassPathStream(ignorePaths, clientDir, profile.getClassPath()); - for (OptionalAction a : actions) { - if (a instanceof OptionalActionClassPath) - result = Stream.concat(result, resolveClassPathStream(ignorePaths, clientDir, ((OptionalActionClassPath) a).args)); + if(actions != null) { + for (OptionalAction a : actions) { + if (a instanceof OptionalActionClassPath) + result = Stream.concat(result, resolveClassPathStream(ignorePaths, clientDir, ((OptionalActionClassPath) a).args)); + } } return result; } From 2379fe5798d8e9ce31bcb581d95840c33c5e1dfb Mon Sep 17 00:00:00 2001 From: Gravita <12893402+gravit0@users.noreply.github.com> Date: Wed, 14 May 2025 20:30:49 +0700 Subject: [PATCH 7/9] [FIX] Enter serverName and address in console --- .../main/java/pro/gravit/launcher/server/ServerWrapper.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java b/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java index 6319a3b3..b97787a3 100644 --- a/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java +++ b/ServerWrapper/src/main/java/pro/gravit/launcher/server/ServerWrapper.java @@ -282,12 +282,12 @@ public void setConfig(Config config) { @Override public Config getDefaultConfig() { Config newConfig = new Config(); - newConfig.serverName = "your server name"; + newConfig.serverName = ""; newConfig.mainclass = ""; newConfig.extendedTokens = new HashMap<>(); newConfig.args = new ArrayList<>(); newConfig.classpath = new ArrayList<>(); - newConfig.address = "ws://localhost:9274/api"; + newConfig.address = ""; newConfig.classLoaderConfig = ClientProfile.ClassLoaderConfig.SYSTEM_ARGS; newConfig.env = LauncherConfig.LauncherEnvironment.STD; newConfig.properties = new HashMap<>(); From 3c9e00943308408066079de81cac58cde56c60f8 Mon Sep 17 00:00:00 2001 From: Antoni Date: Sun, 18 May 2025 21:43:14 +0300 Subject: [PATCH 8/9] [ANY] Rename Proguard Libraries for release --- .github/workflows/push.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index ec012898..b9cd5e3f 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -35,6 +35,7 @@ jobs: run: | mkdir -p artifacts/modules cd LaunchServer/build/libs + mv proguard proguard-libraries zip -r -9 ../../../artifacts/libraries.zip * -x "LaunchServer.jar" -x "LaunchServer-clean.jar" cp LaunchServer.jar ../../../artifacts/LaunchServer.jar cd ../../.. From 261e16cecb1b8066da56a0b378dadee0e6c92040 Mon Sep 17 00:00:00 2001 From: Gravita <12893402+gravit0@users.noreply.github.com> Date: Sun, 25 May 2025 12:19:02 +0700 Subject: [PATCH 9/9] [ANY] 5.6.15 stable --- LauncherCore/src/main/java/pro/gravit/utils/Version.java | 2 +- build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/LauncherCore/src/main/java/pro/gravit/utils/Version.java b/LauncherCore/src/main/java/pro/gravit/utils/Version.java index c8301391..dd3bcf83 100644 --- a/LauncherCore/src/main/java/pro/gravit/utils/Version.java +++ b/LauncherCore/src/main/java/pro/gravit/utils/Version.java @@ -6,7 +6,7 @@ public final class Version implements Comparable { public static final int MAJOR = 5; public static final int MINOR = 6; - public static final int PATCH = 14; + public static final int PATCH = 15; public static final int BUILD = 1; public static final Version.Type RELEASE = Type.STABLE; public final int major; diff --git a/build.gradle b/build.gradle index a2f55153..2cb7fbc8 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ id 'org.openjfx.javafxplugin' version '0.1.0' apply false } group = 'pro.gravit.launcher' -version = '5.6.14' +version = '5.6.15' apply from: 'props.gradle'