diff --git a/src/main/java/net/minecraft/server/StatisticManager.java b/src/main/java/net/minecraft/server/StatisticManager.java
index ba8e2134e7..86d186a569 100644
--- a/src/main/java/net/minecraft/server/StatisticManager.java
+++ b/src/main/java/net/minecraft/server/StatisticManager.java
@@ -20,6 +20,12 @@ public class StatisticManager {
 
     public void b(EntityHuman entityhuman, Statistic statistic, int i) {
         if (!statistic.d() || this.b((Achievement) statistic)) {
+            // CraftBukkit start
+            org.bukkit.event.Cancellable cancellable = org.bukkit.craftbukkit.event.CraftEventFactory.handleStatisticsIncrease(entityhuman, statistic, a(statistic), i);
+            if (cancellable != null && cancellable.isCancelled()) {
+                return;
+            }
+            // CraftBukkit end
             this.a(entityhuman, statistic, this.a(statistic) + i);
         }
     }
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftAchievement.java b/src/main/java/org/bukkit/craftbukkit/CraftAchievement.java
deleted file mode 100644
index f5897e5835..0000000000
--- a/src/main/java/org/bukkit/craftbukkit/CraftAchievement.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package org.bukkit.craftbukkit;
-
-import org.bukkit.Achievement;
-
-import com.google.common.base.CaseFormat;
-import com.google.common.collect.BiMap;
-import com.google.common.collect.ImmutableBiMap;
-import com.google.common.collect.ImmutableMap;
-
-public class CraftAchievement {
-    private static final BiMap<String, Achievement> achievements;
-    static {
-        ImmutableMap<String, Achievement> specialCases = ImmutableMap.<String, Achievement>builder()
-                .put("achievement.buildWorkBench", Achievement.BUILD_WORKBENCH)
-                .put("achievement.diamonds", Achievement.GET_DIAMONDS)
-                .put("achievement.portal", Achievement.NETHER_PORTAL)
-                .put("achievement.ghast", Achievement.GHAST_RETURN)
-                .put("achievement.theEnd", Achievement.END_PORTAL)
-                .put("achievement.theEnd2", Achievement.THE_END)
-                .put("achievement.blazeRod", Achievement.GET_BLAZE_ROD)
-                .put("achievement.potion", Achievement.BREW_POTION)
-                .build();
-
-        ImmutableBiMap.Builder<String, Achievement> builder = ImmutableBiMap.<String, Achievement>builder();
-        for (Achievement achievement : Achievement.values()) {
-            if (specialCases.values().contains(achievement)) {
-                continue;
-            }
-            builder.put("achievement."+CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, achievement.name()), achievement);
-        }
-
-        builder.putAll(specialCases);
-
-        achievements = builder.build();
-    }
-
-    public static String getAchievementName(Achievement material) {
-        return achievements.inverse().get(material);
-    }
-
-    public static Achievement getAchievement(String name) {
-        return achievements.get(name);
-    }
-}
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftStatistic.java b/src/main/java/org/bukkit/craftbukkit/CraftStatistic.java
new file mode 100644
index 0000000000..7190c5702f
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/CraftStatistic.java
@@ -0,0 +1,142 @@
+package org.bukkit.craftbukkit;
+
+import net.minecraft.server.EntityTypes;
+import net.minecraft.server.MonsterEggInfo;
+import net.minecraft.server.StatisticList;
+
+import org.bukkit.Achievement;
+import org.bukkit.Statistic;
+import org.bukkit.Material;
+import org.bukkit.entity.EntityType;
+
+import com.google.common.base.CaseFormat;
+import com.google.common.collect.BiMap;
+import com.google.common.collect.ImmutableBiMap;
+import com.google.common.collect.ImmutableMap;
+
+public class CraftStatistic {
+    private static final BiMap<String, org.bukkit.Statistic> statistics;
+    private static final BiMap<String, org.bukkit.Achievement> achievements;
+
+    static {
+        ImmutableMap<String, org.bukkit.Achievement> specialCases = ImmutableMap.<String, org.bukkit.Achievement> builder()
+                .put("achievement.buildWorkBench", Achievement.BUILD_WORKBENCH)
+                .put("achievement.diamonds", Achievement.GET_DIAMONDS)
+                .put("achievement.portal", Achievement.NETHER_PORTAL)
+                .put("achievement.ghast", Achievement.GHAST_RETURN)
+                .put("achievement.theEnd", Achievement.END_PORTAL)
+                .put("achievement.theEnd2", Achievement.THE_END)
+                .put("achievement.blazeRod", Achievement.GET_BLAZE_ROD)
+                .put("achievement.potion", Achievement.BREW_POTION)
+                .build();
+        ImmutableBiMap.Builder<String, org.bukkit.Statistic> statisticBuilder = ImmutableBiMap.<String, org.bukkit.Statistic>builder();
+        ImmutableBiMap.Builder<String, org.bukkit.Achievement> achievementBuilder = ImmutableBiMap.<String, org.bukkit.Achievement>builder();
+        for (Statistic statistic : Statistic.values()) {
+            if (statistic == Statistic.PLAY_ONE_TICK) {
+                statisticBuilder.put("stat.playOneMinute", statistic);
+            } else {
+                statisticBuilder.put("stat." + CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, statistic.name()), statistic);
+            }
+        }
+        for (Achievement achievement : Achievement.values()) {
+            if (specialCases.values().contains(achievement)) {
+                continue;
+            }
+            achievementBuilder.put("achievement." + CaseFormat.UPPER_UNDERSCORE.to(CaseFormat.LOWER_CAMEL, achievement.name()), achievement);
+        }
+
+        achievementBuilder.putAll(specialCases);
+
+        statistics = statisticBuilder.build();
+        achievements = achievementBuilder.build();
+    }
+
+    private CraftStatistic() {}
+
+    public static org.bukkit.Achievement getBukkitAchievement(net.minecraft.server.Achievement achievement) {
+        return getBukkitAchievementByName(achievement.e);
+    }
+
+    public static org.bukkit.Achievement getBukkitAchievementByName(String name) {
+        return achievements.get(name);
+    }
+
+    public static org.bukkit.Statistic getBukkitStatistic(net.minecraft.server.Statistic statistic) {
+        return getBukkitStatisticByName(statistic.e);
+    }
+
+    public static org.bukkit.Statistic getBukkitStatisticByName(String name) {
+        if (name.startsWith("stat.killEntity")) {
+            name = "stat.killEntity";
+        }
+        if (name.startsWith("stat.entityKilledBy")) {
+            name = "stat.entityKilledBy";
+        }
+        if (name.startsWith("stat.breakItem")) {
+            name = "stat.breakItem";
+        }
+        if (name.startsWith("stat.useItem")) {
+            name = "stat.useItem";
+        }
+        if (name.startsWith("stat.mineBlock")) {
+            name = "stat.mineBlock";
+        }
+        if (name.startsWith("stat.craftItem")) {
+            name = "stat.craftItem";
+        }
+        return statistics.get(name);
+    }
+
+    public static net.minecraft.server.Statistic getNMSStatistic(org.bukkit.Statistic statistic) {
+        return StatisticList.a(statistics.inverse().get(statistic));
+    }
+
+    public static net.minecraft.server.Achievement getNMSAchievement(org.bukkit.Achievement achievement) {
+        return (net.minecraft.server.Achievement) StatisticList.a(achievements.inverse().get(achievement));
+    }
+
+    public static net.minecraft.server.Statistic getMaterialStatistic(org.bukkit.Statistic stat, Material material) {
+        try {
+            if (stat == Statistic.MINE_BLOCK) {
+                return StatisticList.C[material.getId()];
+            }
+            if (stat == Statistic.CRAFT_ITEM) {
+                return StatisticList.D[material.getId()];
+            }
+            if (stat == Statistic.USE_ITEM) {
+                return StatisticList.E[material.getId()];
+            }
+            if (stat == Statistic.BREAK_ITEM) {
+                return StatisticList.F[material.getId()];
+            }
+        } catch (ArrayIndexOutOfBoundsException e) {
+            return null;
+        }
+        return null;
+    }
+
+    public static net.minecraft.server.Statistic getEntityStatistic(org.bukkit.Statistic stat, EntityType entity) {
+        MonsterEggInfo monsteregginfo = (MonsterEggInfo) EntityTypes.a.get(Integer.valueOf(entity.getTypeId()));
+
+        if (monsteregginfo != null) {
+            return monsteregginfo.d;
+        }
+        return null;
+    }
+
+    public static EntityType getEntityTypeFromStatistic(net.minecraft.server.Statistic statistic) {
+        String statisticString = statistic.e;
+        return EntityType.fromName(statisticString.substring(statisticString.lastIndexOf(".") + 1));
+    }
+
+    public static Material getMaterialFromStatistic(net.minecraft.server.Statistic statistic) {
+        String statisticString = statistic.e;
+        int id;
+        try {
+            id = Integer.valueOf(statisticString.substring(statisticString.lastIndexOf(".") + 1));
+        } catch (NumberFormatException e) {
+            return null;
+        }
+        return Material.getMaterial(id);
+    }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index 33169e2978..9935e7a4c2 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -20,11 +20,11 @@ import net.minecraft.server.*;
 
 import org.apache.commons.lang.Validate;
 import org.apache.commons.lang.NotImplementedException;
-
 import org.bukkit.*;
 import org.bukkit.Achievement;
-import org.bukkit.Material;
 import org.bukkit.Statistic;
+import org.bukkit.Material;
+import org.bukkit.Statistic.Type;
 import org.bukkit.World;
 import org.bukkit.configuration.serialization.DelegateDeserialization;
 import org.bukkit.conversations.Conversation;
@@ -35,6 +35,7 @@ import org.bukkit.craftbukkit.CraftEffect;
 import org.bukkit.craftbukkit.CraftOfflinePlayer;
 import org.bukkit.craftbukkit.CraftServer;
 import org.bukkit.craftbukkit.CraftSound;
+import org.bukkit.craftbukkit.CraftStatistic;
 import org.bukkit.craftbukkit.CraftWorld;
 import org.bukkit.craftbukkit.map.CraftMapView;
 import org.bukkit.craftbukkit.map.RenderData;
@@ -51,7 +52,6 @@ import org.bukkit.inventory.InventoryView.Property;
 import org.bukkit.map.MapView;
 import org.bukkit.metadata.MetadataValue;
 import org.bukkit.plugin.Plugin;
-import org.bukkit.plugin.messaging.Messenger;
 import org.bukkit.plugin.messaging.StandardMessenger;
 import org.bukkit.scoreboard.Scoreboard;
 
@@ -517,7 +517,29 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
 
     @Override
     public void awardAchievement(Achievement achievement) {
-        // TODO - non-functional as of ID purge
+        Validate.notNull(achievement, "Achievement cannot be null");
+        if (achievement.hasParent() && !hasAchievement(achievement.getParent())) {
+            awardAchievement(achievement.getParent());
+        }
+        getHandle().x().a(getHandle(), CraftStatistic.getNMSAchievement(achievement), 1);
+        getHandle().x().b(getHandle());
+    }
+
+    @Override
+    public void removeAchievement(Achievement achievement) {
+        Validate.notNull(achievement, "Achievement cannot be null");
+        for (Achievement achieve : Achievement.values()) {
+            if (achieve.getParent() == achievement && hasAchievement(achieve)) {
+                removeAchievement(achieve);
+            }
+        }
+        getHandle().x().a(getHandle(), CraftStatistic.getNMSAchievement(achievement), 0);
+    }
+
+    @Override
+    public boolean hasAchievement(Achievement achievement) {
+        Validate.notNull(achievement, "Achievement cannot be null");
+        return getHandle().x().a(CraftStatistic.getNMSAchievement(achievement));
     }
 
     @Override
@@ -525,9 +547,37 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
         incrementStatistic(statistic, 1);
     }
 
+    @Override
+    public void decrementStatistic(Statistic statistic) {
+        decrementStatistic(statistic, 1);
+    }
+
+    @Override
+    public int getStatistic(Statistic statistic) {
+        Validate.notNull(statistic, "Statistic cannot be null");
+        Validate.isTrue(statistic.getType() == Type.UNTYPED, "Must supply additional paramater for this statistic");
+        return getHandle().x().a(CraftStatistic.getNMSStatistic(statistic));
+    }
+
     @Override
     public void incrementStatistic(Statistic statistic, int amount) {
-        // TODO - non-functional as of ID purge
+        Validate.isTrue(amount > 0, "Amount must be greater than 0");
+        setStatistic(statistic, getStatistic(statistic) + amount);
+    }
+
+    @Override
+    public void decrementStatistic(Statistic statistic, int amount) {
+        Validate.isTrue(amount > 0, "Amount must be greater than 0");
+        setStatistic(statistic, getStatistic(statistic) - amount);
+    }
+
+    @Override
+    public void setStatistic(Statistic statistic, int newValue) {
+        Validate.notNull(statistic, "Statistic cannot be null");
+        Validate.isTrue(statistic.getType() == Type.UNTYPED, "Must supply additional paramater for this statistic");
+        Validate.isTrue(newValue >= 0, "Value must be greater than or equal to 0");
+        net.minecraft.server.Statistic nmsStatistic = CraftStatistic.getNMSStatistic(statistic);
+        getHandle().x().a(getHandle(), nmsStatistic, newValue);
     }
 
     @Override
@@ -535,9 +585,85 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
         incrementStatistic(statistic, material, 1);
     }
 
+    @Override
+    public void decrementStatistic(Statistic statistic, Material material) {
+        decrementStatistic(statistic, material, 1);
+    }
+
+    @Override
+    public int getStatistic(Statistic statistic, Material material) {
+        Validate.notNull(statistic, "Statistic cannot be null");
+        Validate.notNull(material, "Material cannot be null");
+        Validate.isTrue(statistic.getType() == Type.BLOCK || statistic.getType() == Type.ITEM, "This statistic does not take a Material parameter");
+        net.minecraft.server.Statistic nmsStatistic = CraftStatistic.getMaterialStatistic(statistic, material);
+        Validate.notNull(nmsStatistic, "The supplied Material does not have a corresponding statistic");
+        return getHandle().x().a(nmsStatistic);
+    }
+
     @Override
     public void incrementStatistic(Statistic statistic, Material material, int amount) {
-        // TODO - non-functional as of ID purge
+        Validate.isTrue(amount > 0, "Amount must be greater than 0");
+        setStatistic(statistic, material, getStatistic(statistic, material) + amount);
+    }
+
+    @Override
+    public void decrementStatistic(Statistic statistic, Material material, int amount) {
+        Validate.isTrue(amount > 0, "Amount must be greater than 0");
+        setStatistic(statistic, material, getStatistic(statistic, material) - amount);
+    }
+
+    @Override
+    public void setStatistic(Statistic statistic, Material material, int newValue) {
+        Validate.notNull(statistic, "Statistic cannot be null");
+        Validate.notNull(material, "Material cannot be null");
+        Validate.isTrue(newValue >= 0, "Value must be greater than or equal to 0");
+        Validate.isTrue(statistic.getType() == Type.BLOCK || statistic.getType() == Type.ITEM, "This statistic does not take a Material parameter");
+        net.minecraft.server.Statistic nmsStatistic = CraftStatistic.getMaterialStatistic(statistic, material);
+        Validate.notNull(nmsStatistic, "The supplied Material does not have a corresponding statistic");
+        getHandle().x().a(getHandle(), nmsStatistic, newValue);
+    }
+
+    @Override
+    public void incrementStatistic(Statistic statistic, EntityType entityType) {
+        incrementStatistic(statistic, entityType, 1);
+    }
+
+    @Override
+    public void decrementStatistic(Statistic statistic, EntityType entityType) {
+        decrementStatistic(statistic, entityType, 1);
+    }
+
+    @Override
+    public int getStatistic(Statistic statistic, EntityType entityType) {
+        Validate.notNull(statistic, "Statistic cannot be null");
+        Validate.notNull(entityType, "EntityType cannot be null");
+        Validate.isTrue(statistic.getType() == Type.ENTITY, "This statistic does not take an EntityType parameter");
+        net.minecraft.server.Statistic nmsStatistic = CraftStatistic.getEntityStatistic(statistic, entityType);
+        Validate.notNull(nmsStatistic, "The supplied EntityType does not have a corresponding statistic");
+        return getHandle().x().a(nmsStatistic);
+    }
+
+    @Override
+    public void incrementStatistic(Statistic statistic, EntityType entityType, int amount) {
+        Validate.isTrue(amount > 0, "Amount must be greater than 0");
+        setStatistic(statistic, entityType, getStatistic(statistic, entityType) + amount);
+    }
+
+    @Override
+    public void decrementStatistic(Statistic statistic, EntityType entityType, int amount) {
+        Validate.isTrue(amount > 0, "Amount must be greater than 0");
+        setStatistic(statistic, entityType, getStatistic(statistic, entityType) - amount);
+    }
+
+    @Override
+    public void setStatistic(Statistic statistic, EntityType entityType, int newValue) {
+        Validate.notNull(statistic, "Statistic cannot be null");
+        Validate.notNull(entityType, "EntityType cannot be null");
+        Validate.isTrue(newValue >= 0, "Value must be greater than or equal to 0");
+        Validate.isTrue(statistic.getType() == Type.ENTITY, "This statistic does not take an EntityType parameter");
+        net.minecraft.server.Statistic nmsStatistic = CraftStatistic.getEntityStatistic(statistic, entityType);
+        Validate.notNull(nmsStatistic, "The supplied EntityType does not have a corresponding statistic");
+        getHandle().x().a(getHandle(), nmsStatistic, newValue);
     }
 
     @Override
diff --git a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
index a654a3e605..721215475c 100644
--- a/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
+++ b/src/main/java/org/bukkit/craftbukkit/event/CraftEventFactory.java
@@ -32,10 +32,12 @@ import net.minecraft.server.WorldServer;
 import org.bukkit.Bukkit;
 import org.bukkit.Material;
 import org.bukkit.Server;
+import org.bukkit.Statistic.Type;
 import org.bukkit.block.Block;
 import org.bukkit.block.BlockFace;
 import org.bukkit.block.BlockState;
 import org.bukkit.craftbukkit.CraftServer;
+import org.bukkit.craftbukkit.CraftStatistic;
 import org.bukkit.craftbukkit.CraftWorld;
 import org.bukkit.craftbukkit.block.CraftBlock;
 import org.bukkit.craftbukkit.block.CraftBlockState;
@@ -47,6 +49,7 @@ import org.bukkit.craftbukkit.util.CraftDamageSource;
 import org.bukkit.craftbukkit.util.CraftMagicNumbers;
 import org.bukkit.entity.Arrow;
 import org.bukkit.entity.Creeper;
+import org.bukkit.entity.EntityType;
 import org.bukkit.entity.Horse;
 import org.bukkit.entity.LightningStrike;
 import org.bukkit.entity.LivingEntity;
@@ -56,6 +59,7 @@ import org.bukkit.entity.Player;
 import org.bukkit.entity.Projectile;
 import org.bukkit.entity.ThrownExpBottle;
 import org.bukkit.entity.ThrownPotion;
+import org.bukkit.event.Cancellable;
 import org.bukkit.event.Event;
 import org.bukkit.event.block.*;
 import org.bukkit.event.block.BlockIgniteEvent.IgniteCause;
@@ -729,4 +733,44 @@ public class CraftEventFactory {
         entity.world.getServer().getPluginManager().callEvent(event);
         return event;
     }
+
+    public static Cancellable handleStatisticsIncrease(EntityHuman entityHuman, net.minecraft.server.Statistic statistic, int current, int incrementation) {
+        Player player = ((EntityPlayer) entityHuman).getBukkitEntity();
+        Event event;
+        if (statistic instanceof net.minecraft.server.Achievement) {
+            if (current != 0) {
+                return null;
+            }
+            event = new PlayerAchievementAwardedEvent(player, CraftStatistic.getBukkitAchievement((net.minecraft.server.Achievement) statistic));
+        } else {
+            org.bukkit.Statistic stat = CraftStatistic.getBukkitStatistic(statistic);
+            switch (stat) {
+                case FALL_ONE_CM:
+                case BOAT_ONE_CM:
+                case CLIMB_ONE_CM:
+                case DIVE_ONE_CM:
+                case FLY_ONE_CM:
+                case HORSE_ONE_CM:
+                case MINECART_ONE_CM:
+                case PIG_ONE_CM:
+                case PLAY_ONE_TICK:
+                case SWIM_ONE_CM:
+                case WALK_ONE_CM:
+                    // Do not process event for these - too spammy
+                    return null;
+                default:
+            }
+            if (stat.getType() == Type.UNTYPED) {
+                event = new PlayerStatisticIncrementEvent(player, stat, current, current + incrementation);
+            } else if (stat.getType() == Type.ENTITY) {
+                EntityType entityType = CraftStatistic.getEntityTypeFromStatistic(statistic);
+                event = new PlayerStatisticIncrementEvent(player, stat, current, current + incrementation, entityType);
+            } else {
+                Material material = CraftStatistic.getMaterialFromStatistic(statistic);
+                event = new PlayerStatisticIncrementEvent(player, stat, current, current + incrementation, material);
+            }
+        }
+        entityHuman.world.getServer().getPluginManager().callEvent(event);
+        return (Cancellable) event;
+    }
 }
diff --git a/src/test/java/org/bukkit/AchievementTest.java b/src/test/java/org/bukkit/AchievementTest.java
deleted file mode 100644
index 813568e3bb..0000000000
--- a/src/test/java/org/bukkit/AchievementTest.java
+++ /dev/null
@@ -1,37 +0,0 @@
-package org.bukkit;
-
-import static org.junit.Assert.*;
-import static org.hamcrest.Matchers.*;
-
-import java.util.Collections;
-import java.util.List;
-
-import net.minecraft.server.AchievementList;
-
-import org.bukkit.craftbukkit.CraftAchievement;
-import org.bukkit.support.AbstractTestingBase;
-import org.bukkit.support.Util;
-import org.junit.Test;
-
-import com.google.common.collect.Lists;
-
-public class AchievementTest extends AbstractTestingBase {
-    @Test
-    @SuppressWarnings("unchecked")
-    public void verifyMapping() throws Throwable {
-        List<Achievement> achievements = Lists.newArrayList(Achievement.values());
-
-        for (net.minecraft.server.Achievement statistic : (List<net.minecraft.server.Achievement>) AchievementList.e) {
-            String name = statistic.e;
-
-            String message = String.format("org.bukkit.Achievement is missing: '%s'", name);
-
-            Achievement subject = CraftAchievement.getAchievement(name);
-            assertNotNull(message, subject);
-
-            assertTrue(name, achievements.remove(subject));
-        }
-
-        assertThat("org.bukkit.Achievement has too many achievements", achievements, is(Collections.EMPTY_LIST));
-    }
-}
diff --git a/src/test/java/org/bukkit/ArtTest.java b/src/test/java/org/bukkit/ArtTest.java
index 46804f7ed4..c8e11d86bc 100644
--- a/src/test/java/org/bukkit/ArtTest.java
+++ b/src/test/java/org/bukkit/ArtTest.java
@@ -30,7 +30,7 @@ public class ArtTest {
 
             Art subject = Art.getById(id);
 
-            String message = String.format("org.bukkit.Art is missing id: %d named: '%s'", id - Achievement.STATISTIC_OFFSET, name);
+            String message = String.format("org.bukkit.Art is missing id: %d named: '%s'", id, name);
             assertNotNull(message, subject);
 
             assertThat(Art.getByName(name), is(subject));
diff --git a/src/test/java/org/bukkit/StatisticsAndAchievementsTest.java b/src/test/java/org/bukkit/StatisticsAndAchievementsTest.java
new file mode 100644
index 0000000000..306c2a41e6
--- /dev/null
+++ b/src/test/java/org/bukkit/StatisticsAndAchievementsTest.java
@@ -0,0 +1,61 @@
+package org.bukkit;
+
+import static org.junit.Assert.*;
+import static org.hamcrest.Matchers.*;
+
+import java.util.Collections;
+import java.util.List;
+
+import net.minecraft.server.AchievementList;
+import net.minecraft.server.StatisticList;
+
+import org.bukkit.craftbukkit.CraftStatistic;
+import org.bukkit.support.AbstractTestingBase;
+import org.junit.Test;
+
+import com.google.common.collect.HashMultiset;
+import com.google.common.collect.Lists;
+
+public class StatisticsAndAchievementsTest extends AbstractTestingBase {
+    @Test
+    @SuppressWarnings("unchecked")
+    public void verifyAchievementMapping() throws Throwable {
+        List<Achievement> achievements = Lists.newArrayList(Achievement.values());
+        for (net.minecraft.server.Achievement achievement : (List<net.minecraft.server.Achievement>) AchievementList.e) {
+            String name = achievement.e;
+
+            String message = String.format("org.bukkit.Achievement is missing: '%s'", name);
+
+            Achievement subject = CraftStatistic.getBukkitAchievement(achievement);
+            assertThat(message, subject, is(not(nullValue())));
+
+            assertThat(name, achievements.remove(subject), is(true));
+        }
+
+        assertThat("org.bukkit.Achievement has too many achievements", achievements, is(empty()));
+    }
+
+    @Test
+    @SuppressWarnings("unchecked")
+    public void verifyStatisticMapping() throws Throwable {
+        HashMultiset<Statistic> statistics = HashMultiset.create();
+        for (net.minecraft.server.Statistic statistic : (List<net.minecraft.server.Statistic>) StatisticList.b) {
+            if (statistic instanceof net.minecraft.server.Achievement) {
+                continue;
+            }
+            String name = statistic.e;
+
+            String message = String.format("org.bukkit.Statistic is missing: '%s'", name);
+
+            Statistic subject = CraftStatistic.getBukkitStatistic(statistic);
+            assertThat(message, subject, is(not(nullValue())));
+
+            statistics.add(subject);
+        }
+
+        for (Statistic statistic : Statistic.values()) {
+            String message = String.format("org.bukkit.Statistic.%s does not have a corresponding minecraft statistic", statistic.name());
+            assertThat(message, statistics.remove(statistic, statistics.count(statistic)), is(greaterThan(0)));
+        }
+    }
+}