1
0
mirror of https://github.com/SKCraft/Launcher.git synced 2024-11-24 12:16:28 +01:00

Split up LauncherFrame.

This commit is contained in:
sk89q 2015-02-19 21:34:57 -08:00
parent 415de32f6a
commit 8cf14a5ce1
8 changed files with 392 additions and 199 deletions

View File

@ -0,0 +1,66 @@
/*
* SKCraft Launcher
* Copyright (C) 2010-2014 Albert Pham <http://www.sk89q.com> 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<Instance> delete(Window window, Instance instance) {
// Execute the deleter
Remover resetter = new Remover(instance);
ObservableFuture<Instance> future = new ObservableFuture<Instance>(
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<Instance> hardUpdate(Window window, Instance instance) {
// Execute the resetter
HardResetter resetter = new HardResetter(instance);
ObservableFuture<Instance> future = new ObservableFuture<Instance>(
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<InstanceList> reloadInstances(Window window) {
InstanceList.Enumerator loader = launcher.getInstances().createEnumerator();
ObservableFuture<InstanceList> future = new ObservableFuture<InstanceList>(launcher.getExecutor().submit(loader), loader);
ProgressDialog.showProgress(window, future, SharedLocale.tr("launcher.checkingTitle"), SharedLocale.tr("launcher.checkingStatus"));
SwingHelper.addErrorDialogCallback(window, future);
return future;
}
}

View File

@ -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();
}
/**

View File

@ -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<URL> future = launcher.getExecutor().submit(new UpdateChecker(launcher));
Futures.addCallback(future, new FutureCallback<URL>() {
@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<File> future = new ObservableFuture<File>(
launcher.getExecutor().submit(downloader), downloader);
Futures.addCallback(future, new FutureCallback<File>() {
@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<Instance> future = new ObservableFuture<Instance>(
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<Instance> 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<Instance> future = new ObservableFuture<Instance>(
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<Instance> 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<InstanceList> future = new ObservableFuture<InstanceList>(
launcher.getExecutor().submit(loader), loader);
ObservableFuture<InstanceList> 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<Instance> future = new ObservableFuture<Instance>(
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() {
launcher.getLaunchSupervisor().launch(this, instance, permitUpdate, new LaunchListener() {
@Override
public void run() {
public void instancesUpdated() {
instancesModel.update();
}
}, SwingExecutor.INSTANCE);
// On success, launch also
Futures.addCallback(future, new FutureCallback<Instance>() {
@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<Process> processFuture = new ObservableFuture<Process>(
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<Process>() {
@Override
public void onSuccess(Process result) {
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());
}
}

View File

@ -0,0 +1,17 @@
/*
* SKCraft Launcher
* Copyright (C) 2010-2014 Albert Pham <http://www.sk89q.com> 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();
}

View File

@ -64,8 +64,6 @@ public class LaunchProcessHandler implements Function<Process, ProcessConsoleFra
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new LauncherFrame(launcher).setVisible(true);
if (consoleFrame != null) {
consoleFrame.setProcess(null);
consoleFrame.requestFocus();

View File

@ -0,0 +1,162 @@
/*
* SKCraft Launcher
* Copyright (C) 2010-2014 Albert Pham <http://www.sk89q.com> 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<Instance> future = new ObservableFuture<Instance>(
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<Instance>() {
@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<Process> processFuture = new ObservableFuture<Process>(
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<Process>() {
@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());
}
}

View File

@ -22,8 +22,6 @@ import java.util.concurrent.Executors;
public class SelfUpdater implements Callable<File>, 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<File>, ProgressObservable {
progress = installer;
installer.execute();
updatedAlready = true;
return file;
} finally {
executor.shutdownNow();

View File

@ -0,0 +1,110 @@
/*
* SKCraft Launcher
* Copyright (C) 2010-2014 Albert Pham <http://www.sk89q.com> 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<URL> future = launcher.getExecutor().submit(new UpdateChecker(launcher));
Futures.addCallback(future, new FutureCallback<URL>() {
@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<File> future = new ObservableFuture<File>(
launcher.getExecutor().submit(downloader), downloader);
Futures.addCallback(future, new FutureCallback<File>() {
@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;
}
}