Updater 2.3- Easy, Safe, and Policy-Compliant auto-updating for your plugins [NEW!]

Discussion in 'Resources' started by Gravity, Aug 28, 2012.

Thread Status:
Not open for further replies.
  1. Offline

    Gravity

    Updater - Version 2.3
    Updater is an easy-to-use but robust and fully policy-compliant plugin updating system. It provides plugin developers with the ability to both check for and download new updates straight from BukkitDev, and requires no web server setup to function.

    Download and Source:
    Updater is a single class file that you need to add to your plugin. Simply create a new class somewhere in your plugin named "Updater", and populate it with Updater's source code, which you can find by clicking the "Get Updater" link below. Then, go to the "How to use it" section to learn how adding one line of code to your plugin will implement Updater.

    Get Updater

    Features:

    • No more hassle! Never worry about configuring your Dropbox text files to the latest build's url, or forgetting to update external files again. Upload once to BukkitDev, and as soon as your file is approved clients will start downloading it, even if the approval comes at 4am and you're fast asleep.
    • Setup is as easy as copying a class and giving it your BukkitDev project slug. Updater will do the rest.
    • Ability to tag certain builds as non-update builds. For instance, on my Jenkins system every build is tagged with -DEV, so that people who are using it do not get switched to the official latest build, and can stay testing the pre-release. Simply edit the "noUpdateTag" array in the class to define what kind of builds should be left alone.
    • Don't hassle your users anymore. Server admins have enough on their hands, don't concern them with updates, because they just will /not/ update. From personal experience, I know that the only time I cared about a plugin update was when something broke. It's far too difficult to worry about a new file every day, but if you let Updater automatically install updates, your users will rejoice!
    • Be safe. EVERY file that Updater downloads has been approved by BukkitDev staff. Real humans go line-by-line through the code of each plugin that is approved on dev.bukkit.org, to verify it is free of any malicious code. Your user's shouldn't have to blindly accept your trust, you can instead show and prove to them that by using updater, you are keeping them secure.
    • Works with both .jar file and .zip file updates.
    -- Get Updater --


    How it works:

    - First, Updater connects to BukkitDev API and requests information about your project.

    - It then searches the information for the latest file, and obtains information about it like its name and version number.

    - Optionally, Updater will run a version check, comparing the newest file with the plugin's current version. NOTE: For this to work, your file titles must be named in this format: 'PluginName vVersionNumber', such as 'AntiCheat v1.0' (or simply 'v1.0', the name is not needed, but suggested). Here's a screenshot of how this should look, if done properly:
    File titles with proper version numbers (open)

    [​IMG]

    - Assuming that an update is needed, Updater will download the file from dev.bukkit.org and store it in the update folder. This is a folder defined in the bukkit.yml file where any stored jars will be switched with its currently-in-use counterpart when the system is reloaded or restarted. This means that the user does not need to worry about replacing the downloaded file with the current file; it's all done in the background.

    How to use it:

    If you are using Maven to manage your project you can use my Maven repository to get the dependency. To do this, edit your pom.xml to add the following repository:
    Code:
        <repositories>
            <repository>
                <id>gravity-repo</id>
                <url>http://repo.gravitydevelopment.net</url>
            </repository>
        </repositories>
    
    Then, add the following dependency:
    Code:
        <dependencies>
            <dependency>
                <groupId>net.gravitydevelopment.updater</groupId>
                <artifactId>updater</artifactId>
                <version>2.1</version>
            </dependency>
        </dependencies>
    
    Otherwise, you can use the traditional way and download the source code for Updater. Simply place this somewhere within your plugin's packages, and then switch over to your main class to get to work.

    As with most of my projects, I boast the fact that you only need one line of code added to your main class (the one that extends JavaPlugin) to make this work (along with my Updater class, of course), so here's what it is:

    Code:
    Updater updater = new Updater(this, id, this.getFile(), Updater.UpdateType.DEFAULT, false);
    That's it! This single line of code will literally keep the user updated for the rest of their life. Here's a breakdown of what all these values are:

    1) "this" - The plugin instance. I suggest using this in your onEnable() method, so that you can properly issue the 'this' keyword. Other methods that are called before onEnable() will not work (but anything after it, or that is called BY onEnable() does work).

    2) "id" - This is how Updater finds your project on BukkitDev. If you don't know what this is, follow the instructions on this wiki article.

    3) "this.getFile()" - The plugin's file, this is so that Updater can properly replace your plugin with the update when it is downloaded. Note that this is a protected value, and so it can only be accessed within your plugin's main class

    4) "Updater.UpdateType.DEFAULT" - This allows you to choose which type of update you would like to take place. Currently there are 3 options:
    - DEFAULT - Typically what you would want. Do an update check, and then if it's out of date download and install the latest update.
    - NO_VERSION_CHECK - In case you know you need (or want) to update, skip version checking and just download the latest file, regardless of any it's details.
    - NO_DOWNLOAD - In case you just want to do a version check. No files will be downloaded, but you still get information about the newest build on DBO, like it's version number and size.

    5) "false" - This is a value declaring whether you want Updater to announce the progress of the download, as it takes place. This is similar to what this output (to the console) will look like:
    Output (open)

    2012-08-29 16:30:56 [INFO] [AntiCheat] Enabling AntiCheat v1.3.6-DEV
    2012-08-29 16:30:57 [INFO] About to download a new update: AntiCheat v1.3.5
    2012-08-29 16:30:57 [INFO] Downloading update: 10% of 93738 bytes.
    2012-08-29 16:30:57 [INFO] Downloading update: 20% of 93738 bytes.
    2012-08-29 16:30:57 [INFO] Downloading update: 30% of 93738 bytes.
    2012-08-29 16:30:57 [INFO] Downloading update: 50% of 93738 bytes.
    2012-08-29 16:30:57 [INFO] Downloading update: 70% of 93738 bytes.
    2012-08-29 16:30:57 [INFO] Downloading update: 80% of 93738 bytes.
    2012-08-29 16:30:57 [INFO] Downloading update: 90% of 93738 bytes.
    2012-08-29 16:30:57 [INFO] Downloading update: 100% of 93738 bytes.
    2012-08-29 16:30:57 [INFO] Finished updating.

    If this option is true, and there is no update, there will be no output to the console.

    You can also see these values documented in JavaDocs here: http://gravitydevelopment.net/docs/updater/

    ------------------------------------------------------------------------------------------------------------------------------------
    NOTICE:
    As of Updater 2.0, a configuration file is created to allow server administrators to globally toggle updating for any plugin using this class. While this option does provide a convenient method for server admins to disable all Updater instances, Bukkit project submission guidelines still require that you make your plugin's Updater instance specifically toggleable with its own configuration option. This gives server administrators the opportunity to only disable the updating capabilities of one plugin in particular, should they choose to do so. You may read more about compliance with this policy here.
    ------------------------------------------------------------------------------------------------------------------------------------


    Expanding updater:

    Note: The following contains more advanced user information on controlling Updater. While Updater is very simple to use, it also gives a great deal of feedback and control to the developer if they want to use it. If you are just starting to develop plugins, it is recommended that you stop here and just use Updater as you have learned to use it so far. If you are an advanced user, you may continue on, but know that all of the following info is optional, and only necessary if you want to customize your experience.

    Now, of course you may want to know what the outcome of the process was, so you can inform the user or update some values in your plugin to reflect that it is now updated. This result can easily be obtained by using the "getResult()" call. This returns an UpdateResult that reflects what happened.​

    Code:
            Updater.UpdateResult result = updater.getResult();
            switch(result)
            {
                case SUCCESS:
                    // Success: The updater found an update, and has readied it to be loaded the next time the server restarts/reloads
                    break;
                case NO_UPDATE:
                    // No Update: The updater did not find an update, and nothing was downloaded.
                    break;
                case DISABLED:
                    // Won't Update: The updater was disabled in its configuration file.
                    break;
                case FAIL_DOWNLOAD:
                    // Download Failed: The updater found an update, but was unable to download it.
                    break;
                case FAIL_DBO:
                    // dev.bukkit.org Failed: For some reason, the updater was unable to contact DBO to download the file.
                    break;
                case FAIL_NOVERSION:
                    // No version found: When running the version check, the file on DBO did not contain the a version in the format 'vVersion' such as 'v1.0'.
                    break;
                case FAIL_BADID:
                    // Bad id: The id provided by the plugin running the updater was invalid and doesn't exist on DBO.
                    break;
                case FAIL_APIKEY:
                    // Bad API key: The user provided an invalid API key for the updater to use.
                    break;
                case UPDATE_AVAILABLE:
                  // There was an update found, but because you had the UpdateType set to NO_DOWNLOAD, it was not downloaded.
            }
    All these values, of course, are documented in easy-to-read HTML here: http://gravitydevelopment.net/docs/updater/

    You also may want to know information about the newest update. Some people prefer to have Updater run a version check ONLY (using UpdateType.NO_DOWNLOAD), then, if there is an update available, start notifying admins as they log in that there is a new version ready, with information like file size and version. An admin would then issue a command, and the developer would run Updater again but this time with UpdateType set to NO_VERSION_CHECK, thus downloading the newest build at the admin's request.

    We have a few methods available for you to use for this information. We already know that we can determine the outcome of the version check by calling getResult(), but here are some more methods you can call to get information about the newest file:

    - getLatestName() - Returns the name of the latest file you have uploaded to BukkitDev (Ex: "AntiCheat v1.5.9")
    - getLatestType() - Returns the type of the latest file you have uploaded to BukkitDev (Alpha, Beta, Release)
    - getLatestGameVersion() - Returns the compatible Game Version of the latest file you have uploaded to BukkitDev (Ex: "CB 1.6.2-R1.0")
    - getLatestFileLink() - Returns the link to the latest file you have uploaded.

    The scenario mentioned about would look something like this (pseudocode):

    Code:
    // In main class
    
    public static boolean update = false;
    public static String name = "";
    public static ReleaseType type = null;
    public static String version = "";
    public static String link = "";
    // You would want to make getter methods in your class, this is just for simplicity.
    
    public void onEnable()
    {
      Updater updater = new Updater(this, YOUR_ID_HERE, this.getFile(), Updater.UpdateType.NO_DOWNLOAD, false); // Start Updater but just do a version check
      update = updater.getResult() == Updater.UpdateResult.UPDATE_AVAILABLE; // Determine if there is an update ready for us
      name = updater.getLatestName(); // Get the latest name
      version = updater.getLatestGameVersion(); // Get the latest game version
      type = updater.getLatestType(); // Get the latest file's type
      link = updater.getLatestFileLink(); // Get the latest link
    }
    
    // In a listener class:
    
    @EventHandler
    public void onPlayerJoin(PlayerJoinEvent event)
    {
      Player player = event.getPlayer();
      if(player.hasPermission("foo.bar") && Main.update)
      {
        player.sendMessage("An update is available: " + Main.name + ", a " + Main.type + " for " + Main.version + " available at " + Main.link);
        // Will look like - An update is available: AntiCheat v1.5.9, a release for CB 1.6.2-R0.1 available at http://media.curseforge.com/XYZ
        player.sendMessage("Type /update if you would like to automatically update.");
      }
    }
    
    // And then later in a CommandExecutor class, when they type /update:
    
    Updater updater = new Updater(this, YOUR_ID_HERE, this.getFile(), Updater.UpdateType.NO_VERSION_CHECK, true); // Go straight to downloading, and announce progress to console.
    

     
    Last edited: May 6, 2016
    FisheyLP, Nathat23, Eathuis and 36 others like this.
  2. Offline

    Gravity

    Plumeex the issue is:
    [SimpleBroadcast] The updater could not contact dev.bukkit.org for updating.
    [SimpleBroadcast] If you have not recently modified your configuration and this is the first time you are seeing this message, the site may be experiencing temporary downtime.

    The plugin really should be threading its use of Updater so that errors like this don't kill the server. I do as much as I can within Updater itself to thread things, but there are limitations. As far as the error goes, try it again -- the site might just have been unresponsive for a bit.
     
  3. Offline

    Alshain01

    Gravity
    Question: Is this running it's own timer or if I want it to do a new check daily do I have to do that myself? In your example you put the class instantiation in the onEnable, does that do only a single check?
     
  4. Offline

    Gravity

    Yes, that only runs a single check when the plugin starts. If you wish to run multiple checks, be my guest. I personally see no reason to do so. Remember that all checks need to have a config option to turn them off completely
     
  5. Offline

    Bobcat00

    As a server owner, I do not want updates to be installed automatically,* but it would be great if your system could notify me that updated versions of plugins were available. Is there any way to do this with the new Updater? If not, could it be added?

    * I've seen way too many cases where new plugin versions make drastic configuration changes or require me to make changes before using the new version. Therefore, I do not want plugins to update themselves.
     
  6. Offline

    FerusGrim

    Bobcat00
    If you're a server owner, this shouldn't be an issue. Any plugin developer who submits a plugin to Bukkit is required by policy to have a configuration option to turn Updater off. Not only that, but Updater itself has a global toggle. If you are making use of a plugin that does not allow you to disable an automatic update, you should report it and, in the meantime either remove the plugin, or globally disallow updates from 'plugins/Updater/config.yml'.

    If you're a plugin developer, you can (and should) be doing something like this:
    Code:
    Updater updater = new Updater(this, ID, this.getFile(), ConfigManager.Config.getBoolean("PluginName.Update-Automatically")? Updater.UpdateType.DEFAULT : Updater.UpdateType.NO_DOWNLOAD, false);
    The above uses yml (from class ConfigManager) to determine whether or not "Update-Automatically" is true or false. If true, it downloads the update to be used after a restart. If false, it grabs information about the plugin that you can use to notify the user/console of the update, but doesn't download anything.

    Gravity
    I've recently started using Updater in my plugin. Before now, I didn't have the proper naming schema, however. I've recently updated this from my Bukkit page (and it's gone through to Curse), however Updater is still recognizing the only naming schema.

    Just curious if you happen to know how long it takes for Curse's backend to actually render the changes.
     
  7. Offline

    Bobcat00

    Yes, I know, and I've turned off automatic updating. But what I would like is a global notify only option so I know when plugins have been updated, without any actual updating being performed.
     
  8. Offline

    Gravity

    It's the plugin's responsibility / choice to notify you of updates. If they don't want to and only want to have a boolean for turning on and off auto-updating, that's their prerogative. I can't force them to do anything.

    If you want to download a separate plugin to check all the other ones you have installed, that might be a better route to look down. This thread isn't going to be able to accomplish anything with your issue.
    It could take a bit, you can load up the API in your browser to see if they've changed. If you give me more information about the project I can have a look.
     
  9. Offline

    FerusGrim

    It's updated, now. It took about 8 hours. :p
     
  10. Offline

    Chrisbotcom

    Very awesome! Thank you, Gravity!
     
  11. Offline

    ToastHelmi

    Hey nice Updata but i have on Problem

    I dont like to download the plugin, i only like to informe players about updates

    this is my code
    Code:java
    1. public class UpdateListener implements Listener{
    2.  
    3. Updater u;
    4. boolean ignoreUpdate = false;
    5.  
    6. public UpdateListener(File f){
    7. u = new Updater(GrandTheftMinecart.getInstance(), 63438, f, UpdateType.NO_DOWNLOAD, false);
    8. }
    9.  
    10. public void toggleInfo(){
    11. ignoreUpdate = !ignoreUpdate;
    12. }
    13. public boolean isInfoEnabeld(){
    14. return !ignoreUpdate;
    15. }
    16. @EventHandler
    17. public void onPlayerJoin(PlayerJoinEvent e){
    18. e.getPlayer().sendMessage(""+(u.getResult() == UpdateResult.UPDATE_AVAILABLE));
    19. if(e.getPlayer().hasPermission("gtm.UpdateInfo") &&
    20. u.getResult() == UpdateResult.UPDATE_AVAILABLE &&
    21. !ignoreUpdate){
    22. e.getPlayer().sendMessage(ChatColor.GOLD +""+ ChatColor.BOLD + "A new Version of Grand Theft Minecart is available!");
    23. e.getPlayer().sendMessage("You can download it here: " + u.getLatestFileLink());
    24. }
    25. }
    26. }


    but it always tells me that ther is an update even if i ste down the version in the plugin.yml
    i also named the files on bukkit right as you can see here
    can you tell me what my mistake is
     
  12. Offline

    Gravity

    Remember: if the remote version is at all different from the current version, it will be considered a new update. That is, if you have a different version running than is available, it is considered to be outdated UNLESS the build has a no update tag or you introduce a version calculation to the code. See this commit and the added javadoc for more info: https://github.com/gravitylow/Updater/commit/69ef5ca3b67b23aad875d948d99a8ca1772a9ceb
     
    ToastHelmi likes this.
  13. Offline

    AnorZaken

    I've been gone from plugin development for a long time but now I'm back (and hopefully I will be able to stay for a while). Anyway... To the point: I think that is a great idea and will put it on my todo-list for my PluginUpdater. Don't expect it any time soon though, but if I get around to it I will credit you for the idea.

    On a completely different plugin update related note: I wanted to do exactly this:
    http://dev.bukkit.org/bukkit-plugins/ultimatepluginupdater/
    with the 2.0 version of PluginUpdater, but then I was gone for a very long time and now someone else has beat me to it :p ...oh well ~
     
  14. Offline

    Squawkers13

    Could you possibly upload a version compatible with java 6?
     
  15. Offline

    Gravity

    The current source is compatible with Java 6.
     
  16. Offline

    NABOSUECO98

    I'm not sure if I'm doing something wrong, but it appears to only find files for my project every few checks. On one run it will say that there are no files found, and a few runs later it will find the files and download them.
    This is the output:
    Code:
    [20:20:30 INFO]: [MoreTNT] Loading MoreTNT v0.0.5
    [20:20:30 INFO]: [MoreTNT] Enabling MoreTNT v0.0.5
    [20:20:30 WARN]: [MoreTNT] The updater could not find any files for the project id 77915
    [20:20:30 INFO]: [MoreTNT] Registering Recipes
    [20:20:30 INFO]: [MoreTNT] Shrapnel Explosive
    [20:20:30 INFO]: [MoreTNT] Incendiary Explosive
    [20:20:30 INFO]: [MoreTNT] Toxic Explosive
    [20:20:30 INFO]: Server permissions file permissions.yml is empty, ignoring it
    [20:20:30 INFO]: CONSOLE: Reload complete.
    >reload
    [20:20:32 INFO]: [MoreTNT] Disabling MoreTNT v0.0.5
    [20:20:32 INFO]: [MoreTNT] Loading MoreTNT v0.0.5
    [20:20:32 INFO]: [MoreTNT] Enabling MoreTNT v0.0.5
    [20:20:32 INFO]: [MoreTNT] About to download a new update: MoreTNT v0.0.5.jar
    [20:20:32 INFO]: [MoreTNT] Downloading update: 10% of 29306 bytes.
    [20:20:32 INFO]: [MoreTNT] Downloading update: 40% of 29306 bytes.
    [20:20:32 INFO]: [MoreTNT] Downloading update: 50% of 29306 bytes.
    [20:20:32 INFO]: [MoreTNT] Downloading update: 60% of 29306 bytes.
    [20:20:32 INFO]: [MoreTNT] Downloading update: 70% of 29306 bytes.
    [20:20:32 INFO]: [MoreTNT] Downloading update: 100% of 29306 bytes.
    [20:20:32 INFO]: [MoreTNT] Finished updating.
     
  17. Offline

    lycano

    NABOSUECO98 this error will be thrown when the JSON parser could not do his work. Sometimes a connection is successful (HTTP 200) but the output will be empty. This happens when the connection is reset due to load balancing or a short interruption in cloud services or instable internet connection on your side. I do not know if this happened in your case just a possible explaination.

    Can you reproduce this effect like will it happend everytime your plugin will be loaded? I.e. on server start?
     
  18. Offline

    NABOSUECO98

    lycano The error seems to have resolved itself. It must have been a temporary instability with my internet connection.
     
  19. Offline

    StephenTheHero

    How do you make a simple config that turns updating on/off ?
    EDIT: Never-mind
     
  20. Offline

    PatoTheBest

    Gravity, what do you think of my shouldUpdate() method? that way if you have a later version, it will not say you have an update. It also supports 1.1.1 and 1.1. Doesn't support only an integer, but that can be easily added,
    Code:java
    1. String localVersionString = localVersion.replace(".", ""), remoteVersionString = remoteVersion.replace(".", "");
    2. boolean shouldUpdate;
    3. if(localVersionString.length() == 2){
    4. localVersionString = localVersionString + "0";
    5. }
    6. if(remoteVersionString.length() == 2){
    7. remoteVersionString = remoteVersionString + "0";
    8. }
    9. int localVersionInt = Integer.parseInt(localVersionString), remoteVersionInt = Integer.parseInt(remoteVersionString);
    10. if(localVersionInt < remoteVersionInt){
    11. shouldUpdate = true;
    12. } else {
    13. shouldUpdate = false;
    14. }
    15. return shouldUpdate;
     
  21. Offline

    Condolent

    Updater is not grabbing the latest file of my plugin McRPG. The file got approved around 3 hours ago and uploaded yesterday. Still says version 1.1 is the latest one. Does it take a while for it to register or is something probably wrong here?
     
  22. Offline

    bdubz4552

    Gravity
    I have been using this with Java 7, no problem. I recently moved to Ubuntu which uses Java 6 openJDK, and the @override annotation appears to no longer be valid on the very last void method run(). Is there any reason for this?
     
  23. Offline

    Gravity

    Sometimes the Curse API takes a bit to update. If it's still not working, let me know.
    Not sure about the specifics of the openJDK, but to my knowledge you should only have that issue if you switch to Java 5 -- that version doesn't allow @Override tags on interface methods, but the others should. In any case, if it's giving you errors it's probably just safe to remove that tag.
     
  24. Offline

    Condolent

    Gravity Works perfect now! Think it was due to the servers that was a bit on edge yesterday :p
     
    Gravity likes this.
  25. Offline

    ArsenArsen

    Hey, Gravity i want to make my plugin to have jar witch is auto-installer, and i want to prevent it from downloading using updater, so how i can do it?
     
  26. Offline

    Gravity

    Sorry, I'm not sure I understand what you mean. Could you clarify?
     
  27. Offline

    adam561

    Thanks for making this awesome class for people to use. But I have an issue.
    I keep getting the =: File version should follow the format"PluginName vVERSION" but I have the file name on the post as: "MoreEnchantsPlus v1.5". Do I need to wait for curse to update the file or am I doing something wrong :/

    Thanks for any help :p Nope, I just needed to wait for a bit,
     
  28. Offline

    ArsenArsen

    I wanna upload 2 JAR files, first is downloading ALL needed data(installer), and second is plugin itself(exported from eclipse or maven), and i dont want updater to download first JAR(installer).
     
  29. Offline

    Gravity

    Thanks! You should edit the shouldUpdate method of the updater to return false if the remoteVersion is an installer. This means you're going to need to edit the installer to have a different tag in the version string than the actual plugin. For instance, if your installers are versioned like "1.2-INSTALLER" and your plugin is simply versioned like "1.2" you can exclude the installers with code to this affect:

    Code:
        public boolean shouldUpdate(String localVersion, String remoteVersion) {
            if (remoteVersion.contains("INSTALLER") return false;
            return !localVersion.equalsIgnoreCase(remoteVersion);
        }
    
     
    ArsenArsen likes this.
  30. Offline

    ArsenArsen

    AMAZING Totaly changing to this updater!

    EDIT: you forgoted bracket:
    Code:
        public boolean shouldUpdate(String localVersion, String remoteVersion) {
            if (remoteVersion.contains("INSTALLER")) return false;
            return !localVersion.equalsIgnoreCase(remoteVersion);
        }
    
     
  31. Offline

    Gravity

    Good catch!
     
    ArsenArsen likes this.
Thread Status:
Not open for further replies.

Share This Page