Update updater
- Updated the updater to use the same system BigDoors uses. This system is much cleaner and better and it uses Spiget to check for updates instead of DBO. - Introduced new config option "auto-update" to control if new updates are downloaded automatically or not.
This commit is contained in:
parent
51854fa150
commit
e093b18124
@ -10,7 +10,7 @@ import nl.pim16aap2.armoredElytra.util.ArmorTier;
|
||||
import nl.pim16aap2.armoredElytra.util.ArmorTierName;
|
||||
import nl.pim16aap2.armoredElytra.util.ConfigLoader;
|
||||
import nl.pim16aap2.armoredElytra.util.Messages;
|
||||
import nl.pim16aap2.armoredElytra.util.Update;
|
||||
import nl.pim16aap2.armoredElytra.util.UpdateManager;
|
||||
import org.bstats.bukkit.Metrics;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.ChatColor;
|
||||
@ -25,10 +25,10 @@ import java.util.Objects;
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
// TODO: Use this for NBT stuff: https://www.spigotmc.org/resources/item-entity-tile-nbt-api.7939/
|
||||
// TODO: Figure out if the config really does read the list of enchantments accurately. A bug report with a customized config seemed to load the default settings...
|
||||
// TODO: Verify enchantments on startup. Remove them from the list if they're invalid.
|
||||
// TODO: Don't delete the config file. Look at BigDoors.
|
||||
// TODO: Don't delete the config/translation file. Look at BigDoors.
|
||||
// TODO: Enchanting should require XP.
|
||||
|
||||
public class ArmoredElytra extends JavaPlugin implements Listener
|
||||
{
|
||||
@ -36,13 +36,13 @@ public class ArmoredElytra extends JavaPlugin implements Listener
|
||||
private Messages messages;
|
||||
private ConfigLoader config;
|
||||
|
||||
// private String leatherName, ironName, goldName, chainName, diamondName;
|
||||
private final Map<ArmorTier, ArmorTierName> armorTierNames = new EnumMap(ArmorTier.class);
|
||||
private final Map<ArmorTier, ArmorTierName> armorTierNames = new EnumMap<>(ArmorTier.class);
|
||||
private String elytraReceivedMessage;
|
||||
private String usageDeniedMessage;
|
||||
private String elytraLore;
|
||||
private boolean upToDate;
|
||||
private boolean is1_9;
|
||||
private UpdateManager updateManager;
|
||||
|
||||
@Override
|
||||
public void onEnable()
|
||||
@ -52,48 +52,10 @@ public class ArmoredElytra extends JavaPlugin implements Listener
|
||||
messages = new Messages(this);
|
||||
readMessages();
|
||||
|
||||
updateManager = new UpdateManager(this, 47136);
|
||||
|
||||
// Check if the user allows checking for updates.
|
||||
if (config.checkForUpdates())
|
||||
{
|
||||
// Check for updates in a new thread, so the server won't hang when it cannot contact the update servers.
|
||||
final Thread thread = new Thread(
|
||||
() ->
|
||||
{
|
||||
final ArmoredElytra plugin = getPlugin();
|
||||
final Update update = new Update(278437, plugin);
|
||||
final String latestVersion = update.getLatestVersion();
|
||||
|
||||
if (latestVersion == null)
|
||||
plugin.myLogger(Level.WARNING,
|
||||
"Encountered problem contacting update servers! Please check manually! The error above does not affect the plugin!");
|
||||
else
|
||||
{
|
||||
final String thisVersion = plugin.getDescription().getVersion();
|
||||
// Check if this is the latest version or not.
|
||||
final int updateStatus = update.versionCompare(latestVersion, thisVersion);
|
||||
|
||||
if (updateStatus > 0)
|
||||
{
|
||||
// Load the loginHandler to show messages to the user when they join.
|
||||
Bukkit.getPluginManager()
|
||||
.registerEvents(new LoginHandler(plugin, "The Armored Elytra plugin is out of date!"),
|
||||
plugin);
|
||||
plugin.myLogger(Level.INFO, "Plugin out of date! You are using version " + thisVersion +
|
||||
" but the latest version is version " + latestVersion + "!");
|
||||
plugin.setUpToDate(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
plugin.setUpToDate(true);
|
||||
plugin.myLogger(Level.INFO, "You seem to be using the latest version of this plugin!");
|
||||
}
|
||||
}
|
||||
});
|
||||
thread.start();
|
||||
}
|
||||
else
|
||||
myLogger(Level.INFO,
|
||||
"Plugin update checking not enabled! You will not receive any messages about new updates for this plugin. Please consider turning this on in the config.");
|
||||
updateManager.setEnabled(config.checkForUpdates(), config.autoDLUpdate());
|
||||
|
||||
if (config.allowStats())
|
||||
{
|
||||
@ -280,6 +242,11 @@ public class ArmoredElytra extends JavaPlugin implements Listener
|
||||
player.getInventory().addItem(item);
|
||||
}
|
||||
|
||||
public UpdateManager getUpdateManager()
|
||||
{
|
||||
return updateManager;
|
||||
}
|
||||
|
||||
// Check + initialize for the correct version of Minecraft.
|
||||
public boolean compatibleMCVer()
|
||||
{
|
||||
|
@ -1,5 +1,9 @@
|
||||
package nl.pim16aap2.armoredElytra.util;
|
||||
|
||||
import nl.pim16aap2.armoredElytra.ArmoredElytra;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
@ -9,11 +13,6 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.configuration.file.FileConfiguration;
|
||||
|
||||
import nl.pim16aap2.armoredElytra.ArmoredElytra;
|
||||
|
||||
public class ConfigLoader
|
||||
{
|
||||
private final String header;
|
||||
@ -25,6 +24,7 @@ public class ConfigLoader
|
||||
private int IRON_TO_FULL;
|
||||
private boolean uninstallMode;
|
||||
private boolean checkForUpdates;
|
||||
private boolean autoDLUpdate;
|
||||
private int LEATHER_TO_FULL;
|
||||
private int DIAMONDS_TO_FULL;
|
||||
private boolean noFlightDurability;
|
||||
@ -33,7 +33,7 @@ public class ConfigLoader
|
||||
public boolean bypassWearPerm;
|
||||
public boolean bypassCraftPerm;
|
||||
|
||||
private ArrayList<ConfigOption<?>> configOptionsList;
|
||||
private ArrayList<nl.pim16aap2.armoredElytra.util.ConfigOption<?>> configOptionsList;
|
||||
private ArmoredElytra plugin;
|
||||
|
||||
public ConfigLoader(ArmoredElytra plugin)
|
||||
@ -48,22 +48,22 @@ public class ConfigLoader
|
||||
private void makeConfig()
|
||||
{
|
||||
// All the comments for the various config options.
|
||||
String[] unbreakableComment =
|
||||
String[] unbreakableComment =
|
||||
{
|
||||
"Setting this to true will cause armored elytras to be unbreakable.",
|
||||
"Changing this to false will NOT make unbreakable elytras breakable again!"
|
||||
"Setting this to true will cause armored elytras to be unbreakable.",
|
||||
"Changing this to false will NOT make unbreakable elytras breakable again!"
|
||||
};
|
||||
String[] flyDurabilityComment =
|
||||
String[] flyDurabilityComment =
|
||||
{
|
||||
"Setting this to true will cause armored elytras to not lose any durability while flying.",
|
||||
"This is not a permanent option and will affect ALL elytras."
|
||||
"Setting this to true will cause armored elytras to not lose any durability while flying.",
|
||||
"This is not a permanent option and will affect ALL elytras."
|
||||
};
|
||||
String[] repairComment =
|
||||
String[] repairComment =
|
||||
{
|
||||
"Amount of items it takes to fully repair an armored elytra",
|
||||
"Repair cost for every tier of armored elytra in number of items to repair 100%."
|
||||
};
|
||||
String[] enchantmentsComment =
|
||||
String[] enchantmentsComment =
|
||||
{
|
||||
"List of enchantments that are allowed to be put on an armored elytra.",
|
||||
"If you do not want to allow any enchantments at all, remove them all and add \"NONE\"",
|
||||
@ -71,20 +71,25 @@ public class ConfigLoader
|
||||
"https://hub.spigotmc.org/javadocs/spigot/org/bukkit/enchantments/Enchantment.html",
|
||||
"Note that only 1 protection enchantment (PROTECTION_FIRE, PROTECTION_ENVIRONMENTAL etc) can be active on an elytra."
|
||||
};
|
||||
String[] updateComment =
|
||||
String[] updateComment =
|
||||
{
|
||||
"Allow this plugin to check for updates on startup. It will not download new versions!"
|
||||
"Allow this plugin to check for updates on startup. It will not download new versions unless \"auto-update is enabled\'!"
|
||||
};
|
||||
String[] bStatsComment =
|
||||
String[] autoDLUpdateComment =
|
||||
{
|
||||
"Allow this plugin to automatically download new updates. They will be applied on restart.",
|
||||
"This option has no effect if \"checkForUpdates\" is disabled."
|
||||
};
|
||||
String[] bStatsComment =
|
||||
{
|
||||
"Allow this plugin to send (anonymised) stats using bStats. Please consider keeping it enabled.",
|
||||
"It has a negligible impact on performance and more users on stats keeps me more motivated to support this plugin!"
|
||||
};
|
||||
String[] debugComment =
|
||||
String[] debugComment =
|
||||
{
|
||||
"Print debug messages to console. You will most likely never need this."
|
||||
};
|
||||
String[] uninstallComment =
|
||||
String[] uninstallComment =
|
||||
{
|
||||
"Setting this to true will disable this plugin and remove any armored elytras it can find.",
|
||||
"It will check player's inventories and their end chest upon login and any regular chest when it is opened.",
|
||||
@ -92,7 +97,7 @@ public class ConfigLoader
|
||||
"a lot of resources, so you can just leave the plugin enabled and ignore it.",
|
||||
"Please do not forget to MAKE A BACKUP before enabling this option!"
|
||||
};
|
||||
String[] languageFileComment =
|
||||
String[] languageFileComment =
|
||||
{
|
||||
"Specify a language file to be used. Note that en_US.txt will get regenerated!"
|
||||
};
|
||||
@ -112,8 +117,9 @@ public class ConfigLoader
|
||||
|
||||
// Set default list of allowed enchantments.
|
||||
allowedEnchantments = new ArrayList<>(Arrays.asList("DURABILITY", "PROTECTION_FIRE", "PROTECTION_EXPLOSIONS",
|
||||
"PROTECTION_PROJECTILE", "PROTECTION_ENVIRONMENTAL", "THORNS",
|
||||
"BINDING_CURSE", "VANISHING_CURSE", "MENDING"));
|
||||
"PROTECTION_PROJECTILE", "PROTECTION_ENVIRONMENTAL",
|
||||
"THORNS",
|
||||
"BINDING_CURSE", "VANISHING_CURSE", "MENDING"));
|
||||
|
||||
FileConfiguration config = plugin.getConfig();
|
||||
|
||||
@ -123,9 +129,12 @@ public class ConfigLoader
|
||||
GOLD_TO_FULL = addNewConfigOption(config, "goldRepair", 5, null);
|
||||
IRON_TO_FULL = addNewConfigOption(config, "ironRepair", 4, null);
|
||||
DIAMONDS_TO_FULL = addNewConfigOption(config, "diamondsRepair", 3, null);
|
||||
allowedEnchantments = addNewConfigOption(config, "allowedEnchantments", allowedEnchantments, enchantmentsComment);
|
||||
allowMultipleProtectionEnchantments = addNewConfigOption(config, "allowMultipleProtectionEnchantments", false, allowMultipleProtectionEnchantmentsComment);
|
||||
allowedEnchantments = addNewConfigOption(config, "allowedEnchantments", allowedEnchantments,
|
||||
enchantmentsComment);
|
||||
allowMultipleProtectionEnchantments = addNewConfigOption(config, "allowMultipleProtectionEnchantments", false,
|
||||
allowMultipleProtectionEnchantmentsComment);
|
||||
checkForUpdates = addNewConfigOption(config, "checkForUpdates", true, updateComment);
|
||||
autoDLUpdate = addNewConfigOption(config, "auto-update", true, autoDLUpdateComment);
|
||||
allowStats = addNewConfigOption(config, "allowStats", true, bStatsComment);
|
||||
enableDebug = addNewConfigOption(config, "enableDebug", false, debugComment);
|
||||
uninstallMode = addNewConfigOption(config, "uninstallMode", false, uninstallComment);
|
||||
@ -138,7 +147,8 @@ public class ConfigLoader
|
||||
|
||||
private <T> T addNewConfigOption(FileConfiguration config, String optionName, T defaultValue, String[] comment)
|
||||
{
|
||||
ConfigOption<T> option = new ConfigOption<>(plugin, config, optionName, defaultValue, comment);
|
||||
nl.pim16aap2.armoredElytra.util.ConfigOption<T> option = new nl.pim16aap2.armoredElytra.util.ConfigOption<>(
|
||||
plugin, config, optionName, defaultValue, comment);
|
||||
configOptionsList.add(option);
|
||||
return option.getValue();
|
||||
}
|
||||
@ -161,7 +171,7 @@ public class ConfigLoader
|
||||
saveTo.delete();
|
||||
saveTo.createNewFile();
|
||||
}
|
||||
FileWriter fw = new FileWriter(saveTo, true);
|
||||
FileWriter fw = new FileWriter(saveTo, true);
|
||||
PrintWriter pw = new PrintWriter(fw);
|
||||
|
||||
if (header != null)
|
||||
@ -169,16 +179,17 @@ public class ConfigLoader
|
||||
|
||||
for (int idx = 0; idx < configOptionsList.size(); ++idx)
|
||||
pw.println(configOptionsList.get(idx).toString() +
|
||||
// Only print an additional newLine if the next config option has a comment.
|
||||
(idx < configOptionsList.size() - 1 && configOptionsList.get(idx + 1).getComment() == null ? ""
|
||||
: "\n"));
|
||||
// Only print an additional newLine if the next config option has a comment.
|
||||
(idx < configOptionsList.size() - 1 &&
|
||||
configOptionsList.get(idx + 1).getComment() == null ? "" : "\n"));
|
||||
|
||||
pw.flush();
|
||||
pw.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
Bukkit.getLogger().log(Level.SEVERE, "Could not save config.yml! Please contact pim16aap2 and show him the following code:");
|
||||
Bukkit.getLogger().log(Level.SEVERE,
|
||||
"Could not save config.yml! Please contact pim16aap2 and show him the following code:");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
@ -239,6 +250,11 @@ public class ConfigLoader
|
||||
return checkForUpdates;
|
||||
}
|
||||
|
||||
public boolean autoDLUpdate()
|
||||
{
|
||||
return autoDLUpdate;
|
||||
}
|
||||
|
||||
public boolean noFlightDurability()
|
||||
{
|
||||
return noFlightDurability;
|
||||
|
466
src/main/java/nl/pim16aap2/armoredElytra/util/UpdateChecker.java
Normal file
466
src/main/java/nl/pim16aap2/armoredElytra/util/UpdateChecker.java
Normal file
@ -0,0 +1,466 @@
|
||||
package nl.pim16aap2.armoredElytra.util;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import nl.pim16aap2.armoredElytra.ArmoredElytra;
|
||||
import org.apache.commons.lang.math.NumberUtils;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.time.Instant;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.logging.Level;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A utility class to assist in checking for updates for plugins uploaded to
|
||||
* <a href="https://spigotmc.org/resources/">SpigotMC</a>. Before any members of
|
||||
* this class are accessed, {@link #init(ArmoredElytra, int)} must be invoked by the plugin, preferably in its {@link
|
||||
* JavaPlugin#onEnable()} method, though that is not a requirement.
|
||||
* <p>
|
||||
* This class performs asynchronous queries to
|
||||
* <a href="https://spiget.org">SpiGet</a>, an REST server which is updated
|
||||
* periodically. If the results of {@link #requestUpdateCheck()} are inconsistent with what is published on SpigotMC, it
|
||||
* may be due to SpiGet's cache. Results will be updated in due time.
|
||||
* <p>
|
||||
* Some modifications were made to support downloading of updates and storing the age of an update.
|
||||
*
|
||||
* @author Parker Hawke - 2008Choco
|
||||
*/
|
||||
public final class UpdateChecker
|
||||
{
|
||||
public static final VersionScheme VERSION_SCHEME_DECIMAL = (first, second) ->
|
||||
{
|
||||
String[] firstSplit = splitVersionInfo(first), secondSplit = splitVersionInfo(second);
|
||||
if (firstSplit == null || secondSplit == null)
|
||||
return null;
|
||||
|
||||
for (int i = 0; i < Math.min(firstSplit.length, secondSplit.length); i++)
|
||||
{
|
||||
int currentValue = NumberUtils.toInt(firstSplit[i]), newestValue = NumberUtils.toInt(secondSplit[i]);
|
||||
|
||||
if (newestValue > currentValue)
|
||||
return second;
|
||||
else if (newestValue < currentValue)
|
||||
return first;
|
||||
}
|
||||
|
||||
return (secondSplit.length > firstSplit.length) ? second : first;
|
||||
};
|
||||
|
||||
private static final String USER_AGENT = "ArmoredElytra-update-checker";
|
||||
private static final String UPDATE_URL = "https://api.spiget.org/v2/resources/%d/versions?size=1&sort=-releaseDate";
|
||||
private static final Pattern DECIMAL_SCHEME_PATTERN = Pattern.compile("\\d+(?:\\.\\d+)*");
|
||||
private final String downloadURL;
|
||||
|
||||
private static UpdateChecker instance;
|
||||
|
||||
private UpdateResult lastResult = null;
|
||||
|
||||
private final ArmoredElytra plugin;
|
||||
private final int pluginID;
|
||||
private final VersionScheme versionScheme;
|
||||
|
||||
private UpdateChecker(final ArmoredElytra plugin, final int pluginID, final VersionScheme versionScheme)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
this.pluginID = pluginID;
|
||||
this.versionScheme = versionScheme;
|
||||
downloadURL = "https://api.spiget.org/v2/resources/" + pluginID + "/download";
|
||||
}
|
||||
|
||||
/**
|
||||
* Requests an update check to SpiGet. This request is asynchronous and may not complete immediately as an HTTP GET
|
||||
* request is published to the SpiGet API.
|
||||
*
|
||||
* @return a future update result
|
||||
*/
|
||||
public CompletableFuture<UpdateResult> requestUpdateCheck()
|
||||
{
|
||||
return CompletableFuture.supplyAsync(
|
||||
() ->
|
||||
{
|
||||
int responseCode = -1;
|
||||
try
|
||||
{
|
||||
URL url = new URL(String.format(UPDATE_URL, pluginID));
|
||||
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
|
||||
connection.addRequestProperty("User-Agent", USER_AGENT);
|
||||
|
||||
InputStreamReader reader = new InputStreamReader(connection.getInputStream());
|
||||
responseCode = connection.getResponseCode();
|
||||
|
||||
JsonElement element = new JsonParser().parse(reader);
|
||||
if (!element.isJsonArray())
|
||||
return new UpdateResult(UpdateReason.INVALID_JSON);
|
||||
|
||||
reader.close();
|
||||
|
||||
JsonObject versionObject = element.getAsJsonArray().get(0).getAsJsonObject();
|
||||
|
||||
long age = -1;
|
||||
String ageString = versionObject.get("releaseDate").getAsString();
|
||||
try
|
||||
{
|
||||
age = getAge(Long.parseLong(ageString));
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
{
|
||||
plugin.myLogger(Level.WARNING,
|
||||
"Failed to obtain age of update from ageString: \"" + ageString + "\"");
|
||||
}
|
||||
|
||||
String current = plugin.getDescription().getVersion(), newest = versionObject.get("name")
|
||||
.getAsString();
|
||||
String latest = versionScheme.compareVersions(current, newest);
|
||||
|
||||
if (latest == null)
|
||||
return new UpdateResult(UpdateReason.UNSUPPORTED_VERSION_SCHEME);
|
||||
else if (latest.equals(current))
|
||||
return new UpdateResult(current.equals(newest) ?
|
||||
UpdateReason.UP_TO_DATE :
|
||||
UpdateReason.UNRELEASED_VERSION, current, age);
|
||||
else if (latest.equals(newest))
|
||||
return new UpdateResult(UpdateReason.NEW_UPDATE, latest, age);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
return new UpdateResult(UpdateReason.COULD_NOT_CONNECT);
|
||||
}
|
||||
catch (JsonSyntaxException e)
|
||||
{
|
||||
return new UpdateResult(UpdateReason.INVALID_JSON);
|
||||
}
|
||||
|
||||
return new UpdateResult(responseCode == 401 ?
|
||||
UpdateReason.UNAUTHORIZED_QUERY : UpdateReason.UNKNOWN_ERROR);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the difference in seconds between a given time and the current time.
|
||||
*
|
||||
* @param updateTime A moment in time to compare the current time to.
|
||||
* @return The difference in seconds between a given time and the current time.
|
||||
*/
|
||||
private long getAge(final long updateTime)
|
||||
{
|
||||
long currentTime = Instant.now().getEpochSecond();
|
||||
return currentTime - updateTime;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last update result that was queried by {@link #requestUpdateCheck()}. If no update check was performed
|
||||
* since this class' initialization, this method will return null.
|
||||
*
|
||||
* @return the last update check result. null if none.
|
||||
*/
|
||||
public UpdateResult getLastResult()
|
||||
{
|
||||
return lastResult;
|
||||
}
|
||||
|
||||
private static String[] splitVersionInfo(String version)
|
||||
{
|
||||
Matcher matcher = DECIMAL_SCHEME_PATTERN.matcher(version);
|
||||
if (!matcher.find())
|
||||
return null;
|
||||
|
||||
return matcher.group().split("\\.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the url to download the latest version from.
|
||||
*
|
||||
* @return The url to download the latest version from.
|
||||
*/
|
||||
public String getDownloadUrl()
|
||||
{
|
||||
return downloadURL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the latest update.
|
||||
*
|
||||
* @return True if the download was successful.
|
||||
*/
|
||||
public boolean downloadUpdate()
|
||||
{
|
||||
boolean downloadSuccessfull = false;
|
||||
try
|
||||
{
|
||||
File updateFolder = Bukkit.getUpdateFolderFile();
|
||||
if (!updateFolder.exists())
|
||||
if (!updateFolder.mkdirs())
|
||||
throw new RuntimeException("Failed to create update folder!");
|
||||
|
||||
String fileName = plugin.getName() + ".jar";
|
||||
File updateFile = new File(updateFolder + "/" + fileName);
|
||||
|
||||
// Follow any and all redirects until we've finally found the actual file.
|
||||
String location = downloadURL;
|
||||
HttpURLConnection httpConnection = null;
|
||||
for (; ; )
|
||||
{
|
||||
URL url = new URL(location);
|
||||
httpConnection = (HttpURLConnection) url.openConnection();
|
||||
httpConnection.setInstanceFollowRedirects(false);
|
||||
httpConnection.setRequestProperty("User-Agent", "ArmoredElytraUpdater");
|
||||
String redirectLocation = httpConnection.getHeaderField("Location");
|
||||
if (redirectLocation == null)
|
||||
break;
|
||||
location = redirectLocation;
|
||||
httpConnection.disconnect();
|
||||
}
|
||||
|
||||
if (httpConnection == null)
|
||||
{
|
||||
plugin.myLogger(Level.WARNING, "Failed to construct connection: " + location);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (httpConnection.getResponseCode() != 200)
|
||||
{
|
||||
plugin.myLogger(Level.WARNING,
|
||||
Util.exceptionToString(new RuntimeException("Download returned status #"
|
||||
+ httpConnection
|
||||
.getResponseCode() + "\n for URL: " + downloadURL)));
|
||||
return false;
|
||||
}
|
||||
|
||||
int grabSize = 4096;
|
||||
BufferedInputStream in = new BufferedInputStream(httpConnection.getInputStream());
|
||||
FileOutputStream fos = new FileOutputStream(updateFile);
|
||||
BufferedOutputStream bout = new BufferedOutputStream(fos, grabSize);
|
||||
|
||||
byte[] data = new byte[grabSize];
|
||||
int grab;
|
||||
while ((grab = in.read(data, 0, grabSize)) >= 0)
|
||||
bout.write(data, 0, grab);
|
||||
|
||||
bout.flush();
|
||||
bout.close();
|
||||
in.close();
|
||||
fos.flush();
|
||||
fos.close();
|
||||
downloadSuccessfull = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return downloadSuccessfull;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes this update checker with the specified values and return its instance. If an instance of
|
||||
* UpdateChecker has already been initialized, this method will act similarly to {@link #get()} (which is
|
||||
* recommended after initialization).
|
||||
*
|
||||
* @param plugin the plugin for which to check updates. Cannot be null
|
||||
* @param pluginID the ID of the plugin as identified in the SpigotMC resource link. For example,
|
||||
* "https://www.spigotmc.org/resources/veinminer.<b>12038</b>/" would expect "12038" as a
|
||||
* value. The value must be greater than 0
|
||||
* @param versionScheme a custom version scheme parser. Cannot be null
|
||||
* @return the UpdateChecker instance
|
||||
*/
|
||||
public static UpdateChecker init(final ArmoredElytra plugin, final int pluginID, final VersionScheme versionScheme)
|
||||
{
|
||||
Preconditions.checkArgument(pluginID > 0, "Plugin ID must be greater than 0");
|
||||
|
||||
return (instance == null) ? instance = new UpdateChecker(plugin, pluginID, versionScheme) : instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes this update checker with the specified values and return its instance. If an instance of
|
||||
* UpdateChecker has already been initialized, this method will act similarly to {@link #get()} (which is
|
||||
* recommended after initialization).
|
||||
*
|
||||
* @param plugin the plugin for which to check updates. Cannot be null
|
||||
* @param pluginID the ID of the plugin as identified in the SpigotMC resource link. For example,
|
||||
* "https://www.spigotmc.org/resources/veinminer.<b>12038</b>/" would expect "12038" as a value. The
|
||||
* value must be greater than 0
|
||||
* @return the UpdateChecker instance
|
||||
*/
|
||||
public static UpdateChecker init(final ArmoredElytra plugin, final int pluginID)
|
||||
{
|
||||
return init(plugin, pluginID, VERSION_SCHEME_DECIMAL);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the initialized instance of UpdateChecker. If {@link #init(ArmoredElytra, int)} has not yet been invoked,
|
||||
* this method will throw an exception.
|
||||
*
|
||||
* @return the UpdateChecker instance
|
||||
*/
|
||||
public static UpdateChecker get()
|
||||
{
|
||||
Preconditions.checkState(instance != null,
|
||||
"Instance has not yet been initialized. Be sure #init() has been invoked");
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the UpdateChecker has been initialized or not (if {@link #init(ArmoredElytra, int)} has been
|
||||
* invoked) and {@link #get()} is safe to use.
|
||||
*
|
||||
* @return true if initialized, false otherwise
|
||||
*/
|
||||
public static boolean isInitialized()
|
||||
{
|
||||
return instance != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A functional interface to compare two version Strings with similar version schemes.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public static interface VersionScheme
|
||||
{
|
||||
|
||||
/**
|
||||
* Compare two versions and return the higher of the two. If null is returned, it is assumed that at least one
|
||||
* of the two versions are unsupported by this version scheme parser.
|
||||
*
|
||||
* @param first the first version to check
|
||||
* @param second the second version to check
|
||||
* @return the greater of the two versions. null if unsupported version schemes
|
||||
*/
|
||||
public String compareVersions(String first, String second);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* A constant reason for the result of {@link UpdateResult}.
|
||||
*/
|
||||
public static enum UpdateReason
|
||||
{
|
||||
|
||||
/**
|
||||
* A new update is available for download on SpigotMC.
|
||||
*/
|
||||
NEW_UPDATE, // The only reason that requires an update
|
||||
|
||||
/**
|
||||
* A successful connection to the SpiGet API could not be established.
|
||||
*/
|
||||
COULD_NOT_CONNECT,
|
||||
|
||||
/**
|
||||
* The JSON retrieved from SpiGet was invalid or malformed.
|
||||
*/
|
||||
INVALID_JSON,
|
||||
|
||||
/**
|
||||
* A 401 error was returned by the SpiGet API.
|
||||
*/
|
||||
UNAUTHORIZED_QUERY,
|
||||
|
||||
/**
|
||||
* The version of the plugin installed on the server is greater than the one uploaded to SpigotMC's resources
|
||||
* section.
|
||||
*/
|
||||
UNRELEASED_VERSION,
|
||||
|
||||
/**
|
||||
* An unknown error occurred.
|
||||
*/
|
||||
UNKNOWN_ERROR,
|
||||
|
||||
/**
|
||||
* The plugin uses an unsupported version scheme, therefore a proper comparison between versions could not be
|
||||
* made.
|
||||
*/
|
||||
UNSUPPORTED_VERSION_SCHEME,
|
||||
|
||||
/**
|
||||
* The plugin is up to date with the version released on SpigotMC's resources section.
|
||||
*/
|
||||
UP_TO_DATE
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a result for an update query performed by {@link UpdateChecker#requestUpdateCheck()}.
|
||||
*/
|
||||
public final class UpdateResult
|
||||
{
|
||||
private final UpdateReason reason;
|
||||
private final String newestVersion;
|
||||
private final long age;
|
||||
|
||||
{ // An actual use for initializer blocks. This is madness!
|
||||
lastResult = this;
|
||||
}
|
||||
|
||||
private UpdateResult(final UpdateReason reason, final String newestVersion, final long age)
|
||||
{
|
||||
this.reason = reason;
|
||||
this.newestVersion = newestVersion;
|
||||
this.age = age;
|
||||
}
|
||||
|
||||
private UpdateResult(final UpdateReason reason)
|
||||
{
|
||||
Preconditions
|
||||
.checkArgument(reason != UpdateReason.NEW_UPDATE && reason != UpdateReason.UP_TO_DATE,
|
||||
"Reasons that might require updates must also provide the latest version String");
|
||||
this.reason = reason;
|
||||
newestVersion = plugin.getDescription().getVersion();
|
||||
age = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the constant reason of this result.
|
||||
*
|
||||
* @return the reason
|
||||
*/
|
||||
public UpdateReason getReason()
|
||||
{
|
||||
return reason;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether or not this result requires the user to update.
|
||||
*
|
||||
* @return true if requires update, false otherwise
|
||||
*/
|
||||
public boolean requiresUpdate()
|
||||
{
|
||||
return reason == UpdateReason.NEW_UPDATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the latest version of the plugin. This may be the currently installed version, it may not be. This
|
||||
* depends entirely on the result of the update.
|
||||
*
|
||||
* @return the newest version of the plugin
|
||||
*/
|
||||
public String getNewestVersion()
|
||||
{
|
||||
return newestVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the number of seconds since the last update was released.
|
||||
*
|
||||
* @return The number of seconds since the last update was released or -1 if unavailable.
|
||||
*/
|
||||
public long getAge()
|
||||
{
|
||||
return age;
|
||||
}
|
||||
}
|
||||
}
|
117
src/main/java/nl/pim16aap2/armoredElytra/util/UpdateManager.java
Normal file
117
src/main/java/nl/pim16aap2/armoredElytra/util/UpdateManager.java
Normal file
@ -0,0 +1,117 @@
|
||||
package nl.pim16aap2.armoredElytra.util;
|
||||
|
||||
import nl.pim16aap2.armoredElytra.ArmoredElytra;
|
||||
import nl.pim16aap2.armoredElytra.util.UpdateChecker.UpdateReason;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import org.bukkit.scheduler.BukkitTask;
|
||||
|
||||
import java.util.logging.Level;
|
||||
|
||||
/**
|
||||
* @author Pim
|
||||
*/
|
||||
public final class UpdateManager
|
||||
{
|
||||
private final ArmoredElytra plugin;
|
||||
private boolean checkForUpdates = false;
|
||||
private boolean downloadUpdates = false;
|
||||
private boolean updateDownloaded = false;
|
||||
|
||||
private UpdateChecker updater;
|
||||
private BukkitTask updateRunner = null;
|
||||
|
||||
public UpdateManager(final ArmoredElytra plugin, final int pluginID)
|
||||
{
|
||||
this.plugin = plugin;
|
||||
updater = UpdateChecker.init(plugin, pluginID);
|
||||
}
|
||||
|
||||
public void setEnabled(final boolean newCheckForUpdates, final boolean newDownloadUpdates)
|
||||
{
|
||||
checkForUpdates = newCheckForUpdates;
|
||||
downloadUpdates = newDownloadUpdates;
|
||||
initUpdater();
|
||||
}
|
||||
|
||||
public boolean hasUpdateBeenDownloaded()
|
||||
{
|
||||
return updateDownloaded;
|
||||
}
|
||||
|
||||
public String getNewestVersion()
|
||||
{
|
||||
if (!checkForUpdates || updater.getLastResult() == null)
|
||||
return null;
|
||||
return updater.getLastResult().getNewestVersion();
|
||||
}
|
||||
|
||||
public boolean updateAvailable()
|
||||
{
|
||||
// Updates disabled, so no new updates available by definition.
|
||||
if (!checkForUpdates || updater.getLastResult() == null)
|
||||
return false;
|
||||
|
||||
// There's a newer version available.
|
||||
if (updater.getLastResult().requiresUpdate())
|
||||
return true;
|
||||
|
||||
// The plugin is "up-to-date", but this is a dev-build, so it must be newer.
|
||||
if (updater.getLastResult().getReason().equals(UpdateReason.UP_TO_DATE))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public void checkForUpdates()
|
||||
{
|
||||
updater.requestUpdateCheck().whenComplete(
|
||||
(result, throwable) ->
|
||||
{
|
||||
boolean updateAvailable = updateAvailable();
|
||||
if (updateAvailable)
|
||||
plugin.myLogger(Level.INFO,
|
||||
"A new update is available: " + plugin.getUpdateManager().getNewestVersion());
|
||||
|
||||
if (downloadUpdates && updateAvailable)
|
||||
{
|
||||
updateDownloaded = updater.downloadUpdate();
|
||||
if (updateDownloaded)
|
||||
plugin.myLogger(Level.INFO, "Update downloaded! Restart to apply it! " +
|
||||
"New version is " + updater.getLastResult().getNewestVersion() +
|
||||
", Currently running " + plugin.getDescription().getVersion());
|
||||
else
|
||||
plugin.myLogger(Level.INFO,
|
||||
"Failed to download latest version! You can download it manually at: " +
|
||||
updater.getDownloadUrl());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void initUpdater()
|
||||
{
|
||||
if (checkForUpdates)
|
||||
{
|
||||
// Run the UpdateChecker regularly.
|
||||
if (updateRunner == null)
|
||||
updateRunner = new BukkitRunnable()
|
||||
{
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
checkForUpdates();
|
||||
}
|
||||
}.runTaskTimer(plugin, 0L, 288000L); // Run immediately, then every 4 hours.
|
||||
}
|
||||
else
|
||||
{
|
||||
plugin.myLogger(Level.INFO,
|
||||
"Plugin update checking not enabled! You will not receive any messages about new updates " +
|
||||
"for this plugin. Please consider turning this on in the config.");
|
||||
if (updateRunner != null)
|
||||
{
|
||||
updateRunner.cancel();
|
||||
updateRunner = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user