1.12.2 Set player-specific scoreboard with global teams

Discussion in 'Spigot Plugin Development' started by Smikkel_Bakje, Jan 29, 2020.

  1. Hi there,

    I was working on a project and I stumbled upon a problem. I had to make a player-specific scoreboard with for example money, level, etc. with global teams (which I use for name-tag colors, which can be seen by everybody.) The problem is that a player can obviously only have 1 scoreboard, and teams are assigned to a scoreboard. How would one go about this?

    My current code:
    Code (Java):
    public HashMap<Player, Scoreboard> scoreboards = new HashMap<>();

    public void loadScoreboard(Player p) {
            Scoreboard sb = scoreboards.get(p);

            sb.getObjective(DisplaySlot.SIDEBAR).unregister();
            Objective obj = sb.registerNewObjective(p.getName(), "dummy");

            if (Stad.getbyWorld(p.getWorld()) == null) return;

            String k = Stad.getbyWorld(p.getWorld()).getKleur();
            MTPlayer mp = MTPlayer.fromPlayer(p);

            Score s1 = obj.getScore(ChatColor.GOLD + "");
            s1.setScore(13);

            Score s2 = obj.getScore(ChatColor.translateAlternateColorCodes('&', k + "Tijd:"));
            s2.setScore(12);

            SimpleDateFormat formatter = new SimpleDateFormat("HH:mm");
            Date date = new Date();

            Score s3 = obj.getScore(" " + formatter.format(date));
            s3.setScore(11);

            Score spacer3 = obj.getScore(ChatColor.DARK_AQUA + "");
            spacer3.setScore(10);

            Score s4 = obj.getScore(ChatColor.translateAlternateColorCodes('&', k + "Banksaldo:"));
            s4.setScore(9);

            Score s5 = obj.getScore(" € " + mp.getBanksaldo());
            s5.setScore(8);

            Score spacer2 = obj.getScore(ChatColor.GRAY + "");
            spacer2.setScore(7);

            Score s6 = obj.getScore(ChatColor.translateAlternateColorCodes('&', k + "Level:"));
            s6.setScore(6);

            Score s7 = obj.getScore(" " + mp.getLevel());
            s7.setScore(5);

            Score spacer1 = obj.getScore(ChatColor.RED + "");
            spacer1.setScore(4);

            Score s8 = obj.getScore(ChatColor.translateAlternateColorCodes('&', k + "Fitheid:"));
            s8.setScore(3);

            Score s9 = obj.getScore(" " + mp.getFitheid());
            s9.setScore(2);

            Score spacer = obj.getScore("");
            spacer.setScore(1);

            Score s11 = obj.getScore(ChatColor.translateAlternateColorCodes('&', k + "play.classicminetopia.nl"));
            s11.setScore(0);

        }

        private void initScoreboard(Player p) {
            ScoreboardManager manager = Bukkit.getScoreboardManager();
            scoreboards.put(p, manager.getNewScoreboard());

            loadScoreboard(p);
        }

        private void initNametags() {
            Character[] validColors = new Character[]{'1','2','3','4','5','6','7','8','9','0','a','b','c','d','e','f'};

            for (Character color : validColors) {
                //Add teams to a scoreboard
            }
        }
    Will be looking forward to your suggestions!
    Smikkel_Bakje
     
  2. How about sending the scoreboard to the player as a packet instead? afaik that wouldnt mess with the teams but im not 100 % sure
     
    • Useful Useful x 1
  3. Yup, also thought about that. Do you mind giving me an example of what packets I should use?
     
  4. You can, instead of sending scoreboard through packets, send the names through packet. There are quite a lot of solutions, but I don’t know how to use them. I use MultiLineAPI for that. It was meant for another use, but it works fine.
     
  5. I've tried that, but that messes with tab complete and changes the normal name of the player, too. I'd rather use teams.
     
  6. I said, use MultiLineAPI.
    It’s pretty difficult to figure out how to use it in general and in the way that you need, but it will be good. You can also have more lines in the name.
     
  7. Well, I got it working using ProtocolLib. But there's still one (minor) issue. The objective display-name is also global. I'm trying to change the packet for the displayname to display the world the player is in but when I do that the scoreboard doesn't show up anymore, which seems logical but I can't find a way to bypass that.

    My current code

    Main class:
    Code (Java):

    public void updateScoreboard() {
        Objective obj = sb.getObjective(DisplaySlot.SIDEBAR);
        obj.setDisplaySlot(DisplaySlot.SIDEBAR);

        Score s2 = obj.getScore("%tijdkleur%");
        s2.setScore(12);

        Score s3 = obj.getScore("%tijd%");
        s3.setScore(11);

        Score spacer3 = obj.getScore(ChatColor.DARK_AQUA + "");
        spacer3.setScore(10);

        Score s4 = obj.getScore("%bankkleur%");
        s4.setScore(9);

        Score s5 = obj.getScore("%bank%");
        s5.setScore(8);

        Score spacer2 = obj.getScore(ChatColor.GRAY + "");
        spacer2.setScore(7);

        Score s6 = obj.getScore("%levelkleur%");
        s6.setScore(6);

        Score s7 = obj.getScore("%level%");
        s7.setScore(5);

        Score spacer1 = obj.getScore(ChatColor.RED + "");
        spacer1.setScore(4);

        Score s8 = obj.getScore("%fitheidkleur%");
        s8.setScore(3);

        Score s9 = obj.getScore("%fitheid%");
        s9.setScore(2);

        Score spacer = obj.getScore("");
        spacer.setScore(1);

        Score s11 = obj.getScore("%ipkleur%");
        s11.setScore(0);
    }

    private void initScoreboard() {
        sb = Bukkit.getScoreboardManager().getNewScoreboard();
        Objective obj = sb.registerNewObjective("CLASSICMINETOPIA", "dummy");
        obj.setDisplaySlot(DisplaySlot.SIDEBAR);
    }

    private void initNametags() {
        Character[] validColors = new Character[]{'1','2','3','4','5','6','7','8','9','0','a','b','c','d','e','f'};

        for (Character color : validColors) {
            Team t = sb.registerNewTeam(color.toString());
            t.setPrefix(ChatColor.translateAlternateColorCodes('&', "&" + color));
        }

    }
     
    MTScoreboard class (to listen for outgoing scoreboard packets):

    Code (Java):

    public class MTScoreboard {

        public static void listen() {
            ProtocolManager manager = ClassicMinetopia.get().manager;

            manager.addPacketListener(
                    new PacketAdapter(ClassicMinetopia.get(), ListenerPriority.HIGH, PacketType.Play.Server.SCOREBOARD_SCORE) {
                        @Override
                        public void onPacketSending(PacketEvent event) {
                            WrapperPlayServerScoreboardScore wbs = new WrapperPlayServerScoreboardScore(event.getPacket());

                            if (wbs.getObjectiveName().equals(ChatColor.translateAlternateColorCodes('&', "&l" + event.getPlayer().getWorld().getName().toUpperCase())) || wbs.getObjectiveName().equals("CLASSICMINETOPIA")) {
                                MTPlayer mp = MTPlayer.fromPlayer(event.getPlayer());
                                Stad s = Stad.getbyWorld(event.getPlayer().getWorld());

                                if (s == null) return;

                                String k = s.getKleur();

                                switch (wbs.getScoreName()) {
                                    case "%tijdkleur%":
                                        wbs.setScoreName(ChatColor.translateAlternateColorCodes('&', k + "Tijd:"));
                                        break;
                                    case "%tijd%":
                                        SimpleDateFormat formatter = new SimpleDateFormat("HH:mm");
                                        Date date = new Date();
                                        wbs.setScoreName(" " + formatter.format(date));
                                        break;
                                    case "%levelkleur%":
                                        wbs.setScoreName(ChatColor.translateAlternateColorCodes('&', k + "Level:"));
                                        break;
                                    case "%level%":
                                        wbs.setScoreName(" " + mp.getLevel());
                                        break;
                                    case "%bankkleur%":
                                        wbs.setScoreName(ChatColor.translateAlternateColorCodes('&', k + "Banksaldo:"));
                                        break;
                                    case "%bank%":
                                        wbs.setScoreName(" € " + mp.getBanksaldo());
                                        break;
                                    case "%fitheidkleur%":
                                        wbs.setScoreName(ChatColor.translateAlternateColorCodes('&', k + "Fitheid:"));
                                        break;
                                    case "%fitheid%":
                                        wbs.setScoreName(" " + mp.getFitheid() + " / 100");
                                        break;
                                    case "%ipkleur%":
                                        wbs.setScoreName(ChatColor.translateAlternateColorCodes('&', k + "play.classicminetopia.nl"));
                                }
                            }
                        }
                    }
            );

            manager.addPacketListener(
                    new PacketAdapter(ClassicMinetopia.get(), ListenerPriority.HIGH, PacketType.Play.Server.SCOREBOARD_DISPLAY_OBJECTIVE) {
                        @Override
                        public void onPacketSending(PacketEvent event) {
                            WrapperPlayServerScoreboardDisplayObjective wdo = new WrapperPlayServerScoreboardDisplayObjective(event.getPacket());

                            if (wdo.getPosition() == 1) {
                                Stad s = Stad.getbyWorld(event.getPlayer().getWorld());

                                if (s == null) return;

                                wdo.setScoreName(ChatColor.translateAlternateColorCodes('&', "&l" + s.getWereld().getName().toUpperCase()));
                            }
                        }
                    }
            );

        }

    }
     
    Both listeners run when I set the scoreboard for a player. When I remove the SCOREBOARD_DISPLAY_OBJECTIVE listener, the code runs fine but the displayname of the scoreboards stays at CLASSICMINETOPIA, but when I enable it the scoreboard vanishes.

    EDIT: Just tried it out with an alt, scoreboard still isn't global... Makes sense because the scores are only updated once for the entire scoreboard. Hoping to get some answers

    EDIT 2: Just figured out that the SCOREBOARD_DISPLAY_OBJECTIVE packet runs for each player so I think I have to do something with that. Not too sure how to change scores with that packet though. Will be looking further to a solution
     
    #7 Smikkel_Bakje, Jan 30, 2020
    Last edited: Jan 30, 2020
  8. I've changed my code a bit, but it's still not working. I deleted the SCOREBOARD_SCORE packet listener and kept the SCOREBOARD_DISPLAY_OBJECTIVE, as it updates every scoreboard refreshes for each player. I create a new SCOREBOARD_SCORE packet inside the SCOREBOARD_DISPLAY_OBJECTIVE listener every time it's ran, which I alter the scores with like so:


    Code (Java):
    manager.addPacketListener(
                    new PacketAdapter(ClassicMinetopia.get(), ListenerPriority.HIGH, PacketType.Play.Server.SCOREBOARD_DISPLAY_OBJECTIVE) {
                        @Override
                        public void onPacketSending(PacketEvent event) {
                            WrapperPlayServerScoreboardDisplayObjective wdo = new WrapperPlayServerScoreboardDisplayObjective(event.getPacket());

                            if (wdo.getPosition() == 1) {
                                Bukkit.getLogger().info("ran");
                                Stad s = Stad.getbyWorld(event.getPlayer().getWorld());

                                if (s == null) return;

                                String k = s.getKleur();

                                wdo.setScoreName(ChatColor.translateAlternateColorCodes('&', "&l" + s.getWereld().getName().toUpperCase()));

                                PacketContainer changeScorePacket = manager.createPacket(PacketType.Play.Server.SCOREBOARD_SCORE); {
                                    WrapperPlayServerScoreboardScore wss = new WrapperPlayServerScoreboardScore(changeScorePacket);
                                    wss.setObjectiveName(wdo.getScoreName());

                                    String[] scores = {"%tijdkleur%", "%tijd%", "%levelkleur%", "%level%", "%bankkleur%", "%bank%", "%fitheidkleur%", "%fitheid%", "%ipkleur%"};

                                    wss.setScoreboardAction(EnumWrappers.ScoreboardAction.CHANGE);

                                    MTPlayer mp = MTPlayer.fromPlayer(event.getPlayer());

                                    for (String score : scores) {
                                        switch (score) {
                                            case "%tijdkleur%":
                                                wss.setScoreName(ChatColor.translateAlternateColorCodes('&', k + "Tijd:"));
                                                wss.setValue(12);
                                                break;
                                            case "%tijd%":
                                                SimpleDateFormat formatter = new SimpleDateFormat("HH:mm");
                                                Date date = new Date();
                                                wss.setScoreName(" " + formatter.format(date));
                                                wss.setValue(11);
                                                break;
                                            case "%levelkleur%":
                                                wss.setScoreName(ChatColor.translateAlternateColorCodes('&', k + "Level:"));
                                                wss.setValue(6);
                                                break;
                                            case "%level%":
    //                                            wss.setScoreName(" " + mp.getLevel());
                                                wss.setValue(5);
                                                break;
                                            case "%bankkleur%":
                                                wss.setScoreName(ChatColor.translateAlternateColorCodes('&', k + "Banksaldo:"));
                                                wss.setValue(9);
                                                break;
                                            case "%bank%":
    //                                            wss.setScoreName(" € " + mp.getBanksaldo());
                                                wss.setValue(8);
                                                break;
                                            case "%fitheidkleur%":
                                                wss.setScoreName(ChatColor.translateAlternateColorCodes('&', k + "Fitheid:"));
                                                wss.setValue(3);
                                            case "%fitheid%":
    //                                            wss.setScoreName(" " + mp.getFitheid() + " / 100");
                                                wss.setValue(2);
                                                break;
                                            case "%ipkleur%":
                                                wss.setScoreName(ChatColor.translateAlternateColorCodes('&', k + "play.classicminetopia.nl"));
                                                wss.setValue(0);
                                        }

                                        try {
                                            manager.sendServerPacket(event.getPlayer(), changeScorePacket);
                                        } catch (InvocationTargetException e) {
                                            e.printStackTrace();
                                        }

                                    }



                                }


                            }
                        }
                    }
            );
    There're 2 problems now:
    1: At the documented lines, the console throws an error when the scoreboard is first initialized for the player, after that this runs fine.
    2: The scoreboard doens't show up at all, even when I document the lines that the errors occur at.

    EDIT:
    Made some progress, scoreboard still isn't showing up though... Current code:

    Code (Java):
    public class MTScoreboard {

        public static void send(Player p) {
            ProtocolManager manager = ClassicMinetopia.get().manager;

            Stad s = Stad.getbyWorld(p.getWorld());

            if (s == null) return;

            String k = s.getKleur();
            MTPlayer mp = MTPlayer.fromPlayer(p);

            PacketContainer removeObjective = manager.createPacket(PacketType.Play.Server.SCOREBOARD_OBJECTIVE); {
                WrapperPlayServerScoreboardObjective wso = new WrapperPlayServerScoreboardObjective(removeObjective);
                wso.setName("CMScoreboard");
                wso.setMode(1);

                wso.sendPacket(p);
            }

            PacketContainer createObjective = manager.createPacket(PacketType.Play.Server.SCOREBOARD_OBJECTIVE); {
                WrapperPlayServerScoreboardObjective wso = new WrapperPlayServerScoreboardObjective(createObjective);
                wso.setName("CMScoreboard");
                wso.setMode(0);
                wso.setDisplayName(WrappedChatComponent.fromText(ChatColor.translateAlternateColorCodes('&', "&l" + s.getWereld().getName())));

                PacketContainer sendObjective = manager.createPacket(PacketType.Play.Server.SCOREBOARD_DISPLAY_OBJECTIVE); {
                    WrapperPlayServerScoreboardDisplayObjective wdo = new WrapperPlayServerScoreboardDisplayObjective(sendObjective);

                    wdo.setScoreName("CMScoreboard");

                    wso.sendPacket(p);
                    wdo.sendPacket(p);

                    for (int i = 0; i <= 12; i++) {
                        PacketContainer sendScore = manager.createPacket(PacketType.Play.Server.SCOREBOARD_SCORE); {
                            WrapperPlayServerScoreboardScore wss = new WrapperPlayServerScoreboardScore(sendScore);
                            wss.setObjectiveName(wdo.getScoreName());

                            wss.setValue(i);
                            wss.setScoreboardAction(EnumWrappers.ScoreboardAction.CHANGE);

                            switch (i) {
                                case 0:
                                    //wdo.setScoreName(ChatColor.translateAlternateColorCodes('&', k + "play.classicminetopia.nl"));
                                    break;
                                case 1:
                                    wdo.setScoreName(ChatColor.RED + "");
                                    break;
                                case 2:
                                    wdo.setScoreName("lol " + mp.getFitheid());
                                    break;
                                case 3:
                                    wdo.setScoreName(ChatColor.translateAlternateColorCodes('&', k + "Fitheid:"));
                                    break;
                                case 4:
                                    wdo.setScoreName(ChatColor.BLUE + "");
                                    break;
                                case 5:
                                    wdo.setScoreName(" " + mp.getLevel());
                                    break;
                                case 6:
                                    wdo.setScoreName(ChatColor.translateAlternateColorCodes('&', k + "Level:"));
                                    break;
                                case 7:
                                    wdo.setScoreName(ChatColor.GRAY + "");
                                    break;
                                case 8:
                                    wdo.setScoreName(" € " + mp.getBanksaldo());
                                    break;
                                case 9:
                                    wdo.setScoreName(ChatColor.translateAlternateColorCodes('&', k + "Banksaldo:"));
                                    break;
                                case 10:
                                    wdo.setScoreName(ChatColor.DARK_AQUA + "");
                                    break;
                                case 11:
                                    SimpleDateFormat formatter = new SimpleDateFormat("HH:mm");
                                    Date date = new Date();
                                    wdo.setScoreName(" " + formatter.format(date));
                                    break;
                                case 12:
                                    wdo.setScoreName(ChatColor.translateAlternateColorCodes('&', k + "Tijd:"));
                                    break;
                            }

                            wss.sendPacket(p);

                        }
                    }
                }
            }
        }
    Removed the listener completely and I am now sending the scoreboard via packets. Idk why but the scoreboard isn't showing up.
     
    #8 Smikkel_Bakje, Jan 30, 2020
    Last edited: Jan 30, 2020
    • Like Like x 1
  9. Hey Smikkel_Bakje,

    I am having quite EXACTLY the same problem right now in my state of development. I know this thread is 11 months old but maybe you have enough motivation to answer with how you have done it at the end. No need to elaborate much, just a quick explanation what is possible or which additional assets you used at the end would be much appreciated!
     
  10. Hi there, just had a quick look back at the code. No packets were needed afaik, you have to create a new scoreboard for every player and create all the teams needed per scoreboard. Then you add all players to their respective teams per scoreboard.

    So:
    1. Player needs scoreboard -> Create new scoreboard and create teams for scoreboard and store the scoreboard somewhere
    2. Player needs color -> Add player to the right team in every scoreboard for every player using the scoreboards you stored.

    It's been a long time since I've worked on this so I hope I got it right.

    Good luck!