Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions INSTALLATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,14 @@ like [Shadow](https://gradleup.com/shadow/).
```

You will need to shade these dependencies and relocate them with [maven-shade-plugin](https://maven.apache.org/plugins/maven-shade-plugin/).

## ViaVersion awareness

For [ViaVersion awareness](https://github.com/vytskalt/scoreboard-library?tab=readme-ov-file#features) to be enabled, make sure to add `ViaVersion`
to `softdepend` or `depend` of your `plugin.yml`:

```yaml
softdepend: ["ViaVersion"]
# or
depend: ["ViaVersion"]
```
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,19 @@ Join the [Discord](https://discord.gg/v7nmTDTW8W) or create an issue for support
players
- Objectives.
- Full support for new 1.20.4 features (score display names, custom score formats)
- Doesn't require extra dependencies (assuming you're targeting modern versions of Paper)
- No extra dependencies (assuming you're targeting modern versions of Paper)
- Packet-level, meaning it works with other scoreboard plugins
- Supports [Folia](https://github.com/PaperMC/Folia)
- Fully async. All packet work is done asynchronously, so you can use the library from the main
thread without sacrificing any performance
- Automatically works with `TranslatableComponent`s. All components are translated using `GlobalTranslator` for
each player's client locale and automatically update whenever the player changes it in their settings
- ViaVersion awareness: automatically detects 1.12.2- on 1.13+ servers connected through ViaVersion and sends them
the optimal packets to maximise sidebar line lengths

## Supported server versions

All Paper and Spigot versions from 1.7.10 to 1.21.11 are now natively supported.
All Paper and Spigot versions from 1.7.10 to 1.21.11 are natively supported.

## Installation

Expand Down
2 changes: 1 addition & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
allprojects {
version = "2.6.0"
version = "2.7.0-SNAPSHOT"
group = "net.megavex"
description = "Powerful packet-level scoreboard library for Paper/Spigot servers"
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,17 +17,16 @@
import static net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.parseChar;

public final class LegacyFormatUtil {
private static final Map<NamedTextColor, Character> legacyMap;
private static final Map<NamedTextColor, ChatColor> chatColorMap;

static {
ChatColor[] values = ChatColor.values();
legacyMap = CollectionProvider.map(values.length);
chatColorMap = CollectionProvider.map(values.length);
for (ChatColor value : values) {
if (!value.isColor()) continue;

char c = value.getChar();
LegacyFormat format = Objects.requireNonNull(parseChar(c));
legacyMap.put((NamedTextColor) format.color(), c);
LegacyFormat format = Objects.requireNonNull(parseChar(value.getChar()));
chatColorMap.put((NamedTextColor) format.color(), value);
}
}

Expand Down Expand Up @@ -63,6 +62,12 @@ public static String serialize(@Nullable Component component, @Nullable Locale l
public static char getChar(@Nullable NamedTextColor color) {
if (color == null) return 'r';

return legacyMap.getOrDefault(color, '\0');
ChatColor chatColor = chatColorMap.getOrDefault(color, ChatColor.WHITE);
return chatColor.getChar();
}

public static int getIndex(@Nullable NamedTextColor color) {
ChatColor chatColor = chatColorMap.getOrDefault(color == null ? NamedTextColor.WHITE : color, ChatColor.WHITE);
return chatColor.ordinal();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import net.megavex.scoreboardlibrary.api.exception.NoPacketAdapterAvailableException;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketAdapterProvider;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

Expand All @@ -14,14 +15,14 @@ public final class PacketAdapterLoader {
private PacketAdapterLoader() {
}

public static @NotNull PacketAdapterProvider loadPacketAdapter() throws NoPacketAdapterAvailableException {
public static @NotNull PacketAdapterProvider loadPacketAdapter(Plugin plugin) throws NoPacketAdapterAvailableException {
Class<?> nmsClass = findAndLoadImplementationClass();
if (nmsClass == null) {
throw new NoPacketAdapterAvailableException();
}

try {
return (PacketAdapterProvider) nmsClass.getConstructors()[0].newInstance();
return (PacketAdapterProvider) nmsClass.getConstructors()[0].newInstance(plugin);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new RuntimeException("couldn't initialize packet adapter", e);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public ScoreboardLibraryImpl(@NotNull Plugin plugin) throws NoPacketAdapterAvail
}

this.plugin = plugin;
this.packetAdapter = PacketAdapterLoader.loadPacketAdapter();
this.packetAdapter = PacketAdapterLoader.loadPacketAdapter(plugin);
this.taskScheduler = TaskScheduler.create(plugin);

boolean localeEventExists = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ public void hide(@NotNull Collection<Player> players) {
}

pd.removeScore(players, player);
info.packetAdapter().removeTeam(players);
packetAdapter.removeTeam(players);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public void show(@NotNull Collection<Player> players) {
@Override
public void hide(@NotNull Collection<Player> players) {
handler.localeLineHandler().sidebar().packetAdapter().removeScore(players, info.player());
info.packetAdapter().removeTeam(players);
packetAdapter.removeTeam(players);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ public void addPlayer(@NotNull Player player) {
public void removePlayer(@NotNull Player player) {
TeamDisplayImpl teamDisplay = Objects.requireNonNull(displayMap.remove(player));
if (teamDisplay.players().remove(player)) {
packetAdapter.removeTeam(Collections.singleton(player));
teamDisplay.packetAdapter().removeTeam(Collections.singleton(player));
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,9 @@ public boolean tick() {

if (task instanceof TeamManagerTask.Close) {
for (ScoreboardTeamImpl team : teams.values()) {
Set<Player> removePlayers = CollectionProvider.set(internalPlayers.size());
for (TeamDisplayImpl value : team.displayMap().values()) {
removePlayers.addAll(value.players());
value.packetAdapter().removeTeam(value.players());
}
team.packetAdapter().removeTeam(removePlayers);
}

for (Player player : internalPlayers) {
Expand Down Expand Up @@ -216,16 +214,9 @@ public boolean tick() {
}
} else if (task instanceof TeamManagerTask.RemoveTeam) {
TeamManagerTask.RemoveTeam removeTeamTask = (TeamManagerTask.RemoveTeam) task;
List<Player> playersInTeam = CollectionProvider.list(removeTeamTask.team().displayMap().size());
for (Map.Entry<Player, TeamDisplayImpl> entry : removeTeamTask.team().displayMap().entrySet()) {
Player player = entry.getKey();
TeamDisplayImpl teamDisplay = entry.getValue();
if (teamDisplay.players().contains(player)) {
playersInTeam.add(player);
}
for (TeamDisplayImpl value : removeTeamTask.team().displayMap().values()) {
value.packetAdapter().removeTeam(value.players());
}

removeTeamTask.team().packetAdapter().removeTeam(playersInTeam);
} else if (task instanceof TeamManagerTask.UpdateTeamDisplay) {
TeamManagerTask.UpdateTeamDisplay updateTeamDisplayTask = (TeamManagerTask.UpdateTeamDisplay) task;
@NotNull TeamDisplayImpl teamDisplay = updateTeamDisplayTask.teamDisplay();
Expand Down
10 changes: 9 additions & 1 deletion test-env/init
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ create_server() {
printf "Creating server \033[32m$name\033[0m on port \033[32m$port\033[0m\n"

echo "eula=true" > eula.txt
printf "server-port=$port\ndifficulty=peaceful\ngenerate-structures=false\nlevel-type=flat\nallow-nether=false" > server.properties
printf "server-port=$port\ndifficulty=peaceful\ngenerate-structures=false\nlevel-type=flat\nallow-nether=false\nonline-mode=false" > server.properties
cp "$root/bukkit.yml" .

mkdir -p config
Expand All @@ -30,6 +30,10 @@ create_server() {
ln -sf "$plugin_path" plugins/test.jar
ln -sf "$jar_path" server.jar

ln -sf $root/cache/viaversion.jar plugins/viaversion.jar
ln -sf $root/cache/viabackwards.jar plugins/viabackwards.jar
ln -sf $root/cache/viarewind.jar plugins/viarewind.jar

local flags="-DPaper.IgnoreJavaVersion=true -Xms512M -Xmx1024M -XX:+UseG1GC -XX:+ParallelRefProcEnabled -XX:MaxGCPauseMillis=200 -XX:+UnlockExperimentalVMOptions -XX:+DisableExplicitGC -XX:+AlwaysPreTouch -XX:G1HeapWastePercent=5 -XX:G1MixedGCCountTarget=4 -XX:InitiatingHeapOccupancyPercent=15 -XX:G1MixedGCLiveThresholdPercent=90 -XX:G1RSetUpdatingPauseTimePercent=5 -XX:SurvivorRatio=32 -XX:+PerfDisableSharedMem -XX:MaxTenuringThreshold=1 -Dusing.aikars.flags=https://mcflags.emc.gs -Daikars.new.flags=true -XX:G1NewSizePercent=30 -XX:G1MaxNewSizePercent=40 -XX:G1HeapRegionSize=8M -XX:G1ReservePercent=20"

printf "#!/bin/sh\nnix develop .#$nixshell -c sh -c 'java $flags -jar server.jar nogui'" > start
Expand Down Expand Up @@ -97,6 +101,10 @@ download_file() {

download_file "buildtools.jar" "https://hub.spigotmc.org/jenkins/job/BuildTools/197/artifact/target/BuildTools.jar" "9a5b059ace22eaef28fb62f96970f0abe1490f06c860229b94627cd72eaf6ea5"

download_file "viaversion.jar" "https://ci.viaversion.com/job/ViaVersion/1328/artifact/build/libs/ViaVersion-5.7.2-SNAPSHOT.jar" "ddb25eb56ddf1f89388ba654f6da09ba4162f55c291f9b5c2249ef1e8330b260"
download_file "viabackwards.jar" "https://ci.viaversion.com/view/ViaBackwards/job/ViaBackwards/707/artifact/build/libs/ViaBackwards-5.7.2-SNAPSHOT.jar" "f59125c704bc3b2b7d0acd2946b2c35024cce2517a3bbb0b38b439c75d65edb0"
download_file "viarewind.jar" "https://ci.viaversion.com/view/ViaRewind/job/ViaRewind/209/artifact/build/libs/ViaRewind-4.0.15-SNAPSHOT.jar" "dd8b0db2e5469a7d8d9754b9a42bb8b81681bbe8805ddd733f2c4931dd86cbab"

# --- 1.7.10 ---
download_file "1.7.10-paper.jar" "https://fill-data.papermc.io/v1/objects/33772078d92e9dbb027602da016524ef29af5b4c12eaddac1fe2465b01108185/paper-1.7.10-2025.jar" "33772078d92e9dbb027602da016524ef29af5b4c12eaddac1fe2465b01108185"
create_server "1.7.10-paper" "jdk8"
Expand Down
9 changes: 8 additions & 1 deletion test-plugin/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ dependencies {

// bundled adventure:
implementation(libs.adventureApi)
implementation(libs.adventureTextSerializerGson)
implementation(libs.adventureTextSerializerGson) {
//exclude("com.google.code.gson")
}
implementation(libs.adventureTextSerializerLegacy)
}

tasks.shadowJar {
// relocate("com.google.gson", "net.megavex.scoreboardlibrary.testplugin.lib.gson")
// relocate("net.kyori", "net.megavex.scoreboardlibrary.testplugin.lib.kyori")
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,18 @@
import net.megavex.scoreboardlibrary.api.sidebar.component.ComponentSidebarLayout;
import net.megavex.scoreboardlibrary.api.sidebar.component.LineDrawable;
import net.megavex.scoreboardlibrary.api.sidebar.component.SidebarComponent;
import net.megavex.scoreboardlibrary.implementation.sidebar.PlayerDependantLocaleSidebar;
import net.megavex.scoreboardlibrary.implementation.sidebar.SidebarTask;
import net.megavex.scoreboardlibrary.testplugin.ScoreboardPlugin;
import net.megavex.scoreboardlibrary.testplugin.TestTranslator;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.HandlerList;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.player.PlayerQuitEvent;
import org.bukkit.event.player.PlayerToggleSneakEvent;
import org.bukkit.scheduler.BukkitTask;
import org.jetbrains.annotations.NotNull;

Expand Down Expand Up @@ -73,6 +78,15 @@ public void onQuit(final PlayerQuitEvent event) {
sidebar.removePlayer(event.getPlayer());
}

@EventHandler
public void onSneak(PlayerToggleSneakEvent event) {
if (!event.isSneaking()) return;
for (final Player player : Bukkit.getOnlinePlayers()) {
player.sendMessage("Reloading you");
((PlayerDependantLocaleSidebar) this.sidebar).taskQueue().add(new SidebarTask.ReloadPlayer(player));
}
}

@Override
public void draw(final @NotNull LineDrawable drawable) {
drawable.drawLine(empty(), ScoreFormat.styled(style(this.hueColor())));
Expand Down
1 change: 1 addition & 0 deletions test-plugin/src/main/resources/plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ name: scoreboard-library-test
version: 0.1.0
main: net.megavex.scoreboardlibrary.testplugin.ScoreboardPlugin
api-version: 1.13
softdepend: ["ViaVersion"]
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketAdapterProvider;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamsPacketAdapter;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;

@SuppressWarnings("unused")
public class PacketAdapterProviderImpl implements PacketAdapterProvider {
public PacketAdapterProviderImpl(Plugin plugin) {}

@Override
public @NotNull TeamsPacketAdapter createTeamPacketAdapter(@NotNull String teamName) {
return new TeamsPacketAdapterImpl(teamName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,6 @@ public TeamsPacketAdapterImpl(@NotNull String teamName) {
this.teamName = teamName;
}

@Override
public void removeTeam(@NotNull Iterable<Player> players) {
if (removePacket == null) {
removePacket = PacketAccessors.TEAM_CONSTRUCTOR.invoke();
PacketAccessors.TEAM_NAME_FIELD.set(removePacket, teamName);
PacketAccessors.TEAM_MODE_FIELD.set(removePacket, TeamConstants.MODE_REMOVE);
}

LegacyPacketSender.INSTANCE.sendPacket(players, removePacket);
}

@Override
public @NotNull TeamDisplayPacketAdapter createTeamDisplayAdapter(@NotNull ImmutableTeamProperties<Component> properties) {
return new AdventureTeamDisplayPacketAdapter(properties);
Expand All @@ -55,6 +44,17 @@ public AbstractTeamDisplayPacketAdapter(@NotNull ImmutableTeamProperties<C> prop
this.properties = properties;
}

@Override
public void removeTeam(@NotNull Iterable<Player> players) {
if (removePacket == null) {
removePacket = PacketAccessors.TEAM_CONSTRUCTOR.invoke();
PacketAccessors.TEAM_NAME_FIELD.set(removePacket, teamName);
PacketAccessors.TEAM_MODE_FIELD.set(removePacket, TeamConstants.MODE_REMOVE);
}

LegacyPacketSender.INSTANCE.sendPacket(players, removePacket);
}

@Override
public void sendEntries(@NotNull EntriesPacketType packetType, @NotNull Collection<Player> players, @NotNull Collection<String> entries) {
Object packet = PacketAccessors.TEAM_CONSTRUCTOR.invoke();
Expand All @@ -70,9 +70,9 @@ public void sendProperties(@NotNull PropertiesPacketType packetType, @NotNull Co
LegacyPacketSender.INSTANCE,
players,
locale -> {
String displayName = limitLegacyText(toLegacy(properties.displayName(), locale), TeamConstants.LEGACY_CHAR_LIMIT);
String prefix = limitLegacyText(toLegacy(properties.prefix(), locale), TeamConstants.LEGACY_CHAR_LIMIT);
String suffix = limitLegacyText(toLegacy(properties.suffix(), locale), TeamConstants.LEGACY_CHAR_LIMIT);
String displayName = limitLegacyText(toLegacy(properties.displayName(), locale), TeamConstants.DISPLAY_NAME_LEGACY_LIMIT);
String prefix = limitLegacyText(toLegacy(properties.prefix(), locale), TeamConstants.PREFIX_SUFFIX_LEGACY_LIMIT);
String suffix = limitLegacyText(toLegacy(properties.suffix(), locale), TeamConstants.PREFIX_SUFFIX_LEGACY_LIMIT);

Object packet = PacketAccessors.TEAM_CONSTRUCTOR.invoke();
PacketAccessors.TEAM_NAME_FIELD.set(packet, teamName);
Expand Down
7 changes: 7 additions & 0 deletions versions/modern/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,18 @@ plugins {
//id("io.papermc.paperweight.userdev") version "2.0.0-beta.19"
}

repositories {
maven("https://repo.viaversion.com")
}

dependencies {
compileOnly(project(":scoreboard-library-packet-adapter-base")) {
//exclude(group = "org.spigotmc", module = "spigot-api")
}
compileOnly(libs.spigotApi)
compileOnly("com.viaversion:viaversion-api:5.7.1")
compileOnly("io.netty:netty-buffer:4.2.10.Final")
compileOnly("io.netty:netty-handler:4.2.10.Final")
//paperweight.paperDevBundle("1.21.11-R0.1-SNAPSHOT")
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,10 +184,11 @@ private PacketAccessors() {
Object[] chatFormattings = CHAT_FORMATTING_CLASS.getEnumConstants();
FieldAccessor<Object, Object> charField = ReflectUtil.findFieldUnchecked(CHAT_FORMATTING_CLASS, 0, char.class);

outer: for (NamedTextColor color : NamedTextColor.NAMES.values()) {
outer:
for (NamedTextColor color : NamedTextColor.NAMES.values()) {
char c = LegacyFormatUtil.getChar(color);
for (Object chatFormatting : chatFormattings) {
if (c == (char)charField.get(chatFormatting)) {
if (c == (char) charField.get(chatFormatting)) {
NMS_CHAT_FORMATTING_MAP.put(color, chatFormatting);
continue outer;
}
Expand Down
Loading