Skip to content

Instantly share code, notes, and snippets.

@Aurelien30000
Forked from WeiiswurstDev/ScoreboardSign.java
Last active May 31, 2023 05:30
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Aurelien30000/7824cd4bcf91ee2eca37c7fa3c4e9bca to your computer and use it in GitHub Desktop.
Save Aurelien30000/7824cd4bcf91ee2eca37c7fa3c4e9bca to your computer and use it in GitHub Desktop.
A simple tool to manage scoreboards in minecraft (lines up to 48 characters !). This Fork uses ProtocolLib and is therefore compatible with 1.13.x - 1.16.x. To use it in your project, you need to use ProtocolLib as a dependency!

ScoreboardSign


// Your plugin package, like "fr.myplugin.utils;"
import com.comphenix.protocol.PacketType;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import com.comphenix.protocol.events.PacketContainer;
import com.comphenix.protocol.wrappers.EnumWrappers;
import com.comphenix.protocol.wrappers.WrappedChatComponent;
import com.google.common.collect.Lists;
import org.bukkit.entity.Player;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @author zyuiop
* <p>
* Updated by Weiiswurst on 21/05/2020 to use the ProtocolLib API
* instead of reflection, which allows cross-version compatibility.
* <p>
* Updated by Giovanni75 on 10/07/2020 for some code cleanup.
* Reviewed and shared to Spigot by Aurelien30000 on 10/07/2020.
* 1.8 - 1.12.2 Support dropped to make this library working with 1.13.2+, by Aurelien30000 on 27/08/2020.
*/
public class ScoreboardSign {
private static final ProtocolManager pm = ProtocolLibrary.getProtocolManager();
private final Player player;
private String objectiveName;
private boolean created;
private final VirtualTeam[] lines = new VirtualTeam[15];
/**
* Create a scoreboard sign for a given player and using a specific objective name.
*
* @param player the player viewing it
* @param objectiveName its name (displayed at the top of the scoreboard)
*/
public ScoreboardSign(final Player player, final String objectiveName) {
this.player = player;
this.objectiveName = objectiveName;
}
private void sendPacket(final PacketContainer pc) {
try {
pm.sendServerPacket(player, pc);
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
/**
* Send the initial creation packets for this scoreboard sign. Must be called at least once.
*/
public void create() {
if (created) return;
sendPacket(createObjectivePacket(0, objectiveName));
sendPacket(setObjectiveSlot());
for (int i = 0; i < lines.length; i++)
sendLine(i);
created = true;
}
/**
* Send the packets to remove this scoreboard sign. A destroyed scoreboard sign must
* be recreated using {@link ScoreboardSign#create()} in order to be used again.
*/
public void destroy() {
if (!created) return;
sendPacket(createObjectivePacket(1, null));
for (VirtualTeam team : lines)
if (team != null)
sendPacket(team.removeTeam());
created = false;
}
/**
* Change the name of the objective. The name is displayed at the top of the scoreboard.
*
* @param name the name of the objective - max 32 characters
*/
public void setObjectiveName(final String name) {
this.objectiveName = name;
if (created)
sendPacket(createObjectivePacket(2, name));
}
/**
* Change a scoreboard line and send the packets to the player. Can be called asynchronously.
*
* @param line the number of the line - between 0 and 14
* @param value the new value for the scoreboard line
*/
public void setLine(final int line, final String value) {
final VirtualTeam team = getOrCreateTeam(line);
final String old = team.getCurrentPlayer();
if (value.equals(old)) return;
if (old != null && created)
sendPacket(removeLine(old));
team.setValue(value);
sendLine(line);
}
/**
* Set all scoreboard lines to the list and send these to the player.
*
* @param list the list of the new scoreboard lines
*/
public void setLines(final Iterable<String> list) {
int i = 0;
for (String s : list) {
setLine(i, s);
i++;
}
}
/**
* Remove a given scoreboard line.
*
* @param line the line to remove
*/
public void removeLine(final int line) {
final VirtualTeam team = getOrCreateTeam(line);
final String old = team.getCurrentPlayer();
if (old != null && created) {
sendPacket(removeLine(old));
sendPacket(team.removeTeam());
}
lines[line] = null;
}
/**
* Get the current value for a line.
*
* @param line the line
* @return its content
*/
public String getLine(final int line) {
return line < 0 || line > 14 ? null : getOrCreateTeam(line).getValue();
}
/**
* Get the team assigned to a line.
*
* @return the {@link VirtualTeam} used to display this line
*/
public VirtualTeam getTeam(final int line) {
return line < 0 || line > 14 ? null : getOrCreateTeam(line);
}
private void sendLine(final int line) {
if (line < 0 || line > 14 || !created) return;
final VirtualTeam team = getOrCreateTeam(line);
for (PacketContainer pc : team.sendLine())
sendPacket(pc);
sendPacket(sendScore(team.getCurrentPlayer(), line));
team.reset();
}
private VirtualTeam getOrCreateTeam(final int line) {
if (lines[line] == null)
lines[line] = new VirtualTeam("__fakeScore" + line, "", "");
return lines[line];
}
// 0 : Create
// 1 : Delete
// 2 : Update
private PacketContainer createObjectivePacket(final int mode, final String displayName) {
final PacketContainer pc = pm.createPacket(PacketType.Play.Server.SCOREBOARD_OBJECTIVE, true);
pc.getIntegers().write(0, mode);
pc.getStrings().write(0, player.getName());
if (mode == 0 || mode == 2)
pc.getChatComponents().write(0, WrappedChatComponent.fromText(displayName));
return pc;
}
private PacketContainer setObjectiveSlot() {
final PacketContainer pc = pm.createPacket(PacketType.Play.Server.SCOREBOARD_DISPLAY_OBJECTIVE);
pc.getIntegers().write(0, 1);
pc.getStrings().write(0, player.getName());
return pc;
}
private PacketContainer sendScore(final String line, final int score) {
final PacketContainer pc = pm.createPacket(PacketType.Play.Server.SCOREBOARD_SCORE);
pc.getIntegers().write(0, score);
pc.getScoreboardActions().write(0, EnumWrappers.ScoreboardAction.CHANGE);
pc.getStrings().write(0, line).write(1, player.getName());
return pc;
}
private PacketContainer removeLine(final String line) {
final PacketContainer pc = pm.createPacket(PacketType.Play.Server.SCOREBOARD_SCORE);
pc.getScoreboardActions().write(0, EnumWrappers.ScoreboardAction.REMOVE);
pc.getStrings().write(0, line);
return pc;
}
/**
* This class is used to manage the content of a line. Advanced users can use it as they want, but they are
* encouraged to read and understand the code before doing so. Thus, use these methods at your own risk.
*/
public static class VirtualTeam {
private final String name;
private String currentPlayer, oldPlayer;
private String prefix, suffix;
private boolean playerChanged, prefixChanged, suffixChanged;
private boolean first = true;
// Virtual team
private VirtualTeam(final String name, final String prefix, final String suffix) {
this.name = name;
this.prefix = prefix;
this.suffix = suffix;
}
public void reset() {
prefixChanged = false;
suffixChanged = false;
playerChanged = false;
oldPlayer = null;
}
public String getName() {
return name;
}
// Prefix
public String getPrefix() {
return prefix;
}
public void setPrefix(final String prefix) {
if (this.prefix == null || !this.prefix.equals(prefix))
this.prefixChanged = true;
this.prefix = prefix;
}
// Suffix
public String getSuffix() {
return suffix;
}
public void setSuffix(final String suffix) {
if (this.suffix == null || !this.suffix.equals(prefix))
this.suffixChanged = true;
this.suffix = suffix;
}
// Packets
private static final WrappedChatComponent emptyWrappedChatComponent = WrappedChatComponent.fromText("");
private PacketContainer createPacket(final int mode) {
final PacketContainer pc = pm.createPacket(PacketType.Play.Server.SCOREBOARD_TEAM, true);
pc.getStrings().write(0, name).write(1, "always");
pc.getChatComponents().write(0, emptyWrappedChatComponent).write(1, WrappedChatComponent.fromText(prefix)).write(2, WrappedChatComponent.fromText(suffix));
pc.getIntegers().write(0, mode);
return pc;
}
public Iterable<PacketContainer> sendLine() {
final List<PacketContainer> packets = new ArrayList<>();
if (first) {
first = false;
packets.add(createTeam());
} else if (prefixChanged || suffixChanged) {
packets.add(updateTeam());
}
if (first || playerChanged) {
if (oldPlayer != null)
packets.add(addOrRemovePlayer(4, oldPlayer));
packets.add(changePlayer());
}
return packets;
}
// Team
public PacketContainer createTeam() {
return createPacket(0);
}
public PacketContainer removeTeam() {
final PacketContainer pc = pm.createPacket(PacketType.Play.Server.SCOREBOARD_TEAM);
pc.getIntegers().write(0, 1);
pc.getStrings().write(0, name);
first = true;
return pc;
}
public PacketContainer updateTeam() {
return createPacket(2);
}
// Player
public PacketContainer addOrRemovePlayer(final int mode, final String playerName) {
final PacketContainer pc = pm.createPacket(PacketType.Play.Server.SCOREBOARD_TEAM);
pc.getIntegers().write(0, mode);
pc.getSpecificModifier(Collection.class).write(0, Lists.newArrayList(playerName));
pc.getStrings().write(0, name);
return pc;
}
public PacketContainer changePlayer() {
return addOrRemovePlayer(3, currentPlayer);
}
public String getCurrentPlayer() {
return currentPlayer;
}
public void setPlayer(final String name) {
if (this.currentPlayer == null || !this.currentPlayer.equals(name))
this.playerChanged = true;
this.oldPlayer = this.currentPlayer;
this.currentPlayer = name;
}
// Value
public String getValue() {
return getPrefix() + getCurrentPlayer() + getSuffix();
}
public void setValue(final String value) {
final int length = value.length();
if (length <= 64) {
setPrefix("");
setPlayer(value);
setSuffix("");
} else if (length <= 80) {
setPrefix(value.substring(0, 64));
setPlayer(value.substring(64));
setSuffix("");
} else if (length <= 144) {
setPrefix(value.substring(0, 64));
setPlayer(value.substring(64, 80));
setSuffix(value.substring(80));
} else {
throw new IllegalArgumentException("Too long virtual team value (" + length + " > 48 characters)");
}
}
}
}
@WeiiswurstDev
Copy link

Just out of curiosity, what did you change in your fork of my gist?
The github diff just shows one block of removals and one block of additions, and I was only able to notice that you removed the package at the top (makes sense)

@Aurelien30000
Copy link
Author

Just out of curiosity, what did you change in your fork of my gist?
The github diff just shows one block of removals and one block of additions, and I was only able to notice that you removed the package at the top (makes sense)

Only some values caching and code formatting, but I don't remember, @giovanni75 did the majority of the changes ^^

@nitramleo
Copy link

Hey!

I'm trying to use this on 1.16.4, however, Teams don't seem to work, I have set VirtualTeam to public, and my code which gives an error is the following: https://pastebin.com/DaTrUSfQ I've tried both having VirtualTeam as it is and in its own class which I am currently doing.

My error: https://pastebin.com/A4fWGdTq

I've been trying to figure this stuff out for quite some time and I can't see any packet changes from 1.16.2 to 1.16.4

@Aurelien30000
Copy link
Author

Hey!

I'm trying to use this on 1.16.4, however, Teams don't seem to work, I have set VirtualTeam to public, and my code which gives an error is the following: https://pastebin.com/DaTrUSfQ I've tried both having VirtualTeam as it is and in its own class which I am currently doing.

My error: https://pastebin.com/A4fWGdTq

I've been trying to figure this stuff out for quite some time and I can't see any packet changes from 1.16.2 to 1.16.4

Why are you trying to create instances of VirtualTeam? You absolutely don't need them with the basic purpose of this library in mind. What are you really trying to do, I guess you're expecting something completely unrelated 😅

Otherwise, should be related to dmulloy2/ProtocolLib#1044, update to latest dev build and it should work perfectly ^^

@nitramleo
Copy link

Hey!
I'm trying to use this on 1.16.4, however, Teams don't seem to work, I have set VirtualTeam to public, and my code which gives an error is the following: https://pastebin.com/DaTrUSfQ I've tried both having VirtualTeam as it is and in its own class which I am currently doing.
My error: https://pastebin.com/A4fWGdTq
I've been trying to figure this stuff out for quite some time and I can't see any packet changes from 1.16.2 to 1.16.4

Why are you trying to create instances of VirtualTeam? You absolutely don't need them with the basic purpose of this library in mind. What are you really trying to do, I guess you're expecting something completely unrelated 😅

Otherwise, should be related to dmulloy2/ProtocolLib#1044, update to latest dev build and it should work perfectly ^^

I'm trying to change the colour of the name above the players head😅 After some reading of this original class I figured it would somehow be possible, probably just missunderstood someone down the line😅

@Aurelien30000
Copy link
Author

Hey!
I'm trying to use this on 1.16.4, however, Teams don't seem to work, I have set VirtualTeam to public, and my code which gives an error is the following: https://pastebin.com/DaTrUSfQ I've tried both having VirtualTeam as it is and in its own class which I am currently doing.
My error: https://pastebin.com/A4fWGdTq
I've been trying to figure this stuff out for quite some time and I can't see any packet changes from 1.16.2 to 1.16.4

Why are you trying to create instances of VirtualTeam? You absolutely don't need them with the basic purpose of this library in mind. What are you really trying to do, I guess you're expecting something completely unrelated 😅
Otherwise, should be related to dmulloy2/ProtocolLib#1044, update to latest dev build and it should work perfectly ^^

I'm trying to change the colour of the name above the players head😅 After some reading of this original class I figured it would somehow be possible, probably just missunderstood someone down the line😅

Okay, just try to update to https://ci.dmulloy2.net/job/ProtocolLib/lastStableBuild/, should work and let you progress in your work :)

@nitramleo
Copy link

Hey!
I'm trying to use this on 1.16.4, however, Teams don't seem to work, I have set VirtualTeam to public, and my code which gives an error is the following: https://pastebin.com/DaTrUSfQ I've tried both having VirtualTeam as it is and in its own class which I am currently doing.
My error: https://pastebin.com/A4fWGdTq
I've been trying to figure this stuff out for quite some time and I can't see any packet changes from 1.16.2 to 1.16.4

Why are you trying to create instances of VirtualTeam? You absolutely don't need them with the basic purpose of this library in mind. What are you really trying to do, I guess you're expecting something completely unrelated 😅
Otherwise, should be related to dmulloy2/ProtocolLib#1044, update to latest dev build and it should work perfectly ^^

I'm trying to change the colour of the name above the players head😅 After some reading of this original class I figured it would somehow be possible, probably just missunderstood someone down the line😅

Okay, just try to update to https://ci.dmulloy2.net/job/ProtocolLib/lastStableBuild/, should work and let you progress in your work :)

Hey thanks! It got a me a lot further than I was, however, now I just get the problem where ProtocolLib tells me it isn't a dependency in my plugin when it is. Probably a dumb question but honestly I gave up trying to work with protocollib years ago 😅 and I'm trying to get back into it, any ideas?

@Aurelien30000
Copy link
Author

What's the exact message/error you're facing on?

@nitramleo
Copy link

nitramleo commented Jan 25, 2021

What's the exact message/error you're facing on?

WARNING: Illegal reflective access by com.comphenix.protocol.reflect.compiler.StructureCompiler (file:/F:/nitramleo/project%20dpmaskus/server/Hub/plugins/ProtocolLib.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int)

WARNING: Please consider reporting this to the maintainers of com.comphenix.protocol.reflect.compiler.StructureCompiler

WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations

WARNING: All illegal access operations will be denied in a future release

[19:26:47 WARN]: [ProtocolLib] Loaded class com.comphenix.protocol.wrappers.WrappedChatComponent from Rydr v0.0.1 which is not a depend, softdepend or loadbefore of this plugin.

@Aurelien30000
Copy link
Author

Aurelien30000 commented Jan 25, 2021

Don't worry about both ^^

First is about your Java version, you probably updated to 11+, just completely ignore it.
The second one is simple a warning, you can also ignore it. However, to fix it and make your plugin more stable, add ProtocolLib to the dependencies list (plugin.yml).

Something like this:

depend: [ ProtocolLib ]

@nitramleo
Copy link

Don't worry about both ^^

First is about your Java version your probably updated to 11+, just completely ignore it.
The second one is simple a warning, you can also ignore it. However, to fix it and make your plugin more stable, add ProtocolLib to the dependencies list (plugin.yml).

Something like this:

depend: [ ProtocolLib ]

https://i.imgur.com/YjKQsO0.png

Weird thing is that this is my plugin.yml and I'm still having the issue, I've heard other people have this too I just don't remember how they fixed it

@Aurelien30000
Copy link
Author

Don't worry about both ^^
First is about your Java version your probably updated to 11+, just completely ignore it.
The second one is simple a warning, you can also ignore it. However, to fix it and make your plugin more stable, add ProtocolLib to the dependencies list (plugin.yml).
Something like this:

depend: [ ProtocolLib ]

https://i.imgur.com/YjKQsO0.png

Weird thing is that this is my plugin.yml and I'm still having the issue, I've heard other people have this too I just don't remember how they fixed it

:/ I don't know any more, this isn't an annoying issue anyways.

@nitramleo
Copy link

Don't worry about both ^^
First is about your Java version your probably updated to 11+, just completely ignore it.
The second one is simple a warning, you can also ignore it. However, to fix it and make your plugin more stable, add ProtocolLib to the dependencies list (plugin.yml).
Something like this:

depend: [ ProtocolLib ]

https://i.imgur.com/YjKQsO0.png
Weird thing is that this is my plugin.yml and I'm still having the issue, I've heard other people have this too I just don't remember how they fixed it

:/ I don't know any more, this isn't an annoying issue anyways.

That's fine, thank you for trying anyways! I'll edit this if I find a fix so anyone else who might have the same issue can see how I resolved it, incase I resolve it

@Aurelien30000
Copy link
Author

Perhaps the checks are sensitive to the position of the line, try to write it before and also try to remove spaces I put in my example.

@D151l
Copy link

D151l commented Oct 3, 2021

Is there a version for the 1.17.1?

@Aurelien30000
Copy link
Author

Is there a version for the 1.17.1?

@D151l Probably doesn't work, this library is a bit outdated tbh, I would recommend this one instead for recent versions: https://github.com/MrMicky-FR/FastBoard

@D151l
Copy link

D151l commented Oct 5, 2021

Is there a version for the 1.17.1?

@D151l Probably doesn't work, this library is a bit outdated tbh, I would recommend this one instead for recent versions: https://github.com/MrMicky-FR/FastBoard

thx

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment