[1.5 SCOREBOARDS] Change player nametag color

Discussion in 'Resources' started by wacossusca34, Mar 15, 2013.

Thread Status:
Not open for further replies.
  1. Offline

    wacossusca34

    UPDATE 2: There's now an API that uses scoreboard teams that I created for NametagEdit. If you want to use these features, I recommend using that.

    UPDATE 1: For any of you still gazing at this thread, there's a plugin for this with updated code (and it's open source). It's called NametagEdit.


    I won't be updating this thread anymore, so just check out the source on that plugin if you're still curious.

    So, people have been searching all around for ways to change a player's nametag color through a Bukkit plugin, without a client mod. Sure you could just replace the player entity with another with the formatting codes, but then you sacrifice the player skin! Up until the 1.5 update, you had to send a packet to correctly update a player's nametag. This method leaves the actual nametag of the player enact while still giving it prefixes and suffixes.

    With the scoreboard features introduced in the 1.5 udpate, players can join certain 'teams' which can be assigned certain prefixes and suffixes. These can be formatting codes for colors, italics, bold fonts, and even that 'k' formatting code. If you really wanted to, you could even include some sort of word in the prefix.

    Here's how to setup a team and assign it to a player in Bukkit (outdated):

    Code:
     
        // Sets the team for the specified player
        public static void prefix(String player, String name, org.bukkit.World world) {
            World mcWorld = ((CraftWorld) world).getHandle();
            mcWorld.getScoreboard().a(player, mcWorld.getScoreboard().e(name));
        }
     
        // Sets up the teams for setting player prefixes (make sure to call this on onEnable()! )
        public static void setup() {
     
            org.bukkit.World world = Bukkit.getWorld("world");
     
            World mcWorld = ((CraftWorld) world).getHandle();
            mcWorld.getScoreboard().f("blue").b("§9");
            mcWorld.getScoreboard().f("red").b("§c");
            mcWorld.getScoreboard().f("purple").b("§5");
            mcWorld.getScoreboard().f("clear").b("§f");
        }
    
    Here's a class that dynamically creates teams when a prefix / suffix change is requested:

    Code:java
    1. package ca.wacos;
    2. package ca.wacos;
    3.  
    4. import java.util.ArrayList;
    5. import java.util.LinkedHashMap;
    6. import java.util.List;
    7.  
    8. import net.minecraft.server.v1_5_R2.ScoreboardTeam;
    9. import net.minecraft.server.v1_5_R2.World;
    10.  
    11. import org.bukkit.Bukkit;
    12. import org.bukkit.craftbukkit.v1_5_R2.CraftWorld;
    13.  
    14. public class ScoreboardManager {
    15. static List<Integer> list = new ArrayList<Integer>();
    16.  
    17. @SuppressWarnings("unchecked")
    18. public static void load() {
    19. World mcWorld = ((CraftWorld) Bukkit.getWorlds().get(0)).getHandle();
    20. for (String str : (String[]) mcWorld.getScoreboard().getTeamNames().toArray(new String[mcWorld.getScoreboard().getTeamNames().size()])) {
    21. int entry = -1;
    22. try {
    23. entry = Integer.parseInt(str);
    24. }
    25. catch (Exception e) {};
    26. if (entry != -1) {
    27. list.add(entry);
    28. }
    29. }
    30. }
    31.  
    32. public static void update(String player, String prefix, String suffix) {
    33.  
    34. World mcWorld = ((CraftWorld) Bukkit.getWorlds().get(0)).getHandle();
    35.  
    36. if (prefix == null || prefix.isEmpty())
    37. prefix = getPrefix(player, mcWorld);
    38. if (suffix == null || suffix.isEmpty())
    39. suffix = getSuffix(player, mcWorld);
    40.  
    41. ScoreboardTeam s = get(prefix, suffix);
    42.  
    43. mcWorld.getScoreboard().addPlayerToTeam(player, s);
    44.  
    45. }
    46. public static void overlap(String player, String prefix, String suffix) {
    47.  
    48. World mcWorld = ((CraftWorld) Bukkit.getWorlds().get(0)).getHandle();
    49.  
    50. if (prefix == null)
    51. prefix = "";
    52. if (suffix == null)
    53. suffix = "";
    54.  
    55. ScoreboardTeam s = get(prefix, suffix);
    56.  
    57. mcWorld.getScoreboard().addPlayerToTeam(player, s);
    58.  
    59. }
    60. public static void clear(String player) {
    61.  
    62. World mcWorld = ((CraftWorld) Bukkit.getWorlds().get(0)).getHandle();
    63.  
    64. ScoreboardTeam s = getTeam(player, mcWorld);
    65.  
    66. if (s != null)
    67. mcWorld.getScoreboard().removePlayerFromTeam(player, s);
    68.  
    69. }
    70.  
    71. @SuppressWarnings("unchecked")
    72. private static String getPrefix(String player, World mcWorld) {
    73. for (ScoreboardTeam team : (ScoreboardTeam[]) mcWorld.getScoreboard().getTeams().toArray(new ScoreboardTeam[mcWorld.getScoreboard().getTeams().size()])) {
    74. if (team.getPlayerNameSet().contains(player))
    75. return team.getPrefix();
    76. }
    77. return "";
    78. }
    79. @SuppressWarnings("unchecked")
    80. private static String getSuffix(String player, World mcWorld) {
    81. for (ScoreboardTeam team : (ScoreboardTeam[]) mcWorld.getScoreboard().getTeams().toArray(new ScoreboardTeam[mcWorld.getScoreboard().getTeams().size()])) {
    82. if (team.getPlayerNameSet().contains(player))
    83. return team.getSuffix();
    84. }
    85. return "";
    86. }
    87. @SuppressWarnings("unchecked")
    88. private static ScoreboardTeam getTeam(String player, World mcWorld) {
    89. for (ScoreboardTeam team : (ScoreboardTeam[]) mcWorld.getScoreboard().getTeams().toArray(new ScoreboardTeam[mcWorld.getScoreboard().getTeams().size()])) {
    90. if (team.getPlayerNameSet().contains(player))
    91. return team;
    92. }
    93. return null;
    94. }
    95.  
    96. private static ScoreboardTeam declareTeam(World mcWorld, String name, String prefix, String suffix) {
    97. if (mcWorld.getScoreboard().getTeam(name) != null) {
    98. mcWorld.getScoreboard().removeTeam(mcWorld.getScoreboard().getTeam(name));
    99. }
    100. mcWorld.getScoreboard().createTeam(name);
    101. mcWorld.getScoreboard().getTeam(name).setPrefix(prefix);
    102. mcWorld.getScoreboard().getTeam(name).setSuffix(suffix);
    103. return mcWorld.getScoreboard().getTeam(name);
    104. }
    105.  
    106. private static ScoreboardTeam get(String prefix, String suffix) {
    107.  
    108. World mcWorld = ((CraftWorld) Bukkit.getWorlds().get(0)).getHandle();
    109.  
    110. update(mcWorld);
    111.  
    112. for (int t : list.toArray(new Integer[list.size()])) {
    113.  
    114. if (mcWorld.getScoreboard().getTeam("" + t) != null) {
    115. ScoreboardTeam s = mcWorld.getScoreboard().getTeam("" + t);
    116. if (s.getSuffix().equals(suffix) && s.getPrefix().equals(prefix)) {
    117. return s;
    118. }
    119. }
    120. }
    121. return declareTeam(mcWorld, nextName() + "", prefix, suffix);
    122.  
    123. }
    124. private static int nextName() {
    125. int at = 0;
    126. boolean cont = true;
    127. while (cont) {
    128. cont = false;
    129. for (int t : list.toArray(new Integer[list.size()])) {
    130. if (t == at) {
    131. at++;
    132. cont = true;
    133. }
    134.  
    135. }
    136. }
    137. list.add(at);
    138. return at;
    139. }
    140. @SuppressWarnings("unchecked")
    141. private static void update(World mcWorld) {
    142.  
    143. for (ScoreboardTeam team : (ScoreboardTeam[]) mcWorld.getScoreboard().getTeams().toArray(new ScoreboardTeam[mcWorld.getScoreboard().getTeams().size()])) {
    144. int entry = -1;
    145. try {
    146. entry = Integer.parseInt(team.getName());
    147. }
    148. catch (Exception e) {};
    149. if (entry != -1) {
    150. if (team.getPlayerNameSet().size() == 0) {
    151. mcWorld.getScoreboard().removeTeam(team);
    152. list.remove(new Integer(entry));
    153. }
    154. }
    155. }
    156. }
    157. }
    158.  
    159.  


    As you can see, I've setup teams accordingly to their colors to be assigned to players.

    Just make sure to setup your teams prior to assigning players to them, and you're good to go!

    As far as I know, I think I'm the first to point this out (as the Bukkit 1.5 development build was released today). I might whip up a simple plugin for server owners to use soon.

    Cheers!
     
  2. Offline

    chasechocolate

    Cool, thanks!
     
  3. Offline

    1SmallVille1

    or you could just use player.setDisplayName(ChatColor.BLUE + player.getName())
     
  4. Offline

    wacossusca34

    :confused:

    Maybe read a little more closely? This has to do with player's nametags above their heads. This was impossible to do without losing the player skins prior to the 1.5 udpate, and now, if you manipulate the scoreboard teams, you can add prefixes and suffixes.

    This has nothing to do with the game chat.

    On another note, I have a working plugin for this!

    http://dev.bukkit.org/server-mods/nametagedit/

    If you don't want to wait for it to get approved, here's a direct download:

    http://wacos.ca/downloads/NametagEdit.jar

    Refer to the bukkit dev project for documentation. And for developers, here's the key class that dynamically handles teams and assigns players to them:


    Code:java
    1. package ca.wacos;
    2.  
    3. import java.util.ArrayList;
    4. import java.util.List;
    5.  
    6. import net.minecraft.server.v1_5_R1.ScoreboardTeam;
    7. import net.minecraft.server.v1_5_R1.World;
    8.  
    9. import org.bukkit.Bukkit;
    10. import org.bukkit.craftbukkit.v1_5_R1.CraftWorld;
    11.  
    12. public class ScoreboardManager {
    13. static List<Integer> list = new ArrayList<Integer>();
    14.  
    15. @SuppressWarnings("unchecked")
    16. public static void load() {
    17. World mcWorld = ((CraftWorld) Bukkit.getWorlds().get(0)).getHandle();
    18. for (String str : (String[]) mcWorld.getScoreboard().f().toArray(new String[mcWorld.getScoreboard().f().size()])) {
    19. int entry = -1;
    20. try {
    21. entry = Integer.parseInt(str);
    22. }
    23. catch (Exception e) {};
    24. if (entry != -1) {
    25. list.add(entry);
    26. }
    27. }
    28. }
    29.  
    30. public static void update(String player, String prefix, String suffix) {
    31.  
    32. World mcWorld = ((CraftWorld) Bukkit.getWorlds().get(0)).getHandle();
    33.  
    34. if (prefix == null || prefix.isEmpty())
    35. prefix = getPrefix(player, mcWorld);
    36. if (suffix == null || suffix.isEmpty())
    37. suffix = getSuffix(player, mcWorld);
    38.  
    39. ScoreboardTeam s = get(prefix, suffix);
    40.  
    41. mcWorld.getScoreboard().a(player, s);
    42.  
    43. }
    44. public static void clear(String player) {
    45.  
    46. World mcWorld = ((CraftWorld) Bukkit.getWorlds().get(0)).getHandle();
    47.  
    48. ScoreboardTeam s = getTeam(player, mcWorld);
    49.  
    50. if (s != null)
    51. mcWorld.getScoreboard().b(player, s);
    52.  
    53. }
    54.  
    55. @SuppressWarnings("unchecked")
    56. private static String getPrefix(String player, World mcWorld) {
    57. for (ScoreboardTeam team : (ScoreboardTeam[]) mcWorld.getScoreboard().g().toArray(new ScoreboardTeam[mcWorld.getScoreboard().f().size()])) {
    58. if (team.d().contains(player))
    59. return team.e();
    60. }
    61. return "";
    62. }
    63. @SuppressWarnings("unchecked")
    64. private static String getSuffix(String player, World mcWorld) {
    65. for (ScoreboardTeam team : (ScoreboardTeam[]) mcWorld.getScoreboard().g().toArray(new ScoreboardTeam[mcWorld.getScoreboard().f().size()])) {
    66. if (team.d().contains(player))
    67. return team.f();
    68. }
    69. return "";
    70. }
    71. @SuppressWarnings("unchecked")
    72. private static ScoreboardTeam getTeam(String player, World mcWorld) {
    73. for (ScoreboardTeam team : (ScoreboardTeam[]) mcWorld.getScoreboard().g().toArray(new ScoreboardTeam[mcWorld.getScoreboard().f().size()])) {
    74. if (team.d().contains(player))
    75. return team;
    76. }
    77. return null;
    78. }
    79.  
    80. private static ScoreboardTeam declareTeam(World mcWorld, String name, String prefix, String suffix) {
    81. if (mcWorld.getScoreboard().e(name) != null) {
    82. mcWorld.getScoreboard().d(mcWorld.getScoreboard().e(name));
    83. }
    84. mcWorld.getScoreboard().f(name);
    85. mcWorld.getScoreboard().e(name).b(prefix);
    86. mcWorld.getScoreboard().e(name).c(suffix);
    87. return mcWorld.getScoreboard().e(name);
    88. }
    89.  
    90. private static ScoreboardTeam get(String prefix, String suffix) {
    91.  
    92. World mcWorld = ((CraftWorld) Bukkit.getWorlds().get(0)).getHandle();
    93.  
    94. update(mcWorld);
    95.  
    96. for (int t : list.toArray(new Integer[list.size()])) {
    97.  
    98. if (mcWorld.getScoreboard().e("" + t) != null) {
    99. ScoreboardTeam s = mcWorld.getScoreboard().e("" + t);
    100. if (s.f().equals(suffix) && s.e().equals(prefix)) {
    101. return s;
    102. }
    103. }
    104. }
    105. return declareTeam(mcWorld, nextName() + "", prefix, suffix);
    106.  
    107. }
    108. private static int nextName() {
    109. int at = 0;
    110. boolean cont = true;
    111. while (cont) {
    112. cont = false;
    113. for (int t : list.toArray(new Integer[list.size()])) {
    114. if (t == at) {
    115. at++;
    116. cont = true;
    117. }
    118.  
    119. }
    120. }
    121. list.add(at);
    122. return at;
    123. }
    124. @SuppressWarnings("unchecked")
    125. private static void update(World mcWorld) {
    126.  
    127. for (ScoreboardTeam team : (ScoreboardTeam[]) mcWorld.getScoreboard().g().toArray(new ScoreboardTeam[mcWorld.getScoreboard().f().size()])) {
    128. int entry = -1;
    129. try {
    130. entry = Integer.parseInt(team.c());
    131. }
    132. catch (Exception e) {};
    133. if (entry != -1) {
    134. if (team.d().size() == 0) {
    135. mcWorld.getScoreboard().d(team);
    136. list.remove(new Integer(entry));
    137. }
    138. }
    139. }
    140. }
    141. }
    142.  



    To be honest, it's pretty small. You should be able to use this in any of your plugins.

    Enjoy!

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 31, 2016
  5. Offline

    tomash9966

    Thanks, i'll use it in my guild plugin :3
     
  6. Offline

    chasechocolate

    wacossusca34 It was possible, it just wasn't as simple as it is now. You could use Packet20NamedEntitySpawn (check out TagAPI).
     
  7. Offline

    XDemonic25

    I thought TagAPI wouldnt work if ur name was longer than 15 characters (i think 15 or 13?)
     
  8. Offline

    wacossusca34

    It's 16. And this plugin has a 16 character limit for each prefix and suffix. That means you can add a total of 32 characters to your name with this.

    The 16 character limit is placed by the minecraft client. If you go beyond this on the server end, the client disconnects.

    And chasechocolate , TagAPI changes the name of the actual entity and updates it for all clients, but this actually adds prefixes and suffixes without changing the player name. I guess you could consider this 'safer' in a way.

    I'm curious to see what this would do in conjunction with TagAPI, as you could have a 48 character name with it!
     
  9. Offline

    Serilum

    Thanks, just managed to use your code instead of TagAPI! It's a lot more resource friendly :)
     
  10. Offline

    mbaxter ʇıʞʞnq ɐ sɐɥ ı

    Wat.

    Also, this code is not multi-plugin friendly in any way. TagAPI (sadly. *shakes fist at Dinnerbone*) is still the friendlier way of handling it. Editing the entire scoreboard just for name tags means no other plugin can touch scoreboard stuff.
     
  11. Offline

    Comphenix

    As players can only belong to a single team, it is inevitable. Even if you implement an event system similar to TagAPI and encourage plugins to use it, it may still break plugins that utilize different aspects of the scoreboard feature, like the invisibility toggle or the objectives themselves. It will also interact poorly with any custom maps out there.

    But the fundamental problem is the scoreboard feature itself, especially how it was implemented on the client-side. Ideally, the client should be a fairly "dumb" terminal and leave as much processing and decision-making as possible off to the server, with certain constraints of course (bandwidth and latency comes to mind).

    For instance, it would probably have been a good idea to split up the prefix/suffix aspect and the scoreboard into two distinct features, instead of coupling it directly with the team in the "team" packet. The server would then have constructed the display name by combining the prefix and suffix (or however it wanted), and sending the resulting string directly in a different packet. The client shouldn't have to be limited by the "single team per player" on this front, even if the server typically would need to as part of a streamlined command interface.

    It also seems that most of the scoreboard information is synchronized with the client - objective, team information and score - even down to whether or not a team is friendly fire. Maybe that makes it easier client-side mods to interpret the result and decide how to display the information, but it doesn't leave a whole lot up for the server in terms of flexibility. If the server instead simply sent strings to generic GUI displays, it would have been easier to customize in plugins.

    I don't necessarily disagree with sending more semantic data - i.e. the actual team roster and score objectives instead of raw text - but the server could have a way of suggesting how to format and display them.
     
  12. Offline

    wacossusca34

    Well, this isn't an API, and I have no argument on which is better to use.

    However, other plugins can touch scoreboard information. This just creates teams with only numbers as their names, so any teams that contain any other symbols or letters are left out by this programming.

    In fact, you don't necessarily even have to change the scoreboard data dragged along by the main world; you could create your own to send to the client.

    I do plan on actually making this an API though, after I finish up with my current project.
     
  13. Offline

    gcflames5

    Trying to readjust this to the English, i'm just stuck on .d()
     
  14. Offline

    Hoolean

    TagAPI is definitely a nicer way to handle this :)

    *pats mbaxter on the back*
     
  15. Offline

    Darkblader

    @wacossusca34

    Thanks a lot for the code! :)
    But it seems to be broken with craftbukkit 1.5.1.
    Any fix for this?
     
  16. Offline

    CreeperShift

    I was unaware that TagAPI was able to add 16 characters before and after the name without changing the skin.
     
    Waffletastic likes this.
  17. Offline

    lol768

    It's not. It can add formatting codes at the expense of two characters per formatting code without affecting skins.
     
  18. Offline

    CreeperShift

    I forgot to add the sarcasm tags, here you go, have em :))) [sarcasm] [/sarcasm]

    That was basically what I was trying to say with my post. Sure TagAPI is better coded and works perfectly but the point of this thread isn't to replace TagAPI but more to utilize what's new in 1.5 and not usable in TagAPI right now.
     
  19. Offline

    lol768

    Sorry, my sarcasm detection must have failed :p

    I'd like a Bukkit API for this. I believe a few people are working on one, which would avoid the need to use NMS/OBC.
     
  20. Offline

    chasechocolate

    Yeah, hopefully it comes in the next stable release of Bukkit 1.5.
     
  21. Offline

    Hoolean

    Missed that bit :p
     
  22. Offline

    CreeperShift

    Yeah I'm waiting for the bukkit API aswell, lets hope they finish soon :)
    I'm eager to see bigger plugins like factions and towny make use of the new scoreboards as that could be really neat :3
     
  23. Offline

    wacossusca34

    Post updated for 1.5.1, updated methods in the ScoreboardManager class in the post.

    My plugin has had numerous edits so that you can assign prefixes and suffixes to permission nodes, so that you can give a whole group of users a prefix / suffix. A few servers are making pretty good use of this now!

    I'm planning on still expanding on this though, possibly adding an API for my plugin (for those of you who can't wait for a bukkit one)
     
  24. Offline

    Darkblader

    Thanks a lot!
    Is in that code the crash bug when joining in different worlds fixed?
     
  25. Offline

    wacossusca34

    In my plugin, yes, but not from that class. That crash bug can be fixed by updating a users nametag while they are in the main world, teleporting them to the main world on a PlayerLoginEvent, setting their tag on a PlayerJoinEvent, and then teleporting them back afterwards.

    You will need to load, edit, and save the NBT data in the player '.dat' file location in the world folder to change their location before they even join the server. I was pretty surprised to find that there was nothing in the Bukkit API to get this information :confused:
     
  26. Offline

    Darkblader

    I tried to do it like that, but everytime I get this error:

    Code:
    2013-03-28 19:04:19 [SEVERE] Error occurred while enabling PPS v3.0 (Is it up to date?)
    java.lang.NoClassDefFoundError: net/minecraft/server/v1_5_R2/NBTBase
        at de.PPS.main.PPS.listeners(PPS.java:189)
        at de.PPS.main.PPS.onEnable(PPS.java:67)
        at org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:217)
        at org.bukkit.plugin.java.JavaPluginLoader.enablePlugin(JavaPluginLoader.java:457)
        at org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:383)
        at org.bukkit.craftbukkit.v1_5_R1.CraftServer.loadPlugin(CraftServer.java:304)
        at org.bukkit.craftbukkit.v1_5_R1.CraftServer.enablePlugins(CraftServer.java:286)
        at net.minecraft.server.v1_5_R1.MinecraftServer.j(MinecraftServer.java:309)
        at net.minecraft.server.v1_5_R1.MinecraftServer.e(MinecraftServer.java:288)
        at net.minecraft.server.v1_5_R1.MinecraftServer.a(MinecraftServer.java:248)
        at net.minecraft.server.v1_5_R1.DedicatedServer.init(DedicatedServer.java:154)
        at net.minecraft.server.v1_5_R1.MinecraftServer.run(MinecraftServer.java:387)
        at net.minecraft.server.v1_5_R1.ThreadServerApplication.run(SourceFile:573)
    Caused by: java.lang.ClassNotFoundException: net.minecraft.server.v1_5_R2.NBTBase
        at org.bukkit.plugin.java.PluginClassLoader.findClass0(PluginClassLoader.java:70)
        at org.bukkit.plugin.java.PluginClassLoader.findClass(PluginClassLoader.java:53)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:423)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:356)
        ... 13 more
    Do you have any idea what it could be?

    Hahaha I'm stupid xD
    I had craftbukkit 1.5 in my dev server.
    It works nice with 1.5.1.! Thank you

    EDIT by Moderator: merged posts, please use the edit button instead of double posting.
     
    Last edited by a moderator: May 31, 2016
Thread Status:
Not open for further replies.

Share This Page