/*
* 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.launch.LaunchOptions.UpdatePolicy;
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(LaunchOptions options) {
final Window window = options.getWindow();
final Instance instance = options.getInstance();
final LaunchListener listener = options.getListener();
try {
boolean update = options.getUpdatePolicy().isUpdateEnabled() && instance.isUpdatePending();
// Store last access date
Date now = new Date();
instance.setLastAccessed(now);
Persistence.commitAndForget(instance);
// Perform login
final Session session;
if (options.getSession() != null) {
session = options.getSession();
} else {
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(options.getUpdatePolicy() == UpdatePolicy.ALWAYS_UPDATE || 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());
}
}