Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Non-flickering scoreboard implementation; create scoreboards with ease.
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(this.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);
}
}
}
@058c37a272bed3464d47f0b01038a16a

This comment has been minimized.

Copy link

@058c37a272bed3464d47f0b01038a16a 058c37a272bed3464d47f0b01038a16a commented Dec 5, 2015

nice credit 😢

@mkotb

This comment has been minimized.

Copy link
Owner Author

@mkotb mkotb commented Dec 5, 2015

you suck ducks

@058c37a272bed3464d47f0b01038a16a

This comment has been minimized.

Copy link

@058c37a272bed3464d47f0b01038a16a 058c37a272bed3464d47f0b01038a16a commented Dec 5, 2015

ok

@Mrtenz

This comment has been minimized.

Copy link

@Mrtenz Mrtenz commented Dec 27, 2015

It doesn't work on Java 7.

@orkun1675

This comment has been minimized.

Copy link

@orkun1675 orkun1675 commented Jan 6, 2016

Amazing class! Thank you.
Small bug: Line 195 should read "obj.setDisplayName(this.title);"

@Phineas

This comment has been minimized.

Copy link

@Phineas Phineas commented Jan 25, 2016

@M44rtenz Java 7 doesn't have streams. Idek why you're using Java 7, that's outdated now.

@ItsVinnyX

This comment has been minimized.

Copy link

@ItsVinnyX ItsVinnyX commented Jun 23, 2016

Mine is still flickering 1.8 - 1.9 Any fixes? The community needs this.

@ItsVinnyX

This comment has been minimized.

Copy link

@ItsVinnyX ItsVinnyX commented Jun 23, 2016

This is how I am making my scoreboard and it flickers:

`public void hubScoreboard(Player p) {
new BukkitRunnable() {
final ColorScrollPlus cs = new ColorScrollPlus(ChatColor.WHITE, "VenomSurge", "§a", "§f", "§a", true, false,
ScrollType.FORWARD);
ScoreboardAPI scoreboard;

        public void run() {

            if (cs.getScrollType() == ScrollType.FORWARD) {
                if (cs.getPosition() >= cs.getString().length()) {
                    cs.setScrollType(ScrollType.BACKWARD);
                }
            } else {
                if (cs.getPosition() <= -1) {
                    cs.setScrollType(ScrollType.FORWARD);
                }
            }
            scoreboard = new ScoreboardAPI(cs.next());
            scoreboard.add(C.dgray + C.bold + C.strike + "---------------", 7);
            if (plugin.permPlug.getRank(p).equals(RankType.OWNER)) {
                scoreboard.add(C.white + "Rank: " + RankUtil.Owner, 6);
            } else if (plugin.permPlug.getRank(p).equals(RankType.ADMIN)) {
                scoreboard.add(C.white + "Rank: " + RankUtil.Admin, 6);
            } else if (plugin.permPlug.getRank(p).equals(RankType.MOD)) {
                scoreboard.add(C.white + "Rank: " + RankUtil.Mod, 6);
            } else if (plugin.permPlug.getRank(p).equals(RankType.HELPER)) {
                scoreboard.add(C.white + "Rank: " + RankUtil.Helper, 6);
            } else if (plugin.permPlug.getRank(p).equals(RankType.MEMBER)) {
                scoreboard.add(C.white + "Rank: " + C.yellow + C.bold + "MEMBER", 6);
            }
            scoreboard.add(C.white + "Votes: " + C.green + "0", 5);
            scoreboard.add(" ", 4);
            scoreboard.add(C.white + "Lobby: " + C.green + "#1", 3);
            scoreboard.add(C.white + "Online: " + C.green + Bukkit.getOnlinePlayers().size(), 2);
            scoreboard.add(C.dgray + C.bold + C.strike + "---------------", 1);
            scoreboard.update();
            scoreboard.send(p);
        }
    }.runTaskTimer(plugin, 0, 3);
}`
@Fabb111

This comment has been minimized.

Copy link

@Fabb111 Fabb111 commented Jun 27, 2016

It doesn't work for multiple players, right? (I mean if someone has e.g. 200 Coins and then the scoreboard updates for a player with 500 coins, player 1 & 2 have 500 coins in the scoreboard)

@Cuthmf

This comment has been minimized.

Copy link

@Cuthmf Cuthmf commented Sep 27, 2016

I'm not getting update:

@eventhandler
public void wow(PlayerJoinEvent e) {
SimpleScoreboard ss = new SimpleScoreboard("WoW");
ss.add("Money: "+ a, 13);
ss.update();
ss.send(e.getPlayer());
new BukkitRunnable() {

        @Override
        public void run() {
            a = new Random().nextInt(900);
                ss.update();
                for (Player p : Bukkit.getServer().getOnlinePlayers()) {
                    ss.update();
                    ss.send(p);
                }


        }
    }.runTaskTimerAsynchronously(this, 20, 10);
@CodingAir

This comment has been minimized.

Copy link

@CodingAir CodingAir commented Dec 12, 2016

It works perfect on 1.8, thanks a lot 👍

I just wrote some new code in it ;)

@matt11matthew

This comment has been minimized.

Copy link

@matt11matthew matt11matthew commented Mar 5, 2018

Anyway I can get this to work for 1.7 with 40 lines I'm guessing using suffix and prefix?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.