diff --git a/src/main/java/net/minecraftforge/installer/DownloadUtils.java b/src/main/java/net/minecraftforge/installer/DownloadUtils.java index ca1150f..d96b437 100644 --- a/src/main/java/net/minecraftforge/installer/DownloadUtils.java +++ b/src/main/java/net/minecraftforge/installer/DownloadUtils.java @@ -43,6 +43,7 @@ public class DownloadUtils { public static final String MANIFEST_URL = "https://piston-meta.mojang.com/mc/game/version_manifest_v2.json"; + public static final String SERVER_STARTER_JAR = "https://github.com/NeoForged/serverstarterjar/releases/latest/download/server.jar"; public static boolean OFFLINE_MODE = false; diff --git a/src/main/java/net/minecraftforge/installer/SimpleInstaller.java b/src/main/java/net/minecraftforge/installer/SimpleInstaller.java index 140b09d..3415b74 100644 --- a/src/main/java/net/minecraftforge/installer/SimpleInstaller.java +++ b/src/main/java/net/minecraftforge/installer/SimpleInstaller.java @@ -40,6 +40,7 @@ import net.minecraftforge.installer.actions.Actions; import net.minecraftforge.installer.actions.FatInstallerAction; import net.minecraftforge.installer.actions.ProgressCallback; +import net.minecraftforge.installer.actions.ServerInstall; import net.minecraftforge.installer.json.InstallV1; import net.minecraftforge.installer.json.Util; import net.minecraftforge.installer.ui.InstallerPanel; @@ -84,6 +85,8 @@ public static void main(String[] args) throws IOException, URISyntaxException { OptionSpec clientInstallOption = parser.acceptsAll(Arrays.asList("installClient", "install-client"), "Install a client to the specified directory, defaulting to the MC installation directory").withOptionalArg().ofType(File.class).defaultsTo(getMCDir()); OptionSpec serverInstallOption = parser.acceptsAll(Arrays.asList("installServer", "install-server"), "Install a server to the current directory").withOptionalArg().ofType(File.class).defaultsTo(new File(".")); + OptionSpec serverStarterOption = parser.acceptsAll(Arrays.asList("server-starter", "server.jar", "server-jar"), "Download the server starter jar for arg-free executable launches"); + OptionSpec fatInstallerOption = parser.acceptsAll(Arrays.asList("fat-installer", "fat", "generate-fat"), "Generate a fat installer jar").withOptionalArg().ofType(File.class).defaultsTo(new File(installer.getParent(), installer.getName().replace(".jar", "-fat.jar"))); OptionSpec fatIncludeMC = parser.acceptsAll(Arrays.asList("fat-include-minecraft"), "Include the Minecraft client / server jar in the fat installer").availableIf(fatInstallerOption); OptionSpec fatIncludeMCLibs = parser.acceptsAll(Arrays.asList("fat-include-minecraft-libs"), "Include the Minecraft libraries in the fat installer").availableIf(fatInstallerOption); @@ -133,6 +136,7 @@ public static void main(String[] args) throws IOException, URISyntaxException { if (optionSet.has(serverInstallOption)) { action = Actions.SERVER; target = optionSet.valueOf(serverInstallOption); + ServerInstall.serverStarterJar = optionSet.has(serverStarterOption); } else if (optionSet.has(clientInstallOption)) { action = Actions.CLIENT; target = optionSet.valueOf(clientInstallOption); diff --git a/src/main/java/net/minecraftforge/installer/actions/FatInstallerAction.java b/src/main/java/net/minecraftforge/installer/actions/FatInstallerAction.java index 69a57f7..285e8fe 100644 --- a/src/main/java/net/minecraftforge/installer/actions/FatInstallerAction.java +++ b/src/main/java/net/minecraftforge/installer/actions/FatInstallerAction.java @@ -86,6 +86,9 @@ public boolean run(File target, Predicate optionals, File installer) thr } if (OPTIONS.contains(Options.INSTALLER_LIBS)) { libraries.addAll(Arrays.asList(processors.getLibraries())); + + monitor.stage("Downloading server starter jar"); + writeFromUrl(out, "serverstarter.jar", DownloadUtils.SERVER_STARTER_JAR); } Set duplicates = new HashSet<>(); diff --git a/src/main/java/net/minecraftforge/installer/actions/ServerInstall.java b/src/main/java/net/minecraftforge/installer/actions/ServerInstall.java index 485fbf7..7baf51b 100644 --- a/src/main/java/net/minecraftforge/installer/actions/ServerInstall.java +++ b/src/main/java/net/minecraftforge/installer/actions/ServerInstall.java @@ -21,6 +21,8 @@ import java.util.List; import java.util.Map; import java.util.function.Predicate; + +import net.minecraftforge.installer.DownloadUtils; import net.minecraftforge.installer.SimpleInstaller; import net.minecraftforge.installer.json.Artifact; import net.minecraftforge.installer.json.InstallV1; @@ -30,7 +32,9 @@ import net.minecraftforge.installer.ui.TranslatedMessage; public class ServerInstall extends Action { - private List grabbed = new ArrayList<>(); + public static boolean serverStarterJar; + + private final List grabbed = new ArrayList<>(); public ServerInstall(InstallV1 profile, ProgressCallback monitor) { super(profile, monitor, false); @@ -105,9 +109,11 @@ public boolean run(File target, Predicate optionals, File installer) thr if (!processors.process(librariesDir, serverTarget, target, installer)) return false; - // TODO: Optionals - //if (!OptionalLibrary.saveModListJson(librariesDir, new File(target, "mods/mod_list.json"), VersionInfo.getOptionals(), optionals)) - // return false; + if (serverStarterJar) { + monitor.downloader(DownloadUtils.SERVER_STARTER_JAR) + .localPath("serverstarter.jar") + .download(new File(target, "server.jar")); + } return true; } diff --git a/src/main/java/net/minecraftforge/installer/ui/InstallerPanel.java b/src/main/java/net/minecraftforge/installer/ui/InstallerPanel.java index bb68840..c1fcd29 100644 --- a/src/main/java/net/minecraftforge/installer/ui/InstallerPanel.java +++ b/src/main/java/net/minecraftforge/installer/ui/InstallerPanel.java @@ -41,6 +41,7 @@ import net.minecraftforge.installer.actions.Actions; import net.minecraftforge.installer.actions.FatInstallerAction; import net.minecraftforge.installer.actions.ProgressCallback; +import net.minecraftforge.installer.actions.ServerInstall; import net.minecraftforge.installer.actions.TargetValidator; import net.minecraftforge.installer.json.InstallV1; import net.minecraftforge.installer.json.OptionalLibrary; @@ -63,8 +64,10 @@ public class InstallerPanel extends JPanel { private final AtomicReference action = new AtomicReference<>(Actions.CLIENT); private JPanel fileEntryPanel; private JPanel fatInstallerOptionsPanel; + private JPanel serverOptionsPanel; private JCheckBox fatIncludeMC, fatIncludeMCLibs, fatIncludeInstallerLibs, fatOffline; + private JCheckBox serverStarterJar; private List optionals = new ArrayList<>(); private Map> actions = new HashMap<>(); @@ -221,10 +224,19 @@ public InstallerPanel(File targetDir, InstallV1 profile, File installer) { this.remove(fileEntryPanel); this.add(fatInstallerOptionsPanel); } else { - this.add(fileEntryPanel); + if (!Arrays.asList(this.getComponents()).contains(fileEntryPanel)) { + this.add(fileEntryPanel); + } this.remove(fatInstallerOptionsPanel); } }); + } else if (action == Actions.SERVER) { + radioButton.addChangeListener(e -> { + if (radioButton.isSelected() != serverStarterJar.isSelected()) { + serverStarterJar.setVisible(radioButton.isSelected()); + revalidate(); + } + }); } } choicePanel.setAlignmentX(CENTER_ALIGNMENT); @@ -256,6 +268,14 @@ public InstallerPanel(File targetDir, InstallV1 profile, File installer) { infoLabel.setForeground(Color.RED); infoLabel.setVisible(false); + serverOptionsPanel = centreAlignedPanel(); + serverStarterJar = TRANSLATIONS.checkBox("installer.action.install.server.starterjar"); + serverStarterJar.setMargin(new Insets(3, 0, 3, 0)); + serverStarterJar.setVisible(false); + TRANSLATIONS.setTooltip(serverStarterJar, "installer.action.install.server.starterjar.tooltip"); + serverOptionsPanel.add(serverStarterJar); + this.add(serverOptionsPanel); + fileEntryPanel = new JPanel(); fileEntryPanel.setLayout(new BoxLayout(fileEntryPanel, BoxLayout.Y_AXIS)); fileEntryPanel.add(infoLabel); @@ -265,10 +285,7 @@ public InstallerPanel(File targetDir, InstallV1 profile, File installer) { fileEntryPanel.setAlignmentY(TOP_ALIGNMENT); this.add(fileEntryPanel); - fatInstallerOptionsPanel = new JPanel(); - fatInstallerOptionsPanel.setLayout(new BoxLayout(fatInstallerOptionsPanel, BoxLayout.Y_AXIS)); - fatInstallerOptionsPanel.setAlignmentX(CENTER_ALIGNMENT); - fatInstallerOptionsPanel.setAlignmentY(TOP_ALIGNMENT); + fatInstallerOptionsPanel = centreAlignedPanel(); this.fatIncludeMC = TRANSLATIONS.checkBox("installer.fat.includemc"); TRANSLATIONS.setTooltip(fatIncludeMC, "installer.fat.includemc.tooltip"); @@ -300,6 +317,14 @@ public InstallerPanel(File targetDir, InstallV1 profile, File installer) { updateFilePath(); } + private JPanel centreAlignedPanel() { + JPanel panel = new JPanel(); + panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS)); + panel.setAlignmentX(CENTER_ALIGNMENT); + panel.setAlignmentY(TOP_ALIGNMENT); + return panel; + } + private void updateFilePath() { try { targetDir = targetDir.getCanonicalFile(); @@ -388,6 +413,8 @@ public void run(ProgressCallback monitor) { FatInstallerAction.OPTIONS.add(FatInstallerAction.Options.INSTALLER_LIBS); } targetDir = new File(installer.getParent(), installer.getName().replace(".jar", "-fat.jar")); + } else if (action.get() == Actions.SERVER) { + ServerInstall.serverStarterJar = serverStarterJar.isSelected(); } ProgressFrame prog = new ProgressFrame(monitor, Thread.currentThread()::interrupt, "installer.frame.installing", profile.getProfile(), profile.getVersion()); diff --git a/src/main/resources/neoforged/installer.xml b/src/main/resources/neoforged/installer.xml index 04162ee..dc90894 100644 --- a/src/main/resources/neoforged/installer.xml +++ b/src/main/resources/neoforged/installer.xml @@ -19,6 +19,9 @@ Successfully downloaded minecraft server and installed %s Successfully downloaded minecraft server, downloaded %d libraries and installed %s + Server starter jar + Generate a server.jar file that can be used to launch the server as an executable, as an alternative to the launch scripts + Create fat installer Create an installer that packs downloaded libs, for eventual offline use Successfully created fat installer for version %s