Better handling for updates checking, can now autoupdate on dev and release builds

This commit is contained in:
libraryaddict 2020-04-27 05:09:13 +12:00
parent 41dffe1b46
commit b247a49a23
No known key found for this signature in database
GPG Key ID: 052E4FBCD257AEA4
14 changed files with 760 additions and 457 deletions

View File

@ -13,6 +13,7 @@ import me.libraryaddict.disguise.utilities.parser.DisguiseParseException;
import me.libraryaddict.disguise.utilities.parser.DisguiseParser;
import me.libraryaddict.disguise.utilities.parser.DisguisePerm;
import me.libraryaddict.disguise.utilities.reflection.NmsVersion;
import me.libraryaddict.disguise.utilities.reflection.ReflectionManager;
import me.libraryaddict.disguise.utilities.translations.LibsMsg;
import me.libraryaddict.disguise.utilities.translations.TranslateType;
import org.bukkit.Bukkit;
@ -26,16 +27,16 @@ import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Entity;
import org.bukkit.permissions.Permission;
import org.bukkit.permissions.PermissionDefault;
import org.bukkit.scheduler.BukkitTask;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Random;
import java.util.concurrent.TimeUnit;
public class DisguiseConfig {
@Getter
@ -46,9 +47,6 @@ public class DisguiseConfig {
private static HashMap<DisguisePerm, String> customDisguises = new HashMap<>();
@Getter
@Setter
private static String updateNotificationPermission;
@Getter
@Setter
private static UpdatesBranch updatesBranch = UpdatesBranch.SAME_BUILDS;
@Getter
@Setter
@ -231,6 +229,127 @@ public class DisguiseConfig {
@Getter
@Setter
private static int tablistRemoveDelay;
@Getter
private static boolean usingReleaseBuild = true;
@Getter
private static boolean bisectHosted = true;
@Getter
private static String savedServerIp = "";
@Getter
private static boolean autoUpdate;
@Getter
private static boolean notifyUpdate;
private static BukkitTask updaterTask;
public static void setAutoUpdate(boolean update) {
if (isAutoUpdate() == update) {
return;
}
autoUpdate = update;
doUpdaterTask();
}
public static void setNotifyUpdate(boolean update) {
if (isNotifyUpdate() == update) {
return;
}
notifyUpdate = update;
doUpdaterTask();
}
private static void doUpdaterTask() {
boolean startTask = isAutoUpdate() || isNotifyUpdate();
// Don't ever run the auto updater on a custom build..
if (!LibsDisguises.getInstance().isNumberedBuild()) {
return;
}
if (updaterTask == null != startTask) {
return;
}
if (!startTask) {
updaterTask.cancel();
updaterTask = null;
return;
}
updaterTask = Bukkit.getScheduler().runTaskTimerAsynchronously(LibsDisguises.getInstance(), new Runnable() {
@Override
public void run() {
LibsDisguises.getInstance().getUpdateChecker().doAutoUpdateCheck();
}
}, 0, (20 * TimeUnit.HOURS.toSeconds(6))); // Check every 6 hours
}
public static void setUsingReleaseBuilds(boolean useReleaseBuilds) {
if (useReleaseBuilds == isUsingReleaseBuild()) {
return;
}
usingReleaseBuild = useReleaseBuilds;
saveInternalConfig();
}
public static void setBisectHosted(boolean isBisectHosted, String serverIP) {
if (isBisectHosted() == isBisectHosted && getSavedServerIp().equals(serverIP)) {
return;
}
bisectHosted = isBisectHosted;
savedServerIp = serverIP;
saveInternalConfig();
}
public static void loadInternalConfig() {
File internalFile = new File(LibsDisguises.getInstance().getDataFolder(), "internal.yml");
if (!internalFile.exists()) {
saveInternalConfig();
}
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(internalFile);
bisectHosted = configuration.getBoolean("Bisect-Hosted", isBisectHosted());
savedServerIp = configuration.getString("Server-IP", getSavedServerIp());
usingReleaseBuild = configuration.getBoolean("ReleaseBuild", isUsingReleaseBuild());
if (!configuration.contains("Bisect-Hosted") || !configuration.contains("Server-IP") ||
!configuration.contains("ReleaseBuild")) {
saveInternalConfig();
}
}
public static void saveInternalConfig() {
File internalFile = new File(LibsDisguises.getInstance().getDataFolder(), "internal.yml");
String internalConfig = ReflectionManager
.getResourceAsString(LibsDisguises.getInstance().getFile(), "internal.yml");
// Bisect hosted, server ip, release builds
for (Object s : new Object[]{isBisectHosted(), getSavedServerIp(), isUsingReleaseBuild()}) {
internalConfig = internalConfig.replaceFirst("%data%", "" + s);
}
internalFile.delete();
try {
internalFile.createNewFile();
}
catch (IOException e) {
e.printStackTrace();
}
try (PrintWriter writer = new PrintWriter(internalFile, "UTF-8")) {
writer.write(internalConfig);
}
catch (FileNotFoundException | UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public static PermissionDefault getCommandVisibility() {
return commandVisibility;
@ -438,7 +557,6 @@ public class DisguiseConfig {
setUUIDGeneratedVersion(config.getInt("UUIDVersion"));
setUndisguiseOnWorldChange(config.getBoolean("UndisguiseOnWorldChange"));
setUpdateGameProfiles(config.getBoolean("UpdateGameProfiles"));
setUpdateNotificationPermission(config.getString("Permission"));
setUseTranslations(config.getBoolean("Translations"));
setVelocitySent(config.getBoolean("SendVelocity"));
setViewDisguises(config.getBoolean("ViewSelfDisguises"));
@ -447,6 +565,7 @@ public class DisguiseConfig {
setWolfDyeable(config.getBoolean("DyeableWolf"));
setScoreboardDisguiseNames(config.getBoolean("ScoreboardNames"));
setTablistRemoveDelay(config.getInt("TablistRemoveDelay"));
setAutoUpdate(config.getBoolean("AutoUpdate"));
if (!LibsPremium.isPremium() && (isSavePlayerDisguises() || isSaveEntityDisguises())) {
DisguiseUtilities.getLogger().warning("You must purchase the plugin to use saved disguises!");
@ -561,7 +680,8 @@ public class DisguiseConfig {
if (missingConfigs > 0) {
DisguiseUtilities.getLogger().warning("Your config is missing " + missingConfigs +
" options! Please consider regenerating your config!");
DisguiseUtilities.getLogger().info("You can also add the missing entries yourself! Try '/libsdisguises config'");
DisguiseUtilities.getLogger()
.info("You can also add the missing entries yourself! Try '/libsdisguises config'");
}
}

View File

@ -17,7 +17,7 @@ import me.libraryaddict.disguise.commands.undisguise.UndisguiseRadiusCommand;
import me.libraryaddict.disguise.commands.utils.*;
import me.libraryaddict.disguise.utilities.DisguiseUtilities;
import me.libraryaddict.disguise.utilities.LibsPremium;
import me.libraryaddict.disguise.utilities.UpdateChecker;
import me.libraryaddict.disguise.utilities.updates.UpdateChecker;
import me.libraryaddict.disguise.utilities.listeners.DisguiseListener;
import me.libraryaddict.disguise.utilities.metrics.MetricsInitalizer;
import me.libraryaddict.disguise.utilities.packets.PacketsManager;
@ -31,10 +31,13 @@ import org.bukkit.command.CommandExecutor;
import org.bukkit.command.PluginCommand;
import org.bukkit.command.TabCompleter;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.craftbukkit.libs.org.apache.commons.io.FileUtils;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.util.FileUtil;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.stream.Collectors;
@ -81,6 +84,8 @@ public class LibsDisguises extends JavaPlugin {
getLogger().info("Build Date: " + pluginYml.getString("build-date"));
DisguiseConfig.loadInternalConfig();
LibsPremium.check(getDescription().getVersion(), getFile());
if (!LibsPremium.isPremium()) {
@ -98,6 +103,17 @@ public class LibsDisguises extends JavaPlugin {
return;
}
// If this is a release build, even if jenkins build..
if (isReleaseBuild()) {
// If downloaded from spigot, forcibly set release build to true
if (LibsPremium.getUserID().matches("[0-9]+")) {
DisguiseConfig.setUsingReleaseBuilds(true);
}
// Otherwise leave it untouched as they might've just happened to hit a dev build, which is a release build
} else {
DisguiseConfig.setUsingReleaseBuilds(false);
}
ReflectionManager.init();
PacketsManager.init();

View File

@ -1,7 +1,8 @@
package me.libraryaddict.disguise.commands.libsdisguises;
import me.libraryaddict.disguise.LibsDisguises;
import me.libraryaddict.disguise.utilities.UpdateChecker;
import me.libraryaddict.disguise.utilities.DisguiseUtilities;
import me.libraryaddict.disguise.utilities.updates.UpdateChecker;
import me.libraryaddict.disguise.utilities.plugin.PluginInformation;
import me.libraryaddict.disguise.utilities.translations.LibsMsg;
import org.bukkit.Bukkit;
@ -18,7 +19,10 @@ import java.util.List;
public class LDUpdate implements LDCommand {
@Override
public List<String> getTabComplete() {
return Arrays.asList("update", "update!");
// Update by download
// Update check
// Update to latest dev build
return Arrays.asList("update", "update?", "update!");
}
@Override
@ -35,16 +39,21 @@ public class LDUpdate implements LDCommand {
return;
}
boolean check = args[0].endsWith("?");
boolean force = args[0].endsWith("!");
if (!force) {
if (checker.getLatestSnapshot() <= 0) {
sender.sendMessage(LibsMsg.UPDATE_NOT_READY.get());
if (!check && !force && checker.getUpdate() != null) {
if (checker.getUpdate().getVersion().equals(checker.getUpdate().isReleaseBuild() ?
LibsDisguises.getInstance().getDescription().getDescription() :
LibsDisguises.getInstance().getBuildNumber())) {
sender.sendMessage(LibsMsg.UPDATE_ON_LATEST.get());
return;
}
if (checker.getLatestSnapshot() == LibsDisguises.getInstance().getBuildNumber()) {
sender.sendMessage(LibsMsg.UPDATE_ON_LATEST.get());
if (checker.getLastDownload() != null && checker.getUpdate().getVersion()
.equals(checker.isUsingReleaseBuilds() ? checker.getLastDownload().getVersion() :
checker.getLastDownload().getBuildNumber())) {
sender.sendMessage(LibsMsg.UPDATE_ALREADY_DOWNLOADED.get());
return;
}
}
@ -52,29 +61,49 @@ public class LDUpdate implements LDCommand {
new BukkitRunnable() {
@Override
public void run() {
PluginInformation result;
LibsMsg updateResult = null;
if (force) {
result = checker.grabLatestSnapshot();
} else {
result = checker.grabSnapshotBuild();
if (check || checker.getUpdate() == null || force) {
updateResult = checker.doUpdateCheck();
}
if (checker.getUpdate() == null) {
sender.sendMessage(LibsMsg.UPDATE_FAILED.get());
return;
}
if (!checker.isUpdateReady()) {
sender.sendMessage(LibsMsg.UPDATE_ON_LATEST.get());
return;
}
if (check) {
if (updateResult != null) {
sender.sendMessage(updateResult.get());
} else {
for (String msg : checker.getUpdateMessage()) {
sender.sendMessage(msg);
}
}
return;
}
PluginInformation result = checker.doUpdate();
if (result == null) {
sender.sendMessage(LibsMsg.UPDATE_FAILED.get());
return;
}
sender.sendMessage(LibsMsg.UPDATE_SUCCESS.get()); // Update success, please restart to update
sender.sendMessage(LibsMsg.UPDATE_INFO
.get(result.getVersion(), result.getBuildNumber(), result.getParsedBuildDate().toString(),
result.getSize() / 1024));
for (String msg : checker.getUpdateMessage()) {
sender.sendMessage(msg);
}
if (sender instanceof Player) {
Bukkit.getConsoleSender().sendMessage(LibsMsg.UPDATE_SUCCESS.get());
Bukkit.getConsoleSender().sendMessage(LibsMsg.UPDATE_INFO
.get(result.getVersion(), result.getBuildNumber(), result.getParsedBuildDate().toString(),
result.getSize() / 1024));
for (String msg : checker.getUpdateMessage()) {
DisguiseUtilities.getLogger().info(msg);
}
}
}
}.runTaskAsynchronously(LibsDisguises.getInstance());

View File

@ -1,286 +0,0 @@
package me.libraryaddict.disguise.utilities;
import com.google.gson.Gson;
import lombok.Getter;
import me.libraryaddict.disguise.LibsDisguises;
import me.libraryaddict.disguise.utilities.plugin.PluginInformation;
import org.bukkit.Bukkit;
import org.bukkit.craftbukkit.libs.org.apache.commons.io.FileUtils;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
public class UpdateChecker {
private final String resourceID;
@Getter
private String latestVersion;
@Getter
private int latestSnapshot;
private final long started = System.currentTimeMillis();
private int lastDownload;
public UpdateChecker(String resourceID) {
this.resourceID = resourceID;
}
public PluginInformation grabSnapshotBuild() {
if (getLatestSnapshot() == 0) {
throw new IllegalArgumentException();
}
if (lastDownload == -1) {
return null;
}
if (getLatestSnapshot() == lastDownload) {
return null;
}
return grabSnapshotBuild(getLatestSnapshot());
}
public PluginInformation grabSnapshotBuild(int buildNo) {
PluginInformation result = grabSnapshotBuild(
"https://ci.md-5.net/job/LibsDisguises/" + buildNo + "/artifact/target/LibsDisguises.jar");
if (result != null) {
lastDownload = buildNo;
}
return result;
}
public PluginInformation grabLatestSnapshot() {
PluginInformation result = grabSnapshotBuild(
"https://ci.md-5.net/job/LibsDisguises/lastSuccessfulBuild/artifact/target/LibsDisguises.jar");
if (result != null) {
lastDownload = LibsDisguises.getInstance().getBuildNumber();
}
return result;
}
public boolean isDownloading() {
return lastDownload == -1;
}
public int getLastDownload() {
return lastDownload;
}
public PluginInformation grabSnapshotBuild(String urlString) {
DisguiseUtilities.getLogger().info("Now downloading latest build of Lib's Disguises from " + urlString);
lastDownload = -1;
File dest = new File(Bukkit.getUpdateFolderFile(), "LibsDisguises.jar");
if (dest.exists()) {
dest.delete();
}
dest.getParentFile().mkdirs();
try {
// We're connecting to spigot's API
URL url = new URL(urlString);
// Creating a connection
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setDefaultUseCaches(false);
// Get the input stream, what we receive
try (InputStream input = con.getInputStream()) {
FileUtils.copyInputStreamToFile(input, dest);
}
DisguiseUtilities.getLogger().info("Download success!");
return LibsPremium.getInformation(dest);
}
catch (Exception ex) {
// Failed, set the last download back to previous build
dest.delete();
DisguiseUtilities.getLogger().warning("Failed to download snapshot build.");
lastDownload = 0;
ex.printStackTrace();
}
return null;
}
public void checkSnapshotUpdate(int buildNumber) {
Map<String, Object> lastBuild = fetchLastSnapshotBuild();
if (lastBuild == null || !lastBuild.containsKey("id") || !lastBuild.containsKey("timestamp")) {
return;
}
int newBuildNumber = Integer.parseInt((String) lastBuild.get("id"));
// If new build number is same or older
if (newBuildNumber <= buildNumber) {
return;
}
Date newBuildDate = new Date(((Number) lastBuild.get("timestamp")).longValue());
// If the new snapshot is at least 3 days old
/*if (newBuildDate.getTime() >= System.currentTimeMillis() - TimeUnit.DAYS.toMillis(3)) {
return;
}*/
latestSnapshot = newBuildNumber;
}
public void checkOfficialUpdate(String currentVersion) {
String version = fetchSpigotVersion();
if (version == null) {
return;
}
if (!isNewerVersion(currentVersion, version)) {
return;
}
latestVersion = version;
}
/**
* Asks spigot for the version
*/
private String fetchSpigotVersion() {
try {
// We're connecting to spigot's API
URL url = new URL("https://api.spigotmc.org/legacy/update.php?resource=" + resourceID);
// Creating a connection
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setDefaultUseCaches(false);
// Get the input stream, what we receive
try (InputStream input = con.getInputStream()) {
// Read it to string
String version = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)).lines()
.collect(Collectors.joining("\n"));
// If the version is not empty, return it
if (!version.isEmpty()) {
return version;
}
}
}
catch (Exception ex) {
DisguiseUtilities.getLogger().warning("Failed to check for a update on spigot.");
}
return null;
}
private boolean isNewerVersion(String currentVersion, String newVersion) {
currentVersion = currentVersion.replaceAll("(v)|(-SNAPSHOT)", "");
newVersion = newVersion.replaceAll("(v)|(-SNAPSHOT)", "");
// If the server has been online for less than 6 hours and both versions are 1.1.1 kind of versions
if (started + TimeUnit.HOURS.toMillis(6) > System.currentTimeMillis() &&
currentVersion.matches("[0-9]+(\\.[0-9]+)*") && newVersion.matches("[0-9]+(\\.[0-9]+)*")) {
int cVersion = Integer.parseInt(currentVersion.replace(".", ""));
int nVersion = Integer.parseInt(newVersion.replace(".", ""));
// If the current version is a higher version, and is only a higher version by 3 minor numbers
// Then we have a cache problem
if (cVersion > nVersion && nVersion + 3 > cVersion) {
return false;
}
}
// Lets just ignore all this fancy logic, and say that if you're not on the current release, you're outdated!
return !currentVersion.equals(newVersion);
/*
// Remove 'v' and '-SNAPSHOT' from string, split by decimal points
String[] cSplit = currentVersion.replaceAll("(v)|(-SNAPSHOT)", "").split("\\.");
String[] nSplit = newVersion.replaceAll("(v)|(-SNAPSHOT)", "").split("\\.");
// Lets just ignore all this fancy logic, and say that if you're not on the current release, you're outdated!
return !Arrays.equals(cSplit, nSplit);
// Iterate over the versions from left to right
for (int i = 0; i < Math.max(cSplit.length, nSplit.length); i++) {
// If the current version doesn't have the next version, then it's older
if (cSplit.length <= i) {
return true;
} else if (nSplit.length <= i) {
// If the new version doesn't have the next version, then it's older
return false;
}
// If both strings are numerical
if (cSplit[i].matches("[0-9]+") && nSplit[i].matches("[0-9]+")) {
int cInt = Integer.parseInt(cSplit[i]);
int nInt = Integer.parseInt(nSplit[i]);
// Same version
if (cInt == nInt) {
continue;
}
// Return if current version is inferior to new version
return cInt < nInt;
}
// String compare the versions, should perform the same as an int compare
int compareResult = cSplit[i].compareTo(nSplit[i]);
// Same version
if (compareResult == 0) {
continue;
}
// Return if current version is inferior to new versio
return compareResult < 0;
}
// Both versions should be the same, return false as it's not a newer version
return false;*/
}
/**
* Fetches from jenkins, using the REST api the last snapshot build information
*/
private Map<String, Object> fetchLastSnapshotBuild() {
try {
// We're connecting to md_5's jenkins REST api
URL url = new URL("https://ci.md-5.net/job/LibsDisguises/lastSuccessfulBuild/api/json");
// Creating a connection
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setDefaultUseCaches(false);
Map<String, Object> jsonObject;
// Get the input stream, what we receive
try (InputStream input = con.getInputStream()) {
// Read it to string
String json = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)).lines()
.collect(Collectors.joining("\n"));
jsonObject = new Gson().fromJson(json, Map.class);
}
return jsonObject;
}
catch (Exception ex) {
DisguiseUtilities.getLogger().warning("Failed to check for a snapshot update on jenkins.");
}
return null;
}
}

View File

@ -14,7 +14,7 @@ import me.libraryaddict.disguise.disguisetypes.TargetedDisguise;
import me.libraryaddict.disguise.utilities.DisguiseUtilities;
import me.libraryaddict.disguise.utilities.LibsEntityInteract;
import me.libraryaddict.disguise.utilities.LibsPremium;
import me.libraryaddict.disguise.utilities.UpdateChecker;
import me.libraryaddict.disguise.utilities.updates.UpdateChecker;
import me.libraryaddict.disguise.utilities.modded.ModdedEntity;
import me.libraryaddict.disguise.utilities.modded.ModdedManager;
import me.libraryaddict.disguise.utilities.plugin.PluginInformation;
@ -53,13 +53,9 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
public class DisguiseListener implements Listener {
private String currentVersion;
private HashMap<String, LibsEntityInteract> interactions = new HashMap<>();
private HashMap<String, BukkitRunnable> disguiseRunnable = new HashMap<>();
private String latestVersion;
private LibsMsg updateMessage;
private LibsDisguises plugin;
private BukkitTask updaterTask;
public DisguiseListener(LibsDisguises libsDisguises) {
plugin = libsDisguises;
@ -117,87 +113,6 @@ public class DisguiseListener implements Listener {
DisguiseUtilities.getLogger()
.info("Plugin will attempt to auto update when new builds are ready! Check config to disable.");
}
updaterTask = Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, new Runnable() {
@Override
public void run() {
try {
UpdateChecker updateChecker = LibsDisguises.getInstance().getUpdateChecker();
boolean checkReleases = isCheckReleases();
if (checkReleases) {
currentVersion = plugin.getDescription().getVersion();
updateChecker.checkOfficialUpdate(currentVersion);
String version = updateChecker.getLatestVersion();
if (version == null) {
return;
}
latestVersion = version;
updateMessage = LibsMsg.UPDATE_READY;
} else {
updateChecker.checkSnapshotUpdate(plugin.getBuildNumber());
if (updateChecker.getLatestSnapshot() <= 0) {
return;
}
latestVersion = "" + updateChecker.getLatestSnapshot();
if (autoUpdate && plugin.isNumberedBuild()) {
PluginInformation result = updateChecker.grabSnapshotBuild();
updateMessage = result != null ? LibsMsg.UPDATE_SUCCESS : LibsMsg.UPDATE_FAILED;
Bukkit.getConsoleSender().sendMessage(LibsMsg.UPDATE_INFO
.get(result.getVersion(), result.getBuildNumber(),
result.getParsedBuildDate().toString(), result.getSize() / 1024));
} else {
currentVersion = plugin.getBuildNo();
updateMessage = LibsMsg.UPDATE_READY_SNAPSHOT;
}
}
Bukkit.getScheduler().runTask(plugin, new Runnable() {
@Override
public void run() {
notifyUpdate(Bukkit.getConsoleSender());
for (Player p : Bukkit.getOnlinePlayers()) {
notifyUpdate(p);
}
}
});
}
catch (Exception ex) {
DisguiseUtilities.getLogger()
.warning(String.format("Failed to check for update: %s", ex.getMessage()));
}
}
}, 0, (20 * TimeUnit.HOURS.toSeconds(6))); // Check every 6 hours
}
private void notifyUpdate(CommandSender player) {
if (!player.hasPermission(DisguiseConfig.getUpdateNotificationPermission())) {
return;
}
if (latestVersion == null) {
return;
}
if (updateMessage == LibsMsg.UPDATE_SUCCESS || updateMessage == LibsMsg.UPDATE_FAILED) {
if (player instanceof Player) {
player.sendMessage(updateMessage.get());
} else {
DisguiseUtilities.getLogger().info(updateMessage.get());
}
} else {
if (player instanceof Player) {
player.sendMessage(updateMessage.get(currentVersion, latestVersion));
} else {
DisguiseUtilities.getLogger().info(updateMessage.get(currentVersion, latestVersion));
}
}
}
public void cleanup() {
@ -206,7 +121,6 @@ public class DisguiseListener implements Listener {
}
interactions.clear();
updaterTask.cancel();
}
private void checkPlayerCanBlowDisguise(Player player) {
@ -423,7 +337,7 @@ public class DisguiseListener implements Listener {
public void onJoin(PlayerJoinEvent event) {
Player p = event.getPlayer();
notifyUpdate(p);
plugin.getUpdateChecker().notifyUpdate(p);
if (DisguiseConfig.isSaveGameProfiles() && DisguiseConfig.isUpdateGameProfiles() &&
DisguiseUtilities.hasGameProfile(p.getName())) {

View File

@ -1,5 +1,6 @@
package me.libraryaddict.disguise.utilities.plugin;
import me.libraryaddict.disguise.DisguiseConfig;
import me.libraryaddict.disguise.LibsDisguises;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
@ -13,24 +14,15 @@ import java.net.URL;
*/
public class BisectHosting {
public boolean isBisectHosted(String pluginName) {
File configFile = new File("plugins/" + pluginName + "/internal.yml");
boolean claimedHosted = false;
String serverIp = Bukkit.getIp().replaceAll("[^:0-9.]", "");
boolean claimedHosted = DisguiseConfig.isBisectHosted();
String ip = Bukkit.getIp();
String parsedIP = ip.replaceAll("[^:0-9.]", "");
if (configFile.exists()) {
YamlConfiguration configuration = YamlConfiguration.loadConfiguration(configFile);
if (configuration.contains("Bisect-Hosted") && configuration.contains("Server-IP")) {
claimedHosted = configuration.getBoolean("Bisect-Hosted");
// If not hosted by bisect
if (!claimedHosted && configuration.getString("Server-IP").equals(serverIp)) {
return false;
}
}
// If not hosted by bisect
if (!claimedHosted && DisguiseConfig.getSavedServerIp().equals(parsedIP)) {
return false;
}
String ip = Bukkit.getIp();
boolean hostedBy = false;
if (ip.matches("((25[0-5]|(2[0-4]|1[0-9]|[1-9]|)[0-9])(\\.(?!$)|$)){4}")) {
@ -45,27 +37,11 @@ public class BisectHosting {
}
}
// If config doesn't exist, or it's not a bisect server
if (!configFile.exists()) {
if (!configFile.getParentFile().exists()) {
configFile.getParentFile().mkdirs();
}
if (claimedHosted != hostedBy || !DisguiseConfig.getSavedServerIp().equals(parsedIP)) {
DisguiseConfig.setBisectHosted(hostedBy, Bukkit.getIp());
}
try (PrintWriter writer = new PrintWriter(configFile, "UTF-8")) {
// This setting is if the server should check if you are using Bisect Hosting",
writer.write("# If you're using BisectHosting, this will tell the server to enable premium for free!");
writer.write("\n# However if you're not using BisectHosting, this is false so the server won't waste " +
"time");
writer.write(
"\n# Coupon 'libraryaddict' for 25% off your first invoice on any of their gaming servers");
writer.write("\n# Be sure to visit through this link! https://bisecthosting.com/libraryaddict");
writer.write("\nBisect-Hosted: " + hostedBy);
writer.write("\nServer-IP: " + serverIp);
}
catch (FileNotFoundException | UnsupportedEncodingException e) {
e.printStackTrace();
}
} else if (claimedHosted) {
if (!hostedBy && !DisguiseConfig.getSavedServerIp().equals("")) {
// Just a small message for those who tried to enable it
LibsDisguises.getInstance().getLogger().severe("Check for BisectHosting failed! Connection error?");
}

View File

@ -16,6 +16,7 @@ import me.libraryaddict.disguise.utilities.LibsPremium;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.bukkit.*;
import org.bukkit.configuration.InvalidConfigurationException;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.*;
import org.bukkit.inventory.EquipmentSlot;
@ -158,21 +159,13 @@ public class ReflectionManager {
return true;
}
/**
* Copied from Bukkit
*/
public static YamlConfiguration getPluginYAML(File file) {
public static String getResourceAsString(File file, String fileName) {
try (JarFile jar = new JarFile(file)) {
JarEntry entry = jar.getJarEntry("plugin.yml");
JarEntry entry = jar.getJarEntry(fileName);
try (InputStream stream = jar.getInputStream(entry)) {
YamlConfiguration config = new YamlConfiguration();
String configString = new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8)).lines()
return new BufferedReader(new InputStreamReader(stream, StandardCharsets.UTF_8)).lines()
.collect(Collectors.joining("\n"));
config.loadFromString(configString);
return config;
}
}
catch (Exception ex) {
@ -182,6 +175,30 @@ public class ReflectionManager {
return null;
}
/**
* Copied from Bukkit
*/
public static YamlConfiguration getPluginYAML(File file) {
try {
String s = getResourceAsString(file, "plugin.yml");
if (s == null) {
return null;
}
YamlConfiguration config = new YamlConfiguration();
config.loadFromString(getResourceAsString(file, "plugin.yml"));
return config;
}
catch (InvalidConfigurationException e) {
e.printStackTrace();
}
return null;
}
public static int getNewEntityId() {
return getNewEntityId(true);
}

View File

@ -143,9 +143,8 @@ public enum LibsMsg {
NO_MODS(ChatColor.RED + "%s is not using any mods!"),
MODS_LIST(ChatColor.DARK_GREEN + "%s has the mods:" + ChatColor.AQUA + " %s"),
NO_PERM(ChatColor.RED + "You are forbidden to use this command."),
UPDATE_NOT_READY(ChatColor.RED +
"Lib's Disguises doesn't know what's the latest update! Use 'update!' to force an update to latest!"),
UPDATE_ON_LATEST(ChatColor.RED + "You are already on the latest version of LibsDisguises!"),
UPDATE_ALREADY_DOWNLOADED(ChatColor.RED + "That update has already been downloaded!"),
UPDATE_FAILED(ChatColor.RED + "LibsDisguises update failed! Check console for errors."),
UPDATE_SUCCESS(ChatColor.DARK_GREEN + "LibsDisguises update success! Restart server to update!"),
UPDATE_INFO(ChatColor.DARK_GREEN + "Lib's Disguises v%s, build %s, built %s and size %skb"),

View File

@ -0,0 +1,24 @@
package me.libraryaddict.disguise.utilities.updates;
import java.util.Date;
/**
* Created by libraryaddict on 26/04/2020.
*/
public interface DisguiseUpdate {
/**
* Null if invalid
*/
String getVersion();
boolean isReleaseBuild();
String getDownload();
String[] getChangelog();
/**
* When was this update fetched?
*/
Date getFetched();
}

View File

@ -0,0 +1,95 @@
package me.libraryaddict.disguise.utilities.updates;
import com.google.gson.Gson;
import lombok.AllArgsConstructor;
import lombok.Getter;
import me.libraryaddict.disguise.utilities.DisguiseUtilities;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.stream.Collectors;
/**
* Created by libraryaddict on 26/04/2020.
*/
public class LDGithub {
@Getter
@AllArgsConstructor
private class GithubUpdate implements DisguiseUpdate {
private String version;
private String[] changelog;
private String download;
private final Date fetched = new Date();
@Override
public boolean isReleaseBuild() {
return true;
}
}
@Getter
private class GithubData {
@Getter
class Asset {
String browser_download_url;
String name;
String content_type;
}
String name;
String tag_name;
String body;
Date published_at;
Asset[] assets;
}
public DisguiseUpdate getLatestRelease() {
try {
// We're connecting to md_5's jenkins REST api
URL url = new URL("https://api.github.com/repos/libraryaddict/LibsDisguises/releases/latest");
// Creating a connection
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestProperty("User-Agent", "libraryaddict/LibsDisguises");
con.setRequestProperty("Accept", "application/vnd.github.v3+json");
GithubData gitData;
// Get the input stream, what we receive
try (InputStream input = con.getInputStream()) {
// Read it to string
String json = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)).lines()
.collect(Collectors.joining("\n"));
gitData = new Gson().fromJson(json, GithubData.class);
}
String download = null;
for (GithubData.Asset asset : gitData.getAssets()) {
if (!asset.getName().endsWith(".jar")) {
continue;
}
download = asset.getBrowser_download_url();
break;
}
if (download == null) {
throw new IllegalStateException("Download url is missing");
}
return new GithubUpdate(gitData.getTag_name().replace("v", ""), gitData.getBody().split("(\\r|\\n)+"), download);
}
catch (Exception ex) {
DisguiseUtilities.getLogger().warning("Failed to check for a release on Github");
ex.printStackTrace();
}
return null;
}
}

View File

@ -0,0 +1,104 @@
package me.libraryaddict.disguise.utilities.updates;
import com.google.gson.Gson;
import lombok.AllArgsConstructor;
import lombok.Getter;
import me.libraryaddict.disguise.LibsDisguises;
import me.libraryaddict.disguise.utilities.DisguiseUtilities;
import me.libraryaddict.disguise.utilities.plugin.PluginInformation;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Created by libraryaddict on 26/04/2020.
*/
public class LDJenkins {
private UpdateChecker updateChecker;
@AllArgsConstructor
@Getter
private class JenkinsUpdate implements DisguiseUpdate {
private final Date fetched = new Date();
private final String version;
private final String[] changelog;
@Override
public String getDownload() {
return "https://ci.md-5.net/job/LibsDisguises/" + getVersion() + "/artifact/target/LibsDisguises.jar";
}
@Override
public boolean isReleaseBuild() {
return false;
}
}
/**
* Fetches from jenkins, using the REST api the last snapshot build information
*/
private Map<String, Object> fetchLastSnapshotBuild() {
try {
// We're connecting to md_5's jenkins REST api
URL url = new URL("https://ci.md-5.net/job/LibsDisguises/lastSuccessfulBuild/api/json");
// Creating a connection
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setDefaultUseCaches(false);
Map<String, Object> jsonObject;
// Get the input stream, what we receive
try (InputStream input = con.getInputStream()) {
// Read it to string
String json = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)).lines()
.collect(Collectors.joining("\n"));
jsonObject = new Gson().fromJson(json, Map.class);
}
return jsonObject;
}
catch (Exception ex) {
DisguiseUtilities.getLogger().warning("Failed to check for a snapshot update on jenkins.");
ex.printStackTrace();
}
return null;
}
public DisguiseUpdate getLatestSnapshot() {
Map<String, Object> lastBuild = fetchLastSnapshotBuild();
if (lastBuild == null || !lastBuild.containsKey("id") || !lastBuild.containsKey("timestamp")) {
return null;
}
ArrayList<String> changelog = new ArrayList<>();
if (lastBuild.get("changeSet") instanceof Map) {
Object items = ((Map) lastBuild.get("changeSet")).get("items");
if (items instanceof Map[]) {
for (Map item : (Map[]) items) {
String msg = (String) item.get("msg");
if (msg == null) {
continue;
}
changelog.add(msg);
}
}
}
return new JenkinsUpdate((String) lastBuild.get("id"), changelog.toArray(new String[0]));
}
}

View File

@ -0,0 +1,290 @@
package me.libraryaddict.disguise.utilities.updates;
import lombok.Getter;
import me.libraryaddict.disguise.DisguiseConfig;
import me.libraryaddict.disguise.LibsDisguises;
import me.libraryaddict.disguise.utilities.DisguiseUtilities;
import me.libraryaddict.disguise.utilities.LibsPremium;
import me.libraryaddict.disguise.utilities.plugin.PluginInformation;
import me.libraryaddict.disguise.utilities.translations.LibsMsg;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.bukkit.craftbukkit.libs.org.apache.commons.io.FileUtils;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitTask;
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
public class UpdateChecker {
private final String resourceID;
private final long started = System.currentTimeMillis();
@Getter
private PluginInformation lastDownload;
private final AtomicBoolean downloading = new AtomicBoolean(false);
@Getter
private DisguiseUpdate update;
private LDGithub githubUpdater = new LDGithub();
private LDJenkins jenkinsUpdater = new LDJenkins();
@Getter
private String[] updateMessage = new String[0];
public UpdateChecker(String resourceID) {
this.resourceID = resourceID;
}
public boolean isDownloading() {
return downloading.get();
}
public boolean isUsingReleaseBuilds() {
DisguiseConfig.UpdatesBranch builds = DisguiseConfig.getUpdatesBranch();
return builds == DisguiseConfig.UpdatesBranch.RELEASES ||
(builds == DisguiseConfig.UpdatesBranch.SAME_BUILDS && DisguiseConfig.isUsingReleaseBuild());
}
public void notifyUpdate(CommandSender player) {
if (!DisguiseConfig.isNotifyUpdate() || !player.hasPermission("libsdisguises.update")) {
return;
}
if (updateMessage == null || updateMessage.length == 0) {
return;
}
if (player instanceof Player) {
player.sendMessage(updateMessage);
} else {
for (String s : updateMessage) {
DisguiseUtilities.getLogger().info(s);
}
}
}
public boolean isUpdateReady() {
if (getUpdate() == null) {
return false;
}
String version;
if (getUpdate().isReleaseBuild()) {
if (lastDownload != null) {
version = lastDownload.getVersion();
} else {
version = LibsDisguises.getInstance().getDescription().getVersion();
}
} else {
if (lastDownload != null) {
version = lastDownload.getBuildNumber();
} else {
version = LibsDisguises.getInstance().getBuildNo();
}
}
return getUpdate() != null && !getUpdate().getVersion().equals(version);
}
public void doAutoUpdateCheck() {
try {
DisguiseUpdate oldUpdate = getUpdate();
updateMessage = new String[0];
doUpdateCheck();
if (!isUpdateReady() || (oldUpdate != null && oldUpdate.getVersion().equals(getUpdate().getVersion()))) {
return;
}
notifyUpdate(Bukkit.getConsoleSender());
if (DisguiseConfig.isAutoUpdate()) {
// Update message changed by download
grabJarDownload(getUpdate().getDownload());
notifyUpdate(Bukkit.getConsoleSender());
}
Bukkit.getScheduler().runTask(LibsDisguises.getInstance(), () -> {
for (Player p : Bukkit.getOnlinePlayers()) {
notifyUpdate(p);
}
});
}
catch (Exception ex) {
DisguiseUtilities.getLogger().warning(String.format("Failed to check for update: %s", ex.getMessage()));
}
}
public PluginInformation doUpdate() {
// If no update on file, or more than 6 hours hold. Check for update
if (getUpdate() == null ||
getUpdate().getFetched().before(new Date(System.currentTimeMillis() - TimeUnit.HOURS.toMillis(6)))) {
doUpdateCheck();
}
if (getUpdate() == null) {
return null;
}
return grabJarDownload(getUpdate().getDownload());
}
public LibsMsg doUpdateCheck() {
downloading.set(false);
try {
update = null;
if (isUsingReleaseBuilds()) {
update = githubUpdater.getLatestRelease();
} else {
update = jenkinsUpdater.getLatestSnapshot();
}
}
finally {
downloading.set(false);
}
if (getUpdate() == null) {
return LibsMsg.UPDATE_FAILED;
}
if (getUpdate().isReleaseBuild()) {
String currentVersion = LibsDisguises.getInstance().getDescription().getVersion();
if (!isNewerVersion(currentVersion, getUpdate().getVersion())) {
return LibsMsg.UPDATE_ON_LATEST;
}
updateMessage = new String[]{LibsMsg.UPDATE_READY.get(currentVersion, getUpdate().getVersion())};
} else {
if (!getUpdate().getVersion().matches("[0-9]+")) {
return LibsMsg.UPDATE_FAILED;
}
int newBuild = Integer.parseInt(getUpdate().getVersion());
if (newBuild <= LibsDisguises.getInstance().getBuildNumber()) {
return LibsMsg.UPDATE_ON_LATEST;
}
updateMessage = new String[]{
LibsMsg.UPDATE_READY_SNAPSHOT.get(LibsDisguises.getInstance().getBuildNo(), newBuild)};
}
return null;
}
private PluginInformation grabJarDownload(String urlString) {
downloading.set(true);
DisguiseUtilities.getLogger().info("Now downloading build of Lib's Disguises from " + urlString);
File dest = new File(Bukkit.getUpdateFolderFile(), LibsDisguises.getInstance().getFile().getName());
if (dest.exists()) {
dest.delete();
}
dest.getParentFile().mkdirs();
try {
// We're connecting to spigot's API
URL url = new URL(urlString);
// Creating a connection
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setDefaultUseCaches(false);
// Get the input stream, what we receive
try (InputStream input = con.getInputStream()) {
FileUtils.copyInputStreamToFile(input, dest);
}
DisguiseUtilities.getLogger().info("Download success!");
PluginInformation result = LibsPremium.getInformation(dest);
lastDownload = result;
updateMessage = new String[]{LibsMsg.UPDATE_SUCCESS.get(),
LibsMsg.UPDATE_INFO.get(result.getVersion(), result.getBuildNumber(),
result.getParsedBuildDate().toString(), result.getSize() / 1024)};
return result;
}
catch (Exception ex) {
// Failed, set the last download back to previous build
dest.delete();
DisguiseUtilities.getLogger().warning("Failed to download snapshot build.");
ex.printStackTrace();
}
finally {
downloading.set(false);
}
return null;
}
/**
* Asks spigot for the version
*/
private String fetchSpigotVersion() {
try {
// We're connecting to spigot's API
URL url = new URL("https://api.spigotmc.org/legacy/update.php?resource=" + resourceID);
// Creating a connection
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setDefaultUseCaches(false);
// Get the input stream, what we receive
try (InputStream input = con.getInputStream()) {
// Read it to string
String version = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)).lines()
.collect(Collectors.joining("\n"));
// If the version is not empty, return it
if (!version.isEmpty()) {
return version;
}
}
}
catch (Exception ex) {
DisguiseUtilities.getLogger().warning("Failed to check for a update on spigot.");
}
return null;
}
private boolean isNewerVersion(String currentVersion, String newVersion) {
currentVersion = currentVersion.replaceAll("(v)|(-SNAPSHOT)", "");
newVersion = newVersion.replaceAll("(v)|(-SNAPSHOT)", "");
// If the server has been online for less than 6 hours and both versions are 1.1.1 kind of versions
if (started + TimeUnit.HOURS.toMillis(6) > System.currentTimeMillis() &&
currentVersion.matches("[0-9]+(\\.[0-9]+)*") && newVersion.matches("[0-9]+(\\.[0-9]+)*")) {
int cVersion = Integer.parseInt(currentVersion.replace(".", ""));
int nVersion = Integer.parseInt(newVersion.replace(".", ""));
// If the current version is a higher version, and is only a higher version by 3 minor numbers
// Then we have a cache problem
if (cVersion > nVersion && nVersion + 3 > cVersion) {
return false;
}
}
// Lets just ignore all this fancy logic, and say that if you're not on the current release, you're outdated!
return !currentVersion.equals(newVersion);
}
}

View File

@ -99,19 +99,16 @@ Scoreboard:
WarnConflict: true
# Shall I notify those with the correct permission when there's a LibsDisguises update?
# Disabling this will also disable auto updating
# Disabling this will also disable notifications when the plugin updated
NotifyUpdate: true
# Whats the permission to get the notification?
Permission: 'libsdisguises.update'
# Should the plugin automatically update?
AutoUpdate: true
# Where should the plugin check for updates?
# SAME_BUILDS - Will check snapshots if you're not using a release build
# RELEASES - Only check for actual releases
# SNAPSHOTS - Only check for new snapshots
UpdatesBranch: SAME_BUILDS
# Should the plugin automatically update if the server is using dev builds?
AutoUpdateDev: true
# Whats the max size allowed for command disguiseradius
DisguiseRadiusMax: 50

View File

@ -0,0 +1,8 @@
# If you're using BisectHosting, this will tell the server to enable premium for free!
# However if you're not using BisectHosting, this is false so the server won't waste time
# Coupon 'libraryaddict' for 25% off your first invoice on any of their gaming server
# Be sure to visit through this link! https://bisecthosting.com/libraryaddict
Bisect-Hosted: %data%
Server-IP: %data%
# Should the plugin be doing release or dev builds updating?
ReleaseBuild: %data%