diff --git a/launcher/src/main/java/com/skcraft/launcher/InstanceTasks.java b/launcher/src/main/java/com/skcraft/launcher/InstanceTasks.java new file mode 100644 index 0000000..c46e79c --- /dev/null +++ b/launcher/src/main/java/com/skcraft/launcher/InstanceTasks.java @@ -0,0 +1,66 @@ +/* + * SKCraft Launcher + * Copyright (C) 2010-2014 Albert Pham and contributors + * Please see LICENSE.txt for license information. + */ + +package com.skcraft.launcher; + +import com.skcraft.concurrency.ObservableFuture; +import com.skcraft.launcher.dialog.ProgressDialog; +import com.skcraft.launcher.swing.SwingHelper; +import com.skcraft.launcher.update.HardResetter; +import com.skcraft.launcher.update.Remover; +import com.skcraft.launcher.util.SharedLocale; + +import java.awt.*; + +import static com.skcraft.launcher.util.SharedLocale.tr; + +public class InstanceTasks { + + private final Launcher launcher; + + public InstanceTasks(Launcher launcher) { + this.launcher = launcher; + } + + public ObservableFuture delete(Window window, Instance instance) { + // Execute the deleter + Remover resetter = new Remover(instance); + ObservableFuture future = new ObservableFuture( + launcher.getExecutor().submit(resetter), resetter); + + // Show progress + ProgressDialog.showProgress( + window, future, SharedLocale.tr("instance.deletingTitle"), tr("instance.deletingStatus", instance.getTitle())); + SwingHelper.addErrorDialogCallback(window, future); + + return future; + } + + public ObservableFuture hardUpdate(Window window, Instance instance) { + // Execute the resetter + HardResetter resetter = new HardResetter(instance); + ObservableFuture future = new ObservableFuture( + launcher.getExecutor().submit(resetter), resetter); + + // Show progress + ProgressDialog.showProgress(window, future, SharedLocale.tr("instance.resettingTitle"), + tr("instance.resettingStatus", instance.getTitle())); + SwingHelper.addErrorDialogCallback(window, future); + + return future; + } + + public ObservableFuture reloadInstances(Window window) { + InstanceList.Enumerator loader = launcher.getInstances().createEnumerator(); + ObservableFuture future = new ObservableFuture(launcher.getExecutor().submit(loader), loader); + + ProgressDialog.showProgress(window, future, SharedLocale.tr("launcher.checkingTitle"), SharedLocale.tr("launcher.checkingStatus")); + SwingHelper.addErrorDialogCallback(window, future); + + return future; + } + +} diff --git a/launcher/src/main/java/com/skcraft/launcher/Launcher.java b/launcher/src/main/java/com/skcraft/launcher/Launcher.java index a54ad52..bdab9ee 100644 --- a/launcher/src/main/java/com/skcraft/launcher/Launcher.java +++ b/launcher/src/main/java/com/skcraft/launcher/Launcher.java @@ -15,9 +15,11 @@ import com.skcraft.launcher.auth.AccountList; import com.skcraft.launcher.auth.LoginService; import com.skcraft.launcher.auth.YggdrasilLoginService; import com.skcraft.launcher.dialog.LauncherFrame; +import com.skcraft.launcher.launch.LaunchSupervisor; import com.skcraft.launcher.model.minecraft.VersionManifest; import com.skcraft.launcher.persistence.Persistence; import com.skcraft.launcher.swing.SwingHelper; +import com.skcraft.launcher.update.UpdateManager; import com.skcraft.launcher.util.HttpRequest; import com.skcraft.launcher.util.SharedLocale; import com.skcraft.launcher.util.SimpleLogFormatter; @@ -54,6 +56,9 @@ public final class Launcher { @Getter private final Configuration config; @Getter private final AccountList accounts; @Getter private final AssetsRoot assets; + @Getter private final LaunchSupervisor launchSupervisor = new LaunchSupervisor(this); + @Getter private final UpdateManager updateManager = new UpdateManager(this); + @Getter private final InstanceTasks instanceTasks = new InstanceTasks(this); /** * Create a new launcher instance with the given base directory. @@ -82,6 +87,8 @@ public final class Launcher { cleanupExtractDir(); } }); + + updateManager.checkForUpdate(); } /** diff --git a/launcher/src/main/java/com/skcraft/launcher/dialog/LauncherFrame.java b/launcher/src/main/java/com/skcraft/launcher/dialog/LauncherFrame.java index 139c5cc..9c400b7 100644 --- a/launcher/src/main/java/com/skcraft/launcher/dialog/LauncherFrame.java +++ b/launcher/src/main/java/com/skcraft/launcher/dialog/LauncherFrame.java @@ -6,29 +6,17 @@ package com.skcraft.launcher.dialog; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; -import com.google.common.util.concurrent.ListenableFuture; import com.skcraft.concurrency.ObservableFuture; import com.skcraft.launcher.Instance; import com.skcraft.launcher.InstanceList; import com.skcraft.launcher.Launcher; -import com.skcraft.launcher.auth.Session; -import com.skcraft.launcher.launch.Runner; -import com.skcraft.launcher.launch.LaunchProcessHandler; -import com.skcraft.launcher.persistence.Persistence; -import com.skcraft.launcher.selfupdate.UpdateChecker; -import com.skcraft.launcher.selfupdate.SelfUpdater; +import com.skcraft.launcher.launch.LaunchListener; import com.skcraft.launcher.swing.*; -import com.skcraft.launcher.update.HardResetter; -import com.skcraft.launcher.update.Remover; -import com.skcraft.launcher.update.Updater; import com.skcraft.launcher.util.SharedLocale; import com.skcraft.launcher.util.SwingExecutor; import lombok.NonNull; import lombok.extern.java.Log; import net.miginfocom.swing.MigLayout; -import org.apache.commons.io.FileUtils; import javax.swing.*; import javax.swing.event.TableModelEvent; @@ -37,13 +25,11 @@ import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; import java.io.File; -import java.io.IOException; import java.net.URL; -import java.util.Date; -import java.util.logging.Level; -import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor; import static com.skcraft.launcher.util.SharedLocale.tr; /** @@ -87,7 +73,6 @@ public class LauncherFrame extends JFrame { SwingHelper.setIconImage(this, Launcher.class, "icon.png"); loadInstances(); - checkLauncherUpdate(); } private void initComponents() { @@ -95,7 +80,17 @@ public class LauncherFrame extends JFrame { webView = WebpagePanel.forURL(launcher.getNewsURL(), false); splitPane = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, instanceScroll, webView); - selfUpdateButton.setVisible(false); + selfUpdateButton.setVisible(launcher.getUpdateManager().getPendingUpdate()); + + launcher.getUpdateManager().addPropertyChangeListener(new PropertyChangeListener() { + @Override + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName().equals("pendingUpdate")) { + selfUpdateButton.setVisible((Boolean) evt.getNewValue()); + + } + } + }); updateCheck.setSelected(true); instancesTable.setModel(instancesModel); @@ -125,14 +120,14 @@ public class LauncherFrame extends JFrame { @Override public void actionPerformed(ActionEvent e) { loadInstances(); - checkLauncherUpdate(); + launcher.getUpdateManager().checkForUpdate(); } }); selfUpdateButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { - selfUpdate(); + launcher.getUpdateManager().performUpdate(LauncherFrame.this); } }); @@ -164,64 +159,6 @@ public class LauncherFrame extends JFrame { }); } - private void checkLauncherUpdate() { - if (SelfUpdater.updatedAlready) { - return; - } - - ListenableFuture future = launcher.getExecutor().submit(new UpdateChecker(launcher)); - - Futures.addCallback(future, new FutureCallback() { - @Override - public void onSuccess(URL result) { - if (result != null) { - requestUpdate(result); - } - } - - @Override - public void onFailure(Throwable t) { - - } - }, SwingExecutor.INSTANCE); - } - - private void selfUpdate() { - URL url = updateUrl; - if (url != null) { - SelfUpdater downloader = new SelfUpdater(launcher, url); - ObservableFuture future = new ObservableFuture( - launcher.getExecutor().submit(downloader), downloader); - - Futures.addCallback(future, new FutureCallback() { - @Override - public void onSuccess(File result) { - selfUpdateButton.setVisible(false); - SwingHelper.showMessageDialog( - LauncherFrame.this, - SharedLocale.tr("launcher.selfUpdateComplete"), - SharedLocale.tr("launcher.selfUpdateCompleteTitle"), - null, - JOptionPane.INFORMATION_MESSAGE); - } - - @Override - public void onFailure(Throwable t) { - } - }, SwingExecutor.INSTANCE); - - ProgressDialog.showProgress(this, future, SharedLocale.tr("launcher.selfUpdatingTitle"), SharedLocale.tr("launcher.selfUpdatingStatus")); - SwingHelper.addErrorDialogCallback(this, future); - } else { - selfUpdateButton.setVisible(false); - } - } - - private void requestUpdate(URL url) { - this.updateUrl = url; - selfUpdateButton.setVisible(true); - } - /** * Popup the menu for the instances. * @@ -334,15 +271,7 @@ public class LauncherFrame extends JFrame { return; } - // Execute the deleter - Remover resetter = new Remover(instance); - ObservableFuture future = new ObservableFuture( - launcher.getExecutor().submit(resetter), resetter); - - // Show progress - ProgressDialog.showProgress( - this, future, SharedLocale.tr("instance.deletingTitle"), tr("instance.deletingStatus", instance.getTitle())); - SwingHelper.addErrorDialogCallback(this, future); + ObservableFuture future = launcher.getInstanceTasks().delete(this, instance); // Update the list of instances after updating future.addListener(new Runnable() { @@ -358,15 +287,7 @@ public class LauncherFrame extends JFrame { return; } - // Execute the resetter - HardResetter resetter = new HardResetter(instance); - ObservableFuture future = new ObservableFuture( - launcher.getExecutor().submit(resetter), resetter); - - // Show progress - ProgressDialog.showProgress( this, future, SharedLocale.tr("instance.resettingTitle"), - tr("instance.resettingStatus", instance.getTitle())); - SwingHelper.addErrorDialogCallback(this, future); + ObservableFuture future = launcher.getInstanceTasks().hardUpdate(this, instance); // Update the list of instances after updating future.addListener(new Runnable() { @@ -379,9 +300,7 @@ public class LauncherFrame extends JFrame { } private void loadInstances() { - InstanceList.Enumerator loader = launcher.getInstances().createEnumerator(); - ObservableFuture future = new ObservableFuture( - launcher.getExecutor().submit(loader), loader); + ObservableFuture future = launcher.getInstanceTasks().reloadInstances(this); future.addListener(new Runnable() { @Override @@ -404,107 +323,25 @@ public class LauncherFrame extends JFrame { } private void launch() { - try { - final Instance instance = launcher.getInstances().get(instancesTable.getSelectedRow()); - boolean update = updateCheck.isSelected() && instance.isUpdatePending(); + boolean permitUpdate = updateCheck.isSelected(); + Instance instance = launcher.getInstances().get(instancesTable.getSelectedRow()); - // Store last access date - Date now = new Date(); - instance.setLastAccessed(now); - Persistence.commitAndForget(instance); - - // Perform login - final Session session = LoginDialog.showLoginRequest(this, launcher); - if (session == null) { - return; - } - - // If we have to update, we have to update - if (!instance.isInstalled()) { - update = true; - } - - if (update) { - // Execute the updater - Updater updater = new Updater(launcher, instance); - updater.setOnline(session.isOnline()); - ObservableFuture future = new ObservableFuture( - launcher.getExecutor().submit(updater), updater); - - // Show progress - ProgressDialog.showProgress( - this, future, SharedLocale.tr("launcher.updatingTitle"), tr("launcher.updatingStatus", instance.getTitle())); - SwingHelper.addErrorDialogCallback(this, future); - - // Update the list of instances after updating - future.addListener(new Runnable() { - @Override - public void run() { - instancesModel.update(); - } - }, SwingExecutor.INSTANCE); - - // On success, launch also - Futures.addCallback(future, new FutureCallback() { - @Override - public void onSuccess(Instance result) { - launch(instance, session); - } - - @Override - public void onFailure(Throwable t) { - } - }, SwingExecutor.INSTANCE); - } else { - launch(instance, session); - } - } catch (ArrayIndexOutOfBoundsException e) { - SwingHelper.showErrorDialog(this, SharedLocale.tr("launcher.noInstanceError"), SharedLocale.tr("launcher.noInstanceTitle")); - } - } - - private void launch(Instance instance, Session session) { - final File extractDir = launcher.createExtractDir(); - - // Get the process - Runner task = new Runner(launcher, instance, session, extractDir); - ObservableFuture processFuture = new ObservableFuture( - launcher.getExecutor().submit(task), task); - - // Show process for the process retrieval - ProgressDialog.showProgress( - this, processFuture, SharedLocale.tr("launcher.launchingTItle"), tr("launcher.launchingStatus", instance.getTitle())); - - // If the process is started, get rid of this window - Futures.addCallback(processFuture, new FutureCallback() { + launcher.getLaunchSupervisor().launch(this, instance, permitUpdate, new LaunchListener() { @Override - public void onSuccess(Process result) { + public void instancesUpdated() { + instancesModel.update(); + } + + @Override + public void gameStarted() { dispose(); } @Override - public void onFailure(Throwable t) { + public void gameClosed() { + new LauncherFrame(launcher).setVisible(true); } }); - - // Watch the created process - ListenableFuture future = Futures.transform( - processFuture, new LaunchProcessHandler(launcher), launcher.getExecutor()); - SwingHelper.addErrorDialogCallback(null, future); - - // Clean up at the very end - future.addListener(new Runnable() { - @Override - public void run() { - try { - log.info("Process ended; cleaning up " + extractDir.getAbsolutePath()); - FileUtils.deleteDirectory(extractDir); - } catch (IOException e) { - log.log(Level.WARNING, "Failed to clean up " + extractDir.getAbsolutePath(), e); - } - instancesModel.update(); - } - }, sameThreadExecutor()); } } diff --git a/launcher/src/main/java/com/skcraft/launcher/launch/LaunchListener.java b/launcher/src/main/java/com/skcraft/launcher/launch/LaunchListener.java new file mode 100644 index 0000000..a7c47e2 --- /dev/null +++ b/launcher/src/main/java/com/skcraft/launcher/launch/LaunchListener.java @@ -0,0 +1,17 @@ +/* + * SKCraft Launcher + * Copyright (C) 2010-2014 Albert Pham and contributors + * Please see LICENSE.txt for license information. + */ + +package com.skcraft.launcher.launch; + +public interface LaunchListener { + + public void instancesUpdated(); + + public void gameStarted(); + + public void gameClosed(); + +} diff --git a/launcher/src/main/java/com/skcraft/launcher/launch/LaunchProcessHandler.java b/launcher/src/main/java/com/skcraft/launcher/launch/LaunchProcessHandler.java index 0e4934e..cfa0483 100644 --- a/launcher/src/main/java/com/skcraft/launcher/launch/LaunchProcessHandler.java +++ b/launcher/src/main/java/com/skcraft/launcher/launch/LaunchProcessHandler.java @@ -64,8 +64,6 @@ public class LaunchProcessHandler implements Function and contributors + * Please see LICENSE.txt for license information. + */ + +package com.skcraft.launcher.launch; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.skcraft.concurrency.ObservableFuture; +import com.skcraft.launcher.Instance; +import com.skcraft.launcher.Launcher; +import com.skcraft.launcher.auth.Session; +import com.skcraft.launcher.dialog.LoginDialog; +import com.skcraft.launcher.dialog.ProgressDialog; +import com.skcraft.launcher.persistence.Persistence; +import com.skcraft.launcher.swing.SwingHelper; +import com.skcraft.launcher.update.Updater; +import com.skcraft.launcher.util.SharedLocale; +import com.skcraft.launcher.util.SwingExecutor; +import lombok.extern.java.Log; +import org.apache.commons.io.FileUtils; + +import javax.swing.*; +import java.awt.*; +import java.io.File; +import java.io.IOException; +import java.util.Date; +import java.util.logging.Level; + +import static com.google.common.util.concurrent.MoreExecutors.sameThreadExecutor; +import static com.skcraft.launcher.util.SharedLocale.tr; + +@Log +public class LaunchSupervisor { + + private final Launcher launcher; + + public LaunchSupervisor(Launcher launcher) { + this.launcher = launcher; + } + + public void launch(final Window window, final Instance instance, boolean permitUpdate, final LaunchListener listener) { + try { + boolean update = permitUpdate && instance.isUpdatePending(); + + // Store last access date + Date now = new Date(); + instance.setLastAccessed(now); + Persistence.commitAndForget(instance); + + // Perform login + final Session session = LoginDialog.showLoginRequest(window, launcher); + if (session == null) { + return; + } + + // If we have to update, we have to update + if (!instance.isInstalled()) { + update = true; + } + + if (update) { + // Execute the updater + Updater updater = new Updater(launcher, instance); + updater.setOnline(session.isOnline()); + ObservableFuture future = new ObservableFuture( + launcher.getExecutor().submit(updater), updater); + + // Show progress + ProgressDialog.showProgress(window, future, SharedLocale.tr("launcher.updatingTitle"), tr("launcher.updatingStatus", instance.getTitle())); + SwingHelper.addErrorDialogCallback(window, future); + + // Update the list of instances after updating + future.addListener(new Runnable() { + @Override + public void run() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + listener.instancesUpdated(); + } + }); + } + }, SwingExecutor.INSTANCE); + + // On success, launch also + Futures.addCallback(future, new FutureCallback() { + @Override + public void onSuccess(Instance result) { + launch(window, instance, session, listener); + } + + @Override + public void onFailure(Throwable t) { + } + }, SwingExecutor.INSTANCE); + } else { + launch(window, instance, session, listener); + } + } catch (ArrayIndexOutOfBoundsException e) { + SwingHelper.showErrorDialog(window, SharedLocale.tr("launcher.noInstanceError"), SharedLocale.tr("launcher.noInstanceTitle")); + } + } + + private void launch(Window window, Instance instance, Session session, final LaunchListener listener) { + final File extractDir = launcher.createExtractDir(); + + // Get the process + Runner task = new Runner(launcher, instance, session, extractDir); + ObservableFuture processFuture = new ObservableFuture( + launcher.getExecutor().submit(task), task); + + // Show process for the process retrieval + ProgressDialog.showProgress( + window, processFuture, SharedLocale.tr("launcher.launchingTItle"), tr("launcher.launchingStatus", instance.getTitle())); + + // If the process is started, get rid of this window + Futures.addCallback(processFuture, new FutureCallback() { + @Override + public void onSuccess(Process result) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + listener.gameStarted(); + } + }); + } + + @Override + public void onFailure(Throwable t) { + } + }); + + // Watch the created process + ListenableFuture future = Futures.transform( + processFuture, new LaunchProcessHandler(launcher), launcher.getExecutor()); + SwingHelper.addErrorDialogCallback(null, future); + + // Clean up at the very end + future.addListener(new Runnable() { + @Override + public void run() { + try { + log.info("Process ended; cleaning up " + extractDir.getAbsolutePath()); + FileUtils.deleteDirectory(extractDir); + } catch (IOException e) { + log.log(Level.WARNING, "Failed to clean up " + extractDir.getAbsolutePath(), e); + } + + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + listener.gameClosed(); + } + }); + } + }, sameThreadExecutor()); + } +} diff --git a/launcher/src/main/java/com/skcraft/launcher/selfupdate/SelfUpdater.java b/launcher/src/main/java/com/skcraft/launcher/selfupdate/SelfUpdater.java index edb59d8..0557004 100644 --- a/launcher/src/main/java/com/skcraft/launcher/selfupdate/SelfUpdater.java +++ b/launcher/src/main/java/com/skcraft/launcher/selfupdate/SelfUpdater.java @@ -22,8 +22,6 @@ import java.util.concurrent.Executors; public class SelfUpdater implements Callable, ProgressObservable { - public static boolean updatedAlready = false; - private final Launcher launcher; private final URL url; private final Installer installer; @@ -52,8 +50,6 @@ public class SelfUpdater implements Callable, ProgressObservable { progress = installer; installer.execute(); - updatedAlready = true; - return file; } finally { executor.shutdownNow(); diff --git a/launcher/src/main/java/com/skcraft/launcher/update/UpdateManager.java b/launcher/src/main/java/com/skcraft/launcher/update/UpdateManager.java new file mode 100644 index 0000000..b1a4daa --- /dev/null +++ b/launcher/src/main/java/com/skcraft/launcher/update/UpdateManager.java @@ -0,0 +1,110 @@ +/* + * SKCraft Launcher + * Copyright (C) 2010-2014 Albert Pham and contributors + * Please see LICENSE.txt for license information. + */ + +package com.skcraft.launcher.update; + +import com.google.common.util.concurrent.FutureCallback; +import com.google.common.util.concurrent.Futures; +import com.google.common.util.concurrent.ListenableFuture; +import com.skcraft.concurrency.ObservableFuture; +import com.skcraft.launcher.Launcher; +import com.skcraft.launcher.dialog.ProgressDialog; +import com.skcraft.launcher.selfupdate.SelfUpdater; +import com.skcraft.launcher.selfupdate.UpdateChecker; +import com.skcraft.launcher.swing.SwingHelper; +import com.skcraft.launcher.util.SharedLocale; +import com.skcraft.launcher.util.SwingExecutor; +import lombok.Getter; + +import javax.swing.*; +import javax.swing.event.SwingPropertyChangeSupport; +import java.awt.*; +import java.beans.PropertyChangeListener; +import java.io.File; +import java.net.URL; + +public class UpdateManager { + + @Getter + private final SwingPropertyChangeSupport propertySupport = new SwingPropertyChangeSupport(this); + private final Launcher launcher; + private URL pendingUpdateUrl; + + public UpdateManager(Launcher launcher) { + this.launcher = launcher; + } + + public void addPropertyChangeListener(PropertyChangeListener listener) { + propertySupport.addPropertyChangeListener(listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + propertySupport.removePropertyChangeListener(listener); + } + + public boolean getPendingUpdate() { + return pendingUpdateUrl != null; + } + + public void checkForUpdate() { + ListenableFuture future = launcher.getExecutor().submit(new UpdateChecker(launcher)); + + Futures.addCallback(future, new FutureCallback() { + @Override + public void onSuccess(URL result) { + if (result != null) { + requestUpdate(result); + } + } + + @Override + public void onFailure(Throwable t) { + + } + }, SwingExecutor.INSTANCE); + } + + public void performUpdate(final Window window) { + final URL url = pendingUpdateUrl; + + if (url != null) { + SelfUpdater downloader = new SelfUpdater(launcher, url); + ObservableFuture future = new ObservableFuture( + launcher.getExecutor().submit(downloader), downloader); + + Futures.addCallback(future, new FutureCallback() { + @Override + public void onSuccess(File result) { + propertySupport.firePropertyChange("pendingUpdate", true, false); + UpdateManager.this.pendingUpdateUrl = null; + + SwingHelper.showMessageDialog( + window, + SharedLocale.tr("launcher.selfUpdateComplete"), + SharedLocale.tr("launcher.selfUpdateCompleteTitle"), + null, + JOptionPane.INFORMATION_MESSAGE); + } + + @Override + public void onFailure(Throwable t) { + } + }, SwingExecutor.INSTANCE); + + ProgressDialog.showProgress(window, future, SharedLocale.tr("launcher.selfUpdatingTitle"), SharedLocale.tr("launcher.selfUpdatingStatus")); + SwingHelper.addErrorDialogCallback(window, future); + } else { + propertySupport.firePropertyChange("pendingUpdate", false, false); + } + } + + private void requestUpdate(URL url) { + propertySupport.firePropertyChange("pendingUpdate", getPendingUpdate(), url != null); + this.pendingUpdateUrl = url; + } + + +}