package de.oppermann.bastian.spleef.util; import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; import java.util.ArrayList; import java.util.logging.Level; import org.bukkit.Bukkit; import org.bukkit.ChatColor; import org.bukkit.command.CommandSender; import org.bukkit.plugin.InvalidDescriptionException; import org.bukkit.plugin.InvalidPluginException; import org.bukkit.plugin.UnknownDependencyException; import org.json.simple.JSONArray; import org.json.simple.JSONObject; import org.json.simple.JSONValue; import com.google.common.io.Files; import de.oppermann.bastian.spleef.SpleefMain; /** * Modified version of https://github.com/gravitylow/ServerModsAPI-Example/blob/master/Update.java */ public class UpdateChecker { // The project's unique ID private static final int PROJECT_ID = 88202; // no key in use at the moment //private final String apiKey = null; // Keys for extracting file information from JSON response private static final String API_NAME_VALUE = "name"; private static final String API_LINK_VALUE = "downloadUrl"; private static final String API_RELEASE_TYPE_VALUE = "releaseType"; private static final String API_FILE_NAME_VALUE = "fileName"; private static final String API_GAME_VERSION_VALUE = "gameVersion"; // Static information for querying the API private static final String API_QUERY = "/servermods/files?projectIds="; private static final String API_HOST = "https://api.curseforge.com"; private static final long QUERY_DELAY_MILLIS = 5 * 60 * 1000; private static EpicSpleefVersion[] lastQuery = null; private static long lastQueryTimestamp = 0; /** * Gets the newest version. * * @param unsafeUpdates Whether it should search for unsafe Updates, too, or not. * @return The newest version. Could be null if there is no newer version. */ public static EpicSpleefVersion getNewestVersion(boolean unsafeUpdates) { if (Bukkit.isPrimaryThread()) { throw new IllegalStateException("Must not be invoked from the main thread."); } EpicSpleefVersion[] versions = UpdateChecker.queryVersions(); ArrayList<EpicSpleefVersion> matches = new ArrayList<>(); String serverVersion = Bukkit.getVersion(); serverVersion = serverVersion.substring(serverVersion.indexOf("(MC: ") + 5, serverVersion.length()); serverVersion = serverVersion.substring(0, serverVersion.lastIndexOf(")")); String[] serverVersionArray = serverVersion.split("\\."); for (EpicSpleefVersion version : versions) { String pluginGameVersion = version.getVersionGameVersion(); String[] pluginGameVersionArray = pluginGameVersion.split("\\."); // just check the first and the second, cause the 3rd is unimportant most of the time (no huge api changes) int serverFirst = Integer.valueOf(serverVersionArray[0]); int serverSecond = Integer.valueOf(serverVersionArray[1]); int pluginFirst = Integer.valueOf(pluginGameVersionArray[0]); int pluginSecond = Integer.valueOf(pluginGameVersionArray[1]); if (serverFirst > pluginFirst) { continue; } if (serverFirst == pluginFirst && serverSecond > pluginSecond) { continue; } if (!unsafeUpdates && (serverFirst != pluginFirst || serverSecond != pluginSecond)) { continue; // no safe match } matches.add(version); } EpicSpleefVersion newest = null; versionLoop: for (EpicSpleefVersion version : matches) { String[] pluginVersion = version.getVersionName().replace("EpicSpleef-", "").replace("-SNAPSHOT", "").replace("-BETA", "").replace(".jar", "").split("\\."); String[] currentVersion = SpleefMain.getInstance().getDescription().getVersion().split("\\."); String[] newestVersion = newest == null ? new String[]{"0", "0", "0"} : newest.getVersionName().replace("EpicSpleef-", "").replace("-SNAPSHOT", "").replace("-BETA", "").replace(".jar", "").split("\\.");; int[] pluginVersionInt = new int[]{ Integer.valueOf(pluginVersion[0]), Integer.valueOf(pluginVersion[1]), Integer.valueOf(pluginVersion[2])}; int[] currentVersionInt = new int[]{ Integer.valueOf(currentVersion[0]), Integer.valueOf(currentVersion[1]), Integer.valueOf(currentVersion[2])}; int[] newestVersionInt = new int[]{ Integer.valueOf(newestVersion[0]), Integer.valueOf(newestVersion[1]), Integer.valueOf(newestVersion[2])}; for (int i = 0; i < 3; i++) { if (currentVersionInt[i] > pluginVersionInt[i] || newestVersionInt[i] > pluginVersionInt[i]) { continue versionLoop; } } if (currentVersionInt[0] == pluginVersionInt[0] && currentVersionInt[1] == pluginVersionInt[1] && currentVersionInt[2] == pluginVersionInt[2]) { continue versionLoop; } newest = version; } return newest; } /** * Compares two minecraft versions. * * @return True if the server is equal or higher. */ public static boolean compareMinecraftVersionServerIsHigherOrEqual(String version) { String serverVersion = Bukkit.getVersion(); serverVersion = serverVersion.substring(serverVersion.indexOf("(MC: ") + 5, serverVersion.length()); serverVersion = serverVersion.substring(0, serverVersion.lastIndexOf(")")); String[] serverVersionArray = serverVersion.split("\\."); String[] toCompareVersionArray = version.split("\\."); if (serverVersionArray.length == 2) { int serverFirst = Integer.valueOf(serverVersionArray[0]); int toCompareFirst = Integer.valueOf(toCompareVersionArray[0]); if (toCompareFirst != serverFirst) { return toCompareFirst < serverFirst; } int serverSecond = Integer.valueOf(serverVersionArray[1]); int toCompareSecond = Integer.valueOf(toCompareVersionArray[1]); if (toCompareSecond != serverSecond) { return toCompareSecond < serverSecond; } if (toCompareVersionArray.length == 3) { return false; } return true; } if (serverVersionArray.length == 3) { int serverFirst = Integer.valueOf(serverVersionArray[0]); int toCompareFirst = Integer.valueOf(toCompareVersionArray[0]); if (toCompareFirst != serverFirst) { return toCompareFirst < serverFirst; } int serverSecond = Integer.valueOf(serverVersionArray[1]); int toCompareSecond = Integer.valueOf(toCompareVersionArray[1]); if (toCompareSecond != serverSecond) { return toCompareSecond < serverSecond; } if (toCompareVersionArray.length != 3) { return true; } int serverThird = Integer.valueOf(serverVersionArray[2]); int toCompareThird = Integer.valueOf(toCompareVersionArray[2]); if (toCompareThird != serverThird) { return toCompareThird < serverThird; } return true; } return false; } /** * Gets all versions newer than the current one. */ public static EpicSpleefVersion[] getUpdates() { if (Bukkit.isPrimaryThread()) { throw new IllegalStateException("Must not be invoked from the main thread."); } ArrayList<EpicSpleefVersion> newerVersions = new ArrayList<>(); EpicSpleefVersion[] versions = UpdateChecker.queryVersions(); versionLoop: for (EpicSpleefVersion version : versions) { String[] pluginVersion = version.getVersionName().replace("EpicSpleef-", "").replace("-SNAPSHOT", "").replace("-BETA", "").replace(".jar", "").split("\\."); String[] currentVersion = SpleefMain.getInstance().getDescription().getVersion().split("\\."); int[] pluginVersionInt = new int[]{ Integer.valueOf(pluginVersion[0]), Integer.valueOf(pluginVersion[1]), Integer.valueOf(pluginVersion[2])}; int[] currentVersionInt = new int[]{ Integer.valueOf(currentVersion[0]), Integer.valueOf(currentVersion[1]), Integer.valueOf(currentVersion[2])}; for (int i = 0; i < 3; i++) { if (currentVersionInt[i] > pluginVersionInt[i]) { continue versionLoop; } } if (currentVersionInt[0] == pluginVersionInt[0] && currentVersionInt[1] == pluginVersionInt[1] && currentVersionInt[2] == pluginVersionInt[2]) { continue versionLoop; } newerVersions.add(version); } return newerVersions.toArray(new EpicSpleefVersion[newerVersions.size()]); } /** * Downloads the newest file. * * @param version The version to download. * @param install Whether the downloaded file should be installed or not. * @param messageReceiver The receiver of the messages. Can be null. */ public static void downloadFile(EpicSpleefVersion version, boolean install, CommandSender messageReceiver) { if (Bukkit.isPrimaryThread()) { throw new IllegalStateException("Must not be invoked from the main thread."); } BufferedInputStream in = null; FileOutputStream fout = null; try { URL fileUrl = new URL(version.getVersionLink()); final int fileLength = fileUrl.openConnection().getContentLength(); in = new BufferedInputStream(fileUrl.openStream()); final File file = new File(SpleefMain.getInstance().getDataFolder().getPath() + File.separator + "updates", version.getVersionFileName()); file.getParentFile().mkdirs(); file.createNewFile(); fout = new FileOutputStream(file); final byte[] data = new byte[1024]; int count; long downloaded = 0; int nextLogPercentage = 0; while ((count = in.read(data, 0, 1024)) != -1) { downloaded += count; fout.write(data, 0, count); final int percent = (int) ((downloaded * 100) / fileLength); if (percent == nextLogPercentage) { String message = Language.UPDATE_PROGRESS.toString().replace("%percentage%", String.valueOf(percent)).replace("%downloaded%", String.valueOf((int) (downloaded / 1024))).replace("%total%", String.valueOf((int) (fileLength / 1024))); if (messageReceiver != null) { messageReceiver.sendMessage(message); } SpleefMain.getInstance().log(Level.INFO, ChatColor.stripColor(message)); nextLogPercentage += 10; } } if (install) { if (messageReceiver != null) { messageReceiver.sendMessage(Language.UPDATE_DOWNLOAD_SUCCESSFULLY_NOW_INSTALL.toString()); } SpleefMain.getInstance().log(Level.INFO, ChatColor.stripColor(Language.UPDATE_DOWNLOAD_SUCCESSFULLY_NOW_INSTALL.toString())); } else { if (messageReceiver != null) { messageReceiver.sendMessage(Language.UPDATE_DOWNLOAD_SUCCESSFULLY.toString()); } SpleefMain.getInstance().log(Level.INFO, ChatColor.stripColor(Language.UPDATE_DOWNLOAD_SUCCESSFULLY.toString())); } // run sync if (install) { Bukkit.getScheduler().runTask(SpleefMain.getInstance(), new Runnable() { @Override public void run() { final File currentFile = SpleefMain.getInstance().getFile(); final String TO_PATH = SpleefMain.getInstance().getDataFolder().getParentFile().getPath() + File.separator + file.getName(); Bukkit.getPluginManager().disablePlugin(SpleefMain.getInstance()); new Thread(new Runnable() { @Override public void run() { currentFile.delete(); File to = new File(TO_PATH); try { Files.move(file, to); } catch (IOException e) { e.printStackTrace(); } try { Bukkit.getPluginManager().enablePlugin(Bukkit.getPluginManager().loadPlugin(to)); } catch (UnknownDependencyException e) { // should not happen (EpicSpleef does not have dependencies!) e.printStackTrace(); } catch (InvalidPluginException e) { // should not happen e.printStackTrace(); } catch (InvalidDescriptionException e) { // should not happen e.printStackTrace(); } } }).start();; } }); } } catch (Exception e) { if (messageReceiver != null) { messageReceiver.sendMessage(Language.UPDATE_ERROR.toString()); } SpleefMain.getInstance().log(Level.SEVERE, ChatColor.stripColor(Language.UPDATE_ERROR.toString())); e.printStackTrace(); } finally { try { if (in != null) { in.close(); } } catch (final IOException e) { e.printStackTrace(); } try { if (fout != null) { fout.close(); } } catch (final IOException e) { e.printStackTrace(); } } } /** * Query the API to find the approved file's details. */ public static EpicSpleefVersion[] queryVersions() { if (Bukkit.isPrimaryThread()) { throw new IllegalStateException("Must not be invoked from the main thread."); } if (lastQueryTimestamp + QUERY_DELAY_MILLIS > System.currentTimeMillis()) { // mojang does'nt like spam return lastQuery; } URL url = null; try { // Create the URL to query using the project's ID url = new URL(API_HOST + API_QUERY + PROJECT_ID); } catch (MalformedURLException e) { // There was an error creating the URL SpleefMain.getInstance().log(Level.SEVERE, "Could not connect to curse api!"); e.printStackTrace(); return new EpicSpleefVersion[0]; } try { // Open a connection and query the project URLConnection conn = url.openConnection(); /* if (apiKey != null) { // Add the API key to the request if present conn.addRequestProperty("X-API-Key", apiKey); } */ // Add the user-agent to identify the program conn.addRequestProperty("User-Agent", "EpicSpleef - Update Checker"); // Read the response of the query // The response will be in a JSON format, so only reading one line is necessary. final BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream())); String response = reader.readLine(); // Parse the array of files from the query's response JSONArray array = (JSONArray) JSONValue.parse(response); EpicSpleefVersion[] versions = new EpicSpleefVersion[array.size()]; for (int i = 0; i < array.size(); i++) { JSONObject jsonObject = (JSONObject) array.get(i); // Get the version's title String versionName = (String) jsonObject.get(API_NAME_VALUE); // Get the version's link String versionLink = (String) jsonObject.get(API_LINK_VALUE); // Get the version's release type String versionType = (String) jsonObject.get(API_RELEASE_TYPE_VALUE); // Get the version's file name String versionFileName = (String) jsonObject.get(API_FILE_NAME_VALUE); // Get the version's game version String versionGameVersion = (String) jsonObject.get(API_GAME_VERSION_VALUE); versions[i] = new EpicSpleefVersion(versionName, versionLink, versionType, versionFileName, versionGameVersion); } lastQuery = versions; lastQueryTimestamp = System.currentTimeMillis(); return versions; } catch (IOException e) { // There was an error reading the query SpleefMain.getInstance().log(Level.SEVERE, "Could not get information about plugin updates!"); e.printStackTrace(); return new EpicSpleefVersion[0]; } } }