diff --git a/src/com/massivecraft/massivecore/Engine.java b/src/com/massivecraft/massivecore/Engine.java index b5cdd7d8..873013b8 100644 --- a/src/com/massivecraft/massivecore/Engine.java +++ b/src/com/massivecraft/massivecore/Engine.java @@ -1,5 +1,7 @@ package com.massivecraft.massivecore; +import java.util.Set; + import org.bukkit.Bukkit; import org.bukkit.event.Event; import org.bukkit.event.HandlerList; @@ -8,10 +10,19 @@ import org.bukkit.event.block.BlockMultiPlaceEvent; import org.bukkit.event.block.BlockPlaceEvent; import org.bukkit.scheduler.BukkitTask; +import com.massivecraft.massivecore.collections.MassiveSet; import com.massivecraft.massivecore.predicate.PredicateStartsWithIgnoreCase; public abstract class Engine implements Active, Listener, Runnable { + // -------------------------------------------- // + // REGISTRY + // -------------------------------------------- // + + private static final transient Set allInstances = new MassiveSet<>(); + public static Set getAllInstances() { return allInstances; } + + // -------------------------------------------- // // PLUGIN // -------------------------------------------- // @@ -56,12 +67,10 @@ public abstract class Engine implements Active, Listener, Runnable // ACTIVE // -------------------------------------------- // - private boolean active = false; - @Override public boolean isActive() { - return this.active; + return getAllInstances().contains(this); } @Override @@ -70,7 +79,14 @@ public abstract class Engine implements Active, Listener, Runnable this.setActiveListener(active); this.setActiveTask(active); this.setActiveInner(active); - this.active = active; + if (active) + { + getAllInstances().add(this); + } + else + { + getAllInstances().remove(this); + } } @Override diff --git a/src/com/massivecraft/massivecore/MassiveCore.java b/src/com/massivecraft/massivecore/MassiveCore.java index e0e603ea..b9799fd3 100644 --- a/src/com/massivecraft/massivecore/MassiveCore.java +++ b/src/com/massivecraft/massivecore/MassiveCore.java @@ -88,6 +88,7 @@ import com.massivecraft.massivecore.ps.PS; import com.massivecraft.massivecore.ps.PSAdapter; import com.massivecraft.massivecore.store.ModificationPollerLocal; import com.massivecraft.massivecore.store.ModificationPollerRemote; +import com.massivecraft.massivecore.util.BoardUtil; import com.massivecraft.massivecore.util.IdUtil; import com.massivecraft.massivecore.util.MUtil; import com.massivecraft.massivecore.util.PlayerUtil; @@ -263,6 +264,7 @@ public class MassiveCore extends MassivePlugin // Util PlayerUtil.class, + BoardUtil.class, // Integration IntegrationVault.class, diff --git a/src/com/massivecraft/massivecore/MassivePlugin.java b/src/com/massivecraft/massivecore/MassivePlugin.java index 1c14209e..ec293a4c 100644 --- a/src/com/massivecraft/massivecore/MassivePlugin.java +++ b/src/com/massivecraft/massivecore/MassivePlugin.java @@ -1,6 +1,9 @@ package com.massivecraft.massivecore; import java.io.IOException; +import java.util.Collection; +import java.util.Collections; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; @@ -10,6 +13,7 @@ import org.bukkit.command.ConsoleCommandSender; import org.bukkit.event.Listener; import org.bukkit.plugin.java.JavaPlugin; +import com.massivecraft.massivecore.collections.MassiveList; import com.massivecraft.massivecore.command.MassiveCommand; import com.massivecraft.massivecore.store.Coll; import com.massivecraft.massivecore.util.ReflectionUtil; @@ -140,14 +144,13 @@ public abstract class MassivePlugin extends JavaPlugin implements Listener, Name public void onDisable() { // Commands - MassiveCommand.unregister(this); + this.deactivate(MassiveCommand.getAllInstances()); + + // Engines + this.deactivate(Engine.getAllInstances()); // Collections - for (Coll coll : Coll.getInstances()) - { - if (coll.getPlugin() != this) continue; - coll.setActive(false); - } + this.deactivate(Coll.getInstances()); log("Disabled"); } @@ -202,6 +205,28 @@ public abstract class MassivePlugin extends JavaPlugin implements Listener, Name throw new IllegalArgumentException("Neither Active nor Class: " + object); } + private void deactivate(Collection actives) + { + // Fail Fast + if (actives == null) throw new NullPointerException("actives"); + + // Clone to Avoid CME + List all = new MassiveList<>(actives); + + // Reverse to Disable Reversely + Collections.reverse(all); + + // Loop + for (Active active : all) + { + // Check + if ( ! this.equals(active.getActivePlugin())) continue; + + // Deactivate + active.setActive(false); + } + } + // -------------------------------------------- // // LOGGING // -------------------------------------------- // diff --git a/src/com/massivecraft/massivecore/command/MassiveCommand.java b/src/com/massivecraft/massivecore/command/MassiveCommand.java index 0076c6a3..964bcccb 100644 --- a/src/com/massivecraft/massivecore/command/MassiveCommand.java +++ b/src/com/massivecraft/massivecore/command/MassiveCommand.java @@ -5,7 +5,6 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Iterator; -import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; @@ -24,6 +23,7 @@ import com.massivecraft.massivecore.MassiveException; import com.massivecraft.massivecore.MassivePlugin; import com.massivecraft.massivecore.collections.MassiveList; import com.massivecraft.massivecore.collections.MassiveMap; +import com.massivecraft.massivecore.collections.MassiveSet; import com.massivecraft.massivecore.command.requirement.Requirement; import com.massivecraft.massivecore.command.requirement.RequirementAbstract; import com.massivecraft.massivecore.command.requirement.RequirementHasPerm; @@ -52,22 +52,8 @@ public class MassiveCommand implements Active, PluginIdentifiableCommand // This task unregisters /all/ registered MCommands and then register them all again. // When registering again we use the fresh and current aliases. - // STATIC - private static final transient Map registry = new LinkedHashMap(); - public static Map getRegistry() { return registry; } - public static Set getRegisteredCommands() { return registry.keySet(); } - public static void unregister(Plugin plugin) - { - Iterator> iter = registry.entrySet().iterator(); - while (iter.hasNext()) - { - Entry entry = iter.next(); - if (plugin.equals(entry.getValue())) - { - iter.remove(); - } - } - } + private static final transient Set allInstances = new MassiveSet<>(); + public static Set getAllInstances() { return allInstances; } // -------------------------------------------- // // ACTIVE @@ -76,32 +62,36 @@ public class MassiveCommand implements Active, PluginIdentifiableCommand @Override public boolean isActive() { - return registry.containsKey(this); + return getAllInstances().contains(this); } @Override public void setActive(boolean active) { - // NOTE: Not Implemented + if (active) + { + getAllInstances().add(this); + } + else + { + getAllInstances().remove(this); + } } + private MassivePlugin activePlugin = null; + @Override public MassivePlugin setActivePlugin(MassivePlugin activePlugin) { - if (activePlugin == null) - { - return registry.remove(this); - } - else - { - return registry.put(this, activePlugin); - } + MassivePlugin ret = this.activePlugin; + this.activePlugin = activePlugin; + return ret; } @Override public MassivePlugin getActivePlugin() { - return registry.get(this); + return this.activePlugin; } @Override diff --git a/src/com/massivecraft/massivecore/engine/EngineMassiveCoreCommandRegistration.java b/src/com/massivecraft/massivecore/engine/EngineMassiveCoreCommandRegistration.java index 158535e9..3bb28f1f 100644 --- a/src/com/massivecraft/massivecore/engine/EngineMassiveCoreCommandRegistration.java +++ b/src/com/massivecraft/massivecore/engine/EngineMassiveCoreCommandRegistration.java @@ -53,7 +53,7 @@ public class EngineMassiveCoreCommandRegistration extends Engine // Step #2: Create a "name --> target" map that contains the MassiveCommands that /should/ be registered in Bukkit. Map nameTargets = new HashMap(); // For each MassiveCommand that is supposed to be registered ... - for (MassiveCommand massiveCommand : MassiveCommand.getRegisteredCommands()) + for (MassiveCommand massiveCommand : MassiveCommand.getAllInstances()) { // ... and for each of it's aliases ... for (String alias : massiveCommand.getAliases()) diff --git a/src/com/massivecraft/massivecore/nms/NmsTeamColor.java b/src/com/massivecraft/massivecore/nms/NmsTeamColor.java new file mode 100644 index 00000000..8683ee12 --- /dev/null +++ b/src/com/massivecraft/massivecore/nms/NmsTeamColor.java @@ -0,0 +1,170 @@ +package com.massivecraft.massivecore.nms; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +import org.bukkit.ChatColor; +import org.bukkit.scoreboard.Team; + +import com.google.common.collect.BiMap; +import com.google.common.collect.ImmutableBiMap; +import com.massivecraft.massivecore.particleeffect.ReflectionUtils.PackageType; +import com.massivecraft.massivecore.util.ReflectionUtil; + +public class NmsTeamColor extends NmsAbstract +{ + // -------------------------------------------- // + // INSTANCE & CONSTRUCT + // -------------------------------------------- // + + private static NmsTeamColor i = new NmsTeamColor(); + public static NmsTeamColor get () { return i; } + + // -------------------------------------------- // + // FIELDS + // -------------------------------------------- // + + // org.bukkit.craftbukkit.scoreboard.CraftTeam + private Class classCraftTeam; + + // org.bukkit.craftbukkit.scoreboard.CraftTeam#team + private Field fieldCraftTeamHandle; + + // net.minecraft.server.ScoreboardTeam + private Class classNmsTeam; + + // net.minecraft.server.ScoreboardTeam#k <--- color + private Field fieldNmsTeamColor; + + // net.minecraft.server.EnumChatFormat + private Class classNmsColor; + + // net.minecraft.server.EnumChatFormat#C <-- code + private Field fieldNmsColorCode; + + // net.minecraft.server.EnumChatFormat.a(int i) <-- for code + private Method methodNmsColorFor; + + // -------------------------------------------- // + // OVERRIDE + // -------------------------------------------- // + + @Override + public int getRequiredVersion() + { + return 9; + } + + @Override + protected void setup() throws Throwable + { + this.classCraftTeam = PackageType.CRAFTBUKKIT_SCOREBOARD.getClass("CraftTeam"); + this.fieldCraftTeamHandle = ReflectionUtil.getField(this.classCraftTeam, "team"); + + this.classNmsTeam = PackageType.MINECRAFT_SERVER.getClass("ScoreboardTeam"); + this.fieldNmsTeamColor = ReflectionUtil.getField(this.classNmsTeam, "k"); + + this.classNmsColor = PackageType.MINECRAFT_SERVER.getClass("EnumChatFormat"); + this.fieldNmsColorCode = ReflectionUtil.getField(this.classNmsColor, "C"); + this.methodNmsColorFor = ReflectionUtil.getMethod(this.classNmsColor, "a", int.class); + } + + // -------------------------------------------- // + // ACCESS + // -------------------------------------------- // + + public ChatColor get(Team team) + { + if ( ! this.isAvailable()) return null; + + Object nmsTeam = convertTeam(team); + Object nmsColor = ReflectionUtil.getField(this.fieldNmsTeamColor, nmsTeam); + + return convertColor(nmsColor); + } + + public void set(Team team, ChatColor color) + { + if ( ! this.isAvailable()) return; + + Object nmsTeam = convertTeam(team); + Object nmsColor = convertColor(color); + ReflectionUtil.setField(this.fieldNmsTeamColor, nmsTeam, nmsColor); + + // This is a quick and dirty solution. + // It makes sure the scoreboard is updated. + team.setDisplayName(team.getDisplayName()); + } + + // -------------------------------------------- // + // CONVERT TEAM + // -------------------------------------------- // + + private Object convertTeam(Team team) + { + return ReflectionUtil.getField(this.fieldCraftTeamHandle, team); + } + + // -------------------------------------------- // + // CONVERT COLOR + // -------------------------------------------- // + + private ChatColor convertColor(Object nms) + { + if (nms == null) return null; + int code = ReflectionUtil.getField(this.fieldNmsColorCode, nms); + return code(code); + } + + private Object convertColor(ChatColor bukkit) + { + if (bukkit == null) return null; + int code = code(bukkit); + return ReflectionUtil.invokeMethod(this.methodNmsColorFor, null, code); + } + + // -------------------------------------------- // + // CODE + // -------------------------------------------- // + + private static ChatColor code(int code) + { + ChatColor ret = COLOR_TO_CODE.inverse().get(code); + if (ret == null) throw new IllegalArgumentException("Unsupported Code " + code); + return ret; + } + + private static int code(ChatColor color) + { + Integer ret = COLOR_TO_CODE.get(color); + if (ret == null) throw new IllegalArgumentException("Unsupported Color " + color); + return ret; + } + + private static final BiMap COLOR_TO_CODE = ImmutableBiMap.builder() + .put(ChatColor.BLACK, 0) + .put(ChatColor.DARK_BLUE, 1) + .put(ChatColor.DARK_GREEN, 2) + .put(ChatColor.DARK_AQUA, 3) + .put(ChatColor.DARK_RED, 4) + .put(ChatColor.DARK_PURPLE, 5) + .put(ChatColor.GOLD, 6) + .put(ChatColor.GRAY, 7) + .put(ChatColor.DARK_GRAY, 8) + .put(ChatColor.BLUE, 9) + .put(ChatColor.GREEN, 10) + .put(ChatColor.AQUA, 11) + .put(ChatColor.RED, 12) + .put(ChatColor.LIGHT_PURPLE, 13) + .put(ChatColor.YELLOW, 14) + .put(ChatColor.WHITE, 15) + // The only supported format is RESET. + // .put(ChatColor.MAGIC, ???) + // .put(ChatColor.BOLD, ???) + // .put(ChatColor.STRIKETHROUGH, ???) + // .put(ChatColor.UNDERLINE, ???) + // .put(ChatColor.ITALIC, ???) + .put(ChatColor.RESET, -1) + .build(); + +} diff --git a/src/com/massivecraft/massivecore/store/Coll.java b/src/com/massivecraft/massivecore/store/Coll.java index e38931f5..72277d2d 100644 --- a/src/com/massivecraft/massivecore/store/Coll.java +++ b/src/com/massivecraft/massivecore/store/Coll.java @@ -982,17 +982,9 @@ public class Coll> extends CollAbstract // -------------------------------------------- // @Override - public MassivePlugin setActivePlugin(MassivePlugin plugin) + public boolean isActive() { - MassivePlugin ret = this.plugin; - this.plugin = plugin; - return ret; - } - - @Override - public MassivePlugin getActivePlugin() - { - return this.plugin; + return name2instance.containsKey(this.getName()); } @Override @@ -1029,9 +1021,17 @@ public class Coll> extends CollAbstract } @Override - public boolean isActive() + public MassivePlugin getActivePlugin() { - return name2instance.containsKey(this.getName()); + return this.plugin; + } + + @Override + public MassivePlugin setActivePlugin(MassivePlugin plugin) + { + MassivePlugin ret = this.plugin; + this.plugin = plugin; + return ret; } @Override diff --git a/src/com/massivecraft/massivecore/util/BoardUtil.java b/src/com/massivecraft/massivecore/util/BoardUtil.java new file mode 100644 index 00000000..7d076251 --- /dev/null +++ b/src/com/massivecraft/massivecore/util/BoardUtil.java @@ -0,0 +1,901 @@ +package com.massivecraft.massivecore.util; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; + +import org.bukkit.Bukkit; +import org.bukkit.ChatColor; +import org.bukkit.entity.Entity; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.EventPriority; +import org.bukkit.event.player.PlayerQuitEvent; +import org.bukkit.scoreboard.DisplaySlot; +import org.bukkit.scoreboard.Objective; +import org.bukkit.scoreboard.Score; +import org.bukkit.scoreboard.Scoreboard; +import org.bukkit.scoreboard.Team; +import org.bukkit.scoreboard.Team.Option; +import org.bukkit.scoreboard.Team.OptionStatus; + +import com.massivecraft.massivecore.Engine; +import com.massivecraft.massivecore.collections.MassiveList; +import com.massivecraft.massivecore.collections.MassiveMap; +import com.massivecraft.massivecore.collections.MassiveSet; +import com.massivecraft.massivecore.nms.NmsTeamColor; + +// # RESEARCH > CLEANUP +// The main server scoreboard is the only one that is saved to NBT. +// We must make sure to clean up after ourselves if we use that one. +// The other scoreboards are just temporary. +// For this reason there is a value to avoiding using the main scoreboard at all. +// However so long as we clean up after ourselves properly there is a simplicity to using all known scoreboards. +// +// # RESEARCH > DEFAULT TEAM +// Per default players have no team. +// To disable collisions we must set a team flag. +// This means some sort of default team creation can be useful. +// For this we use so called personal teams with only one member. +// +// # TERMIOLOGY +// Board: the "score board" +// Objective: the score board "objective" +// Id: the unchangeable "name" +// Name: the changeable "display name" +// Slot: the "display slot" +// Entries: Map from key to value +// Key: the player name or stringified entity uuid +// Value: the integer objective score value +// Team: the score board team +// Members: the score board team members. These are of Key type. +// +// # DESIGN +// NoChange: Do not trigger network packets in vain through detecting "same setting". +public class BoardUtil extends Engine +{ + // -------------------------------------------- // + // INSTANCE & CONSTRUCT + // -------------------------------------------- // + + private static BoardUtil i = new BoardUtil(); + public static BoardUtil get() { return i; } + public BoardUtil() + { + this.setPeriod(1L); + } + + // -------------------------------------------- // + // DATA + // -------------------------------------------- // + + // All online players at the beginning of the tick. + private static Collection players; + public static Collection getPlayers() { return players; } + + // The boards based off the players above. + private static Set boards; + public static Set getBoards() { return boards; } + + // Ensure things, possibly strictly. + + private static boolean ensureBoardEnabled = false; + public static boolean isEnsureBoardEnabled() { return ensureBoardEnabled; } + public static void setEnsureBoardEnabled() { ensureBoardEnabled = true; } + + private static boolean ensureBoardStrict = false; + public static boolean isEnsureBoardStrict() { return ensureBoardStrict; } + public static void setEnsureBoardStrict() { ensureBoardStrict = true; } + + private static boolean ensureTeamEnabled = false; + public static boolean isEnsureTeamEnabled() { return ensureTeamEnabled; } + public static void setEnsureTeamEnabled() { ensureTeamEnabled = true; } + + private static boolean ensureTeamStrict = false; + public static boolean isEnsureTeamStrict() { return ensureTeamStrict; } + public static void setEnsureTeamStrict() { ensureTeamStrict = true; } + + // Temporary Fake Fields + public static Set temporaryObjectives = new MassiveSet<>(); + public static Set getTemporaryObjectives() { return temporaryObjectives; } + + public static Set temporaryTeams = new MassiveSet<>(); + public static Set getTemporaryTeams() { return temporaryTeams; } + + // -------------------------------------------- // + // UPDATE + // -------------------------------------------- // + + @Override + public void setActiveInner(boolean active) + { + if (active) + { + // We do not trigger an update here. + // We must wait for the first server tick. + // Otherwise the Scoreboard manager is null. + } + else + { + // We delete everything marked as temporary on deactivation. + + List objectives = new MassiveList<>(getTemporaryObjectives()); + for (Objective objective : objectives) + { + deleteObjective(objective); + } + + List teams = new MassiveList<>(getTemporaryTeams()); + for (Team team : teams) + { + deleteTeam(team); + } + } + } + + @Override + public void run() + { + update(); + } + + public static void update() + { + updatePlayers(); + updateBoards(); + updateEnsure(); + } + + public static void updatePlayers() + { + // Create + Collection players = MUtil.getOnlinePlayers(); + players = Collections.unmodifiableCollection(players); + + // Set + BoardUtil.players = players; + } + + public static void updateBoards() + { + // Create + Set boards = new MassiveSet<>(); + + // Fill > Simple + boards.add(getBoardMain()); + boards.add(getBoardOur()); + + // Fill > Players + for (Player player : getPlayers()) + { + Scoreboard board = getBoard(player); + boards.add(board); + } + + // Set + boards = Collections.unmodifiableSet(boards); + BoardUtil.boards = boards; + } + + public static void updateEnsure() + { + for (Player player : getPlayers()) + { + if (isEnsureBoardEnabled()) + { + ensureBoard(player, isEnsureBoardStrict()); + } + + if (isEnsureTeamEnabled()) + { + for (Scoreboard board : getBoards()) + { + ensureTeam(board, player, isEnsureTeamStrict()); + } + } + } + } + + // -------------------------------------------- // + // ENSURE + // -------------------------------------------- // + + public static Scoreboard ensureBoard(Player player, boolean strict) + { + Scoreboard board = getBoard(player); + + if (isBoardOur(board)) return board; + if ( ! strict && ! isBoardMain(board)) return board; + + board = getBoardOur(); + setBoard(player, board); + + return board; + } + + public static Team ensureTeam(Scoreboard board, Player player, boolean strict) + { + Team team = getKeyTeam(board, player); + + if (isPersonalTeam(team, player)) return team; + if ( ! strict && team != null) return team; + + team = getPersonalTeam(board, player, true); + return team; + } + + // -------------------------------------------- // + // CLEAN + // -------------------------------------------- // + + public static void clean(Player player) + { + // Delete scores for temporary objectives. + for (Objective objective : getTemporaryObjectives()) + { + setObjectiveValue(objective, player, 0); + } + + // Delete player team if temporary and sole player. + for (Scoreboard board : getBoards()) + { + Team team = getKeyTeam(board, player); + if (isTeamPersistent(team)) continue; + if (getTeamMembers(team).size() > 1) continue; + deleteTeam(team); + } + } + + @EventHandler(priority = EventPriority.MONITOR) + public void clean(final PlayerQuitEvent event) + { + Bukkit.getScheduler().runTask(this.getPlugin(), new Runnable() + { + @Override + public void run() + { + clean(event.getPlayer()); + } + }); + } + + // -------------------------------------------- // + // KEY + // -------------------------------------------- // + + public static String getKey(Object key) + { + if (key == null) return null; + if (key instanceof String) return (String)key; + if (key instanceof Player) return ((Player)key).getName(); + if (key instanceof Entity) return ((Entity)key).getUniqueId().toString(); + throw new IllegalArgumentException(key.toString()); + } + + // -------------------------------------------- // + // BOARD + // -------------------------------------------- // + + public static Scoreboard getBoard(Player player) + { + return player.getScoreboard(); + } + + public static void setBoard(Player player, Scoreboard board) + { + player.setScoreboard(board); + } + + // -------------------------------------------- // + // BOARD > MAIN + // -------------------------------------------- // + + public static Scoreboard getBoardMain() + { + return Bukkit.getScoreboardManager().getMainScoreboard(); + } + + public static boolean isBoardMain(Scoreboard board) + { + return getBoardMain().equals(board); + } + + // -------------------------------------------- // + // BOARD > OUR + // -------------------------------------------- // + + private static Scoreboard BOARD_OUR = null; + + public static Scoreboard getBoardOur() + { + if (BOARD_OUR == null) BOARD_OUR = Bukkit.getScoreboardManager().getNewScoreboard(); + return BOARD_OUR; + } + + public static boolean isBoardOur(Scoreboard board) + { + return getBoardOur().equals(board); + } + + // -------------------------------------------- // + // OBJECTIVE + // -------------------------------------------- // + + // Note that "dummy" actually seems to be the right word with a certain meaning to the vanilla server. + // http://minecraft.gamepedia.com/Scoreboard + public static final String OBJECTIVE_CRITERIA_DUMMY = "dummy"; + + public static Objective createObjective(Scoreboard board, String id) + { + return board.registerNewObjective(id, OBJECTIVE_CRITERIA_DUMMY); + } + + public static Objective createObjective(Scoreboard board, String id, boolean persistent, String name, DisplaySlot slot, Map entries) + { + Objective objective = createObjective(board, id); + setObjective(objective, persistent, name, slot, entries); + return objective; + } + + public static void deleteObjective(Objective objective) + { + if (objective == null) return; + getTemporaryObjectives().remove(objective); + try + { + objective.unregister(); + } + catch (IllegalStateException e) + { + // Already Done + } + } + + public static void deleteObjective(Scoreboard board, String id) + { + Objective objective = board.getObjective(id); + deleteObjective(objective); + } + + public static Objective getObjective(Scoreboard board, String id, boolean creative) + { + Objective objective = board.getObjective(id); + if (objective == null && creative) createObjective(board, id); + return objective; + } + + public static Objective getObjective(Scoreboard board, String id, boolean creative, Boolean persistent, String name, DisplaySlot slot, Map entries) + { + Objective objective = getObjective(board, id, creative); + if (objective == null) return null; + setObjective(objective, persistent, name, slot, entries); + return objective; + } + + public static void setObjective(Objective objective, Boolean persistent, String name, DisplaySlot slot, Map entries) + { + setObjectivePersistent(objective, persistent); + setObjectiveName(objective, name); + setObjectiveSlot(objective, slot); + setObjectiveEntries(objective, entries); + } + + // -------------------------------------------- // + // OBJECTIVE > ID + // -------------------------------------------- // + + public static String getObjectiveId(Objective objective) + { + return objective.getName(); + } + + // -------------------------------------------- // + // OBJECTIVE > PERSISTENT + // -------------------------------------------- // + + public static boolean isObjectivePersistent(Objective objective) + { + return ! getTemporaryObjectives().contains(objective); + } + + public static void setObjectivePersistent(Objective objective, Boolean persistent) + { + if (persistent == null) return; + + if (persistent) + { + getTemporaryObjectives().remove(objective); + } + else + { + getTemporaryObjectives().add(objective); + } + } + + // -------------------------------------------- // + // OBJECTIVE > NAME + // -------------------------------------------- // + + public static String getObjectiveName(Objective objective) + { + return objective.getDisplayName(); + } + + public static void setObjectiveName(Objective objective, String name) + { + if (name == null) return; + String before = getObjectiveName(objective); + if (MUtil.equals(before, name)) return; + objective.setDisplayName(name); + } + + // -------------------------------------------- // + // OBJECTIVE > SLOT + // -------------------------------------------- // + + public static DisplaySlot getObjectiveSlot(Objective objective) + { + return objective.getDisplaySlot(); + } + + public static void setObjectiveSlot(Objective objective, DisplaySlot slot) + { + if (slot == null) return; + DisplaySlot before = getObjectiveSlot(objective); + if (MUtil.equals(before, slot)) return; + objective.setDisplaySlot(slot); + } + + // -------------------------------------------- // + // OBJECTIVE > VALUE + // -------------------------------------------- // + + public static int getObjectiveValue(Objective objective, Object key) + { + Score score = objective.getScore(getKey(key)); + return getScoreValue(score); + } + + public static void setObjectiveValue(Objective objective, Object key, Integer value) + { + if (value == null) return; + Score score = objective.getScore(getKey(key)); + setScoreValue(score, value); + } + + // -------------------------------------------- // + // OBJECTIVE > ENTRIES + // -------------------------------------------- // + + public static Map getObjectiveEntries(Objective objective) + { + // Create + Map ret = new MassiveMap<>(); + + // Fill + for (String key : objective.getScoreboard().getEntries()) + { + int value = getObjectiveValue(objective, key); + if (value == 0) continue; + ret.put(key, value); + } + + // Return + return ret; + } + + public static void setObjectiveEntries(Objective objective, Map entries) + { + if (entries == null) return; + + // Add or Update + for (Entry entry : entries.entrySet()) + { + String key = entry.getKey(); + Integer value = entry.getValue(); + setObjectiveValue(objective, key, value); + } + + // Remove + for (String key : objective.getScoreboard().getEntries()) + { + if (entries.containsKey(key)) continue; + setObjectiveValue(objective, key, 0); + } + } + + // -------------------------------------------- // + // SCORE > VALUE + // -------------------------------------------- // + + public static int getScoreValue(Score score) + { + return score.getScore(); + } + + public static void setScoreValue(Score score, Integer value) + { + if (value == null) return; + int before = getScoreValue(score); + if (before == value) return; + score.setScore(value); + } + + // -------------------------------------------- // + // TEAM + // -------------------------------------------- // + + public static Team createTeam(Scoreboard board, String id) + { + return board.registerNewTeam(id); + } + + public static Team createTeam(Scoreboard board, String id, Boolean persistent, String name, String prefix, String suffix, ChatColor color, Boolean friendlyFireEnabled, Boolean friendlyTruesightEnabled, Map options, Set members) + { + Team team = createTeam(board, id); + setTeam(team, persistent, name, prefix, suffix, color, friendlyFireEnabled, friendlyTruesightEnabled, options, members); + return team; + } + + public static void deleteTeam(Team team) + { + if (team == null) return; + getTemporaryTeams().remove(team); + try + { + team.unregister(); + } + catch (IllegalStateException e) + { + // Already Done + } + } + + public static void deleteTeam(Scoreboard board, String id) + { + Team team = board.getTeam(id); + deleteTeam(team); + } + + public static Team getTeam(Scoreboard board, String id, boolean creative) + { + Team team = board.getTeam(id); + if (team == null && creative) team = createTeam(board, id); + return team; + } + + public static Team getTeam(Scoreboard board, String id, boolean creative, Boolean persistent, String name, String prefix, String suffix, ChatColor color, Boolean friendlyFireEnabled, Boolean friendlyTruesightEnabled, Map options, Set members) + { + Team team = getTeam(board, id, creative); + if (team == null) return null; + setTeam(team, persistent, name, prefix, suffix, color, friendlyFireEnabled, friendlyTruesightEnabled, options, members); + return team; + } + + public static void setTeam(Team team, Boolean persistent, String name, String prefix, String suffix, ChatColor color, Boolean friendlyFireEnabled, Boolean friendlyTruesightEnabled, Map options, Set members) + { + setTeamPersistent(team, persistent); + setTeamName(team, name); + setTeamPrefix(team, prefix); + setTeamSuffix(team, suffix); + setTeamColor(team, color); + setTeamFriendlyFireEnabled(team, friendlyFireEnabled); + setTeamFriendlyTruesightEnabled(team, friendlyTruesightEnabled); + setTeamOptions(team, options); + setTeamMembers(team, members); + } + + // -------------------------------------------- // + // TEAM > ID + // -------------------------------------------- // + + public static String getTeamId(Team team) + { + return team.getName(); + } + + // -------------------------------------------- // + // TEAM > PERSISTENT + // -------------------------------------------- // + + public static boolean isTeamPersistent(Team team) + { + return ! getTemporaryTeams().contains(team); + } + + public static void setTeamPersistent(Team team, Boolean persistent) + { + if (persistent == null) return; + + if (persistent) + { + getTemporaryTeams().remove(team); + } + else + { + getTemporaryTeams().add(team); + } + } + + // -------------------------------------------- // + // TEAM > NAME + // -------------------------------------------- // + + public static String getTeamName(Team team) + { + return team.getDisplayName(); + } + + public static void setTeamName(Team team, String name) + { + if (name == null) return; + String before = getTeamName(team); + if (MUtil.equals(before, name)) return; + team.setDisplayName(name); + } + + // -------------------------------------------- // + // TEAM > PREFIX + // -------------------------------------------- // + + public static String getTeamPrefix(Team team) + { + return team.getPrefix(); + } + + public static void setTeamPrefix(Team team, String prefix) + { + if (prefix == null) return; + String before = getTeamPrefix(team); + if (MUtil.equals(before, prefix)) return; + team.setPrefix(prefix); + } + + // -------------------------------------------- // + // TEAM > SUFFIX + // -------------------------------------------- // + + public static String getTeamSuffix(Team team) + { + return team.getSuffix(); + } + + public static void setTeamSuffix(Team team, String suffix) + { + if (suffix == null) return; + String before = getTeamSuffix(team); + if (MUtil.equals(before, suffix)) return; + team.setSuffix(suffix); + } + + // -------------------------------------------- // + // TEAM > COLOR + // -------------------------------------------- // + // SINCE: Minecraft 1.9 + // NOTE: We use reflected NMS implementation since Spigot does not have an implementation yet. + + public static ChatColor getTeamColor(Team team) + { + return NmsTeamColor.get().get(team); + } + + public static void setTeamColor(Team team, ChatColor color) + { + if (color == null) return; + ChatColor before = getTeamColor(team); + if (MUtil.equals(before, color)) return; + NmsTeamColor.get().set(team, color); + } + + // -------------------------------------------- // + // TEAM > FRIENDLY FIRE ENABLED + // -------------------------------------------- // + + public static boolean isTeamFriendlyFireEnabled(Team team) + { + return team.allowFriendlyFire(); + } + + public static void setTeamFriendlyFireEnabled(Team team, Boolean friendlyFireEnabled) + { + if (friendlyFireEnabled == null) return; + boolean before = isTeamFriendlyFireEnabled(team); + if (MUtil.equals(before, friendlyFireEnabled)) return; + team.setAllowFriendlyFire(friendlyFireEnabled); + } + + // -------------------------------------------- // + // TEAM > FRIENDLY TRUESIGHT ENABLED + // -------------------------------------------- // + + public static boolean isTeamFriendlyTruesightEnabled(Team team) + { + return team.canSeeFriendlyInvisibles(); + } + + public static void setTeamFriendlyTruesightEnabled(Team team, Boolean friendlyTruesightEnabled) + { + if (friendlyTruesightEnabled == null) return; + boolean before = isTeamFriendlyTruesightEnabled(team); + if (MUtil.equals(before, friendlyTruesightEnabled)) return; + team.setCanSeeFriendlyInvisibles(friendlyTruesightEnabled); + } + + // -------------------------------------------- // + // TEAM > OPTION + // -------------------------------------------- // + + public static OptionStatus getTeamOption(Team team, Option option) + { + return team.getOption(option); + } + + public static void setTeamOption(Team team, Option option, OptionStatus status) + { + if (status == null) return; + OptionStatus before = getTeamOption(team, option); + if (before == status) return; + team.setOption(option, status); + } + + // -------------------------------------------- // + // TEAM > OPTIONS + // -------------------------------------------- // + + public static Map getTeamOptions(Team team) + { + // Create + Map ret = new MassiveMap<>(); + + // Fill + for (Option option : Option.values()) + { + OptionStatus status = getTeamOption(team, option); + ret.put(option, status); + } + + // Return + return ret; + } + + public static void setTeamOptions(Team team, Map options) + { + if (options == null) return; + + for (Entry entry : options.entrySet()) + { + Option option = entry.getKey(); + OptionStatus status = entry.getValue(); + setTeamOption(team, option, status); + } + } + + // -------------------------------------------- // + // TEAM > MEMBERS + // -------------------------------------------- // + + public static void addTeamMember(Team team, Object key) + { + team.addEntry(getKey(key)); + } + + public static void removeTeamMember(Team team, Object key) + { + team.removeEntry(getKey(key)); + } + + public static boolean isTeamMember(Team team, Object key) + { + return team.hasEntry(getKey(key)); + } + + public static Set getTeamMembers(Team team) + { + return team.getEntries(); + } + + public static void setTeamMembers(Team team, Set members) + { + if (members == null) return; + Set befores = getTeamMembers(team); + + // Add + for (String member : members) + { + if (befores.contains(member)) continue; + team.addEntry(member); + } + + // Remove + for (String before : befores) + { + if (members.contains(before)) continue; + team.removeEntry(before); + } + } + + // -------------------------------------------- // + // KEY TEAM + // -------------------------------------------- // + // Treating the team like a property of the key. + // Get and set the team for the key. + + public static Team getKeyTeam(Scoreboard board, Object key) + { + return board.getEntryTeam(getKey(key)); + } + + public static void setKeyTeam(Scoreboard board, Object key, Team team) + { + Team before = getKeyTeam(board, key); + if (MUtil.equals(before, team)) return; + if (before != null) removeTeamMember(before, key); + if (team != null) addTeamMember(team, key); + } + + // -------------------------------------------- // + // PERSONAL TEAM + // -------------------------------------------- // + // The id is the player name. + + private static final Boolean PERSONAL_DEFAULT_PERSISTENT = false; + private static final String PERSONAL_DEFAULT_NAME = null; + private static final String PERSONAL_DEFAULT_PREFIX = ""; + private static final String PERSONAL_DEFAULT_SUFFIX = ChatColor.RESET.toString(); + private static final ChatColor PERSONAL_DEFAULT_COLOR = ChatColor.RESET; + private static final Boolean PERSONAL_DEFAULT_FRIENDLY_FIRE_ENABLED = true; + private static final Boolean PERSONAL_DEFAULT_FRIENDLY_TRUESIGHT_ENABLED = false; + private static final Map PERSONAL_DEFAULT_OPTIONS = new MassiveMap<>( + Option.COLLISION_RULE, OptionStatus.ALWAYS, + Option.DEATH_MESSAGE_VISIBILITY, OptionStatus.ALWAYS, + Option.NAME_TAG_VISIBILITY, OptionStatus.ALWAYS + ); + + public static boolean isPersonalTeam(Scoreboard board, Object key) + { + Team team = getKeyTeam(board, key); + return isPersonalTeam(team, key); + } + + public static boolean isPersonalTeam(Team team, Object key) + { + if (team == null) return false; + String id = getTeamId(team); + return id.equals(getKey(key)); + } + + public static Team createPersonalTeam(Scoreboard board, Object key) + { + String id = getKey(key); + Boolean persistent = PERSONAL_DEFAULT_PERSISTENT; + String name = PERSONAL_DEFAULT_NAME; + String prefix = PERSONAL_DEFAULT_PREFIX; + String suffix = PERSONAL_DEFAULT_SUFFIX; + ChatColor color = PERSONAL_DEFAULT_COLOR; + Boolean friendlyFireEnabled = PERSONAL_DEFAULT_FRIENDLY_FIRE_ENABLED; + Boolean friendlyTruesightEnabled = PERSONAL_DEFAULT_FRIENDLY_TRUESIGHT_ENABLED; + Map options = PERSONAL_DEFAULT_OPTIONS; + Set members = Collections.singleton(id); + return createTeam(board, id, persistent, name, prefix, suffix, color, friendlyFireEnabled, friendlyTruesightEnabled, options, members); + } + + public static Team getPersonalTeam(Scoreboard board, Object key, boolean creative) + { + String id = getKey(key); + Team team = getTeam(board, id, false); + if (team == null && creative) team = createPersonalTeam(board, key); + addTeamMember(team, key); + return team; + } + + public static void deletePersonalTeam(Scoreboard board, Object key) + { + Team team = getPersonalTeam(board, key, false); + if ( ! isPersonalTeam(team, key)) return; + deleteTeam(team); + } + +} diff --git a/src/com/massivecraft/massivecore/util/MUtil.java b/src/com/massivecraft/massivecore/util/MUtil.java index 3f10adbc..da73fb7a 100644 --- a/src/com/massivecraft/massivecore/util/MUtil.java +++ b/src/com/massivecraft/massivecore/util/MUtil.java @@ -57,7 +57,10 @@ import org.bukkit.potion.PotionEffect; import org.bukkit.potion.PotionEffectType; import org.bukkit.projectiles.ProjectileSource; +import com.massivecraft.massivecore.Active; +import com.massivecraft.massivecore.Engine; import com.massivecraft.massivecore.MassiveCore; +import com.massivecraft.massivecore.MassivePlugin; import com.massivecraft.massivecore.collections.MassiveList; import com.massivecraft.massivecore.collections.MassiveSet; import com.massivecraft.massivecore.collections.MassiveTreeSet; @@ -133,7 +136,7 @@ public class MUtil return ret; } - public static Collection getOnlinePlayers() + public static Collection getOnlinePlayers() { // Fetch some kind of playersObject. Object playersObject = null; @@ -160,7 +163,7 @@ public class MUtil if (playersObject instanceof Collection) { @SuppressWarnings("unchecked") - Collection playersCollection = (Collection)playersObject; + Collection playersCollection = (Collection)playersObject; return playersCollection; } else if (playersObject instanceof Player[])