From 2ee703c8f984480a3b7391899ee38bd47022c4a9 Mon Sep 17 00:00:00 2001 From: Stanislav Usenkov Date: Wed, 13 Apr 2016 19:59:22 +0600 Subject: [PATCH] =?UTF-8?q?0.9.22b:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D1=8F?= =?UTF-8?q?=20=D0=B0=D0=B2=D1=82=D0=BE=D0=BC=D0=B0=D1=82=D0=B8=D1=87=D0=B5?= =?UTF-8?q?=D1=81=D0=BA=D0=BE=D0=B3=D0=BE=20=D0=BE=D0=B1=D0=BD=D0=BE=D0=B2?= =?UTF-8?q?=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 2 +- .../simsonic/rscPermissions/API/Settings.java | 1 + .../Bukkit/BukkitEventListener.java | 9 + .../Bukkit/Commands/BukkitCommands.java | 5 + .../Bukkit/Commands/CommandUpdate.java | 27 ++ .../rscPermissions/BukkitPluginMain.java | 28 +- .../rscPermissions/Updater/BukkitUpdater.java | 247 ++++++++++++++++++ .../rscPermissions/Updater/Latest.java | 10 + 8 files changed, 318 insertions(+), 11 deletions(-) create mode 100644 src/main/java/ru/simsonic/rscPermissions/Bukkit/Commands/CommandUpdate.java create mode 100644 src/main/java/ru/simsonic/rscPermissions/Updater/BukkitUpdater.java create mode 100644 src/main/java/ru/simsonic/rscPermissions/Updater/Latest.java diff --git a/pom.xml b/pom.xml index ab2450f..dbf8fed 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ ru.simsonic rscPermissions - 0.9.20b + 0.9.22b jar rscPermissions diff --git a/src/main/java/ru/simsonic/rscPermissions/API/Settings.java b/src/main/java/ru/simsonic/rscPermissions/API/Settings.java index 78fe44c..e9c24fa 100644 --- a/src/main/java/ru/simsonic/rscPermissions/API/Settings.java +++ b/src/main/java/ru/simsonic/rscPermissions/API/Settings.java @@ -5,6 +5,7 @@ import ru.simsonic.rscCommonsLibrary.ConnectionMySQL.ConnectionParams; public interface Settings { + public static final String updaterURL = "http://simsonic.github.io/rscPermissions/latest.json"; public static final String chatPrefix = "{GOLD}[rscp] {_LS}"; public static final String separator = "."; public static final String separatorRegExp = "\\."; diff --git a/src/main/java/ru/simsonic/rscPermissions/Bukkit/BukkitEventListener.java b/src/main/java/ru/simsonic/rscPermissions/Bukkit/BukkitEventListener.java index b86954e..b049e29 100644 --- a/src/main/java/ru/simsonic/rscPermissions/Bukkit/BukkitEventListener.java +++ b/src/main/java/ru/simsonic/rscPermissions/Bukkit/BukkitEventListener.java @@ -9,6 +9,7 @@ import org.bukkit.event.EventPriority; import org.bukkit.event.Listener; import org.bukkit.event.player.AsyncPlayerPreLoginEvent; import org.bukkit.event.player.PlayerExpChangeEvent; +import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerKickEvent; import org.bukkit.event.player.PlayerLevelChangeEvent; import org.bukkit.event.player.PlayerLoginEvent; @@ -60,6 +61,14 @@ public class BukkitEventListener implements Listener { rscp.permissionManager.recalculatePlayer(event.getPlayer()); } + @EventHandler(priority = EventPriority.LOWEST) + public void onPlayerJoin(PlayerJoinEvent event) + { + final Player player = event.getPlayer(); + // Inform admins about updates + if(player.hasPermission("rscp.admin")) + rscp.updating.onAdminJoin(player, true); + } @EventHandler public void onPlayerExp(PlayerLevelChangeEvent event) { diff --git a/src/main/java/ru/simsonic/rscPermissions/Bukkit/Commands/BukkitCommands.java b/src/main/java/ru/simsonic/rscPermissions/Bukkit/Commands/BukkitCommands.java index f2f95e8..f9d8799 100644 --- a/src/main/java/ru/simsonic/rscPermissions/Bukkit/Commands/BukkitCommands.java +++ b/src/main/java/ru/simsonic/rscPermissions/Bukkit/Commands/BukkitCommands.java @@ -23,6 +23,7 @@ public class BukkitCommands private final CommandFetch cmdFetch; private final CommandDebug cmdDebug; private final CommandReload cmdReload; + private final CommandUpdate cmdUpdate; public final BukkitDatabaseFetcher threadFetchDatabaseContents; public BukkitCommands(final BukkitPluginMain plugin) { @@ -31,6 +32,7 @@ public class BukkitCommands cmdFetch = new CommandFetch(rscp); cmdDebug = new CommandDebug(rscp); cmdReload = new CommandReload(rscp); + cmdUpdate = new CommandUpdate(rscp); threadFetchDatabaseContents = new BukkitDatabaseFetcher(rscp); } public Thread threadMigrateFromPExSQL(final CommandSender sender) @@ -167,6 +169,9 @@ public class BukkitCommands /* rscp reload */ cmdReload.execute(sender); return; + case "update": + cmdUpdate.execute(sender, args); + return; case "help": default: break; diff --git a/src/main/java/ru/simsonic/rscPermissions/Bukkit/Commands/CommandUpdate.java b/src/main/java/ru/simsonic/rscPermissions/Bukkit/Commands/CommandUpdate.java new file mode 100644 index 0000000..357122d --- /dev/null +++ b/src/main/java/ru/simsonic/rscPermissions/Bukkit/Commands/CommandUpdate.java @@ -0,0 +1,27 @@ +package ru.simsonic.rscPermissions.Bukkit.Commands; + +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import ru.simsonic.rscMinecraftLibrary.Bukkit.CommandAnswerException; +import ru.simsonic.rscPermissions.BukkitPluginMain; + +public class CommandUpdate +{ + private final BukkitPluginMain rscp; + CommandUpdate(BukkitPluginMain plugin) + { + this.rscp = plugin; + } + public void execute(CommandSender sender, String args[]) throws CommandAnswerException + { + if(sender.hasPermission("rscp.admin")) + { + if(args.length > 0 && "do".equals(args[0])) + { + rscp.updating.doUpdate(sender instanceof Player ? (Player)sender : null); + } else { + rscp.updating.checkUpdate(sender instanceof Player ? (Player)sender : null); + } + } + } +} diff --git a/src/main/java/ru/simsonic/rscPermissions/BukkitPluginMain.java b/src/main/java/ru/simsonic/rscPermissions/BukkitPluginMain.java index 50614f9..f6bddcd 100644 --- a/src/main/java/ru/simsonic/rscPermissions/BukkitPluginMain.java +++ b/src/main/java/ru/simsonic/rscPermissions/BukkitPluginMain.java @@ -6,11 +6,13 @@ import java.util.logging.Logger; import org.bukkit.Bukkit; import org.bukkit.command.Command; import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitScheduler; import org.mcstats.MetricsLite; import ru.simsonic.rscMinecraftLibrary.Bukkit.CommandAnswerException; import ru.simsonic.rscMinecraftLibrary.Bukkit.GenericChatCodes; +import ru.simsonic.rscMinecraftLibrary.Bukkit.Tools; import ru.simsonic.rscPermissions.API.Settings; import ru.simsonic.rscPermissions.Bukkit.BukkitEventListener; import ru.simsonic.rscPermissions.Bukkit.BukkitPermissionManager; @@ -23,20 +25,22 @@ import ru.simsonic.rscPermissions.Engine.Backends.BackendJson; import ru.simsonic.rscPermissions.Engine.Backends.DatabaseContents; import ru.simsonic.rscPermissions.Engine.InternalCache; import ru.simsonic.rscPermissions.Engine.Phrases; +import ru.simsonic.rscPermissions.Updater.BukkitUpdater; public final class BukkitPluginMain extends JavaPlugin { public static final Logger consoleLog = Bukkit.getLogger(); - public final Settings settings = new BukkitPluginConfiguration(this); - public final BridgeForBukkitAPI bridgeForBukkit = new BridgeForBukkitAPI(this); - public final BukkitEventListener bukkitListener = new BukkitEventListener(this); - public final BackendJson localStorage = new BackendJson(getDataFolder()); - public final BackendDatabase connection = new BackendDatabase(consoleLog); - public final InternalCache internalCache = new InternalCache(); - public final BukkitPermissionManager permissionManager = new BukkitPermissionManager(this); - public final BukkitRegionProviders regionListProvider = new BukkitRegionProviders(this); - private final RegionUpdateObserver regionUpdateObserver = new RegionUpdateObserver(this); - public final BukkitCommands commandHelper = new BukkitCommands(this); + public final Settings settings = new BukkitPluginConfiguration(this); + public final BukkitUpdater updating = new BukkitUpdater(this, Settings.updaterURL, Settings.chatPrefix); + public final BackendJson localStorage = new BackendJson(getDataFolder()); + public final BackendDatabase connection = new BackendDatabase(consoleLog); + public final InternalCache internalCache = new InternalCache(); + public final BukkitCommands commandHelper = new BukkitCommands(this); + public final BridgeForBukkitAPI bridgeForBukkit = new BridgeForBukkitAPI(this); + public final BukkitEventListener bukkitListener = new BukkitEventListener(this); + public final BukkitPermissionManager permissionManager = new BukkitPermissionManager(this); + public final BukkitRegionProviders regionListProvider = new BukkitRegionProviders(this); + private final RegionUpdateObserver regionUpdateObserver = new RegionUpdateObserver(this); private MetricsLite metrics; @Override public void onLoad() @@ -51,6 +55,7 @@ public final class BukkitPluginMain extends JavaPlugin { // Read settings and setup components settings.readSettings(); + updating.onEnable(); bukkitListener.onEnable(); internalCache.setDefaultGroup( settings.getDefaultGroup(), @@ -99,6 +104,9 @@ public final class BukkitPluginMain extends JavaPlugin if(settings.getAutoReloadDelayTicks() > 0) commandHelper.threadFetchDatabaseContents.startDeamon(); // Done + for(Player online : Tools.getOnlinePlayers()) + if(online.hasPermission("rscm.admin")) + updating.onAdminJoin(online, false); consoleLog.info(Phrases.PLUGIN_ENABLED.toString()); } @Override diff --git a/src/main/java/ru/simsonic/rscPermissions/Updater/BukkitUpdater.java b/src/main/java/ru/simsonic/rscPermissions/Updater/BukkitUpdater.java new file mode 100644 index 0000000..30d185e --- /dev/null +++ b/src/main/java/ru/simsonic/rscPermissions/Updater/BukkitUpdater.java @@ -0,0 +1,247 @@ +package ru.simsonic.rscPermissions.Updater; + +import com.google.gson.Gson; +import com.google.gson.JsonParseException; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.channels.Channels; +import java.nio.channels.ReadableByteChannel; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import org.bukkit.command.ConsoleCommandSender; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.player.PlayerKickEvent; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.plugin.java.JavaPlugin; +import ru.simsonic.rscCommonsLibrary.RestartableThread; +import ru.simsonic.rscMinecraftLibrary.Bukkit.GenericChatCodes; + +public final class BukkitUpdater implements Listener +{ + private final JavaPlugin plugin; + private final String latestURL; + private final HashSet staff = new HashSet<>(); + private final String chatPrefix; + public BukkitUpdater(JavaPlugin plugin, String latestURL, String chatPrefix) + { + this.plugin = plugin; + this.latestURL = latestURL; + this.chatPrefix = chatPrefix; + } + public void onEnable() + { + plugin.getServer().getPluginManager().registerEvents(BukkitUpdater.this, plugin); + checkUpdate(null); + } + public void checkUpdate(Player sender) + { + if(sender != null) + staff.add(sender); + threadCheck.start(); + } + public void doUpdate(Player sender) + { + if(sender != null) + staff.add(sender); + threadUpdate.start(); + } + private final RestartableThread threadCheck = new RestartableThread() + { + @Override + public void run() + { + checkForUpdate(); + final ArrayList lines = latestToLines(); + if(lines != null) + runLines(lines.toArray(new String[lines.size()])); + else + runLine("You are using the latest version."); + } + }; + private final RestartableThread threadUpdate = new RestartableThread() + { + @Override + public void run() + { + runLine("Downloading update..."); + if(downloadUpdate()) + { + // SUCCESS + runLine("Installing update..."); + installUpdate(); + runLines(new String[] + { + "{_LG}Installation complete!", + "Please restart your server to avoid errors.", + }); + } else { + // FAILED + runLines(new String[] { + "{_LR}Downloading error!", + "Cannot download update file. Please try later.", + }); + } + } + }; + private Latest latest = new Latest(); + private void checkForUpdate() + { + try + { + this.latest = new Gson().fromJson(downloadJson(latestURL), Latest.class); + } catch(IOException ex) { + this.latest = new Latest(); + } + if(latest.note == null) + latest.note = "New version: " + latest.version; + if(latest.notes == null) + latest.notes = new String[] { latest.note }; + if(latest.version == null) + latest.version = plugin.getDescription().getVersion(); + } + private void runLine(final String line) + { + runLines(new String[] { line }); + } + private void runLines(final String[] lines) + { + final Runnable syncTask = new Runnable() + { + @Override + public synchronized void run() + { + // CONSOLE + final ConsoleCommandSender console = plugin.getServer().getConsoleSender(); + for(String line : lines) + if(line != null) + console.sendMessage(GenericChatCodes.processStringStatic(chatPrefix + line)); + // PLAYERS + for(Player online : staff) + for(String line : lines) + if(line != null) + online.sendMessage(GenericChatCodes.processStringStatic(chatPrefix + line)); + notify(); + } + }; + try + { + synchronized(syncTask) + { + plugin.getServer().getScheduler().runTask(plugin, syncTask); + syncTask.wait(); + } + } catch(InterruptedException ex) { + } + } + private ArrayList latestToLines() + { + // THERE IS NO UPDATE + if(plugin.getDescription().getVersion().equals(latest.version)) + return null; + // THERE IS AN UPDATE + final ArrayList result = new ArrayList<>(); + result.add("New " + + (latest.snapshot ? "{_DS}snapshot {_LS}" : "{_WH}release {_LS}") + + "version {_LG}" + latest.version + "{_LS} is available!"); + result.addAll(Arrays.asList(latest.notes)); + result.add("Apply this update with command {GOLD}/rscfjd update do"); + return result; + } + public void onAdminJoin(Player player, boolean fromEvent) + { + staff.add(player); + if(fromEvent) + { + final ArrayList lines = latestToLines(); + if(lines != null) + for(String line : lines) + if(line != null) + player.sendMessage(GenericChatCodes.processStringStatic(chatPrefix + line)); + } + } + @EventHandler + protected void onPlayerQuit(PlayerQuitEvent event) + { + staff.add(event.getPlayer()); + } + @EventHandler + protected void onPlayerKick(PlayerKickEvent event) + { + staff.add(event.getPlayer()); + } + private static String downloadJson(String url) throws IOException + { + try + { + final HttpURLConnection connection = (HttpURLConnection)new URL(url).openConnection(); + connection.setDoOutput(true); + connection.setDoInput(true); + connection.setConnectTimeout(5000); + connection.setReadTimeout(5000); + connection.setUseCaches(false); + final int responseCode = connection.getResponseCode(); + if(responseCode == HttpURLConnection.HTTP_OK) + return readUnicodeStream(connection.getInputStream()); + throw new IOException(new StringBuilder() + .append(Integer.toString(responseCode)) + .append("Erroneous result of executing web-method: ") + .append(connection.getResponseMessage()) + .append("\r\n") + .append(readUnicodeStream(connection.getErrorStream())) + .toString()); + } catch(JsonParseException | MalformedURLException ex) { + throw new IOException(ex); + } catch(IOException ex) { + throw ex; + } + } + private static String readUnicodeStream(InputStream is) throws IOException + { + try(ByteArrayOutputStream baos = new ByteArrayOutputStream()) + { + final byte[] buffer = new byte[1024]; + for(int length = 0; length != -1; length = is.read(buffer)) + baos.write(buffer, 0, length); + return new String(baos.toByteArray(), "UTF-8"); + } + } + private boolean downloadUpdate() + { + final File folder = plugin.getDataFolder().getParentFile(); + final File target = new File(folder, plugin.getName() + "_v" + latest.version + ".jar"); + try(FileOutputStream fos = new FileOutputStream(target)) + { + final ReadableByteChannel rbc = Channels.newChannel(new URL(latest.url).openStream()); + fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); + fos.flush(); + return true; + } catch(MalformedURLException ex) { + System.err.println(ex); + } catch(IOException ex) { + System.err.println(ex); + } + return false; + } + private void installUpdate() + { + // RENAME OLD VERSION + try + { + final String outdatedJarPath = plugin.getClass().getProtectionDomain().getCodeSource().getLocation().toURI().getPath(); + final File outdatedJarSrc = new File(outdatedJarPath); + final File outdatedJarDst = new File(outdatedJarPath + "-outdated"); + outdatedJarSrc.renameTo(outdatedJarDst); + } catch(URISyntaxException ex) { + } + } +} diff --git a/src/main/java/ru/simsonic/rscPermissions/Updater/Latest.java b/src/main/java/ru/simsonic/rscPermissions/Updater/Latest.java new file mode 100644 index 0000000..ae3c725 --- /dev/null +++ b/src/main/java/ru/simsonic/rscPermissions/Updater/Latest.java @@ -0,0 +1,10 @@ +package ru.simsonic.rscPermissions.Updater; + +public class Latest +{ + public String version; + public String note; + public String[] notes; + public String url; + public boolean snapshot; +}