From f05d50f0e7670156b96763d5e69a49fccb24009b Mon Sep 17 00:00:00 2001 From: Dominilk01 <108392199+Dominilk01@users.noreply.github.com> Date: Tue, 4 Aug 2026 23:29:41 +0200 Subject: [PATCH 1/8] commit --- .../java/fr/mrmicky/fastboard/FastBoard.java | 17 +++- .../fr/mrmicky/fastboard/FastBoardBase.java | 99 +++++++++---------- .../fastboard/adventure/FastBoard.java | 8 +- 3 files changed, 68 insertions(+), 56 deletions(-) diff --git a/src/main/java/fr/mrmicky/fastboard/FastBoard.java b/src/main/java/fr/mrmicky/fastboard/FastBoard.java index 5cc67f0..84e8ce3 100644 --- a/src/main/java/fr/mrmicky/fastboard/FastBoard.java +++ b/src/main/java/fr/mrmicky/fastboard/FastBoard.java @@ -133,10 +133,9 @@ protected void sendLineChange(int score) throws Throwable { suffix = suffix.substring(0, Math.min(maxLength, suffix.length())); } - if (VersionType.V1_20_3.isCurrentAtLeast()) { + if (hasCustomScores()) { sendModernScorePacket(score, ScoreboardAction.CHANGE); } else { - sendScorePacket(score, ScoreboardAction.CHANGE); sendTeamPacket(score, TeamMode.UPDATE, prefix, suffix); } } @@ -170,4 +169,18 @@ protected String emptyLine() { protected boolean hasLinesMaxLength() { return !VersionType.V1_13.isCurrentAtLeast(); } + + /** + * Returns whether scoreboard lines should use the score as text. + * By default, this is true only on Minecraft 1.20.3 and higher. + * Override this method for compatibility with plugins that provide multi-version support. + *

+ * If not overridden, with server above 1.20.3 and player under 1.20.3 will not see text lines. + *

+ * example: {@code return Via.getAPI().getPlayerVersion(getPlayer()) >= ProtocolVersion.v1_20_3.getVersion();} + * @return true should use new score as text, false for legacy team + */ + protected boolean hasCustomScores() { + return VersionType.V1_20_3.isCurrentAtLeast(); + } } diff --git a/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java b/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java index 4b98ee0..47ec3d5 100644 --- a/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java +++ b/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java @@ -107,9 +107,8 @@ public abstract class FastBoardBase { Class packetSbObjClass = FastReflection.nmsClass(gameProtocolPackage, "PacketPlayOutScoreboardObjective", "ClientboundSetObjectivePacket"); Class packetSbDisplayObjClass = FastReflection.nmsClass(gameProtocolPackage, "PacketPlayOutScoreboardDisplayObjective", "ClientboundSetDisplayObjectivePacket"); Class packetSbScoreClass = FastReflection.nmsClass(gameProtocolPackage, "PacketPlayOutScoreboardScore", "ClientboundSetScorePacket"); - Class packetSbTeamClass = VersionType.V1_20_3.isCurrentAtLeast() - ? null : FastReflection.nmsClass(gameProtocolPackage, "PacketPlayOutScoreboardTeam", "ClientboundSetPlayerTeamPacket"); - Class sbTeamClass = VersionType.V1_17.isCurrentAtLeast() && !VersionType.V1_20_3.isCurrentAtLeast() + Class packetSbTeamClass = FastReflection.nmsClass(gameProtocolPackage, "PacketPlayOutScoreboardTeam", "ClientboundSetPlayerTeamPacket"); + Class sbTeamClass = VersionType.V1_17.isCurrentAtLeast() ? FastReflection.innerClass(packetSbTeamClass, innerClass -> !innerClass.isEnum()) : null; Field playerConnectionField = Arrays.stream(entityPlayerClass.getFields()) .filter(field -> field.getType().isAssignableFrom(playerConnectionClass)) @@ -129,30 +128,29 @@ public abstract class FastBoardBase { SEND_PACKET = lookup.unreflect(sendPacketMethod); Class scoreboardClass = FastReflection.nmsClass("world.scores", "Scoreboard"); - Class playerTeamClass = VersionType.V1_20_3.isCurrentAtLeast() ? - null : FastReflection.nmsClass("world.scores", "ScoreboardTeam", "PlayerTeam"); + Class playerTeamClass = FastReflection.nmsClass("world.scores", "ScoreboardTeam", "PlayerTeam"); Class objectiveClass = FastReflection.nmsClass("world.scores", "ScoreboardObjective", "Objective"); Class objectiveCriteriaClass = FastReflection.nmsClass("world.scores.criteria", "IScoreboardCriteria", "ObjectiveCriteria"); + PLAYER_TEAM = lookup.unreflectConstructor(playerTeamClass.getConstructor(scoreboardClass, String.class)); Class objectiveRenderTypeClass = FastReflection.nmsOptionalClass("world.scores.criteria", "IScoreboardCriteria$EnumScoreboardHealthDisplay", "ObjectiveCriteria$RenderType").orElse(null); + Optional> numberFormat = FastReflection.nmsOptionalClass("network.chat.numbers", "NumberFormat"); MethodHandle packetSbSetScore; MethodHandle packetSbResetScore = null; MethodHandle fixedFormatConstructor = null; - MethodHandle packetSbSerializableTeam = null; - MethodHandle packetSbTeam = null; - MethodHandle playerTeam = null; Object blankNumberFormat = null; boolean scoreOptionalComponents = false; - if (VersionType.V1_20_3.isCurrentAtLeast()) { - Class numberFormatClass = FastReflection.nmsClass("network.chat.numbers", "NumberFormat"); - OBJECTIVE = lookup.unreflectConstructor(objectiveClass.getConstructor(scoreboardClass, String.class, objectiveCriteriaClass, CHAT_COMPONENT_CLASS, objectiveRenderTypeClass, boolean.class, numberFormatClass)); + if (numberFormat.isPresent()) { // 1.20.3 + OBJECTIVE = lookup.unreflectConstructor(objectiveClass.getConstructor(scoreboardClass, String.class, objectiveCriteriaClass, CHAT_COMPONENT_CLASS, objectiveRenderTypeClass, boolean.class, numberFormat.get())); + PACKET_SB_OBJ = lookup.unreflectConstructor(packetSbObjClass.getConstructor(objectiveClass, int.class)); + PACKET_SB_DISPLAY_OBJ = lookup.unreflectConstructor(packetSbDisplayObjClass.getConstructor(DISPLAY_SLOT_TYPE, objectiveClass)); Class blankFormatClass = FastReflection.nmsClass("network.chat.numbers", "BlankFormat"); Class fixedFormatClass = FastReflection.nmsClass("network.chat.numbers", "FixedFormat"); Class resetScoreClass = FastReflection.nmsClass(gameProtocolPackage, "ClientboundResetScorePacket"); - MethodType scoreType = MethodType.methodType(void.class, String.class, String.class, int.class, CHAT_COMPONENT_CLASS, numberFormatClass); + MethodType scoreType = MethodType.methodType(void.class, String.class, String.class, int.class, CHAT_COMPONENT_CLASS, numberFormat.get()); MethodType scoreTypeOptional = MethodType.methodType(void.class, String.class, String.class, int.class, Optional.class, Optional.class); MethodType removeScoreType = MethodType.methodType(void.class, String.class, String.class); MethodType fixedFormatType = MethodType.methodType(void.class, CHAT_COMPONENT_CLASS); @@ -165,51 +163,46 @@ public abstract class FastBoardBase { scoreOptionalComponents = optionalScorePacket.isPresent(); packetSbResetScore = lookup.findConstructor(resetScoreClass, removeScoreType); blankNumberFormat = blankField.isPresent() ? blankField.get().get(null) : null; + } else if (VersionType.V1_17.isCurrentAtLeast()) { + Class enumSbAction = FastReflection.nmsClass("server", "ScoreboardServer$Action", "ServerScoreboard$Method"); + MethodType scoreType = MethodType.methodType(void.class, enumSbAction, String.class, String.class, int.class); + packetSbSetScore = lookup.findConstructor(packetSbScoreClass, scoreType); + OBJECTIVE = lookup.unreflectConstructor(objectiveClass.getConstructor(scoreboardClass, String.class, objectiveCriteriaClass, CHAT_COMPONENT_CLASS, objectiveRenderTypeClass)); + PACKET_SB_OBJ = lookup.unreflectConstructor(packetSbObjClass.getConstructor(objectiveClass, int.class)); + PACKET_SB_DISPLAY_OBJ = lookup.unreflectConstructor(packetSbDisplayObjClass.getConstructor(displaySlotEnum.orElse(int.class), objectiveClass)); } else { - Constructor packetSbTeamConstructor = sbTeamClass != null ? packetSbTeamClass.getDeclaredConstructor(String.class, int.class, Optional.class, Collection.class) : packetSbTeamClass.getDeclaredConstructor(); - packetSbTeamConstructor.setAccessible(true); - packetSbTeam = lookup.unreflectConstructor(packetSbTeamConstructor); - packetSbSerializableTeam = sbTeamClass != null ? lookup.unreflectConstructor(sbTeamClass.getConstructor(playerTeamClass)) : null; - - playerTeam = lookup.unreflectConstructor(playerTeamClass.getConstructor(scoreboardClass, String.class)); - if (VersionType.V1_17.isCurrentAtLeast()) { - Class enumSbAction = FastReflection.nmsClass("server", "ScoreboardServer$Action", "ServerScoreboard$Method"); - MethodType scoreType = MethodType.methodType(void.class, enumSbAction, String.class, String.class, int.class); - packetSbSetScore = lookup.findConstructor(packetSbScoreClass, scoreType); + packetSbSetScore = lookup.findConstructor(packetSbScoreClass, MethodType.methodType(void.class)); + if (VersionType.V1_13.isCurrentAtLeast()) { OBJECTIVE = lookup.unreflectConstructor(objectiveClass.getConstructor(scoreboardClass, String.class, objectiveCriteriaClass, CHAT_COMPONENT_CLASS, objectiveRenderTypeClass)); } else { - packetSbSetScore = lookup.findConstructor(packetSbScoreClass, MethodType.methodType(void.class)); - if (VersionType.V1_13.isCurrentAtLeast()) { - OBJECTIVE = lookup.unreflectConstructor(objectiveClass.getConstructor(scoreboardClass, String.class, objectiveCriteriaClass, CHAT_COMPONENT_CLASS, objectiveRenderTypeClass)); - } else { - OBJECTIVE = lookup.unreflectConstructor(objectiveClass.getConstructor(scoreboardClass, String.class, objectiveCriteriaClass)); - } - } - - for (Class clazz : Arrays.asList(packetSbScoreClass, packetSbTeamClass, sbTeamClass, playerTeamClass, objectiveClass)) { - if (clazz == null) { - continue; - } - Field[] fields = Arrays.stream(clazz.getDeclaredFields()) - .filter(field -> !Modifier.isStatic(field.getModifiers())) - .toArray(Field[]::new); - for (Field field : fields) { - field.setAccessible(true); - } - PACKETS.put(clazz, fields); + OBJECTIVE = lookup.unreflectConstructor(objectiveClass.getConstructor(scoreboardClass, String.class, objectiveCriteriaClass)); } + PACKET_SB_OBJ = lookup.unreflectConstructor(packetSbObjClass.getConstructor(objectiveClass, int.class)); + PACKET_SB_DISPLAY_OBJ = lookup.unreflectConstructor(packetSbDisplayObjClass.getConstructor(int.class, objectiveClass)); } - PACKET_SB_OBJ = lookup.unreflectConstructor(packetSbObjClass.getConstructor(objectiveClass, int.class)); - PACKET_SB_DISPLAY_OBJ = lookup.unreflectConstructor(packetSbDisplayObjClass.getConstructor(DISPLAY_SLOT_TYPE, objectiveClass)); PACKET_SB_SET_SCORE = packetSbSetScore; PACKET_SB_RESET_SCORE = packetSbResetScore; - PACKET_SB_TEAM = packetSbTeam; - PACKET_SB_SERIALIZABLE_TEAM = packetSbSerializableTeam; + Constructor packetSbTeamConstructor = sbTeamClass != null ? packetSbTeamClass.getDeclaredConstructor(String.class, int.class, Optional.class, Collection.class) : packetSbTeamClass.getDeclaredConstructor(); + packetSbTeamConstructor.setAccessible(true); + PACKET_SB_TEAM = lookup.unreflectConstructor(packetSbTeamConstructor); + PACKET_SB_SERIALIZABLE_TEAM = sbTeamClass != null ? lookup.unreflectConstructor(sbTeamClass.getConstructor(playerTeamClass)) : null; FIXED_NUMBER_FORMAT = fixedFormatConstructor; BLANK_NUMBER_FORMAT = blankNumberFormat; SCORE_OPTIONAL_COMPONENTS = scoreOptionalComponents; - PLAYER_TEAM = playerTeam; + + for (Class clazz : Arrays.asList(packetSbScoreClass, packetSbTeamClass, sbTeamClass, playerTeamClass, objectiveClass)) { + if (clazz == null) { + continue; + } + Field[] fields = Arrays.stream(clazz.getDeclaredFields()) + .filter(field -> !Modifier.isStatic(field.getModifiers())) + .toArray(Field[]::new); + for (Field field : fields) { + field.setAccessible(true); + } + PACKETS.put(clazz, fields); + } if (VersionType.V1_8.isCurrentAtLeast()) { String enumSbActionClass = VersionType.V1_13.isCurrentAtLeast() @@ -453,13 +446,13 @@ public synchronized void updateLines(Collection lines, Collection scores) if (oldLines.size() > linesSize) { for (int i = oldLinesCopy.size(); i > linesSize; i--) { - if (!VersionType.V1_20_3.isCurrentAtLeast()) { + if (!hasCustomScores()) { sendTeamPacket(i - 1, TeamMode.REMOVE); } sendScorePacket(i - 1, ScoreboardAction.REMOVE); oldLines.remove(0); } - } else if (!VersionType.V1_20_3.isCurrentAtLeast()) { + } else if (!hasCustomScores()) { for (int i = oldLinesCopy.size(); i < linesSize; i++) { sendScorePacket(i, ScoreboardAction.CHANGE); sendTeamPacket(i, TeamMode.CREATE, null, null); @@ -470,7 +463,7 @@ public synchronized void updateLines(Collection lines, Collection scores) for (int i = 0; i < linesSize; i++) { boolean isNewTextDifferentFromOld = !Objects.equals(getLineByScore(oldLines, i), getLineByScore(i)); boolean isNewFormatDifferentFromOld = !Objects.equals(getLineByScore(oldScores, i), getLineByScore(this.scores, i)); - if (VersionType.V1_20_3.isCurrentAtLeast() && (isNewTextDifferentFromOld || isNewFormatDifferentFromOld)) { + if (hasCustomScores() && (isNewTextDifferentFromOld || isNewFormatDifferentFromOld)) { sendModernScorePacket(i, ScoreboardAction.CHANGE); } else if (isNewTextDifferentFromOld) { sendLineChange(i); @@ -496,7 +489,7 @@ public synchronized void updateScore(int line, T score) { this.scores.set(line, score); try { - if (VersionType.V1_20_3.isCurrentAtLeast()) { + if (hasCustomScores()) { sendModernScorePacket(getScoreByLine(line), ScoreboardAction.CHANGE); } } catch (Throwable e) { @@ -551,7 +544,7 @@ public synchronized void updateScores(Collection scores) { this.scores.set(i, newScores.get(i)); try { - if (VersionType.V1_20_3.isCurrentAtLeast()) { + if (hasCustomScores()) { sendModernScorePacket(getScoreByLine(i), ScoreboardAction.CHANGE); } } catch (Throwable e) { @@ -607,7 +600,7 @@ public synchronized void delete() { try { for (int i = 0; i < this.lines.size(); i++) { - if (VersionType.V1_20_3.isCurrentAtLeast()) { + if (hasCustomScores()) { sendScorePacket(i, ScoreboardAction.REMOVE); } else { sendTeamPacket(i, TeamMode.REMOVE); @@ -635,6 +628,8 @@ public synchronized void delete() { protected abstract T emptyLine(); + protected abstract boolean hasCustomScores(); + private void checkLineNumber(int line, boolean checkInRange, boolean checkMax) { if (line < 0) { throw new IllegalArgumentException("Line number must be positive"); diff --git a/src/main/java/fr/mrmicky/fastboard/adventure/FastBoard.java b/src/main/java/fr/mrmicky/fastboard/adventure/FastBoard.java index 6717ba7..36ee97a 100644 --- a/src/main/java/fr/mrmicky/fastboard/adventure/FastBoard.java +++ b/src/main/java/fr/mrmicky/fastboard/adventure/FastBoard.java @@ -81,10 +81,9 @@ public FastBoard(Player player) { protected void sendLineChange(int score) throws Throwable { Component line = getLineByScore(score); - if (VersionType.V1_20_3.isCurrentAtLeast()) { + if (hasCustomScores()) { sendModernScorePacket(score, ScoreboardAction.CHANGE); } else { - sendScorePacket(score, ScoreboardAction.CHANGE); sendTeamPacket(score, TeamMode.UPDATE, line, null); } } @@ -115,4 +114,9 @@ protected String serializeLine(Component value) { protected Component emptyLine() { return Component.empty(); } + + @Override + protected boolean hasCustomScores() { + return VersionType.V1_20_3.isCurrentAtLeast(); + } } From af607ff09d5c06fe12fd8e32eb45e17e8603486e Mon Sep 17 00:00:00 2001 From: Dominilk01 <108392199+Dominilk01@users.noreply.github.com> Date: Tue, 4 Aug 2026 23:43:07 +0200 Subject: [PATCH 2/8] version --- README.md | 4 ++-- pom.xml | 2 +- src/main/java/fr/mrmicky/fastboard/FastBoardBase.java | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5033e94..23f4161 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ Lightweight packet-based scoreboard API for Bukkit plugins, compatible with all fr.mrmicky fastboard - 2.2.1 + 2.2.2 ``` @@ -81,7 +81,7 @@ repositories { } dependencies { - implementation 'fr.mrmicky:fastboard:2.2.1' + implementation 'fr.mrmicky:fastboard:2.2.2' } shadowJar { diff --git a/pom.xml b/pom.xml index b0fb214..6695de4 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ fr.mrmicky fastboard - 2.2.1 + 2.2.2 FastBoard Lightweight packet-based scoreboard API for Bukkit plugins. diff --git a/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java b/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java index 47ec3d5..00ee43d 100644 --- a/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java +++ b/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java @@ -44,7 +44,7 @@ * The project is on GitHub. * * @author MrMicky - * @version 2.2.1 + * @version 2.2.2 */ public abstract class FastBoardBase { From 924cc5f8442f1b9fff61a9921f63f61f4d835c53 Mon Sep 17 00:00:00 2001 From: Dominilk01 <108392199+Dominilk01@users.noreply.github.com> Date: Tue, 4 Aug 2026 23:50:17 +0200 Subject: [PATCH 3/8] java docs --- src/main/java/fr/mrmicky/fastboard/FastBoard.java | 9 +-------- src/main/java/fr/mrmicky/fastboard/FastBoardBase.java | 10 ++++++++++ .../java/fr/mrmicky/fastboard/adventure/FastBoard.java | 3 +++ 3 files changed, 14 insertions(+), 8 deletions(-) diff --git a/src/main/java/fr/mrmicky/fastboard/FastBoard.java b/src/main/java/fr/mrmicky/fastboard/FastBoard.java index 84e8ce3..5992e65 100644 --- a/src/main/java/fr/mrmicky/fastboard/FastBoard.java +++ b/src/main/java/fr/mrmicky/fastboard/FastBoard.java @@ -171,14 +171,7 @@ protected boolean hasLinesMaxLength() { } /** - * Returns whether scoreboard lines should use the score as text. - * By default, this is true only on Minecraft 1.20.3 and higher. - * Override this method for compatibility with plugins that provide multi-version support. - *

- * If not overridden, with server above 1.20.3 and player under 1.20.3 will not see text lines. - *

- * example: {@code return Via.getAPI().getPlayerVersion(getPlayer()) >= ProtocolVersion.v1_20_3.getVersion();} - * @return true should use new score as text, false for legacy team + * {@inheritDoc} */ protected boolean hasCustomScores() { return VersionType.V1_20_3.isCurrentAtLeast(); diff --git a/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java b/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java index 00ee43d..7b65547 100644 --- a/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java +++ b/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java @@ -628,6 +628,16 @@ public synchronized void delete() { protected abstract T emptyLine(); + /** + * Returns whether scoreboard lines should use the score as text. + * By default, this is true only on Minecraft 1.20.3 and higher. + * Override this method for compatibility with plugins that provide multi-version support. + *

+ * If not overridden, with server above 1.20.3 and player under 1.20.3 will not see text lines. + *

+ * example: {@code return Via.getAPI().getPlayerVersion(getPlayer()) >= ProtocolVersion.v1_20_3.getVersion();} + * @return true should use new score as text, false for legacy team + */ protected abstract boolean hasCustomScores(); private void checkLineNumber(int line, boolean checkInRange, boolean checkMax) { diff --git a/src/main/java/fr/mrmicky/fastboard/adventure/FastBoard.java b/src/main/java/fr/mrmicky/fastboard/adventure/FastBoard.java index 36ee97a..9fc66f7 100644 --- a/src/main/java/fr/mrmicky/fastboard/adventure/FastBoard.java +++ b/src/main/java/fr/mrmicky/fastboard/adventure/FastBoard.java @@ -115,6 +115,9 @@ protected Component emptyLine() { return Component.empty(); } + /** + * {@inheritDoc} + */ @Override protected boolean hasCustomScores() { return VersionType.V1_20_3.isCurrentAtLeast(); From e7e46ac07f5da6e26a8b95bb8f6295476f6c6239 Mon Sep 17 00:00:00 2001 From: Dominilk01 <108392199+Dominilk01@users.noreply.github.com> Date: Tue, 4 Aug 2026 23:54:23 +0200 Subject: [PATCH 4/8] simplify --- src/main/java/fr/mrmicky/fastboard/FastBoard.java | 7 ------- src/main/java/fr/mrmicky/fastboard/FastBoardBase.java | 4 +++- .../java/fr/mrmicky/fastboard/adventure/FastBoard.java | 8 -------- 3 files changed, 3 insertions(+), 16 deletions(-) diff --git a/src/main/java/fr/mrmicky/fastboard/FastBoard.java b/src/main/java/fr/mrmicky/fastboard/FastBoard.java index 5992e65..1e0d698 100644 --- a/src/main/java/fr/mrmicky/fastboard/FastBoard.java +++ b/src/main/java/fr/mrmicky/fastboard/FastBoard.java @@ -169,11 +169,4 @@ protected String emptyLine() { protected boolean hasLinesMaxLength() { return !VersionType.V1_13.isCurrentAtLeast(); } - - /** - * {@inheritDoc} - */ - protected boolean hasCustomScores() { - return VersionType.V1_20_3.isCurrentAtLeast(); - } } diff --git a/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java b/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java index 7b65547..99cd975 100644 --- a/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java +++ b/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java @@ -638,7 +638,9 @@ public synchronized void delete() { * example: {@code return Via.getAPI().getPlayerVersion(getPlayer()) >= ProtocolVersion.v1_20_3.getVersion();} * @return true should use new score as text, false for legacy team */ - protected abstract boolean hasCustomScores(); + protected boolean hasCustomScores() { + return VersionType.V1_20_3.isCurrentAtLeast(); + } private void checkLineNumber(int line, boolean checkInRange, boolean checkMax) { if (line < 0) { diff --git a/src/main/java/fr/mrmicky/fastboard/adventure/FastBoard.java b/src/main/java/fr/mrmicky/fastboard/adventure/FastBoard.java index 9fc66f7..e7cb577 100644 --- a/src/main/java/fr/mrmicky/fastboard/adventure/FastBoard.java +++ b/src/main/java/fr/mrmicky/fastboard/adventure/FastBoard.java @@ -114,12 +114,4 @@ protected String serializeLine(Component value) { protected Component emptyLine() { return Component.empty(); } - - /** - * {@inheritDoc} - */ - @Override - protected boolean hasCustomScores() { - return VersionType.V1_20_3.isCurrentAtLeast(); - } } From 8b923d4f458aade5f4bc34ab8b110e98da46ca2b Mon Sep 17 00:00:00 2001 From: Dominilk01 <108392199+Dominilk01@users.noreply.github.com> Date: Mon, 10 Aug 2026 22:46:01 +0200 Subject: [PATCH 5/8] readme --- README.md | 30 +++++++++++++++++++++++++++--- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 23f4161..21b897c 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,9 @@ Lightweight packet-based scoreboard API for Bukkit plugins, compatible with all Minecraft versions starting with 1.7.10. +> [!WARNING] +> If you're using ViaBackwards, please read the [ViaBackwards compatibility](#viabackwards-compatibility) section. + > [!IMPORTANT] > To use FastBoard on a 1.8 server, the server must be on 1.8.8. @@ -13,7 +16,7 @@ Lightweight packet-based scoreboard API for Bukkit plugins, compatible with all * No flickering (without using a buffer) * Compatible with all Minecraft versions starting with 1.7.10 -* Small (around 750 lines of code with the Javadoc) and no dependencies +* Small and no dependencies * Easy to use * Dynamic scoreboard size: you don't need to add/remove lines, you can directly give a string list (or array) to change all the lines * Everything is at the packet level, so it works with other plugins using scoreboard and/or teams @@ -22,6 +25,7 @@ Lightweight packet-based scoreboard API for Bukkit plugins, compatible with all * No character limit on 1.13 and higher * [RGB HEX colors support](#rgb-colors) on 1.16 and higher * [Custom number formatting](#custom-number-formatting) (including blank) for scores on 1.20.3 and higher +* [Custom score compatibility](#custom-score-compatibility) for multi-version servers * [Adventure components support](#adventure-support) * Support for both Spigot and Mojang mappings @@ -209,7 +213,9 @@ Passing a `null` value as a score will result in a reset to the default blank fo ## ViaBackwards compatibility -When using ViaBackwards on a post-1.13 server with pre-1.13 clients, older clients +### On server versions above 1.13 with clients pre 1.13 + +On a post-1.13 server with pre-1.13 clients, older clients may receive incomplete lines. To solve this problem, you can override the `hasLinesMaxLength()` method and return `true` for older clients. For example, using the ViaVersion API: ```java @@ -218,5 +224,23 @@ FastBoard board = new FastBoard(player) { public boolean hasLinesMaxLength() { return Via.getAPI().getPlayerVersion(getPlayer()) < ProtocolVersion.v1_13.getVersion(); // or just 'return true;' } -}); +}; +``` + +### On server versions above 1.20.3 with clients pre 1.20.3 + + +On a post-1.20.3 server with pre-1.20.3 clients, older clients will receive empty lines. +To solve this problem, you can override the `hasCustomScores()` method and return `false` for older clients. +For example, using the ViaVersion API: + +```java +FastBoard board = new FastBoard(player) { + @Override + public boolean hasCustomScores() { + return Via.getAPI().getPlayerVersion(getPlayer()) >= ProtocolVersion.v1_20_3.getVersion(); + } +}; ``` + +Note: if you don't want to use the score system, you can force `hasCustomScores()` to return `false` for all players, and FastBoard will always use the legacy team-based line format, which is compatible with all versions. From 2e4b0c86ae58799126e720012e593c615c6d2748 Mon Sep 17 00:00:00 2001 From: Dominilk01 <108392199+Dominilk01@users.noreply.github.com> Date: Wed, 12 Aug 2026 23:58:30 +0200 Subject: [PATCH 6/8] safeguards --- src/main/java/fr/mrmicky/fastboard/FastBoard.java | 2 +- src/main/java/fr/mrmicky/fastboard/FastBoardBase.java | 8 ++++---- .../java/fr/mrmicky/fastboard/adventure/FastBoard.java | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/main/java/fr/mrmicky/fastboard/FastBoard.java b/src/main/java/fr/mrmicky/fastboard/FastBoard.java index 1e0d698..31c25b2 100644 --- a/src/main/java/fr/mrmicky/fastboard/FastBoard.java +++ b/src/main/java/fr/mrmicky/fastboard/FastBoard.java @@ -133,7 +133,7 @@ protected void sendLineChange(int score) throws Throwable { suffix = suffix.substring(0, Math.min(maxLength, suffix.length())); } - if (hasCustomScores()) { + if (VersionType.V1_20_3.isCurrentAtLeast() && hasCustomScores()) { sendModernScorePacket(score, ScoreboardAction.CHANGE); } else { sendTeamPacket(score, TeamMode.UPDATE, prefix, suffix); diff --git a/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java b/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java index 99cd975..5142ac1 100644 --- a/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java +++ b/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java @@ -463,7 +463,7 @@ public synchronized void updateLines(Collection lines, Collection scores) for (int i = 0; i < linesSize; i++) { boolean isNewTextDifferentFromOld = !Objects.equals(getLineByScore(oldLines, i), getLineByScore(i)); boolean isNewFormatDifferentFromOld = !Objects.equals(getLineByScore(oldScores, i), getLineByScore(this.scores, i)); - if (hasCustomScores() && (isNewTextDifferentFromOld || isNewFormatDifferentFromOld)) { + if (VersionType.V1_20_3.isCurrentAtLeast() && hasCustomScores() && (isNewTextDifferentFromOld || isNewFormatDifferentFromOld)) { sendModernScorePacket(i, ScoreboardAction.CHANGE); } else if (isNewTextDifferentFromOld) { sendLineChange(i); @@ -489,7 +489,7 @@ public synchronized void updateScore(int line, T score) { this.scores.set(line, score); try { - if (hasCustomScores()) { + if (VersionType.V1_20_3.isCurrentAtLeast() && hasCustomScores()) { sendModernScorePacket(getScoreByLine(line), ScoreboardAction.CHANGE); } } catch (Throwable e) { @@ -544,7 +544,7 @@ public synchronized void updateScores(Collection scores) { this.scores.set(i, newScores.get(i)); try { - if (hasCustomScores()) { + if (VersionType.V1_20_3.isCurrentAtLeast() && hasCustomScores()) { sendModernScorePacket(getScoreByLine(i), ScoreboardAction.CHANGE); } } catch (Throwable e) { @@ -600,7 +600,7 @@ public synchronized void delete() { try { for (int i = 0; i < this.lines.size(); i++) { - if (hasCustomScores()) { + if (VersionType.V1_20_3.isCurrentAtLeast() && hasCustomScores()) { sendScorePacket(i, ScoreboardAction.REMOVE); } else { sendTeamPacket(i, TeamMode.REMOVE); diff --git a/src/main/java/fr/mrmicky/fastboard/adventure/FastBoard.java b/src/main/java/fr/mrmicky/fastboard/adventure/FastBoard.java index e7cb577..c63a5e5 100644 --- a/src/main/java/fr/mrmicky/fastboard/adventure/FastBoard.java +++ b/src/main/java/fr/mrmicky/fastboard/adventure/FastBoard.java @@ -81,7 +81,7 @@ public FastBoard(Player player) { protected void sendLineChange(int score) throws Throwable { Component line = getLineByScore(score); - if (hasCustomScores()) { + if (VersionType.V1_20_3.isCurrentAtLeast() && hasCustomScores()) { sendModernScorePacket(score, ScoreboardAction.CHANGE); } else { sendTeamPacket(score, TeamMode.UPDATE, line, null); From 84229bf8ec8d936bfc9007c522b88c0822d7782f Mon Sep 17 00:00:00 2001 From: Dominilk01 <108392199+Dominilk01@users.noreply.github.com> Date: Thu, 13 Aug 2026 00:02:34 +0200 Subject: [PATCH 7/8] typo --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 21b897c..79ac571 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,6 @@ Lightweight packet-based scoreboard API for Bukkit plugins, compatible with all * No character limit on 1.13 and higher * [RGB HEX colors support](#rgb-colors) on 1.16 and higher * [Custom number formatting](#custom-number-formatting) (including blank) for scores on 1.20.3 and higher -* [Custom score compatibility](#custom-score-compatibility) for multi-version servers * [Adventure components support](#adventure-support) * Support for both Spigot and Mojang mappings From e1bc34d11d20bb6de03c75854df69d209f56a12a Mon Sep 17 00:00:00 2001 From: Dominilk01 <108392199+Dominilk01@users.noreply.github.com> Date: Sat, 15 Aug 2026 20:46:16 +0200 Subject: [PATCH 8/8] true --- src/main/java/fr/mrmicky/fastboard/FastBoardBase.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java b/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java index 5142ac1..c71f0f0 100644 --- a/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java +++ b/src/main/java/fr/mrmicky/fastboard/FastBoardBase.java @@ -639,7 +639,7 @@ public synchronized void delete() { * @return true should use new score as text, false for legacy team */ protected boolean hasCustomScores() { - return VersionType.V1_20_3.isCurrentAtLeast(); + return true; } private void checkLineNumber(int line, boolean checkInRange, boolean checkMax) {