diff --git a/pom.xml b/pom.xml
index afe078e975..b5de605b46 100644
--- a/pom.xml
+++ b/pom.xml
@@ -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 -->
diff --git a/src/main/java/net/minecraft/server/ServerConfigurationManager.java b/src/main/java/net/minecraft/server/ServerConfigurationManager.java
index 3cea43f1e2..1ee34a70bb 100644
--- a/src/main/java/net/minecraft/server/ServerConfigurationManager.java
+++ b/src/main/java/net/minecraft/server/ServerConfigurationManager.java
@@ -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);
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index ac018e97de..763d26aaa0 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -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.");
+            }
+        }
+    }
 }
diff --git a/src/main/java/org/bukkit/craftbukkit/updater/ArtifactDetails.java b/src/main/java/org/bukkit/craftbukkit/updater/ArtifactDetails.java
new file mode 100644
index 0000000000..b9af02d8ba
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/updater/ArtifactDetails.java
@@ -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;
+        }
+    }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/updater/AutoUpdater.java b/src/main/java/org/bukkit/craftbukkit/updater/AutoUpdater.java
new file mode 100644
index 0000000000..ede432e610
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/updater/AutoUpdater.java
@@ -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();
+    }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/updater/BukkitDLUpdaterService.java b/src/main/java/org/bukkit/craftbukkit/updater/BukkitDLUpdaterService.java
new file mode 100644
index 0000000000..cd5984986b
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/updater/BukkitDLUpdaterService.java
@@ -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);
+            }
+        }
+    }
+}
diff --git a/src/main/resources/configurations/bukkit.yml b/src/main/resources/configurations/bukkit.yml
index 875ca16b8f..55c9407de3 100644
--- a/src/main/resources/configurations/bukkit.yml
+++ b/src/main/resources/configurations/bukkit.yml
@@ -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
diff --git a/src/test/java/org/bukkit/craftbukkit/updater/BukkitDLUpdaterServiceTest.java b/src/test/java/org/bukkit/craftbukkit/updater/BukkitDLUpdaterServiceTest.java
new file mode 100644
index 0000000000..701de3a3c5
--- /dev/null
+++ b/src/test/java/org/bukkit/craftbukkit/updater/BukkitDLUpdaterServiceTest.java
@@ -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"));
+    }
+}