Package org.spoutcraft.launcher

Source Code of org.spoutcraft.launcher.UpdateThread$DownloadListenerWrapper

/*
* This file is part of Spoutcraft Launcher.
*
* Copyright (c) 2011 Spout LLC <http://www.spout.org/>
* Spoutcraft Launcher is licensed under the Spout License Version 1.
*
* Spoutcraft Launcher is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* In addition, 180 days after any changes are published, you can use the
* software, incorporating those changes, under the terms of the MIT license,
* as described in the Spout License Version 1.
*
* Spoutcraft Launcher is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License,
* the MIT license and the Spout License Version 1 along with this program.
* If not, see <http://www.gnu.org/licenses/> for the GNU Lesser General Public
* License and see <http://spout.in/licensev1> for the full license,
* including the MIT license.
*/
package org.spoutcraft.launcher;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicReference;
import java.util.jar.JarFile;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.spoutcraft.launcher.api.Launcher;
import org.spoutcraft.launcher.api.SpoutcraftDirectories;
import org.spoutcraft.launcher.exceptions.RestfulAPIException;
import org.spoutcraft.launcher.exceptions.UnsupportedOSException;
import org.spoutcraft.launcher.launch.MinecraftClassLoader;
import org.spoutcraft.launcher.launch.MinecraftLauncher;
import org.spoutcraft.launcher.rest.Library;
import org.spoutcraft.launcher.rest.Minecraft;
import org.spoutcraft.launcher.rest.RestAPI;
import org.spoutcraft.launcher.rest.Versions;
import org.spoutcraft.launcher.util.Download;
import org.spoutcraft.launcher.util.DownloadListener;
import org.spoutcraft.launcher.util.DownloadUtils;
import org.spoutcraft.launcher.util.FileUtils;
import org.spoutcraft.launcher.util.MD5Utils;
import org.spoutcraft.launcher.util.OperatingSystem;
import org.spoutcraft.launcher.util.Utils;
import org.spoutcraft.launcher.util.Download.Result;
import org.spoutcraft.launcher.yml.Resources;
import org.spoutcraft.launcher.yml.YAMLProcessor;

public class UpdateThread extends Thread {
  /**
   * We only want to clean the old logs, temp folders once per startup
   */
  private static final AtomicBoolean cleaned = new AtomicBoolean(false);
  private static final int PRELOAD_CLASSES = 100;
  public static final String[] OLD_ASSETS = { "1.2.3", "1.2.5", "1.3.1", "1.3.2", "1.4.2", "1.4.4", "1.4.5", "1.4.6", "1.4.7", "1.5" };

  private final Logger logger = Logger.getLogger("launcher");
  private final AtomicBoolean waiting = new AtomicBoolean(false);
  private final AtomicBoolean valid = new AtomicBoolean(false);
  private final AtomicBoolean finished = new AtomicBoolean(false);
  private final StartupParameters params = Utils.getStartupParameters();
  private final DownloadListener listener = new DownloadListenerWrapper();
  private volatile SpoutcraftData build;
  private final Thread previous;
  public UpdateThread() {
    this(null);
  }

  public UpdateThread(Thread previous) {
    super("Update Thread");
    setDaemon(true);
    this.previous = previous;
  }

  public SpoutcraftData getBuild() {
    return build;
  }

  @Override
  public void run() {
    try {
      build = new SpoutcraftData();
      boolean interrupted = false;
      while(true) {
        try {
          if (previous != null) {
            previous.join();
          }
          break;
        } catch (InterruptedException e) {
          interrupted = true;
        }
      }
      if (!interrupted) {
        runTasks();
      }
    } catch (Exception e) {
      if (!this.isInterrupted()) {
        Launcher.getLoginFrame().handleException(e);
      }
    }
  }

  private void runTasks() throws IOException {
    while (!valid.get()) {
      boolean minecraftUpdate = isMinecraftUpdateAvailable(build);
      boolean spoutcraftUpdate = minecraftUpdate || isSpoutcraftUpdateAvailable(build);

      if (minecraftUpdate) {
        updateMinecraft(build);
      }
      if (spoutcraftUpdate) {
        updateSpoutcraft(build);
      }

      updateAssets();

      // Download assets
      if (cleaned.compareAndSet(false, true)) {
        Resources.VIP.getYAML();
        Resources.Special.getYAML();
        Versions.getMinecraftVersions();

        cleanLogs();
        cleanTemp();
        updateFiles();

        RestAPI.getCache().cleanup();
      }

      Validator validate = new Validator();
      if (!(params.isIgnoreMD5() || Settings.isIgnoreMD5())) {
        validate.run(build);
        valid.set(validate.isValid());
      } else {
        valid.set(true);
      }
    }

    MinecraftClassLoader loader;
    loader = MinecraftLauncher.getClassLoader(build.getLibraries());

    int loaded = 0;
    while (!waiting.get()) {
      int pass = loader.preloadClasses(PRELOAD_CLASSES);
      loaded += pass;
      // Less than the preload amount, so we are finished
      if (pass != PRELOAD_CLASSES) {
        break;
      }
    }
    logger.info("Preloaded " + loaded + " classes in advance");
    finished.set(true);
  }

  private void updateAssets() {
    YAMLProcessor assets = Resources.Assets.getYAML();
    if (assets != null) {
      updateAssets(assets.getMap(), Utils.getAssetsDirectory());
    }
  }

  @SuppressWarnings("unchecked")
  private void updateAssets(Map<String, Object> assets, File directory) {
    directory.mkdirs();
    for (Entry<String, Object> entry : assets.entrySet()) {
      String key = entry.getKey();
      Object value = entry.getValue();
      if (value instanceof Map<?, ?>) {
        updateAssets((Map<String, Object>)value, new File(directory, key));
      } else if (value instanceof String) {
        String url = (String)value;
        String name = getFileName(url);
        File asset = new File(directory, name);

        stateChanged("Verifying Asset: " + name, 0);

        boolean needDownload = true;
        if (asset.exists() && !(params.isIgnoreMD5() || Settings.isIgnoreMD5())) {
          String md5 = MD5Utils.getMD5(asset);
          Launcher.debug("Checking MD5 of " + asset.getName() + ". Expected MD5: " + key + " | Actual MD5: " + md5);
          needDownload = md5 == null || !md5.equals(key);
        } else if (asset.exists() && (params.isIgnoreMD5() || Settings.isIgnoreMD5())) {
          needDownload = false;
        }

        if (needDownload) {
          try {
            DownloadUtils.downloadFile(url, asset.getPath(), null, key, listener);
          } catch (IOException e) {
            logger.log(Level.SEVERE, "Failed to download asset [" + url + "]", e);
          }
        }
        stateChanged("Verified Asset: " + name, 100);
      } else {
        logger.warning("Unknown asset type for " + key + ". Type is " + value);
      }
    }
  }

  private String getFileName(String url) {
    String[] split = url.split("/");
    return split[split.length - 1];
  }

  private void updateFiles() {
    SpoutcraftDirectories dirs = new SpoutcraftDirectories();
    File oldConfig = new File(Utils.getWorkingDirectory(), "spoutcraft");
    if (oldConfig.exists() && oldConfig.isDirectory()) {
      moveDirectory(oldConfig, dirs.getSpoutcraftDir());
      FileUtils.deleteQuietly(oldConfig);
    }
  }

  private void moveDirectory(File dir, File newDir) {
    if (!dir.exists()) {
      dir.mkdirs();
    }
    for (File file : dir.listFiles()) {
      if (file.isDirectory()) {
        moveDirectory(file, new File(newDir, file.getName()));
      } else {
        file.renameTo(new File(newDir, file.getName()));
      }
    }
  }

  private void cleanTemp() {
    SpoutcraftDirectories dirs = new SpoutcraftDirectories();
    File binDir = dirs.getBinDir();
    for (File f : binDir.listFiles()) {
      if (f.isDirectory() && f.getName().startsWith("temp_")) {
        FileUtils.deleteQuietly(f);
      }
    }
  }

  private void cleanLogs() {
    File logDirectory = new File(Utils.getWorkingDirectory(), "logs");
    if (logDirectory.exists() && logDirectory.isDirectory()) {
      for (File log : logDirectory.listFiles()) {
        if (!log.getName().endsWith(".log")) {
          log.delete();
          continue;
        }

        if (!log.getName().startsWith("spoutcraft")) {
          log.delete();
          continue;
        }

        String[] split = log.getName().split("_");
        if (split.length != 2) {
          log.delete();
          continue;
        }

        String[] date = split[1].split("-");
        if (date.length != 3) {
          log.delete();
          continue;
        }
        date[2] = date[2].substring(0, date[2].length() - 4); // Trim .log extension
        try {
          int logYear = Integer.parseInt(date[0]);
          int logMonth = Integer.parseInt(date[1]);
          int logDay = Integer.parseInt(date[2]);

          Calendar logDate = Calendar.getInstance();
          // Add a month to the calendar (clear logs older than 1 month)
          if (logMonth < 12) {
            logMonth++;
          } else {
            logMonth = 1;
            logYear++;
          }
          logDate.set(logYear, logMonth, logDay);

          if (Calendar.getInstance().after(logDate)) {
            log.delete();
          }
        } catch (NumberFormatException ignore) {
          log.delete();
          continue;
        }
      }
    }
  }

  public void setWaiting(boolean waiting) {
    this.waiting.set(waiting);
  }

  public boolean isFinished() {
    return finished.get();
  }

  public boolean isValidInstall() {
    return valid.get();
  }

  public boolean isSpoutcraftUpdateAvailable(SpoutcraftData build) throws RestfulAPIException {
    if (!Utils.getWorkingDirectory().exists()) {
      return true;
    }
    if (!Launcher.getGameUpdater().getSpoutcraftDir().exists()) {
      return true;
    }

    List<Library> libraries = build.getLibraries();
    int steps = libraries.size() + 2;
    float progress = 100F;

    stateChanged("Checking for Spoutcraft update...", progress / steps);
    progress += 100F;
    File spoutcraft = new File(Launcher.getGameUpdater().getBinDir(), "spoutcraft.jar");
    if (!spoutcraft.exists()) {
      return true;
    }
    if (!Settings.isIgnoreMD5() && !build.getMD5().equalsIgnoreCase(MD5Utils.getMD5(spoutcraft))) {
      return true;
    }
    stateChanged("Checking for Spoutcraft update...", progress / steps);
    progress += 100F;
    File libDir = new File(Launcher.getGameUpdater().getBinDir(), "lib");
    libDir.mkdir();

    for (Library lib : libraries) {
      File libraryFile = new File(libDir, lib.name() + ".jar");
      if (!libraryFile.exists()) {
        return true;
      }
      stateChanged("Checking for Spoutcraft update...", progress / steps);
      progress += 100F;
    }

    return false;
  }

  public boolean isMinecraftUpdateAvailable(SpoutcraftData build) {
    int steps = 7;
    if (!Launcher.getGameUpdater().getBinDir().exists()) {
      return true;
    }
    stateChanged("Checking for Minecraft update...", 100F / steps);
    File nativesDir = new File(Launcher.getGameUpdater().getBinDir(), "natives");
    if (!nativesDir.exists()) {
      return true;
    }
    // Empty directory
    if (nativesDir.listFiles().length == 0) {
      return true;
    }
    stateChanged("Checking for Minecraft update...", 200F / steps);
    File minecraft = new File(Launcher.getGameUpdater().getBinDir(), "minecraft.jar");
    if (!minecraft.exists()) {
      return true;
    }
    stateChanged("Checking for Minecraft update...", 300F / steps);
    File lib = new File(Launcher.getGameUpdater().getBinDir(), "jinput.jar");
    if (!lib.exists()) {
      return true;
    }
    stateChanged("Checking for Minecraft update...", 400F / steps);
    lib = new File(Launcher.getGameUpdater().getBinDir(), "lwjgl.jar");
    if (!lib.exists()) {
      return true;
    }
    stateChanged("Checking for Minecraft update...", 500F / steps);
    lib = new File(Launcher.getGameUpdater().getBinDir(), "lwjgl_util.jar");
    if (!lib.exists()) {
      return true;
    }
    stateChanged("Checking for Minecraft update...", 600F / steps);
    String installed = Settings.getInstalledMC();
    stateChanged("Checking for Minecraft update...", 700F / steps);
    String required = build.getMinecraft().getVersion();
    return installed == null || !installed.equals(required);
  }

  // Searches the Minecraft libraries for the one matching the given artifact ID, returns the MD5
  protected static String findMd5(String artifactId, OperatingSystem platform, List<Library> libraries) {
    for (Library lib : libraries) {
      if (lib.getArtifactId().equalsIgnoreCase(artifactId)) {
        if (platform == null
          || platform.isWindows() && lib.getVersion().toLowerCase().endsWith("windows")
          || platform.isMac() && lib.getVersion().toLowerCase().endsWith("osx")
          || platform.isUnix() && lib.getVersion().toLowerCase().endsWith("linux")) {
          return lib.getMd5();
        }
      }
    }
    // Should never get here...
    throw new IllegalArgumentException("No valid library for " + artifactId + ", with platform " + platform + " was found");
  }

  public void updateMinecraft(SpoutcraftData build) throws IOException {
    Launcher.getGameUpdater().getBinDir().mkdir();
    Launcher.getGameUpdater().getBinCacheDir().mkdir();
    if (Launcher.getGameUpdater().getUpdateDir().exists()) {
      FileUtils.deleteDirectory(Launcher.getGameUpdater().getUpdateDir());
    }
    Launcher.getGameUpdater().getUpdateDir().mkdir();

    final Minecraft minecraft = build.getMinecraft();
    final String minecraftMD5 = minecraft.getMd5();
    final String jinputMD5 = findMd5("jinput", null, minecraft.getLibraries());
    final String lwjgl_utilMD5 = findMd5("lwjgl_util", null, minecraft.getLibraries());
    final String lwjglMD5 = findMd5("lwjgl", null, minecraft.getLibraries());

    // Process minecraft.jar
    logger.info("Minecraft Version: " + build.getMinecraft());
    File mcCache = new File(Launcher.getGameUpdater().getBinCacheDir(), "minecraft_" + minecraft.getVersion() + ".jar");
    if (!mcCache.exists() || (minecraftMD5 == null || !minecraftMD5.equals(MD5Utils.getMD5(mcCache)))) {
      if (Arrays.asList(OLD_ASSETS).contains(minecraft.getVersion())) {
        DownloadUtils.downloadFile("http://assets.minecraft.net/" + minecraft.getVersion().replaceAll("\\.", "_") + "/minecraft.jar", mcCache.getPath(), null, minecraftMD5, listener);
      } else {
        DownloadUtils.downloadFile("http://s3.amazonaws.com/Minecraft.Download/versions/" + minecraft.getVersion() + "/" + minecraft.getVersion() + ".jar", mcCache.getPath(), null, minecraftMD5, listener);
      }
    }
    Utils.copy(mcCache, new File(Launcher.getGameUpdater().getBinDir(), "minecraft.jar"));

    File nativesDir = new File(Launcher.getGameUpdater().getBinDir(), "natives");
    nativesDir.mkdir();

    stateChanged("Extracting Files...", 0);

    Settings.setInstalledMC(build.getMinecraft().getVersion());
    Settings.getYAML().save();
  }

  public void updateSpoutcraft(SpoutcraftData build) throws IOException {
    cleanupBinFoldersFor(build);

    Launcher.getGameUpdater().getUpdateDir().mkdirs();
    Launcher.getGameUpdater().getBinCacheDir().mkdirs();
    Launcher.getGameUpdater().getSpoutcraftDir().mkdirs();
    File cacheDir = new File(Launcher.getGameUpdater().getBinDir(), "cache");
    cacheDir.mkdir();

    // Process spoutcraft.jar
    logger.info("Spoutcraft Build: " + build.getBuild());
    File mcCache = new File(Launcher.getGameUpdater().getBinCacheDir(), "minecraft_" + build.getMinecraft().getVersion() + ".jar");
    File updateMC = new File(Launcher.getGameUpdater().getUpdateDir().getPath() + File.separator + "minecraft.jar");
    if (mcCache.exists()) {
      Utils.copy(mcCache, updateMC);
    }

    File spoutcraft = new File(Launcher.getGameUpdater().getBinDir(), "spoutcraft.jar");
    if (spoutcraft.exists()) {
      File spoutcraftCache;
      if (Integer.parseInt(build.getInstalledBuild()) > 0) {
        // Save our installed copy
        spoutcraftCache = new File(cacheDir, "spoutcraft_" + build.getInstalledBuild() + ".jar");
        if (!spoutcraftCache.exists()) {
          Utils.copy(spoutcraft, spoutcraftCache);
        }
      }
      spoutcraft.delete();
      // Check for an old copy of this build if it is already saved
      spoutcraftCache = new File(cacheDir, "spoutcraft_" + build.getBuild() + ".jar");
      if (spoutcraftCache.exists()) {
        Utils.copy(spoutcraftCache, spoutcraft);
      }
    }

    stateChanged("Looking Up Mirrors...", 0F);

    String url = build.getSpoutcraftURL();

    if (!spoutcraft.exists()) {
      Download download = DownloadUtils.downloadFile(url, Launcher.getGameUpdater().getUpdateDir() + File.separator + "spoutcraft.jar", null, build.getMD5(), listener);
      if (download.getResult() == Result.SUCCESS) {
        Utils.copy(download.getOutFile(), spoutcraft);
      }
    }

    File libDir = new File(Launcher.getGameUpdater().getBinDir(), "lib");
    libDir.mkdir();

    List<Library> allLibraries = new ArrayList<Library>();
    allLibraries.addAll(build.getLibraries());
    allLibraries.addAll(build.getMinecraft().getLibraries());
    for (Library lib : allLibraries) {
      File libraryFile = new File(libDir, lib.name() + ".jar");
      if (lib.getArtifactId().contains("lwjgl") || lib.getArtifactId().contains("jinput")) {

        if (lib.getVersion().contains("natives")) {
          File nativesFolder = new File(Launcher.getGameUpdater().getBinDir(), "natives");
          //This prevents downloading the wrong natives jar
          if (lib.getVersion().contains("osx") && !OperatingSystem.getOS().isMac()) {
            continue;
          } else if (lib.getVersion().contains("linux") && !OperatingSystem.getOS().isUnix()) {
            continue;
          } else if (lib.getVersion().contains("windows") && !OperatingSystem.getOS().isWindows()) {
            continue;
          }

          // Download natives

          File nativesJar = new File(Launcher.getGameUpdater().getUpdateDir(), "natives.jar");
          if (!nativesJar.exists() || !lib.valid(MD5Utils.getMD5(nativesJar))) {
            lib.download(nativesJar, listener);

            // Extract natives into the proper directory
            List<String> ignores = new ArrayList<String>();
            ignores.add("META-INF");
            File tempNatives = new File(Launcher.getGameUpdater().getUpdateDir(), "natives");
            Utils.extractJar(new JarFile(nativesJar), tempNatives, ignores);
            FileUtils.moveDirectory(tempNatives, nativesFolder);

          }
          continue;
        }

        libraryFile = new File(Launcher.getGameUpdater().getBinDir(), lib.getArtifactId() + ".jar");
      }

      if (libraryFile.exists()) {
        String computedMD5 = MD5Utils.getMD5(libraryFile);
        if (!lib.valid(computedMD5)) {
          logger.warning("MD5 check of " + libraryFile.getName() + " failed. Deleting and Redownloading.");
          libraryFile.delete();
        }
      }
      File cachedLibraryFile = new File(cacheDir, lib.name() + ".jar");
      if (cachedLibraryFile.exists()) {
        String computedMD5 = MD5Utils.getMD5(cachedLibraryFile);
        if (lib.valid(computedMD5)) {
          Utils.copy(cachedLibraryFile, libraryFile);
        }
      }
      if (!libraryFile.exists()) {
        lib.download(libraryFile, listener);
      }
    }
  }

  public void cleanupBinFoldersFor(SpoutcraftData build) {
    try {
      if (!Launcher.getGameUpdater().getBinDir().exists()) {
        return;
      }

      HashSet<String> neededBinFiles = new HashSet<String>(Arrays.asList(new String[]{"spoutcraft.jar", "minecraft.jar", "lwjgl.jar", "lwjgl_util.jar", "jinput.jar"}));
      for (File file : Launcher.getGameUpdater().getBinDir().listFiles()) {
        if (!file.isFile()) {
          continue;
        }
        if (neededBinFiles.contains(file.getName())) {
          continue;
        }
        file.delete();
      }
    } catch (Exception e) {
      System.err.println("Error while cleaning files: ");
      e.printStackTrace();
    }
  }

  public DownloadListener getDownloadListener() {
    return listener;
  }

  public void setDownloadListener(DownloadListener listener) {
    ((DownloadListenerWrapper)this.listener).setDownloadListener(listener);
  }

  public void stateChanged(String message, float progress) {
    if (listener != null) {
      listener.stateChanged(message, progress);
    }
  }

  private class DownloadListenerWrapper implements DownloadListener {
    private final AtomicReference<DownloadListener> wrapped = new AtomicReference<DownloadListener>(null);
    public void stateChanged(String fileName, float progress) {
      fileName = (new File(fileName)).getName();
      DownloadListener listener = wrapped.get();
      if (listener != null) {
        listener.stateChanged(fileName, progress);
      }
    }

    public void setDownloadListener(DownloadListener listener) {
      wrapped.set(listener);
    }
  }
}
TOP

Related Classes of org.spoutcraft.launcher.UpdateThread$DownloadListenerWrapper

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.