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.ArmorTierName;
|
||||||
import nl.pim16aap2.armoredElytra.util.ConfigLoader;
|
import nl.pim16aap2.armoredElytra.util.ConfigLoader;
|
||||||
import nl.pim16aap2.armoredElytra.util.Messages;
|
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.bstats.bukkit.Metrics;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.bukkit.ChatColor;
|
import org.bukkit.ChatColor;
|
||||||
@ -25,10 +25,10 @@ import java.util.Objects;
|
|||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.regex.Pattern;
|
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: 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: 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
|
public class ArmoredElytra extends JavaPlugin implements Listener
|
||||||
{
|
{
|
||||||
@ -36,13 +36,13 @@ public class ArmoredElytra extends JavaPlugin implements Listener
|
|||||||
private Messages messages;
|
private Messages messages;
|
||||||
private ConfigLoader config;
|
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 elytraReceivedMessage;
|
||||||
private String usageDeniedMessage;
|
private String usageDeniedMessage;
|
||||||
private String elytraLore;
|
private String elytraLore;
|
||||||
private boolean upToDate;
|
private boolean upToDate;
|
||||||
private boolean is1_9;
|
private boolean is1_9;
|
||||||
|
private UpdateManager updateManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onEnable()
|
public void onEnable()
|
||||||
@ -52,48 +52,10 @@ public class ArmoredElytra extends JavaPlugin implements Listener
|
|||||||
messages = new Messages(this);
|
messages = new Messages(this);
|
||||||
readMessages();
|
readMessages();
|
||||||
|
|
||||||
|
updateManager = new UpdateManager(this, 47136);
|
||||||
|
|
||||||
// Check if the user allows checking for updates.
|
// Check if the user allows checking for updates.
|
||||||
if (config.checkForUpdates())
|
updateManager.setEnabled(config.checkForUpdates(), config.autoDLUpdate());
|
||||||
{
|
|
||||||
// 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.");
|
|
||||||
|
|
||||||
if (config.allowStats())
|
if (config.allowStats())
|
||||||
{
|
{
|
||||||
@ -280,6 +242,11 @@ public class ArmoredElytra extends JavaPlugin implements Listener
|
|||||||
player.getInventory().addItem(item);
|
player.getInventory().addItem(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UpdateManager getUpdateManager()
|
||||||
|
{
|
||||||
|
return updateManager;
|
||||||
|
}
|
||||||
|
|
||||||
// Check + initialize for the correct version of Minecraft.
|
// Check + initialize for the correct version of Minecraft.
|
||||||
public boolean compatibleMCVer()
|
public boolean compatibleMCVer()
|
||||||
{
|
{
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
package nl.pim16aap2.armoredElytra.util;
|
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.File;
|
||||||
import java.io.FileWriter;
|
import java.io.FileWriter;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
@ -9,11 +13,6 @@ import java.util.Arrays;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import org.bukkit.Bukkit;
|
|
||||||
import org.bukkit.configuration.file.FileConfiguration;
|
|
||||||
|
|
||||||
import nl.pim16aap2.armoredElytra.ArmoredElytra;
|
|
||||||
|
|
||||||
public class ConfigLoader
|
public class ConfigLoader
|
||||||
{
|
{
|
||||||
private final String header;
|
private final String header;
|
||||||
@ -25,6 +24,7 @@ public class ConfigLoader
|
|||||||
private int IRON_TO_FULL;
|
private int IRON_TO_FULL;
|
||||||
private boolean uninstallMode;
|
private boolean uninstallMode;
|
||||||
private boolean checkForUpdates;
|
private boolean checkForUpdates;
|
||||||
|
private boolean autoDLUpdate;
|
||||||
private int LEATHER_TO_FULL;
|
private int LEATHER_TO_FULL;
|
||||||
private int DIAMONDS_TO_FULL;
|
private int DIAMONDS_TO_FULL;
|
||||||
private boolean noFlightDurability;
|
private boolean noFlightDurability;
|
||||||
@ -33,7 +33,7 @@ public class ConfigLoader
|
|||||||
public boolean bypassWearPerm;
|
public boolean bypassWearPerm;
|
||||||
public boolean bypassCraftPerm;
|
public boolean bypassCraftPerm;
|
||||||
|
|
||||||
private ArrayList<ConfigOption<?>> configOptionsList;
|
private ArrayList<nl.pim16aap2.armoredElytra.util.ConfigOption<?>> configOptionsList;
|
||||||
private ArmoredElytra plugin;
|
private ArmoredElytra plugin;
|
||||||
|
|
||||||
public ConfigLoader(ArmoredElytra plugin)
|
public ConfigLoader(ArmoredElytra plugin)
|
||||||
@ -48,22 +48,22 @@ public class ConfigLoader
|
|||||||
private void makeConfig()
|
private void makeConfig()
|
||||||
{
|
{
|
||||||
// All the comments for the various config options.
|
// All the comments for the various config options.
|
||||||
String[] unbreakableComment =
|
String[] unbreakableComment =
|
||||||
{
|
{
|
||||||
"Setting this to true will cause armored elytras to be unbreakable.",
|
"Setting this to true will cause armored elytras to be unbreakable.",
|
||||||
"Changing this to false will NOT make unbreakable elytras breakable again!"
|
"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.",
|
"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."
|
"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",
|
"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%."
|
"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.",
|
"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\"",
|
"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",
|
"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."
|
"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.",
|
"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!"
|
"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."
|
"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.",
|
"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.",
|
"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.",
|
"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!"
|
"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!"
|
"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.
|
// Set default list of allowed enchantments.
|
||||||
allowedEnchantments = new ArrayList<>(Arrays.asList("DURABILITY", "PROTECTION_FIRE", "PROTECTION_EXPLOSIONS",
|
allowedEnchantments = new ArrayList<>(Arrays.asList("DURABILITY", "PROTECTION_FIRE", "PROTECTION_EXPLOSIONS",
|
||||||
"PROTECTION_PROJECTILE", "PROTECTION_ENVIRONMENTAL", "THORNS",
|
"PROTECTION_PROJECTILE", "PROTECTION_ENVIRONMENTAL",
|
||||||
"BINDING_CURSE", "VANISHING_CURSE", "MENDING"));
|
"THORNS",
|
||||||
|
"BINDING_CURSE", "VANISHING_CURSE", "MENDING"));
|
||||||
|
|
||||||
FileConfiguration config = plugin.getConfig();
|
FileConfiguration config = plugin.getConfig();
|
||||||
|
|
||||||
@ -123,9 +129,12 @@ public class ConfigLoader
|
|||||||
GOLD_TO_FULL = addNewConfigOption(config, "goldRepair", 5, null);
|
GOLD_TO_FULL = addNewConfigOption(config, "goldRepair", 5, null);
|
||||||
IRON_TO_FULL = addNewConfigOption(config, "ironRepair", 4, null);
|
IRON_TO_FULL = addNewConfigOption(config, "ironRepair", 4, null);
|
||||||
DIAMONDS_TO_FULL = addNewConfigOption(config, "diamondsRepair", 3, null);
|
DIAMONDS_TO_FULL = addNewConfigOption(config, "diamondsRepair", 3, null);
|
||||||
allowedEnchantments = addNewConfigOption(config, "allowedEnchantments", allowedEnchantments, enchantmentsComment);
|
allowedEnchantments = addNewConfigOption(config, "allowedEnchantments", allowedEnchantments,
|
||||||
allowMultipleProtectionEnchantments = addNewConfigOption(config, "allowMultipleProtectionEnchantments", false, allowMultipleProtectionEnchantmentsComment);
|
enchantmentsComment);
|
||||||
|
allowMultipleProtectionEnchantments = addNewConfigOption(config, "allowMultipleProtectionEnchantments", false,
|
||||||
|
allowMultipleProtectionEnchantmentsComment);
|
||||||
checkForUpdates = addNewConfigOption(config, "checkForUpdates", true, updateComment);
|
checkForUpdates = addNewConfigOption(config, "checkForUpdates", true, updateComment);
|
||||||
|
autoDLUpdate = addNewConfigOption(config, "auto-update", true, autoDLUpdateComment);
|
||||||
allowStats = addNewConfigOption(config, "allowStats", true, bStatsComment);
|
allowStats = addNewConfigOption(config, "allowStats", true, bStatsComment);
|
||||||
enableDebug = addNewConfigOption(config, "enableDebug", false, debugComment);
|
enableDebug = addNewConfigOption(config, "enableDebug", false, debugComment);
|
||||||
uninstallMode = addNewConfigOption(config, "uninstallMode", false, uninstallComment);
|
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)
|
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);
|
configOptionsList.add(option);
|
||||||
return option.getValue();
|
return option.getValue();
|
||||||
}
|
}
|
||||||
@ -161,7 +171,7 @@ public class ConfigLoader
|
|||||||
saveTo.delete();
|
saveTo.delete();
|
||||||
saveTo.createNewFile();
|
saveTo.createNewFile();
|
||||||
}
|
}
|
||||||
FileWriter fw = new FileWriter(saveTo, true);
|
FileWriter fw = new FileWriter(saveTo, true);
|
||||||
PrintWriter pw = new PrintWriter(fw);
|
PrintWriter pw = new PrintWriter(fw);
|
||||||
|
|
||||||
if (header != null)
|
if (header != null)
|
||||||
@ -169,16 +179,17 @@ public class ConfigLoader
|
|||||||
|
|
||||||
for (int idx = 0; idx < configOptionsList.size(); ++idx)
|
for (int idx = 0; idx < configOptionsList.size(); ++idx)
|
||||||
pw.println(configOptionsList.get(idx).toString() +
|
pw.println(configOptionsList.get(idx).toString() +
|
||||||
// Only print an additional newLine if the next config option has a comment.
|
// Only print an additional newLine if the next config option has a comment.
|
||||||
(idx < configOptionsList.size() - 1 && configOptionsList.get(idx + 1).getComment() == null ? ""
|
(idx < configOptionsList.size() - 1 &&
|
||||||
: "\n"));
|
configOptionsList.get(idx + 1).getComment() == null ? "" : "\n"));
|
||||||
|
|
||||||
pw.flush();
|
pw.flush();
|
||||||
pw.close();
|
pw.close();
|
||||||
}
|
}
|
||||||
catch (IOException e)
|
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();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -239,6 +250,11 @@ public class ConfigLoader
|
|||||||
return checkForUpdates;
|
return checkForUpdates;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean autoDLUpdate()
|
||||||
|
{
|
||||||
|
return autoDLUpdate;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean noFlightDurability()
|
public boolean noFlightDurability()
|
||||||
{
|
{
|
||||||
return 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