diff --git a/src/main/java/net/minecraft/server/EntityHuman.java b/src/main/java/net/minecraft/server/EntityHuman.java
index 101c2a0ddd..91b44a0319 100644
--- a/src/main/java/net/minecraft/server/EntityHuman.java
+++ b/src/main/java/net/minecraft/server/EntityHuman.java
@@ -436,11 +436,13 @@ public abstract class EntityHuman extends EntityLiving implements ICommandListen
 
     public void c(Entity entity, int i) {
         this.addScore(i);
-        Collection collection = this.getScoreboard().getObjectivesForCriteria(IScoreboardCriteria.e);
+        // CraftBukkit - Get our scores instead
+        Collection<ScoreboardScore> collection = this.world.getServer().getScoreboardManager().getScoreboardScores(IScoreboardCriteria.e, this.getLocalizedName(), new java.util.ArrayList<ScoreboardScore>());
 
         if (entity instanceof EntityHuman) {
             this.a(StatisticList.A, 1);
-            collection.addAll(this.getScoreboard().getObjectivesForCriteria(IScoreboardCriteria.d));
+            // CraftBukkit - Get our scores instead
+            this.world.getServer().getScoreboardManager().getScoreboardScores(IScoreboardCriteria.d, this.getLocalizedName(), collection);
         } else {
             this.a(StatisticList.z, 1);
         }
@@ -448,8 +450,7 @@ public abstract class EntityHuman extends EntityLiving implements ICommandListen
         Iterator iterator = collection.iterator();
 
         while (iterator.hasNext()) {
-            ScoreboardObjective scoreboardobjective = (ScoreboardObjective) iterator.next();
-            ScoreboardScore scoreboardscore = this.getScoreboard().getPlayerScoreForObjective(this.getLocalizedName(), scoreboardobjective);
+            ScoreboardScore scoreboardscore = (ScoreboardScore) iterator.next(); // CraftBukkit - Use our scores instead
 
             scoreboardscore.incrementScore();
         }
@@ -687,10 +688,28 @@ public abstract class EntityHuman extends EntityLiving implements ICommandListen
     }
 
     public boolean a(EntityHuman entityhuman) {
-        ScoreboardTeam scoreboardteam = this.getScoreboardTeam();
-        ScoreboardTeam scoreboardteam1 = entityhuman.getScoreboardTeam();
+        // CraftBukkit start - Change to check player's scoreboard team according to API reference to this (or main) scoreboard
+        org.bukkit.scoreboard.Team team;
+        if (this instanceof EntityPlayer) {
+            EntityPlayer thisPlayer = (EntityPlayer) this;
+            team = thisPlayer.getBukkitEntity().getScoreboard().getPlayerTeam(thisPlayer.getBukkitEntity());
+            if (team == null || team.allowFriendlyFire()) {
+                return true;
+            }
+        } else {
+            // This should never be called, but is implemented anyway
+            org.bukkit.OfflinePlayer thisPlayer = this.world.getServer().getOfflinePlayer(this.name);
+            team = this.world.getServer().getScoreboardManager().getMainScoreboard().getPlayerTeam(thisPlayer);
+            if (team == null || team.allowFriendlyFire()) {
+                return true;
+            }
+        }
 
-        return scoreboardteam != scoreboardteam1 ? true : (scoreboardteam != null ? scoreboardteam.allowFriendlyFire() : true);
+        if (entityhuman instanceof EntityPlayer) {
+            return team.hasPlayer(((EntityPlayer) entityhuman).getBukkitEntity());
+        }
+        return team.hasPlayer(this.world.getServer().getOfflinePlayer(entityhuman.name));
+        // CraftBukkit end
     }
 
     protected void a(EntityLiving entityliving, boolean flag) {
@@ -1494,6 +1513,7 @@ public abstract class EntityHuman extends EntityLiving implements ICommandListen
     }
 
     public String getScoreboardDisplayName() {
+        // TODO: fun
         return ScoreboardTeam.getPlayerDisplayName(this.getScoreboardTeam(), this.name);
     }
 }
diff --git a/src/main/java/net/minecraft/server/EntityPlayer.java b/src/main/java/net/minecraft/server/EntityPlayer.java
index 14f2521e1a..eb07d8e09e 100644
--- a/src/main/java/net/minecraft/server/EntityPlayer.java
+++ b/src/main/java/net/minecraft/server/EntityPlayer.java
@@ -201,14 +201,8 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
 
     public void setHealth(int i) {
         super.setHealth(i);
-        Collection collection = this.getScoreboard().getObjectivesForCriteria(IScoreboardCriteria.f);
-        Iterator iterator = collection.iterator();
-
-        while (iterator.hasNext()) {
-            ScoreboardObjective scoreboardobjective = (ScoreboardObjective) iterator.next();
-
-            this.getScoreboard().getPlayerScoreForObjective(this.getLocalizedName(), scoreboardobjective).updateForList(Arrays.asList(new EntityHuman[] { this}));
-        }
+        // CraftBukkit - Update ALL the scores!
+        this.world.getServer().getScoreboardManager().updateAllScoresForList(IScoreboardCriteria.f, this.getLocalizedName(), com.google.common.collect.ImmutableList.of(this));
     }
 
     public void g() {
@@ -304,12 +298,12 @@ public class EntityPlayer extends EntityHuman implements ICrafting {
         this.closeInventory();
         // CraftBukkit end
 
-        Collection collection = this.world.getScoreboard().getObjectivesForCriteria(IScoreboardCriteria.c);
+        // CraftBukkit - Get our scores instead
+        Collection<ScoreboardScore> collection = this.world.getServer().getScoreboardManager().getScoreboardScores(IScoreboardCriteria.c, this.getLocalizedName(), new java.util.ArrayList<ScoreboardScore>());
         Iterator iterator = collection.iterator();
 
         while (iterator.hasNext()) {
-            ScoreboardObjective scoreboardobjective = (ScoreboardObjective) iterator.next();
-            ScoreboardScore scoreboardscore = this.getScoreboard().getPlayerScoreForObjective(this.getLocalizedName(), scoreboardobjective);
+            ScoreboardScore scoreboardscore = (ScoreboardScore) iterator.next(); // CraftBukkit - Use our scores instead
 
             scoreboardscore.incrementScore();
         }
diff --git a/src/main/java/net/minecraft/server/MinecraftServer.java b/src/main/java/net/minecraft/server/MinecraftServer.java
index 5bf5fdc25a..64d588257d 100644
--- a/src/main/java/net/minecraft/server/MinecraftServer.java
+++ b/src/main/java/net/minecraft/server/MinecraftServer.java
@@ -225,6 +225,8 @@ public abstract class MinecraftServer implements ICommandListener, Runnable, IMo
                 world.getWorld().getPopulators().addAll(gen.getDefaultPopulators(world.getWorld()));
             }
 
+            this.server.scoreboardManager = new org.bukkit.craftbukkit.scoreboard.CraftScoreboardManager(this, world.getScoreboard());
+
             this.server.getPluginManager().callEvent(new org.bukkit.event.world.WorldInitEvent(world.getWorld()));
 
             world.addIWorldAccess(new WorldManager(this, world));
diff --git a/src/main/java/net/minecraft/server/PlayerList.java b/src/main/java/net/minecraft/server/PlayerList.java
index 464b25094d..ed670d92a1 100644
--- a/src/main/java/net/minecraft/server/PlayerList.java
+++ b/src/main/java/net/minecraft/server/PlayerList.java
@@ -126,7 +126,7 @@ public abstract class PlayerList {
         }
     }
 
-    protected void a(ScoreboardServer scoreboardserver, EntityPlayer entityplayer) {
+    public void a(ScoreboardServer scoreboardserver, EntityPlayer entityplayer) { // CraftBukkit - protected -> public
         HashSet hashset = new HashSet();
         Iterator iterator = scoreboardserver.getTeams().iterator();
 
@@ -277,6 +277,8 @@ public abstract class PlayerList {
                 entityplayer1.playerConnection.sendPacket(packet);
             }
         }
+        // This removes the scoreboard (and player reference) for the specific player in the manager
+        this.cserver.getScoreboardManager().removePlayer(entityplayer.getBukkitEntity());
 
         return playerQuitEvent.getQuitMessage();
         // CraftBukkit end
diff --git a/src/main/java/net/minecraft/server/ScoreboardServer.java b/src/main/java/net/minecraft/server/ScoreboardServer.java
index 0f28b32838..863b4db104 100644
--- a/src/main/java/net/minecraft/server/ScoreboardServer.java
+++ b/src/main/java/net/minecraft/server/ScoreboardServer.java
@@ -20,7 +20,7 @@ public class ScoreboardServer extends Scoreboard {
     public void handleScoreChanged(ScoreboardScore scoreboardscore) {
         super.handleScoreChanged(scoreboardscore);
         if (this.b.contains(scoreboardscore.getObjective())) {
-            this.a.getPlayerList().sendAll(new Packet207SetScoreboardScore(scoreboardscore, 0));
+            this.sendAll(new Packet207SetScoreboardScore(scoreboardscore, 0)); // CraftBukkit - Internal packet method
         }
 
         this.b();
@@ -28,7 +28,7 @@ public class ScoreboardServer extends Scoreboard {
 
     public void handlePlayerRemoved(String s) {
         super.handlePlayerRemoved(s);
-        this.a.getPlayerList().sendAll(new Packet207SetScoreboardScore(s));
+        this.sendAll(new Packet207SetScoreboardScore(s)); // CraftBukkit - Internal packet method
         this.b();
     }
 
@@ -38,7 +38,7 @@ public class ScoreboardServer extends Scoreboard {
         super.setDisplaySlot(i, scoreboardobjective);
         if (scoreboardobjective1 != scoreboardobjective && scoreboardobjective1 != null) {
             if (this.h(scoreboardobjective1) > 0) {
-                this.a.getPlayerList().sendAll(new Packet208SetScoreboardDisplayObjective(i, scoreboardobjective));
+                this.sendAll(new Packet208SetScoreboardDisplayObjective(i, scoreboardobjective)); // CraftBukkit - Internal packet method
             } else {
                 this.g(scoreboardobjective1);
             }
@@ -46,7 +46,7 @@ public class ScoreboardServer extends Scoreboard {
 
         if (scoreboardobjective != null) {
             if (this.b.contains(scoreboardobjective)) {
-                this.a.getPlayerList().sendAll(new Packet208SetScoreboardDisplayObjective(i, scoreboardobjective));
+                this.sendAll(new Packet208SetScoreboardDisplayObjective(i, scoreboardobjective)); // CraftBukkit - Internal packet method
             } else {
                 this.e(scoreboardobjective);
             }
@@ -57,13 +57,13 @@ public class ScoreboardServer extends Scoreboard {
 
     public void addPlayerToTeam(String s, ScoreboardTeam scoreboardteam) {
         super.addPlayerToTeam(s, scoreboardteam);
-        this.a.getPlayerList().sendAll(new Packet209SetScoreboardTeam(scoreboardteam, Arrays.asList(new String[] { s}), 3));
+        this.sendAll(new Packet209SetScoreboardTeam(scoreboardteam, Arrays.asList(new String[] { s}), 3)); // CraftBukkit - Internal packet method
         this.b();
     }
 
     public void removePlayerFromTeam(String s, ScoreboardTeam scoreboardteam) {
         super.removePlayerFromTeam(s, scoreboardteam);
-        this.a.getPlayerList().sendAll(new Packet209SetScoreboardTeam(scoreboardteam, Arrays.asList(new String[] { s}), 4));
+        this.sendAll(new Packet209SetScoreboardTeam(scoreboardteam, Arrays.asList(new String[] { s}), 4)); // CraftBukkit - Internal packet method
         this.b();
     }
 
@@ -75,7 +75,7 @@ public class ScoreboardServer extends Scoreboard {
     public void handleObjectiveChanged(ScoreboardObjective scoreboardobjective) {
         super.handleObjectiveChanged(scoreboardobjective);
         if (this.b.contains(scoreboardobjective)) {
-            this.a.getPlayerList().sendAll(new Packet206SetScoreboardObjective(scoreboardobjective, 2));
+            this.sendAll(new Packet206SetScoreboardObjective(scoreboardobjective, 2)); // CraftBukkit - Internal packet method
         }
 
         this.b();
@@ -92,19 +92,19 @@ public class ScoreboardServer extends Scoreboard {
 
     public void handleTeamAdded(ScoreboardTeam scoreboardteam) {
         super.handleTeamAdded(scoreboardteam);
-        this.a.getPlayerList().sendAll(new Packet209SetScoreboardTeam(scoreboardteam, 0));
+        this.sendAll(new Packet209SetScoreboardTeam(scoreboardteam, 0)); // CraftBukkit - Internal packet method
         this.b();
     }
 
     public void handleTeamChanged(ScoreboardTeam scoreboardteam) {
         super.handleTeamChanged(scoreboardteam);
-        this.a.getPlayerList().sendAll(new Packet209SetScoreboardTeam(scoreboardteam, 2));
+        this.sendAll(new Packet209SetScoreboardTeam(scoreboardteam, 2)); // CraftBukkit - Internal packet method
         this.b();
     }
 
     public void handleTeamRemoved(ScoreboardTeam scoreboardteam) {
         super.handleTeamRemoved(scoreboardteam);
-        this.a.getPlayerList().sendAll(new Packet209SetScoreboardTeam(scoreboardteam, 1));
+        this.sendAll(new Packet209SetScoreboardTeam(scoreboardteam, 1)); // CraftBukkit - Internal packet method
         this.b();
     }
 
@@ -146,6 +146,7 @@ public class ScoreboardServer extends Scoreboard {
 
         while (iterator.hasNext()) {
             EntityPlayer entityplayer = (EntityPlayer) iterator.next();
+            if (entityplayer.getBukkitEntity().getScoreboard().getHandle() != this) continue; // CraftBukkit - Only players on this board
             Iterator iterator1 = list.iterator();
 
             while (iterator1.hasNext()) {
@@ -178,6 +179,7 @@ public class ScoreboardServer extends Scoreboard {
 
         while (iterator.hasNext()) {
             EntityPlayer entityplayer = (EntityPlayer) iterator.next();
+            if (entityplayer.getBukkitEntity().getScoreboard().getHandle() != this) continue; // CraftBukkit - Only players on this board
             Iterator iterator1 = list.iterator();
 
             while (iterator1.hasNext()) {
@@ -201,4 +203,14 @@ public class ScoreboardServer extends Scoreboard {
 
         return i;
     }
+
+    // CraftBukkit start - Send to players
+    private void sendAll(Packet packet) {
+        for (EntityPlayer entityplayer : (List<EntityPlayer>) this.a.getPlayerList().players) {
+            if (entityplayer.getBukkitEntity().getScoreboard().getHandle() == this) {
+                entityplayer.playerConnection.sendPacket(packet);
+            }
+        }
+    }
+    // CraftBukkit end
 }
diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
index 7b9c787759..5baed2583c 100644
--- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java
+++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java
@@ -32,8 +32,6 @@ import net.minecraft.server.EnumGamemode;
 import net.minecraft.server.ExceptionWorldConflict;
 import net.minecraft.server.PlayerList;
 import net.minecraft.server.RecipesFurnace;
-import net.minecraft.server.IProgressUpdate;
-import net.minecraft.server.IWorldAccess;
 import net.minecraft.server.Item;
 import net.minecraft.server.MinecraftServer;
 import net.minecraft.server.MobEffectList;
@@ -82,6 +80,7 @@ import org.bukkit.craftbukkit.metadata.PlayerMetadataStore;
 import org.bukkit.craftbukkit.metadata.WorldMetadataStore;
 import org.bukkit.craftbukkit.potion.CraftPotionBrewer;
 import org.bukkit.craftbukkit.scheduler.CraftScheduler;
+import org.bukkit.craftbukkit.scoreboard.CraftScoreboardManager;
 import org.bukkit.craftbukkit.updater.AutoUpdater;
 import org.bukkit.craftbukkit.updater.BukkitDLUpdaterService;
 import org.bukkit.craftbukkit.util.DatFileFilter;
@@ -162,6 +161,7 @@ public final class CraftServer implements Server {
     private File container;
     private WarningState warningState = WarningState.DEFAULT;
     private final BooleanWrapper online = new BooleanWrapper();
+    public CraftScoreboardManager scoreboardManager;
 
     private final class BooleanWrapper {
         private boolean value = true;
@@ -1358,4 +1358,8 @@ public final class CraftServer implements Server {
     public CraftItemFactory getItemFactory() {
         return CraftItemFactory.instance();
     }
+
+    public CraftScoreboardManager getScoreboardManager() {
+        return scoreboardManager;
+    }
 }
diff --git a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
index d37d719a80..a93625c40b 100644
--- a/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
+++ b/src/main/java/org/bukkit/craftbukkit/entity/CraftPlayer.java
@@ -24,7 +24,6 @@ import org.bukkit.*;
 import org.bukkit.Achievement;
 import org.bukkit.Material;
 import org.bukkit.Statistic;
-import org.bukkit.WeatherType;
 import org.bukkit.World;
 import org.bukkit.configuration.serialization.DelegateDeserialization;
 import org.bukkit.conversations.Conversation;
@@ -38,6 +37,7 @@ import org.bukkit.craftbukkit.CraftSound;
 import org.bukkit.craftbukkit.CraftWorld;
 import org.bukkit.craftbukkit.map.CraftMapView;
 import org.bukkit.craftbukkit.map.RenderData;
+import org.bukkit.craftbukkit.scoreboard.CraftScoreboard;
 import org.bukkit.entity.EntityType;
 import org.bukkit.entity.Player;
 import org.bukkit.event.player.PlayerGameModeChangeEvent;
@@ -50,6 +50,7 @@ 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;
 
 @DelegateDeserialization(CraftOfflinePlayer.class)
 public class CraftPlayer extends CraftHumanEntity implements Player {
@@ -420,6 +421,7 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
         server.getHandle().playerFileData.save(getHandle());
     }
 
+    @Deprecated
     public void updateInventory() {
         getHandle().updateInventory(getHandle().activeContainer);
     }
@@ -974,4 +976,13 @@ public class CraftPlayer extends CraftHumanEntity implements Player {
         super.resetMaxHealth();
         getHandle().triggerHealthUpdate();
     }
+
+    public CraftScoreboard getScoreboard() {
+        return this.server.getScoreboardManager().getPlayerBoard(this);
+    }
+
+    public void setScoreboard(Scoreboard scoreboard) {
+        Validate.notNull(scoreboard, "Scoreboard cannot be null");
+        this.server.getScoreboardManager().setPlayerBoard(this, scoreboard);
+    }
 }
diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java
new file mode 100644
index 0000000000..d2e30967d4
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftCriteria.java
@@ -0,0 +1,68 @@
+package org.bukkit.craftbukkit.scoreboard;
+
+import java.util.Map;
+
+import net.minecraft.server.IScoreboardCriteria;
+import net.minecraft.server.ScoreboardObjective;
+
+import com.google.common.collect.ImmutableMap;
+
+final class CraftCriteria {
+    static final Map<String, CraftCriteria> DEFAULTS;
+    static final CraftCriteria DUMMY;
+
+    static {
+        ImmutableMap.Builder<String, CraftCriteria> defaults = ImmutableMap.builder();
+
+        for (Map.Entry<?, ?> entry : ((Map<?,?> ) IScoreboardCriteria.a).entrySet()) {
+            String name = entry.getKey().toString();
+            IScoreboardCriteria criteria = (IScoreboardCriteria) entry.getValue();
+            if (!criteria.getName().equals(name)) {
+                throw new AssertionError("Unexpected entry " + name + " to criteria " + criteria + "(" + criteria.getName() + ")");
+            }
+
+            defaults.put(name, new CraftCriteria(criteria));
+        }
+
+        DEFAULTS = defaults.build();
+        DUMMY = DEFAULTS.get("dummy");
+    }
+
+    final IScoreboardCriteria criteria;
+    final String bukkitName;
+
+    private CraftCriteria(String bukkitName) {
+        this.bukkitName = bukkitName;
+        this.criteria = DUMMY.criteria;
+    }
+
+    private CraftCriteria(IScoreboardCriteria criteria) {
+        this.criteria = criteria;
+        this.bukkitName = criteria.getName();
+    }
+
+    static CraftCriteria getFromNMS(ScoreboardObjective objective) {
+        return DEFAULTS.get(objective.getCriteria().getName());
+    }
+
+    static CraftCriteria getFromBukkit(String name) {
+        final CraftCriteria criteria = DEFAULTS.get(name);
+        if (criteria != null) {
+            return criteria;
+        }
+        return new CraftCriteria(name);
+    }
+
+    @Override
+    public boolean equals(Object that) {
+        if (!(that instanceof CraftCriteria)) {
+            return false;
+        }
+        return ((CraftCriteria) that).bukkitName.equals(this.bukkitName);
+    }
+
+    @Override
+    public int hashCode() {
+        return this.bukkitName.hashCode() ^ CraftCriteria.class.hashCode();
+    }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java
new file mode 100644
index 0000000000..431807a80f
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftObjective.java
@@ -0,0 +1,104 @@
+package org.bukkit.craftbukkit.scoreboard;
+
+import net.minecraft.server.Scoreboard;
+import net.minecraft.server.ScoreboardObjective;
+
+import org.apache.commons.lang.Validate;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.scoreboard.DisplaySlot;
+import org.bukkit.scoreboard.Objective;
+import org.bukkit.scoreboard.Score;
+
+final class CraftObjective extends CraftScoreboardComponent implements Objective {
+    private final ScoreboardObjective objective;
+    private final CraftCriteria criteria;
+
+    CraftObjective(CraftScoreboard scoreboard, ScoreboardObjective objective) {
+        super(scoreboard);
+        this.objective = objective;
+        this.criteria = CraftCriteria.getFromNMS(objective);
+
+        scoreboard.objectives.put(objective.getName(), this);
+    }
+
+    ScoreboardObjective getHandle() {
+        return objective;
+    }
+
+    public String getName() throws IllegalStateException {
+        CraftScoreboard scoreboard = checkState();
+
+        return objective.getName();
+    }
+
+    public String getDisplayName() throws IllegalStateException {
+        CraftScoreboard scoreboard = checkState();
+
+        return objective.getDisplayName();
+    }
+
+    public void setDisplayName(String displayName) throws IllegalStateException, IllegalArgumentException {
+        Validate.notNull(displayName, "Display name cannot be null");
+        Validate.isTrue(displayName.length() <= 32, "Display name '" + displayName + "' is longer than the limit of 32 characters");
+        CraftScoreboard scoreboard = checkState();
+
+        objective.setDisplayName(displayName);
+    }
+
+    public String getCriteria() throws IllegalStateException {
+        CraftScoreboard scoreboard = checkState();
+
+        return criteria.bukkitName;
+    }
+
+    public boolean isModifiable() throws IllegalStateException {
+        CraftScoreboard scoreboard = checkState();
+
+        return !criteria.criteria.isReadOnly();
+    }
+
+    public void setDisplaySlot(DisplaySlot slot) throws IllegalStateException {
+        CraftScoreboard scoreboard = checkState();
+        Scoreboard board = scoreboard.board;
+        ScoreboardObjective objective = this.objective;
+
+        for (int i = 0; i < CraftScoreboardTranslations.MAX_DISPLAY_SLOT; i++) {
+            if (board.getObjectiveForSlot(i) == objective) {
+                board.setDisplaySlot(i, null);
+            }
+        }
+        if (slot != null) {
+            int slotNumber = CraftScoreboardTranslations.fromBukkitSlot(slot);
+            board.setDisplaySlot(slotNumber, getHandle());
+        }
+    }
+
+    public DisplaySlot getDisplaySlot() throws IllegalStateException {
+        CraftScoreboard scoreboard = checkState();
+        Scoreboard board = scoreboard.board;
+        ScoreboardObjective objective = this.objective;
+
+        for (int i = 0; i < CraftScoreboardTranslations.MAX_DISPLAY_SLOT; i++) {
+            if (board.getObjectiveForSlot(i) == objective) {
+                return CraftScoreboardTranslations.toBukkitSlot(i);
+            }
+        }
+        return null;
+    }
+
+    public Score getScore(OfflinePlayer player) throws IllegalArgumentException, IllegalStateException {
+        Validate.notNull(player, "Player cannot be null");
+        CraftScoreboard scoreboard = checkState();
+
+        return new CraftScore(this, player.getName());
+    }
+
+    @Override
+    public void unregister() throws IllegalStateException {
+        CraftScoreboard scoreboard = checkState();
+
+        scoreboard.objectives.remove(this.getName());
+        scoreboard.board.unregisterObjective(objective);
+        setUnregistered();
+    }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java
new file mode 100644
index 0000000000..0ffbe9b08d
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScore.java
@@ -0,0 +1,56 @@
+package org.bukkit.craftbukkit.scoreboard;
+
+import java.util.Map;
+
+import net.minecraft.server.Scoreboard;
+import net.minecraft.server.ScoreboardScore;
+
+import org.bukkit.Bukkit;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.scoreboard.Objective;
+import org.bukkit.scoreboard.Score;
+
+/**
+ * TL;DR: This class is special and lazily grabs a handle...
+ * ...because a handle is a full fledged (I think permanent) hashMap for the associated name.
+ * <p>
+ * Also, as an added perk, a CraftScore will (intentionally) stay a valid reference so long as objective is valid.
+ */
+final class CraftScore implements Score {
+    private final String playerName;
+    private final CraftObjective objective;
+
+    CraftScore(CraftObjective objective, String playerName) {
+        this.objective = objective;
+        this.playerName = playerName;
+    }
+
+    public OfflinePlayer getPlayer() {
+        return Bukkit.getOfflinePlayer(playerName);
+    }
+
+    public Objective getObjective() {
+        return objective;
+    }
+
+    public int getScore() throws IllegalStateException {
+        Scoreboard board = objective.checkState().board;
+
+        if (board.getPlayers().contains(playerName)) { // Lazy
+            Map<String, ScoreboardScore> scores = board.getPlayerObjectives(playerName);
+            ScoreboardScore score = scores.get(objective.getHandle());
+            if (score != null) { // Lazy
+                return score.getScore();
+            }
+        }
+        return 0; // Lazy
+    }
+
+    public void setScore(int score) throws IllegalStateException {
+        objective.checkState().board.getPlayerScoreForObjective(playerName, objective.getHandle()).setScore(score);
+    }
+
+    public CraftScoreboard getScoreboard() {
+        return objective.getScoreboard();
+    }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java
new file mode 100644
index 0000000000..63b8085325
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboard.java
@@ -0,0 +1,135 @@
+package org.bukkit.craftbukkit.scoreboard;
+
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import net.minecraft.server.Scoreboard;
+import net.minecraft.server.ScoreboardObjective;
+import net.minecraft.server.ScoreboardTeam;
+
+import org.apache.commons.lang.Validate;
+import org.bukkit.Bukkit;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.scoreboard.DisplaySlot;
+import org.bukkit.scoreboard.Objective;
+import org.bukkit.scoreboard.Score;
+import org.bukkit.scoreboard.Team;
+
+import com.google.common.collect.ImmutableSet;
+
+public final class CraftScoreboard implements org.bukkit.scoreboard.Scoreboard {
+    final Scoreboard board;
+    final Map<String, CraftObjective> objectives = new HashMap<String, CraftObjective>();
+    final Map<String, CraftTeam> teams = new HashMap<String, CraftTeam>();
+
+    CraftScoreboard(Scoreboard board) {
+        this.board = board;
+
+        for (ScoreboardObjective objective : (Iterable<ScoreboardObjective>) board.getObjectives()) {
+            new CraftObjective(this, objective); // It adds itself to map
+        }
+        for (ScoreboardTeam team : (Iterable<ScoreboardTeam>) board.getTeams()) {
+            new CraftTeam(this, team); // It adds itself to map
+        }
+    }
+
+    public CraftObjective registerNewObjective(String name, String criteria) throws IllegalArgumentException {
+        Validate.notNull(name, "Objective name cannot be null");
+        Validate.notNull(criteria, "Criteria cannot be null");
+        Validate.isTrue(name.length() <= 16, "The name '" + name + "' is longer than the limit of 16 characters");
+        Validate.isTrue(board.getObjective(name) == null, "An objective of name '" + name + "' already exists");
+
+        CraftCriteria craftCriteria = CraftCriteria.getFromBukkit(criteria);
+        ScoreboardObjective objective = board.registerObjective(name, craftCriteria.criteria);
+        return new CraftObjective(this, objective);
+    }
+
+    public Objective getObjective(String name) throws IllegalArgumentException {
+        Validate.notNull(name, "Name cannot be null");
+        return objectives.get(name);
+    }
+
+    public ImmutableSet<Objective> getObjectivesByCriteria(String criteria) throws IllegalArgumentException {
+        Validate.notNull(criteria, "Criteria cannot be null");
+
+        ImmutableSet.Builder<Objective> objectives = ImmutableSet.builder();
+        for (CraftObjective objective : this.objectives.values()) {
+            if (objective.getCriteria().equals(criteria)) {
+                objectives.add(objective);
+            }
+        }
+        return objectives.build();
+    }
+
+    public ImmutableSet<Objective> getObjectives() {
+        return ImmutableSet.copyOf((Collection<? extends Objective>) objectives.values());
+    }
+
+    public Objective getObjective(DisplaySlot slot) throws IllegalArgumentException {
+        Validate.notNull(slot, "Display slot cannot be null");
+        ScoreboardObjective objective = board.getObjectiveForSlot(CraftScoreboardTranslations.fromBukkitSlot(slot));
+        if (objective == null) {
+            return null;
+        }
+        return this.objectives.get(objective.getName());
+    }
+
+    public ImmutableSet<Score> getScores(OfflinePlayer player) throws IllegalArgumentException {
+        Validate.notNull(player, "OfflinePlayer cannot be null");
+
+        ImmutableSet.Builder<Score> scores = ImmutableSet.builder();
+        for (CraftObjective objective : objectives.values()) {
+            scores.add(objective.getScore(player));
+        }
+        return scores.build();
+    }
+
+    public void resetScores(OfflinePlayer player) throws IllegalArgumentException {
+        Validate.notNull(player, "OfflinePlayer cannot be null");
+
+        board.resetPlayerScores(player.getName());
+    }
+
+    public Team getPlayerTeam(OfflinePlayer player) throws IllegalArgumentException {
+        Validate.notNull(player, "OfflinePlayer cannot be null");
+
+        ScoreboardTeam team = board.getTeam(player.getName());
+        return team == null ? null : teams.get(team.getName());
+    }
+
+    public Team getTeam(String teamName) throws IllegalArgumentException {
+        Validate.notNull(teamName, "Team name cannot be null");
+
+        return teams.get(teamName);
+    }
+
+    public ImmutableSet<Team> getTeams() {
+        return ImmutableSet.copyOf((Collection<? extends Team>) teams.values());
+    }
+
+    public Team registerNewTeam(String name) throws IllegalArgumentException {
+        Validate.notNull(name, "Team name cannot be null");
+        Validate.isTrue(name.length() <= 16, "Team name '" + name + "' is longer than the limit of 16 characters");
+        Validate.isTrue(board.getTeam(name) == null, "Team name '" + name + "' is already in use");
+
+        return new CraftTeam(this, board.createTeam(name));
+    }
+
+    public ImmutableSet<OfflinePlayer> getPlayers() {
+        ImmutableSet.Builder<OfflinePlayer> players = ImmutableSet.builder();
+        for (Object playerName : board.getPlayers()) {
+            players.add(Bukkit.getOfflinePlayer(playerName.toString()));
+        }
+        return players.build();
+    }
+
+    public void clearSlot(DisplaySlot slot) throws IllegalArgumentException {
+        Validate.notNull(slot, "Slot cannot be null");
+        board.setDisplaySlot(CraftScoreboardTranslations.fromBukkitSlot(slot), null);
+    }
+
+    // CraftBukkit method
+    public Scoreboard getHandle() {
+        return board;
+    }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardComponent.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardComponent.java
new file mode 100644
index 0000000000..3855a2b79b
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardComponent.java
@@ -0,0 +1,27 @@
+package org.bukkit.craftbukkit.scoreboard;
+
+abstract class CraftScoreboardComponent {
+    private CraftScoreboard scoreboard;
+
+    CraftScoreboardComponent(CraftScoreboard scoreboard) {
+        this.scoreboard = scoreboard;
+    }
+
+    CraftScoreboard checkState() throws IllegalStateException {
+        CraftScoreboard scoreboard = this.scoreboard;
+        if (scoreboard == null) {
+            throw new IllegalStateException("Unregistered scoreboard component");
+        }
+        return scoreboard;
+    }
+
+    public CraftScoreboard getScoreboard() {
+        return scoreboard;
+    }
+
+    abstract void unregister() throws IllegalStateException;
+
+    final void setUnregistered() {
+        scoreboard = null;
+    }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java
new file mode 100644
index 0000000000..c435e3a0ee
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardManager.java
@@ -0,0 +1,118 @@
+package org.bukkit.craftbukkit.scoreboard;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import net.minecraft.server.EntityPlayer;
+import net.minecraft.server.IScoreboardCriteria;
+import net.minecraft.server.MinecraftServer;
+import net.minecraft.server.Packet206SetScoreboardObjective;
+import net.minecraft.server.Packet209SetScoreboardTeam;
+import net.minecraft.server.Scoreboard;
+import net.minecraft.server.ScoreboardObjective;
+import net.minecraft.server.ScoreboardScore;
+import net.minecraft.server.ScoreboardServer;
+import net.minecraft.server.ScoreboardTeam;
+
+import org.apache.commons.lang.Validate;
+import org.bukkit.craftbukkit.entity.CraftPlayer;
+import org.bukkit.craftbukkit.util.WeakCollection;
+import org.bukkit.entity.Player;
+import org.bukkit.scoreboard.ScoreboardManager;
+
+public final class CraftScoreboardManager implements ScoreboardManager {
+    private final CraftScoreboard mainScoreboard;
+    private final MinecraftServer server;
+    private final Collection<CraftScoreboard> scoreboards = new WeakCollection<CraftScoreboard>();
+    private final Map<CraftPlayer, CraftScoreboard> playerBoards = new HashMap<CraftPlayer, CraftScoreboard>();
+
+    public CraftScoreboardManager(MinecraftServer minecraftserver, net.minecraft.server.Scoreboard scoreboardServer) {
+        mainScoreboard = new CraftScoreboard(scoreboardServer);
+        server = minecraftserver;
+        scoreboards.add(mainScoreboard);
+    }
+
+    public CraftScoreboard getMainScoreboard() {
+        return mainScoreboard;
+    }
+
+    public CraftScoreboard getNewScoreboard() {
+        CraftScoreboard scoreboard = new CraftScoreboard(new ScoreboardServer(server));
+        scoreboards.add(scoreboard);
+        return scoreboard;
+    }
+
+    // CraftBukkit method
+    public CraftScoreboard getPlayerBoard(CraftPlayer player) {
+        CraftScoreboard board = playerBoards.get(player);
+        return (CraftScoreboard) (board == null ? getMainScoreboard() : board);
+    }
+
+    // CraftBukkit method
+    public void setPlayerBoard(CraftPlayer player, org.bukkit.scoreboard.Scoreboard bukkitScoreboard) throws IllegalArgumentException {
+        Validate.isTrue(bukkitScoreboard instanceof CraftScoreboard, "Cannot set player scoreboard to an unregistered Scoreboard");
+
+        CraftScoreboard scoreboard = (CraftScoreboard) bukkitScoreboard;
+        net.minecraft.server.Scoreboard oldboard = getPlayerBoard(player).getHandle();
+        net.minecraft.server.Scoreboard newboard = scoreboard.getHandle();
+        EntityPlayer entityplayer = player.getHandle();
+
+        if (oldboard == newboard) {
+            return;
+        }
+
+        if (scoreboard == mainScoreboard) {
+            playerBoards.remove(player);
+        } else {
+            playerBoards.put(player, (CraftScoreboard) scoreboard);
+        }
+
+        // Old objective tracking
+        HashSet<ScoreboardObjective> removed = new HashSet<ScoreboardObjective>();
+        for (int i = 0; i < 3; ++i) {
+            ScoreboardObjective scoreboardobjective = oldboard.getObjectiveForSlot(i);
+            if (scoreboardobjective != null && !removed.contains(scoreboardobjective)) {
+                entityplayer.playerConnection.sendPacket(new Packet206SetScoreboardObjective(scoreboardobjective, 1));
+                removed.add(scoreboardobjective);
+            }
+        }
+
+        // Old team tracking
+        Iterator<?> iterator = oldboard.getTeams().iterator();
+        while (iterator.hasNext()) {
+            ScoreboardTeam scoreboardteam = (ScoreboardTeam) iterator.next();
+            entityplayer.playerConnection.sendPacket(new Packet209SetScoreboardTeam(scoreboardteam, 1));
+        }
+
+        // The above is the reverse of the below method.
+        server.getPlayerList().a((ScoreboardServer) newboard, player.getHandle());
+    }
+
+    // CraftBukkit method
+    public void removePlayer(Player player) {
+        playerBoards.remove(player);
+    }
+
+    // CraftBukkit method
+    public Collection<ScoreboardScore> getScoreboardScores(IScoreboardCriteria criteria, String name, Collection<ScoreboardScore> collection) {
+        for (CraftScoreboard scoreboard : scoreboards) {
+            Scoreboard board = scoreboard.board;
+            for (ScoreboardObjective objective : (Iterable<ScoreboardObjective>) board.getObjectivesForCriteria(criteria)) {
+                collection.add(board.getPlayerScoreForObjective(name, objective));
+            }
+        }
+        return collection;
+    }
+
+    // CraftBukkit method
+    public void updateAllScoresForList(IScoreboardCriteria criteria, String name, List<EntityPlayer> of) {
+        for (ScoreboardScore score : getScoreboardScores(criteria, name, new ArrayList<ScoreboardScore>())) {
+            score.updateForList(of);
+        }
+    }
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardTranslations.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardTranslations.java
new file mode 100644
index 0000000000..d08e5a281e
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftScoreboardTranslations.java
@@ -0,0 +1,26 @@
+package org.bukkit.craftbukkit.scoreboard;
+
+import net.minecraft.server.Scoreboard;
+
+import org.bukkit.scoreboard.DisplaySlot;
+
+import com.google.common.collect.ImmutableBiMap;
+
+class CraftScoreboardTranslations {
+    static final int MAX_DISPLAY_SLOT = 3;
+    static ImmutableBiMap<DisplaySlot, String> SLOTS = ImmutableBiMap.of(
+            DisplaySlot.BELOW_NAME, "belowName",
+            DisplaySlot.PLAYER_LIST, "list",
+            DisplaySlot.SIDEBAR, "sidebar");
+
+    private CraftScoreboardTranslations() {}
+
+    static DisplaySlot toBukkitSlot(int i) {
+        return SLOTS.inverse().get(Scoreboard.getSlotName(i));
+    }
+
+    static int fromBukkitSlot(DisplaySlot slot) {
+        return Scoreboard.getSlotForName(SLOTS.get(slot));
+    }
+
+}
diff --git a/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java
new file mode 100644
index 0000000000..03a3207828
--- /dev/null
+++ b/src/main/java/org/bukkit/craftbukkit/scoreboard/CraftTeam.java
@@ -0,0 +1,145 @@
+package org.bukkit.craftbukkit.scoreboard;
+
+import java.util.Set;
+
+import org.apache.commons.lang.Validate;
+import org.bukkit.Bukkit;
+import org.bukkit.OfflinePlayer;
+import org.bukkit.scoreboard.Team;
+
+import com.google.common.collect.ImmutableSet;
+
+import net.minecraft.server.ScoreboardTeam;
+
+final class CraftTeam extends CraftScoreboardComponent implements Team {
+    private final ScoreboardTeam team;
+
+    CraftTeam(CraftScoreboard scoreboard, ScoreboardTeam team) {
+        super(scoreboard);
+        this.team = team;
+        scoreboard.teams.put(team.getName(), this);
+    }
+
+    public String getName() throws IllegalStateException {
+        CraftScoreboard scoreboard = checkState();
+
+        return team.getName();
+    }
+
+    public String getDisplayName() throws IllegalStateException {
+        CraftScoreboard scoreboard = checkState();
+
+        return team.getDisplayName();
+    }
+
+    public void setDisplayName(String displayName) throws IllegalStateException {
+        Validate.notNull(displayName, "Display name cannot be null");
+        Validate.isTrue(displayName.length() <= 32, "Display name '" + displayName + "' is longer than the limit of 32 characters");
+        CraftScoreboard scoreboard = checkState();
+
+        team.setDisplayName(displayName);
+    }
+
+    public String getPrefix() throws IllegalStateException {
+        CraftScoreboard scoreboard = checkState();
+
+        return team.getPrefix();
+    }
+
+    public void setPrefix(String prefix) throws IllegalStateException, IllegalArgumentException {
+        Validate.notNull(prefix, "Prefix cannot be null");
+        Validate.isTrue(prefix.length() <= 32, "Prefix '" + prefix + "' is longer than the limit of 32 characters");
+        CraftScoreboard scoreboard = checkState();
+
+        team.setPrefix(prefix);
+    }
+
+    public String getSuffix() throws IllegalStateException {
+        CraftScoreboard scoreboard = checkState();
+
+        return team.getSuffix();
+    }
+
+    public void setSuffix(String suffix) throws IllegalStateException, IllegalArgumentException {
+        Validate.notNull(suffix, "Suffix cannot be null");
+        Validate.isTrue(suffix.length() <= 32, "Suffix '" + suffix + "' is longer than the limit of 32 characters");
+        CraftScoreboard scoreboard = checkState();
+
+        team.setSuffix(suffix);
+    }
+
+    public boolean allowFriendlyFire() throws IllegalStateException {
+        CraftScoreboard scoreboard = checkState();
+
+        return team.allowFriendlyFire();
+    }
+
+    public void setAllowFriendlyFire(boolean enabled) throws IllegalStateException {
+        CraftScoreboard scoreboard = checkState();
+
+        team.setAllowFriendlyFire(enabled);
+    }
+
+    public boolean canSeeFriendlyInvisibles() throws IllegalStateException {
+        CraftScoreboard scoreboard = checkState();
+
+        return team.canSeeFriendlyInvisibles();
+    }
+
+    public void setCanSeeFriendlyInvisibles(boolean enabled) throws IllegalStateException {
+        CraftScoreboard scoreboard = checkState();
+
+        team.setCanSeeFriendlyInvisibles(enabled);
+    }
+
+    public Set<OfflinePlayer> getPlayers() throws IllegalStateException {
+        CraftScoreboard scoreboard = checkState();
+
+        ImmutableSet.Builder<OfflinePlayer> players = ImmutableSet.builder();
+        for (Object o : team.getPlayerNameSet()) {
+            players.add(Bukkit.getOfflinePlayer(o.toString()));
+        }
+        return players.build();
+    }
+
+    public int getSize() throws IllegalStateException {
+        CraftScoreboard scoreboard = checkState();
+
+        return team.getPlayerNameSet().size();
+    }
+
+    public void addPlayer(OfflinePlayer player) throws IllegalStateException, IllegalArgumentException {
+        Validate.notNull(player, "OfflinePlayer cannot be null");
+        CraftScoreboard scoreboard = checkState();
+
+        scoreboard.board.addPlayerToTeam(player.getName(), team);
+    }
+
+    public boolean removePlayer(OfflinePlayer player) throws IllegalStateException, IllegalArgumentException {
+        Validate.notNull(player, "OfflinePlayer cannot be null");
+        CraftScoreboard scoreboard = checkState();
+
+        if (!team.getPlayerNameSet().contains(player.getName())) {
+            return false;
+        }
+
+        scoreboard.board.removePlayerFromTeam(player.getName(), team);
+        return true;
+    }
+
+    public boolean hasPlayer(OfflinePlayer player) throws IllegalArgumentException, IllegalStateException {
+        Validate.notNull(player, "OfflinePlayer cannot be null");
+        CraftScoreboard scoreboard = checkState();
+
+        return team.getPlayerNameSet().contains(player.getName());
+    }
+
+    @Override
+    public void unregister() throws IllegalStateException {
+        CraftScoreboard scoreboard = checkState();
+
+        scoreboard.board.removeTeam(team);
+        scoreboard.teams.remove(team.getName());
+        setUnregistered();
+    }
+}