diff --git a/INSTALLATION.md b/INSTALLATION.md index 35c4d880..945f8e69 100644 --- a/INSTALLATION.md +++ b/INSTALLATION.md @@ -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"] +``` diff --git a/README.md b/README.md index a12b475b..c1ce9ec1 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/build.gradle.kts b/build.gradle.kts index 9198881b..2800c9e5 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -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" } diff --git a/commons/src/main/java/net/megavex/scoreboardlibrary/implementation/commons/LegacyFormatUtil.java b/commons/src/main/java/net/megavex/scoreboardlibrary/implementation/commons/LegacyFormatUtil.java index c9be47cc..d43213b8 100644 --- a/commons/src/main/java/net/megavex/scoreboardlibrary/implementation/commons/LegacyFormatUtil.java +++ b/commons/src/main/java/net/megavex/scoreboardlibrary/implementation/commons/LegacyFormatUtil.java @@ -17,17 +17,16 @@ import static net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.parseChar; public final class LegacyFormatUtil { - private static final Map legacyMap; + private static final Map 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); } } @@ -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(); } } diff --git a/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/PacketAdapterLoader.java b/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/PacketAdapterLoader.java index 5f753f6e..c3636eee 100644 --- a/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/PacketAdapterLoader.java +++ b/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/PacketAdapterLoader.java @@ -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; @@ -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); } diff --git a/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/ScoreboardLibraryImpl.java b/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/ScoreboardLibraryImpl.java index b9f0f1b9..d41669e9 100644 --- a/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/ScoreboardLibraryImpl.java +++ b/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/ScoreboardLibraryImpl.java @@ -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; diff --git a/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/sidebar/line/locale/LegacyLocaleLine.java b/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/sidebar/line/locale/LegacyLocaleLine.java index 49c3ede4..065f43f9 100644 --- a/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/sidebar/line/locale/LegacyLocaleLine.java +++ b/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/sidebar/line/locale/LegacyLocaleLine.java @@ -157,7 +157,7 @@ public void hide(@NotNull Collection players) { } pd.removeScore(players, player); - info.packetAdapter().removeTeam(players); + packetAdapter.removeTeam(players); } } diff --git a/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/sidebar/line/locale/ModernLocaleLine.java b/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/sidebar/line/locale/ModernLocaleLine.java index 8797e13d..49ceff83 100644 --- a/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/sidebar/line/locale/ModernLocaleLine.java +++ b/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/sidebar/line/locale/ModernLocaleLine.java @@ -61,7 +61,7 @@ public void show(@NotNull Collection players) { @Override public void hide(@NotNull Collection players) { handler.localeLineHandler().sidebar().packetAdapter().removeScore(players, info.player()); - info.packetAdapter().removeTeam(players); + packetAdapter.removeTeam(players); } @Override diff --git a/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/team/ScoreboardTeamImpl.java b/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/team/ScoreboardTeamImpl.java index ac4bc73e..cd359e58 100644 --- a/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/team/ScoreboardTeamImpl.java +++ b/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/team/ScoreboardTeamImpl.java @@ -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)); } } diff --git a/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/team/TeamManagerImpl.java b/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/team/TeamManagerImpl.java index 91b09242..2d664ed6 100644 --- a/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/team/TeamManagerImpl.java +++ b/implementation/src/main/java/net/megavex/scoreboardlibrary/implementation/team/TeamManagerImpl.java @@ -173,11 +173,9 @@ public boolean tick() { if (task instanceof TeamManagerTask.Close) { for (ScoreboardTeamImpl team : teams.values()) { - Set 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) { @@ -216,16 +214,9 @@ public boolean tick() { } } else if (task instanceof TeamManagerTask.RemoveTeam) { TeamManagerTask.RemoveTeam removeTeamTask = (TeamManagerTask.RemoveTeam) task; - List playersInTeam = CollectionProvider.list(removeTeamTask.team().displayMap().size()); - for (Map.Entry 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(); diff --git a/test-env/init b/test-env/init index d18a3f17..7be4f809 100755 --- a/test-env/init +++ b/test-env/init @@ -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 @@ -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 @@ -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" diff --git a/test-plugin/build.gradle.kts b/test-plugin/build.gradle.kts index acc9ffcc..e64f1b12 100644 --- a/test-plugin/build.gradle.kts +++ b/test-plugin/build.gradle.kts @@ -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") +} diff --git a/test-plugin/src/main/java/net/megavex/scoreboardlibrary/testplugin/module/SidebarModule.java b/test-plugin/src/main/java/net/megavex/scoreboardlibrary/testplugin/module/SidebarModule.java index 6f65cc1e..b188a099 100644 --- a/test-plugin/src/main/java/net/megavex/scoreboardlibrary/testplugin/module/SidebarModule.java +++ b/test-plugin/src/main/java/net/megavex/scoreboardlibrary/testplugin/module/SidebarModule.java @@ -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; @@ -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()))); diff --git a/test-plugin/src/main/resources/plugin.yml b/test-plugin/src/main/resources/plugin.yml index 71a7848f..45a336ae 100644 --- a/test-plugin/src/main/resources/plugin.yml +++ b/test-plugin/src/main/resources/plugin.yml @@ -2,3 +2,4 @@ name: scoreboard-library-test version: 0.1.0 main: net.megavex.scoreboardlibrary.testplugin.ScoreboardPlugin api-version: 1.13 +softdepend: ["ViaVersion"] diff --git a/versions/legacy/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/legacy/PacketAdapterProviderImpl.java b/versions/legacy/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/legacy/PacketAdapterProviderImpl.java index 818ce0d6..d10e5eb2 100644 --- a/versions/legacy/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/legacy/PacketAdapterProviderImpl.java +++ b/versions/legacy/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/legacy/PacketAdapterProviderImpl.java @@ -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); diff --git a/versions/legacy/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/legacy/TeamsPacketAdapterImpl.java b/versions/legacy/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/legacy/TeamsPacketAdapterImpl.java index b60d2fd6..893801f3 100644 --- a/versions/legacy/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/legacy/TeamsPacketAdapterImpl.java +++ b/versions/legacy/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/legacy/TeamsPacketAdapterImpl.java @@ -27,17 +27,6 @@ public TeamsPacketAdapterImpl(@NotNull String teamName) { this.teamName = teamName; } - @Override - public void removeTeam(@NotNull Iterable 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 properties) { return new AdventureTeamDisplayPacketAdapter(properties); @@ -55,6 +44,17 @@ public AbstractTeamDisplayPacketAdapter(@NotNull ImmutableTeamProperties prop this.properties = properties; } + @Override + public void removeTeam(@NotNull Iterable 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 players, @NotNull Collection entries) { Object packet = PacketAccessors.TEAM_CONSTRUCTOR.invoke(); @@ -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); diff --git a/versions/modern/build.gradle.kts b/versions/modern/build.gradle.kts index 7a5426e4..cabcdffb 100644 --- a/versions/modern/build.gradle.kts +++ b/versions/modern/build.gradle.kts @@ -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") } diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/PacketAccessors.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/PacketAccessors.java index c914a987..8dc3693d 100644 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/PacketAccessors.java +++ b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/PacketAccessors.java @@ -184,10 +184,11 @@ private PacketAccessors() { Object[] chatFormattings = CHAT_FORMATTING_CLASS.getEnumConstants(); FieldAccessor 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; } diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/PacketAdapterProviderImpl.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/PacketAdapterProviderImpl.java index 1f590df8..45546ed4 100644 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/PacketAdapterProviderImpl.java +++ b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/PacketAdapterProviderImpl.java @@ -1,5 +1,8 @@ package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern; +import com.viaversion.viaversion.api.Via; +import com.viaversion.viaversion.api.ViaAPI; +import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; import net.megavex.scoreboardlibrary.implementation.commons.LineRenderingStrategy; import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketAdapterProvider; import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.objective.PaperObjectivePacketAdapter; @@ -7,32 +10,71 @@ import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.team.PaperTeamsPacketAdapterImpl; import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.team.SpigotTeamsPacketAdapter; import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernComponentProvider; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernPacketSender; import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectivePacketAdapter; import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamsPacketAdapter; import org.bukkit.entity.Player; +import org.bukkit.plugin.Plugin; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.WeakHashMap; @SuppressWarnings("unused") public class PacketAdapterProviderImpl implements PacketAdapterProvider { - public PacketAdapterProviderImpl() { + private final ViaAPI via; + private final ModernPacketSender packetSender; + private final WeakHashMap viaTeamPacketIds = new WeakHashMap<>(); + + public PacketAdapterProviderImpl(Plugin plugin) { + final String viaPlugin = "ViaVersion"; + boolean isViaEnabled = plugin.getServer().getPluginManager().isPluginEnabled(viaPlugin); + boolean isViaAllowed = plugin.getDescription().getSoftDepend().contains(viaPlugin) || plugin.getDescription().getDepend().contains(viaPlugin); + if (isViaEnabled && isViaAllowed) { + //noinspection unchecked + this.via = (ViaAPI) Via.getAPI(); + } else { + this.via = null; + } + this.packetSender = new ModernPacketSender(this.via); } @Override public @NotNull ObjectivePacketAdapter createObjectiveAdapter(@NotNull String objectiveName) { return ModernComponentProvider.IS_NATIVE_ADVENTURE - ? new PaperObjectivePacketAdapter(objectiveName) - : new SpigotObjectivePacketAdapter(objectiveName); + ? new PaperObjectivePacketAdapter(this, objectiveName) + : new SpigotObjectivePacketAdapter(this, objectiveName); } @Override public @NotNull TeamsPacketAdapter createTeamPacketAdapter(@NotNull String teamName) { return ModernComponentProvider.IS_NATIVE_ADVENTURE - ? new PaperTeamsPacketAdapterImpl(teamName) - : new SpigotTeamsPacketAdapter(teamName); + ? new PaperTeamsPacketAdapterImpl(this, teamName) + : new SpigotTeamsPacketAdapter(this, teamName); } @Override public @NotNull LineRenderingStrategy lineRenderingStrategy(@NotNull Player player) { + if (this.via != null) { + final ProtocolVersion ver = this.via.getPlayerProtocolVersion(player); + if (ver.olderThan(ProtocolVersion.v1_13)) { + //System.out.println("[DEBUG] " + player.getName() + " is legacy player"); + return LineRenderingStrategy.LEGACY; + } + } + //System.out.println("[DEBUG] is modern player"); return LineRenderingStrategy.MODERN; } + + public @Nullable ViaAPI via() { + return via; + } + + public @NotNull ModernPacketSender packetSender() { + return packetSender; + } + + public @NotNull WeakHashMap viaTeamPacketIds() { + return viaTeamPacketIds; + } } diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/RelocatedGson.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/RelocatedGson.java new file mode 100644 index 00000000..33c38173 --- /dev/null +++ b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/RelocatedGson.java @@ -0,0 +1,46 @@ +package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern; + +import com.google.gson.JsonElement; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.MethodAccessor; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ReflectUtil; + +import java.lang.invoke.MethodType; +import java.lang.reflect.InvocationTargetException; + +public final class RelocatedGson { + public static final String SERVER_GSON_PKG = "com.go".concat("ogle.gson"); + public static final boolean IS_GSON_RELOCATED; + public static final Object JSON_PARSER; + public static final MethodAccessor PARSE_STRING_METHOD; + + private RelocatedGson() { + } + + static { + IS_GSON_RELOCATED = !JsonElement.class.getName().equals(SERVER_GSON_PKG + ".JsonElement"); + + if (IS_GSON_RELOCATED) { + try { + Class jsonElementClass = Class.forName(SERVER_GSON_PKG + ".JsonElement"); + Class jsonParserClass = Class.forName(SERVER_GSON_PKG + ".JsonParser"); + JSON_PARSER = jsonParserClass.getConstructors()[0].newInstance(); + PARSE_STRING_METHOD = ReflectUtil.findMethod(jsonParserClass, false, MethodType.methodType(jsonElementClass, String.class), "parse"); + } catch (ClassNotFoundException | InvocationTargetException | InstantiationException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } else { + JSON_PARSER = null; + PARSE_STRING_METHOD = null; + } + //System.out.println("GSON RELOCATED = " + IS_GSON_RELOCATED); + } + + public static Object convertToServerGson(final JsonElement element) { + if (IS_GSON_RELOCATED) { + assert PARSE_STRING_METHOD != null; + //System.out.println("fixed"); + return PARSE_STRING_METHOD.invoke(JSON_PARSER, element.toString()); + } + return element; + } +} diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/AbstractObjectivePacketAdapter.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/AbstractObjectivePacketAdapter.java index 60005fc1..d7e5e377 100644 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/AbstractObjectivePacketAdapter.java +++ b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/AbstractObjectivePacketAdapter.java @@ -4,7 +4,7 @@ import net.megavex.scoreboardlibrary.api.objective.ObjectiveRenderType; import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType; import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernPacketSender; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAdapterProviderImpl; import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectiveConstants; import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectivePacketAdapter; import org.bukkit.entity.Player; @@ -16,10 +16,12 @@ import java.util.Optional; public abstract class AbstractObjectivePacketAdapter implements ObjectivePacketAdapter { + protected final PacketAdapterProviderImpl provider; protected final String objectiveName; private Object removePacket; - public AbstractObjectivePacketAdapter(@NotNull String objectiveName) { + public AbstractObjectivePacketAdapter(@NotNull PacketAdapterProviderImpl provider, @NotNull String objectiveName) { + this.provider = provider; this.objectiveName = objectiveName; } @@ -30,7 +32,7 @@ public AbstractObjectivePacketAdapter(@NotNull String objectiveName) { @Override public void display(@NotNull Collection players, @NotNull ObjectiveDisplaySlot slot) { - ModernPacketSender.INSTANCE.sendPacket(players, createDisplayPacket(slot)); + provider.packetSender().sendPacket(players, createDisplayPacket(slot)); } @Override @@ -40,7 +42,7 @@ public void remove(@NotNull Collection players) { PacketAccessors.OBJECTIVE_NAME_FIELD.set(removePacket, objectiveName); PacketAccessors.OBJECTIVE_MODE_FIELD.set(removePacket, ObjectiveConstants.MODE_REMOVE); } - ModernPacketSender.INSTANCE.sendPacket(players, removePacket); + provider.packetSender().sendPacket(players, removePacket); } @Override @@ -53,7 +55,7 @@ public void removeScore(@NotNull Collection players, @NotNull String ent packet = Objects.requireNonNull(PacketAccessors.SCORE_CONSTRUCTOR) .invoke(PacketAccessors.SCORE_1_20_2_METHOD_REMOVE, objectiveName, entry, 0); } - ModernPacketSender.INSTANCE.sendPacket(players, packet); + provider.packetSender().sendPacket(players, packet); } protected @NotNull Object createDisplayPacket(@NotNull ObjectiveDisplaySlot displaySlot) { diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/PaperObjectivePacketAdapter.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/PaperObjectivePacketAdapter.java index 1e7fbc1a..d4493d73 100644 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/PaperObjectivePacketAdapter.java +++ b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/PaperObjectivePacketAdapter.java @@ -5,8 +5,8 @@ import net.megavex.scoreboardlibrary.api.objective.ScoreFormat; import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType; import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAdapterProviderImpl; import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernComponentProvider; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernPacketSender; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -14,8 +14,8 @@ import java.util.Collection; public class PaperObjectivePacketAdapter extends AbstractObjectivePacketAdapter { - public PaperObjectivePacketAdapter(@NotNull String objectiveName) { - super(objectiveName); + public PaperObjectivePacketAdapter(@NotNull PacketAdapterProviderImpl provider, @NotNull String objectiveName) { + super(provider, objectiveName); } @Override @@ -23,7 +23,7 @@ public void sendScore(@NotNull Collection players, @NotNull String entry Object nmsDisplay = display == null ? null : ModernComponentProvider.fromAdventure(display, null); Object numberFormat = ScoreFormatConverter.convert(null, scoreFormat); Object packet = createScorePacket(entry, value, nmsDisplay, numberFormat); - ModernPacketSender.INSTANCE.sendPacket(players, packet); + provider.packetSender().sendPacket(players, packet); } @Override @@ -37,6 +37,6 @@ public void sendProperties( Object nmsValue = PacketAccessors.fromAdventureComponent(value); Object numberFormat = ScoreFormatConverter.convert(null, scoreFormat); Object packet = createObjectivePacket(packetType, nmsValue, renderType, numberFormat); - ModernPacketSender.INSTANCE.sendPacket(players, packet); + provider.packetSender().sendPacket(players, packet); } } diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/ScoreFormatConverter.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/ScoreFormatConverter.java index a19327d3..cef2af65 100644 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/ScoreFormatConverter.java +++ b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/ScoreFormatConverter.java @@ -1,8 +1,8 @@ package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.objective; -import com.google.gson.JsonElement; import net.megavex.scoreboardlibrary.api.objective.ScoreFormat; import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.RelocatedGson; import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernComponentProvider; import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ConstructorAccessor; import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ReflectUtil; @@ -50,7 +50,7 @@ private ScoreFormatConverter() { if (format == ScoreFormat.blank()) { return BLANK; } else if (format instanceof ScoreFormat.Styled) { - JsonElement json = gson().serializer().toJsonTree(((ScoreFormat.Styled) format).style()); + Object json = RelocatedGson.convertToServerGson(gson().serializer().toJsonTree(((ScoreFormat.Styled) format).style())); Object result = PacketAccessors.CODEC_PARSE.invoke(STYLE_CODEC, PacketAccessors.JSON_OPS, json); //noinspection OptionalGetWithoutIsPresent Object style = ((Optional) PacketAccessors.RESULT_UNWRAP_METHOD.invoke(result)).get(); diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/SpigotObjectivePacketAdapter.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/SpigotObjectivePacketAdapter.java index b88fd1f1..e86bb66f 100644 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/SpigotObjectivePacketAdapter.java +++ b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/objective/SpigotObjectivePacketAdapter.java @@ -4,8 +4,8 @@ import net.megavex.scoreboardlibrary.api.objective.ObjectiveRenderType; import net.megavex.scoreboardlibrary.api.objective.ScoreFormat; import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAdapterProviderImpl; import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernComponentProvider; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernPacketSender; import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.LocalePacketUtil; import org.bukkit.entity.Player; import org.jetbrains.annotations.NotNull; @@ -14,13 +14,13 @@ import java.util.Collection; public class SpigotObjectivePacketAdapter extends AbstractObjectivePacketAdapter { - public SpigotObjectivePacketAdapter(@NotNull String objectiveName) { - super(objectiveName); + public SpigotObjectivePacketAdapter(@NotNull PacketAdapterProviderImpl provider, @NotNull String objectiveName) { + super(provider, objectiveName); } @Override public void sendScore(@NotNull Collection players, @NotNull String entry, int value, @Nullable Component display, @Nullable ScoreFormat scoreFormat) { - LocalePacketUtil.sendLocalePackets(ModernPacketSender.INSTANCE, players, locale -> { + LocalePacketUtil.sendLocalePackets(provider.packetSender(), players, locale -> { Object nmsDisplay = display == null ? null : ModernComponentProvider.fromAdventure(display, locale); Object numberFormat = ScoreFormatConverter.convert(locale, scoreFormat); return createScorePacket(entry, value, nmsDisplay, numberFormat); @@ -36,7 +36,7 @@ public void sendProperties( @Nullable ScoreFormat scoreFormat ) { LocalePacketUtil.sendLocalePackets( - ModernPacketSender.INSTANCE, + provider.packetSender(), players, locale -> { Object numberFormat = ScoreFormatConverter.convert(locale, scoreFormat); diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/AbstractTeamsPacketAdapterImpl.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/AbstractTeamsPacketAdapterImpl.java index 40376a57..41e5a943 100644 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/AbstractTeamsPacketAdapterImpl.java +++ b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/AbstractTeamsPacketAdapterImpl.java @@ -4,7 +4,7 @@ import net.kyori.adventure.text.format.NamedTextColor; import net.megavex.scoreboardlibrary.implementation.packetAdapter.ImmutableTeamProperties; import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernPacketSender; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAdapterProviderImpl; import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.EntriesPacketType; import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamConstants; import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamDisplayPacketAdapter; @@ -19,33 +19,18 @@ import java.util.Optional; public abstract class AbstractTeamsPacketAdapterImpl implements TeamsPacketAdapter { + protected final PacketAdapterProviderImpl provider; protected final String teamName; private Object removePacket; - public AbstractTeamsPacketAdapterImpl(@NotNull String teamName) { + public AbstractTeamsPacketAdapterImpl(@NotNull PacketAdapterProviderImpl provider, @NotNull String teamName) { + this.provider = provider; this.teamName = teamName; } @Override - public void removeTeam(@NotNull Iterable players) { - if (removePacket == null) { - if (PacketAccessors.IS_1_17_OR_ABOVE) { - removePacket = PacketAccessors.TEAM_PACKET_CONSTRUCTOR.invoke( - teamName, - TeamConstants.MODE_REMOVE, - null, - Collections.emptyList() - ); - } else { - assert PacketAccessors.TEAM_NAME_FIELD != null; - assert PacketAccessors.TEAM_MODE_FIELD != null; - - removePacket = PacketAccessors.TEAM_PACKET_CONSTRUCTOR.invoke(); - PacketAccessors.TEAM_NAME_FIELD.set(removePacket, teamName); - PacketAccessors.TEAM_MODE_FIELD.set(removePacket, TeamConstants.MODE_REMOVE); - } - } - ModernPacketSender.INSTANCE.sendPacket(players, removePacket); + public @NotNull TeamDisplayPacketAdapter createLegacyTeamDisplayAdapter(@NotNull ImmutableTeamProperties properties) { + return new LegacyTeamDisplayPacketAdapter(provider, teamName, properties); } public abstract class TeamDisplayPacketAdapterImpl implements TeamDisplayPacketAdapter { @@ -55,6 +40,28 @@ public TeamDisplayPacketAdapterImpl(ImmutableTeamProperties propertie this.properties = properties; } + @Override + public void removeTeam(@NotNull Iterable players) { + if (removePacket == null) { + if (PacketAccessors.IS_1_17_OR_ABOVE) { + removePacket = PacketAccessors.TEAM_PACKET_CONSTRUCTOR.invoke( + teamName, + TeamConstants.MODE_REMOVE, + null, + Collections.emptyList() + ); + } else { + assert PacketAccessors.TEAM_NAME_FIELD != null; + assert PacketAccessors.TEAM_MODE_FIELD != null; + + removePacket = PacketAccessors.TEAM_PACKET_CONSTRUCTOR.invoke(); + PacketAccessors.TEAM_NAME_FIELD.set(removePacket, teamName); + PacketAccessors.TEAM_MODE_FIELD.set(removePacket, TeamConstants.MODE_REMOVE); + } + } + provider.packetSender().sendPacket(players, removePacket); + } + @Override public void sendEntries(@NotNull EntriesPacketType packetType, @NotNull Collection players, @NotNull Collection entries) { if (PacketAccessors.IS_1_17_OR_ABOVE) { @@ -64,7 +71,7 @@ public void sendEntries(@NotNull EntriesPacketType packetType, @NotNull Collecti Optional.empty(), entries ); - ModernPacketSender.INSTANCE.sendPacket(players, packet); + provider.packetSender().sendPacket(players, packet); } else { assert PacketAccessors.TEAM_NAME_FIELD != null; assert PacketAccessors.TEAM_MODE_FIELD != null; @@ -74,7 +81,7 @@ public void sendEntries(@NotNull EntriesPacketType packetType, @NotNull Collecti PacketAccessors.TEAM_NAME_FIELD.set(packet, teamName); PacketAccessors.TEAM_MODE_FIELD.set(packet, TeamConstants.mode(packetType)); PacketAccessors.TEAM_ENTRIES_FIELD.set(packet, entries); - ModernPacketSender.INSTANCE.sendPacket(players, packet); + provider.packetSender().sendPacket(players, packet); } } diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/LegacyTeamDisplayPacketAdapter.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/LegacyTeamDisplayPacketAdapter.java new file mode 100644 index 00000000..ad6b5ce1 --- /dev/null +++ b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/LegacyTeamDisplayPacketAdapter.java @@ -0,0 +1,162 @@ +package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.team; + +import com.viaversion.viaversion.api.ViaAPI; +import com.viaversion.viaversion.api.connection.UserConnection; +import com.viaversion.viaversion.api.protocol.Protocol; +import com.viaversion.viaversion.api.protocol.packet.PacketType; +import com.viaversion.viaversion.api.protocol.packet.State; +import com.viaversion.viaversion.api.protocol.packet.provider.PacketTypeMap; +import com.viaversion.viaversion.api.protocol.version.ProtocolVersion; +import com.viaversion.viaversion.api.type.Types; +import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; +import net.megavex.scoreboardlibrary.implementation.commons.LegacyFormatUtil; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.ImmutableTeamProperties; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAdapterProviderImpl; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.EntriesPacketType; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamConstants; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamDisplayPacketAdapter; +import org.bukkit.entity.Player; +import org.jetbrains.annotations.NotNull; + +import java.util.Collection; + +import static net.megavex.scoreboardlibrary.implementation.commons.LegacyFormatUtil.limitLegacyText; + +public final class LegacyTeamDisplayPacketAdapter implements TeamDisplayPacketAdapter { + private final PacketAdapterProviderImpl provider; + private final String teamName; + private final ImmutableTeamProperties properties; + + public LegacyTeamDisplayPacketAdapter(final PacketAdapterProviderImpl provider, final String teamName, final ImmutableTeamProperties properties) { + this.provider = provider; + this.teamName = teamName; + this.properties = properties; + } + + // have to send every team packet from viaversion otherwise it won't work on 1.7 players + // because ViaRewind's 1.8->1.7 conversion cancels team packets it doesn't recognize and fucks everything up + // https://github.com/ViaVersion/ViaRewind/blob/e0c1f5311521bf062bbdae9f96dc9149d8c13e28/common/src/main/java/com/viaversion/viarewind/protocol/v1_8to1_7_6_10/rewriter/ScoreboardPacketRewriter1_8.java#L179 + + @Override + public void removeTeam(@NotNull Iterable players) { + ViaAPI via = provider.via(); + assert via != null; + for (final Player player : players) { + final UserConnection conn = via.getConnection(player.getUniqueId()); + if (conn == null) continue; + + final ByteBuf buf = Unpooled.buffer(128); + Types.VAR_INT.writePrimitive(buf, teamsPacketId(player, conn)); + Types.STRING.write(buf, teamName); + Types.BYTE.writePrimitive(buf, (byte) TeamConstants.MODE_REMOVE); + + via.sendRawPacket(player.getUniqueId(), buf); + } + } + + @Override + public void sendEntries(@NotNull EntriesPacketType packetType, @NotNull Collection players, @NotNull Collection entries) { + ViaAPI via = provider.via(); + assert via != null; + for (final Player player : players) { + final UserConnection conn = via.getConnection(player.getUniqueId()); + if (conn == null) continue; + + final ByteBuf buf = Unpooled.buffer(128); + Types.VAR_INT.writePrimitive(buf, teamsPacketId(player, conn)); + + Types.STRING.write(buf, teamName); + + Types.BYTE.writePrimitive(buf, (byte) TeamConstants.mode(packetType)); + + if (conn.getProtocolInfo().protocolVersion().olderThanOrEqualTo(ProtocolVersion.v1_7_6)) { + Types.SHORT.writePrimitive(buf, (short) properties.syncedEntries().size()); + } else { + Types.VAR_INT.writePrimitive(buf, properties.syncedEntries().size()); + } + + for (final String entry : entries) { + Types.STRING.write(buf, entry); + } + + via.sendRawPacket(player.getUniqueId(), buf); + } + } + + @Override + public void sendProperties(@NotNull PropertiesPacketType packetType, @NotNull Collection players) { + ViaAPI via = provider.via(); + assert via != null; + for (final Player player : players) { + final UserConnection conn = via.getConnection(player.getUniqueId()); + if (conn == null) continue; + + final ByteBuf buf = Unpooled.buffer(128); + Types.VAR_INT.writePrimitive(buf, teamsPacketId(player, conn)); + + Types.STRING.write(buf, teamName); + + Types.BYTE.writePrimitive(buf, (byte) TeamConstants.mode(packetType)); + + final String displayName = limitLegacyText(properties.displayName(), TeamConstants.DISPLAY_NAME_LEGACY_LIMIT); + final String prefix = limitLegacyText(properties.prefix(), TeamConstants.PREFIX_SUFFIX_LEGACY_LIMIT); + final String suffix = limitLegacyText(properties.suffix(), TeamConstants.PREFIX_SUFFIX_LEGACY_LIMIT); + Types.STRING.write(buf, displayName); + Types.STRING.write(buf, prefix); + Types.STRING.write(buf, suffix); + + Types.BYTE.writePrimitive(buf, (byte) properties.packOptions()); + + final boolean is1_7 = conn.getProtocolInfo().protocolVersion().olderThanOrEqualTo(ProtocolVersion.v1_7_6); + if (!is1_7) { + Types.STRING.write(buf, properties.nameTagVisibility().key()); + } + + if (conn.getProtocolInfo().protocolVersion().newerThanOrEqualTo(ProtocolVersion.v1_9)) { + Types.STRING.write(buf, properties.collisionRule().key()); + } + + if (!is1_7) { + Types.BYTE.writePrimitive(buf, (byte) LegacyFormatUtil.getIndex(properties.playerColor())); + } + + if (packetType == PropertiesPacketType.CREATE) { + if (is1_7) { + Types.SHORT.writePrimitive(buf, (short) properties.syncedEntries().size()); + } else { + Types.VAR_INT.writePrimitive(buf, properties.syncedEntries().size()); + } + + for (final String entry : properties.syncedEntries()) { + Types.STRING.write(buf, entry); + } + } + + via.sendRawPacket(player.getUniqueId(), buf); + } + } + + private @NotNull Integer teamsPacketId(final Player player, final UserConnection conn) { + ViaAPI via = provider.via(); + assert via != null; + + Integer teamsPacketId = this.provider.viaTeamPacketIds().get(player); + if (teamsPacketId != null) return teamsPacketId; + + for (final Protocol proto : conn.getProtocolInfo().getPipeline().pipes()) { + final PacketTypeMap map = proto.getPacketTypesProvider().mappedClientboundPacketTypes().get(State.PLAY); + if (map == null) continue; + final PacketType type = map.typeByName("SET_PLAYER_TEAM"); + if (type == null) continue; + teamsPacketId = type.getId(); + break; + } + if (teamsPacketId == null) { + throw new IllegalStateException("team packet id for " + conn.getProtocolInfo().protocolVersion() + " not found"); + } + this.provider.viaTeamPacketIds().put(player, teamsPacketId); + return teamsPacketId; + } +} diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/PaperTeamsPacketAdapterImpl.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/PaperTeamsPacketAdapterImpl.java index 42c61654..8dd0c7e1 100644 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/PaperTeamsPacketAdapterImpl.java +++ b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/PaperTeamsPacketAdapterImpl.java @@ -1,12 +1,11 @@ package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.team; import com.google.common.collect.ImmutableList; -import com.google.gson.Gson; import net.kyori.adventure.text.Component; import net.megavex.scoreboardlibrary.implementation.packetAdapter.ImmutableTeamProperties; import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType; import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernPacketSender; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAdapterProviderImpl; import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamConstants; import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamDisplayPacketAdapter; import org.bukkit.entity.Player; @@ -18,8 +17,8 @@ import java.util.Optional; public class PaperTeamsPacketAdapterImpl extends AbstractTeamsPacketAdapterImpl { - public PaperTeamsPacketAdapterImpl(@NotNull String teamName) { - super(teamName); + public PaperTeamsPacketAdapterImpl(PacketAdapterProviderImpl provider, @NotNull String teamName) { + super(provider, teamName); } @Override @@ -88,10 +87,10 @@ public void sendProperties(@NotNull PropertiesPacketType packetType, @NotNull Co switch (packetType) { case CREATE: - ModernPacketSender.INSTANCE.sendPacket(players, createPacket); + provider.packetSender().sendPacket(players, createPacket); break; case UPDATE: - ModernPacketSender.INSTANCE.sendPacket(players, updatePacket); + provider.packetSender().sendPacket(players, updatePacket); break; } } diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/SpigotTeamsPacketAdapter.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/SpigotTeamsPacketAdapter.java index 36d3ec94..369bfdff 100644 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/SpigotTeamsPacketAdapter.java +++ b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/team/SpigotTeamsPacketAdapter.java @@ -5,8 +5,8 @@ import net.megavex.scoreboardlibrary.implementation.packetAdapter.ImmutableTeamProperties; import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType; import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAdapterProviderImpl; import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernComponentProvider; -import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernPacketSender; import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamConstants; import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamDisplayPacketAdapter; import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.LocalePacketUtil; @@ -19,8 +19,8 @@ import java.util.Optional; public class SpigotTeamsPacketAdapter extends AbstractTeamsPacketAdapterImpl { - public SpigotTeamsPacketAdapter(@NotNull String teamName) { - super(teamName); + public SpigotTeamsPacketAdapter(PacketAdapterProviderImpl provider, @NotNull String teamName) { + super(provider, teamName); } @Override @@ -37,7 +37,7 @@ public TeamDisplayPacketAdapterImpl(ImmutableTeamProperties propertie public void sendProperties(@NotNull PropertiesPacketType packetType, @NotNull Collection players) { Collection entries = ImmutableList.copyOf(properties.syncedEntries()); LocalePacketUtil.sendLocalePackets( - ModernPacketSender.INSTANCE, + provider.packetSender(), players, locale -> { diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/util/ModernComponentProvider.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/util/ModernComponentProvider.java index 9f127520..e47a9c39 100644 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/util/ModernComponentProvider.java +++ b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/util/ModernComponentProvider.java @@ -1,9 +1,9 @@ package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util; -import com.google.gson.JsonElement; import net.kyori.adventure.text.Component; import net.kyori.adventure.translation.GlobalTranslator; import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors; +import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.RelocatedGson; import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.MinecraftClasses; import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ReflectUtil; import org.jetbrains.annotations.NotNull; @@ -62,7 +62,7 @@ private ModernComponentProvider() { for (Method method : PacketAccessors.CHAT_SERIALIZER_CLASS.getMethods()) { if (method.getParameterCount() >= 1 && method.getParameterCount() <= 2 && - method.getParameterTypes()[0] == JsonElement.class + method.getParameterTypes()[0].getName().equals(RelocatedGson.SERVER_GSON_PKG + ".JsonElement") ) { try { handle = MethodHandles.lookup().unreflect(method); @@ -91,7 +91,8 @@ private ModernComponentProvider() { if (locale != null) { translated = GlobalTranslator.render(adventure, locale); } - JsonElement json = gson().serializeToTree(translated); + + Object json = RelocatedGson.convertToServerGson(gson().serializeToTree(translated)); if (FROM_JSON_METHOD == null) { // 1.21.6+ diff --git a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/util/ModernPacketSender.java b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/util/ModernPacketSender.java index 13200631..c3f1545e 100644 --- a/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/util/ModernPacketSender.java +++ b/versions/modern/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/modern/util/ModernPacketSender.java @@ -1,5 +1,8 @@ package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util; +import com.viaversion.viaversion.api.ViaAPI; +import com.viaversion.viaversion.api.connection.UserConnection; +import io.netty.channel.Channel; import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender; import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors; import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.MinecraftClasses; @@ -11,13 +14,14 @@ import java.lang.reflect.Field; public final class ModernPacketSender implements PacketSender { - public static final ModernPacketSender INSTANCE = new ModernPacketSender(); - private static final MethodHandle GET_HANDLE; private static final MethodHandle PLAYER_CONNECTION; private static final MethodHandle SEND_PACKET; - private ModernPacketSender() { + private final ViaAPI via; + + public ModernPacketSender(final ViaAPI via) { + this.via = via; } static { @@ -46,7 +50,7 @@ private ModernPacketSender() { } } } - + if (playerConnection == null) { throw new ExceptionInInitializerError("failed to find player connection field"); } @@ -74,10 +78,27 @@ private ModernPacketSender() { @Override public void sendPacket(Player player, Object packet) { + if (this.via != null) { + final UserConnection conn = this.via.getConnection(player.getUniqueId()); + if (conn != null) { + final Channel channel = conn.getChannel(); + if (channel != null) { + // Paper has some "network optimization" patch that makes the NMS sendPacket method + // not always send the packet immediately to the player's netty channel, + // and this is a problem when we send some packets using ViaVersion API because it always sends + // the packet to the channel immediately, so the packet order between NMS and ViaVersion API can + // get messed up. So for ViaVersion players we send the packet directly to the player's channel + // bypassing Paper logic to work around this. Fortunately ViaVersion provides easy access to the player's channel. + channel.eventLoop().execute(() -> channel.writeAndFlush(packet)); + return; + } + } + } + try { - Object handle = GET_HANDLE.invoke(player); - Object connection = PLAYER_CONNECTION.invoke(handle); - SEND_PACKET.invoke(connection, packet); + Object handle = GET_HANDLE.invoke(player); + Object connection = PLAYER_CONNECTION.invoke(handle); + SEND_PACKET.invoke(connection, packet); } catch (Throwable e) { throw new IllegalStateException("couldn't send packet to player", e); } diff --git a/versions/packet-adapter-base/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/team/TeamConstants.java b/versions/packet-adapter-base/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/team/TeamConstants.java index 28c803fb..d4303aea 100644 --- a/versions/packet-adapter-base/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/team/TeamConstants.java +++ b/versions/packet-adapter-base/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/team/TeamConstants.java @@ -4,7 +4,8 @@ import org.jetbrains.annotations.NotNull; public final class TeamConstants { - public static final int LEGACY_CHAR_LIMIT = 16; + public static final int DISPLAY_NAME_LEGACY_LIMIT = 32; + public static final int PREFIX_SUFFIX_LEGACY_LIMIT = 16; public static final int MODE_CREATE = 0, MODE_REMOVE = 1, diff --git a/versions/packet-adapter-base/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/team/TeamDisplayPacketAdapter.java b/versions/packet-adapter-base/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/team/TeamDisplayPacketAdapter.java index 800b08df..73b3bdfc 100644 --- a/versions/packet-adapter-base/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/team/TeamDisplayPacketAdapter.java +++ b/versions/packet-adapter-base/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/team/TeamDisplayPacketAdapter.java @@ -7,6 +7,8 @@ import java.util.Collection; public interface TeamDisplayPacketAdapter { + void removeTeam(@NotNull Iterable players); + default void updateTeamPackets() { } diff --git a/versions/packet-adapter-base/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/team/TeamsPacketAdapter.java b/versions/packet-adapter-base/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/team/TeamsPacketAdapter.java index a7d46c9c..49e0e36a 100644 --- a/versions/packet-adapter-base/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/team/TeamsPacketAdapter.java +++ b/versions/packet-adapter-base/src/main/java/net/megavex/scoreboardlibrary/implementation/packetAdapter/team/TeamsPacketAdapter.java @@ -6,8 +6,6 @@ import org.jetbrains.annotations.NotNull; public interface TeamsPacketAdapter { - void removeTeam(@NotNull Iterable players); - @NotNull TeamDisplayPacketAdapter createTeamDisplayAdapter(@NotNull ImmutableTeamProperties properties); default @NotNull TeamDisplayPacketAdapter createLegacyTeamDisplayAdapter(@NotNull ImmutableTeamProperties properties) {