-
-
Save marcbal/cadd9918ecce659d1f5b5fa82b6f3fb8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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