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 collection = this.world.getServer().getScoreboardManager().getScoreboardScores(IScoreboardCriteria.e, this.getLocalizedName(), new java.util.ArrayList()); 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 collection = this.world.getServer().getScoreboardManager().getScoreboardScores(IScoreboardCriteria.c, this.getLocalizedName(), new java.util.ArrayList()); 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) 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 DEFAULTS; + static final CraftCriteria DUMMY; + + static { + ImmutableMap.Builder 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. + *

+ * 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 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 objectives = new HashMap(); + final Map teams = new HashMap(); + + CraftScoreboard(Scoreboard board) { + this.board = board; + + for (ScoreboardObjective objective : (Iterable) board.getObjectives()) { + new CraftObjective(this, objective); // It adds itself to map + } + for (ScoreboardTeam team : (Iterable) 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 getObjectivesByCriteria(String criteria) throws IllegalArgumentException { + Validate.notNull(criteria, "Criteria cannot be null"); + + ImmutableSet.Builder objectives = ImmutableSet.builder(); + for (CraftObjective objective : this.objectives.values()) { + if (objective.getCriteria().equals(criteria)) { + objectives.add(objective); + } + } + return objectives.build(); + } + + public ImmutableSet getObjectives() { + return ImmutableSet.copyOf((Collection) 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 getScores(OfflinePlayer player) throws IllegalArgumentException { + Validate.notNull(player, "OfflinePlayer cannot be null"); + + ImmutableSet.Builder 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 getTeams() { + return ImmutableSet.copyOf((Collection) 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 getPlayers() { + ImmutableSet.Builder 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 scoreboards = new WeakCollection(); + private final Map playerBoards = new HashMap(); + + 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 removed = new HashSet(); + 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 getScoreboardScores(IScoreboardCriteria criteria, String name, Collection collection) { + for (CraftScoreboard scoreboard : scoreboards) { + Scoreboard board = scoreboard.board; + for (ScoreboardObjective objective : (Iterable) board.getObjectivesForCriteria(criteria)) { + collection.add(board.getPlayerScoreForObjective(name, objective)); + } + } + return collection; + } + + // CraftBukkit method + public void updateAllScoresForList(IScoreboardCriteria criteria, String name, List of) { + for (ScoreboardScore score : getScoreboardScores(criteria, name, new ArrayList())) { + 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 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 getPlayers() throws IllegalStateException { + CraftScoreboard scoreboard = checkState(); + + ImmutableSet.Builder 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(); + } +}