mirror of https://github.com/mcMMO-Dev/mcMMO.git
604 lines
20 KiB
Java
604 lines
20 KiB
Java
package com.gmail.nossr50.util.scoreboards;
|
|
|
|
import com.gmail.nossr50.config.Config;
|
|
import com.gmail.nossr50.datatypes.database.PlayerStat;
|
|
import com.gmail.nossr50.datatypes.player.McMMOPlayer;
|
|
import com.gmail.nossr50.datatypes.player.PlayerProfile;
|
|
import com.gmail.nossr50.datatypes.skills.PrimarySkillType;
|
|
import com.gmail.nossr50.datatypes.skills.SuperAbilityType;
|
|
import com.gmail.nossr50.events.scoreboard.*;
|
|
import com.gmail.nossr50.locale.LocaleLoader;
|
|
import com.gmail.nossr50.mcMMO;
|
|
import com.gmail.nossr50.skills.child.FamilyTree;
|
|
import com.gmail.nossr50.util.Misc;
|
|
import com.gmail.nossr50.util.player.UserManager;
|
|
import com.gmail.nossr50.util.scoreboards.ScoreboardManager.SidebarType;
|
|
import org.apache.commons.lang.Validate;
|
|
import org.bukkit.ChatColor;
|
|
import org.bukkit.entity.Player;
|
|
import org.bukkit.scheduler.BukkitRunnable;
|
|
import org.bukkit.scheduler.BukkitTask;
|
|
import org.bukkit.scoreboard.DisplaySlot;
|
|
import org.bukkit.scoreboard.Objective;
|
|
import org.bukkit.scoreboard.Score;
|
|
import org.bukkit.scoreboard.Scoreboard;
|
|
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
public class ScoreboardWrapper {
|
|
// Initialization variables
|
|
public final String playerName;
|
|
public final Player player;
|
|
private final Scoreboard scoreboard;
|
|
private boolean tippedKeep = false;
|
|
private boolean tippedClear = false;
|
|
|
|
// Internal usage variables (should exist)
|
|
private SidebarType sidebarType;
|
|
private Objective sidebarObjective;
|
|
private Objective powerObjective;
|
|
|
|
// Parameter variables (May be null / invalid)
|
|
private Scoreboard oldBoard = null;
|
|
public String targetPlayer = null;
|
|
public PrimarySkillType targetSkill = null;
|
|
private PlayerProfile targetProfile = null;
|
|
public int leaderboardPage = -1;
|
|
|
|
private ScoreboardWrapper(Player player, Scoreboard scoreboard) {
|
|
this.player = player;
|
|
this.playerName = player.getName();
|
|
this.scoreboard = scoreboard;
|
|
sidebarType = SidebarType.NONE;
|
|
sidebarObjective = this.scoreboard.registerNewObjective(ScoreboardManager.SIDEBAR_OBJECTIVE, "dummy");
|
|
powerObjective = this.scoreboard.registerNewObjective(ScoreboardManager.POWER_OBJECTIVE, "dummy");
|
|
|
|
if (Config.getInstance().getPowerLevelTagsEnabled()) {
|
|
powerObjective.setDisplayName(ScoreboardManager.TAG_POWER_LEVEL);
|
|
powerObjective.setDisplaySlot(DisplaySlot.BELOW_NAME);
|
|
|
|
for (McMMOPlayer mcMMOPlayer : UserManager.getPlayers()) {
|
|
powerObjective.getScore(mcMMOPlayer.getProfile().getPlayerName()).setScore(mcMMOPlayer.getPowerLevel());
|
|
}
|
|
}
|
|
}
|
|
|
|
public static ScoreboardWrapper create(Player player) {
|
|
//Call our custom event
|
|
McMMOScoreboardMakeboardEvent event = new McMMOScoreboardMakeboardEvent(mcMMO.p.getServer().getScoreboardManager().getNewScoreboard(), player.getScoreboard(), player, ScoreboardEventReason.CREATING_NEW_SCOREBOARD);
|
|
player.getServer().getPluginManager().callEvent(event);
|
|
//Use the values from the event
|
|
return new ScoreboardWrapper(event.getTargetPlayer(), event.getTargetBoard());
|
|
}
|
|
|
|
public BukkitTask updateTask = null;
|
|
|
|
private class ScoreboardQuickUpdate extends BukkitRunnable {
|
|
@Override
|
|
public void run() {
|
|
updateSidebar();
|
|
updateTask = null;
|
|
}
|
|
}
|
|
|
|
public BukkitTask revertTask = null;
|
|
|
|
private class ScoreboardChangeTask extends BukkitRunnable {
|
|
@Override
|
|
public void run() {
|
|
tryRevertBoard();
|
|
revertTask = null;
|
|
}
|
|
}
|
|
|
|
public BukkitTask cooldownTask = null;
|
|
|
|
private class ScoreboardCooldownTask extends BukkitRunnable {
|
|
@Override
|
|
public void run() {
|
|
// Stop updating if it's no longer something displaying cooldowns
|
|
if (isBoardShown() && (isSkillScoreboard() || isCooldownScoreboard())) {
|
|
doSidebarUpdateSoon();
|
|
}
|
|
else {
|
|
stopCooldownUpdating();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public void doSidebarUpdateSoon() {
|
|
if (updateTask == null) {
|
|
// To avoid spamming the scheduler, store the instance and run 2 ticks later
|
|
updateTask = new ScoreboardQuickUpdate().runTaskLater(mcMMO.p, 2L);
|
|
}
|
|
}
|
|
|
|
private void startCooldownUpdating() {
|
|
if (cooldownTask == null) {
|
|
// Repeat every 5 seconds.
|
|
// Cancels once all cooldowns are done, using stopCooldownUpdating().
|
|
cooldownTask = new ScoreboardCooldownTask().runTaskTimer(mcMMO.p, 5 * Misc.TICK_CONVERSION_FACTOR, 5 * Misc.TICK_CONVERSION_FACTOR);
|
|
}
|
|
}
|
|
|
|
private void stopCooldownUpdating() {
|
|
if (cooldownTask != null) {
|
|
try {
|
|
cooldownTask.cancel();
|
|
}
|
|
catch (Throwable ignored) {
|
|
}
|
|
|
|
cooldownTask = null;
|
|
}
|
|
}
|
|
|
|
public boolean isSkillScoreboard() {
|
|
return sidebarType == SidebarType.SKILL_BOARD;
|
|
}
|
|
|
|
public boolean isCooldownScoreboard() {
|
|
return sidebarType == SidebarType.COOLDOWNS_BOARD;
|
|
}
|
|
|
|
public boolean isStatsScoreboard() {
|
|
return sidebarType == SidebarType.STATS_BOARD;
|
|
}
|
|
|
|
/**
|
|
* Set the old targetBoard, for use in reverting.
|
|
*/
|
|
public void setOldScoreboard() {
|
|
Player player = mcMMO.p.getServer().getPlayerExact(playerName);
|
|
|
|
if (player == null) {
|
|
ScoreboardManager.cleanup(this);
|
|
return;
|
|
}
|
|
|
|
Scoreboard oldBoard = player.getScoreboard();
|
|
|
|
if (oldBoard == scoreboard) { // Already displaying it
|
|
if (this.oldBoard == null) {
|
|
// (Shouldn't happen) Use failsafe value - we're already displaying our board, but we don't have the one we should revert to
|
|
this.oldBoard = mcMMO.p.getServer().getScoreboardManager().getMainScoreboard();
|
|
}
|
|
}
|
|
else {
|
|
this.oldBoard = oldBoard;
|
|
}
|
|
}
|
|
|
|
public void showBoardWithNoRevert() {
|
|
Player player = mcMMO.p.getServer().getPlayerExact(playerName);
|
|
|
|
if (player == null) {
|
|
ScoreboardManager.cleanup(this);
|
|
return;
|
|
}
|
|
|
|
if (revertTask != null) {
|
|
revertTask.cancel();
|
|
}
|
|
|
|
player.setScoreboard(scoreboard);
|
|
revertTask = null;
|
|
}
|
|
|
|
public void showBoardAndScheduleRevert(int ticks) {
|
|
Player player = mcMMO.p.getServer().getPlayerExact(playerName);
|
|
|
|
if (player == null) {
|
|
ScoreboardManager.cleanup(this);
|
|
return;
|
|
}
|
|
|
|
if (revertTask != null) {
|
|
revertTask.cancel();
|
|
}
|
|
|
|
player.setScoreboard(scoreboard);
|
|
revertTask = new ScoreboardChangeTask().runTaskLater(mcMMO.p, ticks);
|
|
|
|
// TODO is there any way to do the time that looks acceptable?
|
|
// player.sendMessage(LocaleLoader.getString("Commands.Scoreboard.Timer", StringUtils.capitalize(sidebarType.toString().toLowerCase(Locale.ENGLISH)), ticks / 20F));
|
|
|
|
if(UserManager.getPlayer(playerName) == null)
|
|
return;
|
|
|
|
PlayerProfile profile = UserManager.getPlayer(player).getProfile();
|
|
|
|
if (profile.getScoreboardTipsShown() >= Config.getInstance().getTipsAmount()) {
|
|
return;
|
|
}
|
|
|
|
if (!tippedKeep) {
|
|
tippedKeep = true;
|
|
player.sendMessage(LocaleLoader.getString("Commands.Scoreboard.Tip.Keep"));
|
|
}
|
|
else if (!tippedClear) {
|
|
tippedClear = true;
|
|
player.sendMessage(LocaleLoader.getString("Commands.Scoreboard.Tip.Clear"));
|
|
profile.increaseTipsShown();
|
|
}
|
|
}
|
|
|
|
public void tryRevertBoard() {
|
|
Player player = mcMMO.p.getServer().getPlayerExact(playerName);
|
|
|
|
if (player == null) {
|
|
ScoreboardManager.cleanup(this);
|
|
return;
|
|
}
|
|
|
|
if (oldBoard != null) {
|
|
if (player.getScoreboard() == scoreboard) {
|
|
/**
|
|
* Call the revert scoreboard custom event
|
|
*/
|
|
McMMOScoreboardRevertEvent event = new McMMOScoreboardRevertEvent(oldBoard, player.getScoreboard(), player, ScoreboardEventReason.REVERTING_BOARD);
|
|
player.getServer().getPluginManager().callEvent(event);
|
|
//Modify the player based on the event
|
|
event.getTargetPlayer().setScoreboard(event.getTargetBoard());
|
|
oldBoard = null;
|
|
}
|
|
else {
|
|
mcMMO.p.debug("Not reverting targetBoard for " + playerName + " - targetBoard was changed by another plugin (Consider disabling the mcMMO scoreboards if you don't want them!)");
|
|
}
|
|
}
|
|
|
|
cancelRevert();
|
|
|
|
sidebarType = SidebarType.NONE;
|
|
targetPlayer = null;
|
|
targetSkill = null;
|
|
targetProfile = null;
|
|
leaderboardPage = -1;
|
|
}
|
|
|
|
public boolean isBoardShown() {
|
|
Player player = mcMMO.p.getServer().getPlayerExact(playerName);
|
|
|
|
if (player == null) {
|
|
ScoreboardManager.cleanup(this);
|
|
return false;
|
|
}
|
|
|
|
return player.getScoreboard() == scoreboard;
|
|
}
|
|
|
|
public void cancelRevert() {
|
|
if (revertTask == null) {
|
|
return;
|
|
}
|
|
|
|
revertTask.cancel();
|
|
revertTask = null;
|
|
}
|
|
|
|
// Board Type Changing 'API' methods
|
|
|
|
public void setTypeNone() {
|
|
this.sidebarType = SidebarType.NONE;
|
|
|
|
targetPlayer = null;
|
|
targetSkill = null;
|
|
targetProfile = null;
|
|
leaderboardPage = -1;
|
|
|
|
loadObjective("");
|
|
}
|
|
|
|
public void setTypeSkill(PrimarySkillType skill) {
|
|
this.sidebarType = SidebarType.SKILL_BOARD;
|
|
targetSkill = skill;
|
|
|
|
targetPlayer = null;
|
|
targetProfile = null;
|
|
leaderboardPage = -1;
|
|
|
|
loadObjective(ScoreboardManager.skillLabels.get(skill));
|
|
}
|
|
|
|
public void setTypeSelfStats() {
|
|
this.sidebarType = SidebarType.STATS_BOARD;
|
|
|
|
targetPlayer = null;
|
|
targetSkill = null;
|
|
targetProfile = null;
|
|
leaderboardPage = -1;
|
|
|
|
loadObjective(ScoreboardManager.HEADER_STATS);
|
|
}
|
|
|
|
public void setTypeInspectStats(PlayerProfile profile) {
|
|
this.sidebarType = SidebarType.STATS_BOARD;
|
|
targetPlayer = profile.getPlayerName();
|
|
targetProfile = profile;
|
|
|
|
targetSkill = null;
|
|
leaderboardPage = -1;
|
|
|
|
loadObjective(LocaleLoader.getString("Scoreboard.Header.PlayerInspect", targetPlayer));
|
|
}
|
|
|
|
public void setTypeCooldowns() {
|
|
this.sidebarType = SidebarType.COOLDOWNS_BOARD;
|
|
|
|
targetPlayer = null;
|
|
targetSkill = null;
|
|
targetProfile = null;
|
|
leaderboardPage = -1;
|
|
|
|
loadObjective(ScoreboardManager.HEADER_COOLDOWNS);
|
|
}
|
|
|
|
public void setTypeSelfRank() {
|
|
this.sidebarType = SidebarType.RANK_BOARD;
|
|
targetPlayer = null;
|
|
|
|
targetSkill = null;
|
|
targetProfile = null;
|
|
leaderboardPage = -1;
|
|
|
|
loadObjective(ScoreboardManager.HEADER_RANK);
|
|
}
|
|
|
|
public void setTypeInspectRank(String otherPlayer) {
|
|
this.sidebarType = SidebarType.RANK_BOARD;
|
|
targetPlayer = otherPlayer;
|
|
|
|
targetSkill = null;
|
|
targetProfile = null;
|
|
leaderboardPage = -1;
|
|
|
|
loadObjective(ScoreboardManager.HEADER_RANK);
|
|
}
|
|
|
|
public void setTypeTopPower(int page) {
|
|
this.sidebarType = SidebarType.TOP_BOARD;
|
|
leaderboardPage = page;
|
|
targetSkill = null;
|
|
|
|
targetPlayer = null;
|
|
targetProfile = null;
|
|
|
|
int endPosition = page * 10;
|
|
int startPosition = endPosition - 9;
|
|
loadObjective(String.format("%s (%2d - %2d)", ScoreboardManager.POWER_LEVEL, startPosition, endPosition));
|
|
}
|
|
|
|
public void setTypeTop(PrimarySkillType skill, int page) {
|
|
this.sidebarType = SidebarType.TOP_BOARD;
|
|
leaderboardPage = page;
|
|
targetSkill = skill;
|
|
|
|
targetPlayer = null;
|
|
targetProfile = null;
|
|
|
|
int endPosition = page * 10;
|
|
int startPosition = endPosition - 9;
|
|
loadObjective(String.format("%s (%2d - %2d)", ScoreboardManager.skillLabels.get(skill), startPosition, endPosition));
|
|
}
|
|
|
|
// Setup for after a board type change
|
|
protected void loadObjective(String displayName) {
|
|
//Unregister objective
|
|
McMMOScoreboardObjectiveEvent unregisterEvent = callObjectiveEvent(ScoreboardObjectiveEventReason.UNREGISTER_THIS_OBJECTIVE);
|
|
if(!unregisterEvent.isCancelled()) {
|
|
sidebarObjective.unregister();
|
|
}
|
|
|
|
//Register objective
|
|
McMMOScoreboardObjectiveEvent registerEvent = callObjectiveEvent(ScoreboardObjectiveEventReason.REGISTER_NEW_OBJECTIVE);
|
|
if(!registerEvent.isCancelled())
|
|
sidebarObjective = registerEvent.getTargetBoard().registerNewObjective(ScoreboardManager.SIDEBAR_OBJECTIVE, "dummy");
|
|
|
|
if (displayName.length() > 32) {
|
|
displayName = displayName.substring(0, 32);
|
|
}
|
|
|
|
sidebarObjective.setDisplayName(displayName);
|
|
|
|
updateSidebar();
|
|
// Do last! Minimize packets!
|
|
sidebarObjective.setDisplaySlot(DisplaySlot.SIDEBAR);
|
|
}
|
|
|
|
private McMMOScoreboardObjectiveEvent callObjectiveEvent(ScoreboardObjectiveEventReason reason) {
|
|
McMMOScoreboardObjectiveEvent event = new McMMOScoreboardObjectiveEvent(sidebarObjective, reason, scoreboard, scoreboard, player, ScoreboardEventReason.OBJECTIVE);
|
|
player.getServer().getPluginManager().callEvent(event);
|
|
return event;
|
|
}
|
|
|
|
/**
|
|
* Load new values into the sidebar.
|
|
*/
|
|
private void updateSidebar() {
|
|
try {
|
|
updateTask.cancel();
|
|
}
|
|
catch (Throwable ignored) {
|
|
} // catch NullPointerException and IllegalStateException and any Error; don't care
|
|
|
|
updateTask = null;
|
|
|
|
if (sidebarType == SidebarType.NONE) {
|
|
return;
|
|
}
|
|
|
|
Player player = mcMMO.p.getServer().getPlayerExact(playerName);
|
|
|
|
if (player == null) {
|
|
ScoreboardManager.cleanup(this);
|
|
return;
|
|
}
|
|
|
|
McMMOPlayer mcMMOPlayer = UserManager.getPlayer(player);
|
|
|
|
if(mcMMOPlayer == null)
|
|
return;
|
|
|
|
switch (sidebarType) {
|
|
case NONE:
|
|
break;
|
|
|
|
case SKILL_BOARD:
|
|
Validate.notNull(targetSkill);
|
|
|
|
if (!targetSkill.isChildSkill()) {
|
|
int currentXP = mcMMOPlayer.getSkillXpLevel(targetSkill);
|
|
|
|
sidebarObjective.getScore(ScoreboardManager.LABEL_CURRENT_XP).setScore(currentXP);
|
|
sidebarObjective.getScore(ScoreboardManager.LABEL_REMAINING_XP).setScore(mcMMOPlayer.getXpToLevel(targetSkill) - currentXP);
|
|
}
|
|
else {
|
|
for (PrimarySkillType parentSkill : FamilyTree.getParents(targetSkill)) {
|
|
sidebarObjective.getScore(ScoreboardManager.skillLabels.get(parentSkill)).setScore(mcMMOPlayer.getSkillLevel(parentSkill));
|
|
}
|
|
}
|
|
|
|
sidebarObjective.getScore(ScoreboardManager.LABEL_LEVEL).setScore(mcMMOPlayer.getSkillLevel(targetSkill));
|
|
|
|
if (targetSkill.getAbility() != null) {
|
|
boolean stopUpdating;
|
|
|
|
if (targetSkill == PrimarySkillType.MINING) {
|
|
// Special-Case: Mining has two abilities, both with cooldowns
|
|
Score cooldownSB = sidebarObjective.getScore(ScoreboardManager.abilityLabelsSkill.get(SuperAbilityType.SUPER_BREAKER));
|
|
Score cooldownBM = sidebarObjective.getScore(ScoreboardManager.abilityLabelsSkill.get(SuperAbilityType.BLAST_MINING));
|
|
int secondsSB = Math.max(mcMMOPlayer.calculateTimeRemaining(SuperAbilityType.SUPER_BREAKER), 0);
|
|
int secondsBM = Math.max(mcMMOPlayer.calculateTimeRemaining(SuperAbilityType.BLAST_MINING), 0);
|
|
|
|
cooldownSB.setScore(secondsSB);
|
|
cooldownBM.setScore(secondsBM);
|
|
|
|
stopUpdating = (secondsSB == 0 && secondsBM == 0);
|
|
}
|
|
else {
|
|
SuperAbilityType ability = targetSkill.getAbility();
|
|
Score cooldown = sidebarObjective.getScore(ScoreboardManager.abilityLabelsSkill.get(ability));
|
|
int seconds = Math.max(mcMMOPlayer.calculateTimeRemaining(ability), 0);
|
|
|
|
cooldown.setScore(seconds);
|
|
|
|
stopUpdating = seconds == 0;
|
|
}
|
|
|
|
if (stopUpdating) {
|
|
stopCooldownUpdating();
|
|
}
|
|
else {
|
|
startCooldownUpdating();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case COOLDOWNS_BOARD:
|
|
boolean anyCooldownsActive = false;
|
|
|
|
for (SuperAbilityType ability : SuperAbilityType.values()) {
|
|
int seconds = Math.max(mcMMOPlayer.calculateTimeRemaining(ability), 0);
|
|
|
|
if (seconds != 0) {
|
|
anyCooldownsActive = true;
|
|
}
|
|
|
|
sidebarObjective.getScore(ScoreboardManager.abilityLabelsColored.get(ability)).setScore(seconds);
|
|
}
|
|
|
|
if (anyCooldownsActive) {
|
|
startCooldownUpdating();
|
|
}
|
|
else {
|
|
stopCooldownUpdating();
|
|
}
|
|
break;
|
|
|
|
case STATS_BOARD:
|
|
// Select the profile to read from
|
|
PlayerProfile newProfile;
|
|
|
|
if (targetProfile != null) {
|
|
newProfile = targetProfile; // offline
|
|
}
|
|
else if (targetPlayer == null) {
|
|
newProfile = mcMMOPlayer.getProfile(); // self
|
|
}
|
|
else {
|
|
newProfile = UserManager.getPlayer(targetPlayer).getProfile(); // online
|
|
}
|
|
|
|
// Calculate power level here
|
|
int powerLevel = 0;
|
|
for (PrimarySkillType skill : PrimarySkillType.NON_CHILD_SKILLS) { // Don't include child skills, makes the list too long
|
|
int level = newProfile.getSkillLevel(skill);
|
|
|
|
powerLevel += level;
|
|
|
|
// TODO: Verify that this is what we want - calculated in power level but not displayed
|
|
if (!skill.getPermissions(player)) {
|
|
continue;
|
|
}
|
|
|
|
sidebarObjective.getScore(ScoreboardManager.skillLabels.get(skill)).setScore(level);
|
|
}
|
|
|
|
sidebarObjective.getScore(ScoreboardManager.LABEL_POWER_LEVEL).setScore(powerLevel);
|
|
break;
|
|
|
|
case RANK_BOARD:
|
|
case TOP_BOARD:
|
|
/*
|
|
* @see #acceptRankData(Map<PrimarySkillType, Integer> rank)
|
|
* @see #acceptLeaderboardData(List<PlayerStat> stats)
|
|
*/
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
public void acceptRankData(Map<PrimarySkillType, Integer> rankData) {
|
|
Integer rank;
|
|
Player player = mcMMO.p.getServer().getPlayerExact(playerName);
|
|
|
|
for (PrimarySkillType skill : PrimarySkillType.NON_CHILD_SKILLS) {
|
|
if (!skill.getPermissions(player)) {
|
|
continue;
|
|
}
|
|
|
|
rank = rankData.get(skill);
|
|
|
|
if (rank != null) {
|
|
sidebarObjective.getScore(ScoreboardManager.skillLabels.get(skill)).setScore(rank);
|
|
}
|
|
}
|
|
|
|
rank = rankData.get(null);
|
|
|
|
if (rank != null) {
|
|
sidebarObjective.getScore(ScoreboardManager.LABEL_POWER_LEVEL).setScore(rank);
|
|
}
|
|
}
|
|
|
|
public void acceptLeaderboardData(List<PlayerStat> leaderboardData) {
|
|
for (PlayerStat stat : leaderboardData) {
|
|
String name = stat.name;
|
|
|
|
if (name.equals(playerName)) {
|
|
name = ChatColor.GOLD + "--You--";
|
|
}
|
|
|
|
sidebarObjective.getScore(name).setScore(stat.statVal);
|
|
}
|
|
}
|
|
|
|
public void updatePowerLevel(Player player, int newPowerLevel) {
|
|
powerObjective.getScore(player.getName()).setScore(newPowerLevel);
|
|
}
|
|
}
|