Added a built-in update checker. See http://wiki.bukkit.org/Bukkit.yml#auto-updater for new bukkit.yml options.

This commit is contained in:
Nathan Adams 2012-02-16 22:31:40 +00:00
parent 64264f61b8
commit 236cfed616
8 changed files with 317 additions and 0 deletions

View file

@ -130,6 +130,11 @@
<version>1.2.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.1</version>
</dependency>
</dependencies>
<!-- This builds a completely 'ready to start' jar with all dependencies inside -->

View file

@ -135,6 +135,7 @@ public class ServerConfigurationManager {
if ((joinMessage != null) && (joinMessage.length() > 0)) {
this.server.serverConfigurationManager.sendAll(new Packet3Chat(joinMessage));
}
this.cserver.onPlayerJoin(playerJoinEvent.getPlayer());
// CraftBukkit end
worldserver.addEntity(entityplayer);

View file

@ -78,6 +78,8 @@ import org.bukkit.craftbukkit.map.CraftMapView;
import org.bukkit.craftbukkit.potion.CraftPotionBrewer;
import org.bukkit.scheduler.BukkitWorker;
import org.bukkit.craftbukkit.scheduler.CraftScheduler;
import org.bukkit.craftbukkit.updater.AutoUpdater;
import org.bukkit.craftbukkit.updater.BukkitDLUpdaterService;
import org.bukkit.craftbukkit.util.DatFileFilter;
import org.bukkit.craftbukkit.util.Versioning;
import org.bukkit.util.permissions.DefaultPermissions;
@ -106,6 +108,7 @@ public final class CraftServer implements Server {
private YamlConfiguration configuration;
private final Yaml yaml = new Yaml(new SafeConstructor());
private final Map<String, OfflinePlayer> offlinePlayers = new MapMaker().softValues().makeMap();
private AutoUpdater updater;
static {
ConfigurationSerialization.registerClass(CraftOfflinePlayer.class);
@ -137,6 +140,12 @@ public final class CraftServer implements Server {
saveConfig();
((SimplePluginManager) pluginManager).useTimings(configuration.getBoolean("settings.plugin-profiling", false));
updater = new AutoUpdater(new BukkitDLUpdaterService(configuration.getString("auto-updater.host")), getLogger(), configuration.getString("auto-updater.preferred-channel"));
updater.setEnabled(configuration.getBoolean("auto-updater.enabled"));
updater.getOnBroken().addAll(configuration.getStringList("auto-updater.on-broken"));
updater.getOnUpdate().addAll(configuration.getStringList("auto-updater.on-update"));
updater.check(serverVersion);
loadPlugins();
enablePlugins(PluginLoadOrder.STARTUP);
@ -1013,4 +1022,14 @@ public final class CraftServer implements Server {
return result;
}
public void onPlayerJoin(Player player) {
if ((updater.isEnabled()) && (player.hasPermission(Server.BROADCAST_CHANNEL_ADMINISTRATIVE))) {
if ((updater.getCurrent().isBroken()) && (updater.getOnBroken().contains(updater.WARN_OPERATORS))) {
player.sendMessage(ChatColor.DARK_RED + "The version of CraftBukkit that this server is running is known to be broken. Please consider updating to the latest version at dl.bukkit.org.");
} else if ((updater.isUpdateAvailable()) && (updater.getOnUpdate().contains(updater.WARN_OPERATORS))) {
player.sendMessage(ChatColor.DARK_PURPLE + "The version of CraftBukkit that this server is running is out of date. Please consider updating to the latest version at dl.bukkit.org.");
}
}
}
}

View file

@ -0,0 +1,81 @@
package org.bukkit.craftbukkit.updater;
import java.util.Date;
public class ArtifactDetails {
private String brokenReason;
private boolean isBroken;
private int buildNumber;
private String htmlUrl;
private String version;
private Date created;
private FileDetails file;
public FileDetails getFile() {
return file;
}
public void setFile(FileDetails file) {
this.file = file;
}
public String getBrokenReason() {
return brokenReason;
}
public void setBrokenReason(String brokenReason) {
this.brokenReason = brokenReason;
}
public int getBuildNumber() {
return buildNumber;
}
public void setBuildNumber(int buildNumber) {
this.buildNumber = buildNumber;
}
public Date getCreated() {
return created;
}
public void setCreated(Date created) {
this.created = created;
}
public String getHtmlUrl() {
return htmlUrl;
}
public void setHtmlUrl(String htmlUrl) {
this.htmlUrl = htmlUrl;
}
public boolean isBroken() {
return isBroken;
}
public void setBroken(boolean isBroken) {
this.isBroken = isBroken;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public static class FileDetails {
private String url;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
}

View file

@ -0,0 +1,107 @@
package org.bukkit.craftbukkit.updater;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
public class AutoUpdater {
public static final String WARN_CONSOLE = "warn-console";
public static final String WARN_OPERATORS = "warn-ops";
private final BukkitDLUpdaterService service;
private final List<String> onUpdate = new ArrayList<String>();
private final List<String> onBroken = new ArrayList<String>();
private final Logger log;
private final String channel;
private boolean enabled;
private ArtifactDetails current = null;
private ArtifactDetails latest = null;
public AutoUpdater(BukkitDLUpdaterService service, Logger log, String channel) {
this.service = service;
this.log = log;
this.channel = channel;
}
public String getChannel() {
return channel;
}
public boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean isEnabled) {
this.enabled = isEnabled;
}
public List<String> getOnBroken() {
return onBroken;
}
public List<String> getOnUpdate() {
return onUpdate;
}
public boolean isUpdateAvailable() {
if ((latest == null) || (current == null) || (!isEnabled())) {
return false;
} else {
return latest.getCreated().after(current.getCreated());
}
}
public ArtifactDetails getCurrent() {
return current;
}
public ArtifactDetails getLatest() {
return latest;
}
public void check(final String currentSlug) {
if (!isEnabled()) return;
new Thread() {
@Override
public void run() {
current = service.getArtifact(currentSlug);
latest = service.getArtifact("latest-" + channel);
if (isUpdateAvailable()) {
if ((current.isBroken()) && (onBroken.contains(WARN_CONSOLE))) {
log.severe("----- Bukkit Auto Updater -----");
log.severe("Your version of CraftBukkit is known to be broken. It is strongly advised that you update to a more recent version ASAP.");
log.severe("Known issues with your version:");
for (String line : current.getBrokenReason().split("\n")) {
log.severe("> " + line);
}
log.severe("Newer version " + latest.getVersion() + " (build #" + latest.getBuildNumber() + ") was released on " + latest.getCreated() + ".");
log.severe("Details: " + latest.getHtmlUrl());
log.severe("Download: " + latest.getFile().getUrl());
log.severe("----- ------------------- -----");
} else if (onUpdate.contains(WARN_CONSOLE)) {
log.warning("----- Bukkit Auto Updater -----");
log.warning("Your version of CraftBukkit is out of date. Version " + latest.getVersion() + " (build #" + latest.getBuildNumber() + ") was released on " + latest.getCreated() + ".");
log.warning("Details: " + latest.getHtmlUrl());
log.warning("Download: " + latest.getFile().getUrl());
log.warning("----- ------------------- -----");
}
} else if ((current != null) && (current.isBroken()) && (onBroken.contains(WARN_CONSOLE))) {
log.severe("----- Bukkit Auto Updater -----");
log.severe("Your version of CraftBukkit is known to be broken. It is strongly advised that you update to a more recent version ASAP.");
log.severe("Known issues with your version:");
for (String line : current.getBrokenReason().split("\n")) {
log.severe("> " + line);
}
log.severe("Unfortunately, there is not yet a newer version suitable for your server. We would advise you wait an hour or two, or try out a dev build.");
log.severe("----- ------------------- -----");
}
}
}.start();
}
}

View file

@ -0,0 +1,68 @@
package org.bukkit.craftbukkit.updater;
import com.google.gson.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Type;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
public class BukkitDLUpdaterService {
private static final String API_PREFIX = "/api/1.0/downloads/projects/craftbukkit/view/";
private static final DateDeserializer dateDeserializer = new DateDeserializer();
private final String host;
public BukkitDLUpdaterService(String host) {
this.host = host;
}
public ArtifactDetails getArtifact(String slug) {
try {
return fetchArtifact(slug);
} catch (UnsupportedEncodingException ex) {
Logger.getLogger(BukkitDLUpdaterService.class.getName()).log(Level.WARNING, "Could not get Artifact details for the auto-updater", ex);
} catch (IOException ex) {
Logger.getLogger(BukkitDLUpdaterService.class.getName()).log(Level.WARNING, "Could not get Artifact details for the auto-updater", ex);
}
return null;
}
public ArtifactDetails fetchArtifact(String slug) throws UnsupportedEncodingException, IOException {
URL url = new URL("http", host, API_PREFIX + slug);
InputStreamReader reader = null;
try {
reader = new InputStreamReader(url.openStream());
Gson gson = new GsonBuilder().registerTypeAdapter(Date.class, dateDeserializer).setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES).create();
ArtifactDetails fromJson = gson.fromJson(reader, ArtifactDetails.class);
return fromJson;
} finally {
if (reader != null) {
reader.close();
}
}
}
static class DateDeserializer implements JsonDeserializer<Date> {
private static final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public Date deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException {
try {
return format.parse(je.getAsString());
} catch (ParseException ex) {
throw new JsonParseException("Date is not formatted correctly", ex);
}
}
}
}

View file

@ -24,6 +24,12 @@ settings:
ticks-per:
animal-spawns: 400
monster-spawns: 1
auto-updater:
enabled: true
on-broken: [warn-console, warn-ops]
on-update: [warn-console, warn-ops]
preferred-channel: rb
host: dl.bukkit.org
aliases:
icanhasbukkit:
- version

View file

@ -0,0 +1,30 @@
package org.bukkit.craftbukkit.updater;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import static org.junit.Assert.assertNotNull;
import org.junit.Test;
public class BukkitDLUpdaterServiceTest {
@Test(expected=IOException.class)
public void testHostNotFound() throws UnsupportedEncodingException, IOException {
BukkitDLUpdaterService service = new BukkitDLUpdaterService("404.example.org");
service.fetchArtifact("rb");
}
@Test(expected=FileNotFoundException.class)
public void testArtifactNotFound() throws UnsupportedEncodingException, IOException {
BukkitDLUpdaterService service = new BukkitDLUpdaterService("dl.bukkit.org");
service.fetchArtifact("meep");
}
@Test
public void testArtifactExists() throws UnsupportedEncodingException, IOException {
BukkitDLUpdaterService service = new BukkitDLUpdaterService("dl.bukkit.org");
assertNotNull(service.fetchArtifact("latest-dev"));
}
}