-
-
Save WeiiswurstDev/8e9a6fd0f8eaaa57c2c0fc2ed9fcd0fb to your computer and use it in GitHub Desktop.
package dev.wwst.scoreboard; | |
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.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 | |
* @author Weiiswurst | |
* I changed the ScoreboardSign to use the ProtocolLib API. | |
* This means: No java.reflect, and most importantly, cross-version compatibility!! | |
* | |
*/ | |
public class ScoreboardSign { | |
private boolean created = false; | |
private final VirtualTeam[] lines = new VirtualTeam[15]; | |
private final Player player; | |
private String objectiveName; | |
private final ProtocolManager pm; | |
/** | |
* Create a scoreboard sign for a given player and using a specifig objective name | |
* @param player the player viewing the scoreboard sign | |
* @param objectiveName the name of the scoreboard sign (displayed at the top of the scoreboard) | |
*/ | |
public ScoreboardSign(final Player player, final String objectiveName) { | |
this.player = player; | |
this.objectiveName = objectiveName; | |
this.pm = ProtocolLibrary.getProtocolManager(); | |
} | |
private void sendPacket(PacketContainer packet) { | |
try { | |
pm.sendServerPacket(player,packet); | |
} 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; | |
//final PlayerConnection player = getPlayer(); | |
sendPacket(createObjectivePacket(0, objectiveName)); | |
sendPacket(setObjectiveSlot()); | |
int i = 0; | |
while (i < lines.length) | |
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 (final 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 char | |
*/ | |
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 async. | |
* @param line the number of the line (0 <= line < 15) | |
* @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 new scoreboard lines | |
*/ | |
public void setLines(final Iterable<String> list) { | |
int i = 0; | |
for(final String x : list) { | |
setLine(i,x); | |
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 the content of the line | |
*/ | |
public String getLine(final int line) { | |
if (line > 14) | |
return null; | |
if (line < 0) | |
return null; | |
return 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) { | |
if (line > 14) | |
return null; | |
if (line < 0) | |
return null; | |
return getOrCreateTeam(line); | |
} | |
/*private PlayerConnection getPlayer() { | |
return ((CraftPlayer) player).getHandle().playerConnection; | |
}*/ | |
private void sendLine(final int line) { | |
if (line > 14) | |
return; | |
if (line < 0) | |
return; | |
if (!created) | |
return; | |
int score = (15 - line); | |
VirtualTeam val = getOrCreateTeam(line); | |
for (final PacketContainer packet : val.sendLine()) | |
sendPacket(packet); | |
sendPacket(sendScore(val.getCurrentPlayer(), score)); | |
val.reset(); | |
} | |
private VirtualTeam getOrCreateTeam(final int line) { | |
if (lines[line] == null) | |
lines[line] = new VirtualTeam("__fakeScore" + line); | |
return lines[line]; | |
} | |
/* | |
Factories | |
*/ | |
private PacketContainer createObjectivePacket(final int mode, final String displayName) { | |
//PacketPlayOutScoreboardObjective packet = new PacketPlayOutScoreboardObjective(); | |
PacketContainer packet2 = pm.createPacket(PacketType.Play.Server.SCOREBOARD_OBJECTIVE, true); | |
packet2.getStrings().write(0,player.getName()); | |
packet2.getIntegers().write(0,mode); | |
// Nom de l'objectif | |
//setField(packet, "a", player.getName()); | |
// Mode | |
// 0 : créer | |
// 1 : Supprimer | |
// 2 : Mettre à jour | |
//setField(packet, "d", mode); | |
if (mode == 0 || mode == 2) { | |
packet2.getStrings().write(1,displayName); | |
//setField(packet, "b", displayName); | |
//setField(packet, "c", IScoreboardCriteria.EnumScoreboardHealthDisplay.INTEGER); | |
} | |
return packet2; | |
} | |
private PacketContainer setObjectiveSlot() { | |
//PacketPlayOutScoreboardDisplayObjective packet = new PacketPlayOutScoreboardDisplayObjective(); | |
PacketContainer packet2 = pm.createPacket(PacketType.Play.Server.SCOREBOARD_DISPLAY_OBJECTIVE); | |
packet2.getIntegers().write(0,1); | |
packet2.getStrings().write(0,player.getName()); | |
// Slot | |
//setField(packet, "a", 1); | |
//setField(packet, "b", player.getName()); | |
return packet2; | |
} | |
private PacketContainer sendScore(final String line,final int score) { | |
//PacketPlayOutScoreboardScore packet = new PacketPlayOutScoreboardScore(line); | |
PacketContainer packet2 = pm.createPacket(PacketType.Play.Server.SCOREBOARD_SCORE); | |
packet2.getStrings().write(0,line); | |
packet2.getStrings().write(1,player.getName()); | |
packet2.getIntegers().write(0,score); | |
packet2.getScoreboardActions().write(0, EnumWrappers.ScoreboardAction.CHANGE); | |
//setField(packet, "b", player.getName()); | |
//setField(packet, "c", score); | |
//setField(packet, "d", PacketPlayOutScoreboardScore.EnumScoreboardAction.CHANGE); | |
return packet2; | |
} | |
private PacketContainer removeLine(String line) { | |
PacketContainer packet2 = pm.createPacket(PacketType.Play.Server.SCOREBOARD_SCORE); | |
packet2.getStrings().write(0,line); | |
packet2.getScoreboardActions().write(0, EnumWrappers.ScoreboardAction.REMOVE); | |
return packet2; | |
} | |
/** | |
* 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. Use these methods at your own risk. | |
*/ | |
private static class VirtualTeam { | |
private final String name; | |
private String prefix; | |
private String suffix; | |
private String currentPlayer; | |
private String oldPlayer; | |
private final ProtocolManager pm; | |
private boolean prefixChanged, suffixChanged, playerChanged = false; | |
private boolean first = true; | |
private VirtualTeam(final String name, final String prefix, final String suffix) { | |
this.name = name; | |
this.prefix = prefix; | |
this.suffix = suffix; | |
this.pm = ProtocolLibrary.getProtocolManager(); | |
} | |
private VirtualTeam(final String name) { | |
this(name, "", ""); | |
} | |
public String getName() { | |
return name; | |
} | |
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; | |
} | |
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; | |
} | |
private PacketContainer createPacket(final int mode) { | |
//PacketPlayOutScoreboardTeam packet = new PacketPlayOutScoreboardTeam(); | |
PacketContainer packet2 = pm.createPacket(PacketType.Play.Server.SCOREBOARD_TEAM,true); | |
packet2.getStrings().write(0,name); | |
packet2.getStrings().write(1,""); | |
packet2.getStrings().write(2,prefix); | |
packet2.getStrings().write(3,suffix); | |
packet2.getStrings().write(4,"always"); | |
packet2.getIntegers().write(0,0); | |
packet2.getIntegers().write(1,mode); | |
packet2.getIntegers().write(2,0); | |
//setField(packet, "a", name); | |
//setField(packet, "h", mode); | |
//setField(packet, "b", ""); | |
//setField(packet, "c", prefix); | |
//setField(packet, "d", suffix); | |
//setField(packet, "i", 0); | |
//setField(packet, "e", "always"); | |
//setField(packet, "f", 0); | |
return packet2; | |
} | |
public PacketContainer createTeam() { | |
return createPacket(0); | |
} | |
public PacketContainer updateTeam() { | |
return createPacket(2); | |
} | |
public PacketContainer removeTeam() { | |
PacketContainer packet2 = pm.createPacket(PacketType.Play.Server.SCOREBOARD_TEAM); | |
packet2.getStrings().write(0,name); | |
packet2.getIntegers().write(1,1); | |
//PacketPlayOutScoreboardTeam packet = new PacketPlayOutScoreboardTeam(); | |
//setField(packet, "a", name); | |
//setField(packet, "h", 1); | |
first = true; | |
return packet2; | |
} | |
public void setPlayer(final String name) { | |
if (this.currentPlayer == null || !this.currentPlayer.equals(name)) | |
this.playerChanged = true; | |
this.oldPlayer = this.currentPlayer; | |
this.currentPlayer = name; | |
} | |
public Iterable<PacketContainer> sendLine() { | |
List<PacketContainer> packets = new ArrayList<>(); | |
if (first) { | |
packets.add(createTeam()); | |
} else if (prefixChanged || suffixChanged) { | |
packets.add(updateTeam()); | |
} | |
if (first || playerChanged) { | |
if (oldPlayer != null) // remove these two lines ? | |
packets.add(addOrRemovePlayer(4, oldPlayer)); // | |
packets.add(changePlayer()); | |
} | |
if (first) | |
first = false; | |
return packets; | |
} | |
public void reset() { | |
prefixChanged = false; | |
suffixChanged = false; | |
playerChanged = false; | |
oldPlayer = null; | |
} | |
public PacketContainer changePlayer() { | |
return addOrRemovePlayer(3, currentPlayer); | |
} | |
public PacketContainer addOrRemovePlayer(final int mode, final String playerName) { | |
//PacketPlayOutScoreboardTeam packet = new PacketPlayOutScoreboardTeam(); | |
//setField(packet, "a", name); | |
//setField(packet, "h", mode); | |
PacketContainer packet2 = pm.createPacket(PacketType.Play.Server.SCOREBOARD_TEAM); | |
packet2.getStrings().write(0,name); | |
packet2.getIntegers().write(1,mode); | |
Collection<String> playerNames = Lists.newArrayList(playerName); | |
packet2.getSpecificModifier(Collection.class).write(0,playerNames); | |
/*try { | |
Field f = packet.getClass().getDeclaredField("g"); | |
f.setAccessible(true); | |
((List<String>) f.get(packet)).add(playerName); | |
} catch (NoSuchFieldException | IllegalAccessException e) { | |
e.printStackTrace(); | |
}*/ | |
return packet2; | |
} | |
public String getCurrentPlayer() { | |
return currentPlayer; | |
} | |
public String getValue() { | |
return getPrefix() + getCurrentPlayer() + getSuffix(); | |
} | |
public void setValue(final String value) { | |
if (value.length() <= 16) { | |
setPrefix(""); | |
setSuffix(""); | |
setPlayer(value); | |
} else if (value.length() <= 32) { | |
setPrefix(value.substring(0, 16)); | |
setPlayer(value.substring(16)); | |
setSuffix(""); | |
} else if (value.length() <= 48) { | |
setPrefix(value.substring(0, 16)); | |
setPlayer(value.substring(16, 32)); | |
setSuffix(value.substring(32)); | |
} else { | |
throw new IllegalArgumentException("Too long value ! Max 48 characters, value was " + value.length() + " !"); | |
} | |
} | |
} | |
/*private static void setField(final Object edit, final String fieldName, final Object value) { | |
try { | |
final Field field = edit.getClass().getDeclaredField(fieldName); | |
field.setAccessible(true); | |
field.set(edit, value); | |
} catch (final NoSuchFieldException | IllegalAccessException e) { | |
e.printStackTrace(); | |
} | |
}*/ | |
} |
did works on minecraft 1.15.2 ?
whenever I try to execute my plugin it display :
com.comphenix.protocol.reflect.FieldAccessException: Field index out of bounds. (Index: 1, Size: 1)
EDIT: i forgot to put my code:
public Map<Player, ScoreboardSign> boards = new HashMap<>();
@Override
public void onEnable(){
getServer().getPluginManager().registerEvents(this, this);
}
@EventHandler
public void onJoin(final PlayerJoinEvent event){
Player player = event.getPlayer();
ScoreboardSign scoreboard = new ScoreboardSign(player, "TestScore");
scoreboard.create();
scoreboard.setLine(0, "TestPlayer");
boards.put(player, scoreboard);
}
Hi, very great !
Do you have it done for 1.16.1 ?
Hi!
I sadly have no idea how the new packets look like.
Although there is this updated fork: https://gist.github.com/Aurelien30000/7824cd4bcf91ee2eca37c7fa3c4e9bca
It has some changes, you should try it out and maybe it works....?
I'm sorry that this is the only help I can give you.
It doesn't work :/ I can't figure out any issue or change, weird...
Oww I found, in 1.8, b field is a string and in 1.15.2+ (maybe also 1.12, 1.13), b field is an IChatBaseComponent... but how to get it with PLib?
Should be fixed, despite legacy versions support dropped! https://www.spigotmc.org/resources/simple-scoreboard-library-only-for-java-developers.65455/updates
Do you allow me to reupload it on spigot (https://www.spigotmc.org/resources/simple-scoreboard-library-only-for-java-developers.65455/) ?
Thanks