/* * SK's Minecraft Launcher * Copyright (C) 2010-2014 Albert Pham <http://www.sk89q.com> and contributors * Please see LICENSE.txt for license information. */ package com.skcraft.launcher.creator.controller; import com.google.common.base.Optional; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import com.google.common.util.concurrent.ListeningExecutorService; import com.skcraft.concurrency.Deferred; import com.skcraft.concurrency.Deferreds; import com.skcraft.concurrency.SettableProgress; import com.skcraft.launcher.InstanceList; import com.skcraft.launcher.Launcher; import com.skcraft.launcher.auth.OfflineSession; import com.skcraft.launcher.auth.Session; import com.skcraft.launcher.builder.BuilderConfig; import com.skcraft.launcher.builder.FnPatternList; import com.skcraft.launcher.creator.Creator; import com.skcraft.launcher.creator.model.creator.*; import com.skcraft.launcher.creator.controller.task.*; import com.skcraft.launcher.creator.dialog.*; import com.skcraft.launcher.creator.dialog.BuildDialog.BuildOptions; import com.skcraft.launcher.creator.dialog.DeployServerDialog.DeployOptions; import com.skcraft.launcher.creator.model.swing.PackTableModel; import com.skcraft.launcher.creator.server.TestServer; import com.skcraft.launcher.creator.server.TestServerBuilder; import com.skcraft.launcher.creator.swing.PackDirectoryFilter; import com.skcraft.launcher.dialog.ConfigurationDialog; import com.skcraft.launcher.dialog.ConsoleFrame; import com.skcraft.launcher.dialog.LoginDialog; import com.skcraft.launcher.dialog.ProgressDialog; import com.skcraft.launcher.model.modpack.LaunchModifier; import com.skcraft.launcher.persistence.Persistence; import com.skcraft.launcher.swing.PopupMouseAdapter; import com.skcraft.launcher.swing.SwingHelper; import com.skcraft.launcher.util.MorePaths; import com.skcraft.launcher.util.SwingExecutor; import lombok.Getter; import javax.swing.*; import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; import java.io.IOException; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Date; import java.util.List; import java.util.regex.Pattern; public class PackManagerController { private static final DateFormat VERSION_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss"); private static final Pattern FILENAME_SANITIZE = Pattern.compile("[^a-z0-9_\\-\\.]+"); @Getter private final File workspaceDir; @Getter private final Creator creator; @Getter private final File workspaceFile; @Getter private final File dataDir; @Getter private final File distDir; @Getter private final File launcherDir; @Getter private File webRoot; @Getter private Workspace workspace; @Getter private final Launcher launcher; @Getter private final ListeningExecutorService executor; @Getter private final TestServer testServer; private File lastServerDestDir; private final PackManagerFrame frame; private PackTableModel packTableModel; public PackManagerController(PackManagerFrame frame, File workspaceDir, Creator creator) throws IOException { this.workspaceDir = workspaceDir; this.creator = creator; this.dataDir = Workspace.getDataDir(workspaceDir); workspaceFile = Workspace.getWorkspaceFile(workspaceDir); this.distDir = new File(workspaceDir, "_upload"); launcherDir = new File(dataDir, "staging/launcher"); File launcherConfigDir = new File(creator.getDataDir(), "launcher"); this.webRoot = new File(dataDir, "staging/www"); launcherDir.mkdirs(); launcherConfigDir.mkdirs(); webRoot.mkdirs(); this.launcher = new Launcher(launcherDir, launcherConfigDir); this.executor = launcher.getExecutor(); this.frame = frame; TestServerBuilder builder = new TestServerBuilder(); builder.setBaseDir(webRoot); builder.setPort(0); testServer = builder.build(); } public void show() { frame.setVisible(true); frame.setTitle("Modpack Creator - [" + workspaceDir.getAbsolutePath() + "]"); initListeners(); loadWorkspace(); Deferreds.makeDeferred(executor.submit(() -> { startServer(); return null; })) .handle( result -> { }, (ex) -> SwingHelper.showErrorDialog(frame, "Failed to start a local web server. You will be unable to test modpacks.", "Error", ex) ); } private void startServer() throws Exception { testServer.start(); launcher.getProperties().setProperty("newsUrl", "http://localhost:" + testServer.getLocalPort() + "/news.html"); launcher.getProperties().setProperty("packageListUrl", "http://localhost:" + testServer.getLocalPort() + "/packages.json"); launcher.getProperties().setProperty("selfUpdateUrl", "http://localhost:" + testServer.getLocalPort() + "/latest.json"); } private void loadWorkspace() { PackLoader loader = new PackLoader(); SettableProgress progress = new SettableProgress("Loading workspace...", -1); Deferred<?> deferred = Deferreds.makeDeferred(executor.submit(() -> { Workspace workspace = Persistence.load(workspaceFile, Workspace.class); workspace.setDirectory(workspaceDir); workspace.load(); if (!workspaceFile.exists()) { Persistence.commitAndForget(workspace); } this.workspace = workspace; return workspace; })) .thenTap(() -> progress.observe(loader)) .thenApply(loader) .thenApplyAsync(packs -> { JTable table = frame.getPackTable(); packTableModel = new PackTableModel(packs); table.setModel(packTableModel); packTableModel.fireTableDataChanged(); table.getRowSorter().toggleSortOrder(1); if (packTableModel.getRowCount() > 0) { table.addRowSelectionInterval(0, 0); } return packs; }, SwingExecutor.INSTANCE); ProgressDialog.showProgress(frame, deferred, progress, "Loading workspace...", "Loading workspace..."); SwingHelper.addErrorDialogCallback(frame, deferred); } private boolean checkPackLoaded(Pack pack) { if (pack.isLoaded()) { return true; } else { SwingHelper.showErrorDialog(frame, "The selected pack could not be loaded. You will have to remove it from the workspace or change its location.", "Error"); return false; } } public Optional<Pack> getPackFromIndex(int selectedIndex, boolean requireLoaded) { if (selectedIndex >= 0) { Pack pack = workspace.getPacks().get(selectedIndex); if (pack != null && (!requireLoaded || checkPackLoaded(pack))) { return Optional.fromNullable(pack); } } return Optional.absent(); } public Optional<Pack> getSelectedPack(boolean requireLoaded) { JTable table = frame.getPackTable(); int selectedIndex = table.getSelectedRow(); if (selectedIndex >= 0) { selectedIndex = table.convertRowIndexToModel(selectedIndex); Pack pack = workspace.getPacks().get(selectedIndex); if (pack != null && (!requireLoaded || checkPackLoaded(pack))) { return Optional.fromNullable(pack); } } SwingHelper.showErrorDialog(frame, "Please select a modpack from the list.", "Error"); return Optional.absent(); } public boolean writeWorkspace() { try { Persistence.commit(workspace); return true; } catch (IOException e) { SwingHelper.showErrorDialog(frame, "Failed to write the current state of the workspace to disk. " + "Any recent changes to the list of modpacks may not appear in the workspace on the next load.", "Error", e); return false; } } public boolean writeBuilderConfig(Pack pack, BuilderConfig config) { try { Persistence.write(pack.getConfigFile(), config, Persistence.L2F_LIST_PRETTY_PRINTER); return true; } catch (IOException e) { SwingHelper.showErrorDialog(frame, "Failed to write modpack.json to disk. Aborting.", "Error", e); return false; } } private boolean isOfflineEnabled() { CreatorConfig config = creator.getConfig(); if (config.isOfflineEnabled()) { return true; } else { Session session = LoginDialog.showLoginRequest(frame, launcher); if (session != null) { config.setOfflineEnabled(true); Persistence.commitAndForget(config); return true; } else { return false; } } } public boolean canAddPackDir(File dir) { try { if (dir.exists() && !dir.isDirectory()) { SwingHelper.showErrorDialog(frame, "The selected path is a file that already exists. It must be a directory.", "Error"); return false; } else if (dir.getCanonicalPath().equals(workspaceDir.getCanonicalPath())) { SwingHelper.showErrorDialog(frame, "You cannot choose the workspace directory.", "Error"); return false; } else if (workspace.hasPack(dir)) { SwingHelper.showErrorDialog(frame, "There is already a modpack in this workspace that uses that directory.", "Error"); return false; } else { return true; } } catch (IOException e) { SwingHelper.showErrorDialog(frame, "An unexpected error occurred while checking if the modpack being added can be added.", "Error", e); return false; } } public boolean addPackToWorkspace(Pack pack) { pack.load(); try { File base = workspaceDir; File child = pack.getDirectory(); if (MorePaths.isSubDirectory(base, child)) { pack.setLocation(MorePaths.relativize(base, child)); } } catch (IOException e) { SwingHelper.showErrorDialog(frame, "An unexpected error occurred that could have been caused by the removal " + "of the workspace or modpack directory.", "Error", e); return false; } List<Pack> packs = workspace.getPacks(); pack.setWorkspace(workspace); packs.add(pack); packTableModel.fireTableRowsInserted(packs.size() - 1, packs.size() - 1); writeWorkspace(); return true; } public void updatePackInWorkspace(Pack pack) { List<Pack> packs = workspace.getPacks(); pack.load(); int index = packs.indexOf(pack); if (index >= 0) { packTableModel.fireTableRowsUpdated(index, index); } writeWorkspace(); } public boolean removePackFromWorkspace(Pack pack) { if (workspace.getPacks().remove(pack)) { packTableModel.fireTableDataChanged(); writeWorkspace(); return true; } else { return false; } } private void addDefaultConfig(BuilderConfig config) { LaunchModifier launchModifier = new LaunchModifier(); launchModifier.setFlags(ImmutableList.of("-Dfml.ignoreInvalidMinecraftCertificates=true")); config.setLaunchModifier(launchModifier); FnPatternList userFiles = new FnPatternList(); userFiles.setInclude(Lists.newArrayList("options.txt", "optionsshaders.txt")); userFiles.setExclude(Lists.<String>newArrayList()); config.setUserFiles(userFiles); } private void initListeners() { frame.addWindowListener(new WindowAdapter() { @Override public void windowClosed(WindowEvent e) { try { testServer.stop(); } catch (Exception ignored) { } System.exit(0); // TODO: Proper shutdown } }); frame.getPackTable().addMouseListener(new MouseAdapter() { public void mousePressed(MouseEvent e) { if (e.getClickCount() == 2) { JTable table = (JTable) e.getSource(); Point point = e.getPoint(); int selectedIndex = table.rowAtPoint(point); if (selectedIndex >= 0) { selectedIndex = table.convertRowIndexToModel(selectedIndex); Optional<Pack> optional = getPackFromIndex(selectedIndex, true); if (optional.isPresent()) { if (e.isControlDown()) { SwingHelper.browseDir(optional.get().getDirectory(), frame); } else { startTest(optional.get(), false); } } } } } }); frame.getPackTable().addMouseListener(new PopupMouseAdapter() { @Override protected void showPopup(MouseEvent e) { JTable table = (JTable) e.getSource(); Point point = e.getPoint(); int selectedIndex = table.rowAtPoint(point); if (selectedIndex >= 0) { table.setRowSelectionInterval(selectedIndex, selectedIndex); Optional<Pack> optional = getSelectedPack(false); if (optional.isPresent()) { popupPackMenu(e.getComponent(), e.getX(), e.getY(), optional.get()); } } } }); frame.getNewPackMenuItem().addActionListener(event -> tryAddPackViaDialog()); frame.getNewPackAtLocationMenuItem().addActionListener(e -> tryAddPackViaDirectory(true)); frame.getImportPackMenuItem().addActionListener(event -> tryAddPackViaDirectory(false)); frame.getRemovePackItem().addActionListener(e -> { Optional<Pack> optional = getSelectedPack(false); if (optional.isPresent()) { Pack pack = optional.get(); if (!pack.isLoaded() || SwingHelper.confirmDialog(frame, "Are you sure that you want to remove this modpack? No files will be deleted " + "and you can later re-import the modpack using 'Add Existing'.", "Confirm")) { removePackFromWorkspace(pack); } } }); frame.getDeletePackItem().addActionListener(e -> { Optional<Pack> optional = getSelectedPack(false); if (optional.isPresent()) { Pack pack = optional.get(); String input = JOptionPane.showInputDialog( frame, "Are you sure that you want to delete '" + pack.getDirectory().getAbsolutePath() + "'? " + "If yes, type 'delete' below.", "Confirm", JOptionPane.WARNING_MESSAGE); if (input != null && input.replaceAll("'", "").equalsIgnoreCase("delete")) { removePackFromWorkspace(pack); DirectoryDeleter deleter = new DirectoryDeleter(pack.getDirectory()); Deferred<?> deferred = Deferreds.makeDeferred(executor.submit(deleter), executor); ProgressDialog.showProgress(frame, deferred, deleter, "Deleting modpack...", "Deleting modpack..."); SwingHelper.addErrorDialogCallback(frame, deferred); } else if (input != null) { SwingHelper.showMessageDialog(frame, "You did not enter the correct word. Nothing was deleted.", "Failure", null, JOptionPane.INFORMATION_MESSAGE); } } }); frame.getChangePackLocationMenuItem().addActionListener(e -> { Optional<Pack> optional = getSelectedPack(false); if (optional.isPresent()) { Pack pack = optional.get(); JFileChooser chooser = new JFileChooser(); chooser.setDialogTitle("Choose New Folder for Pack"); chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); chooser.setFileFilter(new PackDirectoryFilter()); File dir = workspaceDir; do { chooser.setCurrentDirectory(dir); int returnVal = chooser.showOpenDialog(frame); if (returnVal == JFileChooser.APPROVE_OPTION) { dir = chooser.getSelectedFile(); } else { return; } } while (!canAddPackDir(dir)); pack.setLocation(dir.getAbsolutePath()); updatePackInWorkspace(pack); } }); frame.getRefreshMenuItem().addActionListener(e -> loadWorkspace()); frame.getQuitMenuItem().addActionListener(e -> { frame.dispose(); }); frame.getEditConfigMenuItem().addActionListener(e -> { Optional<Pack> optional = getSelectedPack(true); if (optional.isPresent()) { Pack pack = optional.get(); File file = pack.getConfigFile(); BuilderConfig config = Persistence.read(file, BuilderConfig.class); if (BuilderConfigDialog.showEditor(frame, config)) { writeBuilderConfig(pack, config); updatePackInWorkspace(pack); } } }); frame.getOpenFolderMenuItem().addActionListener(e -> { Optional<Pack> optional = getSelectedPack(true); if (optional.isPresent()) { SwingHelper.browseDir(optional.get().getDirectory(), frame); } }); frame.getCheckProblemsMenuItem().addActionListener(e -> { Optional<Pack> optional = getSelectedPack(true); if (optional.isPresent()) { ProblemChecker checker = new ProblemChecker(optional.get()); Deferred<?> deferred = Deferreds.makeDeferred(executor.submit(checker), executor) .handleAsync(this::showProblems, (ex) -> { }, SwingExecutor.INSTANCE); SwingHelper.addErrorDialogCallback(frame, deferred); } }); frame.getTestMenuItem().addActionListener(e -> { Optional<Pack> optional = getSelectedPack(true); if (optional.isPresent()) { Pack pack = optional.get(); startTest(pack, false); } }); frame.getTestOnlineMenuItem().addActionListener(e -> { Optional<Pack> optional = getSelectedPack(true); if (optional.isPresent()) { Pack pack = optional.get(); startTest(pack, true); } }); frame.getOptionsMenuItem().addActionListener(e -> { ConfigurationDialog configDialog = new ConfigurationDialog(frame, launcher); configDialog.setVisible(true); }); frame.getClearInstanceMenuItem().addActionListener(e -> { DirectoryDeleter deleter = new DirectoryDeleter(launcher.getInstancesDir()); Deferred<?> deferred = Deferreds.makeDeferred(executor.submit(deleter), executor); ProgressDialog.showProgress(frame, deferred, deleter, "Deleting test instances...", "Deleting test instances..."); SwingHelper.addErrorDialogCallback(frame, deferred); }); frame.getClearWebRootMenuItem().addActionListener(e -> { DirectoryDeleter deleter = new DirectoryDeleter(webRoot); Deferred<?> deferred = Deferreds.makeDeferred(executor.submit(deleter), executor); ProgressDialog.showProgress(frame, deferred, deleter, "Deleting web server files...", "Deleting web server files..."); SwingHelper.addErrorDialogCallback(frame, deferred); }); frame.getBuildMenuItem().addActionListener(e -> { Optional<Pack> optional = getSelectedPack(true); if (optional.isPresent()) { Pack pack = optional.get(); buildPack(pack); } }); frame.getDeployServerMenuItem().addActionListener(e -> { Optional<Pack> optional = getSelectedPack(true); if (optional.isPresent()) { Pack pack = optional.get(); DeployOptions options = DeployServerDialog.showDeployDialog(frame, lastServerDestDir); if (options != null) { ConsoleFrame.showMessages(); File destDir = options.getDestDir(); destDir.mkdirs(); lastServerDestDir = destDir; ServerDeploy deploy = new ServerDeploy(pack.getSourceDir(), options); Deferred<?> deferred = Deferreds.makeDeferred(executor.submit(deploy), executor) .handleAsync(r -> SwingHelper.showMessageDialog(frame, "Server deployment complete!", "Success", null, JOptionPane.INFORMATION_MESSAGE), ex -> { }, SwingExecutor.INSTANCE); ProgressDialog.showProgress(frame, deferred, deploy, "Deploying files...", "Deploying server files..."); SwingHelper.addErrorDialogCallback(frame, deferred); } } }); frame.getGeneratePackagesMenuItem().addActionListener(e -> { List<ManifestEntry> entries = workspace.getPackageListingEntries(); ManifestInfoEnumerator enumerator = new ManifestInfoEnumerator(distDir); Deferred<?> deferred = Deferreds.makeDeferred(executor.submit(() -> enumerator.apply(entries))) .handleAsync(loaded -> { GenerateListingDialog dialog = new GenerateListingDialog(frame); GenerateListingController controller = new GenerateListingController(dialog, workspace, loaded, executor); controller.setOutputDir(distDir); controller.show(); }, ex -> { }, SwingExecutor.INSTANCE); ProgressDialog.showProgress(frame, deferred, new SettableProgress("Searching...", -1), "Searching for manifests...", "Searching for manifests..."); SwingHelper.addErrorDialogCallback(frame, deferred); }); frame.getVersionCheckMenuItem().addActionListener(e -> { Optional<Pack> optional = getSelectedPack(true); if (optional.isPresent()) { Pack pack = optional.get(); VersionCheckDialog dialog = new VersionCheckDialog(frame); VersionCheckController controller = new VersionCheckController(dialog, executor); controller.showUpdates(pack.getModsDir(), pack.getCachedConfig().getGameVersion(), frame); } }); frame.getOpenOutputFolderMenuItem().addActionListener(e -> SwingHelper.browseDir(distDir, frame)); frame.getOpenWorkspaceFolderMenuItem().addActionListener(e1 -> SwingHelper.browseDir(workspaceDir, frame)); frame.getOpenLauncherFolderMenuItem().addActionListener(e1 -> SwingHelper.browseDir(launcherDir, frame)); frame.getOpenWebRootMenuItem().addActionListener(e1 -> SwingHelper.browseDir(webRoot, frame)); frame.getOpenConsoleMenuItem().addActionListener(e -> { ConsoleFrame.showMessages(); }); frame.getDocsMenuItem().addActionListener(e -> { SwingHelper.openURL("https://github.com/SKCraft/Launcher/wiki", frame); }); frame.getAboutMenuItem().addActionListener(e -> { AboutDialog.showAboutDialog(frame); }); SwingHelper.addActionListeners(frame.getNewPackButton(), frame.getNewPackMenuItem().getActionListeners()); SwingHelper.addActionListeners(frame.getImportButton(), frame.getImportPackMenuItem().getActionListeners()); SwingHelper.addActionListeners(frame.getEditConfigButton(), frame.getEditConfigMenuItem().getActionListeners()); SwingHelper.addActionListeners(frame.getOpenFolderButton(), frame.getOpenFolderMenuItem().getActionListeners()); SwingHelper.addActionListeners(frame.getCheckProblemsButton(), frame.getCheckProblemsMenuItem().getActionListeners()); SwingHelper.addActionListeners(frame.getTestButton(), frame.getTestMenuItem().getActionListeners()); SwingHelper.addActionListeners(frame.getBuildButton(), frame.getBuildMenuItem().getActionListeners()); } private void popupPackMenu(Component component, int x, int y, Pack pack) { JPopupMenu popup = new JPopupMenu(); JMenuItem menuItem; menuItem = new JMenuItem("Edit modpack.json..."); menuItem.addActionListener(e -> frame.getEditConfigMenuItem().doClick()); popup.add(menuItem); menuItem = new JMenuItem("Open Directory"); menuItem.addActionListener(e -> frame.getOpenFolderMenuItem().doClick()); popup.add(menuItem); menuItem = new JMenuItem("Check for Problems"); menuItem.addActionListener(e -> frame.getCheckProblemsMenuItem().doClick()); popup.add(menuItem); popup.addSeparator(); menuItem = new JMenuItem("Test"); menuItem.addActionListener(e -> frame.getTestMenuItem().doClick()); popup.add(menuItem); menuItem = new JMenuItem("Test Online"); menuItem.addActionListener(e -> frame.getTestOnlineMenuItem().doClick()); popup.add(menuItem); menuItem = new JMenuItem("Build..."); menuItem.addActionListener(e -> frame.getBuildMenuItem().doClick()); popup.add(menuItem); menuItem = new JMenuItem("Deploy Server..."); menuItem.addActionListener(e -> frame.getDeployServerMenuItem().doClick()); popup.add(menuItem); popup.addSeparator(); menuItem = new JMenuItem("Change Location..."); menuItem.addActionListener(e -> frame.getChangePackLocationMenuItem().doClick()); popup.add(menuItem); menuItem = new JMenuItem("Remove..."); menuItem.addActionListener(e -> frame.getRemovePackItem().doClick()); popup.add(menuItem); menuItem = new JMenuItem("Delete Forever..."); menuItem.addActionListener(e -> frame.getDeletePackItem().doClick()); popup.add(menuItem); popup.show(component, x, y); } private void tryAddPackViaDialog() { BuilderConfig config = new BuilderConfig(); addDefaultConfig(config); Pack pack = new Pack(); File dir; do { if (BuilderConfigDialog.showEditor(frame, config)) { dir = new File(workspaceDir, config.getName()); } else { return; } } while (!canAddPackDir(dir)); pack.setLocation(dir.getAbsolutePath()); if (pack.getConfigFile().exists()) { if (SwingHelper.confirmDialog(frame, "There's already an existing modpack with that name, though " + "it is not imported into this workspace. Would you like to import it and ignore " + "the new modpack that you just entered?", "Conflict")) { addPackToWorkspace(pack); } } else { if (writeBuilderConfig(pack, config)) { pack.createGuideFolders(); addPackToWorkspace(pack); } } } private void tryAddPackViaDirectory(boolean createNew) { JFileChooser chooser = new JFileChooser(); chooser.setDialogTitle("Choose Folder for Pack"); chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); chooser.setFileFilter(new PackDirectoryFilter()); File dir = workspaceDir; do { chooser.setCurrentDirectory(dir); int returnVal = chooser.showOpenDialog(frame); if (returnVal == JFileChooser.APPROVE_OPTION) { dir = chooser.getSelectedFile(); } else { return; } } while (!canAddPackDir(dir)); Pack pack = new Pack(); pack.setLocation(dir.getAbsolutePath()); if (pack.getConfigFile().exists()) { if (createNew) { if (SwingHelper.confirmDialog(frame, "There's already a modpack in that directory. Do you want to import it instead?", "Exists Already")) { addPackToWorkspace(pack); } } else { addPackToWorkspace(pack); } } else if (createNew || SwingHelper.confirmDialog(frame, "You've selected a directory that doesn't seem to be a modpack " + "(at least with the directory structure this program expects). Would you like to create the files necessary to turn " + "that folder into a modpack?", "Import Error")) { BuilderConfig config = new BuilderConfig(); addDefaultConfig(config); if (BuilderConfigDialog.showEditor(frame, config)) { if (writeBuilderConfig(pack, config)) { pack.createGuideFolders(); addPackToWorkspace(pack); } } } } private void startTest(Pack pack, boolean online) { Session session; if (online) { session = LoginDialog.showLoginRequest(frame, launcher); if (session == null) { return; } } else { if (!isOfflineEnabled()) { return; } session = new OfflineSession("Player"); } String version = generateVersionFromDate(); PackBuilder builder = new PackBuilder(pack, webRoot, version, "staging.json", false, false); InstanceList.Enumerator enumerator = launcher.getInstances().createEnumerator(); TestLauncher instanceLauncher = new TestLauncher(launcher, frame, pack.getCachedConfig().getName(), session); SettableProgress progress = new SettableProgress(builder); ConsoleFrame.showMessages(); Deferred<?> deferred = Deferreds.makeDeferred(executor.submit(builder), executor) .thenTap(() -> progress.set("Loading instance in test launcher...", -1)) .thenRun(enumerator) .thenTap(() -> progress.set("Launching", -1)) .thenApply(instanceLauncher) .handleAsync(result -> ConsoleFrame.hideMessages(), ex -> {}, SwingExecutor.INSTANCE); ProgressDialog.showProgress(frame, deferred, progress, "Setting up test instance...", "Preparing files for launch..."); SwingHelper.addErrorDialogCallback(frame, deferred); } private void buildPack(Pack pack) { String initialVersion = generateVersionFromDate(); BuildOptions options = BuildDialog.showBuildDialog(frame, initialVersion, generateManifestName(pack), distDir); if (options != null) { ConsoleFrame.showMessages(); PackBuilder builder = new PackBuilder(pack, options.getDestDir(), options.getVersion(), options.getManifestFilename(), false, true); Deferred<?> deferred = Deferreds.makeDeferred(executor.submit(builder), executor) .handleAsync(result -> { ConsoleFrame.hideMessages(); SwingHelper.showMessageDialog(frame, "Successfully generated the package files.", "Success", null, JOptionPane.INFORMATION_MESSAGE); }, ex -> {}, SwingExecutor.INSTANCE); ProgressDialog.showProgress(frame, deferred, builder, "Building modpack...", "Building modpack..."); SwingHelper.addErrorDialogCallback(frame, deferred); } } private void showProblems(List<Problem> problems) { if (problems.isEmpty()) { SwingHelper.showMessageDialog(frame, "No potential problems found!", "Success", null, JOptionPane.INFORMATION_MESSAGE); } else { ProblemViewer viewer = new ProblemViewer(frame, problems); viewer.setVisible(true); } } public String generateManifestName(Pack pack) { File file = pack.getConfigFile(); if (file.exists()) { BuilderConfig config = Persistence.read(file, BuilderConfig.class, true); if (config != null) { String name = Strings.nullToEmpty(config.getName()); name = name.toLowerCase(); name = FILENAME_SANITIZE.matcher(name).replaceAll("-"); name = name.trim(); if (!name.isEmpty()) { return name + ".json"; } } } return "my_modpack.json"; } public static String generateVersionFromDate() { Date today = Calendar.getInstance().getTime(); return VERSION_DATE_FORMAT.format(today); } }