Newer
Older
sklauncher / src / main / java / com / skcraft / launcher / update / BaseUpdater.java
/*
 * 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.update;

import com.skcraft.launcher.AssetsRoot;
import com.skcraft.launcher.Instance;
import com.skcraft.launcher.Launcher;
import com.skcraft.launcher.install.FileMover;
import com.skcraft.launcher.install.InstallLog;
import com.skcraft.launcher.install.Installer;
import com.skcraft.launcher.install.UpdateCache;
import com.skcraft.launcher.model.minecraft.Asset;
import com.skcraft.launcher.model.minecraft.AssetsIndex;
import com.skcraft.launcher.model.minecraft.Library;
import com.skcraft.launcher.model.minecraft.VersionManifest;
import com.skcraft.launcher.model.modpack.Manifest;
import com.skcraft.launcher.model.modpack.ManifestEntry;
import com.skcraft.launcher.persistence.Persistence;
import com.skcraft.launcher.util.Environment;
import com.skcraft.launcher.util.HttpRequest;
import lombok.NonNull;
import lombok.extern.java.Log;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;

import static com.skcraft.launcher.LauncherUtils.checkInterrupted;
import static com.skcraft.launcher.LauncherUtils.concat;

@Log
public abstract class BaseUpdater {

    private static final long JAR_SIZE_ESTIMATE = 5 * 1024 * 1024;
    private static final long LIBRARY_SIZE_ESTIMATE = 3 * 1024 * 1024;

    private final Launcher launcher;
    private final Environment environment = Environment.getInstance();
    private final List<Runnable> executeOnCompletion = new ArrayList<Runnable>();

    protected BaseUpdater(@NonNull Launcher launcher) {
        this.launcher = launcher;
    }

    protected void complete() {
        for (Runnable runnable : executeOnCompletion) {
            runnable.run();
        }
    }

    protected Manifest installPackage(@NonNull Installer installer, @NonNull Instance instance) throws Exception {
        final File contentDir = instance.getContentDir();
        final File logPath = new File(instance.getDir(), "install_log.json");
        final File cachePath = new File(instance.getDir(), "update_cache.json");

        final InstallLog previousLog = Persistence.read(logPath, InstallLog.class);
        final InstallLog currentLog = new InstallLog();
        currentLog.setBaseDir(contentDir);
        final UpdateCache updateCache = Persistence.read(cachePath, UpdateCache.class);

        Manifest manifest = HttpRequest
                .get(instance.getManifestURL())
                .execute()
                .expectResponseCode(200)
                .returnContent()
                .saveContent(instance.getManifestPath())
                .asJson(Manifest.class);

        if (manifest.getBaseUrl() == null) {
            manifest.setBaseUrl(instance.getManifestURL());
        }

        for (ManifestEntry entry : manifest.getTasks()) {
            entry.install(installer, currentLog, updateCache, contentDir);
        }

        executeOnCompletion.add(new Runnable() {
            @Override
            public void run() {
                for (Map.Entry<String, Set<String>> entry : previousLog.getEntrySet()) {
                    for (String path : entry.getValue()) {
                        if (!currentLog.has(path)) {
                            new File(contentDir, path).delete();
                        }
                    }
                }

                try {
                    Persistence.write(logPath, currentLog);
                } catch (IOException e) {
                    log.log(Level.WARNING, "Failed to write install log", e);
                }

                try {
                    Persistence.write(cachePath, updateCache);
                } catch (IOException e) {
                    log.log(Level.WARNING, "Failed to write update cache", e);
                }
            }
        });

        return manifest;
    }

    protected void installJar(@NonNull Installer installer,
                              @NonNull File jarFile,
                              @NonNull URL url) throws InterruptedException {
        // If the JAR does not exist, install it
        if (!jarFile.exists()) {
            List<File> targets = new ArrayList<File>();

            File tempFile = installer.getDownloader().download(url, "", JAR_SIZE_ESTIMATE, jarFile.getName());
            installer.queue(new FileMover(tempFile, jarFile));
            log.info("Installing " + jarFile.getName() + " from " + url);
        }
    }

    protected void installAssets(@NonNull Installer installer,
                                 @NonNull VersionManifest versionManifest,
                                 @NonNull URL indexUrl,
                                 @NonNull List<URL> sources) throws IOException, InterruptedException {
        AssetsRoot assetsRoot = launcher.getAssets();

        AssetsIndex index = HttpRequest
                .get(indexUrl)
                .execute()
                .expectResponseCode(200)
                .returnContent()
                .saveContent(assetsRoot.getIndexPath(versionManifest))
                .asJson(AssetsIndex.class);

        for (Map.Entry<String, Asset> entry : index.getObjects().entrySet()) {
            checkInterrupted();

            String hash = entry.getValue().getHash();
            String path = String.format("%s/%s", hash.subSequence(0, 2), hash);
            File targetFile = assetsRoot.getObjectPath(entry.getValue());

            if (!targetFile.exists()) {
                List<URL> urls = new ArrayList<URL>();
                for (URL sourceUrl : sources) {
                    try {
                        urls.add(concat(sourceUrl, path));
                    } catch (MalformedURLException e) {
                        log.log(Level.WARNING, "Bad source URL for library: " + sourceUrl);
                    }
                }

                File tempFile = installer.getDownloader().download(
                        sources, "", entry.getValue().getSize(), entry.getKey());
                installer.queue(new FileMover(tempFile, targetFile));
                log.info("Fetching " + path + " from " + urls);
            }
        }
    }

    protected void installLibraries(@NonNull Installer installer,
                                    @NonNull VersionManifest versionManifest,
                                    @NonNull File librariesDir,
                                    @NonNull List<URL> sources) throws InterruptedException {

        for (Library library : versionManifest.getLibraries()) {
            if (library.matches(environment)) {
                checkInterrupted();

                String path = library.getPath(environment);
                File targetFile = new File(librariesDir, path);

                if (!targetFile.exists()) {
                    List<URL> urls = new ArrayList<URL>();
                    for (URL sourceUrl : sources) {
                        try {
                            urls.add(concat(sourceUrl, path));
                        } catch (MalformedURLException e) {
                            log.log(Level.WARNING, "Bad source URL for library: " + sourceUrl);
                        }
                    }

                    File tempFile = installer.getDownloader().download(sources, "", LIBRARY_SIZE_ESTIMATE,
                            library.getName() + ".jar");
                    installer.queue(new FileMover( tempFile, targetFile));
                    log.info("Fetching " + path + " from " + urls);
                }
            }
        }
    }

}