diff --git a/.github/dependabot.yml b/.github/dependabot.yml
index e06ad4553bfe30e718279191e2ed5a2d3a834741..02489077ce9c2395da04addd694bae8ae8fd95d8 100644
--- a/.github/dependabot.yml
+++ b/.github/dependabot.yml
@@ -14,7 +14,7 @@ updates:
     labels:
      - "dependencies"
      - "github-actions"
-    target-branch: "dependabot"
+    target-branch: "develop"
     pull-request-branch-name:
       separator: "-"
 
@@ -30,7 +30,7 @@ updates:
     labels:
       - "dependencies"
       - "gradle"
-    target-branch: "dependabot"
+    target-branch: "develop"
     pull-request-branch-name:
       separator: "-"
 
@@ -43,7 +43,7 @@ updates:
     labels:
       - "dependencies"
       - "gradle"
-    target-branch: "dependabot"
+    target-branch: "develop"
     pull-request-branch-name:
       separator: "-"
 
@@ -56,7 +56,7 @@ updates:
     labels:
       - "dependencies"
       - "gradle"
-    target-branch: "dependabot"
+    target-branch: "develop"
     pull-request-branch-name:
       separator: "-"
 
@@ -72,7 +72,7 @@ updates:
     labels:
       - "dependencies"
       - "npm"
-    target-branch: "dependabot"
+    target-branch: "develop"
     pull-request-branch-name:
       separator: "-"
 
@@ -88,6 +88,6 @@ updates:
     labels:
       - "dependencies"
       - "docker"
-    target-branch: "dependabot"
+    target-branch: "develop"
     pull-request-branch-name:
       separator: "-"
diff --git a/.runConfigurations/Run App.run.xml b/.runConfigurations/Run App.run.xml
index 7487e4db9b1867341616de18212ea4579c82df31..cca44e66dcfa447b6d8ad54e356d3a5bbf08473e 100644
--- a/.runConfigurations/Run App.run.xml	
+++ b/.runConfigurations/Run App.run.xml	
@@ -2,7 +2,7 @@
   <configuration default="false" name="Run App" type="JetRunConfigurationType">
     <option name="MAIN_CLASS_NAME" value="de.griefed.serverpackcreator.app.ServerPackCreatorKt" />
     <module name="serverpackcreator.serverpackcreator-app.main" />
-    <option name="PROGRAM_PARAMETERS" value="--home &quot;$ProjectFileDir$/serverpackcreator-app/tests&quot;" />
+    <option name="PROGRAM_PARAMETERS" value="--home &quot;$ProjectFileDir$/serverpackcreator-app/tests&quot; -cli" />
     <shortenClasspath name="NONE" />
     <option name="WORKING_DIRECTORY" value="$PROJECT_DIR$/serverpackcreator-app/tests" />
     <method v="2">
diff --git a/HELP.md b/HELP.md
index c67dbe18d07d95fa800db02fdd483da35e031d01..1698f4c021a6c3a3d511139b7dd5c2136b213de4 100644
--- a/HELP.md
+++ b/HELP.md
@@ -111,6 +111,8 @@ The following placeholders will be replaced by ServerPackCreator during the crea
 | SPC_JABBA_INSTALL_URL_PS_SPC           | The URL to the PowerShell install-script for Jabba.                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
 | SPC_JABBA_INSTALL_URL_SH_SPC           | The URL to the Shell install-script for Jabba.                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
 | SPC_JABBA_INSTALL_VERSION_SPC          | The version of Jabba to install.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
+| SPC_SERVERSTARTERJAR_FORCE_FETCH_SPC   | Whether the ServerStarterJar, when using Forge or NeoForge, should be refreshed at every start. Also affects updates to newer versions.                                                                                                                                                                                                                                                                                                                                                                         |
+| SPC_SERVERSTARTERJAR_VERSION_SPC       | The version of the ServerStarterJar to use. Set to 'latest' to always use the latest available version.                                                                                                                                                                                                                                                                                                                                                                                                         |
 
 Jabba is a piece of software which makes the installation and usage of a JDK according to the system you are on very easy.
 It is used by the `install_java.xxx`-scripts to supply the correct Java version for your modded server should the criteria
@@ -138,6 +140,9 @@ The contents of the variables are as follows. See `### Default values` above for
 | JDK_VENDOR                     | SPC_JDK_VENDOR_SPC                                           |
 | JABBA_INSTALL_URL              | SPC_JABBA_INSTALL_URL_PS_SPC or SPC_JABBA_INSTALL_URL_SH_SPC |
 | JABBA_INSTALL_VERSION          | SPC_JABBA_INSTALL_VERSION_SPC                                |
+| JABBA_INSTALL_VERSION          | SPC_JABBA_INSTALL_VERSION_SPC                                |
+| SERVERSTARTERJAR_FORCE_FETCH   | SPC_SERVERSTARTERJAR_FORCE_FETCH_SPC                         |
+| SERVERSTARTERJAR_VERSION       | SPC_SERVERSTARTERJAR_VERSION_SPC                             |
 
 Plus any additional custom key-value pair you added to your server pack config.
 
@@ -481,54 +486,56 @@ There are a couple more ways to use/run ServerPackCreator which may or may not b
 depending on how you plan on using it:
 
 ```
-  -lang: Allows you to use one of the available languages for ServerPackCreator. I can not
-         guarantee that each of the following available languages is 100% translated.
-         You best choice is en_us, or not specifying any as that is the default, because
-         I write ServerPackCreator with english in mind. Available usages:
-         -lang en_us
-         -lang uk_ua
-         -lang de_de
-
-  -cgen: Only available for the commandline interface. This will start the generation of
-         a new configuration file. You will be asked to enter information about your modpack
-         step-by-step. Each setting you enter will be checked for errors before it is saved.
-         If everything you enter is valid and without errors, it will be written to a new
-         serverpackcreator.conf and ServerPackCreator will immediately start a run with said
-         configuration file, generating a server pack for you.
-
--update: Check whether a new version of ServerPackCreator is available for download.
-         If an update is available, the version and link to the release of said update are
-         written to the console so you can from work with it from there.
-         Note: Automatic updates are currently not planned nor supported, and neither are
-         downloads of any available updates to your system. You need to update manually.
-
-   -cli: Run ServerPackCreator in Command-line interface mode. Checks the serverpackcreator.conf
-         for errors and if none are found, starts the generation of a server pack with the configuration
-         provided by your serverpackcreator.conf.
-
-   -web: Run ServerPackCreator as a webservice available at http://localhost:8080. The webservice
-         provides the same functionality as running ServerPackCreator in GUI mode (so no Commandline
-         arguments and a non-headless environment) as well as a REST API which can be used in different ways.
-         For more information about the REST API, please see the Java documentation:
-          - GitHub Pages: https://griefed.github.io/ServerPackCreator/
-          - GitLab Pages: https://griefed.pages.griefed.de/ServerPackCreator/
-
-   -gui: Run ServerPackCreator using the graphical user interface. If your environment supports
-         graphics, i.e. is not headless, then this is the default mode in which ServerPackCreator
-         started as when no arguments are used.
-
---setup: Set up and prepare the environment for subsequent runs of ServerPackCreator.
-         This will create/copy all files needed for ServerPackCreator to function
-         properly from inside its JAR-file and setup everything else, too. You can pass a properties-file, too
-         if you so desire.
-         Examples:
-         --setup "/path/to/custom.properties"
-         --setup "C:\path\to\custom.properties"
-
---home:  Override the home-directory setting for your user.
-         Examples:
-         --home "/path/to/directory"
-         --home "C:\users\<YOUR_USER>\SPC"
+          -lang: Allows you to use one of the available languages for ServerPackCreator. I can not
+                 guarantee that each of the following available languages is 100% translated.
+                 You best choice is en_us, or not specifying any as that is the default, because
+                 I write ServerPackCreator with english in mind. Available usages:
+                 -lang en_us
+                 -lang uk_ua
+                 -lang de_de
+
+          -cgen: Only available for the commandline interface. This will start the generation of
+                 a new configuration file. You will be asked to enter information about your modpack
+                 step-by-step. Each setting you enter will be checked for errors before it is saved.
+                 If everything you enter is valid and without errors, it will be written to a new
+                 serverpackcreator.conf and ServerPackCreator will immediately start a run with said
+                 configuration file, generating a server pack for you.
+
+        -update: Check whether a new version of ServerPackCreator is available for download.
+                 If an update is available, the version and link to the release of said update are
+                 written to the console so you can from work with it from there.
+                 Note: Automatic updates are currently not planned nor supported, and neither are
+                 downloads of any available updates to your system. You need to update manually.
+
+           -cli: Run ServerPackCreator in an interactive commandline-mode.
+
+        -config: Generate a server pack from a specific server pack configuration from the commandline.
+  --destination: Only effective in combination with -config. Sets the destination in which the server pack
+                 will be generated in.
+
+           -web: Run ServerPackCreator as a webservice available at http://localhost:8080. The webservice
+                 provides the same functionality as running ServerPackCreator in GUI mode (so no Commandline
+                 arguments and a non-headless environment) as well as a REST API which can be used in different ways.
+                 For more information about the REST API, please see the Java documentation:
+                  - GitHub Pages: https://griefed.github.io/ServerPackCreator/
+                  - GitLab Pages: https://griefed.pages.griefed.de/ServerPackCreator/
+
+           -gui: Run ServerPackCreator using the graphical user interface. If your environment supports
+                 graphics, i.e. is not headless, then this is the default mode in which ServerPackCreator
+                 started as when no arguments are used.
+
+        --setup: Set up and prepare the environment for subsequent runs of ServerPackCreator.
+                 This will create/copy all files needed for ServerPackCreator to function
+                 properly from inside its JAR-file and setup everything else, too. You can pass a properties-file, too
+                 if you so desire.
+                 Examples:
+                 --setup "/path/to/custom.properties"
+                 --setup "C:\path\to\custom.properties"
+
+        --home:  Override the home-directory setting for your user.
+                 Examples:
+                 --home "/path/to/directory"
+                 --home "C:\users\<YOUR_USER>\SPC"
 ```
 
 Each of these modes has its advantages and disadvantages.
@@ -539,7 +546,7 @@ Each of these modes has its advantages and disadvantages.
 |:--------------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------------------------------------------------------------------------------------------|
 | No need for a graphical environment. Can be used on a server to generate a server pack for immediate use.                                   | Gathering of information for a configuration file is tedious.                                                         |
 | Step-by-Step generation of a configuration-file with the use of the `-cgen` argument. Generated config will be used immediately afterwards. | No convenience features file folder-browsing or jumping to the generated server pack after generation.                |
-|                                                                                                                                             | Debugging in case of a broken/erroring configuration file can be time consuming. Careful reading of logs is required. |
+| Automate using `-config`, and optionally `--destination`, in order to create a server pack from a specific config, in a specific location.  | Debugging in case of a broken/erroring configuration file can be time consuming. Careful reading of logs is required. |
 |                                                                                                                                             | Manual editing of the configuration-file in case you want to change it.                                               |
 
 ### GUI:
@@ -824,21 +831,21 @@ For a plugin to be accepted, you must at least provide:
 
 The serverpackcreator.conf file allows you to customize a couple of different things:
 
-| Variable                                                   | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
-|------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| modpackDir                                                 | The path to the directory/ZIP-archive where your modpack resides in.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
-| [[inclusions]]                                             | Each inclusion-specification ensures that the file or directory from `source` gets included in your server pack one way or another.<br>A basic configuration usually contains the `mods` and `config` directories. No need to prefix them with the path to your modpack.<br>ServerPackCreator will take care of that automatically by itself.<br>If you want to include files or folders *outside* of your modpack, then you will need to specify the whole path.<br>Below are some examples for including files from your modpack (`mods` and `config`) as well as files and folders from outside the modpack.<br>[[inclusions]]<br>&nbsp;&nbsp;&nbsp;&nbsp;destination = ""<br>&nbsp;&nbsp;&nbsp;&nbsp;exclusionFilter = ""<br>&nbsp;&nbsp;&nbsp;&nbsp;source = "config"<br>&nbsp;&nbsp;&nbsp;&nbsp;inclusionFilter = ""<br>[[inclusions]]<br>&nbsp;&nbsp;&nbsp;&nbsp;destination = ""<br>&nbsp;&nbsp;&nbsp;&nbsp;exclusionFilter = ""<br>&nbsp;&nbsp;&nbsp;&nbsp;source = "mods"<br>&nbsp;&nbsp;&nbsp;&nbsp;inclusionFilter = ""<br>[[inclusions]]<br>&nbsp;&nbsp;&nbsp;&nbsp;destination = "SomeFiles"<br>&nbsp;&nbsp;&nbsp;&nbsp;exclusionFilter = ""<br>&nbsp;&nbsp;&nbsp;&nbsp;source = "C:\\Some\\Path\\With\Files<br>&nbsp;&nbsp;&nbsp;&nbsp;inclusionFilter = ""<br>[[inclusions]]<br>&nbsp;&nbsp;&nbsp;&nbsp;destination = "somefiles"<br>&nbsp;&nbsp;&nbsp;&nbsp;exclusionFilter = ""<br>&nbsp;&nbsp;&nbsp;&nbsp;source = "/home/myuser/some/files"<br>&nbsp;&nbsp;&nbsp;&nbsp;inclusionFilter = ""<br>[[inclusions]]<br>&nbsp;&nbsp;&nbsp;&nbsp;destination = "MyInstructions.md"<br>&nbsp;&nbsp;&nbsp;&nbsp;exclusionFilter = ""<br>&nbsp;&nbsp;&nbsp;&nbsp;source = "C:\\MyAwesomeModpack\\Stuff\\HowTo.md"br>&nbsp;&nbsp;&nbsp;&nbsp;inclusionFilter = ""<br>[[inclusions]]<br>&nbsp;&nbsp;&nbsp;&nbsp;destination = "MyInstructions.md"<br>&nbsp;&nbsp;&nbsp;&nbsp;exclusionFilter = ""<br>&nbsp;&nbsp;&nbsp;&nbsp;source ="/home/myuser/my_awesome_modpack/HOWTO.md"<br>&nbsp;&nbsp;&nbsp;&nbsp;inclusionFilter = "" |
-| [scripts]<br>&nbsp;&nbsp;&nbsp;&nbsp;SPC_JAVA_SPC = "java" | Path to the Java Installation. On Linux systems use `which java` to find the location of your Java install. On Windows use `where java` and exclude the `.exe`-part. Note, that changing this value only affects the *unzipped* server pack's `variables.txt`. The one in the *zipped* server pack is unaffected by this setting and will always point to `java` to increase compatibility with users who download the *zipped* server pack.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
-| minecraftVersion                                           | The version of Minecraft for which to install the modloader server. The same version of Minecraft your modpack uses.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
-| modLoader                                                  | Which modloader to install. Must be either "Forge", "NeoForge", "Fabric", "Quilt" or "LegacyFabric". The same modloader your modpack uses.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
-| modLoaderVersion                                           | Specific Modloader version to install the server in the serverpack. The same version your modpack uses.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
-| includeServerIcon                                          | Whether to include server-icon.png in your serverpack. Must be `true` or `false`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
-| includeServerProperties                                    | Whether to include server.properties in your serverpack. Must be `true` or `false`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
-| includeZipCreation                                         | Whether to create a zip-file of your serverpack, saved in the directory you specified with `modpackDir`. Must be `true` or `false`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
-| javaArgs                                                   | JVM flags / Java Args to add to the generated start-scripts. Set to "empty" to not use any in your start-scripts.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
-| serverPackSuffix                                           | A suffix to append to the name of the server pack directory and server pack ZIP-archive. Illegal characters are / < > : " \ &#124; ? * # % & { } $ ! ' @ + ´ \` = and must not end with a SPACE<code>&#32;&#32;</code> or a DOT<code>&#32;.&#32;&#32;</code>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
-| serverIconPath                                             | Path to a custom server-icon.png-file to include in the server pack.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
-| serverPropertiesPath                                       | Path to a custom server.properties-file to include in the server pack.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
+| Variable                                                   | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
+|------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| modpackDir                                                 | The path to the directory/ZIP-archive where your modpack resides in.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
+| [[inclusions]]                                             | Each inclusion-specification ensures that the file or directory from `source` gets included in your server pack one way or another.<br>A basic configuration usually contains the `mods` and `config` directories. No need to prefix them with the path to your modpack.<br>ServerPackCreator will take care of that automatically by itself.<br>If you want to include files or folders *outside* of your modpack, then you will need to specify the whole path.<br>Below are some examples for including files from your modpack (`mods` and `config`) as well as files and folders from outside the modpack.<br>[[inclusions]]<br>&nbsp;&nbsp;&nbsp;&nbsp;destination = ""<br>&nbsp;&nbsp;&nbsp;&nbsp;exclusionFilter = "config/bluemap/.*"<br>&nbsp;&nbsp;&nbsp;&nbsp;source = "config"<br>&nbsp;&nbsp;&nbsp;&nbsp;inclusionFilter = ""<br>[[inclusions]]<br>&nbsp;&nbsp;&nbsp;&nbsp;destination = ""<br>&nbsp;&nbsp;&nbsp;&nbsp;exclusionFilter = ""<br>&nbsp;&nbsp;&nbsp;&nbsp;source = "mods"<br>&nbsp;&nbsp;&nbsp;&nbsp;inclusionFilter = ""<br>[[inclusions]]<br>&nbsp;&nbsp;&nbsp;&nbsp;destination = "SomeFiles"<br>&nbsp;&nbsp;&nbsp;&nbsp;exclusionFilter = ""<br>&nbsp;&nbsp;&nbsp;&nbsp;source = "C:\\Some\\Path\\With\\Files"<br>&nbsp;&nbsp;&nbsp;&nbsp;inclusionFilter = ""<br>[[inclusions]]<br>&nbsp;&nbsp;&nbsp;&nbsp;destination = "somefiles"<br>&nbsp;&nbsp;&nbsp;&nbsp;exclusionFilter = ""<br>&nbsp;&nbsp;&nbsp;&nbsp;source = "/home/myuser/some/files"<br>&nbsp;&nbsp;&nbsp;&nbsp;inclusionFilter = ""<br>[[inclusions]]<br>&nbsp;&nbsp;&nbsp;&nbsp;destination = "MyInstructions.md"<br>&nbsp;&nbsp;&nbsp;&nbsp;exclusionFilter = ""<br>&nbsp;&nbsp;&nbsp;&nbsp;source = "C:\\MyAwesomeModpack\\Stuff\\HowTo.md"<br>&nbsp;&nbsp;&nbsp;&nbsp;inclusionFilter = ""<br>[[inclusions]]<br>&nbsp;&nbsp;&nbsp;&nbsp;destination = "MyInstructions.md"<br>&nbsp;&nbsp;&nbsp;&nbsp;exclusionFilter = ""<br>&nbsp;&nbsp;&nbsp;&nbsp;source ="/home/myuser/my_awesome_modpack/HOWTO.md"<br>&nbsp;&nbsp;&nbsp;&nbsp;inclusionFilter = "" |
+| [scripts]<br>&nbsp;&nbsp;&nbsp;&nbsp;SPC_JAVA_SPC = "java" | Path to the Java Installation. On Linux systems use `which java` to find the location of your Java install. On Windows use `where java` and exclude the `.exe`-part. Note, that changing this value only affects the *unzipped* server pack's `variables.txt`. The one in the *zipped* server pack is unaffected by this setting and will always point to `java` to increase compatibility with users who download the *zipped* server pack.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
+| minecraftVersion                                           | The version of Minecraft for which to install the modloader server. The same version of Minecraft your modpack uses.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
+| modLoader                                                  | Which modloader to install. Must be either "Forge", "NeoForge", "Fabric", "Quilt" or "LegacyFabric". The same modloader your modpack uses.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
+| modLoaderVersion                                           | Specific Modloader version to install the server in the serverpack. The same version your modpack uses.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
+| includeServerIcon                                          | Whether to include server-icon.png in your serverpack. Must be `true` or `false`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
+| includeServerProperties                                    | Whether to include server.properties in your serverpack. Must be `true` or `false`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
+| includeZipCreation                                         | Whether to create a zip-file of your serverpack, saved in the directory you specified with `modpackDir`. Must be `true` or `false`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
+| javaArgs                                                   | JVM flags / Java Args to add to the generated start-scripts. Set to "empty" to not use any in your start-scripts.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          |
+| serverPackSuffix                                           | A suffix to append to the name of the server pack directory and server pack ZIP-archive. Illegal characters are / < > : " \ &#124; ? * # % & { } $ ! ' @ + ´ \` = and must not end with a SPACE<code>&#32;&#32;</code> or a DOT<code>&#32;.&#32;&#32;</code>                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               |
+| serverIconPath                                             | Path to a custom server-icon.png-file to include in the server pack.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
+| serverPropertiesPath                                       | Path to a custom server.properties-file to include in the server pack.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
 
 ### serverpackcreator.properties
 
diff --git a/serverpackcreator-api/src/main/kotlin/de/griefed/serverpackcreator/api/config/PackConfig.kt b/serverpackcreator-api/src/main/kotlin/de/griefed/serverpackcreator/api/config/PackConfig.kt
index 3628e274d15361595380b520022074de2f4e51d1..0a3a907377527e5ed8c37395e54c404347d3d0a5 100644
--- a/serverpackcreator-api/src/main/kotlin/de/griefed/serverpackcreator/api/config/PackConfig.kt
+++ b/serverpackcreator-api/src/main/kotlin/de/griefed/serverpackcreator/api/config/PackConfig.kt
@@ -33,6 +33,9 @@ import org.apache.logging.log4j.kotlin.cachedLoggerOf
 import java.io.File
 import java.io.FileNotFoundException
 import java.nio.charset.StandardCharsets
+import java.util.*
+import kotlin.collections.ArrayList
+import kotlin.collections.HashMap
 
 private const val modpackComment =
     "\n Path to your modpack. Can be either relative or absolute." +
@@ -230,6 +233,8 @@ open class PackConfig() {
     var modpackJson: JsonNode? = null
     var configVersion: String? = null
 
+    var customDestination: Optional<File> = Optional.empty()
+
     open var projectID: String? = null
     open var versionID: String? = null
     open var source: ModpackSource = ModpackSource.DIRECTORY
diff --git a/serverpackcreator-api/src/main/kotlin/de/griefed/serverpackcreator/api/serverpack/ServerPackHandler.kt b/serverpackcreator-api/src/main/kotlin/de/griefed/serverpackcreator/api/serverpack/ServerPackHandler.kt
index ed030c249c18254a7404352e8c256135ac1ccf98..fe4ceec29e9e75e6558d00d051518aefa0b27960 100644
--- a/serverpackcreator-api/src/main/kotlin/de/griefed/serverpackcreator/api/serverpack/ServerPackHandler.kt
+++ b/serverpackcreator-api/src/main/kotlin/de/griefed/serverpackcreator/api/serverpack/ServerPackHandler.kt
@@ -287,7 +287,11 @@ class ServerPackHandler(
         val relativeFiles : ArrayList<String> = ArrayList(10000)
         val serverPackManifest: ServerPackManifest
         var serverPackZip: Optional<File> = Optional.empty()
-        val serverPack = File(getServerPackDestination(packConfig))
+        val serverPack = if (packConfig.customDestination.isPresent) {
+            packConfig.customDestination.get()
+        } else {
+            File(getServerPackDestination(packConfig))
+        }
         val existingManifest: File
         val oldManifest: ServerPackManifest
         var oldFile: File
@@ -297,11 +301,6 @@ class ServerPackHandler(
         * Check whether the server pack for the specified modpack already exists and whether overwrite is disabled.
         * If the server pack exists and overwrite is disabled, no new server pack will be generated.
         */
-        try {
-            serverPack.create(createFileOrDir = true, asDirectory = true)
-        } catch (ignored: IOException) {
-        }
-
         if (apiProperties.isServerPacksOverwriteEnabled) {
             // Make sure no files from previously generated server packs interrupt us.
             cleanupEnvironment(true, serverPack.absolutePath)
@@ -310,6 +309,11 @@ class ServerPackHandler(
             deleteExistingServerPackZip(serverPack.absolutePath)
         }
 
+        try {
+            serverPack.create(createFileOrDir = true, asDirectory = true)
+        } catch (ignored: IOException) {
+        }
+
         if (apiProperties.isUpdatingServerPacksEnabled) {
             existingManifest = File(serverPack.absolutePath, "manifest.json")
             if (existingManifest.isFile) {
@@ -333,21 +337,27 @@ class ServerPackHandler(
         // Recursively copy all specified directories and files, excluding clientside-only mods, to server pack.
         files.addAll(
             copyFiles(
-                packConfig,
+                packConfig.modpackDir,
+                packConfig.inclusions,
+                packConfig.clientMods,
+                packConfig.modsWhitelist,
+                packConfig.minecraftVersion,
+                serverPack.absolutePath,
+                packConfig.modloader,
                 !(!apiProperties.isServerPacksOverwriteEnabled && !apiProperties.isUpdatingServerPacksEnabled)
             )
         )
 
         // If true, copy the server-icon.png from server_files to the server pack.
         if (packConfig.isServerIconInclusionDesired) {
-            copyIcon(packConfig)
+            copyIcon(serverPack.absolutePath, packConfig.serverIconPath)
         } else {
             log.info("Not including servericon.")
         }
 
         // If true, copy the server.properties from server_files to the server pack.
         if (packConfig.isServerPropertiesInclusionDesired) {
-            copyProperties(packConfig)
+            copyProperties(serverPack.absolutePath, packConfig.serverPropertiesPath)
         } else {
             log.info("Not including server.properties.")
         }
@@ -375,8 +385,13 @@ class ServerPackHandler(
             * is present. This is because a ZIP-archive, if one is created, is supposed to be uploaded
             * to platforms like CurseForge. We must not have scripts with custom Java paths there.
             */
-            createServerRunFiles(packConfig, false)
-            serverPackZip = zipBuilder(packConfig)
+            createServerRunFiles(packConfig.scriptSettings, serverPack.absolutePath, false)
+            serverPackZip = zipBuilder(
+                packConfig.minecraftVersion,
+                serverPack.absolutePath,
+                packConfig.modloader,
+                packConfig.modloaderVersion
+            )
         } else {
             log.info("Not creating zip archive of serverpack.")
         }
@@ -386,7 +401,7 @@ class ServerPackHandler(
         * The difference to the previous call is that these scripts respect the SPC_JAVA_SPC
         * placeholder setting, if the user has set one
         */
-        createServerRunFiles(packConfig, true)
+        createServerRunFiles(packConfig.scriptSettings, serverPack.absolutePath, true)
 
         // Inform user about location of newly generated server pack.
         log.info("Server pack available at: ${serverPack.absolutePath}")
@@ -413,19 +428,6 @@ class ServerPackHandler(
         )
     }
 
-    /**
-     * Deletes all files, directories and ZIP-archives of previously generated server packs to ensure
-     * newly generated server pack is as clean as possible. This will completely empty the server pack
-     * directory, so use with caution!
-     *
-     * @param deleteZip          Whether to delete the server pack ZIP-archive.
-     * @param packConfig ConfigurationModel containing the modpack directory from which the
-     * destination of the server pack is acquired.
-     * @author Griefed
-     */
-    fun cleanupEnvironment(deleteZip: Boolean, packConfig: PackConfig) =
-        cleanupEnvironment(deleteZip, getServerPackDestination(packConfig))
-
     /**
      * Deletes all files, directories and ZIP-archives of previously generated server packs to ensure
      * newly generated server pack is as clean as possible. This will completely empty the server pack
@@ -452,32 +454,6 @@ class ServerPackHandler(
         File(destination + "_server_pack.zip").deleteQuietly()
     }
 
-    /**
-     * Copies all specified directories and mods, excluding clientside-only mods, from the modpack
-     * directory into the server pack directory. If a `source/file;destination/file`
-     * -combination is provided, the specified source-file is copied to the specified
-     * destination-file. One of the reasons as to why it is recommended to run a given
-     * ConfigurationModel through the ConfigurationHandler first, is because the ConfigurationHandler
-     * will resolve links to their files first before then correcting the given
-     * ConfigurationModel.
-     *
-     * @param packConfig ConfigurationModel containing the modpack directory, list of
-     * directories and files to copy, list of clientside-only mods to
-     * exclude, the Minecraft version used by the modpack and server pack,
-     * and the modloader used by the modpack and server pack.
-     * @author Griefed
-     */
-    fun copyFiles(packConfig: PackConfig, overwrite: Boolean = true) = copyFiles(
-        packConfig.modpackDir,
-        packConfig.inclusions,
-        packConfig.clientMods,
-        packConfig.modsWhitelist,
-        packConfig.minecraftVersion,
-        getServerPackDestination(packConfig),
-        packConfig.modloader,
-        overwrite
-    )
-
     /**
      * Copies all specified directories and mods, excluding clientside-only mods, from the modpack
      * directory into the server pack directory. If a `source/file;destination/file`
@@ -562,72 +538,6 @@ class ServerPackHandler(
         return copiedFiles
     }
 
-    /**
-     * Download and provide the improved Fabric Server Launcher, if it is available for the given
-     * Minecraft and Fabric version.
-     *
-     * @param packConfig ConfigurationModel containing the Minecraft and Fabric version for
-     * which to acquire the improved Fabric Server Launcher.
-     * @author Griefed
-     */
-    fun provideImprovedFabricServerLauncher(packConfig: PackConfig) = getImprovedFabricLauncher(
-        packConfig.minecraftVersion, packConfig.modloaderVersion, getServerPackDestination(packConfig)
-    )
-
-    /**
-     * Copies the server-icon.png into server pack. The sever-icon is automatically scaled to a
-     * resolution of 64x64 pixels.
-     *
-     * @param packConfig Containing the modpack directory to acquire the destination of the
-     * server pack and the path to the server icon to copy.
-     * @author Griefed
-     */
-    fun copyIcon(packConfig: PackConfig) = copyIcon(getServerPackDestination(packConfig), packConfig.serverIconPath)
-
-    /**
-     * Copies the server.properties into server pack.
-     *
-     * @param packConfig Containing the modpack directory to acquire the destination of the
-     * server pack and the path to the server properties to copy.
-     * @author Griefed
-     */
-    fun copyProperties(packConfig: PackConfig) =
-        copyProperties(getServerPackDestination(packConfig), packConfig.serverPropertiesPath)
-
-    /**
-     * Create start-scripts for the generated server pack using the templates the user has defined for
-     * their instance of ServerPackCreator.
-     *
-     * @param packConfig Configuration model containing modpack specific values. keys to be
-     * replaced with their respective values in the start scripts, as well
-     * as the modpack directory from which the destination of the server
-     * pack is acquired.
-     * @param isLocal            Whether the start scripts should be created for a locally usable
-     * server pack. Use `false` if the start scripts should be created
-     * for a server pack about to be zipped.
-     * @author Griefed
-     */
-    fun createServerRunFiles(packConfig: PackConfig, isLocal: Boolean) =
-        createServerRunFiles(packConfig.scriptSettings, getServerPackDestination(packConfig), isLocal)
-
-    /**
-     * Creates a ZIP-archive of the server pack previously generated. Depending on the property
-     * `de.griefed.serverpackcreator.serverpack.zip.exclude.enabled`, files will be excluded. To customize
-     * the files which will be excluded, see the property `de.griefed.serverpackcreator.serverpack.zip.exclude`
-     *
-     * @param packConfig Contains the Minecraft version used by the modpack and server pack,
-     * whether the modloader server was installed, the modpack directory to
-     * acquire the destination of the server pack, the modloader used by the
-     * modpack and server pack and the modloader version.
-     * @author Griefed
-     */
-    fun zipBuilder(packConfig: PackConfig) = zipBuilder(
-        packConfig.minecraftVersion,
-        getServerPackDestination(packConfig),
-        packConfig.modloader,
-        packConfig.modloaderVersion
-    )
-
     fun getServerFiles(
         inclusion: InclusionSpecification,
         modpackDir: String,
@@ -1393,17 +1303,6 @@ class ServerPackHandler(
         }
     }
 
-    /**
-     * Cleans up the server_pack directory by deleting left-over files from modloader installations
-     * and version checking.
-     *
-     * @param packConfig Containing the Minecraft version used by the modpack and server pack,
-     * the modloader version used by the modpack and server pack and the
-     * modpack directory to acquire the destination of the server pack.
-     * @author Griefed
-     */
-    fun cleanUpServerPack(packConfig: PackConfig) = postInstallCleanup(getServerPackDestination(packConfig))
-
     /**
      * Go through the mods in the modpack and exclude any of the user-specified clientside-only mods
      * according to the filter method set in the serverpackcreator.properties. For available filters,
diff --git a/serverpackcreator-api/src/main/kotlin/de/griefed/serverpackcreator/api/utilities/common/SystemUtilities.kt b/serverpackcreator-api/src/main/kotlin/de/griefed/serverpackcreator/api/utilities/common/SystemUtilities.kt
index 5d95bd042d2eb7aa4a2347fe7470a6f716594495..75569f4ac387f5516f5d993aaf3d80bda6e4df9d 100644
--- a/serverpackcreator-api/src/main/kotlin/de/griefed/serverpackcreator/api/utilities/common/SystemUtilities.kt
+++ b/serverpackcreator-api/src/main/kotlin/de/griefed/serverpackcreator/api/utilities/common/SystemUtilities.kt
@@ -21,6 +21,7 @@ package de.griefed.serverpackcreator.api.utilities.common
 
 import org.apache.logging.log4j.kotlin.cachedLoggerOf
 import java.io.File
+import java.util.*
 
 /**
  * Utility-class revolving around the system we are running on.
@@ -36,6 +37,12 @@ class SystemUtilities {
         private val javaPathSuffix = "%s${File.separator}bin${File.separator}java"
         private val javaHome = System.getProperty("java.home")
 
+        private val OS: String = System.getProperty("os.name").lowercase(Locale.getDefault())
+        val IS_WINDOWS: Boolean = (OS.indexOf("win") >= 0)
+        val IS_MAC: Boolean = (OS.indexOf("mac") >= 0)
+        val IS_UNIX: Boolean = (OS.indexOf("nix") >= 0 || OS.indexOf("nux") >= 0 || OS.indexOf("aix") > 0)
+        val IS_SOLARIS: Boolean = (OS.indexOf("sunos") >= 0)
+
         /**
          * Automatically acquire the path to the systems default Java installation.
          *
diff --git a/serverpackcreator-api/src/main/resources/de/griefed/resources/cli_help.txt b/serverpackcreator-api/src/main/resources/de/griefed/resources/cli_help.txt
index ce7afabf92334fb2e42e0ed2186f9739f8cff85c..3255b9942c52baa305d85a275f17e3419f84b8e8 100644
--- a/serverpackcreator-api/src/main/resources/de/griefed/resources/cli_help.txt
+++ b/serverpackcreator-api/src/main/resources/de/griefed/resources/cli_help.txt
@@ -7,54 +7,56 @@ How to use ServerPackCreator:
    mode will automatically be used.
 
  Extra arguments to use ServerPackCreator with: #
-      -lang: Allows you to use one of the available languages for ServerPackCreator. I can not
-             guarantee that each of the following available languages is 100% translated.
-             You best choice is en_us, or not specifying any as that is the default, because
-             I write ServerPackCreator with english in mind. Available usages:
-             -lang en_us
-             -lang uk_ua
-             -lang de_de
-
-      -cgen: Only available for the commandline interface. This will start the generation of
-             a new configuration file. You will be asked to enter information about your modpack
-             step-by-step. Each setting you enter will be checked for errors before it is saved.
-             If everything you enter is valid and without errors, it will be written to a new
-             serverpackcreator.conf and ServerPackCreator will immediately start a run with said
-             configuration file, generating a server pack for you.
-
-    -update: Check whether a new version of ServerPackCreator is available for download.
-             If an update is available, the version and link to the release of said update are
-             written to the console so you can from work with it from there.
-             Note: Automatic updates are currently not planned nor supported, and neither are
-             downloads of any available updates to your system. You need to update manually.
-
-       -cli: Run ServerPackCreator in Command-line interface mode. Checks the serverpackcreator.conf
-             for errors and if none are found, starts the generation of a server pack with the configuration
-             provided by your serverpackcreator.conf.
-
-       -web: Run ServerPackCreator as a webservice available at http://localhost:8080. The webservice
-             provides the same functionality as running ServerPackCreator in GUI mode (so no Commandline
-             arguments and a non-headless environment) as well as a REST API which can be used in different ways.
-             For more information about the REST API, please see the Java documentation:
-              - GitHub Pages: https://griefed.github.io/ServerPackCreator/
-              - GitLab Pages: https://griefed.pages.griefed.de/ServerPackCreator/
-
-       -gui: Run ServerPackCreator using the graphical user interface. If your environment supports
-             graphics, i.e. is not headless, then this is the default mode in which ServerPackCreator
-             started as when no arguments are used.
-
-    --setup: Set up and prepare the environment for subsequent runs of ServerPackCreator.
-             This will create/copy all files needed for ServerPackCreator to function
-             properly from inside its JAR-file and setup everything else, too. You can pass a properties-file, too
-             if you so desire.
-             Examples:
-             --setup "/path/to/custom.properties"
-             --setup "C:\path\to\custom.properties"
-
-    --home:  Override the home-directory setting for your user.
-             Examples:
-             --home "/path/to/directory"
-             --home "C:\users\<YOUR_USER>\SPC"
+          -lang: Allows you to use one of the available languages for ServerPackCreator. I can not
+                 guarantee that each of the following available languages is 100% translated.
+                 You best choice is en_us, or not specifying any as that is the default, because
+                 I write ServerPackCreator with english in mind. Available usages:
+                 -lang en_us
+                 -lang uk_ua
+                 -lang de_de
+
+          -cgen: Only available for the commandline interface. This will start the generation of
+                 a new configuration file. You will be asked to enter information about your modpack
+                 step-by-step. Each setting you enter will be checked for errors before it is saved.
+                 If everything you enter is valid and without errors, it will be written to a new
+                 serverpackcreator.conf and ServerPackCreator will immediately start a run with said
+                 configuration file, generating a server pack for you.
+
+        -update: Check whether a new version of ServerPackCreator is available for download.
+                 If an update is available, the version and link to the release of said update are
+                 written to the console so you can from work with it from there.
+                 Note: Automatic updates are currently not planned nor supported, and neither are
+                 downloads of any available updates to your system. You need to update manually.
+
+           -cli: Run ServerPackCreator in an interactive commandline-mode.
+
+        -config: Generate a server pack from a specific server pack configuration from the commandline.
+  --destination: Only effective in combination with -config. Sets the destination in which the server pack
+                 will be generated in.
+
+           -web: Run ServerPackCreator as a webservice available at http://localhost:8080. The webservice
+                 provides the same functionality as running ServerPackCreator in GUI mode (so no Commandline
+                 arguments and a non-headless environment) as well as a REST API which can be used in different ways.
+                 For more information about the REST API, please see the Java documentation:
+                  - GitHub Pages: https://griefed.github.io/ServerPackCreator/
+                  - GitLab Pages: https://griefed.pages.griefed.de/ServerPackCreator/
+
+           -gui: Run ServerPackCreator using the graphical user interface. If your environment supports
+                 graphics, i.e. is not headless, then this is the default mode in which ServerPackCreator
+                 started as when no arguments are used.
+
+        --setup: Set up and prepare the environment for subsequent runs of ServerPackCreator.
+                 This will create/copy all files needed for ServerPackCreator to function
+                 properly from inside its JAR-file and setup everything else, too. You can pass a properties-file, too
+                 if you so desire.
+                 Examples:
+                 --setup "/path/to/custom.properties"
+                 --setup "C:\path\to\custom.properties"
+
+        --home:  Override the home-directory setting for your user.
+                 Examples:
+                 --home "/path/to/directory"
+                 --home "C:\users\<YOUR_USER>\SPC"
 
  Support:
 
diff --git a/serverpackcreator-api/src/test/resources/serverpackcreator.properties b/serverpackcreator-api/src/test/resources/serverpackcreator.properties
index 7080d8135fad5cffed96a3bba005fb05e4d7d2e8..74c75d83153fb3d3a3b734f04760085dd99c151a 100644
--- a/serverpackcreator-api/src/test/resources/serverpackcreator.properties
+++ b/serverpackcreator-api/src/test/resources/serverpackcreator.properties
@@ -1,15 +1,16 @@
 #For details about each property, see https://help.serverpackcreator.de/settings-and-configs.html
-#Wed Aug 28 11:18:42 CEST 2024
+#Sat Sep 21 15:40:30 CEST 2024
 de.griefed.serverpackcreator.configuration.aikar=-Xms4G -Xmx4G -XX\:+UseG1GC -XX\:+ParallelRefProcEnabled -XX\:MaxGCPauseMillis\=200 -XX\:+UnlockExperimentalVMOptions -XX\:+DisableExplicitGC -XX\:+AlwaysPreTouch -XX\:G1NewSizePercent\=30 -XX\:G1MaxNewSizePercent\=40 -XX\:G1HeapRegionSize\=8M -XX\:G1ReservePercent\=20 -XX\:G1HeapWastePercent\=5 -XX\:G1MixedGCCountTarget\=4 -XX\:InitiatingHeapOccupancyPercent\=15 -XX\:G1MixedGCLiveThresholdPercent\=90 -XX\:G1RSetUpdatingPauseTimePercent\=5 -XX\:SurvivorRatio\=32 -XX\:+PerfDisableSharedMem -XX\:MaxTenuringThreshold\=1 -Dusing.aikars.flags\=https\://mcflags.emc.gs -Daikars.new.flags\=true
 de.griefed.serverpackcreator.configuration.directories.mustinclude=mods,config,kubejs,defaultconfigs,scripts
 de.griefed.serverpackcreator.configuration.directories.shouldexclude=overrides,packmenu,resourcepacks,server_pack,fancymenu,libraries,downloads,logs,profileImage,resourcepacks,screenshots,shaderpacks,tv-cache,asm
 de.griefed.serverpackcreator.configuration.fallback.updateurl=https\://raw.githubusercontent.com/Griefed/ServerPackCreator/main/serverpackcreator-api/src/main/resources/serverpackcreator.properties
-de.griefed.serverpackcreator.configuration.fallbackmodslist=Ping-Wheel-
+de.griefed.serverpackcreator.configuration.fallbackmodslist=3dskinlayers-,Absolutely-Not-A-Zoom-Mod-,AdvancedChat-,AdvancedChatCore-,AdvancedChatHUD-,AdvancedCompas-,Ambience,AmbientEnvironment-,AmbientSounds_,AnimaticaReforged-,AreYouBlind-,Armor Status HUD-,ArmorSoundTweak-,BH-Menu-,Batty's Coordinates PLUS Mod,BetterAdvancements-,BetterAnimationsCollection-,BetterDarkMode-,BetterF3-,BetterFog-,BetterFoliage-,BetterModsButton-,BetterPingDisplay-,BetterPlacement-,BetterTaskbar-,BetterThirdPerson,BetterTitleScreen-,Blur-,BorderlessWindow-,CTM-,Chat Ping ,ChunkAnimator-,Clear-Water-,ClientTweaks_,CompletionistsIndex-,Controller Support-,Controlling-,CraftPresence-,CullLessLeaves-,CustomCursorMod-,CustomMainMenu-,DefaultOptions_,DefaultSettings-,DeleteWorldsToTrash-,DetailArmorBar-,Ding-,DistantHorizons-,DripSounds-,Durability101-,DurabilityNotifier-,DynamicSurroundings-,DynamicSurroundingsHuds-,EasyLAN-,EffectsLeft-,EiraMoticons_,EnchantmentDescriptions-,EnhancedVisuals_,EquipmentCompare-,EuphoriaPatcher-,FPS-Monitor-,FabricCustomCursorMod-,FadingNightVision-,Fallingleaves-,FancySpawnEggs,FancyVideo-API-,FirstPersonMod,FogTweaker-,ForgeCustomCursorMod-,FpsReducer-,FpsReducer2-,FullscreenWindowed-,GameMenuModOption-,HealthOverlay-,HeldItemTooltips-,HorseStatsMod-,ImmediatelyFast-,ImmediatelyFastReforged-,InventoryEssentials_,InventoryHud_,InventorySpam-,InventoryTweaks-,ItemBorders-,ItemLocks-,ItemPhysicLite_,ItemStitchingFix-,JBRA-Client-,JustEnoughCalculation-,JustEnoughEffects-,JustEnoughProfessions-,LLOverlayReloaded-,LOTRDRP-,LeaveMyBarsAlone-,LegendaryTooltips,LegendaryTooltips-,LightOverlay-,MineMenu-,MinecraftCapes,MinecraftCapes ,MoBends,ModernUI-,MouseTweaks-,MyServerIsCompatible-,Neat,Neat ,Neat-,NekosEnchantedBooks-,NoAutoJump-,NoFog-,Notes-,NotifMod-,OldJavaWarning-,OptiFine,OptiFine_,OptiForge,OptiForge-,OverflowingBars-,PackMenu-,PackModeMenu-,PickUpNotifier-,Ping-,PingHUD-,PresenceFootsteps-,RPG-HUD-,ReAuth-,Reforgium-,ResourceLoader-,ResourcePackOrganizer,RyoamicLights-,Ryoamiclights-,ShoulderSurfing-,ShulkerTooltip-,SimpleDiscordRichPresence-,SimpleWorldTimer-,SoundFilters-,SpawnerFix-,StylishEffects-,TRansliterationLib-,TextruesRubidiumOptions-,TipTheScales-,Tips-,Toast Control-,Toast-Control-,ToastControl-,TravelersTitles-,VR-Combat_,VoidFog-,WindowedFullscreen-,WorldNameRandomizer-,YeetusExperimentus-,YungsMenuTweaks-,[1.12.2]DamageIndicatorsMod-,[1.12.2]bspkrscore-,antighost-,anviltooltipmod-,armorchroma-,armorpointspp-,auditory-,authme-,auto-reconnect-,autojoin-,autoreconnect-,axolotl-item-fix-,backtools-,bannerunlimited-,beenfo-,better-clouds-,better-recipe-book-,betterbiomeblend-,bhmenu-,blur-,borderless-mining-,cat_jam-,catalogue-,cfwinfo-,charmonium-,chat_heads-,cherishedworlds-,cirback-1.0-,classicbar-,clickadv-,clienttweaks-,combat_music-,connectedness-,controllable-,cullleaves-,cullparticles-,custom-crosshair-mod-,customdiscordrpc-,darkness-,dashloader-,defaultoptions-,desiredservers-,discordrpc-,drippyloadingscreen-,drippyloadingscreen_,durabilitytooltip-,dynamic-fps-,dynamic-music-,dynamiclights-,dynamiclightsreforged-,dynmus-,e4mc-,effective-,eggtab-,eguilib-,eiramoticons-,embeddium-,enchantment-lore-,entity-texture-features-,entityculling-,essential_,exhaustedstamina-,extremesoundmuffler-,fabricemotes-,fabricmod_VoxelMap-,fancymenu_,fancymenu_video_extension,farsight-,fast-ip-ping-,firstperson-,flickerfix-,fm_audio_extension_,forgemod_VoxelMap-,freecam-,freelook-,galacticraft-rpc-,gamestagesviewer-,gpumemleakfix-,grid-,helium-,hiddenrecipebook-,hiddenrecipebook_,infinitemusic-,inventoryhud.,inventoryprofiles,invtweaks-,itemzoom,itlt-,jeed-,jehc-,jeiintegration_,jumpoverfences-,just-enough-harvestcraft-,justenoughbeacons-,justenoughdrags-,justzoom_,keymap-,keywizard-,lazurite-,lazydfu-,lib39-,light-overlay-,lightfallclient-,lightspeed-,loadmyresources_,lock_minecart_view-,lootbeams-,lwl-,macos-input-fixes-,magnesium_extras-,maptooltip-,massunbind,mcbindtype-,mcwifipnp-,medievalmusic-,memoryusagescreen-,mightyarchitect-,mindful-eating-,minetogether-,mobplusplus-,modcredits-,modernworldcreation_,modnametooltip-,modnametooltip_,moreoverlays-,mousewheelie-,movement-vision-,multihotbar-,music-duration-reducer-,musicdr-,neiRecipeHandlers-,ngrok-lan-expose-mod-,no_nv_flash-,nopotionshift_,notenoughanimations-,oculus-,ornaments-,overloadedarmorbar-,panorama-,paperdoll-,phosphor-,physics-mod-,preciseblockplacing-,radon-,realm-of-lost-souls-,rebind-narrator-,rebind_narrator-,rebindnarrator-,rebrand-,reforgium-,replanter-,rrls-,rubidium-,rubidium_extras-,screenshot-to-clipboard-,servercountryflags-,shutupexperimentalsettings-,shutupmodelloader-,signtools-,simple-rpc-,simpleautorun-,skinlayers3d-forge,smartcursor-,smarthud-,smoothboot-,smoothfocus-,sodium-fabric-,sodiumdynamiclights-,sounddeviceoptions-,soundreloader-,spoticraft-,tconplanner-,textrues_embeddium_options-,timestamps-,tooltipscroller-,torchoptimizer-,torohealth-,totaldarkness,toughnessbar-,watermedia-,whats-that-slot-forge-,wisla-,xenon-,xlifeheartcolors-,yisthereautojump-
 de.griefed.serverpackcreator.configuration.hastebinserver=https\://haste.zneix.eu/documents
 de.griefed.serverpackcreator.configuration.modswhitelist=Ping-Wheel-
 de.griefed.serverpackcreator.firstrun=false
 de.griefed.serverpackcreator.java=C\:\\Program Files\\Eclipse Adoptium\\jdk-21.0.3.9-hotspot\\bin\\java.exe
 de.griefed.serverpackcreator.language=en_GB
+de.griefed.serverpackcreator.loglevel=DEBUG
 de.griefed.serverpackcreator.minecraft.snapshots=true
 de.griefed.serverpackcreator.script.java.autoupdate=true
 de.griefed.serverpackcreator.serverpack.autodiscovery.enabled=false
@@ -23,5 +24,5 @@ de.griefed.serverpackcreator.spring.schedules.database.cleanup=0 0 0 * * *
 de.griefed.serverpackcreator.spring.schedules.files.cleanup=0 30 0 * * *
 de.griefed.serverpackcreator.spring.schedules.versions.refresh=0 0 0 * * *
 de.griefed.serverpackcreator.versioncheck.prerelease=false
-server.tomcat.basedir=F\:\\GitLab\\ServerPackCreator\\serverpackcreator-api\\tests
+server.tomcat.basedir=F\:\\GitLab\\ServerPackCreator\\serverpackcreator-app\\tests
 spring.datasource.url=jdbc\:postgresql\://localhost\:5432/serverpackcreator
diff --git a/serverpackcreator-app/build.gradle.kts b/serverpackcreator-app/build.gradle.kts
index f176c25b7b63b5309e5b7ff7c51f49b040b8fbfd..70b6deb27365e8f470ea3d68706b508b653119b0 100644
--- a/serverpackcreator-app/build.gradle.kts
+++ b/serverpackcreator-app/build.gradle.kts
@@ -32,6 +32,9 @@ configurations {
 dependencies {
     api(project(":serverpackcreator-api"))
 
+    //CLI
+    api("info.picocli:picocli-shell-jline3:4.7.6")
+
     //GUI
     api("commons-io:commons-io:2.16.1")
     api("org.jetbrains.kotlinx:kotlinx-coroutines-swing:1.8.0")
diff --git a/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/CommandlineParser.kt b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/CommandlineParser.kt
index 6ec5f2ccfe1068e233e33016ce691a897de506b4..08891be19aef5468035f2dde12a10347006c673f 100644
--- a/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/CommandlineParser.kt
+++ b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/CommandlineParser.kt
@@ -53,6 +53,8 @@ open class CommandlineParser(args: Array<String>, appInfo: JarInformation) {
     } else {
         File(appInfo.jarFolder, "serverpackcreator.properties")
     }
+    var serverPackConfig : Optional<File> = Optional.empty()
+    var serverPackDestination : Optional<File> = Optional.empty()
 
     var homeDir: Optional<File> = Optional.empty()
 
@@ -113,6 +115,28 @@ open class CommandlineParser(args: Array<String>, appInfo: JarInformation) {
                 return@run
             }
 
+            /*
+            * Check whether the user wants to generate a specific server pack config from the commandline.
+            */
+            if (argsList.any { entry -> entry.contains(Mode.CONFIG.argument()) }) {
+                val confPos = argsList.indexOf(Mode.CONFIG.argument()) + 1
+                val confArg = argsList[confPos]
+                val confFile = File(confArg)
+                if (argsList.size > 1 && confFile.isFile) {
+                    serverPackConfig = Optional.of(confFile)
+                }
+
+                if (argsList.any { entry -> entry.contains(Mode.DESTINATION.argument()) }) {
+                    val destPos = argsList.indexOf(Mode.DESTINATION.argument()) + 1
+                    val destArg = argsList[destPos]
+                    val destFile = File(destArg)
+                    serverPackDestination = Optional.of(destFile)
+                }
+
+                mode = Mode.CONFIG
+                return@run
+            }
+
             /*
             * Check whether the user wants to run in commandline-mode or whether a GUI would not be supported.
             */
diff --git a/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/Mode.kt b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/Mode.kt
index 88fb71f7849d3986ab4a632eccc5e7ea9e443179..a1818009e874259827182d53897687849b626e3a 100644
--- a/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/Mode.kt
+++ b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/Mode.kt
@@ -54,6 +54,23 @@ enum class Mode(private val argument: String) {
      * **Priority 3**
      *
      *
+     * Run ServerPackCreator from the commandline and generate a server pack from a specific server pack config.
+     */
+    CONFIG("-config"),
+
+    /**
+     * **Priority 3.1**
+     *
+     *
+     * Generate the server pack from the config specified in [CONFIG] in a specific location.
+     * This argument requires [CONFIG] being present, too.
+     */
+    DESTINATION("--destination"),
+
+    /**
+     * **Priority 4**
+     *
+     *
      * Run ServerPackCreator in commandline-mode. If no graphical environment is supported, this is
      * the default ServerPackCreator will enter, even when starting ServerPackCreator with no extra
      * arguments at all.
@@ -61,7 +78,7 @@ enum class Mode(private val argument: String) {
     CLI("-cli"),
 
     /**
-     * **Priority 4**
+     * **Priority 5**
      *
      *
      * Run ServerPackCreator as a webservice.
@@ -69,7 +86,7 @@ enum class Mode(private val argument: String) {
     WEB("-web"),
 
     /**
-     * **Priority 5**
+     * **Priority 6**
      *
      *
      * Run ServerPackCreator with our GUI. If a graphical environment is supported, this is the
@@ -79,7 +96,7 @@ enum class Mode(private val argument: String) {
     GUI("-gui"),
 
     /**
-     * **Priority 6**
+     * **Priority 7**
      *
      *
      * Set up and prepare the environment for subsequent runs of ServerPackCreator. This will
@@ -102,7 +119,7 @@ enum class Mode(private val argument: String) {
     HOME("--home"),
 
     /**
-     * **Priority 7**
+     * **Priority 8**
      *
      *
      * Exit ServerPackCreator.
diff --git a/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/ServerPackCreator.kt b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/ServerPackCreator.kt
index 9139fbf2ffd4e8d6e088d2c53d4e9e9de2670c39..6bf977fd4816742f99654621ea1bc3be1d11d179 100644
--- a/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/ServerPackCreator.kt
+++ b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/ServerPackCreator.kt
@@ -25,11 +25,9 @@ import com.formdev.flatlaf.fonts.jetbrains_mono.FlatJetBrainsMonoFont
 import com.formdev.flatlaf.intellijthemes.materialthemeuilite.FlatMaterialDarkerIJTheme
 import de.comahe.i18n4k.Locale
 import de.griefed.serverpackcreator.api.ApiWrapper
-import de.griefed.serverpackcreator.api.config.PackConfig
 import de.griefed.serverpackcreator.api.utilities.common.JarInformation
 import de.griefed.serverpackcreator.api.utilities.common.JarUtilities
-import de.griefed.serverpackcreator.api.utilities.common.readText
-import de.griefed.serverpackcreator.app.cli.ConfigurationEditor
+import de.griefed.serverpackcreator.app.cli.InteractiveCommandLine
 import de.griefed.serverpackcreator.app.gui.MainWindow
 import de.griefed.serverpackcreator.app.gui.splash.SplashScreen
 import de.griefed.serverpackcreator.app.updater.MigrationManager
@@ -49,8 +47,6 @@ import java.util.prefs.Preferences
 import javax.swing.JFileChooser
 import javax.swing.JOptionPane
 import javax.xml.parsers.ParserConfigurationException
-import kotlin.system.exitProcess
-
 
 /**
  * Entry point for the app. Creates a new instance of [ServerPackCreator] and executes [ServerPackCreator.run] with the
@@ -126,43 +122,33 @@ class ServerPackCreator(private val args: Array<String>) {
     }
 
     @get:Synchronized
-    val configurationEditor: ConfigurationEditor by lazy {
-        ConfigurationEditor(
-            apiWrapper.configurationHandler,
-            apiWrapper.apiProperties,
-            apiWrapper.utilities,
-            apiWrapper.versionMeta
-        )
+    val interactiveCommandLine: InteractiveCommandLine by lazy {
+        InteractiveCommandLine(apiWrapper, updateChecker)
     }
 
     fun run(mode: Mode = Mode.GUI) {
         log.info("Running with args: ${args.joinToString(" ")}")
-        log.info("Running in mode: $mode")
+        log.info("Running in mode:   $mode")
         log.info("App information:")
-        log.info("App Folder:      ${appInfo.jarFolder}")
-        log.info("App File:        ${appInfo.jarFile}")
-        log.info("App Path:        ${appInfo.jarPath}")
-        log.info("App Name:        ${appInfo.jarFileName}")
-        log.info("Java version:    ${apiWrapper.apiProperties.getJavaVersion()}")
-        log.info("OS architecture: ${apiWrapper.apiProperties.getOSArch()}")
-        log.info("OS name:         ${apiWrapper.apiProperties.getOSName()}")
-        log.info("OS version:      ${apiWrapper.apiProperties.getOSVersion()}")
+        log.info("App Folder:        ${appInfo.jarFolder}")
+        log.info("Appy File:         ${appInfo.jarFile}")
+        log.info("App Path:          ${appInfo.jarPath}")
+        log.info("App Name:          ${appInfo.jarFileName}")
+        log.info("Java version:      ${apiWrapper.apiProperties.getJavaVersion()}")
+        log.info("OS architecture:   ${apiWrapper.apiProperties.getOSArch()}")
+        log.info("OS name:           ${apiWrapper.apiProperties.getOSName()}")
+        log.info("OS version:        ${apiWrapper.apiProperties.getOSVersion()}")
 
         when (mode) {
             Mode.HELP -> {
-                System.setProperty("java.awt.headless", "true")
-                printHelp()
-                continuedRunOptions()
+                interactiveCommandLine.helpCommand.run()
             }
 
             Mode.UPDATE -> {
-                System.setProperty("java.awt.headless", "true")
-                updateChecker.updateCheck(true)
-                continuedRunOptions()
+                interactiveCommandLine.updateCommand.run()
             }
 
             Mode.WEB -> {
-                System.setProperty("java.awt.headless", "true")
                 apiWrapper.stageOne()
                 migrationManager.migrate()
                 apiWrapper.stageTwo()
@@ -172,22 +158,29 @@ class ServerPackCreator(private val args: Array<String>) {
             }
 
             Mode.CGEN -> {
-                System.setProperty("java.awt.headless", "true")
                 apiWrapper.stageOne()
                 migrationManager.migrate()
                 apiWrapper.stageTwo()
-                createDefaultConfig()
-                runConfigurationEditor()
-                continuedRunOptions()
+                interactiveCommandLine.configGenCommand.run()
+            }
+
+            Mode.CONFIG -> {
+                apiWrapper.stageOne()
+                migrationManager.migrate()
+                apiWrapper.stageTwo()
+                apiWrapper.stageThree()
+                interactiveCommandLine.runHeadlessCommand.runHeadless(
+                    commandlineParser.serverPackConfig.get(),
+                    commandlineParser.serverPackDestination
+                )
             }
 
             Mode.CLI -> {
-                System.setProperty("java.awt.headless", "true")
                 apiWrapper.stageOne()
                 migrationManager.migrate()
                 apiWrapper.stageTwo()
                 apiWrapper.stageThree()
-                runHeadless()
+                interactiveCommandLine.run(args)
             }
 
             Mode.GUI -> {
@@ -210,8 +203,7 @@ class ServerPackCreator(private val args: Array<String>) {
             }
 
             Mode.SETUP -> {
-                System.setProperty("java.awt.headless", "true")
-                apiWrapper.setup(force = true)
+                interactiveCommandLine.setupCommand.run()
                 log.info("Setup completed.")
                 log.debug("Exiting...")
             }
@@ -221,202 +213,6 @@ class ServerPackCreator(private val args: Array<String>) {
         }
     }
 
-    /**
-     * Check whether a `serverpackcreator.conf`-file exists. If it doesn't exist, and we are not
-     * running in [Mode.CLI] or [Mode.CGEN], create an unconfigured default one which can
-     * then be loaded into the GUI.
-     *
-     * @return `true` if a `serverpackcreator.conf`-file was created.
-     * @author Griefed
-     */
-    private fun createDefaultConfig(): Boolean {
-        return if (!apiWrapper.apiProperties.defaultConfig.exists()) {
-            JarUtilities.copyFileFromJar(
-                "de/griefed/resources/${apiWrapper.apiProperties.defaultConfig.name}",
-                apiWrapper.apiProperties.defaultConfig, this.javaClass
-            )
-        } else {
-            false
-        }
-    }
-
-    /**
-     * Prints the help-text to the console. The help text contains information about:
-     *
-     *  * running ServerPackCreator in different modes:
-     *
-     *  * [Mode.CGEN]
-     *  * [Mode.UPDATE]
-     *  * [Mode.CLI]
-     *  * [Mode.WEB]
-     *  * [Mode.GUI]
-     *  * [Mode.SETUP]
-     *
-     *  * available languages
-     *  * where to report issues
-     *  * where to get support
-     *  * where to find the wiki
-     *  * how to support me
-     *
-     *
-     * @author Griefed
-     */
-    private fun printHelp() {
-        try {
-            println(
-                this.javaClass.getResourceAsStream("/de/griefed/resources/cli_help.txt")!!.readText()
-            )
-        } catch (e: IOException) {
-            throw RuntimeException(e)
-        }
-    }
-
-    /**
-     * Print the text-menu so the user may decide what they would like to do next.
-     *
-     * @author Griefed
-     */
-    private fun printMenu() {
-        println()
-        println("What would you like to do next?")
-        println("(1) : Print help")
-        println("(2) : Check for updates")
-        println("(3) : Change locale")
-        println("(4) : Generate a new configuration")
-        println("(5) : Run ServerPackCreator in CLI-mode")
-        println("(6) : Run ServerPackCreator as a webservice")
-        println("(7) : Run ServerPackCreator with a GUI")
-        println("(0) : Exit")
-        println("-------------------------------------------")
-        print("Enter the number of your selection: ")
-    }
-
-    /**
-     * Offer the user to continue using ServerPackCreator when running in [Mode.HELP], [Mode.UPDATE] or [Mode.CGEN].
-     *
-     * @throws IOException                  When the [de.griefed.serverpackcreator.api.versionmeta.VersionMeta] had to
-     * be instantiated, but an error occurred during the parsing of a manifest.
-     * @throws ParserConfigurationException When the [de.griefed.serverpackcreator.api.versionmeta.VersionMeta] had to
-     * be instantiated, but an error occurred during the parsing of a manifest.
-     * @throws SAXException                 When the [de.griefed.serverpackcreator.api.versionmeta.VersionMeta] had to
-     * be instantiated, but an error occurred during the parsing of a manifest.
-     * @author Griefed
-     */
-    @Throws(IOException::class, ParserConfigurationException::class, SAXException::class)
-    private fun continuedRunOptions() {
-        printMenu()
-        val scanner = Scanner(System.`in`)
-        var selection: Int
-        do {
-            try {
-                selection = scanner.nextInt()
-                if (selection == 7 && GraphicsEnvironment.isHeadless()) {
-                    println("You environment does not support a GUI.")
-                    selection = 100
-                }
-                when (selection) {
-                    1 -> {
-                        printHelp()
-                        printMenu()
-                        selection = 100
-                    }
-
-                    2 -> {
-                        updateChecker.updateCheck(true)
-                        printMenu()
-                        selection = 100
-                    }
-
-                    3 -> {
-                        changeLocale()
-                        printMenu()
-                        selection = 100
-                    }
-
-                    4 -> {
-                        runConfigurationEditor()
-                        printMenu()
-                        selection = 100
-                    }
-
-                    else -> if (selection > 7) {
-                        println("Not a valid number. Please pick a number from 0 to 7.")
-                        printMenu()
-                    }
-                }
-            } catch (ex: InputMismatchException) {
-                println("Not a valid number. Please pick a number from 0 to 7.")
-                selection = 100
-            } catch (ex: ParserConfigurationException) {
-                println("Not a valid number. Please pick a number from 0 to 7.")
-                selection = 100
-            } catch (ex: SAXException) {
-                println("Not a valid number. Please pick a number from 0 to 7.")
-                selection = 100
-            }
-        } while (selection > 7)
-        scanner.close()
-        when (selection) {
-            5 -> run(Mode.CLI)
-            6 -> run(Mode.WEB)
-            7 -> run(Mode.GUI)
-            0 -> println("Exiting...")
-            else -> println("Exiting...")
-        }
-    }
-
-    /**
-     * Run in [Mode.CGEN] and allow the user to load, edit and create a
-     * `serverpackcreator.conf`-file using the CLI.
-     *
-     * @throws IOException                  When the [de.griefed.serverpackcreator.api.versionmeta.VersionMeta] had to be instantiated, but
-     * an error occurred during the parsing of a manifest.
-     * @throws ParserConfigurationException When the [de.griefed.serverpackcreator.api.versionmeta.VersionMeta] had to be instantiated, but
-     * an error occurred during the parsing of a manifest.
-     * @throws SAXException                 When the [de.griefed.serverpackcreator.api.versionmeta.VersionMeta] had to be instantiated, but
-     * an error occurred during the parsing of a manifest.
-     * @author Griefed
-     */
-    @Throws(IOException::class, ParserConfigurationException::class, SAXException::class)
-    private fun runConfigurationEditor() {
-        configurationEditor.continuedRunOptions()
-    }
-
-    /**
-     * Run ServerPackCreator in [Mode.CLI]. Requires a `serverpackcreator.conf`-file to be
-     * present.
-     *
-     * @throws IOException                  When the [de.griefed.serverpackcreator.api.versionmeta.VersionMeta] had to be instantiated, but
-     * an error occurred during the parsing of a manifest.
-     * @throws ParserConfigurationException When the [de.griefed.serverpackcreator.api.versionmeta.VersionMeta] had to be instantiated, but
-     * an error occurred during the parsing of a manifest.
-     * @throws SAXException                 When the [de.griefed.serverpackcreator.api.versionmeta.VersionMeta] had to be instantiated, but
-     * an error occurred during the parsing of a manifest.
-     * @author Griefed
-     */
-    @Throws(IOException::class, ParserConfigurationException::class, SAXException::class)
-    private fun runHeadless() {
-        if (!apiWrapper.apiProperties.defaultConfig.exists()) {
-            log.warn("No serverpackcreator.conf found...")
-            log.info("If you want to run ServerPackCreator in CLI-mode, a serverpackcreator.conf is required.")
-            log.info(
-                "Either copy an existing config, or run ServerPackCreator with the '-cgen'-argument to generate one via commandline."
-            )
-            exitProcess(1)
-        } else {
-            val packConfig = PackConfig()
-            if (!apiWrapper.configurationHandler.checkConfiguration(
-                    apiWrapper.apiProperties.defaultConfig, packConfig
-                ).allChecksPassed
-            ) {
-                exitProcess(1)
-            }
-            if (!apiWrapper.serverPackHandler.run(packConfig).success) {
-                exitProcess(1)
-            }
-        }
-    }
-
     @get:Synchronized
     var splashScreen: SplashScreen? = null
         get() {
@@ -537,40 +333,4 @@ class ServerPackCreator(private val args: Array<String>) {
             log.debug("File-watcher started...")
         }
     }
-
-    /**
-     * Allow the user to change the locale used in localization.
-     *
-     * @author Griefed
-     */
-    private fun changeLocale() {
-        println("What locale would you like to use?")
-        println("(Locale format is en_gb, de_de, uk_ua etc.)")
-        println("Note: Changing the locale only affects the GUI. CLI always uses en_US.")
-        val scanner = Scanner(System.`in`)
-        val regex = "^[a-zA-Z]+_[a-zA-Z]+$".toRegex()
-        var userLocale: String
-
-        // For a list of locales, see https://stackoverflow.com/a/3191729/12537638 or
-        // https://stackoverflow.com/a/28357857/12537638
-        do {
-            userLocale = scanner.next()
-            if (!userLocale.matches(regex)) {
-                println(
-                    "Incorrect format. ServerPackCreator currently only supports locales in the format of en_us (Language, Country)."
-                )
-            } else {
-                try {
-                    apiWrapper.apiProperties.i18n4kConfig.locale = Locale(userLocale)
-                } catch (e: RuntimeException) {
-                    println(
-                        "Incorrect format. ServerPackCreator currently only supports locales in the format of en_GB (Language, Country)."
-                    )
-                    userLocale = "en_GB"
-                }
-            }
-        } while (!userLocale.matches(regex))
-        scanner.close()
-        println("Using language: ${Translations.localeName}")
-    }
 }
\ No newline at end of file
diff --git a/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/ConfigurationEditor.kt b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/ConfigurationEditor.kt
index e9b033889c83ac8f9677c2dff1dff7dceb0c3162..0031c8e302ccd7e6c33070be8fe4243344e66277 100644
--- a/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/ConfigurationEditor.kt
+++ b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/ConfigurationEditor.kt
@@ -29,7 +29,6 @@ import de.griefed.serverpackcreator.api.config.PackConfig
 import de.griefed.serverpackcreator.api.utilities.common.BooleanUtilities
 import de.griefed.serverpackcreator.api.utilities.common.ListUtilities
 import de.griefed.serverpackcreator.api.utilities.common.StringUtilities
-import de.griefed.serverpackcreator.api.utilities.common.Utilities
 import de.griefed.serverpackcreator.api.versionmeta.VersionMeta
 import org.apache.logging.log4j.kotlin.cachedLoggerOf
 import java.io.File
@@ -49,7 +48,6 @@ import kotlin.io.path.moveTo
 class ConfigurationEditor(
     private val configurationHandler: ConfigurationHandler,
     private val apiProperties: ApiProperties,
-    private val utilities: Utilities,
     private val versionMeta: VersionMeta
 ) {
     private val log by lazy { cachedLoggerOf(this.javaClass) }
diff --git a/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/InteractiveCommandLine.kt b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/InteractiveCommandLine.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2b1eac0bc58a4b2a44741ddcfbf91571d79c5c6c
--- /dev/null
+++ b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/InteractiveCommandLine.kt
@@ -0,0 +1,156 @@
+/* Copyright (C) 2024  Griefed
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
+ * USA
+ *
+ * The full license can be found at https:github.com/Griefed/ServerPackCreator/blob/main/LICENSE
+ */
+package de.griefed.serverpackcreator.app.cli
+
+import de.griefed.serverpackcreator.api.ApiWrapper
+import de.griefed.serverpackcreator.app.cli.commands.*
+import de.griefed.serverpackcreator.app.updater.UpdateChecker
+import org.apache.logging.log4j.kotlin.cachedLoggerOf
+import org.jline.builtins.ConfigurationPath
+import org.jline.console.CmdLine
+import org.jline.console.SystemRegistry
+import org.jline.console.impl.Builtins
+import org.jline.console.impl.SystemRegistryImpl
+import org.jline.keymap.KeyMap
+import org.jline.reader.*
+import org.jline.reader.impl.DefaultParser
+import org.jline.reader.impl.LineReaderImpl
+import org.jline.terminal.TerminalBuilder
+import org.jline.widget.TailTipWidgets
+import org.jline.widget.TailTipWidgets.TipType
+import picocli.CommandLine
+import picocli.shell.jline3.PicocliCommands
+import picocli.shell.jline3.PicocliCommands.ClearScreen
+import picocli.shell.jline3.PicocliCommands.PicocliCommandsFactory
+import java.io.PrintWriter
+import java.nio.file.Path
+import java.util.function.Supplier
+
+class InteractiveCommandLine(private val apiWrapper: ApiWrapper, updateChecker: UpdateChecker) {
+
+    private val log by lazy { cachedLoggerOf(this.javaClass) }
+
+    @get:Synchronized
+    val configurationEditor: ConfigurationEditor by lazy {
+        ConfigurationEditor(
+            apiWrapper.configurationHandler,
+            apiWrapper.apiProperties,
+            apiWrapper.versionMeta
+        )
+    }
+
+    val configGenCommand = ConfigGenCommand(apiWrapper, configurationEditor)
+    val helpCommand = HelpCommand()
+    val homeDirCommand = HomeDirCommand()
+    val languageCommand = LanguageCommand(apiWrapper)
+    val runHeadlessCommand = RunHeadlessCommand(apiWrapper)
+    val setupCommand = SetupCommand(apiWrapper)
+    val updateCommand = UpdateCommand(updateChecker)
+
+
+    @CommandLine.Command(
+        name = "",
+        description = ["Interactive shell with completion and autosuggestions."],
+        subcommands = [
+            ConfigGenCommand::class,
+            HelpCommand::class,
+            HomeDirCommand::class,
+            LanguageCommand::class,
+            RunHeadlessCommand::class,
+            SetupCommand::class,
+            UpdateCommand::class,
+            ClearScreen::class,
+            CommandLine.HelpCommand::class
+        ]
+    )
+    class CliCommands : Runnable {
+        private var reader: LineReaderImpl? = null
+        var out: PrintWriter? = null
+
+        fun setReader(reader: LineReader) {
+            this.reader = reader as LineReaderImpl
+            out = reader.terminal.writer()
+        }
+
+        override fun run() {
+            out!!.println(CommandLine(this).usageMessage)
+        }
+    }
+
+    fun run(args: Array<String> = arrayOf("")) {
+        try {
+            val workDir: Supplier<Path> = Supplier<Path> { apiWrapper.apiProperties.homeDirectory.absoluteFile.toPath() }
+            val builtins = Builtins(workDir, ConfigurationPath(workDir.get(), workDir.get()), null)
+            builtins.rename(Builtins.Command.TTOP, "top")
+            builtins.alias("zle", "widget")
+            builtins.alias("bindkey", "keymap")
+
+            val commands = CliCommands()
+            val factory = PicocliCommandsFactory()
+            val cmd = CommandLine(commands, factory)
+            val picocliCommands = PicocliCommands(cmd)
+            val parser: Parser = DefaultParser()
+
+            TerminalBuilder.builder().build().use { terminal ->
+                val systemRegistry: SystemRegistry = SystemRegistryImpl(parser, terminal, workDir, null)
+                systemRegistry.setCommandRegistries(builtins, picocliCommands)
+                systemRegistry.register("help", picocliCommands)
+
+                val reader = LineReaderBuilder.builder()
+                    .terminal(terminal)
+                    .completer(systemRegistry.completer())
+                    .parser(parser)
+                    .variable(LineReader.LIST_MAX, 50) // max tab completion candidates
+                    .build()
+                builtins.setLineReader(reader)
+                commands.setReader(reader)
+                factory.setTerminal(terminal)
+                val widgets = TailTipWidgets(
+                    reader,
+                    { line: CmdLine? -> systemRegistry.commandDescription(line) }, 5, TipType.COMPLETER
+                )
+                widgets.enable()
+                val keyMap = reader.keyMaps["main"]!!
+                keyMap.bind(Reference("tailtip-toggle"), KeyMap.alt("s"))
+
+                val prompt = "ServerPackCreator> "
+                val rightPrompt: String? = null
+
+                // start the shell and process input until the user quits with Ctrl-D
+                var line: String?
+                while (true) {
+                    try {
+                        systemRegistry.cleanUp()
+                        line = reader.readLine(prompt, rightPrompt, null as MaskingCallback?, null)
+                        systemRegistry.execute(line)
+                    } catch (e: UserInterruptException) {
+                        // Ignore
+                    } catch (e: EndOfFileException) {
+                        return
+                    } catch (e: Exception) {
+                        systemRegistry.trace(e)
+                    }
+                }
+            }
+        } catch (t: Throwable) {
+            log.error("Error initializing terminal.", t)
+        }
+    }
+}
\ No newline at end of file
diff --git a/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/commands/Command.kt b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/commands/Command.kt
new file mode 100644
index 0000000000000000000000000000000000000000..bcd1bdf7f681e2adaec9740f2559e84cf6711922
--- /dev/null
+++ b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/commands/Command.kt
@@ -0,0 +1,24 @@
+/* Copyright (C) 2024  Griefed
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
+ * USA
+ *
+ * The full license can be found at https:github.com/Griefed/ServerPackCreator/blob/main/LICENSE
+ */
+package de.griefed.serverpackcreator.app.cli.commands
+
+interface Command : Runnable {
+
+}
\ No newline at end of file
diff --git a/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/commands/ConfigGenCommand.kt b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/commands/ConfigGenCommand.kt
new file mode 100644
index 0000000000000000000000000000000000000000..94983763914351779dd9e402926b8d4119d27f51
--- /dev/null
+++ b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/commands/ConfigGenCommand.kt
@@ -0,0 +1,84 @@
+/* Copyright (C) 2024  Griefed
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
+ * USA
+ *
+ * The full license can be found at https:github.com/Griefed/ServerPackCreator/blob/main/LICENSE
+ */
+package de.griefed.serverpackcreator.app.cli.commands
+
+import de.griefed.serverpackcreator.api.ApiWrapper
+import de.griefed.serverpackcreator.api.utilities.common.JarUtilities
+import de.griefed.serverpackcreator.app.Mode
+import de.griefed.serverpackcreator.app.cli.ConfigurationEditor
+import org.xml.sax.SAXException
+import picocli.CommandLine
+import picocli.shell.jline3.PicocliCommands.ClearScreen
+import java.io.IOException
+import javax.xml.parsers.ParserConfigurationException
+
+@CommandLine.Command(
+    name = "cgen", mixinStandardHelpOptions = true,
+    description = ["Interactively generate a new server pack configuration."],
+    subcommands = [ClearScreen::class, CommandLine.HelpCommand::class]
+)
+class ConfigGenCommand(
+    private val apiWrapper: ApiWrapper = ApiWrapper.api(),
+    private val configurationEditor: ConfigurationEditor = ConfigurationEditor(
+        apiWrapper.configurationHandler,
+        apiWrapper.apiProperties,
+        apiWrapper.versionMeta)
+) : Command {
+
+    override fun run() {
+        createDefaultConfig()
+        runConfigurationEditor()
+    }
+
+    /**
+     * Run in [Mode.CGEN] and allow the user to load, edit and create a
+     * `serverpackcreator.conf`-file using the CLI.
+     *
+     * @throws IOException                  When the [de.griefed.serverpackcreator.api.versionmeta.VersionMeta] had to be instantiated, but
+     * an error occurred during the parsing of a manifest.
+     * @throws ParserConfigurationException When the [de.griefed.serverpackcreator.api.versionmeta.VersionMeta] had to be instantiated, but
+     * an error occurred during the parsing of a manifest.
+     * @throws SAXException                 When the [de.griefed.serverpackcreator.api.versionmeta.VersionMeta] had to be instantiated, but
+     * an error occurred during the parsing of a manifest.
+     * @author Griefed
+     */
+    @Throws(IOException::class, ParserConfigurationException::class, SAXException::class)
+    private fun runConfigurationEditor() {
+        configurationEditor.continuedRunOptions()
+    }
+
+    /**
+     * Check whether a `serverpackcreator.conf`-file exists. If it doesn't exist, and we are not
+     * running in [Mode.CLI] or [Mode.CGEN], create an unconfigured default one which can
+     * then be loaded into the GUI.
+     *
+     * @return `true` if a `serverpackcreator.conf`-file was created.
+     * @author Griefed
+     */
+    private fun createDefaultConfig() {
+        if (!apiWrapper.apiProperties.defaultConfig.exists()) {
+            JarUtilities.copyFileFromJar(
+                "de/griefed/resources/${apiWrapper.apiProperties.defaultConfig.name}",
+                apiWrapper.apiProperties.defaultConfig, this.javaClass
+            )
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/commands/HelpCommand.kt b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/commands/HelpCommand.kt
new file mode 100644
index 0000000000000000000000000000000000000000..1dd42f86ee81bd4d599d956630b757ebdc42d579
--- /dev/null
+++ b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/commands/HelpCommand.kt
@@ -0,0 +1,70 @@
+/* Copyright (C) 2024  Griefed
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
+ * USA
+ *
+ * The full license can be found at https:github.com/Griefed/ServerPackCreator/blob/main/LICENSE
+ */
+package de.griefed.serverpackcreator.app.cli.commands
+
+import de.griefed.serverpackcreator.api.utilities.common.readText
+import de.griefed.serverpackcreator.app.Mode
+import picocli.CommandLine
+import picocli.shell.jline3.PicocliCommands.ClearScreen
+import java.io.IOException
+
+@CommandLine.Command(
+    name = "printHelp", mixinStandardHelpOptions = true,
+    description = ["Print a list of arguments to start ServerPackCreator with, as well as some general help."],
+    subcommands = [ClearScreen::class, CommandLine.HelpCommand::class]
+)
+class HelpCommand : Command {
+
+    override fun run() {
+        printHelp()
+    }
+
+    /**
+     * Prints the help-text to the console. The help text contains information about:
+     *
+     *  * running ServerPackCreator in different modes:
+     *
+     *  * [Mode.CGEN]
+     *  * [Mode.UPDATE]
+     *  * [Mode.CLI]
+     *  * [Mode.WEB]
+     *  * [Mode.GUI]
+     *  * [Mode.SETUP]
+     *
+     *  * available languages
+     *  * where to report issues
+     *  * where to get support
+     *  * where to find the wiki
+     *  * how to support me
+     *
+     *
+     * @author Griefed
+     */
+    private fun printHelp() {
+        try {
+            println(
+                HelpCommand::class.java.getResourceAsStream("/de/griefed/resources/cli_help.txt")!!.readText()
+            )
+        } catch (e: IOException) {
+            throw RuntimeException(e)
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/commands/HomeDirCommand.kt b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/commands/HomeDirCommand.kt
new file mode 100644
index 0000000000000000000000000000000000000000..2dd2e2b20bbe26c6bcfba23c198deb076bfe7a78
--- /dev/null
+++ b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/commands/HomeDirCommand.kt
@@ -0,0 +1,66 @@
+/* Copyright (C) 2024  Griefed
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
+ * USA
+ *
+ * The full license can be found at https:github.com/Griefed/ServerPackCreator/blob/main/LICENSE
+ */
+package de.griefed.serverpackcreator.app.cli.commands
+
+import de.griefed.serverpackcreator.api.utilities.common.SystemUtilities
+import picocli.CommandLine
+import picocli.shell.jline3.PicocliCommands.ClearScreen
+import java.io.File
+import java.util.*
+import java.util.prefs.Preferences
+
+@Suppress("DuplicatedCode")
+@CommandLine.Command(
+    name = "homeDir", mixinStandardHelpOptions = true,
+    description = [
+        "Change the home-directory for ServerPackCreator.",
+        "Changing the home-directory requires a restart of ServerPackCreator afterwards."
+                  ],
+    subcommands = [ClearScreen::class, CommandLine.HelpCommand::class]
+)
+class HomeDirCommand : Command {
+    override fun run() {
+        changeHomeDirectory()
+    }
+
+    private fun changeHomeDirectory() {
+        val scanner = Scanner(System.`in`)
+        println("Enter the full path to the new ServerPackCreator home-directory.")
+        if (SystemUtilities.IS_WINDOWS) {
+            println("Don't forget to escape any \\ in your paths, so 'C:\\Some\\Path' becomes 'C:\\\\Some\\\\Path'.")
+        }
+
+        var path: String
+        do {
+            print("Path: ")
+            path = scanner.nextLine()
+            if (!File(path).isDirectory) {
+                println("Directory '$path' does not exist.")
+            }
+        } while (!File(path).isDirectory)
+
+        Preferences.userRoot().node("ServerPackCreator").put(
+            "de.griefed.serverpackcreator.home",
+            path
+        )
+
+        println("You MUST restart ServerPackCreator for this change to take full effect.")
+    }
+}
\ No newline at end of file
diff --git a/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/commands/LanguageCommand.kt b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/commands/LanguageCommand.kt
new file mode 100644
index 0000000000000000000000000000000000000000..4ecc631186c27146013dce828edf2c8f7c8bc050
--- /dev/null
+++ b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/commands/LanguageCommand.kt
@@ -0,0 +1,65 @@
+/* Copyright (C) 2024  Griefed
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
+ * USA
+ *
+ * The full license can be found at https:github.com/Griefed/ServerPackCreator/blob/main/LICENSE
+ */
+package de.griefed.serverpackcreator.app.cli.commands
+
+import Translations
+import de.comahe.i18n4k.Locale
+import de.griefed.serverpackcreator.api.ApiWrapper
+import picocli.CommandLine
+import picocli.shell.jline3.PicocliCommands.ClearScreen
+import java.util.*
+
+@CommandLine.Command(
+    name = "lang", mixinStandardHelpOptions = true,
+    description = [
+        "Change the language to use for ServerPackCreator.",
+        "Mostly affects the GUI of ServerPackCreator, but the CLI-mode.",
+        "A restart of ServerPackCreator is recommended after changing the language."
+                  ],
+    subcommands = [ClearScreen::class, CommandLine.HelpCommand::class]
+)
+class LanguageCommand(private val apiWrapper: ApiWrapper = ApiWrapper.api()) : Command {
+    override fun run() {
+        printAvailableLanguages()
+        chooseAndSwitchLanguage()
+    }
+
+    private fun printAvailableLanguages() {
+        for (locale in Translations.locales) {
+            println(locale)
+        }
+    }
+
+    private fun chooseAndSwitchLanguage() {
+        val scanner = Scanner(System.`in`)
+        println("Choose one of the available languages above.")
+
+        var userLocale: String
+        do {
+            print("Language: ")
+            userLocale = scanner.next()
+            if (!Translations.locales.map { entry -> entry.language }.contains(userLocale)) {
+                println("Unsupported locale $userLocale.")
+            }
+        } while (!Translations.locales.map { entry -> entry.language }.contains(userLocale))
+        val lang = scanner.nextLine()
+        apiWrapper.apiProperties.changeLocale(Locale(lang))
+    }
+}
\ No newline at end of file
diff --git a/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/commands/RunHeadlessCommand.kt b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/commands/RunHeadlessCommand.kt
new file mode 100644
index 0000000000000000000000000000000000000000..f1922890d0bbbd3d2516264b375f351d5659f25b
--- /dev/null
+++ b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/commands/RunHeadlessCommand.kt
@@ -0,0 +1,142 @@
+/* Copyright (C) 2024  Griefed
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
+ * USA
+ *
+ * The full license can be found at https:github.com/Griefed/ServerPackCreator/blob/main/LICENSE
+ */
+package de.griefed.serverpackcreator.app.cli.commands
+
+import de.griefed.serverpackcreator.api.ApiWrapper
+import de.griefed.serverpackcreator.api.config.PackConfig
+import org.apache.logging.log4j.kotlin.cachedLoggerOf
+import org.xml.sax.SAXException
+import picocli.CommandLine
+import picocli.shell.jline3.PicocliCommands.ClearScreen
+import java.io.File
+import java.io.IOException
+import java.util.*
+import javax.xml.parsers.ParserConfigurationException
+
+@Suppress("DuplicatedCode")
+@CommandLine.Command(
+    name = "run", mixinStandardHelpOptions = true,
+    description = [
+        "Run a config check and server pack generation using the default server pack config.",
+        "The default config file is expected at '</path/to/ServerPackCreator-home-directory/serverpackcreator.conf'.",
+        "Windows Users: You can use / instead of \\ in your paths, too."
+    ],
+    subcommands = [ClearScreen::class, CommandLine.HelpCommand::class]
+)
+class RunHeadlessCommand(private val apiWrapper: ApiWrapper = ApiWrapper.api()) : Command {
+    private val log by lazy { cachedLoggerOf(this.javaClass) }
+
+    override fun run() {
+        runHeadless()
+    }
+
+    @CommandLine.Command(
+        mixinStandardHelpOptions = true, subcommands = [CommandLine.HelpCommand::class],
+        description = [
+            "Run a config check and server pack generation using a specified server pack config.",
+            "You will be asked to enter the path to the desired config after starting this command."
+        ]
+    )
+    fun withSpecificConfig(
+        @CommandLine.Option(
+            names = ["-c", "--config"],
+            description = [
+                "The path to the config file.",
+                "Windows Users: You can use / instead of \\ in your paths, too."
+            ],
+            required = false
+        ) configFile: String?,
+        @CommandLine.Option(
+            names = ["-d", "--destination"],
+            description = [
+                "A destination in which the server pack will be created in.",
+                "All folders, including parent folders, will be created in the process.",
+                "Windows Users: You can use / instead of \\ in your paths, too."
+            ],
+            required = false
+        ) destination: File?
+    ) {
+        val config = if (configFile == null) {
+            requestConfigFile()
+        } else {
+            File(configFile)
+        }
+        runHeadless(config, Optional.ofNullable(destination))
+    }
+
+    @CommandLine.Command(
+        mixinStandardHelpOptions = true, subcommands = [CommandLine.HelpCommand::class],
+        description = [
+            "Run server pack generations for all configs in the config-directory.",
+            "The config-directory is inside ServerPackCreators home-directory."
+        ]
+    )
+    fun withAllInConfigDir() {
+        val configs = apiWrapper.apiProperties.configsDirectory.listFiles()
+        for (config in configs) {
+            runHeadless(config)
+        }
+    }
+
+    private fun requestConfigFile(): File {
+        val scanner = Scanner(System.`in`)
+        println("Enter the full path to the new ServerPackCreator home-directory.")
+
+        var path: String
+        do {
+            print("Path: ")
+            path = scanner.nextLine()
+            if (!File(path).isFile) {
+                println("File '$path' does not exist.")
+            }
+        } while (!File(path).isFile)
+        return File(path)
+    }
+
+    @Throws(IOException::class, ParserConfigurationException::class, SAXException::class)
+    fun runHeadless(
+        config: File = apiWrapper.apiProperties.defaultConfig,
+        destination: Optional<File> = Optional.empty()
+    ) {
+        if (!config.isFile) {
+            log.warn("${config.absolutePath} not found...")
+        } else {
+            val packConfig = PackConfig()
+            packConfig.customDestination = destination
+            val check = apiWrapper.configurationHandler.checkConfiguration(config, packConfig)
+            if (!check.allChecksPassed) {
+                println("Encountered the following errors/problems with the config:")
+                for (error in check.encounteredErrors) {
+                    println(error)
+                }
+            } else {
+                val generation = apiWrapper.serverPackHandler.run(packConfig)
+                if (!generation.success) {
+                    println("Error generating Server Pack:")
+                    for (error in generation.errors) {
+                        println(error)
+                    }
+                } else {
+                    println("Successfully generated Server Pack: ${generation.serverPack.absolutePath}")
+                }
+            }
+        }
+    }
+}
\ No newline at end of file
diff --git a/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/commands/SetupCommand.kt b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/commands/SetupCommand.kt
new file mode 100644
index 0000000000000000000000000000000000000000..c69d27c30155035977d6df6ea46595fa72e7b7b4
--- /dev/null
+++ b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/commands/SetupCommand.kt
@@ -0,0 +1,41 @@
+/* Copyright (C) 2024  Griefed
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
+ * USA
+ *
+ * The full license can be found at https:github.com/Griefed/ServerPackCreator/blob/main/LICENSE
+ */
+package de.griefed.serverpackcreator.app.cli.commands
+
+import de.griefed.serverpackcreator.api.ApiWrapper
+import picocli.CommandLine
+import picocli.shell.jline3.PicocliCommands.ClearScreen
+
+@CommandLine.Command(
+    name = "setup", mixinStandardHelpOptions = true,
+    description = [
+        "Force-run the ServerPackCreator API setup, generating and providing all required working- and template-files and folders."
+    ],
+    subcommands = [ClearScreen::class, CommandLine.HelpCommand::class]
+)
+class SetupCommand(private val apiWrapper: ApiWrapper = ApiWrapper.api()) : Command {
+    override fun run() {
+        forceApiSetup()
+    }
+
+    private fun forceApiSetup() {
+        apiWrapper.setup(force = true)
+    }
+}
\ No newline at end of file
diff --git a/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/commands/UpdateCommand.kt b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/commands/UpdateCommand.kt
new file mode 100644
index 0000000000000000000000000000000000000000..eecfe73e854c523f4143bbe5cb85d280e2e2b702
--- /dev/null
+++ b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/cli/commands/UpdateCommand.kt
@@ -0,0 +1,224 @@
+/* Copyright (C) 2024  Griefed
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
+ * USA
+ *
+ * The full license can be found at https:github.com/Griefed/ServerPackCreator/blob/main/LICENSE
+ */
+package de.griefed.serverpackcreator.app.cli.commands
+
+import Translations
+import com.install4j.api.Util
+import com.install4j.api.context.UserCanceledException
+import com.install4j.api.launcher.ApplicationLauncher
+import com.install4j.api.launcher.Variables
+import com.install4j.api.update.ApplicationDisplayMode
+import com.install4j.api.update.UpdateDescriptor
+import com.install4j.api.update.UpdateDescriptorEntry
+import de.griefed.serverpackcreator.api.ApiWrapper
+import de.griefed.serverpackcreator.app.updater.UpdateChecker
+import org.apache.logging.log4j.kotlin.cachedLoggerOf
+import picocli.CommandLine
+import picocli.shell.jline3.PicocliCommands.ClearScreen
+import java.io.IOException
+import java.nio.file.Files
+import java.nio.file.Paths
+import java.util.concurrent.ExecutionException
+
+@CommandLine.Command(
+    name = "update", mixinStandardHelpOptions = true,
+    description = [
+        "Check for available updates.",
+        "If you installed ServerPackCreator using the official installers, and if an update is available, the update will be installed automatically."
+    ],
+    subcommands = [ClearScreen::class, CommandLine.HelpCommand::class]
+)
+class UpdateCommand(private val updateChecker: UpdateChecker = UpdateChecker(ApiWrapper.api().apiProperties)) : Command {
+    private val log by lazy { cachedLoggerOf(this.javaClass) }
+    private var i4JUpdatable = false
+    private var i4JDownload = false
+    private var i4JExecute = false
+
+    init {
+        checkForUpdateWithApi()
+    }
+
+    override fun run() {
+        checkAndRunUpdate()
+    }
+
+    private fun checkAndRunUpdate() {
+        val updateAvailable = updateChecker.updateCheck(true)
+        if (updateAvailable) {
+            return
+        }
+        if (i4JUpdatable && !i4JDownload && !i4JExecute) {
+            checkForUpdateI4J()
+            if (i4JUpdatable) {
+                if (i4JDownload) {
+                    downloadAndUpdate()
+                } else if (i4JExecute) {
+                    executeUpdate()
+                }
+            }
+        }
+    }
+
+    private fun executeUpdate() {
+        // The arguments that are passed to the installer switch the default GUI mode to an unattended
+        // mode with a progress bar. "-q" activates unattended mode, and "-splash Updating hello world ..."
+        // shows a progress bar with the specified title.
+        Thread {
+            com.install4j.api.update.UpdateChecker.executeScheduledUpdate(
+                mutableListOf(
+                    "-q",
+                    "Updating ServerPackCreator ...",
+                    "-alerts"
+                ), true, null
+            )
+        }.start()
+    }
+
+    private fun downloadAndUpdate() {
+        // Here the background update downloader is launched
+        // Note the third argument which makes the call to the background update downloader blocking.
+        // The callback receives progress information from the update downloader and changes the text on the button
+        try {
+            ApplicationLauncher.launchApplication("524", null, true, object : ApplicationLauncher.Callback {
+                override fun exited(exitValue: Int) {
+                }
+
+                override fun prepareShutdown() {
+                }
+
+                override fun createProgressListener(): ApplicationLauncher.ProgressListener {
+                    return object : ApplicationLauncher.ProgressListenerAdapter() {
+                        var downloading: Boolean = false
+                        override fun actionStarted(id: String) {
+                            downloading = id == "downloadFile"
+                        }
+
+                        override fun percentCompleted(value: Int) {
+                            if (downloading) {
+                                printProgress(value)
+                            }
+                        }
+
+                        override fun indeterminateProgress(indeterminateProgress: Boolean) {
+                            printProgress(-1)
+                        }
+                    }
+                }
+            })
+            // At this point, the update downloader has returned, and we can check if the "Schedule update installation"
+            // action has registered an update installer for execution
+            // We now switch to the EDT in done() for terminating the application
+
+            if (com.install4j.api.update.UpdateChecker.isUpdateScheduled()) {
+                println(Translations.update_dialog_update_message.toString())
+                // We execute the update immediately, but you could ask the user whether the update should be
+                // installed now. The scheduling of update installers is persistent, so this will also work
+                // after a restart of the launcher.
+                executeUpdate()
+            } else {
+                log.error(Translations.update_dialog_update_failed_message.toString())
+            }
+        } catch (e: InterruptedException) {
+            log.error("Update interrupted.", e)
+        } catch (e: ExecutionException) {
+            log.error(Translations.update_dialog_update_failed_cause(e.cause!!.message.toString()))
+            log.error("Update could not be executed.", e)
+        }
+
+    }
+
+    private fun printProgress(value: Int) {
+        if (value == -1) {
+            println("Downloading...")
+        } else {
+            println("$value%...")
+        }
+    }
+
+    private fun checkForUpdateI4J() {
+        // Here, the "Standalone update downloader" application is launched in a new process.
+        // The ID of the installer application is shown in the install4j IDE on the Installer->Screens & Actions step
+        // when the "Show IDs" toggle button is selected.
+        // Use the "Integration wizard" button on the "Launcher integration" tab in the configuration
+        // panel of the installer application, to get such a code snippet.
+        try {
+            ApplicationLauncher.launchApplication("462", null, false, null)
+            // This call returns immediately, because the "blocking" argument is set to false
+        } catch (e: IOException) {
+            log.error("Error launching updater.", e)
+        }
+    }
+
+    private fun checkForUpdateWithApi() {
+        try {
+            if (isI4JUpdatable()) {
+                // The compiler variable sys.updatesUrl holds the URL where the updates.xml file is hosted.
+                // That URL is defined on the "Installer->Auto Update Options" step.
+                // The same compiler variable is used by the "Check for update" actions that are contained in the update
+                // downloaders.
+                val updateUrl: String = Variables.getCompilerVariable("sys.updatesUrl")
+                val updateDescriptor: UpdateDescriptor =
+                    com.install4j.api.update.UpdateChecker.getUpdateDescriptor(updateUrl, ApplicationDisplayMode.GUI)
+
+                try {
+                    // If getPossibleUpdateEntry returns a non-null value, the version number in the updates.xml file
+                    // is greater than the version number of the local installation.
+                    val updateDescriptorEntry: UpdateDescriptorEntry? = updateDescriptor.possibleUpdateEntry
+
+                    // only installers and single bundle archives on macOS are supported for background updates
+                    if (updateDescriptorEntry != null && (!updateDescriptorEntry.isArchive || updateDescriptorEntry.isSingleBundle)) {
+                        if (!updateDescriptorEntry.isDownloaded) {
+                            // An update is available for download
+                            i4JDownload = true
+                        } else if (com.install4j.api.update.UpdateChecker.isUpdateScheduled()) {
+                            // The update has been downloaded, but the installation did not succeed yet.
+                            i4JExecute = true
+                        }
+                    }
+                } catch (e: InterruptedException) {
+                    log.error("Update interrupted.", e)
+                } catch (e: ExecutionException) {
+                    val cause = e.cause
+                    // UserCanceledException means that the user has canceled the proxy dialog
+                    if (cause !is UserCanceledException) {
+                        log.error("Update could not be executed: ${e.message}.")
+                    }
+                }
+
+            } else {
+                i4JUpdatable = false
+            }
+        } catch (ncdfe: NoClassDefFoundError) {
+            i4JUpdatable = false
+            log.debug("Not an install4j installation.")
+        }
+    }
+
+    fun isI4JUpdatable(): Boolean {
+        try {
+            val installationDirectory = Paths.get(Variables.getInstallerVariable("sys.installationDir").toString())
+            i4JUpdatable = true
+            return !Files.getFileStore(installationDirectory).isReadOnly && (Util.isWindows() || Util.isMacOS() || (Util.isLinux() && !Util.isArchive()))
+        } catch (ex: IOException) {
+            log.error("Error checking for install4j updatability.", ex)
+        }
+        return false
+    }
+}
\ No newline at end of file
diff --git a/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/gui/window/UpdateDialogs.kt b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/gui/window/UpdateDialogs.kt
index 04065cdf5867a582e2c386a5b3f5081f57bd3e52..f6a0974549762150003004bf5ac44492c6f1c5c0 100644
--- a/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/gui/window/UpdateDialogs.kt
+++ b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/gui/window/UpdateDialogs.kt
@@ -41,7 +41,6 @@ import java.awt.datatransfer.Clipboard
 import java.awt.datatransfer.StringSelection
 import java.awt.event.ActionListener
 import java.io.IOException
-import java.net.URISyntaxException
 import java.nio.file.Files
 import java.nio.file.Paths
 import java.util.*
@@ -190,7 +189,7 @@ class UpdateDialogs(
         }
     }
 
-    private fun isUpdatable(): Boolean {
+    private fun isI4JUpdatable(): Boolean {
         try {
             val installationDirectory = Paths.get(Variables.getInstallerVariable("sys.installationDir").toString())
             i4JUpdatable = true
@@ -203,7 +202,7 @@ class UpdateDialogs(
 
     private fun checkForUpdateWithApi() {
         try {
-            if (isUpdatable()) {
+            if (isI4JUpdatable()) {
                 // Here we check for updates in the background with the API.
                 object : SwingWorker<UpdateDescriptorEntry, Any?>() {
                     @Throws(Exception::class)
diff --git a/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/updater/UpdateChecker.kt b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/updater/UpdateChecker.kt
index 0fd1832de9f79a538f5d2c7e6d7503291aae85c5..0bf5b2676c03cad0f71ab6697f3a158a66a0d1de 100644
--- a/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/updater/UpdateChecker.kt
+++ b/serverpackcreator-app/src/main/kotlin/de/griefed/serverpackcreator/app/updater/UpdateChecker.kt
@@ -83,10 +83,7 @@ class UpdateChecker(private val apiProperties: ApiProperties) {
      * @return The update, if available, as well as the download URL.
      * @author Griefed
      */
-    fun checkForUpdate(
-        version: String,
-        preReleaseCheck: Boolean
-    ): Optional<Update> {
+    fun checkForUpdate(version: String, preReleaseCheck: Boolean): Optional<Update> {
         if (version.equals("dev", ignoreCase = true) || gitHub == null) {
             log.warn("Not checking for updates. Either using a dev-version, or GitHub Checking is not initialized.")
             return Optional.empty<Update>()
@@ -102,7 +99,7 @@ class UpdateChecker(private val apiProperties: ApiProperties) {
      * @param logToConsole Whether to log update information to console or to logs.
      * @author Griefed
      */
-    fun updateCheck(logToConsole: Boolean = false) {
+    fun updateCheck(logToConsole: Boolean = false): Boolean {
         refresh()
         val update: Optional<Update> = checkForUpdate(
             apiProperties.apiVersion,
@@ -126,5 +123,7 @@ class UpdateChecker(private val apiProperties: ApiProperties) {
                 log.info("No updates available.")
             }
         }
+
+        return update.isPresent
     }
 }
\ No newline at end of file
diff --git a/serverpackcreator-app/src/main/resources/de/griefed/resources/gui/LICENSE-AGREEMENT b/serverpackcreator-app/src/main/resources/de/griefed/resources/gui/LICENSE-AGREEMENT
index 8ec620bcc4690832eb3d840d9e9d1345a0002e77..4626602205366ae59a5d52b258f1dc0e0c40f44b 100644
--- a/serverpackcreator-app/src/main/resources/de/griefed/resources/gui/LICENSE-AGREEMENT
+++ b/serverpackcreator-app/src/main/resources/de/griefed/resources/gui/LICENSE-AGREEMENT
@@ -474,7 +474,7 @@ Used libraries / dependencies licenses:
 
 -------------------------------------------------------
 
-(1 of 38)
+(1 of 40)
 Group:   com.cronutils
 Name:    cron-utils
 Version: 9.2.1
@@ -497,7 +497,7 @@ URL: https://www.apache.org/licenses/LICENSE-2.0
 
 #######################################
 
-(2 of 38)
+(2 of 40)
 Group:   com.electronwill.night-config
 Name:    toml
 Version: 3.7.2
@@ -515,176 +515,9 @@ License: GNU Lesser General Public License v3.0
 URL: https://www.gnu.org/licenses/lgpl-3.0.txt
 
 
-                   GNU LESSER GENERAL PUBLIC LICENSE
-                       Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-
-  This version of the GNU Lesser General Public License incorporates
-the terms and conditions of version 3 of the GNU General Public
-License, supplemented by the additional permissions listed below.
-
-  0. Additional Definitions.
-
-  As used herein, "this License" refers to version 3 of the GNU Lesser
-General Public License, and the "GNU GPL" refers to version 3 of the GNU
-General Public License.
-
-  "The Library" refers to a covered work governed by this License,
-other than an Application or a Combined Work as defined below.
-
-  An "Application" is any work that makes use of an interface provided
-by the Library, but which is not otherwise based on the Library.
-Defining a subclass of a class defined by the Library is deemed a mode
-of using an interface provided by the Library.
-
-  A "Combined Work" is a work produced by combining or linking an
-Application with the Library.  The particular version of the Library
-with which the Combined Work was made is also called the "Linked
-Version".
-
-  The "Minimal Corresponding Source" for a Combined Work means the
-Corresponding Source for the Combined Work, excluding any source code
-for portions of the Combined Work that, considered in isolation, are
-based on the Application, and not on the Linked Version.
-
-  The "Corresponding Application Code" for a Combined Work means the
-object code and/or source code for the Application, including any data
-and utility programs needed for reproducing the Combined Work from the
-Application, but excluding the System Libraries of the Combined Work.
-
-  1. Exception to Section 3 of the GNU GPL.
-
-  You may convey a covered work under sections 3 and 4 of this License
-without being bound by section 3 of the GNU GPL.
-
-  2. Conveying Modified Versions.
-
-  If you modify a copy of the Library, and, in your modifications, a
-facility refers to a function or data to be supplied by an Application
-that uses the facility (other than as an argument passed when the
-facility is invoked), then you may convey a copy of the modified
-version:
-
-   a) under this License, provided that you make a good faith effort to
-   ensure that, in the event an Application does not supply the
-   function or data, the facility still operates, and performs
-   whatever part of its purpose remains meaningful, or
-
-   b) under the GNU GPL, with none of the additional permissions of
-   this License applicable to that copy.
-
-  3. Object Code Incorporating Material from Library Header Files.
-
-  The object code form of an Application may incorporate material from
-a header file that is part of the Library.  You may convey such object
-code under terms of your choice, provided that, if the incorporated
-material is not limited to numerical parameters, data structure
-layouts and accessors, or small macros, inline functions and templates
-(ten or fewer lines in length), you do both of the following:
-
-   a) Give prominent notice with each copy of the object code that the
-   Library is used in it and that the Library and its use are
-   covered by this License.
-
-   b) Accompany the object code with a copy of the GNU GPL and this license
-   document.
-
-  4. Combined Works.
-
-  You may convey a Combined Work under terms of your choice that,
-taken together, effectively do not restrict modification of the
-portions of the Library contained in the Combined Work and reverse
-engineering for debugging such modifications, if you also do each of
-the following:
-
-   a) Give prominent notice with each copy of the Combined Work that
-   the Library is used in it and that the Library and its use are
-   covered by this License.
-
-   b) Accompany the Combined Work with a copy of the GNU GPL and this license
-   document.
-
-   c) For a Combined Work that displays copyright notices during
-   execution, include the copyright notice for the Library among
-   these notices, as well as a reference directing the user to the
-   copies of the GNU GPL and this license document.
-
-   d) Do one of the following:
-
-       0) Convey the Minimal Corresponding Source under the terms of this
-       License, and the Corresponding Application Code in a form
-       suitable for, and under terms that permit, the user to
-       recombine or relink the Application with a modified version of
-       the Linked Version to produce a modified Combined Work, in the
-       manner specified by section 6 of the GNU GPL for conveying
-       Corresponding Source.
-
-       1) Use a suitable shared library mechanism for linking with the
-       Library.  A suitable mechanism is one that (a) uses at run time
-       a copy of the Library already present on the user's computer
-       system, and (b) will operate properly with a modified version
-       of the Library that is interface-compatible with the Linked
-       Version.
-
-   e) Provide Installation Information, but only if you would otherwise
-   be required to provide such information under section 6 of the
-   GNU GPL, and only to the extent that such information is
-   necessary to install and execute a modified version of the
-   Combined Work produced by recombining or relinking the
-   Application with a modified version of the Linked Version. (If
-   you use option 4d0, the Installation Information must accompany
-   the Minimal Corresponding Source and Corresponding Application
-   Code. If you use option 4d1, you must provide the Installation
-   Information in the manner specified by section 6 of the GNU GPL
-   for conveying Corresponding Source.)
-
-  5. Combined Libraries.
-
-  You may place library facilities that are a work based on the
-Library side by side in a single library together with other library
-facilities that are not Applications and are not covered by this
-License, and convey such a combined library under terms of your
-choice, if you do both of the following:
-
-   a) Accompany the combined library with a copy of the same work based
-   on the Library, uncombined with any other library facilities,
-   conveyed under the terms of this License.
-
-   b) Give prominent notice with the combined library that part of it
-   is a work based on the Library, and explaining where to find the
-   accompanying uncombined form of the same work.
-
-  6. Revised Versions of the GNU Lesser General Public License.
-
-  The Free Software Foundation may publish revised and/or new versions
-of the GNU Lesser General Public License from time to time. Such new
-versions will be similar in spirit to the present version, but may
-differ in detail to address new problems or concerns.
-
-  Each version is given a distinguishing version number. If the
-Library as you received it specifies that a certain numbered version
-of the GNU Lesser General Public License "or any later version"
-applies to it, you have the option of following the terms and
-conditions either of that published version or of any later version
-published by the Free Software Foundation. If the Library as you
-received it does not specify a version number of the GNU Lesser
-General Public License, you may choose any version of the GNU Lesser
-General Public License ever published by the Free Software Foundation.
-
-  If the Library as you received it specifies that a proxy can decide
-whether future versions of the GNU Lesser General Public License shall
-apply, that proxy's public statement of acceptance of any version is
-permanent authorization for you to choose that version for the
-Library.
-
-
 #######################################
 
-(3 of 38)
+(3 of 40)
 Group:   com.fasterxml.jackson.core
 Name:    jackson-databind
 Version: 2.17.0
@@ -927,7 +760,7 @@ from the source code management (SCM) system project uses.
 
 #######################################
 
-(4 of 38)
+(4 of 40)
 Group:   com.fasterxml.jackson.module
 Name:    jackson-module-kotlin
 Version: 2.17.1
@@ -979,10 +812,10 @@ from the source code management (SCM) system project uses.
 
 #######################################
 
-(5 of 38)
+(5 of 40)
 Group:   com.formdev
 Name:    flatlaf
-Version: 3.4
+Version: 3.5
 POM Project URL: https://github.com/JFormDesigner/FlatLaf
 
 
@@ -1197,7 +1030,7 @@ Embedded license:
 
 #######################################
 
-(6 of 38)
+(6 of 40)
 Group:   com.formdev
 Name:    flatlaf-extras
 Version: 3.4
@@ -1415,7 +1248,7 @@ Embedded license:
 
 #######################################
 
-(7 of 38)
+(7 of 40)
 Group:   com.formdev
 Name:    flatlaf-fonts-inter
 Version: 4.0
@@ -1731,7 +1564,7 @@ OTHER DEALINGS IN THE FONT SOFTWARE.
 
 #######################################
 
-(8 of 38)
+(8 of 40)
 Group:   com.formdev
 Name:    flatlaf-fonts-jetbrains-mono
 Version: 2.304
@@ -1953,7 +1786,7 @@ Embedded license:
 
 #######################################
 
-(9 of 38)
+(9 of 40)
 Group:   com.formdev
 Name:    flatlaf-fonts-roboto
 Version: 2.137
@@ -2375,7 +2208,7 @@ Embedded license:
 
 #######################################
 
-(10 of 38)
+(10 of 40)
 Group:   com.formdev
 Name:    flatlaf-fonts-roboto-mono
 Version: 3.000
@@ -2797,10 +2630,10 @@ Embedded license:
 
 #######################################
 
-(11 of 38)
+(11 of 40)
 Group:   com.formdev
 Name:    flatlaf-intellij-themes
-Version: 3.4
+Version: 3.5
 POM Project URL: https://github.com/JFormDesigner/FlatLaf
 
 
@@ -3015,7 +2848,7 @@ Embedded license:
 
 #######################################
 
-(12 of 38)
+(12 of 40)
 Group:   com.formdev
 Name:    svgSalamander
 Version: 1.1.4
@@ -3043,7 +2876,33 @@ URL: https://opensource.org/licenses/BSD-2-Clause
 
 #######################################
 
-(13 of 38)
+(13 of 40)
+Group:   com.github.MCRcortex
+Name:    nekodetector
+Version: Version-1.1-pre
+POM Project URL: https://github.com/MCRcortex/nekodetector
+
+
+POM License: MIT License
+ - https://opensource.org/licenses/MIT
+
+
+POM license(s): 
+
+
+License: MIT License
+URL: https://opensource.org/licenses/MIT
+
+
+#######################################
+
+(14 of 40)
+Group:   com.install4j
+Name:    install4j-runtime
+Version: 10.0.9
+#######################################
+
+(15 of 40)
 Group:   com.miglayout
 Name:    miglayout-swing
 Version: 11.3
@@ -3063,7 +2922,7 @@ URL: https://opensource.org/licenses/0BSD
 
 #######################################
 
-(14 of 38)
+(16 of 40)
 Group:   commons-io
 Name:    commons-io
 Version: 2.16.1
@@ -3291,19 +3150,30 @@ The Apache Software Foundation (https://www.apache.org/).
 
 #######################################
 
-(15 of 38)
+(17 of 40)
 Group:   de.comahe.i18n4k
 Name:    i18n4k-core
-Version: 0.7.0
-No license information found
+Version: 0.9.0
+POM Project URL: https://comahe-de.github.io/i18n4k/
+
+
+POM License: Apache License, Version 2.0
+ - https://www.apache.org/licenses/LICENSE-2.0
+
+
+POM license(s): 
+
+
+License: Apache License, Version 2.0
+URL: https://www.apache.org/licenses/LICENSE-2.0
 
 
 #######################################
 
-(16 of 38)
+(18 of 40)
 Group:   de.comahe.i18n4k
 Name:    i18n4k-core-jvm
-Version: 0.7.0
+Version: 0.9.0
 POM Project URL: https://comahe-de.github.io/i18n4k/
 
 
@@ -3320,25 +3190,67 @@ URL: https://www.apache.org/licenses/LICENSE-2.0
 
 #######################################
 
-(17 of 38)
+(19 of 40)
 Group:   de.jensklingenberg.ktorfit
 Name:    ktorfit-lib
-Version: 1.14.0
-No license information found
+Version: 2.0.1
+POM Project URL: https://github.com/Foso/Ktorfit
+
+
+POM License: Apache License, Version 2.0
+ - https://www.apache.org/licenses/LICENSE-2.0
+
+
+POM license(s): 
+
+
+License: Apache License, Version 2.0
+URL: https://www.apache.org/licenses/LICENSE-2.0
+
+
+#######################################
+
+(20 of 40)
+Group:   info.picocli
+Name:    picocli-shell-jline3
+Version: 4.7.6
+POM Project URL: https://picocli.info
+
+
+POM License: Apache License, Version 2.0
+ - https://www.apache.org/licenses/LICENSE-2.0
+
+
+POM license(s): 
+
+
+License: Apache License, Version 2.0
+URL: https://www.apache.org/licenses/LICENSE-2.0
 
 
 #######################################
 
-(18 of 38)
+(21 of 40)
 Group:   io.github.microutils
 Name:    kotlin-logging
 Version: 3.0.5
-No license information found
+POM Project URL: https://github.com/oshai/kotlin-logging
+
+
+POM License: Apache License, Version 2.0
+ - https://www.apache.org/licenses/LICENSE-2.0
+
+
+POM license(s): 
+
+
+License: Apache License, Version 2.0
+URL: https://www.apache.org/licenses/LICENSE-2.0
 
 
 #######################################
 
-(19 of 38)
+(22 of 40)
 Group:   net.java.balloontip
 Name:    balloontip
 Version: 1.2.4.1
@@ -3358,7 +3270,7 @@ URL: https://opensource.org/licenses/BSD-3-Clause
 
 #######################################
 
-(20 of 38)
+(23 of 40)
 Group:   net.lingala.zip4j
 Name:    zip4j
 Version: 2.11.5
@@ -3381,7 +3293,7 @@ URL: https://www.apache.org/licenses/LICENSE-2.0
 
 #######################################
 
-(21 of 38)
+(24 of 40)
 Group:   org.apache.logging.log4j
 Name:    log4j-api-kotlin
 Version: 1.4.0
@@ -3608,7 +3520,7 @@ The Apache Software Foundation (http://www.apache.org/).
 
 #######################################
 
-(22 of 38)
+(25 of 40)
 Group:   org.apache.logging.log4j
 Name:    log4j-core
 Version: 2.23.1
@@ -3836,7 +3748,7 @@ Copyright 2005-2006 Tim Fennell
 
 #######################################
 
-(23 of 38)
+(26 of 40)
 Group:   org.bouncycastle
 Name:    bcpkix-jdk18on
 Version: 1.78
@@ -3856,7 +3768,7 @@ URL: https://www.bouncycastle.org/licence.html
 
 #######################################
 
-(24 of 38)
+(27 of 40)
 Group:   org.javassist
 Name:    javassist
 Version: 3.30.2-GA
@@ -3895,19 +3807,10 @@ URL: https://www.mozilla.org/en-US/MPL/1.1
 
 #######################################
 
-(25 of 38)
+(28 of 40)
 Group:   org.jetbrains.kotlin
 Name:    kotlin-bom
-Version: 1.9.23
-No license information found
-
-
-#######################################
-
-(26 of 38)
-Group:   org.jetbrains.kotlin
-Name:    kotlin-reflect
-Version: 1.9.23
+Version: 2.0.20
 POM Project URL: https://kotlinlang.org/
 
 
@@ -3924,9 +3827,9 @@ URL: https://www.apache.org/licenses/LICENSE-2.0
 
 #######################################
 
-(27 of 38)
+(29 of 40)
 Group:   org.jetbrains.kotlin
-Name:    kotlin-stdlib
+Name:    kotlin-reflect
 Version: 1.9.23
 POM Project URL: https://kotlinlang.org/
 
@@ -3944,10 +3847,10 @@ URL: https://www.apache.org/licenses/LICENSE-2.0
 
 #######################################
 
-(28 of 38)
+(30 of 40)
 Group:   org.jetbrains.kotlin
 Name:    kotlin-stdlib
-Version: 2.0.0-RC1
+Version: 2.0.20
 POM Project URL: https://kotlinlang.org/
 
 
@@ -3964,16 +3867,27 @@ URL: https://www.apache.org/licenses/LICENSE-2.0
 
 #######################################
 
-(29 of 38)
+(31 of 40)
 Group:   org.jetbrains.kotlinx
 Name:    kotlinx-coroutines-core
-Version: 1.8.0
-No license information found
+Version: 1.8.1
+POM Project URL: https://github.com/Kotlin/kotlinx.coroutines
+
+
+POM License: Apache License, Version 2.0
+ - https://www.apache.org/licenses/LICENSE-2.0
+
+
+POM license(s): 
+
+
+License: Apache License, Version 2.0
+URL: https://www.apache.org/licenses/LICENSE-2.0
 
 
 #######################################
 
-(30 of 38)
+(32 of 40)
 Group:   org.jetbrains.kotlinx
 Name:    kotlinx-coroutines-swing
 Version: 1.8.0
@@ -3993,16 +3907,27 @@ URL: https://www.apache.org/licenses/LICENSE-2.0
 
 #######################################
 
-(31 of 38)
+(33 of 40)
 Group:   org.jetbrains.kotlinx
 Name:    kotlinx-datetime
 Version: 0.5.0
-No license information found
+POM Project URL: https://github.com/Kotlin/kotlinx-datetime
+
+
+POM License: Apache License, Version 2.0
+ - https://www.apache.org/licenses/LICENSE-2.0
+
+
+POM license(s): 
+
+
+License: Apache License, Version 2.0
+URL: https://www.apache.org/licenses/LICENSE-2.0
 
 
 #######################################
 
-(32 of 38)
+(34 of 40)
 Group:   org.pf4j
 Name:    pf4j
 Version: 3.11.0
@@ -4019,7 +3944,7 @@ URL: https://www.apache.org/licenses/LICENSE-2.0
 
 #######################################
 
-(33 of 38)
+(35 of 40)
 Group:   org.postgresql
 Name:    postgresql
 Version: 42.7.3
@@ -4171,10 +4096,10 @@ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
 #######################################
 
-(34 of 38)
+(36 of 40)
 Group:   org.springframework.boot
 Name:    spring-boot-devtools
-Version: 3.3.0
+Version: 3.3.2
 POM Project URL: https://spring.io/projects/spring-boot
 
 
@@ -4387,7 +4312,7 @@ Embedded license:
    See the License for the specific language governing permissions and
    limitations under the License.
 
-Spring Boot 3.3.0
+Spring Boot 3.3.2
 Copyright (c) 2012-2024 VMware, Inc.
 
 This product is licensed to you under the Apache License, Version 2.0
@@ -4396,10 +4321,10 @@ the License.
 
 #######################################
 
-(35 of 38)
+(37 of 40)
 Group:   org.springframework.boot
 Name:    spring-boot-starter-data-jpa
-Version: 3.3.0
+Version: 3.3.2
 POM Project URL: https://spring.io/projects/spring-boot
 
 
@@ -4612,7 +4537,7 @@ Embedded license:
    See the License for the specific language governing permissions and
    limitations under the License.
 
-Spring Boot 3.3.0
+Spring Boot 3.3.2
 Copyright (c) 2012-2024 VMware, Inc.
 
 This product is licensed to you under the Apache License, Version 2.0
@@ -4621,10 +4546,10 @@ the License.
 
 #######################################
 
-(36 of 38)
+(38 of 40)
 Group:   org.springframework.boot
 Name:    spring-boot-starter-log4j2
-Version: 3.3.1
+Version: 3.3.2
 POM Project URL: https://spring.io/projects/spring-boot
 
 
@@ -4837,7 +4762,7 @@ Embedded license:
    See the License for the specific language governing permissions and
    limitations under the License.
 
-Spring Boot 3.3.1
+Spring Boot 3.3.2
 Copyright (c) 2012-2024 VMware, Inc.
 
 This product is licensed to you under the Apache License, Version 2.0
@@ -4846,10 +4771,10 @@ the License.
 
 #######################################
 
-(37 of 38)
+(39 of 40)
 Group:   org.springframework.boot
 Name:    spring-boot-starter-web
-Version: 3.3.1
+Version: 3.3.2
 POM Project URL: https://spring.io/projects/spring-boot
 
 
@@ -5062,7 +4987,7 @@ Embedded license:
    See the License for the specific language governing permissions and
    limitations under the License.
 
-Spring Boot 3.3.1
+Spring Boot 3.3.2
 Copyright (c) 2012-2024 VMware, Inc.
 
 This product is licensed to you under the Apache License, Version 2.0
@@ -5071,7 +4996,7 @@ the License.
 
 #######################################
 
-(38 of 38)
+(40 of 40)
 Group:   tokyo.northside
 Name:    tipoftheday
 Version: 0.4.2
diff --git a/spc.install4j b/spc.install4j
index 4ba77d355be3c4fec15b6441b66418d9892ce5e4..cec9c796c28cb59bc25e4f1b25a236a0811c05d0 100644
--- a/spc.install4j
+++ b/spc.install4j
@@ -889,7 +889,7 @@ return console.askYesNo(message, true);
           </screen>
         </screens>
       </application>
-      <application name="Standalone update downloader" id="380" beanClass="com.install4j.runtime.beans.applications.CustomApplication" launchInNewProcess="false">
+      <application name="Standalone update downloader GUI" id="380" beanClass="com.install4j.runtime.beans.applications.CustomApplication" launchInNewProcess="false">
         <serializedBean>
           <property name="customIconImageFiles">
             <add>
@@ -1489,7 +1489,7 @@ return true;</property>
           </group>
         </screens>
       </application>
-      <application name="Background update downloader" id="442" beanClass="com.install4j.runtime.beans.applications.CustomApplication">
+      <application name="Background update downloader GUI" id="442" beanClass="com.install4j.runtime.beans.applications.CustomApplication">
         <serializedBean>
           <property name="customIconImageFiles">
             <add>
@@ -1766,6 +1766,884 @@ file.getParent() != null || (file.getName().endsWith(".app") &amp;&amp; director
           </screen>
         </startup>
       </application>
+      <application name="Standalone update downloader CLI" id="462" beanClass="com.install4j.runtime.beans.applications.CustomApplication" launchInNewProcess="false">
+        <serializedBean>
+          <property name="customIconImageFiles">
+            <add>
+              <object class="com.install4j.api.beans.ExternalFile">
+                <string>${compiler:sys.install4jHome}/resource/updater_16.png</string>
+              </object>
+            </add>
+            <add>
+              <object class="com.install4j.api.beans.ExternalFile">
+                <string>${compiler:sys.install4jHome}/resource/updater_32.png</string>
+              </object>
+            </add>
+            <add>
+              <object class="com.install4j.api.beans.ExternalFile">
+                <string>${compiler:sys.install4jHome}/resource/updater_48.png</string>
+              </object>
+            </add>
+            <add>
+              <object class="com.install4j.api.beans.ExternalFile">
+                <string>${compiler:sys.install4jHome}/resource/updater_128.png</string>
+              </object>
+            </add>
+            <add>
+              <object class="com.install4j.api.beans.ExternalFile">
+                <string>${compiler:sys.install4jHome}/resource/updater_256.png</string>
+              </object>
+            </add>
+          </property>
+          <property name="executableDirectory">
+            <object class="java.io.File">
+              <string>.</string>
+            </object>
+          </property>
+          <property name="executableName" type="string">updater</property>
+          <property name="executionMode" type="enum" class="com.install4j.runtime.beans.applications.ExecutionMode" value="CONSOLE" />
+          <property name="useCustomIcon" type="boolean" value="true" />
+          <property name="windowTitle" type="string">${i18n:updater.WindowTitle("${compiler:sys.fullName}")}</property>
+        </serializedBean>
+        <startup>
+          <screen id="523" beanClass="com.install4j.runtime.beans.screens.StartupScreen" rollbackBarrierExitCode="0" />
+        </startup>
+        <screens>
+          <screen name="Welcome" id="463" beanClass="com.install4j.runtime.beans.screens.FormScreen" styleId="7">
+            <serializedBean>
+              <property name="title" type="string">${i18n:updater.WelcomeTitle("${compiler:sys.fullName}")}</property>
+            </serializedBean>
+            <formComponents>
+              <formComponent id="464" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" insetBottom="20" useExternalParametrization="true" externalParametrizationName="Header" externalParametrizationMode="include">
+                <serializedBean>
+                  <property name="hideIfBlank" type="boolean" value="true" />
+                  <property name="labelText" type="string">${i18n:updater.WelcomeInfoText("${compiler:sys.fullName}")}</property>
+                </serializedBean>
+                <visibilityScript>!context.isConsole()</visibilityScript>
+                <externalParametrizationPropertyNames>
+                  <propertyName>labelText</propertyName>
+                </externalParametrizationPropertyNames>
+              </formComponent>
+            </formComponents>
+          </screen>
+          <screen name="Check for update" id="465" beanClass="com.install4j.runtime.beans.screens.FormScreen">
+            <serializedBean>
+              <property name="subTitle" type="string">${i18n:updater.CheckForUpdateSubtitle}</property>
+              <property name="title" type="string">${i18n:updater.CheckForUpdateTitle}</property>
+            </serializedBean>
+            <postActivation>context.getWizardContext().setControlButtonVisible(ControlButtonType.NEXT, false);
+context.getWizardContext().setControlButtonVisible(ControlButtonType.PREVIOUS, false);
+context.goForward(1, true, true);
+</postActivation>
+            <actions>
+              <action id="466" beanClass="com.install4j.runtime.beans.actions.control.SetProgressAction" actionElevationType="none">
+                <serializedBean>
+                  <property name="progressChangeType" type="enum" class="com.install4j.runtime.beans.actions.control.ProgressChangeType" value="SET_INDETERMINATE" />
+                </serializedBean>
+              </action>
+              <action id="467" beanClass="com.install4j.runtime.beans.actions.update.CheckForUpdateAction" actionElevationType="none" failureStrategy="quit">
+                <serializedBean>
+                  <property name="url" type="string">${installer:updatesUrl?:${compiler:sys.updatesUrl}}</property>
+                  <property name="variable" type="string">updateDescriptor</property>
+                </serializedBean>
+              </action>
+              <action id="468" beanClass="com.install4j.runtime.beans.actions.control.SleepAction" actionElevationType="none" />
+              <action name="Update descriptor entry" id="469" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction">
+                <serializedBean>
+                  <property name="script">
+                    <object class="com.install4j.api.beans.ScriptProperty">
+                      <property name="value" type="string">((UpdateDescriptor)context.getVariable("updateDescriptor")).getPossibleUpdateEntry()</property>
+                    </object>
+                  </property>
+                  <property name="variableName" type="string">updateDescriptorEntry</property>
+                </serializedBean>
+              </action>
+              <group name="Update available" id="470" beanClass="com.install4j.runtime.beans.groups.ActionGroup">
+                <serializedBean>
+                  <property name="conditionExpression">
+                    <object class="com.install4j.api.beans.ScriptProperty">
+                      <property name="value" type="string">context.getVariable("updateDescriptorEntry") != null</property>
+                    </object>
+                  </property>
+                </serializedBean>
+                <beans>
+                  <action name="New version" id="471" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction">
+                    <serializedBean>
+                      <property name="script">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <property name="value" type="string">((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getNewVersion()</property>
+                        </object>
+                      </property>
+                      <property name="variableName" type="string">updaterNewVersion</property>
+                    </serializedBean>
+                  </action>
+                  <action name="Download size" id="472" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction">
+                    <serializedBean>
+                      <property name="script">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <property name="value" type="string">((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getFileSizeVerbose()</property>
+                        </object>
+                      </property>
+                      <property name="variableName" type="string">updaterDownloadSize</property>
+                    </serializedBean>
+                  </action>
+                  <action name="Comment" id="473" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction">
+                    <serializedBean>
+                      <property name="script">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <property name="value" type="string">((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getComment()</property>
+                        </object>
+                      </property>
+                      <property name="variableName" type="string">updaterComment</property>
+                    </serializedBean>
+                  </action>
+                  <action name="Download URL" id="474" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction">
+                    <serializedBean>
+                      <property name="script">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <property name="value" type="string">((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getURL().toExternalForm()</property>
+                        </object>
+                      </property>
+                      <property name="variableName" type="string">updaterDownloadUrl</property>
+                    </serializedBean>
+                  </action>
+                  <action name="Archive" id="475" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction">
+                    <serializedBean>
+                      <property name="script">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <property name="value" type="string">((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).isArchive() ? Boolean.TRUE : Boolean.FALSE</property>
+                        </object>
+                      </property>
+                      <property name="variableName" type="string">isArchive</property>
+                    </serializedBean>
+                  </action>
+                  <action name="DMG" id="476" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction">
+                    <serializedBean>
+                      <property name="script">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <property name="value" type="string">((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getFileName().toLowerCase().endsWith(".dmg")</property>
+                        </object>
+                      </property>
+                      <property name="variableName" type="string">isDmg</property>
+                    </serializedBean>
+                  </action>
+                </beans>
+              </group>
+            </actions>
+            <formComponents>
+              <formComponent id="477" beanClass="com.install4j.runtime.beans.formcomponents.ProgressComponent" useExternalParametrization="true" externalParametrizationName="Directory" externalParametrizationMode="include">
+                <serializedBean>
+                  <property name="initialStatusMessage" type="string">${i18n:updater.CheckForUpdateLabel}</property>
+                </serializedBean>
+                <externalParametrizationPropertyNames>
+                  <propertyName>statusVisible</propertyName>
+                  <propertyName>initialStatusMessage</propertyName>
+                </externalParametrizationPropertyNames>
+              </formComponent>
+            </formComponents>
+          </screen>
+          <group name="Up to date" id="478" beanClass="com.install4j.runtime.beans.groups.ScreenGroup">
+            <serializedBean>
+              <property name="conditionExpression">
+                <object class="com.install4j.api.beans.ScriptProperty">
+                  <property name="value" type="string">context.getVariable("updateDescriptorEntry") == null</property>
+                </object>
+              </property>
+            </serializedBean>
+            <beans>
+              <screen name="Up to date notification" id="479" beanClass="com.install4j.runtime.beans.screens.FormScreen" styleId="7" finishScreen="true">
+                <serializedBean>
+                  <property name="title" type="string">${i18n:updater.UpToDateTitle}</property>
+                </serializedBean>
+                <formComponents>
+                  <formComponent id="480" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" insetBottom="20" useExternalParametrization="true" externalParametrizationName="Header" externalParametrizationMode="include">
+                    <serializedBean>
+                      <property name="hideIfBlank" type="boolean" value="true" />
+                      <property name="labelText" type="string">${i18n:updater.UpToDateInfoText("${compiler:sys.fullName}")}</property>
+                    </serializedBean>
+                    <visibilityScript>!context.isConsole()</visibilityScript>
+                    <externalParametrizationPropertyNames>
+                      <propertyName>labelText</propertyName>
+                    </externalParametrizationPropertyNames>
+                  </formComponent>
+                </formComponents>
+              </screen>
+            </beans>
+          </group>
+          <group name="Update available" id="481" beanClass="com.install4j.runtime.beans.groups.ScreenGroup">
+            <serializedBean>
+              <property name="conditionExpression">
+                <object class="com.install4j.api.beans.ScriptProperty">
+                  <property name="value" type="string">context.getVariable("updateDescriptorEntry") != null</property>
+                </object>
+              </property>
+            </serializedBean>
+            <beans>
+              <screen name="New version available" id="482" beanClass="com.install4j.runtime.beans.screens.FormScreen">
+                <serializedBean>
+                  <property name="subTitle" type="string">${i18n:updater.NewVersionAvailableSubtitle("${compiler:sys.fullName}")}</property>
+                  <property name="title" type="string">${i18n:updater.NewVersionAvailableTitle}</property>
+                </serializedBean>
+                <condition>!context.getBooleanVariable("skipNewVersionAvailable")</condition>
+                <formComponents>
+                  <formComponent id="483" beanClass="com.install4j.runtime.beans.formcomponents.KeyValuePairComponent">
+                    <serializedBean>
+                      <property name="labelText" type="string">${i18n:updater.CurrentVersionLabel}</property>
+                      <property name="valueLabelColor">
+                        <object class="java.awt.Color">
+                          <int>128</int>
+                          <int>0</int>
+                          <int>0</int>
+                          <int>255</int>
+                        </object>
+                      </property>
+                      <property name="valueLabelFontStyle" type="enum" class="com.install4j.runtime.beans.formcomponents.FontStyle" value="BOLD" />
+                      <property name="valueLabelFontType" type="enum" class="com.install4j.runtime.beans.formcomponents.FontType" value="DERIVED" />
+                      <property name="valueLabelText" type="string">${installer:sys.version}</property>
+                    </serializedBean>
+                  </formComponent>
+                  <group id="484" beanClass="com.install4j.runtime.beans.groups.HorizontalFormComponentGroup">
+                    <beans>
+                      <formComponent id="485" beanClass="com.install4j.runtime.beans.formcomponents.KeyValuePairComponent">
+                        <serializedBean>
+                          <property name="labelText" type="string">${i18n:updater.NewVersionLabel}</property>
+                          <property name="valueLabelColor">
+                            <object class="java.awt.Color">
+                              <int>0</int>
+                              <int>128</int>
+                              <int>0</int>
+                              <int>255</int>
+                            </object>
+                          </property>
+                          <property name="valueLabelFontStyle" type="enum" class="com.install4j.runtime.beans.formcomponents.FontStyle" value="BOLD" />
+                          <property name="valueLabelFontType" type="enum" class="com.install4j.runtime.beans.formcomponents.FontType" value="DERIVED" />
+                          <property name="valueLabelText" type="string">${installer:updaterNewVersion}</property>
+                        </serializedBean>
+                      </formComponent>
+                      <formComponent id="486" beanClass="com.install4j.runtime.beans.formcomponents.HyperlinkActionLabelComponent" insetLeft="5">
+                        <serializedBean>
+                          <property name="actionScript">
+                            <object class="com.install4j.api.beans.ScriptProperty">
+                              <property name="value" type="string">context.goForward(1, false, false);</property>
+                            </object>
+                          </property>
+                          <property name="hyperlinkText" type="string">${i18n:updater.ShowComments}</property>
+                        </serializedBean>
+                        <visibilityScript> ((String)context.getVariable("updaterComment")).length() &gt; 0</visibilityScript>
+                      </formComponent>
+                    </beans>
+                  </group>
+                  <formComponent id="487" beanClass="com.install4j.runtime.beans.formcomponents.SpacerComponent" />
+                  <formComponent id="488" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent">
+                    <serializedBean>
+                      <property name="labelText" type="string">${i18n:updater.DownloadLocationLabel}</property>
+                    </serializedBean>
+                  </formComponent>
+                  <formComponent id="489" beanClass="com.install4j.runtime.beans.formcomponents.DirectoryChooserComponent">
+                    <serializedBean>
+                      <property name="initialFile" type="string">${installer:sys.downloadsDir}</property>
+                      <property name="labelText" type="string">${i18n:updater.DownloadToLabel}</property>
+                      <property name="manualEntryAllowed" type="boolean" value="false" />
+                      <property name="variableName" type="string">updaterDownloadLocation</property>
+                    </serializedBean>
+                  </formComponent>
+                  <formComponent id="490" beanClass="com.install4j.runtime.beans.formcomponents.KeyValuePairComponent">
+                    <serializedBean>
+                      <property name="labelText" type="string">${i18n:updater.DownloadSizeLabel}</property>
+                      <property name="valueLabelText" type="string">${installer:updaterDownloadSize}</property>
+                    </serializedBean>
+                  </formComponent>
+                </formComponents>
+              </screen>
+              <screen name="Update message" id="491" beanClass="com.install4j.runtime.beans.screens.FormScreen">
+                <serializedBean>
+                  <property name="scrollable" type="boolean" value="false" />
+                  <property name="subTitle" type="string">${i18n:updater.CommentsSubTitle}</property>
+                  <property name="title" type="string">${i18n:updater.CommentsTitle}</property>
+                </serializedBean>
+                <condition>false // This screen is only shown if the user clicks the "Show comments" hyperlink label in the previous screen.
+</condition>
+                <validation>if (context.isConsole()) {
+    context.goBackInHistory(1);
+}
+return true;</validation>
+                <postActivation>WizardContext wizardContext = context.getWizardContext();
+wizardContext.setControlButtonVisible(ControlButtonType.NEXT, false);
+wizardContext.setControlButtonVisible(ControlButtonType.CANCEL, false);</postActivation>
+                <formComponents>
+                  <formComponent id="492" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" useExternalParametrization="true" externalParametrizationName="Header" externalParametrizationMode="include">
+                    <serializedBean>
+                      <property name="hideIfBlank" type="boolean" value="true" />
+                      <property name="labelText" type="string">${i18n:updater.CommentsLabel}</property>
+                    </serializedBean>
+                    <visibilityScript>!context.isConsole()</visibilityScript>
+                    <externalParametrizationPropertyNames>
+                      <propertyName>labelText</propertyName>
+                    </externalParametrizationPropertyNames>
+                  </formComponent>
+                  <formComponent id="493" beanClass="com.install4j.runtime.beans.formcomponents.HtmlDisplayFormComponent" useExternalParametrization="true" externalParametrizationName="HTML display" externalParametrizationMode="include">
+                    <serializedBean>
+                      <property name="displayedText" type="string">${installer:updaterComment}</property>
+                      <property name="fillVertical" type="boolean" value="true" />
+                      <property name="textSource" type="enum" class="com.install4j.runtime.beans.screens.components.TextSource" value="DIRECT" />
+                    </serializedBean>
+                    <externalParametrizationPropertyNames>
+                      <propertyName>textSource</propertyName>
+                      <propertyName>displayedText</propertyName>
+                      <propertyName>displayedTextFile</propertyName>
+                      <propertyName>variableName</propertyName>
+                    </externalParametrizationPropertyNames>
+                  </formComponent>
+                  <formComponent id="494" beanClass="com.install4j.runtime.beans.formcomponents.ConsoleHandlerFormComponent">
+                    <serializedBean>
+                      <property name="consoleScript">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <property name="value" type="string">console.waitForEnter();
+return true;
+</property>
+                        </object>
+                      </property>
+                    </serializedBean>
+                  </formComponent>
+                </formComponents>
+              </screen>
+              <screen name="Download new version" id="495" beanClass="com.install4j.runtime.beans.screens.FormScreen">
+                <serializedBean>
+                  <property name="subTitle" type="string">${i18n:updater.DownloadSubTitle}</property>
+                  <property name="title" type="string">${i18n:updater.DownloadTitle}</property>
+                </serializedBean>
+                <postActivation>context.getWizardContext().setControlButtonVisible(ControlButtonType.NEXT, false);
+context.getWizardContext().setControlButtonVisible(ControlButtonType.PREVIOUS, false);
+context.goForward(1, true, true);
+</postActivation>
+                <actions>
+                  <action name="Download location" id="496" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction">
+                    <serializedBean>
+                      <property name="script">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <property name="value" type="string">context.getVariable("updaterDownloadLocation") + File.separator + ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getFileName()</property>
+                        </object>
+                      </property>
+                      <property name="variableName" type="string">updaterDownloadFile</property>
+                    </serializedBean>
+                  </action>
+                  <action id="497" beanClass="com.install4j.runtime.beans.actions.net.DownloadFileAction" actionElevationType="elevated" failureStrategy="quit">
+                    <serializedBean>
+                      <property name="targetFile">
+                        <object class="java.io.File">
+                          <string>${installer:updaterDownloadFile}</string>
+                        </object>
+                      </property>
+                      <property name="url" type="string">${installer:updaterDownloadUrl}</property>
+                    </serializedBean>
+                  </action>
+                  <action id="498" beanClass="com.install4j.runtime.beans.actions.files.SetModeAction" actionElevationType="elevated">
+                    <serializedBean>
+                      <property name="files" type="array" class="java.io.File" length="1">
+                        <element index="0">
+                          <object class="java.io.File">
+                            <string>${installer:updaterDownloadFile}</string>
+                          </object>
+                        </element>
+                      </property>
+                      <property name="mode" type="string">755</property>
+                    </serializedBean>
+                  </action>
+                </actions>
+                <formComponents>
+                  <formComponent id="499" beanClass="com.install4j.runtime.beans.formcomponents.ProgressComponent" useExternalParametrization="true" externalParametrizationName="Directory" externalParametrizationMode="include">
+                    <externalParametrizationPropertyNames>
+                      <propertyName>statusVisible</propertyName>
+                      <propertyName>initialStatusMessage</propertyName>
+                    </externalParametrizationPropertyNames>
+                  </formComponent>
+                </formComponents>
+              </screen>
+              <group name="Finish" id="500" beanClass="com.install4j.runtime.beans.groups.ScreenGroup">
+                <beans>
+                  <screen name="Finish" id="501" beanClass="com.install4j.runtime.beans.screens.FormScreen" styleId="7" finishScreen="true">
+                    <serializedBean>
+                      <property name="title" type="string">${i18n:updater.FinishTitle}</property>
+                    </serializedBean>
+                    <condition>!(context.getBooleanVariable("isArchive") &amp;&amp; context.getBooleanVariable("isDmg"))</condition>
+                    <actions>
+                      <group name="Execute installer" id="502" beanClass="com.install4j.runtime.beans.groups.ActionGroup">
+                        <serializedBean>
+                          <property name="conditionExpression">
+                            <object class="com.install4j.api.beans.ScriptProperty">
+                              <property name="value" type="string">!context.getBooleanVariable("isArchive") &amp;&amp; ((Integer)context.getVariable("updaterLaunchSelection")).intValue() == 0</property>
+                            </object>
+                          </property>
+                        </serializedBean>
+                        <beans>
+                          <action name="Set installer arguments" id="503" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction">
+                            <serializedBean>
+                              <property name="script">
+                                <object class="com.install4j.api.beans.ScriptProperty">
+                                  <property name="value" type="string">List&lt;String&gt; args = new ArrayList&lt;String&gt;();
+String installationDirectory = context.getInstallationDirectory().getPath();
+if (context.isUnattended()) {
+    args.add("-q");
+    args.add("-wait");
+    args.add("20");
+    ProgressInterface progressInterface = context.getProgressInterface();
+    if (progressInterface.isUnattendedProgressDialog()) {
+        if (progressInterface.isAlertsShown()) {
+            args.add("-alerts");
+        }
+        args.add("-splash");
+        args.add("Installing");
+    }
+} else if (context.isConsole()) {
+    args.add("-c");
+}
+ args.add("-dir");
+ args.add(installationDirectory);
+ 
+ return args.toArray(new String[args.size()]);
+</property>
+                                </object>
+                              </property>
+                              <property name="variableName" type="string">installerArguments</property>
+                            </serializedBean>
+                          </action>
+                          <action id="504" beanClass="com.install4j.runtime.beans.actions.update.ShutdownCallingLauncherAction" actionElevationType="none" />
+                          <action id="505" beanClass="com.install4j.runtime.beans.actions.misc.RunExecutableAction" actionElevationType="elevated" failureStrategy="quit" errorMessage="${i18n:updater.LaunchError}">
+                            <serializedBean>
+                              <property name="arguments" type="array" elementType="string" length="1">
+                                <element index="0">${installer:installerArguments}</element>
+                              </property>
+                              <property name="executable">
+                                <object class="java.io.File">
+                                  <string>${installer:updaterDownloadFile}</string>
+                                </object>
+                              </property>
+                              <property name="workingDirectory">
+                                <object class="java.io.File">
+                                  <string>${installer:updaterDownloadLocation}</string>
+                                </object>
+                              </property>
+                            </serializedBean>
+                          </action>
+                        </beans>
+                      </group>
+                    </actions>
+                    <formComponents>
+                      <formComponent id="506" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" insetBottom="20" useExternalParametrization="true" externalParametrizationName="Header" externalParametrizationMode="include">
+                        <serializedBean>
+                          <property name="hideIfBlank" type="boolean" value="true" />
+                        </serializedBean>
+                        <visibilityScript>!context.isConsole()</visibilityScript>
+                        <externalParametrizationPropertyNames>
+                          <propertyName>labelText</propertyName>
+                        </externalParametrizationPropertyNames>
+                      </formComponent>
+                      <formComponent id="507" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" insetBottom="20" useExternalParametrization="true" externalParametrizationName="Header" externalParametrizationMode="include">
+                        <serializedBean>
+                          <property name="hideIfBlank" type="boolean" value="true" />
+                          <property name="labelText" type="string">${i18n:updater.FinishInfoText("${compiler:sys.fullName}")}</property>
+                        </serializedBean>
+                        <visibilityScript>!context.isConsole()</visibilityScript>
+                        <externalParametrizationPropertyNames>
+                          <propertyName>labelText</propertyName>
+                        </externalParametrizationPropertyNames>
+                      </formComponent>
+                      <formComponent id="508" beanClass="com.install4j.runtime.beans.formcomponents.LabelComponent">
+                        <serializedBean>
+                          <property name="labelText" type="string">${i18n:updater.LaunchUpdaterQuestion}</property>
+                        </serializedBean>
+                      </formComponent>
+                      <formComponent id="509" beanClass="com.install4j.runtime.beans.formcomponents.SpacerComponent">
+                        <serializedBean>
+                          <property name="height" type="int" value="5" />
+                        </serializedBean>
+                      </formComponent>
+                      <formComponent id="510" beanClass="com.install4j.runtime.beans.formcomponents.RadiobuttonsComponent">
+                        <serializedBean>
+                          <property name="radioButtonLabels" type="array" elementType="string" length="2">
+                            <element index="0">${i18n:updater.LaunchUpdaterLabel}</element>
+                            <element index="1">${i18n:updater.DoNotLaunchUpdaterLabel}</element>
+                          </property>
+                          <property name="variableName" type="string">updaterLaunchSelection</property>
+                        </serializedBean>
+                        <visibilityScript>!context.getBooleanVariable("isArchive")</visibilityScript>
+                      </formComponent>
+                      <formComponent id="511" beanClass="com.install4j.runtime.beans.formcomponents.HyperlinkActionLabelComponent">
+                        <serializedBean>
+                          <property name="actionScript">
+                            <object class="com.install4j.api.beans.ScriptProperty">
+                              <property name="value" type="string">Util.showPath((String)context.getVariable("updaterDownloadFile"));</property>
+                            </object>
+                          </property>
+                          <property name="hyperlinkText" type="string">${i18n:updater.OpenContainingFolderLabel}</property>
+                        </serializedBean>
+                        <visibilityScript>!context.isConsole()</visibilityScript>
+                      </formComponent>
+                      <formComponent id="512" beanClass="com.install4j.runtime.beans.formcomponents.ProgressComponent">
+                        <serializedBean>
+                          <property name="detailVisible" type="boolean" value="false" />
+                          <property name="hideInitially" type="boolean" value="true" />
+                        </serializedBean>
+                      </formComponent>
+                    </formComponents>
+                  </screen>
+                  <screen name="Finish DMG Archive" id="513" beanClass="com.install4j.runtime.beans.screens.FormScreen" styleId="7" finishScreen="true">
+                    <serializedBean>
+                      <property name="title" type="string">${i18n:updater.FinishTitle}</property>
+                    </serializedBean>
+                    <condition>context.getBooleanVariable("isArchive") &amp;&amp; context.getBooleanVariable("isDmg")</condition>
+                    <actions>
+                      <group name="Execute installer" id="514" beanClass="com.install4j.runtime.beans.groups.ActionGroup">
+                        <serializedBean>
+                          <property name="conditionExpression">
+                            <object class="com.install4j.api.beans.ScriptProperty">
+                              <property name="value" type="string">context.getBooleanVariable("updaterOpenDmg")</property>
+                            </object>
+                          </property>
+                        </serializedBean>
+                        <beans>
+                          <action id="515" beanClass="com.install4j.runtime.beans.actions.update.ShutdownCallingLauncherAction" actionElevationType="none" />
+                          <action name="Open DMG" id="516" beanClass="com.install4j.runtime.beans.actions.control.RunScriptAction">
+                            <serializedBean>
+                              <property name="script">
+                                <object class="com.install4j.api.beans.ScriptProperty">
+                                  <property name="value" type="string">Util.showPath((String)context.getVariable("updaterDownloadFile"));
+return true;</property>
+                                </object>
+                              </property>
+                            </serializedBean>
+                          </action>
+                        </beans>
+                      </group>
+                    </actions>
+                    <formComponents>
+                      <formComponent id="517" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" insetBottom="20" useExternalParametrization="true" externalParametrizationName="Header" externalParametrizationMode="include">
+                        <serializedBean>
+                          <property name="hideIfBlank" type="boolean" value="true" />
+                        </serializedBean>
+                        <visibilityScript>!context.isConsole()</visibilityScript>
+                        <externalParametrizationPropertyNames>
+                          <propertyName>labelText</propertyName>
+                        </externalParametrizationPropertyNames>
+                      </formComponent>
+                      <formComponent id="518" beanClass="com.install4j.runtime.beans.formcomponents.MultilineLabelComponent" insetBottom="20" useExternalParametrization="true" externalParametrizationName="Header" externalParametrizationMode="include">
+                        <serializedBean>
+                          <property name="hideIfBlank" type="boolean" value="true" />
+                          <property name="labelText" type="string">${i18n:updater.FinishInfoText("${compiler:sys.fullName}")}</property>
+                        </serializedBean>
+                        <visibilityScript>!context.isConsole()</visibilityScript>
+                        <externalParametrizationPropertyNames>
+                          <propertyName>labelText</propertyName>
+                        </externalParametrizationPropertyNames>
+                      </formComponent>
+                      <formComponent id="519" beanClass="com.install4j.runtime.beans.formcomponents.LabelComponent">
+                        <serializedBean>
+                          <property name="labelText" type="string">${i18n:updater.LaunchUpdaterQuestion}</property>
+                        </serializedBean>
+                      </formComponent>
+                      <formComponent id="520" beanClass="com.install4j.runtime.beans.formcomponents.SpacerComponent">
+                        <serializedBean>
+                          <property name="height" type="int" value="5" />
+                        </serializedBean>
+                      </formComponent>
+                      <formComponent id="521" beanClass="com.install4j.runtime.beans.formcomponents.CheckboxComponent">
+                        <serializedBean>
+                          <property name="checkboxText" type="string">${i18n:updater.OpenContainingFolderLabel}</property>
+                          <property name="initiallySelected" type="boolean" value="true" />
+                          <property name="variableName" type="string">updaterOpenDmg</property>
+                        </serializedBean>
+                      </formComponent>
+                      <formComponent id="522" beanClass="com.install4j.runtime.beans.formcomponents.ProgressComponent">
+                        <serializedBean>
+                          <property name="detailVisible" type="boolean" value="false" />
+                          <property name="hideInitially" type="boolean" value="true" />
+                        </serializedBean>
+                      </formComponent>
+                    </formComponents>
+                  </screen>
+                </beans>
+              </group>
+            </beans>
+          </group>
+        </screens>
+      </application>
+      <application name="Background update downloader CLI" id="524" beanClass="com.install4j.runtime.beans.applications.CustomApplication">
+        <serializedBean>
+          <property name="customIconImageFiles">
+            <add>
+              <object class="com.install4j.api.beans.ExternalFile">
+                <string>${compiler:sys.install4jHome}/resource/updater_16.png</string>
+              </object>
+            </add>
+            <add>
+              <object class="com.install4j.api.beans.ExternalFile">
+                <string>${compiler:sys.install4jHome}/resource/updater_32.png</string>
+              </object>
+            </add>
+            <add>
+              <object class="com.install4j.api.beans.ExternalFile">
+                <string>${compiler:sys.install4jHome}/resource/updater_48.png</string>
+              </object>
+            </add>
+            <add>
+              <object class="com.install4j.api.beans.ExternalFile">
+                <string>${compiler:sys.install4jHome}/resource/updater_128.png</string>
+              </object>
+            </add>
+            <add>
+              <object class="com.install4j.api.beans.ExternalFile">
+                <string>${compiler:sys.install4jHome}/resource/updater_256.png</string>
+              </object>
+            </add>
+          </property>
+          <property name="executableName" type="string">bgupdater</property>
+          <property name="useCustomIcon" type="boolean" value="true" />
+          <property name="vmParameters" type="string">-Dapple.awt.UIElement=true</property>
+          <property name="windowTitle" type="string">${compiler:sys.fullName}</property>
+        </serializedBean>
+        <startup>
+          <screen id="525" beanClass="com.install4j.runtime.beans.screens.StartupScreen" rollbackBarrierExitCode="0">
+            <actions>
+              <action name="Check prerequisites" id="526" beanClass="com.install4j.runtime.beans.actions.control.RunScriptAction" rollbackBarrierExitCode="0" failureStrategy="quit">
+                <serializedBean>
+                  <property name="script">
+                    <object class="com.install4j.api.beans.ScriptProperty">
+                      <property name="value" type="string">import java.nio.file.*;
+
+Path dir = context.getInstallationDirectory().toPath();
+// quit if the current installation is on a read only file system, for example a disk image on macOS
+// or if the directory is not writable on Linux/Unix.
+// If there is no "Request privileges" action in the installer, the condition should also
+// check Files.isWritable(dir)
+return !Files.getFileStore(dir).isReadOnly() &amp;&amp; ((Util.isWindows() &amp;&amp; !Util.isArchive()) || Util.isMacOS() || (Util.isLinux() &amp;&amp; !Util.isArchive()));
+</property>
+                    </object>
+                  </property>
+                </serializedBean>
+              </action>
+              <action id="527" beanClass="com.install4j.runtime.beans.actions.update.CheckForUpdateAction" actionElevationType="none" failureStrategy="quit">
+                <serializedBean>
+                  <property name="url" type="string">${compiler:sys.updatesUrl}</property>
+                  <property name="variable" type="string">updateDescriptor</property>
+                </serializedBean>
+              </action>
+              <action name="Update descriptor entry" id="528" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction" failureStrategy="quit">
+                <serializedBean>
+                  <property name="failIfNull" type="boolean" value="true" />
+                  <property name="script">
+                    <object class="com.install4j.api.beans.ScriptProperty">
+                      <property name="value" type="string">UpdateDescriptorEntry entry = ((UpdateDescriptor)context.getVariable("updateDescriptor")).getPossibleUpdateEntry();
+
+if (entry == null) {
+    return null;
+} else if (entry.isArchive() &amp;&amp; !entry.isSingleBundle()) {
+    // only installers and single bundle archives on macOS are supported
+    return null;
+} else if (entry.isDownloaded()) {
+    // update has been downloaded already
+    return null;
+} else {
+    return entry;
+}</property>
+                    </object>
+                  </property>
+                  <property name="variableName" type="string">updateDescriptorEntry</property>
+                </serializedBean>
+              </action>
+              <group name="Update available" id="529" beanClass="com.install4j.runtime.beans.groups.ActionGroup">
+                <serializedBean>
+                  <property name="conditionExpression">
+                    <object class="com.install4j.api.beans.ScriptProperty">
+                      <property name="value" type="string">context.getVariable("updateDescriptorEntry") != null</property>
+                    </object>
+                  </property>
+                </serializedBean>
+                <beans>
+                  <action name="New version" id="530" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction">
+                    <serializedBean>
+                      <property name="script">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <property name="value" type="string">((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getNewVersion()</property>
+                        </object>
+                      </property>
+                      <property name="variableName" type="string">updaterNewVersion</property>
+                    </serializedBean>
+                  </action>
+                  <action name="Download URL" id="531" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction">
+                    <serializedBean>
+                      <property name="script">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <property name="value" type="string">((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getURL().toExternalForm()</property>
+                        </object>
+                      </property>
+                      <property name="variableName" type="string">updaterDownloadUrl</property>
+                    </serializedBean>
+                  </action>
+                  <action name="Download location" id="532" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction">
+                    <serializedBean>
+                      <property name="script">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <property name="value" type="string">context.getVariable("sys.updateStorageDir") + File.separator + ((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).getFileName()</property>
+                        </object>
+                      </property>
+                      <property name="variableName" type="string">updaterDownloadFile</property>
+                    </serializedBean>
+                  </action>
+                  <action id="533" beanClass="com.install4j.runtime.beans.actions.net.DownloadFileAction" failureStrategy="quit">
+                    <serializedBean>
+                      <property name="targetFile">
+                        <object class="java.io.File">
+                          <string>${installer:updaterDownloadFile}</string>
+                        </object>
+                      </property>
+                      <property name="url" type="string">${installer:updaterDownloadUrl}</property>
+                    </serializedBean>
+                  </action>
+                  <group name="Installer" id="534" beanClass="com.install4j.runtime.beans.groups.ActionGroup">
+                    <serializedBean>
+                      <property name="conditionExpression">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <property name="value" type="string">!((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).isArchive()</property>
+                        </object>
+                      </property>
+                    </serializedBean>
+                    <beans>
+                      <action id="535" beanClass="com.install4j.runtime.beans.actions.files.SetModeAction" actionElevationType="elevated">
+                        <serializedBean>
+                          <property name="files" type="array" class="java.io.File" length="1">
+                            <element index="0">
+                              <object class="java.io.File">
+                                <string>${installer:updaterDownloadFile}</string>
+                              </object>
+                            </element>
+                          </property>
+                          <property name="mode" type="string">755</property>
+                        </serializedBean>
+                      </action>
+                      <action id="536" beanClass="com.install4j.runtime.beans.actions.update.ScheduleUpdateAction" actionElevationType="none" failureStrategy="quit">
+                        <serializedBean>
+                          <property name="installerFile">
+                            <object class="java.io.File">
+                              <string>${installer:updaterDownloadFile}</string>
+                            </object>
+                          </property>
+                          <property name="version" type="string">${installer:updaterNewVersion}</property>
+                        </serializedBean>
+                      </action>
+                    </beans>
+                  </group>
+                  <group name="Single Bundle Archive" id="537" beanClass="com.install4j.runtime.beans.groups.ActionGroup">
+                    <serializedBean>
+                      <property name="conditionExpression">
+                        <object class="com.install4j.api.beans.ScriptProperty">
+                          <property name="value" type="string">((UpdateDescriptorEntry)context.getVariable("updateDescriptorEntry")).isArchive()</property>
+                        </object>
+                      </property>
+                    </serializedBean>
+                    <beans>
+                      <action name="Staging Directory" id="538" beanClass="com.install4j.runtime.beans.actions.control.SetVariableAction">
+                        <serializedBean>
+                          <property name="script">
+                            <object class="com.install4j.api.beans.ScriptProperty">
+                              <property name="value" type="string">String dirName = context.getVariable("updaterDownloadFile") + "_dir";
+new File(dirName).mkdirs();
+return dirName;</property>
+                            </object>
+                          </property>
+                          <property name="variableName" type="string">updaterStagingDir</property>
+                        </serializedBean>
+                      </action>
+                      <action name="Delete Staging Directory" id="539" beanClass="com.install4j.runtime.beans.actions.files.DeleteFileAction" actionElevationType="elevated" rollbackBarrierExitCode="0">
+                        <serializedBean>
+                          <property name="backupForRollback" type="boolean" value="false" />
+                          <property name="files" type="array" class="java.io.File" length="1">
+                            <element index="0">
+                              <object class="java.io.File">
+                                <string>${installer:updaterStagingDir}</string>
+                              </object>
+                            </element>
+                          </property>
+                          <property name="recursive" type="boolean" value="true" />
+                        </serializedBean>
+                        <condition>new File((String)context.getVariable("updaterStagingDir")).exists()</condition>
+                      </action>
+                      <action id="540" beanClass="com.install4j.runtime.beans.actions.files.ExtractDmgFileAction" actionElevationType="elevated" rollbackBarrierExitCode="0" failureStrategy="quit">
+                        <serializedBean>
+                          <property name="archiveFile">
+                            <object class="java.io.File">
+                              <string>${installer:updaterDownloadFile}</string>
+                            </object>
+                          </property>
+                          <property name="destinationDirectory">
+                            <object class="java.io.File">
+                              <string>${installer:updaterStagingDir}</string>
+                            </object>
+                          </property>
+                          <property name="fileFilter">
+                            <object class="com.install4j.api.beans.ScriptProperty">
+                              <property name="value" type="string">// only extract app bundle, no other top level files
+
+import com.install4j.api.unix.UnixFileSystem;
+
+File realFile = new File(dmgMountPoint, file.getPath());
+
+return file.getParent() != null || (file.getName().endsWith(".app") &amp;&amp; realFile.isDirectory() &amp;&amp; !UnixFileSystem.getFileInformation(realFile).isLink());</property>
+                            </object>
+                          </property>
+                        </serializedBean>
+                        <condition>((String)context.getVariable("updaterDownloadFile")).endsWith(".dmg")</condition>
+                      </action>
+                      <action id="541" beanClass="com.install4j.runtime.beans.actions.files.ExtractTarFileAction" actionElevationType="elevated" rollbackBarrierExitCode="0" failureStrategy="quit">
+                        <serializedBean>
+                          <property name="archiveFile">
+                            <object class="java.io.File">
+                              <string>${installer:updaterDownloadFile}</string>
+                            </object>
+                          </property>
+                          <property name="destinationDirectory">
+                            <object class="java.io.File">
+                              <string>${installer:updaterStagingDir}</string>
+                            </object>
+                          </property>
+                          <property name="fileFilter">
+                            <object class="com.install4j.api.beans.ScriptProperty">
+                              <property name="value" type="string">// only extract app bundle, no other top level files
+file.getParent() != null || (file.getName().endsWith(".app") &amp;&amp; directory)</property>
+                            </object>
+                          </property>
+                        </serializedBean>
+                        <condition>!((String)context.getVariable("updaterDownloadFile")).endsWith(".dmg")</condition>
+                      </action>
+                      <action id="542" beanClass="com.install4j.runtime.beans.actions.update.ScheduleUpdateAction" actionElevationType="none" failureStrategy="quit">
+                        <serializedBean>
+                          <property name="installerFile">
+                            <object class="java.io.File">
+                              <string>${installer:updaterStagingDir}</string>
+                            </object>
+                          </property>
+                          <property name="version" type="string">${installer:updaterNewVersion}</property>
+                        </serializedBean>
+                      </action>
+                      <action name="Delete archive" id="543" beanClass="com.install4j.runtime.beans.actions.files.DeleteFileAction" actionElevationType="elevated" rollbackBarrierExitCode="0">
+                        <serializedBean>
+                          <property name="backupForRollback" type="boolean" value="false" />
+                          <property name="files" type="array" class="java.io.File" length="1">
+                            <element index="0">
+                              <object class="java.io.File">
+                                <string>${installer:updaterDownloadFile}</string>
+                              </object>
+                            </element>
+                          </property>
+                        </serializedBean>
+                      </action>
+                    </beans>
+                  </group>
+                </beans>
+              </group>
+            </actions>
+          </screen>
+        </startup>
+      </application>
     </applications>
     <styles defaultStyleId="1">
       <style name="Standard" id="1" beanClass="com.install4j.runtime.beans.styles.FormStyle">