Resource Creating Personalized Scoreboards

Discussion in 'Spigot Plugin Development' started by Lightcaster5, Feb 19, 2020.

  1. About
    Creating personalized scoreboards (per-player) can be difficult if not done correctly. This tutorial will show you how to not only create a scoreboard that displays the player's stats but also how to prevent flickering.

    Note: I prefer using the section sign (§) for color codes over ChatColor.COLOR, sorry if this bothers you!

    Code
    (All sections will be in a spoiler named according to their classes)
    Code (Java):
    public class Main extends JavaPlugin {

        @Override
        public void onEnable() {
            this.getServer().getPluginManager().registerEvents(new PlayerJoin(), this);
            this.getServer().getPluginManager().registerEvents(new PlayerLeave(), this);

            sbmanager.start();
        }

    }
    Code (Java):
    public class ScoreboardManager {

        private final MapHandler map = new MapHandler();
        private final Main plugin = new Main();

        public void createScoreboard(Player player) {
            Scoreboard scoreboard = Bukkit.getScoreboardManager().getNewScoreboard();
            Objective objective = scoreboard.registerNewObjective("§bTest", "dummy");
            objective.setDisplaySlot(DisplaySlot.SIDEBAR);

            map.addPlayerScoreboard(player, scoreboard);

            Score blankLine = objective.getScore("§0");
            blankLine.setScore(15);

            if (scoreboard.getTeam("name") == null) {
                scoreboard.registerNewTeam("name");
            }
            scoreboard.getTeam("name").addEntry("§7");
            scoreboard.getTeam("name").setPrefix("§fName: §7");
            scoreboard.getTeam("name").setSuffix(player.getName());
            objective.getScore("§7").setScore(14);

            if (scoreboard.getTeam("health") == null) {
                scoreboard.registerNewTeam("health");
            }
            scoreboard.getTeam("health").addEntry("§7§7");
            scoreboard.getTeam("health").setPrefix("§fHealth: §7");
            scoreboard.getTeam("health").setSuffix("" + player.getHealth());
            objective.getScore("§7§7").setScore(13);

            map.addOnlinePlayer(player);
            player.setScoreboard(scoreboard);
        }

        public void updateScoreboards() {
            for (Player player : Bukkit.getOnlinePlayers()) {

                Scoreboard scoreboard = map.getPlayerScoreboard(player);

                if (scoreboard.getTeam("name") == null) {
                    scoreboard.registerNewTeam("name");
                }
                scoreboard.getTeam("name").setPrefix("§fName: §7");
                scoreboard.getTeam("name").setSuffix(player.getName());

                if (scoreboard.getTeam("health") == null) {
                    scoreboard.registerNewTeam("health");
                }
                scoreboard.getTeam("health").setPrefix("§fHealth: §7");
                scoreboard.getTeam("health").setSuffix("" + player.getHealth());
            }
        }

        public void start() {
            new BukkitRunnable() {

                @Override
                public void run() {
                    updateScoreboards();
                }

            }.runTaskTimer(plugin, 0L, 5L);
        }

    }
    Code (Java):
    public class MapHandler {

        public HashMap<Player, Scoreboard> playerScoreboards = new HashMap<Player, Scoreboard>();

        public boolean checkPlayerScoreboard(Player player) {
            if (playerScoreboards.containsKey(player)) {
                return true;
            }
            return false;
        }

        public void addPlayerScoreboard(Player player, Scoreboard scoreboard) {
            if (checkPlayerScoreboard(player)) {
                playerScoreboards.remove(player);
            }
            playerScoreboards.put(player, scoreboard);

        }

        public void removePlayerScoreboard(Player player) {
            if (checkPlayerScoreboard(player)) {
                playerScoreboards.remove(player);
            }
        }

        public Scoreboard getPlayerScoreboard(Player player) {
            if (!checkPlayerScoreboard(player)) {
                Bukkit.getServer().getConsoleSender()
                        .sendMessage("§cThere was an error getting " + player.getName() + "'s scoreboard!");
                return null;
            }
            return playerScoreboards.get(player);
        }
       
    }
    Code (Java):
    public class PlayerJoain implements Listener {
       
        private final ScoreboardManager sb = new ScoreboardManager();
       
        @EventHandler
        public void playerJoin(PlayerJoinEvent event) {
            sb.createScoreboard(event.getPlayer());
        }

    }
    Code (Java):
    public class PlayerLeave implements Listener {
       
        private final MapHandler map = new MapHandler();

        @EventHandler
        public void playerLeave(PlayerQuitEvent event) {
            map.removePlayerScoreboard(event.getPlayer());
        }
       
    }

    Explanation

    In onEnable() we register the PlayerJoin listener to listen for when a player joins. The last line in onEnable() runs the method from the ScoreBoardManager class to start updating scoreboards every quarter of a second (5 ticks). When a player joins, a new scoreboard is created and the player along with the scoreboard is added to a hashmap for later use. When updating, we call the scoreboard from our hashmap using Bukkit#getOnlinePlayers() as the object. As we update, the teams aren't modified rather the prefixes and suffixes are to prevent flickering. When a player leaves, they are removed from the hashmap to prevent un-needed usage of the CPU as it must look through more entries in the hashmap. If you have any questions, feel free to reply below and I will do my best to provide help.

    Thanks for reading, I hope this helped.
    If it did, consider leaving a rating as they are much appreciated!
     
    #1 Lightcaster5, Feb 19, 2020
    Last edited: Feb 19, 2020
    • Useful Useful x 1
  2. Isn't it easier to give 1 scoreboard to all players than to create a scoreboard for each player?
    (PlayerJoinEvent)
    I have this fairly simple class created with the Netherboard api, which is updated, does not blink and has an animated title.
    Class:
    Code (Text):

    public class ScoreboardAdmin {
     
        private final Main plugin;
        public ScoreboardAdmin(Main plugin){
            this.plugin = plugin;
        }
     
        private int ini;
        private List<String> list;
        private BPlayerBoard board;

        public void createScoreboard(Player player){
            if(plugin.getConfig().getString("LobbyBrain.Scoreboard.enable").contains("true")) {
                BPlayerBoard gboard = Netherboard.instance().getBoard(player);
                if(gboard == null){
                    gboard = Netherboard.instance().createBoard(player, "MyScoreboard");
                }
                this.board = gboard;
                List<String> lista = plugin.getConfig().getStringList("LobbyBrain.Scoreboard.Scores");
                for(int i = 0;i < lista.size(); i++){
                    board.set(PlaceholderAPI.setPlaceholders(player, ChatColor.translateAlternateColorCodes('&', lista.get(i)))
                            , lista.size()-(i));
                }
                board.setName(PlaceholderAPI.setPlaceholders(player, ChatColor.translateAlternateColorCodes('&', list.get(ini))));
            }
        }
            public void RunnableScoreboard(){ //then the RunnableScoreboard method is called in onEnable ();
                List<String> lista2 = plugin.getConfig().getStringList("LobbyBrain.Scoreboard.Tittle");
                int ticks = plugin.getConfig().getInt("LobbyBrain.Scoreboard.ticks");
                this.list = lista2;
                BukkitScheduler sc = Bukkit.getServer().getScheduler();
                sc.scheduleSyncRepeatingTask(plugin, new Runnable(){
                    @Override
                    public void run(){
                    for(Player p : Bukkit.getOnlinePlayers()){
                        createScoreboard(p);
                    }
                    if(ini < list.size()-1){
                        ini++;
                    }else{
                        ini = 0;
                    }
                }
            }, 0, ticks);    
        }
    }
     
    And don't need so many classes and this gives 1 scoreboard to all players instead of creating 1 scoreboard per player.
    NetherboardAPI: https://github.com/MinusKube/Netherboard
     
    #2 Nico12, Feb 19, 2020
    Last edited: Feb 19, 2020
  3. This way of creating the scoreboards was to prevent the usage of an external API. Also, you cannot change the values to just a single scoreboard but display different content without editing packets.