diff --git a/src/main/java/com/skcraft/launcher/auth/OfflineSession.java b/src/main/java/com/skcraft/launcher/auth/OfflineSession.java index 04223df..0431b2c 100644 --- a/src/main/java/com/skcraft/launcher/auth/OfflineSession.java +++ b/src/main/java/com/skcraft/launcher/auth/OfflineSession.java @@ -62,4 +62,9 @@ public class OfflineSession implements Session { return UserType.LEGACY; } + @Override + public boolean isOnline() { + return false; + } + } diff --git a/src/main/java/com/skcraft/launcher/auth/Session.java b/src/main/java/com/skcraft/launcher/auth/Session.java index 5aa5adf..95b3e58 100644 --- a/src/main/java/com/skcraft/launcher/auth/Session.java +++ b/src/main/java/com/skcraft/launcher/auth/Session.java @@ -64,4 +64,11 @@ public interface Session { */ UserType getUserType(); + /** + * Return true if the user is in an online session. + * + * @return true if online + */ + boolean isOnline(); + } diff --git a/src/main/java/com/skcraft/launcher/auth/YggdrasilLoginService.java b/src/main/java/com/skcraft/launcher/auth/YggdrasilLoginService.java index ca97a47..b686953 100644 --- a/src/main/java/com/skcraft/launcher/auth/YggdrasilLoginService.java +++ b/src/main/java/com/skcraft/launcher/auth/YggdrasilLoginService.java @@ -118,6 +118,11 @@ public class YggdrasilLoginService implements LoginService { public UserType getUserType() { return legacy ? UserType.LEGACY : UserType.MOJANG; } + + @Override + public boolean isOnline() { + return true; + } } } diff --git a/src/main/java/com/skcraft/launcher/dialog/LauncherFrame.java b/src/main/java/com/skcraft/launcher/dialog/LauncherFrame.java index d6783c1..92fe0da 100644 --- a/src/main/java/com/skcraft/launcher/dialog/LauncherFrame.java +++ b/src/main/java/com/skcraft/launcher/dialog/LauncherFrame.java @@ -14,7 +14,7 @@ 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.InstanceLauncher; +import com.skcraft.launcher.launch.Runner; import com.skcraft.launcher.launch.LaunchProcessHandler; import com.skcraft.launcher.persistence.Persistence; import com.skcraft.launcher.selfupdate.UpdateChecker; @@ -430,6 +430,7 @@ public class LauncherFrame extends JFrame { if (update) { // Execute the updater Updater updater = new Updater(launcher, instance); + updater.setOnline(session.isOnline()); ObservableFuture future = new ObservableFuture( launcher.getExecutor().submit(updater), updater); @@ -469,7 +470,7 @@ public class LauncherFrame extends JFrame { final File extractDir = launcher.createExtractDir(); // Get the process - InstanceLauncher task = new InstanceLauncher(launcher, instance, session, extractDir); + Runner task = new Runner(launcher, instance, session, extractDir); ObservableFuture processFuture = new ObservableFuture( launcher.getExecutor().submit(task), task); diff --git a/src/main/java/com/skcraft/launcher/install/HttpDownloader.java b/src/main/java/com/skcraft/launcher/install/HttpDownloader.java index 30a85f0..71b0060 100644 --- a/src/main/java/com/skcraft/launcher/install/HttpDownloader.java +++ b/src/main/java/com/skcraft/launcher/install/HttpDownloader.java @@ -135,7 +135,7 @@ public class HttpDownloader implements Downloader { synchronized (this) { if (failed.size() > 0) { - throw new IOException(failed.size() + "files could not be downloaded"); + throw new IOException(failed.size() + " files could not be downloaded"); } } } finally { diff --git a/src/main/java/com/skcraft/launcher/launch/InstanceLauncher.java b/src/main/java/com/skcraft/launcher/launch/Runner.java similarity index 93% rename from src/main/java/com/skcraft/launcher/launch/InstanceLauncher.java rename to src/main/java/com/skcraft/launcher/launch/Runner.java index b5eae54..a3237cb 100644 --- a/src/main/java/com/skcraft/launcher/launch/InstanceLauncher.java +++ b/src/main/java/com/skcraft/launcher/launch/Runner.java @@ -12,10 +12,7 @@ import com.google.common.base.Strings; import com.google.common.io.Files; import com.skcraft.concurrency.DefaultProgress; import com.skcraft.concurrency.ProgressObservable; -import com.skcraft.launcher.AssetsRoot; -import com.skcraft.launcher.Configuration; -import com.skcraft.launcher.Instance; -import com.skcraft.launcher.Launcher; +import com.skcraft.launcher.*; import com.skcraft.launcher.auth.Session; import com.skcraft.launcher.install.ZipExtract; import com.skcraft.launcher.model.minecraft.AssetsIndex; @@ -43,9 +40,9 @@ import static com.skcraft.launcher.util.SharedLocale._; * Handles the launching of an instance. */ @Log -public class InstanceLauncher implements Callable, ProgressObservable { +public class Runner implements Callable, ProgressObservable { - private ProgressObservable progress = new DefaultProgress(0, _("instanceLauncher.preparing")); + private ProgressObservable progress = new DefaultProgress(0, _("runner.preparing")); private final ObjectMapper mapper = new ObjectMapper(); private final Launcher launcher; @@ -69,8 +66,8 @@ public class InstanceLauncher implements Callable, ProgressObservable { * @param session the session * @param extractDir the directory to extract to */ - public InstanceLauncher(@NonNull Launcher launcher, @NonNull Instance instance, - @NonNull Session session, @NonNull File extractDir) { + public Runner(@NonNull Launcher launcher, @NonNull Instance instance, + @NonNull Session session, @NonNull File extractDir) { this.launcher = launcher; this.instance = instance; this.session = session; @@ -92,6 +89,10 @@ public class InstanceLauncher implements Callable, ProgressObservable { @Override public Process call() throws Exception { + if (!instance.isInstalled()) { + throw new LauncherException("Update required", _("runner.updateRequired")); + } + config = launcher.getConfig(); builder = new JavaProcessBuilder(); assetsRoot = launcher.getAssets(); @@ -105,7 +106,7 @@ public class InstanceLauncher implements Callable, ProgressObservable { progress = assetsBuilder; virtualAssetsDir = assetsBuilder.build(); - progress = new DefaultProgress(0.9, _("instanceLauncher.collectingArgs")); + progress = new DefaultProgress(0.9, _("runner.collectingArgs")); addJvmArgs(); addLibraries(); @@ -119,10 +120,10 @@ public class InstanceLauncher implements Callable, ProgressObservable { ProcessBuilder processBuilder = new ProcessBuilder(builder.buildCommand()); processBuilder.directory(instance.getContentDir()); - log.info("Launching: " + builder); + Runner.log.info("Launching: " + builder); checkInterrupted(); - progress = new DefaultProgress(1, _("instanceLauncher.startingJava")); + progress = new DefaultProgress(1, _("runner.startingJava")); return processBuilder.start(); } diff --git a/src/main/java/com/skcraft/launcher/swing/InstanceTableModel.java b/src/main/java/com/skcraft/launcher/swing/InstanceTableModel.java index eb9bd41..03887bf 100644 --- a/src/main/java/com/skcraft/launcher/swing/InstanceTableModel.java +++ b/src/main/java/com/skcraft/launcher/swing/InstanceTableModel.java @@ -20,12 +20,15 @@ public class InstanceTableModel extends AbstractTableModel { private final InstanceList instances; private final ImageIcon instanceIcon; + private final ImageIcon customInstanceIcon; private final ImageIcon downloadIcon; public InstanceTableModel(InstanceList instances) { this.instances = instances; instanceIcon = new ImageIcon(SwingHelper.readIconImage(Launcher.class, "instance_icon.png") .getScaledInstance(16, 16, Image.SCALE_SMOOTH)); + customInstanceIcon = new ImageIcon(SwingHelper.readIconImage(Launcher.class, "custom_instance_icon.png") + .getScaledInstance(16, 16, Image.SCALE_SMOOTH)); downloadIcon = new ImageIcon(SwingHelper.readIconImage(Launcher.class, "download_icon.png") .getScaledInstance(14, 14, Image.SCALE_SMOOTH)); } @@ -95,11 +98,19 @@ public class InstanceTableModel extends AbstractTableModel { @Override public Object getValueAt(int rowIndex, int columnIndex) { + Instance instance; switch (columnIndex) { case 0: - return instances.get(rowIndex).isLocal() ? instanceIcon : downloadIcon; + instance = instances.get(rowIndex); + if (!instance.isLocal()) { + return downloadIcon; + } else if (instance.getManifestURL() != null) { + return instanceIcon; + } else { + return customInstanceIcon; + } case 1: - Instance instance = instances.get(rowIndex); + instance = instances.get(rowIndex); return "" + SwingHelper.htmlEscape(instance.getTitle()) + getAddendum(instance) + ""; default: return null; diff --git a/src/main/java/com/skcraft/launcher/update/Updater.java b/src/main/java/com/skcraft/launcher/update/Updater.java index 58165c3..deccb77 100644 --- a/src/main/java/com/skcraft/launcher/update/Updater.java +++ b/src/main/java/com/skcraft/launcher/update/Updater.java @@ -12,12 +12,15 @@ import com.skcraft.concurrency.ProgressFilter; import com.skcraft.concurrency.ProgressObservable; import com.skcraft.launcher.Instance; import com.skcraft.launcher.Launcher; +import com.skcraft.launcher.LauncherException; import com.skcraft.launcher.install.Installer; import com.skcraft.launcher.model.minecraft.VersionManifest; import com.skcraft.launcher.model.modpack.Manifest; import com.skcraft.launcher.persistence.Persistence; import com.skcraft.launcher.util.HttpRequest; +import lombok.Getter; import lombok.NonNull; +import lombok.Setter; import lombok.extern.java.Log; import java.io.File; @@ -40,11 +43,13 @@ public class Updater extends BaseUpdater implements Callable, Progress private final Launcher launcher; private final Instance instance; + @Getter @Setter + private boolean online; + private List librarySources = new ArrayList(); private List assetsSources = new ArrayList(); - private ProgressObservable progress = new DefaultProgress( - -1, _("instanceUpdater.preparingUpdate")); + private ProgressObservable progress = new DefaultProgress(-1, _("instanceUpdater.preparingUpdate")); public Updater(@NonNull Launcher launcher, @NonNull Instance instance) { super(launcher); @@ -59,16 +64,34 @@ public class Updater extends BaseUpdater implements Callable, Progress @Override public Instance call() throws Exception { - Updater.log.info("Checking for an update for '" + instance.getName() + "'..."); - if (instance.getManifestURL() == null) { - Updater.log.log(Level.INFO, - "No URL set for {0}, so it can't be updated (the modpack may be removed from the server)", - new Object[] { instance }); - } else if (instance.isUpdatePending() || !instance.isInstalled()) { - Updater.log.log(Level.INFO, "Updating {0}...", new Object[]{instance}); + log.info("Checking for an update for '" + instance.getName() + "'..."); + + boolean updateRequired = !instance.isInstalled(); + boolean updateDesired = (instance.isUpdatePending() || updateRequired); + boolean updateCapable = (instance.getManifestURL() != null); + + if (!online && updateRequired) { + log.info("Can't update " + instance.getTitle() + " because offline"); + String message = _("updater.updateRequiredButOffline"); + throw new LauncherException("Update required but currently offline", message); + } + + if (updateDesired && !updateCapable) { + if (updateRequired) { + log.info("Update required for " + instance.getTitle() + " but there is no manifest"); + String message = _("updater.updateRequiredButNoManifest"); + throw new LauncherException("Update required but no manifest", message); + } else { + log.info("Can't update " + instance.getTitle() + ", but update is not required"); + return instance; // Can't update + } + } + + if (updateDesired) { + log.info("Updating " + instance.getTitle() + "..."); update(instance); } else { - Updater.log.log(Level.INFO, "No update found for {0}.", new Object[] { instance }); + log.info("No update found for " + instance.getTitle()); } return instance; @@ -146,7 +169,7 @@ public class Updater extends BaseUpdater implements Callable, Progress instance.setLocal(true); Persistence.commitAndForget(instance); - Updater.log.log(Level.INFO, instance.getName() + + log.log(Level.INFO, instance.getName() + " has been updated to version " + manifest.getVersion() + "."); } diff --git a/src/main/resources/com/skcraft/launcher/custom_instance_icon.png b/src/main/resources/com/skcraft/launcher/custom_instance_icon.png new file mode 100644 index 0000000..867849c Binary files /dev/null and b/src/main/resources/com/skcraft/launcher/custom_instance_icon.png differ diff --git a/src/main/resources/com/skcraft/launcher/lang/Launcher.properties b/src/main/resources/com/skcraft/launcher/lang/Launcher.properties index 030a1f7..624ab64 100644 --- a/src/main/resources/com/skcraft/launcher/lang/Launcher.properties +++ b/src/main/resources/com/skcraft/launcher/lang/Launcher.properties @@ -142,6 +142,8 @@ installer.copyingFile=Copying from {0} to {1} installer.movingFile=Moving {0} to {1} updater.updating=Updating launcher... +updater.updateRequiredButOffline=An update is required but you need to be in online mode. +updater.updateRequiredButNoManifest=An update is required but update information for this instance is no longer available. instanceUpdater.preparingUpdate=Preparing to update... instanceUpdater.readingManifest=Reading package manifest... @@ -151,9 +153,11 @@ instanceDeleter.deleting=Deleting {0}... instanceResetter.resetting=Resetting {0}... instanceLoader.loadingLocal=Loading local instances from disk... instanceLoader.checkingRemote=Checking for new modpacks... -instanceLauncher.preparing=Preparing for launch... -instanceLauncher.collectingArgs=Collecting arguments... -instanceLauncher.startingJava=Starting java... + +runner.preparing=Preparing for launch... +runner.collectingArgs=Collecting arguments... +runner.startingJava=Starting java... +runner.updateRequired=This instance must be updated before it can be run. assets.expanding1=Expanding {0} asset... ({1} remaining) assets.expandingN=Expanding {0} assets... ({1} remaining)