Skip to content

Instantly share code, notes, and snippets.

@marcbal
Created August 28, 2021 15:58
Show Gist options
  • Save marcbal/cadd9918ecce659d1f5b5fa82b6f3fb8 to your computer and use it in GitHub Desktop.
Save marcbal/cadd9918ecce659d1f5b5fa82b6f3fb8 to your computer and use it in GitHub Desktop.
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import org.bukkit.Bukkit;
import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.scoreboard.DisplaySlot;
import org.bukkit.scoreboard.Objective;
import org.bukkit.scoreboard.Score;
import org.bukkit.scoreboard.Scoreboard;
import net.kyori.adventure.text.Component;
import net.md_5.bungee.api.ChatColor;
/*
* Plugin to test the consistency of the display of custom data in
* the scoreboard’s sidebar, beteen Java client and Bedrock client (using Geyser).
*
* The code provides 2 implementations of the way to update the scorebard.
* Change the method used at line 53-54. Both methods, with their documentations,
* Are implemented below in this file.
*
* Dependency : paper-api 1.17.1-R0.1-SNAPSHOT
*/
public class ScoreboardTest extends JavaPlugin {
@Override
public void onEnable() {
scoreboard = Bukkit.getScoreboardManager().getMainScoreboard();
Bukkit.getScheduler().runTaskTimer(this, this::update, 100, 100);
}
final String[] linePrefixes = new String[] { "1st ", "2nd ", "3rd ", "4th ",
"5th ", "6th ", "7th ", "8th ", "9th ", "10th " };
final int count = linePrefixes.length;
final int[] counters = new int[count];
final Random rnd = new Random();
Scoreboard scoreboard;
private void update() {
counters[rnd.nextInt(count)]++;
String[] lines = new String[count];
for (int i = 0; i < count; i++) {
lines[i] = linePrefixes[i] + counters[i];
}
//ScoreBoardUtil.updateScoreboardSidebarInADumbWay(scoreboard, Component.text("This is a test"), lines);
ScoreBoardUtil.updateScoreboardSidebar(scoreboard, Component.text("This is a test"), lines);
}
}
class ScoreBoardUtil {
/**
* Update the sidebar of the provided scoreboard, with the given title and lines.
*
* Compared to {@link #updateScoreboardSidebar(Scoreboard, Component, String[])}, this method
* is less intelligent, since it reset the objective values at every updates and resend every lines.
* @param scBrd the scoreboard
* @param title the title of the sidebar
* @param lines the lines that have to be displayed. Null values are treated as empty lines.
* The lines support legacy formatting only, and will be truncated to 40 characters.
* Lines present multiple times will have hidden characters appended to make them different.
* Vanilla Java Edition clients only display the 15 first lines.
*/
public static void updateScoreboardSidebarInADumbWay(Scoreboard scBrd, Component title, String[] lines) {
Objective obj = scBrd.getObjective("sidebar_autogen");
if (obj != null) {
obj.unregister();
}
obj = scBrd.registerNewObjective("sidebar_autogen", "dummy", title);
obj.displayName(title);
obj.setDisplaySlot(DisplaySlot.SIDEBAR);
filterLines(lines);
int boardPos = lines.length;
for (String line : lines) {
obj.getScore(line).setScore(boardPos--);
}
}
/**
* Update the sidebar of the provided scoreboard, with the given title and lines.
*
* @param scBrd the scoreboard
* @param title the title of the sidebar
* @param lines the lines that have to be displayed. Null values are treated as empty lines.
* The lines support legacy formatting only, and will be truncated to 40 characters.
* Lines present multiple times will have hidden characters appended to make them different.
* Vanilla Java Edition clients only display the 15 first lines.
* @implNote The implementation makes sure that the minimum amount of data is transmitted to the client,
* to reduce bandwith usage and avoid the sidebar flickering.
* <ul>
* <li>If a provided line is already present in the sidebar, and at the same line number, it will not be updated.
* <li>If a provided line is already present but at another position, only the score (i.e. the line number) is updated.
* <li>If a provided line was not present before, it is added as a new score entry in the scoreboard.
* <li>If a line that was already present is not in the provided lines, it is removed from the scoreboard.
* <li>The title is only updated if it has actually changed.
*/
public static void updateScoreboardSidebar(Scoreboard scBrd, Component title, String[] lines) {
if (scBrd == null) throw new IllegalArgumentException("scBrd doit être non null");
if (lines == null) lines = new String[0];
Objective obj = scBrd.getObjective("sidebar_autogen");
if (obj != null && !obj.getCriteria().equalsIgnoreCase("dummy")) {
// objective present but with wrong criteria, removing it
obj.unregister();
obj = null;
}
if (obj == null) {
obj = scBrd.registerNewObjective("sidebar_autogen", "dummy", title);
obj.setDisplaySlot(DisplaySlot.SIDEBAR);
}
else {
// only update title if needed
if (!title.equals(obj.displayName()))
obj.displayName(title);
// fix display slot if someone else changed it
if (DisplaySlot.SIDEBAR != obj.getDisplaySlot())
obj.setDisplaySlot(DisplaySlot.SIDEBAR);
}
filterLines(lines);
List<String> listLines = Arrays.asList(lines);
// remove lines from the scoreboard that are not in the provided array
Objective fObj = obj;
scBrd.getEntries().stream()
.filter(e -> !listLines.contains(e))
.filter(e -> fObj.getScore(e).isScoreSet())
.forEach(scBrd::resetScores);
// add and update others lines
int boardPos = lines.length;
for (String line : lines) {
Score score = obj.getScore(line);
if (score.getScore() != boardPos)
score.setScore(boardPos);
boardPos--;
}
}
@SuppressWarnings("deprecation")
private static void filterLines(String[] lines) {
List<String> previous = new ArrayList<>();
for (int i = 0; i < lines.length; i++) {
String line = lines[i] == null ? "" : truncateAtLengthWithoutReset(lines[i], 40);
if (previous.contains(line)) {
for (ChatColor c : ChatColor.values()) {
line = truncateAtLengthWithoutReset(lines[i], 38) + c;
if (!previous.contains(line)) {
break;
}
}
}
lines[i] = line;
previous.add(lines[i]);
}
}
private static String truncateAtLengthWithoutReset(String text, int l) {
if (text.length() > l) {
text = text.substring(0, l);
if (text.endsWith("§"))
text = text.substring(0, text.length()-1);
}
return text;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment