-
-
Save vemacs/2952bcc534bd90a8fabc to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import com.google.common.base.Charsets; | |
import com.google.common.base.Splitter; | |
import com.google.common.collect.Lists; | |
import org.bukkit.Bukkit; | |
import org.bukkit.ChatColor; | |
import org.bukkit.OfflinePlayer; | |
import org.bukkit.entity.Player; | |
import org.bukkit.scoreboard.*; | |
import java.lang.reflect.Constructor; | |
import java.util.*; | |
import java.util.concurrent.ConcurrentHashMap; | |
public class SimpleScoreboard { | |
private static Map<String, OfflinePlayer> cache = new HashMap<>(); | |
private Scoreboard scoreboard; | |
private String title; | |
private Map<String, Integer> scores; | |
private Objective obj; | |
private List<Team> teams; | |
private List<Integer> removed; | |
private Set<String> updated; | |
public SimpleScoreboard(String title) { | |
this.scoreboard = Bukkit.getScoreboardManager().getNewScoreboard(); | |
this.title = ChatColor.translateAlternateColorCodes('&', title); | |
this.scores = new ConcurrentHashMap<>(); | |
this.teams = Collections.synchronizedList(Lists.newArrayList()); | |
this.removed = Lists.newArrayList(); | |
this.updated = Collections.synchronizedSet(new HashSet<>()); | |
} | |
public void add(String text, Integer score) { | |
text = ChatColor.translateAlternateColorCodes('&', text); | |
if (remove(score, text, false) || !scores.containsValue(score)) { | |
updated.add(text); | |
} | |
scores.put(text, score); | |
} | |
public boolean remove(Integer score, String text) { | |
return remove(score, text, true); | |
} | |
public boolean remove(Integer score, String n, boolean b) { | |
String toRemove = get(score, n); | |
if (toRemove == null) | |
return false; | |
scores.remove(toRemove); | |
if(b) | |
removed.add(score); | |
return true; | |
} | |
public String get(int score, String n) { | |
String str = null; | |
for (Map.Entry<String, Integer> entry : scores.entrySet()) { | |
if (entry.getValue().equals(score) && | |
!entry.getKey().equals(n)) { | |
str = entry.getKey(); | |
} | |
} | |
return str; | |
} | |
private Map.Entry<Team, OfflinePlayer> createTeam(String text, int pos) { | |
Team team; | |
ChatColor color = ChatColor.values()[pos]; | |
OfflinePlayer result; | |
if (!cache.containsKey(color.toString())) | |
cache.put(color.toString(), getOfflinePlayerSkipLookup(color.toString())); | |
result = cache.get(color.toString()); | |
try { | |
team = scoreboard.registerNewTeam("text-" + (teams.size() + 1)); | |
} catch (IllegalArgumentException e) { | |
team = scoreboard.getTeam("text-" + (teams.size())); | |
} | |
applyText(team, text, result); | |
teams.add(team); | |
return new AbstractMap.SimpleEntry<>(team, result); | |
} | |
private void applyText(Team team, String text, OfflinePlayer result) { | |
Iterator<String> iterator = Splitter.fixedLength(16).split(text).iterator(); | |
String prefix = iterator.next(); | |
team.setPrefix(prefix); | |
if(!team.hasPlayer(result)) | |
team.addPlayer(result); | |
if (text.length() > 16) { | |
String prefixColor = ChatColor.getLastColors(prefix); | |
String suffix = iterator.next(); | |
if (prefix.endsWith(String.valueOf(ChatColor.COLOR_CHAR))) { | |
prefix = prefix.substring(0, prefix.length() - 1); | |
team.setPrefix(prefix); | |
prefixColor = ChatColor.getByChar(suffix.charAt(0)).toString(); | |
suffix = suffix.substring(1); | |
} | |
if (prefixColor == null) | |
prefixColor = ""; | |
if (suffix.length() > 16) { | |
suffix = suffix.substring(0, (13 - prefixColor.length())); // cut off suffix, done if text is over 30 characters | |
} | |
team.setSuffix((prefixColor.equals("") ? ChatColor.RESET : prefixColor) + suffix); | |
} | |
} | |
public void update() { | |
if (updated.isEmpty()) { | |
return; | |
} | |
if (obj == null) { | |
obj = scoreboard.registerNewObjective((title.length() > 16 ? title.substring(0, 15) : title), "dummy"); | |
obj.setDisplayName(title); | |
obj.setDisplaySlot(DisplaySlot.SIDEBAR); | |
} | |
removed.stream().forEach((remove) -> { | |
for (String s : scoreboard.getEntries()) { | |
Score score = obj.getScore(s); | |
if (score == null) | |
continue; | |
if (score.getScore() != remove) | |
continue; | |
scoreboard.resetScores(s); | |
} | |
}); | |
removed.clear(); | |
int index = scores.size(); | |
for (Map.Entry<String, Integer> text : scores.entrySet()) { | |
Team t = scoreboard.getTeam(ChatColor.values()[text.getValue()].toString()); | |
Map.Entry<Team, OfflinePlayer> team; | |
if(!updated.contains(text.getKey())) { | |
continue; | |
} | |
if(t != null) { | |
String color = ChatColor.values()[text.getValue()].toString(); | |
if (!cache.containsKey(color)) { | |
cache.put(color, getOfflinePlayerSkipLookup(color)); | |
} | |
team = new AbstractMap.SimpleEntry<>(t, cache.get(color)); | |
applyText(team.getKey(), text.getKey(), team.getValue()); | |
index -= 1; | |
continue; | |
} else { | |
team = createTeam(text.getKey(), text.getValue()); | |
} | |
Integer score = text.getValue() != null ? text.getValue() : index; | |
obj.getScore(team.getValue()).setScore(score); | |
index -= 1; | |
} | |
updated.clear(); | |
} | |
public void setTitle(String title) { | |
this.title = ChatColor.translateAlternateColorCodes('&', title); | |
if(obj != null) | |
obj.setDisplayName(title); | |
} | |
public void reset() { | |
for (Team t : teams) | |
t.unregister(); | |
teams.clear(); | |
scores.clear(); | |
} | |
public Scoreboard getScoreboard() { | |
return scoreboard; | |
} | |
public void send(Player... players) { | |
for (Player p : players) | |
p.setScoreboard(scoreboard); | |
} | |
private final UUID invalidUserUUID = UUID.nameUUIDFromBytes("InvalidUsername".getBytes(Charsets.UTF_8)); | |
private Class<?> gameProfileClass; | |
private Constructor<?> gameProfileConstructor; | |
private Constructor<?> craftOfflinePlayerConstructor; | |
@SuppressWarnings("deprecation") | |
private OfflinePlayer getOfflinePlayerSkipLookup(String name) { | |
try { | |
if (gameProfileConstructor == null) { | |
try { // 1.7 | |
gameProfileClass = Class.forName("net.minecraft.util.com.mojang.authlib.GameProfile"); | |
} catch (ClassNotFoundException e) { // 1.8 | |
gameProfileClass = Class.forName("com.mojang.authlib.GameProfile"); | |
} | |
gameProfileConstructor = gameProfileClass.getDeclaredConstructor(UUID.class, String.class); | |
gameProfileConstructor.setAccessible(true); | |
} | |
if (craftOfflinePlayerConstructor == null) { | |
Class<?> serverClass = Bukkit.getServer().getClass(); | |
Class<?> craftOfflinePlayerClass = Class.forName(serverClass.getName() | |
.replace("CraftServer", "CraftOfflinePlayer")); | |
craftOfflinePlayerConstructor = craftOfflinePlayerClass.getDeclaredConstructor( | |
serverClass, gameProfileClass | |
); | |
craftOfflinePlayerConstructor.setAccessible(true); | |
} | |
Object gameProfile = gameProfileConstructor.newInstance(invalidUserUUID, name); | |
Object craftOfflinePlayer = craftOfflinePlayerConstructor.newInstance(Bukkit.getServer(), gameProfile); | |
return (OfflinePlayer) craftOfflinePlayer; | |
} catch (Throwable t) { // Fallback if fail | |
return Bukkit.getOfflinePlayer(name); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment