Compare commits

...
84 Commits
Author SHA1 Message Date
vytskalt f277ee4de2 release 2.7.0 2026-02-27 20:13:59 +02:00
vytskalt 1613c8d2c5 testing stuff 2026-02-27 20:11:10 +02:00
vytskalt 91d74ddf4e Merge remote-tracking branch 'origin/main' into dev123 2026-02-25 23:35:14 +02:00
vytskalt 45544a71c7 Merge pull request #128 from huanmeng-qwq/add-entries
add bulk entry add/remove API
2026-02-25 17:17:56 +02:00
huanmeng-qwq a454dff208 fix bulk entry add/remove sending invalid entries 2026-02-25 22:26:14 +08:00
huanmeng-qwq 6075333d7b impl noop 2026-02-25 20:38:41 +08:00
huanmeng-qwq 9d16e5fe32 add bulk entry add/remove API 2026-02-25 20:35:11 +08:00
vytskalt b8babc7542 Merge branch 'main' into dev123 2026-02-24 23:15:30 +02:00
vytskalt 80cb76e98b removePlayerFuture API 2026-02-24 23:14:51 +02:00
vytskalt 1e6c8a033a docs updates 2026-02-24 22:32:36 +02:00
vytskalt cd09080ced cleaned up 2026-02-22 22:00:53 +02:00
vytskalt bf01337fcf workaround relocated gson 2026-02-22 21:49:33 +02:00
vytskalt ef3998bd3b fix 1.7 2026-02-20 17:58:48 +02:00
vytskalt 38d006cf6e almost 2026-02-20 17:24:45 +02:00
vytskalt d19f997846 resolve packet ordering 2026-02-20 16:33:06 +02:00
vytskalt 92d3281fad pull 2026-02-19 23:33:34 +02:00
vytskalt 538edffa1a begin via integration 2026-02-19 23:24:40 +02:00
vytskalt 1c82d3025f release 2.6.0 2026-02-14 12:19:59 +02:00
vytskalt d7558778bb test legacy versions 2026-02-14 01:53:34 +02:00
vytskalt b16dd7cbac beautiful 2026-02-14 01:31:04 +02:00
vytskalt ce1533ded5 yeet packetevents adapter 2026-02-14 01:21:47 +02:00
vytskalt 6158c71cc9 Merge pull request #125 from vytskalt/full-modern
1.13 - 1.16.5 modern support
2026-02-14 01:17:38 +02:00
vytskalt d9fcd8013c finish up 2026-02-14 01:16:11 +02:00
vytskalt a4b9c13bef works on 1.13 2026-02-13 19:20:50 +02:00
vytskalt b5bd7a543d implement 1.16.5 2026-02-11 22:38:45 +02:00
vytskalt 33144908eb begin pre 1.17 support 2026-02-07 00:16:17 +02:00
vytskalt 2c84532f79 release 2.5.0 2026-02-06 22:13:45 +02:00
vytskalt eeff691c95 native adventure test, remove references to mojmap variant 2026-02-06 22:12:36 +02:00
vytskalt 97e027c2d3 Merge pull request #121 from MegavexNetwork/modern-reflect-v2
Remove paperweight
2026-02-06 22:01:21 +02:00
vytskalt 59dbad8311 final touches 2026-02-06 21:59:34 +02:00
vytskalt c15e8c2b9d add spigot servers 2026-02-06 21:15:44 +02:00
vytskalt 8066116d7e fix for 1.20.2 and 1.17 2026-02-06 18:17:40 +02:00
vytskalt 96677233af add remaining paper servers 2026-02-06 17:34:13 +02:00
vytskalt c57aa1ee22 testing poc 2026-02-03 18:10:24 +02:00
vytskalt 8de8502b63 init test env 2026-02-03 02:24:48 +02:00
vytskalt 206bd2e56a Merge branch 'main' into modern-reflect-v2 2026-02-03 01:04:27 +02:00
vytskalt 54135c73b8 fix duplicate objective create packet 2026-02-03 01:03:39 +02:00
vytskalt f23d2e2804 init test-plugin 2026-01-25 23:14:39 +02:00
vytskalt 904b7e5475 works on spigot 2026-01-25 22:47:50 +02:00
vytskalt 7b9d274683 forgot to commit 2026-01-25 17:42:51 +02:00
vytskalt 9e6e1d506d part 7, remove paperweight 2025-12-11 20:39:09 +02:00
vytskalt 96a18d1632 part 6 2025-12-11 20:04:27 +02:00
vytskalt 2092517215 part 5 2025-12-11 19:21:15 +02:00
vytskalt dd735360ce part 4 2025-12-11 18:31:37 +02:00
vytskalt bb74f217f2 part 3 2025-12-10 22:00:54 +02:00
vytskalt b0a69da47c part 2 2025-12-10 21:27:27 +02:00
vytskalt d845cca062 part 1 2025-12-10 21:00:49 +02:00
vytskalt d71e029052 begin 2025-12-10 18:57:26 +02:00
vytskalt 4eb7ed3cd8 update dependencies 2025-12-10 18:33:10 +02:00
vytskalt f8fb6c2a8c release 2.4.4 2025-12-10 18:05:46 +02:00
vytskalt a3e0c6795c mark 1.21.11 as supported 2025-12-10 18:04:56 +02:00
vytskalt 9e37c3e627 release: 2.4.3 2025-10-09 23:26:06 +03:00
vytskalt 8fdf24e07a 1.21.10 2025-10-09 23:23:46 +03:00
vytskalt 1227b28189 ci fix part 2 2025-09-30 19:44:52 +03:00
vytskalt 6fe6200cda ci fix 2025-09-30 19:29:00 +03:00
vytskalt fb4863a9e3 release 2.4.2 2025-09-30 19:16:48 +03:00
vytskalt a298842bce 1.21.9 2025-09-30 19:15:53 +03:00
vytskalt 16314a7d14 fix build 2025-09-30 18:39:29 +03:00
vytskalt c1286520d3 draw up to sidebar#maxLines() 2025-09-30 18:38:58 +03:00
vytskalt bc8e3c2d2e release: 2.4.1 2025-07-17 18:37:46 +03:00
vytskalt 032349b190 mark 1.21.8 as supported 2025-07-17 18:37:08 +03:00
vytskalt d0d5eaa706 release: 2.4.0 2025-07-08 17:24:34 +03:00
vytskalt ac334838ee bump dependencies 2025-07-08 16:57:44 +03:00
vytskalt 162de14e0e Merge pull request #100 from huanmeng-qwq/feat/refresh
Add refresh-related methods
2025-07-08 16:53:38 +03:00
vytskalt 5f5b3efc5f improve docs 2025-07-08 13:55:05 +03:00
vytskalt 41c0a4b3c9 improve objective api 2025-07-08 13:52:12 +03:00
vytskalt a276708162 make sure to update the sidebar score format as well 2025-07-08 13:37:39 +03:00
huanmeng-qwq 793e78284f feat(api): Add refresh-related methods to ScoreboardObjective, Sidebar, and TeamDisplay. 2025-07-08 17:11:47 +08:00
vytskalt 38de97ba01 release: 2.3.3 2025-07-02 15:34:32 +03:00
vytskalt a535f37214 1.21.7 2025-07-02 15:34:04 +03:00
vytskalt 48351b6c81 release: 2.3.2 2025-06-23 01:11:13 +03:00
vytskalt 7dcf1e30eb add 1.21.6 support 2025-06-23 01:06:20 +03:00
vytskalt 2ca874346d release: 2.3.1 2025-05-04 19:29:04 +03:00
vytskalt cb08e5d3d9 Merge remote-tracking branch 'origin/main' 2025-04-26 23:53:16 +03:00
vytskalt eecde934a8 setup snapshot 2025-04-26 23:52:50 +03:00
vytskalt 2a070cdc3d remove transitive dependency on spigot-api 2025-04-26 23:52:11 +03:00
vytskalt 589ab095b2 [ci skip] Update INSTALLATION.md 2025-04-21 19:52:18 +03:00
vytskalt 41fb8aa4aa Update INSTALLATION.md 2025-04-21 19:43:22 +03:00
vytskalt da69391930 release: 2.3.0 2025-04-12 19:31:35 +03:00
vytskalt 47ef9a8143 update junit platform launcher 2025-04-12 19:27:25 +03:00
vytskalt ccf9aad96d update dependencies 2025-04-12 19:23:17 +03:00
vytskalt facb3a4d1c allow getting/setting sidebar objective name 2025-04-03 21:42:29 +03:00
vytskalt f41c1ce0b1 1.21.5 2025-03-27 19:59:49 +02:00
vytskalt e72d442f9b upgrade to gradle 8.13 2025-03-27 18:44:23 +02:00
105 changed files with 2462 additions and 1335 deletions

No files matched your search

+3 -3
View File
@@ -9,8 +9,8 @@ jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v30
- uses: actions/checkout@v6
- uses: cachix/install-nix-action@v31
- name: Cache Gradle
uses: actions/cache@v4
with:
@@ -21,4 +21,4 @@ jobs:
restore-keys: |
${{ runner.os }}-gradle-
- name: ./gradlew build
run: nix develop -i -c sh -c "./gradlew build"
run: nix develop .#jdk25 -i -c sh -c "./gradlew build"
+3
View File
@@ -8,3 +8,6 @@ build/
.project
.settings/
bin/
test-env/servers
test-env/cache
+14 -47
View File
@@ -1,6 +1,6 @@
# Installation
Latest version: `2.2.2`
Latest version: `2.7.0`
## Gradle
@@ -15,26 +15,13 @@ dependencies {
runtimeOnly("net.megavex:scoreboard-library-implementation:$scoreboardLibraryVersion")
implementation("net.megavex:scoreboard-library-extra-kotlin:$scoreboardLibraryVersion") // Kotlin specific extensions (optional)
// Add packet adapter implementations you want:
runtimeOnly("net.megavex:scoreboard-library-modern:$scoreboardLibraryVersion") // 1.17+
runtimeOnly("net.megavex:scoreboard-library-modern:$scoreboardLibraryVersion:mojmap") // Mojang mapped variant (only use if you know what you're doing!)
runtimeOnly("net.megavex:scoreboard-library-packetevents:$scoreboardLibraryVersion") // 1.8+
runtimeOnly("net.megavex:scoreboard-library-legacy:$scoreboardLibraryVersion") // 1.7.10-1.12.2
// If using the PacketEvents implementation, scoreboard-library expects PacketEvents to be in the classpath.
// Follow either of:
// - https://github.com/retrooper/packetevents/wiki/Depending-on-pre%E2%80%90built-PacketEvents
// - https://github.com/retrooper/packetevents/wiki/Shading-PacketEvents
// Example how to load PacketEvents in your plugin:
// https://github.com/retrooper/packetevents-example/blob/24f0c842d47362aef122b794dea29b8fee113fa3/thread-safe-listener/src/main/java/main/Main.java
// If targeting a Minecraft version without native Adventure support, add it as well:
implementation("net.kyori:adventure-platform-bukkit:4.0.1")
implementation("net.kyori:adventure-platform-bukkit:4.3.4")
}
```
You will need to shade these dependencies and relocate them with something
like [Shadow](https://imperceptiblethoughts.com/shadow/).
like [Shadow](https://gradleup.com/shadow/).
## Maven
@@ -58,37 +45,6 @@ like [Shadow](https://imperceptiblethoughts.com/shadow/).
<version>{VERSION HERE}</version>
</dependency>
<!-- Add packet adapter implementations you want: -->
<dependency>
<groupId>net.megavex</groupId>
<artifactId>scoreboard-library-modern</artifactId>
<version>{VERSION HERE}</version>
<scope>runtime</scope>
<!-- For a Mojang mapped variant, uncomment line below (only use if you know what you're doing!): -->
<!-- <classifier>mojmap</classifier> -->
</dependency>
<dependency>
<groupId>net.megavex</groupId>
<artifactId>scoreboard-library-packetevents</artifactId>
<version>{VERSION HERE}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>net.megavex</groupId>
<artifactId>scoreboard-library-legacy</artifactId>
<version>{VERSION HERE}</version>
<scope>runtime</scope>
</dependency>
<!--
If using the PacketEvents implementation, scoreboard-library expects PacketEvents to be in the classpath.
Follow either of:
- https://github.com/retrooper/packetevents/wiki/Depending-on-pre-built-PacketEvents
- https://github.com/retrooper/packetevents/wiki/Shading-PacketEvents
Example how to load PacketEvents in your plugin:
https://github.com/retrooper/packetevents-example/blob/24f0c842d47362aef122b794dea29b8fee113fa3/thread-safe-listener/src/main/java/main/Main.java
-->
<!-- If targeting a Minecraft version without native Adventure support, add it as well: -->
<dependency>
<groupId>net.kyori</groupId>
@@ -99,3 +55,14 @@ like [Shadow](https://imperceptiblethoughts.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"]
```
+7 -10
View File
@@ -13,22 +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
## Available Packet Adapters
## Supported server versions
- **modern.** Supports 1.17-1.21.4. Can take advantage of [Paper](https://papermc.io)'s native adventure support to be more efficient.
- **legacy.** Supports 1.7.10-1.12.2.
- **PacketEvents.** Supports 1.8+. Requires [PacketEvents 2.0](https://github.com/retrooper/packetevents/tree/2.0) to be shaded or installed as a plugin.
> [!NOTE]
> You can add multiple packet adapters, the best one will automatically be picked depending on the server version.
All Paper and Spigot versions from 1.7.10 to 1.21.11 are natively supported.
## Installation
@@ -41,9 +38,9 @@ ScoreboardLibrary scoreboardLibrary;
try {
scoreboardLibrary = ScoreboardLibrary.loadScoreboardLibrary(plugin);
} catch (NoPacketAdapterAvailableException e) {
// If no packet adapter was found, you can fallback to the no-op implementation:
// If server version is not yet supported, you can fallback to the no-op implementation:
scoreboardLibrary = new NoopScoreboardLibrary();
plugin.getLogger().warning("No scoreboard packet adapter available!");
plugin.getLogger().warning("Server version unsupported, scoreboard functionality will not be visible!");
}
// On plugin shutdown:
+5
View File
@@ -1,3 +1,8 @@
plugins {
id("net.megavex.scoreboardlibrary.base-conventions")
}
dependencies {
compileOnlyApi(libs.bundles.adventure)
compileOnly(libs.spigotApi)
}
@@ -5,6 +5,7 @@ import net.megavex.scoreboardlibrary.api.noop.NoopScoreboardLibrary;
import net.megavex.scoreboardlibrary.api.objective.ObjectiveManager;
import net.megavex.scoreboardlibrary.api.sidebar.Sidebar;
import net.megavex.scoreboardlibrary.api.team.TeamManager;
import org.apache.commons.lang.RandomStringUtils;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.ApiStatus;
@@ -26,7 +27,7 @@ public interface ScoreboardLibrary {
* Creates an instance of {@link ScoreboardLibrary}.
*
* @param plugin your plugin instance
* @throws NoPacketAdapterAvailableException if there is no packet adapter available for the current server version
* @throws NoPacketAdapterAvailableException if server version is not supported
*/
static @NotNull ScoreboardLibrary loadScoreboardLibrary(@NotNull Plugin plugin) throws NoPacketAdapterAvailableException {
Class<?> implClass;
@@ -56,7 +57,7 @@ public interface ScoreboardLibrary {
* @return newly created sidebar
*/
default @NotNull Sidebar createSidebar() {
return createSidebar(Sidebar.MAX_LINES, null);
return createSidebar(Sidebar.MAX_LINES);
}
/**
@@ -79,7 +80,21 @@ public interface ScoreboardLibrary {
* or null if the locale should depend on the player
* @return newly created sidebar
*/
@NotNull Sidebar createSidebar(@Range(from = 1, to = Integer.MAX_VALUE) int maxLines, @Nullable Locale locale);
default @NotNull Sidebar createSidebar(@Range(from = 1, to = Integer.MAX_VALUE) int maxLines, @Nullable Locale locale) {
return createSidebar(maxLines, locale, RandomStringUtils.randomAlphanumeric(16));
}
/**
* Creates a new {@link Sidebar}.
*
* @param maxLines max amount of lines the sidebar will have.
* Note that vanilla clients can only display at most {@value Sidebar#MAX_LINES}
* @param locale locale which will be used for translating {@link net.kyori.adventure.text.TranslatableComponent}s,
* or null if the locale should depend on the player
* @param objectiveName objective name the sidebar should use
* @return newly created sidebar
*/
@NotNull Sidebar createSidebar(@Range(from = 1, to = Integer.MAX_VALUE) int maxLines, @Nullable Locale locale, @NotNull String objectiveName);
/**
* Creates a new {@link TeamManager}.
@@ -7,6 +7,7 @@ import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.concurrent.CompletableFuture;
class NoopObjectiveManager implements ObjectiveManager {
private final Map<String, ScoreboardObjective> objectives = new HashMap<>();
@@ -42,6 +43,12 @@ class NoopObjectiveManager implements ObjectiveManager {
return players.remove(player);
}
@Override
public CompletableFuture<Void> removePlayerFuture(@NotNull Player player) {
players.remove(player);
return CompletableFuture.completedFuture(null);
}
@Override
public void close() {
isClosed = true;
@@ -19,9 +19,9 @@ public final class NoopScoreboardLibrary implements ScoreboardLibrary {
private boolean closed = false;
@Override
public @NotNull Sidebar createSidebar(@Range(from = 1, to = Sidebar.MAX_LINES) int maxLines, @Nullable Locale locale) {
public @NotNull Sidebar createSidebar(@Range(from = 1, to = Integer.MAX_VALUE) int maxLines, @Nullable Locale locale, @NotNull String objectiveName) {
checkClosed();
return new NoopSidebar(maxLines, locale);
return new NoopSidebar(maxLines, objectiveName, locale);
}
@Override
@@ -52,6 +52,10 @@ public class NoopScoreboardObjective implements ScoreboardObjective {
this.defaultScoreFormat = defaultScoreFormat;
}
@Override
public void refreshProperties() {
}
@Override
public @Nullable ObjectiveScore scoreInfo(@NotNull String entry) {
return scores.get(entry);
@@ -68,4 +72,8 @@ public class NoopScoreboardObjective implements ScoreboardObjective {
scores.remove(entry);
return this;
}
@Override
public void refreshScore(@NotNull String entry) {
}
}
@@ -17,14 +17,16 @@ import static net.kyori.adventure.text.Component.empty;
class NoopSidebar implements Sidebar {
private final Set<Player> players = new HashSet<>();
private final int maxLines;
private final String objectiveName;
private final Locale locale;
private final Component[] lines;
private Component title = empty();
private boolean closed;
NoopSidebar(int maxLines, @Nullable Locale locale) {
NoopSidebar(int maxLines, String objectiveName, @Nullable Locale locale) {
this.maxLines = maxLines;
this.locale = locale;
this.objectiveName = objectiveName;
this.lines = new Component[maxLines];
}
@@ -62,6 +64,11 @@ class NoopSidebar implements Sidebar {
return maxLines;
}
@Override
public @NotNull String objectiveName() {
return objectiveName;
}
@Override
public @Nullable Locale locale() {
return locale;
@@ -74,6 +81,10 @@ class NoopSidebar implements Sidebar {
lines[index] = value == null ? null : value.asComponent();
}
@Override
public void refreshLine(@Range(from = 0, to = Integer.MAX_VALUE - 1) final int index) {
}
@Override
public @Nullable Component line(int line) {
checkLineBounds(line);
@@ -93,6 +104,11 @@ class NoopSidebar implements Sidebar {
this.title = title.asComponent();
}
@Override
public void refreshTitle() {
}
private void checkClosed() {
if (closed) {
throw new IllegalStateException("NoopSidebar is closed");
@@ -48,11 +48,21 @@ class NoopTeamDisplay implements TeamDisplay {
return entries.add(entry);
}
@Override
public boolean addEntries(@NotNull final Collection<String> entries) {
return this.entries.addAll(entries);
}
@Override
public boolean removeEntry(@NotNull String entry) {
return entries.remove(entry);
}
@Override
public boolean removeEntries(@NotNull final Collection<String> entries) {
return this.entries.removeAll(entries);
}
@Override
public @NotNull Component displayName() {
return displayName;
@@ -140,4 +150,8 @@ class NoopTeamDisplay implements TeamDisplay {
this.playerColor = playerColor;
return this;
}
@Override
public void refresh() {
}
}
@@ -9,6 +9,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction;
import java.util.function.Function;
@@ -48,6 +49,12 @@ class NoopTeamManager implements TeamManager {
return true;
}
@Override
public CompletableFuture<Void> removePlayerFuture(@NotNull Player player) {
this.removePlayer(player);
return CompletableFuture.completedFuture(null);
}
@Override
public @NotNull Collection<ScoreboardTeam> teams() {
return closed ? Collections.emptySet() : Collections.unmodifiableCollection(teams.values());
@@ -80,6 +87,7 @@ class NoopTeamManager implements TeamManager {
validateTeamDisplay(team, teamDisplay);
team.displayMap().put(player, teamDisplay);
}
teams.put(name, team);
return team;
}
@@ -5,6 +5,7 @@ import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
/**
* Represents a group of {@link ScoreboardObjective}s.
@@ -85,6 +86,14 @@ public interface ObjectiveManager {
}
}
/**
* Removes a viewer from this ObjectiveManager.
*
* @param player viewer to remove
* @return future that will be completed once all removal packets have been sent in an async task
*/
CompletableFuture<Void> removePlayerFuture(@NotNull Player player);
/**
* Closes this ObjectiveManager.
* This must be called once you no longer need this ObjectiveManager to prevent a memory leak.
@@ -49,6 +49,11 @@ public interface ScoreboardObjective {
*/
void defaultScoreFormat(@Nullable ScoreFormat defaultScoreFormat);
/**
* Refreshes the objective's {@link #value()} and {@link #defaultScoreFormat()}, re-rendering any {@link net.kyori.adventure.text.TranslatableComponent}s.
*/
void refreshProperties();
/**
* Get the score for an entry, or null if the entry has no score.
*
@@ -141,4 +146,11 @@ public interface ScoreboardObjective {
* @param entry entry to remove score of
*/
@NotNull ScoreboardObjective removeScore(@NotNull String entry);
/**
* Refreshes a score, re-rendering any {@link net.kyori.adventure.text.TranslatableComponent}s inside its format.
*
* @param entry entry to refresh score of
*/
void refreshScore(@NotNull String entry);
}
@@ -1,18 +1,17 @@
package net.megavex.scoreboardlibrary.api.sidebar;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
import net.megavex.scoreboardlibrary.api.ScoreboardLibrary;
import net.megavex.scoreboardlibrary.api.objective.ScoreFormat;
import java.util.Collection;
import java.util.Locale;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Range;
import java.util.Collection;
import java.util.Locale;
/**
* Represents a sidebar.
* To get an instance of this interface, use {@link ScoreboardLibrary#createSidebar()}.
@@ -31,7 +30,13 @@ public interface Sidebar {
/**
* @return max amount of lines this sidebar can have
*/
@Range(from = 1, to = Integer.MAX_VALUE) int maxLines();
@Range(from = 1, to = Integer.MAX_VALUE)
int maxLines();
/**
* @return objective name used by this sidebar
*/
@NotNull String objectiveName();
/**
* @return locale which is used for translating {@link net.kyori.adventure.text.TranslatableComponent}s,
@@ -85,6 +90,22 @@ public interface Sidebar {
}
}
/**
* Refreshes a specific line, re-rendering any {@link net.kyori.adventure.text.TranslatableComponent}s.
*
* @param index line index
*/
void refreshLine(@Range(from = 0, to = Integer.MAX_VALUE - 1) int index);
/**
* Refreshes all lines in this sidebar, re-rendering any {@link net.kyori.adventure.text.TranslatableComponent}s.
*/
default void refreshLines() {
for (int i = 0; i < maxLines(); i++) {
refreshLine(i);
}
}
// Title
/**
@@ -99,6 +120,11 @@ public interface Sidebar {
*/
void title(@NotNull ComponentLike title);
/**
* Refresh the title of the sidebar, re-rendering any {@link net.kyori.adventure.text.TranslatableComponent}s.
*/
void refreshTitle();
// Players
/**
@@ -78,7 +78,7 @@ public final class ComponentSidebarLayout {
public void drawLine(@NotNull ComponentLike line, @Nullable ScoreFormat scoreFormat) {
Preconditions.checkNotNull(line);
if (index < Sidebar.MAX_LINES) {
if (index < sidebar.maxLines()) {
sidebar.line(index++, line, scoreFormat);
}
}
@@ -42,6 +42,15 @@ public interface TeamDisplay {
*/
boolean addEntry(@NotNull String entry);
/**
* Adds multiple entries.
*
* @param entries entries to add
* @return whether any entry was added
* @see #entries
*/
boolean addEntries(@NotNull Collection<String> entries);
/**
* Removes an entry.
*
@@ -51,6 +60,15 @@ public interface TeamDisplay {
*/
boolean removeEntry(@NotNull String entry);
/**
* Removes multiple entries.
*
* @param entries entries to remove
* @return whether any entry was removed
* @see #entries
*/
boolean removeEntries(@NotNull Collection<String> entries);
// Properties
/**
@@ -170,4 +188,9 @@ public interface TeamDisplay {
* @since Minecraft 1.8
*/
@NotNull TeamDisplay playerColor(@Nullable NamedTextColor playerColor);
/**
* Refreshes the team display, re-rendering any {@link net.kyori.adventure.text.TranslatableComponent}s.
*/
void refresh();
}
@@ -7,6 +7,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiFunction;
import java.util.function.Function;
@@ -104,7 +105,7 @@ public interface TeamManager {
* The TeamManager will internally be added to a queue for this player who
* will start seeing it once they are removed from all previous TeamManagers.
*
* @param player player to add
* @param player player to add
* @param teamDisplayFunction a function that provides the team display to set for each team
* @return whether the player was added
*/
@@ -152,6 +153,14 @@ public interface TeamManager {
}
}
/**
* Removes a viewer from this TeamManager.
*
* @param player viewer to remove
* @return future that will be completed once all removal packets have been sent in an async task
*/
CompletableFuture<Void> removePlayerFuture(@NotNull Player player);
// Close
/**
+1 -1
View File
@@ -1,5 +1,5 @@
allprojects {
version = "2.2.2"
version = "2.7.0"
group = "net.megavex"
description = "Powerful packet-level scoreboard library for Paper/Spigot servers"
}
@@ -16,16 +16,18 @@ repositories {
mavenCentral()
maven("https://oss.sonatype.org/content/repositories/snapshots")
maven("https://hub.spigotmc.org/nexus/content/repositories/snapshots/")
maven("https://hub.spigotmc.org/nexus/content/repositories/sonatype-nexus-snapshots/") {
content {
includeGroup("net.md-5")
}
}
}
dependencies {
compileOnly(libs.spigotApi)
compileOnly(libs.bundles.adventure)
testImplementation(libs.spigotApi)
testImplementation(libs.bundles.adventure)
testImplementation(libs.junitJupiterApi)
testRuntimeOnly(libs.junitJupiterEngine)
testImplementation(libs.junitJupiter)
testRuntimeOnly(libs.junitPlatformLauncher)
}
indra {
+1
View File
@@ -4,4 +4,5 @@ plugins {
dependencies {
api(project(":scoreboard-library-api"))
compileOnly(libs.spigotApi)
}
@@ -17,17 +17,16 @@ import static net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializ
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);
}
}
@@ -63,6 +62,12 @@ public final class LegacyFormatUtil {
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();
}
}
+6 -4
View File
@@ -1,4 +1,5 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
plugins {
id("net.megavex.scoreboardlibrary.base-conventions")
@@ -12,10 +13,11 @@ kotlin {
dependencies {
api(project(":scoreboard-library-api"))
testImplementation(kotlin("test"))
compileOnly(libs.spigotApi)
}
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions {
jvmTarget = "1.8"
tasks.withType<KotlinJvmCompile>().configureEach {
compilerOptions {
jvmTarget = JvmTarget.JVM_1_8
}
}
Generated
+4 -4
View File
@@ -2,16 +2,16 @@
"nodes": {
"nixpkgs": {
"locked": {
"lastModified": 1722987190,
"narHash": "sha256-68hmex5efCiM2aZlAAEcQgmFI4ZwWt8a80vOeB/5w3A=",
"lastModified": 1770056022,
"narHash": "sha256-yvCz+Qmci1bVucXEyac3TdoSPMtjqVJmVy5wro6j/70=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "21cc704b5e918c5fbf4f9fff22b4ac2681706d90",
"rev": "d04d8548aed39902419f14a8537006426dc1e4fa",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-24.05",
"ref": "nixos-25.11",
"repo": "nixpkgs",
"type": "github"
}
+25 -5
View File
@@ -1,6 +1,6 @@
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
};
outputs = { nixpkgs, ... }: let
@@ -15,10 +15,30 @@
inherit system;
}));
in {
devShells = forAllSystems (pkgs: {
default = pkgs.mkShell {
buildInputs = [
pkgs.temurin-bin-21
devShells = forAllSystems (pkgs: rec {
default = jdk25;
jdk25 = pkgs.mkShellNoCC {
buildInputs = with pkgs; [
jdk25_headless
];
};
jdk21 = pkgs.mkShellNoCC {
buildInputs = with pkgs; [
jdk21_headless
];
};
jdk17 = pkgs.mkShellNoCC {
buildInputs = with pkgs; [
jdk17_headless
];
};
jdk8 = pkgs.mkShellNoCC {
buildInputs = with pkgs; [
jdk8_headless
];
};
});
+6 -9
View File
@@ -1,24 +1,21 @@
[versions]
adventure = "4.17.0"
junitJupiter = "5.11.3"
devBundle = "1.21.4-R0.1-20241208.061653-9" # find latest here: https://repo.papermc.io/repository/maven-snapshots/io/papermc/paper/dev-bundle/1.21.4-R0.1-SNAPSHOT/maven-metadata.xml
adventure = "4.25.0"
[libraries]
spigotApi = "org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT" # do not update
packetEvents = "com.github.retrooper:packetevents-spigot:2.6.0"
buildIndra = { module = "net.kyori:indra-common", version = "3.1.3" }
buildIndra = { module = "net.kyori:indra-common", version = "4.0.0" }
buildNmcp = "com.gradleup.nmcp:com.gradleup.nmcp.gradle.plugin:0.0.9"
adventureApi = { module = "net.kyori:adventure-api", version.ref = "adventure" }
adventureTextSerializerGson = { module = "net.kyori:adventure-text-serializer-gson", version.ref = "adventure" }
adventureTextSerializerLegacy = { module = "net.kyori:adventure-text-serializer-legacy", version.ref = "adventure" }
junitJupiterApi = { module = "org.junit.jupiter:junit-jupiter-api", version.ref = "junitJupiter" }
junitJupiterEngine = { module = "org.junit.jupiter:junit-jupiter-engine", version.ref = "junitJupiter" }
# TODO: v6 only supports java 17
junitJupiter = "org.junit.jupiter:junit-jupiter:5.13.4"
junitPlatformLauncher = "org.junit.platform:junit-platform-launcher:1.14.1"
[plugins]
kotlin = "org.jetbrains.kotlin.jvm:2.1.0"
paperweight = "io.papermc.paperweight.userdev:1.7.7"
kotlin = "org.jetbrains.kotlin.jvm:2.2.21"
[bundles]
adventure = [ "adventureApi", "adventureTextSerializerGson", "adventureTextSerializerLegacy" ]
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Vendored
+4 -8
View File
@@ -1,7 +1,7 @@
#!/bin/sh
#
# Copyright © 2015-2021 the original authors.
# Copyright © 2015 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -86,8 +86,7 @@ done
# shellcheck disable=SC2034
APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
' "$PWD" ) || exit
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum
@@ -115,7 +114,6 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -173,7 +171,6 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -206,15 +203,14 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line.
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@"
# Stop when "xargs" is not available.
Vendored
+1 -2
View File
@@ -70,11 +70,10 @@ goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end
@rem End local scope for the variables with windows NT shell
+4
View File
@@ -5,4 +5,8 @@ plugins {
dependencies {
api(project(":scoreboard-library-api"))
implementation(project(":scoreboard-library-packet-adapter-base"))
compileOnly(libs.spigotApi)
implementation(project(":scoreboard-library-modern"))
implementation(project(":scoreboard-library-legacy"))
}
@@ -3,27 +3,26 @@ package net.megavex.scoreboardlibrary.implementation;
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;
import java.lang.reflect.InvocationTargetException;
public final class PacketAdapterLoader {
private static final String MODERN = "modern",
LEGACY = "legacy",
PACKET_EVENTS = "packetevents";
private static final String MODERN = "modern", LEGACY = "legacy";
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);
}
@@ -36,12 +35,7 @@ public final class PacketAdapterLoader {
version = version.substring(0, dashIndex);
}
Class<?> nmsClass = tryLoadVersion(version);
if (nmsClass != null) {
return nmsClass;
}
return tryLoadPacketEvents();
return tryLoadVersion(version);
}
private static @Nullable Class<?> tryLoadVersion(@NotNull String serverVersion) {
@@ -68,6 +62,23 @@ public final class PacketAdapterLoader {
case "1.12.1":
case "1.12.2":
return tryLoadImplementationClass(LEGACY);
case "1.13":
case "1.13.1":
case "1.13.2":
case "1.14":
case "1.14.1":
case "1.14.2":
case "1.14.3":
case "1.14.4":
case "1.15":
case "1.15.1":
case "1.15.2":
case "1.16":
case "1.16.1":
case "1.16.2":
case "1.16.3":
case "1.16.4":
case "1.16.5":
case "1.17":
case "1.17.1":
case "1.18":
@@ -90,6 +101,13 @@ public final class PacketAdapterLoader {
case "1.21.2":
case "1.21.3":
case "1.21.4":
case "1.21.5":
case "1.21.6":
case "1.21.7":
case "1.21.8":
case "1.21.9":
case "1.21.10":
case "1.21.11":
return tryLoadImplementationClass(MODERN);
default:
// Hide from relocation checkers
@@ -102,20 +120,6 @@ public final class PacketAdapterLoader {
}
}
private static @Nullable Class<?> tryLoadPacketEvents() {
Class<?> nmsClass = tryLoadImplementationClass(PACKET_EVENTS);
if (nmsClass == null) {
return null;
}
try {
Class.forName("com.github.retrooper.packetevents.PacketEvents");
return nmsClass;
} catch (ClassNotFoundException ignored) {
return null;
}
}
private static @Nullable Class<?> tryLoadImplementationClass(@NotNull String name) {
try {
String path = "net.megavex.scoreboardlibrary.implementation.packetAdapter." + name + ".PacketAdapterProviderImpl";
@@ -59,7 +59,7 @@ public class ScoreboardLibraryImpl implements ScoreboardLibrary {
}
this.plugin = plugin;
this.packetAdapter = PacketAdapterLoader.loadPacketAdapter();
this.packetAdapter = PacketAdapterLoader.loadPacketAdapter(plugin);
this.taskScheduler = TaskScheduler.create(plugin);
boolean localeEventExists = false;
@@ -90,7 +90,7 @@ public class ScoreboardLibraryImpl implements ScoreboardLibrary {
}
@Override
public @NotNull Sidebar createSidebar(int maxLines, @Nullable Locale locale) {
public @NotNull Sidebar createSidebar(int maxLines, @Nullable Locale locale, @NotNull String objectiveName) {
checkClosed();
if (maxLines <= 0) {
@@ -99,9 +99,9 @@ public class ScoreboardLibraryImpl implements ScoreboardLibrary {
AbstractSidebar sidebar;
if (locale == null) {
sidebar = new PlayerDependantLocaleSidebar(this, maxLines);
sidebar = new PlayerDependantLocaleSidebar(this, maxLines, objectiveName);
} else {
sidebar = new SingleLocaleSidebar(this, maxLines, locale);
sidebar = new SingleLocaleSidebar(this, maxLines, objectiveName, locale);
}
sidebars().add(sidebar);
@@ -15,6 +15,7 @@ import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
@@ -108,10 +109,23 @@ public class ObjectiveManagerImpl implements ObjectiveManager, PlayerDisplayable
return false;
}
taskQueue.add(new ObjectiveManagerTask.RemovePlayer(player));
taskQueue.add(new ObjectiveManagerTask.RemovePlayer(player, null));
return true;
}
@Override
public CompletableFuture<Void> removePlayerFuture(@NotNull Player player) {
Preconditions.checkNotNull(player);
checkClosed();
if (!players.remove(player)) {
return CompletableFuture.completedFuture(null);
}
CompletableFuture<Void> future = new CompletableFuture<>();
taskQueue.add(new ObjectiveManagerTask.RemovePlayer(player, future));
return future;
}
@Override
public void close() {
if (!closed) {
@@ -146,14 +160,17 @@ public class ObjectiveManagerImpl implements ObjectiveManager, PlayerDisplayable
@NotNull ScoreboardLibraryPlayer slPlayer = library.getOrCreatePlayer(player);
slPlayer.objectiveManagerQueue().add(this);
} else if (task instanceof ObjectiveManagerTask.RemovePlayer) {
Player player = ((ObjectiveManagerTask.RemovePlayer) task).player();
Collection<Player> singleton = Collections.singleton(player);
ObjectiveManagerTask.RemovePlayer removePlayerTask = (ObjectiveManagerTask.RemovePlayer) task;
Collection<Player> singleton = Collections.singleton(removePlayerTask.player());
for (ScoreboardObjectiveImpl objective : objectives.values()) {
objective.packetAdapter().remove(singleton);
}
displayingPlayers.remove(player);
Objects.requireNonNull(library.getPlayer(player)).objectiveManagerQueue().remove(this);
displayingPlayers.remove(removePlayerTask.player());
Objects.requireNonNull(library.getPlayer(removePlayerTask.player())).objectiveManagerQueue().remove(this);
if (removePlayerTask.future() != null) {
removePlayerTask.future().complete(null);
}
} else if (task instanceof ObjectiveManagerTask.ReloadPlayer) {
Player player = ((ObjectiveManagerTask.ReloadPlayer) task).player();
Collection<Player> singleton = Collections.singleton(player);
@@ -168,6 +185,7 @@ public class ObjectiveManagerImpl implements ObjectiveManager, PlayerDisplayable
} else if (task instanceof ObjectiveManagerTask.AddObjective) {
ScoreboardObjectiveImpl objective = ((ObjectiveManagerTask.AddObjective) task).objective();
objective.sendProperties(displayingPlayers, PropertiesPacketType.CREATE);
objective.wasDisplayed(true);
} else if (task instanceof ObjectiveManagerTask.RemoveObjective) {
ScoreboardObjectiveImpl objective = ((ObjectiveManagerTask.RemoveObjective) task).objective();
objective.packetAdapter().remove(displayingPlayers);
@@ -200,6 +218,10 @@ public class ObjectiveManagerImpl implements ObjectiveManager, PlayerDisplayable
Collection<Player> singleton = Collections.singleton(player);
for (ScoreboardObjectiveImpl objective : objectives.values()) {
if (!objective.wasDisplayed()) {
continue;
}
objective.sendProperties(singleton, PropertiesPacketType.CREATE);
for (Map.Entry<String, ObjectiveScore> entry : objective.scores().entrySet()) {
ObjectiveScore score = entry.getValue();
@@ -208,6 +230,9 @@ public class ObjectiveManagerImpl implements ObjectiveManager, PlayerDisplayable
}
for (Map.Entry<ObjectiveDisplaySlot, ScoreboardObjectiveImpl> entry : displaySlots.entrySet()) {
if (!entry.getValue().wasDisplayed()) {
continue;
}
entry.getValue().packetAdapter().display(singleton, entry.getKey());
}
}
@@ -6,6 +6,8 @@ import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.concurrent.CompletableFuture;
public class ObjectiveManagerTask {
private ObjectiveManagerTask() {
}
@@ -31,14 +33,20 @@ public class ObjectiveManagerTask {
public static final class RemovePlayer extends ObjectiveManagerTask {
private final Player player;
private final CompletableFuture<Void> future;
public RemovePlayer(@NotNull Player player) {
public RemovePlayer(@NotNull Player player, @Nullable CompletableFuture<Void> future) {
this.player = player;
this.future = future;
}
public @NotNull Player player() {
return player;
}
public @Nullable CompletableFuture<Void> future() {
return future;
}
}
public static final class ReloadPlayer extends ObjectiveManagerTask {
@@ -26,6 +26,7 @@ public class ScoreboardObjectiveImpl implements ScoreboardObjective {
private ObjectiveRenderType renderType = ObjectiveRenderType.INTEGER;
private ScoreFormat defaultScoreFormat;
private boolean closed;
private boolean wasDisplayed;
public ScoreboardObjectiveImpl(@NotNull ObjectivePacketAdapter packetAdapter, @NotNull Queue<ObjectiveManagerTask> taskQueue, @NotNull String name) {
this.packetAdapter = packetAdapter;
@@ -95,6 +96,13 @@ public class ScoreboardObjectiveImpl implements ScoreboardObjective {
}
}
@Override
public void refreshProperties() {
if (!closed) {
taskQueue.add(new ObjectiveManagerTask.UpdateObjective(this));
}
}
@Override
public @Nullable ObjectiveScore scoreInfo(@NotNull String entry) {
return scores.get(entry);
@@ -117,7 +125,23 @@ public class ScoreboardObjectiveImpl implements ScoreboardObjective {
return this;
}
@Override
public void refreshScore(@NotNull String entry) {
ObjectiveScore score = scores.get(entry);
if (score != null) {
taskQueue.add(new ObjectiveManagerTask.UpdateScore(this, entry, score));
}
}
public void sendProperties(@NotNull Collection<Player> players, @NotNull PropertiesPacketType packetType) {
packetAdapter.sendProperties(players, packetType, value, renderType, defaultScoreFormat);
}
public boolean wasDisplayed() {
return wasDisplayed;
}
public void wasDisplayed(boolean wasDisplayed) {
this.wasDisplayed = wasDisplayed;
}
}
@@ -1,10 +1,6 @@
package net.megavex.scoreboardlibrary.implementation.sidebar;
import com.google.common.base.Preconditions;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.kyori.adventure.translation.GlobalTranslator;
import net.megavex.scoreboardlibrary.api.objective.ObjectiveDisplaySlot;
import net.megavex.scoreboardlibrary.api.objective.ObjectiveRenderType;
import net.megavex.scoreboardlibrary.api.objective.ScoreFormat;
@@ -17,16 +13,22 @@ import net.megavex.scoreboardlibrary.implementation.player.PlayerDisplayable;
import net.megavex.scoreboardlibrary.implementation.player.ScoreboardLibraryPlayer;
import net.megavex.scoreboardlibrary.implementation.sidebar.line.GlobalLineInfo;
import net.megavex.scoreboardlibrary.implementation.sidebar.line.LocaleLineHandler;
import org.apache.commons.lang.RandomStringUtils;
import java.util.Collection;
import java.util.Collections;
import java.util.Objects;
import java.util.Queue;
import java.util.Set;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.Consumer;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.kyori.adventure.translation.GlobalTranslator;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.Range;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.Consumer;
import static net.kyori.adventure.text.Component.empty;
public abstract class AbstractSidebar implements Sidebar, PlayerDisplayable {
@@ -42,10 +44,8 @@ public abstract class AbstractSidebar implements Sidebar, PlayerDisplayable {
private final Queue<SidebarTask> taskQueue = new ConcurrentLinkedQueue<>();
public AbstractSidebar(@NotNull ScoreboardLibraryImpl scoreboardLibrary, int maxLines) {
public AbstractSidebar(@NotNull ScoreboardLibraryImpl scoreboardLibrary, int maxLines, @NotNull String objectiveName) {
this.scoreboardLibrary = scoreboardLibrary;
String objectiveName = RandomStringUtils.randomAlphanumeric(16);
this.packetAdapter = scoreboardLibrary.packetAdapter().createObjectiveAdapter(objectiveName);
this.lines = new GlobalLineInfo[maxLines];
}
@@ -107,6 +107,11 @@ public abstract class AbstractSidebar implements Sidebar, PlayerDisplayable {
return lines.length;
}
@Override
public @NotNull String objectiveName() {
return packetAdapter.objectiveName();
}
@Override
public final void line(@Range(from = 0, to = MAX_LINES - 1) int index, @Nullable ComponentLike value, @Nullable ScoreFormat scoreFormat) {
checkClosed();
@@ -124,6 +129,17 @@ public abstract class AbstractSidebar implements Sidebar, PlayerDisplayable {
}
}
@Override
public void refreshLine(@Range(from = 0, to = Integer.MAX_VALUE - 1) final int index) {
checkClosed();
GlobalLineInfo line = lines[index];
if (line != null) {
boolean updateValue = line.value() != null;
boolean updateScore = line.scoreFormat() != null;
taskQueue.add(new SidebarTask.UpdateLine(index, updateValue, updateScore));
}
}
@Override
public final @Nullable Component line(@Range(from = 0, to = MAX_LINES - 1) int line) {
checkClosed();
@@ -149,6 +165,12 @@ public abstract class AbstractSidebar implements Sidebar, PlayerDisplayable {
}
}
@Override
public void refreshTitle() {
checkClosed();
taskQueue.add(SidebarTask.UpdateTitle.INSTANCE);
}
public final @NotNull ScoreboardLibraryImpl scoreboardLibrary() {
return scoreboardLibrary;
}
@@ -17,8 +17,8 @@ public class PlayerDependantLocaleSidebar extends AbstractSidebar {
private final Map<Player, LocaleLineHandler> playerMap = new HashMap<>();
private final Map<Locale, LocaleLineHandler> localeMap = new HashMap<>();
public PlayerDependantLocaleSidebar(@NotNull ScoreboardLibraryImpl scoreboardLibrary, int maxLines) {
super(scoreboardLibrary, maxLines);
public PlayerDependantLocaleSidebar(@NotNull ScoreboardLibraryImpl scoreboardLibrary, int maxLines, @NotNull String objectiveName) {
super(scoreboardLibrary, maxLines, objectiveName);
}
@Override
@@ -16,8 +16,8 @@ public class SingleLocaleSidebar extends AbstractSidebar {
private final LocaleLineHandler sidebar;
private final Set<Player> internalPlayers;
public SingleLocaleSidebar(@NotNull ScoreboardLibraryImpl scoreboardLibrary, int size, @NotNull Locale locale) {
super(scoreboardLibrary, size);
public SingleLocaleSidebar(@NotNull ScoreboardLibraryImpl scoreboardLibrary, int size, @NotNull String objectiveName, @NotNull Locale locale) {
super(scoreboardLibrary, size, objectiveName);
this.locale = locale;
this.sidebar = new LocaleLineHandler(this, locale);
this.internalPlayers = CollectionProvider.set(8);
@@ -53,7 +53,9 @@ public class SidebarLineHandler {
public void updateScore(int line) {
LocaleLine localeLine = lines[line];
localeLine.sendScore(players);
if (localeLine != null) {
localeLine.sendScore(players);
}
}
public void setLine(int line, Component renderedLine) {
@@ -157,7 +157,7 @@ public class LegacyLocaleLine implements LocaleLine, ImmutableTeamProperties<Str
}
pd.removeScore(players, player);
info.packetAdapter().removeTeam(players);
packetAdapter.removeTeam(players);
}
}
@@ -61,7 +61,7 @@ public class ModernLocaleLine implements LocaleLine, ImmutableTeamProperties<Com
@Override
public void hide(@NotNull Collection<Player> players) {
handler.localeLineHandler().sidebar().packetAdapter().removeScore(players, info.player());
info.packetAdapter().removeTeam(players);
packetAdapter.removeTeam(players);
}
@Override
@@ -97,7 +97,7 @@ public class ScoreboardTeamImpl implements ScoreboardTeam {
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));
}
}
@@ -67,6 +67,20 @@ public class TeamDisplayImpl implements TeamDisplay, ImmutableTeamProperties<Com
return false;
}
@Override
public boolean addEntries(@NotNull Collection<String> entries) {
Set<String> existing = new HashSet<>(this.entries);
List<String> toAdd = new ArrayList<>(entries);
toAdd.removeIf(existing::contains);
if (!toAdd.isEmpty()) {
this.entries.addAll(toAdd);
team.teamManager().taskQueue().add(new TeamManagerTask.AddEntries(this, toAdd));
return true;
}
return false;
}
@Override
public boolean removeEntry(@NotNull String entry) {
if (entries.remove(entry)) {
@@ -77,6 +91,19 @@ public class TeamDisplayImpl implements TeamDisplay, ImmutableTeamProperties<Com
return false;
}
@Override
public boolean removeEntries(@NotNull Collection<String> entries) {
List<String> toRemove = new ArrayList<>(entries);
toRemove.retainAll(this.entries);
if (!toRemove.isEmpty()) {
this.entries.removeAll(toRemove);
team.teamManager().taskQueue().add(new TeamManagerTask.RemoveEntries(this, toRemove));
return true;
}
return false;
}
@Override
public @NotNull Component displayName() {
return displayName;
@@ -210,6 +237,11 @@ public class TeamDisplayImpl implements TeamDisplay, ImmutableTeamProperties<Com
return this;
}
@Override
public void refresh() {
scheduleUpdate();
}
public @NotNull TeamDisplayPacketAdapter packetAdapter() {
return packetAdapter;
}
@@ -14,6 +14,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.function.BiFunction;
@@ -144,11 +145,26 @@ public class TeamManagerImpl implements TeamManager, PlayerDisplayable {
return false;
}
TeamManagerTask.RemovePlayer task = new TeamManagerTask.RemovePlayer(player);
TeamManagerTask.RemovePlayer task = new TeamManagerTask.RemovePlayer(player, null);
taskQueue.add(task);
return true;
}
@Override
public CompletableFuture<Void> removePlayerFuture(@NotNull Player player) {
Preconditions.checkNotNull(player);
checkClosed();
if (!players.remove(player)) {
return CompletableFuture.completedFuture(null);
}
CompletableFuture<Void> future = new CompletableFuture<>();
TeamManagerTask.RemovePlayer task = new TeamManagerTask.RemovePlayer(player, future);
taskQueue.add(task);
return future;
}
public @NotNull ScoreboardLibraryImpl scoreboardLibrary() {
return scoreboardLibrary;
}
@@ -173,11 +189,9 @@ public class TeamManagerImpl implements TeamManager, PlayerDisplayable {
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) {
@@ -197,6 +211,9 @@ public class TeamManagerImpl implements TeamManager, PlayerDisplayable {
internalPlayers.remove(removePlayerTask.player());
Objects.requireNonNull(scoreboardLibrary.getPlayer(removePlayerTask.player())).teamManagerQueue().remove(this);
if (removePlayerTask.future() != null) {
removePlayerTask.future().complete(null);
}
} else if (task instanceof TeamManagerTask.ReloadPlayer) {
TeamManagerTask.ReloadPlayer reloadPlayerTask = (TeamManagerTask.ReloadPlayer) task;
for (ScoreboardTeamImpl team : teams.values()) {
@@ -216,16 +233,9 @@ public class TeamManagerImpl implements TeamManager, PlayerDisplayable {
}
} 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();
@@ -2,8 +2,10 @@ package net.megavex.scoreboardlibrary.implementation.team;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.concurrent.CompletableFuture;
public class TeamManagerTask {
private TeamManagerTask() {
@@ -30,14 +32,20 @@ public class TeamManagerTask {
public static final class RemovePlayer extends TeamManagerTask {
private final Player player;
private final CompletableFuture<Void> future;
public RemovePlayer(@NotNull Player player) {
public RemovePlayer(@NotNull Player player, @Nullable CompletableFuture<Void> future) {
this.player = player;
this.future = future;
}
public @NotNull Player player() {
return player;
}
public @Nullable CompletableFuture<Void> future() {
return future;
}
}
public static final class ReloadPlayer extends TeamManagerTask {
+1
View File
@@ -4,6 +4,7 @@ include(":api")
include(":commons")
include(":implementation")
include(":extra-kotlin")
include(":test-plugin")
include(":packet-adapter-base")
project(":packet-adapter-base").projectDir = file("versions/packet-adapter-base")
+70
View File
@@ -0,0 +1,70 @@
# test-env
## Tested server versions
- [x] 1.7.10
- [x] 1.8.8
- [x] 1.9.4
- [x] 1.10.2
- [x] 1.11.2
- [x] 1.12.2
- [x] 1.13
- [x] 1.14.4
- [x] 1.15.2
- [x] 1.16.5
- [x] 1.17
- [x] 1.18.2
- [x] 1.19.4
- [x] 1.20.2
- [x] 1.20.4
- [x] 1.20.5
- [x] 1.21.3
- [x] 1.21.5
- [x] 1.21.6
- [x] 1.21.9
- [x] 1.21.11
## Untested server versions
- [?] 1.8
- [?] 1.8.3
- [?] 1.8.4
- [?] 1.8.5
- [?] 1.8.6
- [?] 1.8.7
- [?] 1.9
- [?] 1.9.2
- [?] 1.11
- [?] 1.12
- [?] 1.12.1
- [?] 1.13.1
- [?] 1.13.2
- [?] 1.14
- [?] 1.14.1
- [?] 1.14.2
- [?] 1.14.3
- [?] 1.15
- [?] 1.15.1
- [?] 1.16
- [?] 1.16.1
- [?] 1.16.2
- [?] 1.16.3
- [?] 1.16.4
- [?] 1.17.1
- [?] 1.18
- [?] 1.18.1
- [?] 1.19
- [?] 1.19.1
- [?] 1.19.2
- [?] 1.19.3
- [?] 1.20
- [?] 1.20.1
- [-] 1.20.3
- [-] 1.20.6
- [-] 1.21
- [-] 1.21.1
- [-] 1.21.2
- [-] 1.21.4
- [-] 1.21.7
- [-] 1.21.8
- [-] 1.21.10
+6
View File
@@ -0,0 +1,6 @@
settings:
allow-end: false
allow-nether: false
spawn-limits:
monsters: 0
animals: 0
Executable
+4
View File
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -x
rm -r servers
rm -rf cache
Executable
+238
View File
@@ -0,0 +1,238 @@
#!/usr/bin/env bash
set -e
port_cursor=25570
create_server() {
local name=$1
local nixshell=$2
local enable_via=$3
local root=$(pwd)
local plugin_path=$(readlink -f ../test-plugin/build/libs/scoreboard-library-test-plugin-*-all.jar)
local jar_path="$root/cache/$name.jar"
local server_dir="servers/$name"
mkdir -p "$server_dir/plugins"
pushd "$server_dir" &> /dev/null
port=$port_cursor
(( port_cursor++ ))
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\nonline-mode=false" > server.properties
cp "$root/bukkit.yml" .
mkdir -p config
cp "$root/paper-global.yml" config/
ln -sf "$plugin_path" plugins/test.jar
ln -sf "$jar_path" server.jar
if [ "$enable_via" = "true" ]; then
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
fi
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
chmod +x start
popd &> /dev/null
}
create_spigot_server() {
local version=$1
local nixshell=$2
if [ ! -f "cache/$version-spigot.jar" ]; then
echo Running BuildTools for Spigot $version
local root=$(pwd)
pushd cache &> /dev/null
mkdir -p "$version-spigot"
pushd "$version-spigot" &> /dev/null
nix develop ".#$nixshell" -c java -jar ../buildtools.jar --disable-java-check --nogui --rev "$version"
if [ -f "$root/patches/spigot-$version-server.patch" ]; then
echo Applying spigot-$version-server.patch
pushd Spigot/Spigot-Server &> /dev/null
git apply "$root/patches/spigot-$version-server.patch"
popd &> /dev/null
echo Rebuilding Spigot with patch
nix develop ".#$nixshell" -c sh -c 'cd Spigot && bash ../apache-maven-*/bin/mvn package'
mv Spigot/Spigot-Server/target/spigot-*.jar "spigot-$version.jar"
fi
mv "spigot-$version.jar" "../$version-spigot.jar"
popd &> /dev/null
rm -rf "$version-spigot"
popd &> /dev/null
fi
create_server "$version-spigot" "$nixshell"
}
download_file() {
local name=$1
local url=$2
local expected_sha256_hash=$3
local path="cache/$name"
mkdir -p cache
if [ ! -f "$path" ] || [ "$(sha256sum "$path" | cut -f 1 -d ' ')" != "$expected_sha256_hash" ]; then
echo "Downloading $name..."
curl --silent --location --output "$path" "$url"
fi
local actual_hash="$(sha256sum "$path" | cut -f 1 -d ' ')"
if [ "$actual_hash" != "$expected_sha256_hash" ]; then
echo "ERROR: hashes for $name do not match! expected: $expected_sha256_hash, but got $actual_hash"
rm "$path"
exit 1
fi
}
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-Java8/38/artifact/build/libs/ViaVersion-5.7.1-downgraded.jar" "dcce3d30e195de9a3372616298bac1acd29196e5c3676a0fba745be9e3d91808"
download_file "viabackwards.jar" "https://ci.viaversion.com/view/ViaBackwards/job/ViaBackwards-Java8/31/artifact/build/libs/ViaBackwards-5.7.1-downgraded.jar" "9727fd246d3566bffc3f71e1bda0e6db71bf5814a254e7ab5a2941ca0e94ecd6"
download_file "viarewind.jar" "https://ci.viaversion.com/view/ViaRewind/job/ViaRewind-Java8/28/artifact/build/libs/ViaRewind-4.0.14-downgraded.jar" "e84c7afa696b898a99747edba2458dd9e9f5d7c1ee2f604c409964dd4d4d784d"
# --- 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"
# buildtools cannot build 1.7.10
# --- 1.8.8 ---
download_file "1.8.8-paper.jar" "https://fill-data.papermc.io/v1/objects/7ff6d2cec671ef0d95b3723b5c92890118fb882d73b7f8fa0a2cd31d97c55f86/paper-1.8.8-445.jar" "7ff6d2cec671ef0d95b3723b5c92890118fb882d73b7f8fa0a2cd31d97c55f86"
create_server "1.8.8-paper" "jdk8"
create_spigot_server "1.8.8" "jdk8"
# --- 1.9.4 ---
download_file "1.9.4-paper.jar" "https://fill-data.papermc.io/v1/objects/15a5821ddeacc596432c3fbf24262a2d264f556060ecd6f1838fb01ab5629a81/paper-1.9.4-775.jar" "15a5821ddeacc596432c3fbf24262a2d264f556060ecd6f1838fb01ab5629a81"
create_server "1.9.4-paper" "jdk8"
create_spigot_server "1.9.4" "jdk8"
# --- 1.10.2 ---
download_file "1.10.2-paper.jar" "https://fill-data.papermc.io/v1/objects/83354d24a22b6265e76c089b3d17a568abb446c0ccd12c2452f5e148412b16c2/paper-1.10.2-918.jar" "83354d24a22b6265e76c089b3d17a568abb446c0ccd12c2452f5e148412b16c2"
create_server "1.10.2-paper" "jdk8"
create_spigot_server "1.10.2" "jdk8"
# --- 1.11.2 ---
download_file "1.11.2-paper.jar" "https://fill-data.papermc.io/v1/objects/3d0f40ec1f9630dfdbafa626cc20c266d7fb90fc22583dc1b995e7fbfb76830d/paper-1.11.2-1106.jar" "3d0f40ec1f9630dfdbafa626cc20c266d7fb90fc22583dc1b995e7fbfb76830d"
create_server "1.11.2-paper" "jdk8"
create_spigot_server "1.11.2" "jdk8"
# --- 1.12.2 ---
download_file "1.12.2-paper.jar" "https://fill-data.papermc.io/v1/objects/3a2041807f492dcdc34ebb324a287414946e3e05ec3df6fd03f5b5f7d9afc210/paper-1.12.2-1620.jar" "3a2041807f492dcdc34ebb324a287414946e3e05ec3df6fd03f5b5f7d9afc210"
create_server "1.12.2-paper" "jdk8"
create_spigot_server "1.12.2" "jdk8"
# --- 1.13 ---
download_file "1.13-paper.jar" "https://fill-data.papermc.io/v1/objects/00db82d214242c9345266d44ff8d11a8e857a1a02edf7cb5fcc2d1d973283129/paper-1.13-173.jar" "00db82d214242c9345266d44ff8d11a8e857a1a02edf7cb5fcc2d1d973283129"
create_server "1.13-paper" "jdk8" "true"
create_spigot_server "1.13" "jdk8"
# --- 1.14.4 ---
download_file "1.14.4-paper.jar" "https://fill-data.papermc.io/v1/objects/bd8ec5cdb22370d37816a6de26798df3d2b0d6f9c7c96c88ca45a1303fea50e8/paper-1.14.4-245.jar" "bd8ec5cdb22370d37816a6de26798df3d2b0d6f9c7c96c88ca45a1303fea50e8"
create_server "1.14.4-paper" "jdk8"
create_spigot_server "1.14.4" "jdk8" "true"
# --- 1.15.2 ---
download_file "1.15.2-paper.jar" "https://fill-data.papermc.io/v1/objects/bd2dd6f2cc489cf9e2bb800cb4fb6d63e9d293945d3ac10b09dd9c6098fa9f34/paper-1.15.2-393.jar" "bd2dd6f2cc489cf9e2bb800cb4fb6d63e9d293945d3ac10b09dd9c6098fa9f34"
create_server "1.15.2-paper" "jdk8" "true"
create_spigot_server "1.15.2" "jdk8"
# --- 1.16.5 ---
download_file "1.16.5-paper.jar" "https://fill-data.papermc.io/v1/objects/e67da4851d08cde378ab2b89be58849238c303351ed2482181a99c2c2b489276/paper-1.16.5-794.jar" "e67da4851d08cde378ab2b89be58849238c303351ed2482181a99c2c2b489276"
create_server "1.16.5-paper" "jdk8"
create_spigot_server "1.16.5" "jdk8" "true"
# --- 1.17 ---
download_file "1.17-paper.jar" "https://fill-data.papermc.io/v1/objects/760a93b94a58d619bd647d71af84688617d0444d22b716500bc6b343858dc871/paper-1.17-79.jar" "760a93b94a58d619bd647d71af84688617d0444d22b716500bc6b343858dc871"
create_server "1.17-paper" "jdk17" "true"
create_spigot_server "1.17" "jdk17"
# --- 1.18.2 ---
download_file "1.18.2-paper.jar" "https://fill-data.papermc.io/v1/objects/0578f18f4d632b494b468ec56b3b414b5b56fea087ee7d39cf6dcdf4c9d01f05/paper-1.18.2-388.jar" "0578f18f4d632b494b468ec56b3b414b5b56fea087ee7d39cf6dcdf4c9d01f05"
create_server "1.18.2-paper" "jdk17"
create_spigot_server "1.18.2" "jdk17" "true"
# --- 1.19.4 ---
download_file "1.19.4-paper.jar" "https://fill-data.papermc.io/v1/objects/e587d78cba3e99ef8c4bc24cf20cc3bdbbe89e33b0b572070446af4eb6be5ccf/paper-1.19.4-550.jar" "e587d78cba3e99ef8c4bc24cf20cc3bdbbe89e33b0b572070446af4eb6be5ccf"
create_server "1.19.4-paper" "jdk17" "true"
create_spigot_server "1.19.4" "jdk17"
# --- 1.20.2 ---
download_file "1.20.2-paper.jar" "https://fill-data.papermc.io/v1/objects/07f57cdc5948dcf3cb3358e43b9a46fbbe8b595fa30c8749cfc102bf44897b69/paper-1.20.2-318-mojang.jar" "07f57cdc5948dcf3cb3358e43b9a46fbbe8b595fa30c8749cfc102bf44897b69"
create_server "1.20.2-paper" "jdk21"
create_spigot_server "1.20.2" "jdk21" "true"
# --- 1.20.4 ---
download_file "1.20.4-paper.jar" "https://fill-data.papermc.io/v1/objects/e84aa4943cc51d7545b1c9b669bb1e0b143323d248ebb89012182f5554bc13d7/paper-1.20.4-499-mojang.jar" "e84aa4943cc51d7545b1c9b669bb1e0b143323d248ebb89012182f5554bc13d7"
create_server "1.20.4-paper" "jdk21" "true"
create_spigot_server "1.20.4" "jdk21"
# --- 1.20.5 ---
download_file "1.20.5-paper.jar" "https://fill-data.papermc.io/v1/objects/3cd7da2f8df92e082a501a39c674aab3c0343edd179b86f5baccaebfc9974132/paper-1.20.5-22.jar" "3cd7da2f8df92e082a501a39c674aab3c0343edd179b86f5baccaebfc9974132"
create_server "1.20.5-paper" "jdk21"
# spigot doesn't have 1.20.5
create_spigot_server "1.20.6" "jdk21" "true"
# --- 1.21.3 ---
download_file "1.21.3-paper.jar" "https://fill-data.papermc.io/v1/objects/87e973e1d338e869e7fdbc4b8fadc1579d7bb0246a0e0cf6e5700ace6c8bc17e/paper-1.21.3-83.jar" "87e973e1d338e869e7fdbc4b8fadc1579d7bb0246a0e0cf6e5700ace6c8bc17e"
create_server "1.21.3-paper" "jdk21" "true"
create_spigot_server "1.21.3" "jdk21"
# --- 1.21.5 ---
download_file "1.21.5-paper.jar" "https://fill-data.papermc.io/v1/objects/2ae6ae22adf417699746e0f89fc2ef6cb6ee050a5f6608cee58f0535d60b509e/paper-1.21.5-114.jar" "2ae6ae22adf417699746e0f89fc2ef6cb6ee050a5f6608cee58f0535d60b509e"
create_server "1.21.5-paper" "jdk21"
create_spigot_server "1.21.5" "jdk21" "true"
# --- 1.21.6 ---
download_file "1.21.6-paper.jar" "https://fill-data.papermc.io/v1/objects/35e2dfa66b3491b9d2f0bb033679fa5aca1e1fdf097e7a06a80ce8afeda5c214/paper-1.21.6-48.jar" "35e2dfa66b3491b9d2f0bb033679fa5aca1e1fdf097e7a06a80ce8afeda5c214"
create_server "1.21.6-paper" "jdk21" "true"
create_spigot_server "1.21.6" "jdk21"
# --- 1.21.9 ---
download_file "1.21.9-paper.jar" "https://fill-data.papermc.io/v1/objects/aec002e77c7566e49494fdf05430b96078ffd1d7430e652d4f338fef951e7a10/paper-1.21.9-59.jar" "aec002e77c7566e49494fdf05430b96078ffd1d7430e652d4f338fef951e7a10"
create_server "1.21.9-paper" "jdk25"
# spigot doesnt have 1.21.9
create_spigot_server "1.21.10" "jdk25" "true"
# --- 1.21.11 ---
download_file "1.21.11-paper.jar" "https://fill-data.papermc.io/v1/objects/4a558a00005d33dafa4c4d5f9e47b3bd47d92311fceccd9c9754ee6b913f8649/paper-1.21.11-100.jar" "4a558a00005d33dafa4c4d5f9e47b3bd47d92311fceccd9c9754ee6b913f8649"
create_server "1.21.11-paper" "jdk25" "true"
create_spigot_server "1.21.11" "jdk25"
+2
View File
@@ -0,0 +1,2 @@
misc:
enable-nether: false
+13
View File
@@ -0,0 +1,13 @@
diff --git i/src/main/java/org/bukkit/craftbukkit/Main.java w/src/main/java/org/bukkit/craftbukkit/Main.java
index 02e5090db..5f4215b35 100644
--- i/src/main/java/org/bukkit/craftbukkit/Main.java
+++ w/src/main/java/org/bukkit/craftbukkit/Main.java
@@ -161,7 +161,7 @@ public class Main {
System.err.println("Unsupported Java detected (" + javaVersion + "). This version of Minecraft requires at least Java 16. Check your Java version with the command 'java -version'.");
return;
}
- if (javaVersion > 60.0) {
+ if (javaVersion > 60.0 && false) {
System.err.println("Unsupported Java detected (" + javaVersion + "). Only up to Java 16 is supported.");
return;
}
+15
View File
@@ -0,0 +1,15 @@
#!/usr/bin/env bash
total=$(ls servers | wc -l)
i=1
for dir in servers/*; do
echo
echo "--- Starting server $(basename "$dir") [$i/$total]"
echo
(( i++ ))
sleep 1
pushd "$dir" &> /dev/null
bash ./start
popd &> /dev/null
done
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
if ! command -v tmux >/dev/null 2>&1; then
echo ERROR: tmux not found, install it >&2
exit 67
fi
sesh="sl"
if tmux has-session -t "$sesh" 2>/dev/null; then
echo already exists
#tmux a -t "$sesh"
exit 0
fi
tmux new-session -d -s "$sesh" -n "shell"
for dir in servers/*; do
window=$(basename $dir | tr '.' '-')
pushd "$dir" &> /dev/null
tmux new-window -t "$sesh" -n "$window"
popd &> /dev/null
tmux send-keys -t "$sesh:$window" "./start" C-m
done
tmux a -t "$sesh"
+27
View File
@@ -0,0 +1,27 @@
plugins {
id("net.megavex.scoreboardlibrary.base-conventions")
id("com.gradleup.shadow") version "9.3.1"
}
dependencies {
compileOnly(libs.spigotApi)
implementation(project(":scoreboard-library-api"))
implementation(project(":scoreboard-library-implementation"))
compileOnly(libs.adventureApi)
compileOnly(libs.adventureTextSerializerGson)
compileOnly(libs.adventureTextSerializerLegacy)
// bundled adventure:
implementation(libs.adventureApi)
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")
}
@@ -0,0 +1,100 @@
package net.megavex.scoreboardlibrary.testplugin;
import net.kyori.adventure.translation.GlobalTranslator;
import net.megavex.scoreboardlibrary.api.ScoreboardLibrary;
import net.megavex.scoreboardlibrary.api.exception.NoPacketAdapterAvailableException;
import net.megavex.scoreboardlibrary.testplugin.module.Module;
import net.megavex.scoreboardlibrary.testplugin.module.ObjectivesModule;
import net.megavex.scoreboardlibrary.testplugin.module.SidebarModule;
import net.megavex.scoreboardlibrary.testplugin.module.TeamsModule;
import org.bukkit.GameMode;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.bukkit.event.EventHandler;
import org.bukkit.event.Listener;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.event.weather.WeatherChangeEvent;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.Arrays;
import java.util.List;
public final class ScoreboardPlugin extends JavaPlugin implements Listener {
private final TestTranslator translator = new TestTranslator();
private ScoreboardLibrary lib;
private List<Module> modules;
@Override
public void onEnable() {
this.getServer().getPluginManager().registerEvents(this, this);
GlobalTranslator.get().addSource(this.translator);
try {
lib = ScoreboardLibrary.loadScoreboardLibrary(this);
} catch (NoPacketAdapterAvailableException e) {
this.getLogger().warning("No packet adapter found, disabling plugin");
this.getServer().getPluginManager().disablePlugin(this);
return;
}
this.modules = Arrays.asList(
new SidebarModule(this),
new TeamsModule(this),
new ObjectivesModule(this)
);
for (final Module module : this.modules) {
this.getLogger().info("Enabling " + module.getClass().getSimpleName());
module.onEnable();
}
for (final World world : this.getServer().getWorlds()) {
world.setAutoSave(false);
}
this.getServer().getScheduler().runTask(this, () -> {
this.getLogger().info("");
this.getLogger().info("### Server port: " + this.getServer().getPort() + " ###");
this.getLogger().info("");
});
}
@Override
public void onDisable() {
if (this.modules != null) {
for (final Module module : this.modules) {
module.onDisable();
}
}
if (this.lib != null) {
this.lib.close();
}
GlobalTranslator.get().removeSource(this.translator);
}
@EventHandler
public void onJoin(final PlayerJoinEvent event) {
final Player player = event.getPlayer();
player.setOp(true);
player.setGameMode(GameMode.CREATIVE);
player.sendMessage("Server software: " + this.getServer().getName());
player.sendMessage("Server version: " + this.getServer().getVersion());
player.sendMessage("ViaVersion enabled: " + this.getServer().getPluginManager().isPluginEnabled("ViaVersion"));
boolean isNativeAdventure = false;
try {
Class.forName("io.papermc.paper.adventure.AdventureComponent");
isNativeAdventure = true;
} catch (ClassNotFoundException ignored) {
}
player.sendMessage("Is native adventure: " + isNativeAdventure);
}
@EventHandler
public void onWeather(final WeatherChangeEvent event) {
event.setCancelled(true);
}
public ScoreboardLibrary scoreboardLibrary() {
return this.lib;
}
}
@@ -0,0 +1,26 @@
package net.megavex.scoreboardlibrary.testplugin;
import net.kyori.adventure.key.Key;
import net.kyori.adventure.translation.Translator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.text.MessageFormat;
import java.util.Locale;
public final class TestTranslator implements Translator {
public static final String KEY = "test.translatable.key";
@Override
public @NotNull Key name() {
return Key.key("scoreboard-library", "test");
}
@Override
public @Nullable MessageFormat translate(@NotNull String key, @NotNull Locale locale) {
if (key.equals(KEY)) {
return new MessageFormat(locale.toString(), locale);
}
return null;
}
}
@@ -0,0 +1,9 @@
package net.megavex.scoreboardlibrary.testplugin.module;
public interface Module {
default void onEnable() {
}
default void onDisable() {
}
}
@@ -0,0 +1,61 @@
package net.megavex.scoreboardlibrary.testplugin.module;
import net.megavex.scoreboardlibrary.api.objective.*;
import net.megavex.scoreboardlibrary.testplugin.ScoreboardPlugin;
import net.megavex.scoreboardlibrary.testplugin.TestTranslator;
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 static net.kyori.adventure.text.Component.text;
import static net.kyori.adventure.text.Component.translatable;
public final class ObjectivesModule implements Module, Listener {
private final ScoreboardPlugin plugin;
private ObjectiveManager manager;
private ScoreboardObjective objective;
public ObjectivesModule(final ScoreboardPlugin plugin) {
this.plugin = plugin;
}
@Override
public void onEnable() {
this.manager = plugin.scoreboardLibrary().createObjectiveManager();
this.objective = this.manager.create("test");
this.objective.value(text("List Objective"));
this.objective.renderType(ObjectiveRenderType.INTEGER);
this.objective.defaultScoreFormat(ScoreFormat.fixed(text("obj ").append(translatable(TestTranslator.KEY))));
this.manager.display(ObjectiveDisplaySlot.playerList(), this.objective);
this.plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
@Override
public void onDisable() {
HandlerList.unregisterAll(this);
this.manager.close();
}
@EventHandler
public void onJoin(final PlayerJoinEvent event) {
final Player player = event.getPlayer();
this.objective.score(player.getName(), 5);
this.manager.addPlayer(player);
this.objective.removeScore(player.getName());
this.objective.score(player.getName(), 6);
}
@EventHandler
public void onQuit(final PlayerQuitEvent event) {
final Player player = event.getPlayer();
this.manager.removePlayerFuture(player).whenComplete((v, t) -> {
this.plugin.getLogger().info("Removed player from objective " + this.plugin.getServer().isPrimaryThread());
});
this.objective.removeScore(player.getName());
}
}
@@ -0,0 +1,113 @@
package net.megavex.scoreboardlibrary.testplugin.module;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.TextColor;
import net.megavex.scoreboardlibrary.api.objective.ScoreFormat;
import net.megavex.scoreboardlibrary.api.sidebar.Sidebar;
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;
import java.awt.*;
import static net.kyori.adventure.text.Component.*;
import static net.kyori.adventure.text.format.NamedTextColor.*;
import static net.kyori.adventure.text.format.Style.style;
import static net.kyori.adventure.text.format.TextColor.color;
import static net.kyori.adventure.text.format.TextDecoration.BOLD;
public final class SidebarModule implements Module, Listener, SidebarComponent {
private final ScoreboardPlugin plugin;
private Sidebar sidebar;
private int tickCounter;
private BukkitTask timer;
public SidebarModule(final ScoreboardPlugin plugin) {
this.plugin = plugin;
}
@Override
public void onEnable() {
this.sidebar = plugin.scoreboardLibrary().createSidebar();
final SidebarComponent title = SidebarComponent.dynamicLine(() -> text("Sidebar Test", hueColor()));
final ComponentSidebarLayout layout = new ComponentSidebarLayout(title, this);
this.timer = this.plugin.getServer().getScheduler().runTaskTimer(plugin, () -> {
this.tickCounter++;
layout.apply(sidebar);
if ((this.tickCounter / 20) % 10 == 0) {
for (final Player player : this.plugin.getServer().getOnlinePlayers()) {
this.sidebar.removePlayer(player);
}
} else {
for (final Player player : this.plugin.getServer().getOnlinePlayers()) {
this.sidebar.addPlayer(player);
}
}
}, 1, 1);
this.plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
@Override
public void onDisable() {
HandlerList.unregisterAll(this);
this.timer.cancel();
this.sidebar.close();
this.sidebar = null;
}
@EventHandler
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())));
drawable.drawLine(text("Static line"));
drawable.drawLine(text("Tick counter: ").append(text(this.tickCounter, color(0x00FF00))));
if ((this.tickCounter / 20) % 2 == 0) {
drawable.drawLine(text("Disappearing line"), ScoreFormat.styled(style(DARK_RED)));
}
drawable.drawLine(text("Your locale x2: ").append(Component.translatable(TestTranslator.KEY)), ScoreFormat.fixed(translatable(TestTranslator.KEY)));
drawable.drawLine(text("VeryLongLineThatShouldBeCutOffFor1.12.2AndBelowPlayersButNotOnNewerVersions", AQUA, BOLD));
drawable.drawLine(empty());
drawable.drawLine(text("github.com/megavexnetwork/scoreboard-library", YELLOW));
}
private TextColor hueColor() {
final float hue = ((this.tickCounter * 5) % 360) / 360f;
final Color awtColor = Color.getHSBColor(hue, 0.8f, 0.9f);
return color(awtColor.getRGB());
}
}
@@ -0,0 +1,90 @@
package net.megavex.scoreboardlibrary.testplugin.module;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.kyori.adventure.text.format.TextDecoration.State;
import net.megavex.scoreboardlibrary.api.team.ScoreboardTeam;
import net.megavex.scoreboardlibrary.api.team.TeamDisplay;
import net.megavex.scoreboardlibrary.api.team.TeamManager;
import net.megavex.scoreboardlibrary.testplugin.ScoreboardPlugin;
import net.megavex.scoreboardlibrary.testplugin.TestTranslator;
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.scheduler.BukkitTask;
import java.util.*;
import static net.kyori.adventure.text.Component.*;
import static net.kyori.adventure.text.format.NamedTextColor.AQUA;
import static net.kyori.adventure.text.format.NamedTextColor.RED;
import static net.kyori.adventure.text.format.TextDecoration.BOLD;
import static net.kyori.adventure.text.format.TextDecoration.State.FALSE;
import static net.kyori.adventure.text.format.TextDecoration.State.TRUE;
public final class TeamsModule implements Module, Listener {
private final static List<NamedTextColor> NAMED_COLORS = new ArrayList<>(NamedTextColor.NAMES.values());
private final Random random = new Random();
private final ScoreboardPlugin plugin;
private TeamManager manager;
private ScoreboardTeam team;
private BukkitTask task;
public TeamsModule(final ScoreboardPlugin plugin) {
this.plugin = plugin;
}
@Override
public void onEnable() {
this.manager = plugin.scoreboardLibrary().createTeamManager();
this.team = this.manager.createIfAbsent("epic");
final TeamDisplay defaultDisplay = this.team.defaultDisplay();
defaultDisplay.displayName(text("Epic Team"));
defaultDisplay.prefix(text("[Epic Prefix] ").color(AQUA));
defaultDisplay.suffix(space().append(Component.translatable(TestTranslator.KEY)));
defaultDisplay.playerColor(RED);
this.task = this.plugin.getServer().getScheduler().runTaskTimer(this.plugin, () -> {
final State state = defaultDisplay.prefix().decoration(BOLD);
final Component newPrefix = defaultDisplay.prefix().decoration(BOLD, state == TRUE ? FALSE : TRUE);
defaultDisplay.prefix(newPrefix);
defaultDisplay.suffix(defaultDisplay.suffix().color(this.randomNamedColor()));
}, 20, 20);
this.plugin.getServer().getPluginManager().registerEvents(this, plugin);
}
@Override
public void onDisable() {
this.task.cancel();
HandlerList.unregisterAll(this);
this.manager.close();
}
@EventHandler
public void onJoin(final PlayerJoinEvent event) {
final Player player = event.getPlayer();
this.team.defaultDisplay().addEntry(player.getName());
this.manager.addPlayer(player);
this.team.defaultDisplay().removeEntry(player.getName());
this.team.defaultDisplay().addEntries(Collections.singletonList(player.getName()));
}
@EventHandler
public void onQuit(final PlayerQuitEvent event) {
final Player player = event.getPlayer();
this.manager.removePlayerFuture(player).whenComplete((v, t) -> {
this.plugin.getLogger().info("Removed player from team " + this.plugin.getServer().isPrimaryThread());
});
this.team.defaultDisplay().removeEntries(Collections.singletonList(player.getName()));
}
private NamedTextColor randomNamedColor() {
return NAMED_COLORS.get(random.nextInt(NAMED_COLORS.size()));
}
}
@@ -0,0 +1,5 @@
name: scoreboard-library-test
version: 0.1.0
main: net.megavex.scoreboardlibrary.testplugin.ScoreboardPlugin
api-version: 1.13
softdepend: ["ViaVersion"]
+1
View File
@@ -4,4 +4,5 @@ plugins {
dependencies {
compileOnly(project(":scoreboard-library-packet-adapter-base"))
compileOnly(libs.spigotApi)
}
@@ -26,6 +26,11 @@ public class ObjectivePacketAdapterImpl implements ObjectivePacketAdapter {
this.objectiveName = objectiveName;
}
@Override
public @NotNull String objectiveName() {
return objectiveName;
}
@Override
public void display(@NotNull Collection<Player> players, @NotNull ObjectiveDisplaySlot slot) {
Object packet = PacketAccessors.DISPLAY_OBJECTIVE_CONSTRUCTOR.invoke();
@@ -5,10 +5,13 @@ import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.Obje
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);
@@ -27,17 +27,6 @@ public class TeamsPacketAdapterImpl implements TeamsPacketAdapter {
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);
@@ -55,6 +44,17 @@ public class TeamsPacketAdapterImpl implements TeamsPacketAdapter {
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();
@@ -70,9 +70,9 @@ public class TeamsPacketAdapterImpl implements TeamsPacketAdapter {
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);
+17 -17
View File
@@ -1,27 +1,26 @@
plugins {
id("net.megavex.scoreboardlibrary.base-conventions")
alias(libs.plugins.paperweight)
//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"))
paperweight.paperDevBundle(libs.versions.devBundle.get())
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")
}
tasks {
compileJava {
// Workaround to get it to compile targeting Java 8 while using NMS which targets 17
// Why does this work? I have no idea
options.release = null
}
// https://github.com/unnamed/hephaestus-engine/blob/db6deabe1ddb0a549306b0c4b519c3e79e6f1ea8/runtime-bukkit/adapt-v1_20_R3/build.gradle.kts#L25
reobfJar {
outputJar = file("build/libs/scoreboard-library-modern-$version-reobf.jar")
}
assemble {
dependsOn(reobfJar)
javadoc {
exclude("**")
}
}
@@ -35,11 +34,12 @@ indra {
publishing {
publications.getByName<MavenPublication>("maven") {
// Kept for backwards compatibility
artifact(tasks.jar) {
classifier = "mojmap"
}
artifact(tasks.reobfJar)
artifact(tasks.jar)
artifact(tasks.javadocJar)
artifact(tasks.sourcesJar)
}
@@ -1,11 +0,0 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Locale;
public interface ComponentProvider {
@NotNull net.minecraft.network.chat.Component fromAdventure(@NotNull Component adventure, @Nullable Locale locale);
}
@@ -1,78 +0,0 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern;
import com.google.gson.JsonElement;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.translation.GlobalTranslator;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.NativeAdventureUtil;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.RegistryUtil;
import net.minecraft.network.chat.Component.Serializer;
import net.minecraft.network.chat.MutableComponent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.util.Locale;
import static net.kyori.adventure.text.serializer.gson.GsonComponentSerializer.gson;
public class ComponentProviderImpl implements ComponentProvider {
private static final MethodHandle FROM_JSON_METHOD;
static {
MethodHandle handle = null;
for (Method method : Serializer.class.getMethods()) {
if (method.getReturnType() == MutableComponent.class &&
method.getParameterCount() >= 1 &&
method.getParameterCount() <= 2 &&
method.getParameterTypes()[0] == JsonElement.class
) {
try {
handle = MethodHandles.lookup().unreflect(method);
break;
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
if (handle == null) {
throw new ExceptionInInitializerError("failed to find chat component fromJson method");
}
FROM_JSON_METHOD = handle;
}
private final boolean isNativeAdventure;
public ComponentProviderImpl(boolean isNativeAdventure) {
this.isNativeAdventure = isNativeAdventure;
}
@Override
public net.minecraft.network.chat.@NotNull Component fromAdventure(@NotNull Component adventure, @Nullable Locale locale) {
if (isNativeAdventure) {
return NativeAdventureUtil.fromAdventureComponent(adventure);
}
Component translated = adventure;
if (locale != null) {
translated = GlobalTranslator.render(adventure, locale);
}
JsonElement json = gson().serializeToTree(translated);
Object[] args;
if (PacketAccessors.IS_1_20_5_OR_ABOVE) {
args = new Object[]{json, RegistryUtil.MINECRAFT_REGISTRY};
} else {
args = new Object[]{json};
}
try {
return (net.minecraft.network.chat.Component) FROM_JSON_METHOD.invokeWithArguments(args);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}
@@ -1,25 +1,40 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ConstructorAccessor;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.FieldAccessor;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.PacketConstructor;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ReflectUtil;
import net.minecraft.ChatFormatting;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.numbers.NumberFormat;
import net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket;
import net.minecraft.network.protocol.game.ClientboundSetObjectivePacket;
import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket;
import net.minecraft.network.protocol.game.ClientboundSetScorePacket;
import net.minecraft.server.ServerScoreboard;
import net.minecraft.world.scores.Objective;
import net.minecraft.world.scores.criteria.ObjectiveCriteria;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.megavex.scoreboardlibrary.api.team.enums.CollisionRule;
import net.megavex.scoreboardlibrary.api.team.enums.NameTagVisibility;
import net.megavex.scoreboardlibrary.implementation.commons.LegacyFormatUtil;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.*;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Optional;
import java.lang.invoke.MethodType;
import java.util.*;
public final class PacketAccessors {
private PacketAccessors() {
}
public static final boolean IS_1_17_OR_ABOVE, IS_1_20_2_OR_ABOVE, IS_1_20_3_OR_ABOVE, IS_1_20_5_OR_ABOVE, IS_1_21_5_OR_ABOVE, IS_1_21_6_OR_ABOVE;
private static final String OLD_NMS_VERSION_STRING;
static {
boolean is1_17OrAbove = false;
try {
Class.forName("net.minecraft.world.item.BundleItem");
is1_17OrAbove = true;
} catch (ClassNotFoundException ignored) {
}
IS_1_17_OR_ABOVE = is1_17OrAbove;
if (!is1_17OrAbove) {
OLD_NMS_VERSION_STRING = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
} else {
OLD_NMS_VERSION_STRING = null;
}
boolean is1_20_2OrAbove = false;
try {
Class.forName("net.minecraft.world.scores.DisplaySlot");
@@ -38,69 +53,309 @@ public final class PacketAccessors {
boolean is1_20_5OrAbove = false;
try {
Class.forName("net.minecraft.network.protocol.common.ClientboundTransferPacket"); // Random 1.20.5 class
Class.forName("net.minecraft.network.protocol.common.ClientboundTransferPacket");
is1_20_5OrAbove = true;
} catch (ClassNotFoundException ignored) {
}
IS_1_20_5_OR_ABOVE = is1_20_5OrAbove;
if (is1_20_5OrAbove) {
OBJECTIVE_NUMBER_FORMAT_FIELD = ReflectUtil.findFieldUnchecked(ClientboundSetObjectivePacket.class, 0, Optional.class);
SCORE_1_20_2_CONSTRUCTOR = null;
SCORE_1_20_3_CONSTRUCTOR = null;
} else if (is1_20_3OrAbove) {
OBJECTIVE_NUMBER_FORMAT_FIELD = ReflectUtil.findFieldUnchecked(ClientboundSetObjectivePacket.class, 0, NumberFormat.class);
SCORE_1_20_3_CONSTRUCTOR = ReflectUtil.findConstructor(ClientboundSetScorePacket.class, String.class, String.class, int.class, Component.class, NumberFormat.class);
SCORE_1_20_2_CONSTRUCTOR = null;
boolean is1_21_5OrAbove = false;
try {
Class.forName("net.minecraft.world.item.component.BlocksAttacks");
is1_21_5OrAbove = true;
} catch (ClassNotFoundException ignored) {
}
IS_1_21_5_OR_ABOVE = is1_21_5OrAbove;
boolean is1_21_6OrAbove = false;
try {
Class.forName("net.minecraft.server.dialog.Dialog");
is1_21_6OrAbove = true;
} catch (ClassNotFoundException ignored) {
}
IS_1_21_6_OR_ABOVE = is1_21_6OrAbove;
}
public static final Class<?> PKT_CLASS,
SET_OBJECTIVE_PKT_CLASS,
SET_DISPLAY_OBJECTIVE_PKT_CLASS,
SET_SCORE_PKT_CLASS,
RESET_SCORE_PKT_CLASS,
SET_PLAYER_TEAM_PKT_CLASS,
TEAM_PARAMETERS_PKT_CLASS, // 1.17
COMPONENT_CLASS,
CHAT_SERIALIZER_CLASS,
COMPONENT_SERIALIZATION_CLASS,
STYLE_CLASS,
STYLE_SERIALIZER_CLASS,
NUMBER_FORMAT_CLASS,
DISPLAY_SLOT_CLASS, // 1.20.2
OBJECTIVE_CLASS,
TEAM_VISIBILITY_CLASS,
TEAM_COLLISION_RULE_CLASS,
CHAT_FORMATTING_CLASS,
OBJECTIVE_CRITERIA_RENDER_TYPE_CLASS,
DATA_RESULT_CLASS,
DYNAMIC_OPS_CLASS,
JSON_OPS_CLASS,
CODEC_CLASS,
SERVER_PLAYER_CLASS,
PLAYER_CONNECTION_CLASS,
ADVENTURE_COMPONENT_CLASS;
static {
PKT_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.network.protocol.Packet", oldSpigotClassName("Packet"));
SET_OBJECTIVE_PKT_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.network.protocol.game.ClientboundSetObjectivePacket", "net.minecraft.network.protocol.game.PacketPlayOutScoreboardObjective", oldSpigotClassName("PacketPlayOutScoreboardObjective"));
SET_DISPLAY_OBJECTIVE_PKT_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket", "net.minecraft.network.protocol.game.PacketPlayOutScoreboardDisplayObjective", oldSpigotClassName("PacketPlayOutScoreboardDisplayObjective"));
SET_SCORE_PKT_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.network.protocol.game.ClientboundSetScorePacket", "net.minecraft.network.protocol.game.PacketPlayOutScoreboardScore", oldSpigotClassName("PacketPlayOutScoreboardScore"));
RESET_SCORE_PKT_CLASS = ReflectUtil.getOptionalClass("net.minecraft.network.protocol.game.ClientboundResetScorePacket", "net.minecraft.network.protocol.game.ClientboundResetScorePacket");
SET_PLAYER_TEAM_PKT_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket", "net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam", oldSpigotClassName("PacketPlayOutScoreboardTeam"));
TEAM_PARAMETERS_PKT_CLASS = ReflectUtil.getOptionalClass("net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket$Parameters", "net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam$b");
COMPONENT_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.network.chat.Component", "net.minecraft.network.chat.IChatBaseComponent", oldSpigotClassName("IChatBaseComponent"));
CHAT_SERIALIZER_CLASS = ReflectUtil.getOptionalClass("net.minecraft.network.chat.Component$Serializer", "net.minecraft.network.chat.IChatBaseComponent$ChatSerializer", oldSpigotClassName("IChatBaseComponent$ChatSerializer"));
COMPONENT_SERIALIZATION_CLASS = ReflectUtil.getOptionalClass("net.minecraft.network.chat.ComponentSerialization");
STYLE_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.network.chat.Style", "net.minecraft.network.chat.ChatModifier", oldSpigotClassName("ChatModifier"));
STYLE_SERIALIZER_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.network.chat.Style$Serializer", "net.minecraft.network.chat.ChatModifier$ChatModifierSerializer", oldSpigotClassName("ChatModifier$ChatModifierSerializer"));
NUMBER_FORMAT_CLASS = ReflectUtil.getOptionalClass("net.minecraft.network.chat.numbers.NumberFormat");
DISPLAY_SLOT_CLASS = ReflectUtil.getOptionalClass("net.minecraft.world.scores.DisplaySlot");
OBJECTIVE_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.world.scores.Objective", "net.minecraft.world.scores.ScoreboardObjective", oldSpigotClassName("ScoreboardObjective"));
TEAM_VISIBILITY_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.world.scores.Team$Visibility", "net.minecraft.world.scores.ScoreboardTeamBase$EnumNameTagVisibility", oldSpigotClassName("ScoreboardTeamBase$EnumNameTagVisibility"));
TEAM_COLLISION_RULE_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.world.scores.Team$CollisionRule", "net.minecraft.world.scores.ScoreboardTeamBase$EnumTeamPush", oldSpigotClassName("ScoreboardTeamBase$EnumTeamPush"));
CHAT_FORMATTING_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.ChatFormatting", "net.minecraft.EnumChatFormat", oldSpigotClassName("EnumChatFormat"));
OBJECTIVE_CRITERIA_RENDER_TYPE_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.world.scores.criteria.ObjectiveCriteria$RenderType", "net.minecraft.world.scores.criteria.IScoreboardCriteria$EnumScoreboardHealthDisplay", oldSpigotClassName("IScoreboardCriteria$EnumScoreboardHealthDisplay"));
DATA_RESULT_CLASS = ReflectUtil.getOptionalClass("com.mojang.serialization.DataResult");
DYNAMIC_OPS_CLASS = ReflectUtil.getOptionalClass("com.mojang.serialization.DynamicOps");
JSON_OPS_CLASS = ReflectUtil.getOptionalClass("com.mojang.serialization.JsonOps");
CODEC_CLASS = ReflectUtil.getOptionalClass("com.mojang.serialization.Codec");
SERVER_PLAYER_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.server.level.ServerPlayer", "net.minecraft.server.level.EntityPlayer", oldSpigotClassName("EntityPlayer"));
PLAYER_CONNECTION_CLASS = ReflectUtil.getClassOrThrow("net.minecraft.server.network.ServerGamePacketListenerImpl", "net.minecraft.server.network.PlayerConnection", oldSpigotClassName("PlayerConnection"));
ADVENTURE_COMPONENT_CLASS = ReflectUtil.getOptionalClass("io.papermc.paper.adventure.AdventureComponent");
}
static {
if (IS_1_20_3_OR_ABOVE) {
RESET_SCORE_CONSTRUCTOR = ReflectUtil.findConstructor(Objects.requireNonNull(RESET_SCORE_PKT_CLASS), String.class, String.class);
} else {
RESET_SCORE_CONSTRUCTOR = null;
}
if (IS_1_20_5_OR_ABOVE) {
OBJECTIVE_NUMBER_FORMAT_FIELD = ReflectUtil.findFieldUnchecked(SET_OBJECTIVE_PKT_CLASS, 0, Optional.class);
SCORE_CONSTRUCTOR = ReflectUtil.findConstructor(SET_SCORE_PKT_CLASS, String.class, String.class, int.class, Optional.class, Optional.class);
SCORE_1_20_2_METHOD_CHANGE = null;
SCORE_1_20_2_METHOD_REMOVE = null;
} else if (IS_1_20_3_OR_ABOVE) {
assert NUMBER_FORMAT_CLASS != null;
OBJECTIVE_NUMBER_FORMAT_FIELD = ReflectUtil.findFieldUnchecked(SET_OBJECTIVE_PKT_CLASS, 0, NUMBER_FORMAT_CLASS);
SCORE_CONSTRUCTOR = ReflectUtil.findConstructor(SET_SCORE_PKT_CLASS, String.class, String.class, int.class, COMPONENT_CLASS, NUMBER_FORMAT_CLASS);
SCORE_1_20_2_METHOD_CHANGE = null;
SCORE_1_20_2_METHOD_REMOVE = null;
} else {
OBJECTIVE_NUMBER_FORMAT_FIELD = null;
SCORE_1_20_3_CONSTRUCTOR = null;
SCORE_1_20_2_CONSTRUCTOR = ReflectUtil.findConstructor(ClientboundSetScorePacket.class, ServerScoreboard.Method.class, String.class, String.class, int.class);
Class<?> methodClass = ReflectUtil.getClassOrThrow("net.minecraft.server.ServerScoreboard$Method", "net.minecraft.server.ScoreboardServer$Action", oldSpigotClassName("ScoreboardServer$Action"));
SCORE_1_20_2_METHOD_CHANGE = ReflectUtil.getEnumInstance(methodClass, "CHANGE", "a");
SCORE_1_20_2_METHOD_REMOVE = ReflectUtil.getEnumInstance(methodClass, "REMOVE", "b");
SCORE_CONSTRUCTOR = ReflectUtil.findConstructor(SET_SCORE_PKT_CLASS, methodClass, String.class, String.class, int.class);
}
if (PacketAccessors.DATA_RESULT_CLASS != null) {
RESULT_UNWRAP_METHOD = ReflectUtil.findMethod(PacketAccessors.DATA_RESULT_CLASS, false, MethodType.methodType(Optional.class), "result");
try {
JSON_OPS = PacketAccessors.JSON_OPS_CLASS.getField("INSTANCE").get(null);
} catch (IllegalAccessException | NoSuchFieldException e) {
throw new RuntimeException(e);
}
CODEC_PARSE = ReflectUtil.findMethod(PacketAccessors.CODEC_CLASS, false, MethodType.methodType(PacketAccessors.DATA_RESULT_CLASS, PacketAccessors.DYNAMIC_OPS_CLASS, Object.class), "parse");
} else {
RESULT_UNWRAP_METHOD = null;
JSON_OPS = null;
CODEC_PARSE = null;
}
}
public static final boolean IS_1_20_2_OR_ABOVE, IS_1_20_3_OR_ABOVE, IS_1_20_5_OR_ABOVE;
public static final MethodAccessor RESULT_UNWRAP_METHOD;
public static final MethodAccessor CODEC_PARSE;
public static final Object JSON_OPS;
public static final PacketConstructor<ClientboundSetObjectivePacket> OBJECTIVE_PACKET_CONSTRUCTOR =
ReflectUtil.getEmptyConstructor(ClientboundSetObjectivePacket.class);
public static final FieldAccessor<ClientboundSetObjectivePacket, String> OBJECTIVE_NAME_FIELD =
ReflectUtil.findField(ClientboundSetObjectivePacket.class, 0, String.class);
public static final FieldAccessor<ClientboundSetObjectivePacket, net.minecraft.network.chat.Component> OBJECTIVE_VALUE_FIELD =
ReflectUtil.findField(ClientboundSetObjectivePacket.class, 0, net.minecraft.network.chat.Component.class);
public static final FieldAccessor<ClientboundSetObjectivePacket, ObjectiveCriteria.RenderType> OBJECTIVE_RENDER_TYPE_FIELD =
ReflectUtil.findField(ClientboundSetObjectivePacket.class, 0, ObjectiveCriteria.RenderType.class);
public static final Map<NamedTextColor, Object> NMS_CHAT_FORMATTING_MAP = new HashMap<>();
static {
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()) {
char c = LegacyFormatUtil.getChar(color);
for (Object chatFormatting : chatFormattings) {
if (c == (char) charField.get(chatFormatting)) {
NMS_CHAT_FORMATTING_MAP.put(color, chatFormatting);
continue outer;
}
}
throw new RuntimeException("No chat formatting enum constant found for " + color.toString());
}
}
public static final Object NAME_TAG_VISIBILITY_ALWAYS = ReflectUtil.getEnumInstance(TEAM_VISIBILITY_CLASS, "ALWAYS");
public static final Object NAME_TAG_VISIBILITY_NEVER = ReflectUtil.getEnumInstance(TEAM_VISIBILITY_CLASS, "NEVER");
public static final Object NAME_TAG_VISIBILITY_HIDE_FOR_OTHER_TEAMS = ReflectUtil.getEnumInstance(TEAM_VISIBILITY_CLASS, "HIDE_FOR_OTHER_TEAMS");
public static final Object NAME_TAG_VISIBILITY_HIDE_FOR_OWN_TEAM = ReflectUtil.getEnumInstance(TEAM_VISIBILITY_CLASS, "HIDE_FOR_OWN_TEAM");
public static final Object COLLISION_RULE_ALWAYS = ReflectUtil.getEnumInstance(TEAM_COLLISION_RULE_CLASS, "ALWAYS");
public static final Object COLLISION_RULE_NEVER = ReflectUtil.getEnumInstance(TEAM_COLLISION_RULE_CLASS, "NEVER");
public static final Object COLLISION_RULE_PUSH_OTHER_TEAMS = ReflectUtil.getEnumInstance(TEAM_COLLISION_RULE_CLASS, "PUSH_OTHER_TEAMS");
public static final Object COLLISION_RULE_PUSH_OWN_TEAM = ReflectUtil.getEnumInstance(TEAM_COLLISION_RULE_CLASS, "PUSH_OWN_TEAM");
public static final Object RENDER_TYPE_INTEGER = ReflectUtil.getEnumInstance(OBJECTIVE_CRITERIA_RENDER_TYPE_CLASS, "INTEGER");
public static final Object RENDER_TYPE_HEARTS = ReflectUtil.getEnumInstance(OBJECTIVE_CRITERIA_RENDER_TYPE_CLASS, "HEARTS");
public static final List<Object> DISPLAY_SLOT_VALUES = DISPLAY_SLOT_CLASS == null ? null : Arrays.asList(DISPLAY_SLOT_CLASS.getEnumConstants());
public static final ConstructorAccessor<?> ADVENTURE_COMPONENT_CONSTRUCTOR =
ADVENTURE_COMPONENT_CLASS != null ? ReflectUtil.findOptionalConstructor(ADVENTURE_COMPONENT_CLASS, Component.class) : null;
// --- OBJECTIVES ---
public static final PacketConstructor<?> OBJECTIVE_PACKET_CONSTRUCTOR =
ReflectUtil.getEmptyConstructor(SET_OBJECTIVE_PKT_CLASS);
public static final FieldAccessor<Object, String> OBJECTIVE_NAME_FIELD =
ReflectUtil.findFieldUnchecked(SET_OBJECTIVE_PKT_CLASS, 0, String.class);
public static final FieldAccessor<Object, Object> OBJECTIVE_VALUE_FIELD =
ReflectUtil.findFieldUnchecked(SET_OBJECTIVE_PKT_CLASS, 0, COMPONENT_CLASS);
public static final FieldAccessor<Object, Object> OBJECTIVE_RENDER_TYPE_FIELD =
ReflectUtil.findFieldUnchecked(SET_OBJECTIVE_PKT_CLASS, 0, OBJECTIVE_CRITERIA_RENDER_TYPE_CLASS);
// Optional<NumberFormat> for 1.20.5+, NumberFormat for below
public static final FieldAccessor<ClientboundSetObjectivePacket, Object> OBJECTIVE_NUMBER_FORMAT_FIELD;
public static final FieldAccessor<ClientboundSetObjectivePacket, Integer> OBJECTIVE_MODE_FIELD =
ReflectUtil.findField(ClientboundSetObjectivePacket.class, 0, int.class);
public static final FieldAccessor<Object, Object> OBJECTIVE_NUMBER_FORMAT_FIELD;
public static final FieldAccessor<Object, Integer> OBJECTIVE_MODE_FIELD =
ReflectUtil.findFieldUnchecked(SET_OBJECTIVE_PKT_CLASS, 0, int.class);
public static final ConstructorAccessor<ClientboundSetDisplayObjectivePacket> DISPLAY_1_20_1_CONSTRUCTOR =
ReflectUtil.findOptionalConstructor(ClientboundSetDisplayObjectivePacket.class, int.class, Objective.class);
public static final FieldAccessor<ClientboundSetDisplayObjectivePacket, String> DISPLAY_OBJECTIVE_NAME =
ReflectUtil.findField(ClientboundSetDisplayObjectivePacket.class, 0, String.class);
public static final ConstructorAccessor<?> DISPLAY_CONSTRUCTOR;
public static final FieldAccessor<Object, String> DISPLAY_OBJECTIVE_NAME =
ReflectUtil.findFieldUnchecked(SET_DISPLAY_OBJECTIVE_PKT_CLASS, 0, String.class);
public static final FieldAccessor<Object, Object> DISPLAY_SLOT;
public static final ConstructorAccessor<ClientboundSetScorePacket> SCORE_1_20_3_CONSTRUCTOR;
public static final ConstructorAccessor<ClientboundSetScorePacket> SCORE_1_20_2_CONSTRUCTOR;
static {
if (IS_1_20_2_OR_ABOVE) {
assert DISPLAY_SLOT_CLASS != null;
DISPLAY_CONSTRUCTOR = ReflectUtil.findConstructor(SET_DISPLAY_OBJECTIVE_PKT_CLASS, DISPLAY_SLOT_CLASS, OBJECTIVE_CLASS);
DISPLAY_SLOT = null;
} else if (IS_1_17_OR_ABOVE) {
DISPLAY_CONSTRUCTOR = ReflectUtil.findConstructor(SET_DISPLAY_OBJECTIVE_PKT_CLASS, int.class, OBJECTIVE_CLASS);
DISPLAY_SLOT = null;
} else {
DISPLAY_CONSTRUCTOR = ReflectUtil.findConstructor(SET_DISPLAY_OBJECTIVE_PKT_CLASS);
DISPLAY_SLOT = ReflectUtil.findFieldUnchecked(SET_DISPLAY_OBJECTIVE_PKT_CLASS, 0, int.class);
}
}
public static final ConstructorAccessor<ClientboundSetPlayerTeamPacket> TEAM_PACKET_CONSTRUCTOR =
ReflectUtil.findConstructor(ClientboundSetPlayerTeamPacket.class, String.class, int.class, Optional.class, Collection.class);
public static final PacketConstructor<ClientboundSetPlayerTeamPacket.Parameters> PARAMETERS_CONSTRUCTOR =
ReflectUtil.getEmptyConstructor(ClientboundSetPlayerTeamPacket.Parameters.class);
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, Component> DISPLAY_NAME_FIELD =
ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 0, net.minecraft.network.chat.Component.class);
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, net.minecraft.network.chat.Component> PREFIX_FIELD =
ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 1, net.minecraft.network.chat.Component.class);
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, net.minecraft.network.chat.Component> SUFFIX_FIELD =
ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 2, net.minecraft.network.chat.Component.class);
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, String> NAME_TAG_VISIBILITY_FIELD =
ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 0, String.class);
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, String> COLLISION_RULE_FIELD =
ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 1, String.class);
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, ChatFormatting> COLOR_FIELD =
ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 0, ChatFormatting.class);
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, Integer> OPTIONS_FIELD =
ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 0, int.class);
public static final ConstructorAccessor<?> RESET_SCORE_CONSTRUCTOR; // 1.20.3+
private PacketAccessors() {
public static final ConstructorAccessor<?> SCORE_CONSTRUCTOR;
public static final Object SCORE_1_20_2_METHOD_CHANGE, SCORE_1_20_2_METHOD_REMOVE;
// --- TEAMS ---
public static final PacketConstructor<?> PARAMETERS_CONSTRUCTOR;
public static final ConstructorAccessor<?> TEAM_PACKET_CONSTRUCTOR;
public static final FieldAccessor<Object, String> TEAM_NAME_FIELD;
public static final FieldAccessor<Object, Integer> TEAM_MODE_FIELD;
public static final FieldAccessor<Object, Collection<String>> TEAM_ENTRIES_FIELD;
public static final FieldAccessor<Object, Object> DISPLAY_NAME_FIELD;
public static final FieldAccessor<Object, Object> PREFIX_FIELD;
public static final FieldAccessor<Object, Object> SUFFIX_FIELD;
public static final FieldAccessor<Object, Object> NAME_TAG_VISIBILITY_FIELD;
public static final FieldAccessor<Object, Object> COLLISION_RULE_FIELD;
public static final FieldAccessor<Object, Object> COLOR_FIELD;
public static final FieldAccessor<Object, Integer> OPTIONS_FIELD;
static {
if (IS_1_17_OR_ABOVE) {
assert TEAM_PARAMETERS_PKT_CLASS != null;
PARAMETERS_CONSTRUCTOR = ReflectUtil.getEmptyConstructor(TEAM_PARAMETERS_PKT_CLASS);
TEAM_PACKET_CONSTRUCTOR = ReflectUtil.findConstructor(SET_PLAYER_TEAM_PKT_CLASS, String.class, int.class, Optional.class, Collection.class);
TEAM_NAME_FIELD = null;
TEAM_MODE_FIELD = null;
TEAM_ENTRIES_FIELD = null;
DISPLAY_NAME_FIELD = ReflectUtil.findFieldUnchecked(TEAM_PARAMETERS_PKT_CLASS, 0, COMPONENT_CLASS);
PREFIX_FIELD = ReflectUtil.findFieldUnchecked(TEAM_PARAMETERS_PKT_CLASS, 1, COMPONENT_CLASS);
SUFFIX_FIELD = ReflectUtil.findFieldUnchecked(TEAM_PARAMETERS_PKT_CLASS, 2, COMPONENT_CLASS);
if (IS_1_21_5_OR_ABOVE) {
NAME_TAG_VISIBILITY_FIELD = ReflectUtil.findFieldUnchecked(TEAM_PARAMETERS_PKT_CLASS, 0, TEAM_VISIBILITY_CLASS);
COLLISION_RULE_FIELD = ReflectUtil.findFieldUnchecked(TEAM_PARAMETERS_PKT_CLASS, 0, TEAM_COLLISION_RULE_CLASS);
} else {
NAME_TAG_VISIBILITY_FIELD = ReflectUtil.findFieldUnchecked(TEAM_PARAMETERS_PKT_CLASS, 0, String.class);
COLLISION_RULE_FIELD = ReflectUtil.findFieldUnchecked(TEAM_PARAMETERS_PKT_CLASS, 1, String.class);
}
COLOR_FIELD = ReflectUtil.findFieldUnchecked(TEAM_PARAMETERS_PKT_CLASS, 0, CHAT_FORMATTING_CLASS);
OPTIONS_FIELD = ReflectUtil.findFieldUnchecked(TEAM_PARAMETERS_PKT_CLASS, 0, int.class);
} else {
PARAMETERS_CONSTRUCTOR = null;
TEAM_PACKET_CONSTRUCTOR = ReflectUtil.findConstructor(SET_PLAYER_TEAM_PKT_CLASS);
TEAM_NAME_FIELD = ReflectUtil.findFieldUnchecked(SET_PLAYER_TEAM_PKT_CLASS, 0, String.class);
TEAM_MODE_FIELD = ReflectUtil.findFieldUnchecked(SET_PLAYER_TEAM_PKT_CLASS, 0, int.class);
TEAM_ENTRIES_FIELD = ReflectUtil.findFieldUnchecked(SET_PLAYER_TEAM_PKT_CLASS, 0, Collection.class);
DISPLAY_NAME_FIELD = ReflectUtil.findFieldUnchecked(SET_PLAYER_TEAM_PKT_CLASS, 0, COMPONENT_CLASS);
PREFIX_FIELD = ReflectUtil.findFieldUnchecked(SET_PLAYER_TEAM_PKT_CLASS, 1, COMPONENT_CLASS);
SUFFIX_FIELD = ReflectUtil.findFieldUnchecked(SET_PLAYER_TEAM_PKT_CLASS, 2, COMPONENT_CLASS);
NAME_TAG_VISIBILITY_FIELD = ReflectUtil.findFieldUnchecked(SET_PLAYER_TEAM_PKT_CLASS, 1, String.class);
COLLISION_RULE_FIELD = ReflectUtil.findFieldUnchecked(SET_PLAYER_TEAM_PKT_CLASS, 2, String.class);
COLOR_FIELD = ReflectUtil.findFieldUnchecked(SET_PLAYER_TEAM_PKT_CLASS, 0, CHAT_FORMATTING_CLASS);
OPTIONS_FIELD = ReflectUtil.findFieldUnchecked(SET_PLAYER_TEAM_PKT_CLASS, 1, int.class);
}
}
public static Object nameTagVisibility(NameTagVisibility value) {
switch (value) {
case NEVER:
return PacketAccessors.NAME_TAG_VISIBILITY_NEVER;
case ALWAYS:
return PacketAccessors.NAME_TAG_VISIBILITY_ALWAYS;
case HIDE_FOR_OTHER_TEAMS:
return PacketAccessors.NAME_TAG_VISIBILITY_HIDE_FOR_OTHER_TEAMS;
case HIDE_FOR_OWN_TEAM:
return PacketAccessors.NAME_TAG_VISIBILITY_HIDE_FOR_OWN_TEAM;
default:
throw new IllegalStateException("unknown name tag visibility " + value.name());
}
}
public static Object collisionRule(CollisionRule value) {
switch (value) {
case NEVER:
return PacketAccessors.COLLISION_RULE_NEVER;
case ALWAYS:
return PacketAccessors.COLLISION_RULE_ALWAYS;
case PUSH_OTHER_TEAMS:
return PacketAccessors.COLLISION_RULE_PUSH_OTHER_TEAMS;
case PUSH_OWN_TEAM:
return PacketAccessors.COLLISION_RULE_PUSH_OWN_TEAM;
default:
throw new IllegalStateException("unknown collision rule " + value.name());
}
}
public static @NotNull Object fromAdventureComponent(@NotNull Component component) {
return Objects.requireNonNull(PacketAccessors.ADVENTURE_COMPONENT_CONSTRUCTOR).invoke(component);
}
public static @Nullable String oldSpigotClassName(String clazz) {
if (OLD_NMS_VERSION_STRING != null) {
return "net.minecraft.server." + OLD_NMS_VERSION_STRING + "." + clazz;
}
return null;
}
}
@@ -1,62 +1,80 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern;
import net.kyori.adventure.text.Component;
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.PacketSender;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.objective.PaperObjectivePacketAdapter;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.objective.SpigotObjectivePacketAdapter;
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.PacketUtil;
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 net.minecraft.network.protocol.Packet;
import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class PacketAdapterProviderImpl implements PacketAdapterProvider, PacketSender<Packet<?>> {
private boolean isNativeAdventure;
private final ComponentProvider componentProvider;
import java.util.WeakHashMap;
public PacketAdapterProviderImpl() {
try {
Class.forName("io.papermc.paper.adventure.PaperAdventure");
@SuppressWarnings("unused")
public class PacketAdapterProviderImpl implements PacketAdapterProvider {
private final ViaAPI<Player> via;
private final ModernPacketSender packetSender;
private final WeakHashMap<Player, Integer> viaTeamPacketIds = new WeakHashMap<>();
// Hide from relocation checkers
String notRelocatedPackage = "net.ky".concat("ori.adventure.text");
// The native adventure optimisations only work when the adventure library isn't relocated
if (Component.class.getPackage().getName().equals(notRelocatedPackage)) {
isNativeAdventure = true;
}
} catch (ClassNotFoundException ignored) {
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<Player>) Via.getAPI();
} else {
this.via = null;
}
this.componentProvider = new ComponentProviderImpl(isNativeAdventure);
this.packetSender = new ModernPacketSender(this.via);
}
@Override
public @NotNull ObjectivePacketAdapter createObjectiveAdapter(@NotNull String objectiveName) {
return isNativeAdventure
? new PaperObjectivePacketAdapter(this, componentProvider, objectiveName)
: new SpigotObjectivePacketAdapter(this, componentProvider, objectiveName);
return ModernComponentProvider.IS_NATIVE_ADVENTURE
? new PaperObjectivePacketAdapter(this, objectiveName)
: new SpigotObjectivePacketAdapter(this, objectiveName);
}
@Override
public @NotNull TeamsPacketAdapter createTeamPacketAdapter(@NotNull String teamName) {
return isNativeAdventure
? new PaperTeamsPacketAdapterImpl(this, componentProvider, teamName)
: new SpigotTeamsPacketAdapter(this, componentProvider, teamName);
return ModernComponentProvider.IS_NATIVE_ADVENTURE
? 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;
}
@Override
public void sendPacket(@NotNull Player player, @NotNull Packet<?> packet) {
PacketUtil.sendPacket(player, packet);
public @Nullable ViaAPI<Player> via() {
return via;
}
public @NotNull ModernPacketSender packetSender() {
return packetSender;
}
public @NotNull WeakHashMap<Player, Integer> viaTeamPacketIds() {
return viaTeamPacketIds;
}
}
@@ -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;
}
}
@@ -2,21 +2,11 @@ package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.object
import net.megavex.scoreboardlibrary.api.objective.ObjectiveDisplaySlot;
import net.megavex.scoreboardlibrary.api.objective.ObjectiveRenderType;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.ComponentProvider;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors;
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 net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ReflectUtil;
import net.minecraft.network.chat.numbers.NumberFormat;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundResetScorePacket;
import net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket;
import net.minecraft.network.protocol.game.ClientboundSetObjectivePacket;
import net.minecraft.network.protocol.game.ClientboundSetScorePacket;
import net.minecraft.server.ServerScoreboard;
import net.minecraft.world.scores.criteria.ObjectiveCriteria;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -26,80 +16,90 @@ import java.util.Objects;
import java.util.Optional;
public abstract class AbstractObjectivePacketAdapter implements ObjectivePacketAdapter {
protected final PacketSender<Packet<?>> sender;
protected final ComponentProvider componentProvider;
protected final PacketAdapterProviderImpl provider;
protected final String objectiveName;
private ClientboundSetObjectivePacket removePacket;
private Object removePacket;
public AbstractObjectivePacketAdapter(@NotNull PacketSender<Packet<?>> sender, @NotNull ComponentProvider componentProvider, @NotNull String objectiveName) {
this.sender = sender;
this.componentProvider = componentProvider;
public AbstractObjectivePacketAdapter(@NotNull PacketAdapterProviderImpl provider, @NotNull String objectiveName) {
this.provider = provider;
this.objectiveName = objectiveName;
}
@Override
public @NotNull String objectiveName() {
return objectiveName;
}
@Override
public void display(@NotNull Collection<Player> players, @NotNull ObjectiveDisplaySlot slot) {
sender.sendPacket(players, createDisplayPacket(slot));
provider.packetSender().sendPacket(players, createDisplayPacket(slot));
}
@Override
public void remove(@NotNull Collection<Player> players) {
if (removePacket == null) {
this.removePacket = ReflectUtil.getEmptyConstructor(ClientboundSetObjectivePacket.class).invoke();
this.removePacket = PacketAccessors.OBJECTIVE_PACKET_CONSTRUCTOR.invoke();
PacketAccessors.OBJECTIVE_NAME_FIELD.set(removePacket, objectiveName);
PacketAccessors.OBJECTIVE_MODE_FIELD.set(removePacket, ObjectiveConstants.MODE_REMOVE);
}
sender.sendPacket(players, removePacket);
provider.packetSender().sendPacket(players, removePacket);
}
@Override
public void removeScore(@NotNull Collection<Player> players, @NotNull String entry) {
Packet<?> packet;
Object packet;
if (PacketAccessors.IS_1_20_3_OR_ABOVE) {
packet = new ClientboundResetScorePacket(entry, objectiveName);
packet = Objects.requireNonNull(PacketAccessors.RESET_SCORE_CONSTRUCTOR)
.invoke(entry, objectiveName);
} else {
packet = Objects.requireNonNull(PacketAccessors.SCORE_1_20_2_CONSTRUCTOR)
.invoke(ServerScoreboard.Method.REMOVE, objectiveName, entry, 0);
packet = Objects.requireNonNull(PacketAccessors.SCORE_CONSTRUCTOR)
.invoke(PacketAccessors.SCORE_1_20_2_METHOD_REMOVE, objectiveName, entry, 0);
}
sender.sendPacket(players, packet);
provider.packetSender().sendPacket(players, packet);
}
protected @NotNull ClientboundSetDisplayObjectivePacket createDisplayPacket(@NotNull ObjectiveDisplaySlot displaySlot) {
ClientboundSetDisplayObjectivePacket packet;
protected @NotNull Object createDisplayPacket(@NotNull ObjectiveDisplaySlot displaySlot) {
Object packet;
if (PacketAccessors.IS_1_20_2_OR_ABOVE) {
packet = new ClientboundSetDisplayObjectivePacket(DisplaySlotProvider.toNms(displaySlot), null);
} else {
packet = Objects.requireNonNull(PacketAccessors.DISPLAY_1_20_1_CONSTRUCTOR)
packet = Objects.requireNonNull(PacketAccessors.DISPLAY_CONSTRUCTOR)
.invoke(PacketAccessors.DISPLAY_SLOT_VALUES.get(ObjectiveConstants.displaySlotIndex(displaySlot)), null);
} else if (PacketAccessors.IS_1_17_OR_ABOVE){
packet = Objects.requireNonNull(PacketAccessors.DISPLAY_CONSTRUCTOR)
.invoke(ObjectiveConstants.displaySlotIndex(displaySlot), null);
} else {
assert PacketAccessors.DISPLAY_SLOT != null;
packet = Objects.requireNonNull(PacketAccessors.DISPLAY_CONSTRUCTOR).invoke();
PacketAccessors.DISPLAY_SLOT.set(packet, ObjectiveConstants.displaySlotIndex(displaySlot));
}
PacketAccessors.DISPLAY_OBJECTIVE_NAME.set(packet, objectiveName);
return packet;
}
protected @NotNull ClientboundSetScorePacket createScorePacket(
protected @NotNull Object createScorePacket(
@NotNull String entry,
int value,
@Nullable net.minecraft.network.chat.Component nmsDisplay,
@Nullable Object nmsDisplay,
@Nullable Object numberFormat
) {
if (PacketAccessors.IS_1_20_5_OR_ABOVE) {
return new ClientboundSetScorePacket(entry, objectiveName, value, Optional.ofNullable(nmsDisplay), Optional.ofNullable((NumberFormat) numberFormat));
return Objects.requireNonNull(PacketAccessors.SCORE_CONSTRUCTOR)
.invoke(entry, objectiveName, value, Optional.ofNullable(nmsDisplay), Optional.ofNullable(numberFormat));
} else if (PacketAccessors.IS_1_20_3_OR_ABOVE) {
return Objects.requireNonNull(PacketAccessors.SCORE_1_20_3_CONSTRUCTOR)
return Objects.requireNonNull(PacketAccessors.SCORE_CONSTRUCTOR)
.invoke(entry, objectiveName, value, nmsDisplay, numberFormat);
} else {
return Objects.requireNonNull(PacketAccessors.SCORE_1_20_2_CONSTRUCTOR)
.invoke(ServerScoreboard.Method.CHANGE, objectiveName, entry, value);
return Objects.requireNonNull(PacketAccessors.SCORE_CONSTRUCTOR)
.invoke(PacketAccessors.SCORE_1_20_2_METHOD_CHANGE, objectiveName, entry, value);
}
}
protected @NotNull ClientboundSetObjectivePacket createObjectivePacket(
protected @NotNull Object createObjectivePacket(
@NotNull PropertiesPacketType packetType,
@NotNull net.minecraft.network.chat.Component nmsValue,
@NotNull Object nmsValue,
@NotNull ObjectiveRenderType renderType,
@Nullable Object numberFormat
) {
ClientboundSetObjectivePacket packet = PacketAccessors.OBJECTIVE_PACKET_CONSTRUCTOR.invoke();
Object packet = PacketAccessors.OBJECTIVE_PACKET_CONSTRUCTOR.invoke();
PacketAccessors.OBJECTIVE_MODE_FIELD.set(packet, ObjectiveConstants.mode(packetType));
PacketAccessors.OBJECTIVE_NAME_FIELD.set(packet, objectiveName);
PacketAccessors.OBJECTIVE_VALUE_FIELD.set(packet, nmsValue);
@@ -115,16 +115,16 @@ public abstract class AbstractObjectivePacketAdapter implements ObjectivePacketA
PacketAccessors.OBJECTIVE_NUMBER_FORMAT_FIELD.set(packet, value);
}
ObjectiveCriteria.RenderType nmsRenderType;
Object nmsRenderType;
switch (renderType) {
case INTEGER:
nmsRenderType = ObjectiveCriteria.RenderType.INTEGER;
nmsRenderType = PacketAccessors.RENDER_TYPE_INTEGER;
break;
case HEARTS:
nmsRenderType = ObjectiveCriteria.RenderType.HEARTS;
nmsRenderType = PacketAccessors.RENDER_TYPE_HEARTS;
break;
default:
throw new IllegalStateException();
throw new IllegalStateException("unknown render type " + renderType);
}
PacketAccessors.OBJECTIVE_RENDER_TYPE_FIELD.set(packet, nmsRenderType);
@@ -1,18 +0,0 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.objective;
import net.megavex.scoreboardlibrary.api.objective.ObjectiveDisplaySlot;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectiveConstants;
import net.minecraft.world.scores.DisplaySlot;
import org.jetbrains.annotations.NotNull;
// DisplaySlot was added in 1.20.2 so this has to be a separate class
public final class DisplaySlotProvider {
private static final DisplaySlot[] VALUES = DisplaySlot.values();
private DisplaySlotProvider() {
}
public static @NotNull DisplaySlot toNms(@NotNull ObjectiveDisplaySlot slot) {
return VALUES[ObjectiveConstants.displaySlotIndex(slot)];
}
}
@@ -1,15 +1,12 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.objective;
import io.papermc.paper.adventure.AdventureComponent;
import net.kyori.adventure.text.Component;
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.ComponentProvider;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAdapterProviderImpl;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.NativeAdventureUtil;
import net.minecraft.network.protocol.game.ClientboundSetObjectivePacket;
import net.minecraft.network.protocol.game.ClientboundSetScorePacket;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernComponentProvider;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -17,16 +14,16 @@ import org.jetbrains.annotations.Nullable;
import java.util.Collection;
public class PaperObjectivePacketAdapter extends AbstractObjectivePacketAdapter {
public PaperObjectivePacketAdapter(@NotNull PacketAdapterProviderImpl packetAdapter, @NotNull ComponentProvider componentProvider, @NotNull String objectiveName) {
super(packetAdapter, componentProvider, objectiveName);
public PaperObjectivePacketAdapter(@NotNull PacketAdapterProviderImpl provider, @NotNull String objectiveName) {
super(provider, objectiveName);
}
@Override
public void sendScore(@NotNull Collection<Player> players, @NotNull String entry, int value, @Nullable Component display, @Nullable ScoreFormat scoreFormat) {
net.minecraft.network.chat.Component nmsDisplay = display == null ? null : componentProvider.fromAdventure(display, null);
Object numberFormat = ScoreFormatConverter.convert(componentProvider, null, scoreFormat);
ClientboundSetScorePacket packet = createScorePacket(entry, value, nmsDisplay, numberFormat);
sender.sendPacket(players, packet);
Object nmsDisplay = display == null ? null : ModernComponentProvider.fromAdventure(display, null);
Object numberFormat = ScoreFormatConverter.convert(null, scoreFormat);
Object packet = createScorePacket(entry, value, nmsDisplay, numberFormat);
provider.packetSender().sendPacket(players, packet);
}
@Override
@@ -37,9 +34,9 @@ public class PaperObjectivePacketAdapter extends AbstractObjectivePacketAdapter
@NotNull ObjectiveRenderType renderType,
@Nullable ScoreFormat scoreFormat
) {
AdventureComponent nmsValue = NativeAdventureUtil.fromAdventureComponent(value);
Object numberFormat = ScoreFormatConverter.convert(componentProvider, null, scoreFormat);
ClientboundSetObjectivePacket packet = createObjectivePacket(packetType, nmsValue, renderType, numberFormat);
sender.sendPacket(players, packet);
Object nmsValue = PacketAccessors.fromAdventureComponent(value);
Object numberFormat = ScoreFormatConverter.convert(null, scoreFormat);
Object packet = createObjectivePacket(packetType, nmsValue, renderType, numberFormat);
provider.packetSender().sendPacket(players, packet);
}
}
@@ -1,60 +1,62 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.objective;
import com.google.gson.JsonElement;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.JsonOps;
import net.megavex.scoreboardlibrary.api.objective.ScoreFormat;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.ComponentProvider;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors;
import net.minecraft.network.chat.Style;
import net.minecraft.network.chat.numbers.BlankFormat;
import net.minecraft.network.chat.numbers.FixedFormat;
import net.minecraft.network.chat.numbers.StyledFormat;
import org.jetbrains.annotations.NotNull;
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;
import org.jetbrains.annotations.Nullable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Locale;
import java.util.Optional;
import static net.kyori.adventure.text.serializer.gson.GsonComponentSerializer.gson;
public final class ScoreFormatConverter {
private static final MethodHandle RESULT_UNWRAP_METHOD;
private static final Object STYLE_CODEC;
private static final Object BLANK;
private static final ConstructorAccessor<?> STYLED_CONSTRUCTOR;
private static final ConstructorAccessor<?> FIXED_CONSTRUCTOR;
static {
try {
RESULT_UNWRAP_METHOD = MethodHandles.lookup().findVirtual(DataResult.class, "result", MethodType.methodType(Optional.class));
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new RuntimeException(e);
if (PacketAccessors.IS_1_20_3_OR_ABOVE) {
STYLE_CODEC = ReflectUtil.findFieldUnchecked(PacketAccessors.STYLE_SERIALIZER_CLASS, 0, PacketAccessors.CODEC_CLASS, true).get(null);
Class<?> blankFormatClass = ReflectUtil.getClassOrThrow("net.minecraft.network.chat.numbers.BlankFormat");
BLANK = ReflectUtil.findFieldUnchecked(blankFormatClass, 0, blankFormatClass, true).get(null);
Class<?> styledFormatClass = ReflectUtil.getClassOrThrow("net.minecraft.network.chat.numbers.StyledFormat");
STYLED_CONSTRUCTOR = ReflectUtil.findConstructor(styledFormatClass, PacketAccessors.STYLE_CLASS);
Class<?> fixedFormatClass = ReflectUtil.getClassOrThrow("net.minecraft.network.chat.numbers.FixedFormat");
FIXED_CONSTRUCTOR = ReflectUtil.findConstructor(fixedFormatClass, PacketAccessors.COMPONENT_CLASS);
} else {
STYLE_CODEC = null;
BLANK = null;
STYLED_CONSTRUCTOR = null;
FIXED_CONSTRUCTOR = null;
}
}
private ScoreFormatConverter() {
}
public static @Nullable Object convert(@NotNull ComponentProvider componentProvider, @Nullable Locale locale, @Nullable ScoreFormat format) {
public static @Nullable Object convert(@Nullable Locale locale, @Nullable ScoreFormat format) {
if (format == null || !PacketAccessors.IS_1_20_3_OR_ABOVE) {
return null;
}
if (format == ScoreFormat.blank()) {
return BlankFormat.INSTANCE;
return BLANK;
} else if (format instanceof ScoreFormat.Styled) {
JsonElement json = gson().serializer().toJsonTree(((ScoreFormat.Styled) format).style());
Object result = Style.Serializer.CODEC.parse(JsonOps.INSTANCE, json);
Style style;
try {
//noinspection unchecked,rawtypes
style = (Style) ((Optional) RESULT_UNWRAP_METHOD.invokeExact((DataResult) result)).orElseThrow(RuntimeException::new);
} catch (Throwable e) {
throw new RuntimeException(e);
}
return new StyledFormat(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();
return STYLED_CONSTRUCTOR.invoke(style);
} else if (format instanceof ScoreFormat.Fixed) {
return new FixedFormat(componentProvider.fromAdventure(((ScoreFormat.Fixed) format).content(), locale));
return FIXED_CONSTRUCTOR.invoke(ModernComponentProvider.fromAdventure(((ScoreFormat.Fixed) format).content(), locale));
} else {
throw new IllegalArgumentException("Invalid score format: " + format);
}
@@ -4,8 +4,8 @@ import net.kyori.adventure.text.Component;
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.ComponentProvider;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAdapterProviderImpl;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernComponentProvider;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.LocalePacketUtil;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@@ -14,15 +14,15 @@ import org.jetbrains.annotations.Nullable;
import java.util.Collection;
public class SpigotObjectivePacketAdapter extends AbstractObjectivePacketAdapter {
public SpigotObjectivePacketAdapter(@NotNull PacketAdapterProviderImpl packetAdapter, @NotNull ComponentProvider componentProvider, @NotNull String objectiveName) {
super(packetAdapter, componentProvider, objectiveName);
public SpigotObjectivePacketAdapter(@NotNull PacketAdapterProviderImpl provider, @NotNull String objectiveName) {
super(provider, objectiveName);
}
@Override
public void sendScore(@NotNull Collection<Player> players, @NotNull String entry, int value, @Nullable Component display, @Nullable ScoreFormat scoreFormat) {
LocalePacketUtil.sendLocalePackets(sender, players, locale -> {
net.minecraft.network.chat.Component nmsDisplay = display == null ? null : componentProvider.fromAdventure(display, locale);
Object numberFormat = ScoreFormatConverter.convert(componentProvider, locale, scoreFormat);
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,11 +36,11 @@ public class SpigotObjectivePacketAdapter extends AbstractObjectivePacketAdapter
@Nullable ScoreFormat scoreFormat
) {
LocalePacketUtil.sendLocalePackets(
sender,
provider.packetSender(),
players,
locale -> {
Object numberFormat = ScoreFormatConverter.convert(componentProvider, locale, scoreFormat);
return createObjectivePacket(packetType, componentProvider.fromAdventure(value, locale), renderType, numberFormat);
Object numberFormat = ScoreFormatConverter.convert(locale, scoreFormat);
return createObjectivePacket(packetType, ModernComponentProvider.fromAdventure(value, locale), renderType, numberFormat);
}
);
}
@@ -1,22 +1,16 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.team;
import net.kyori.adventure.text.Component;
import net.megavex.scoreboardlibrary.implementation.commons.LegacyFormatUtil;
import net.kyori.adventure.text.format.NamedTextColor;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.ImmutableTeamProperties;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.ComponentProvider;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors;
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 net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamsPacketAdapter;
import net.minecraft.ChatFormatting;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket;
import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket.Parameters;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability;
import java.util.Collection;
@@ -25,37 +19,18 @@ import java.util.Locale;
import java.util.Optional;
public abstract class AbstractTeamsPacketAdapterImpl implements TeamsPacketAdapter {
protected final PacketSender<Packet<?>> sender;
protected final ComponentProvider componentProvider;
protected final PacketAdapterProviderImpl provider;
protected final String teamName;
private ClientboundSetPlayerTeamPacket removePacket;
private Object removePacket;
public AbstractTeamsPacketAdapterImpl(@NotNull PacketSender<Packet<?>> sender, @NotNull ComponentProvider componentProvider, @NotNull String teamName) {
this.sender = sender;
this.componentProvider = componentProvider;
public AbstractTeamsPacketAdapterImpl(@NotNull PacketAdapterProviderImpl provider, @NotNull String teamName) {
this.provider = provider;
this.teamName = teamName;
}
public static ClientboundSetPlayerTeamPacket createTeamsPacket(
int method,
@NotNull String name,
@Nullable Parameters parameters,
@Nullable Collection<String> entries
) {
return PacketAccessors.TEAM_PACKET_CONSTRUCTOR.invoke(
name,
method,
Optional.ofNullable(parameters),
entries == null ? Collections.emptyList() : entries
);
}
@Override
public void removeTeam(@NotNull Iterable<Player> players) {
if (removePacket == null) {
removePacket = createTeamsPacket(TeamConstants.MODE_REMOVE, teamName, null, null);
}
sender.sendPacket(players, removePacket);
public @NotNull TeamDisplayPacketAdapter createLegacyTeamDisplayAdapter(@NotNull ImmutableTeamProperties<String> properties) {
return new LegacyTeamDisplayPacketAdapter(provider, teamName, properties);
}
public abstract class TeamDisplayPacketAdapterImpl implements TeamDisplayPacketAdapter {
@@ -66,22 +41,62 @@ public abstract class AbstractTeamsPacketAdapterImpl implements TeamsPacketAdapt
}
@Override
public void sendEntries(@NotNull EntriesPacketType packetType, @NotNull Collection<Player> players, @NotNull Collection<String> entries) {
sender.sendPacket(players, createTeamsPacket(TeamConstants.mode(packetType), teamName, null, entries));
public void removeTeam(@NotNull Iterable<Player> 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);
}
protected void fillParameters(@NotNull Parameters parameters, @UnknownNullability Locale locale) {
String nameTagVisibilityKey = properties.nameTagVisibility().key();
PacketAccessors.NAME_TAG_VISIBILITY_FIELD.set(parameters, nameTagVisibilityKey);
@Override
public void sendEntries(@NotNull EntriesPacketType packetType, @NotNull Collection<Player> players, @NotNull Collection<String> entries) {
if (PacketAccessors.IS_1_17_OR_ABOVE) {
Object packet = PacketAccessors.TEAM_PACKET_CONSTRUCTOR.invoke(
teamName,
TeamConstants.mode(packetType),
Optional.empty(),
entries
);
provider.packetSender().sendPacket(players, packet);
} else {
assert PacketAccessors.TEAM_NAME_FIELD != null;
assert PacketAccessors.TEAM_MODE_FIELD != null;
assert PacketAccessors.TEAM_ENTRIES_FIELD != null;
String collisionRuleKey = properties.collisionRule().key();
PacketAccessors.COLLISION_RULE_FIELD.set(parameters, collisionRuleKey);
Object packet = PacketAccessors.TEAM_PACKET_CONSTRUCTOR.invoke();
PacketAccessors.TEAM_NAME_FIELD.set(packet, teamName);
PacketAccessors.TEAM_MODE_FIELD.set(packet, TeamConstants.mode(packetType));
PacketAccessors.TEAM_ENTRIES_FIELD.set(packet, entries);
provider.packetSender().sendPacket(players, packet);
}
}
char legacyChar = LegacyFormatUtil.getChar(properties.playerColor());
PacketAccessors.COLOR_FIELD.set(parameters, ChatFormatting.getByCode(legacyChar));
protected void fillParameters(@NotNull Object parameters, @UnknownNullability Locale locale) {
if (PacketAccessors.IS_1_21_5_OR_ABOVE) {
PacketAccessors.NAME_TAG_VISIBILITY_FIELD.set(parameters, PacketAccessors.nameTagVisibility(properties.nameTagVisibility()));
PacketAccessors.COLLISION_RULE_FIELD.set(parameters, PacketAccessors.collisionRule(properties.collisionRule()));
} else {
PacketAccessors.NAME_TAG_VISIBILITY_FIELD.set(parameters, properties.nameTagVisibility().key());
PacketAccessors.COLLISION_RULE_FIELD.set(parameters, properties.collisionRule().key());
}
int options = properties.packOptions();
PacketAccessors.OPTIONS_FIELD.set(parameters, options);
Object color = PacketAccessors.NMS_CHAT_FORMATTING_MAP.get(properties.playerColor() != null ? properties.playerColor() : NamedTextColor.WHITE);
PacketAccessors.COLOR_FIELD.set(parameters, color);
PacketAccessors.OPTIONS_FIELD.set(parameters, properties.packOptions());
}
}
}
@@ -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<String> properties;
public LegacyTeamDisplayPacketAdapter(final PacketAdapterProviderImpl provider, final String teamName, final ImmutableTeamProperties<String> 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<Player> players) {
ViaAPI<Player> 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<Player> players, @NotNull Collection<String> entries) {
ViaAPI<Player> 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<Player> players) {
ViaAPI<Player> 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<Player> 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;
}
}
@@ -3,25 +3,22 @@ package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.team;
import com.google.common.collect.ImmutableList;
import net.kyori.adventure.text.Component;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.ImmutableTeamProperties;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.ComponentProvider;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.NativeAdventureUtil;
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 net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.UnknownNullability;
import java.util.Collection;
import java.util.Locale;
import java.util.Optional;
public class PaperTeamsPacketAdapterImpl extends AbstractTeamsPacketAdapterImpl {
public PaperTeamsPacketAdapterImpl(@NotNull PacketSender<Packet<?>> sender, @NotNull ComponentProvider componentProvider, @NotNull String teamName) {
super(sender, componentProvider, teamName);
public PaperTeamsPacketAdapterImpl(PacketAdapterProviderImpl provider, @NotNull String teamName) {
super(provider, teamName);
}
@Override
@@ -30,18 +27,24 @@ public class PaperTeamsPacketAdapterImpl extends AbstractTeamsPacketAdapterImpl
}
private class TeamDisplayPacketAdapterImpl extends AbstractTeamsPacketAdapterImpl.TeamDisplayPacketAdapterImpl {
private final ClientboundSetPlayerTeamPacket.Parameters parameters = PacketAccessors.PARAMETERS_CONSTRUCTOR.invoke();
private ClientboundSetPlayerTeamPacket createPacket = null;
private ClientboundSetPlayerTeamPacket updatePacket = null;
private Component displayName, prefix, suffix;
private final Object parameters;
private Object createPacket = null;
private Object updatePacket = null;
public TeamDisplayPacketAdapterImpl(@NotNull ImmutableTeamProperties<Component> properties) {
super(properties);
if (PacketAccessors.IS_1_17_OR_ABOVE) {
this.parameters = PacketAccessors.PARAMETERS_CONSTRUCTOR.invoke();
} else {
this.parameters = null;
}
}
@Override
public void updateTeamPackets() {
fillParameters(parameters, null);
if (parameters != null) {
fillParameters(parameters, null);
}
createPacket = null;
updatePacket = null;
}
@@ -50,41 +53,54 @@ public class PaperTeamsPacketAdapterImpl extends AbstractTeamsPacketAdapterImpl
public void sendProperties(@NotNull PropertiesPacketType packetType, @NotNull Collection<Player> players) {
if (createPacket == null || updatePacket == null) {
Collection<String> entries = ImmutableList.copyOf(properties.syncedEntries());
createPacket = createTeamsPacket(TeamConstants.MODE_CREATE, teamName, parameters, entries);
updatePacket = createTeamsPacket(TeamConstants.MODE_UPDATE, teamName, parameters, entries);
if (parameters != null) {
createPacket = PacketAccessors.TEAM_PACKET_CONSTRUCTOR.invoke(
teamName,
TeamConstants.MODE_CREATE,
Optional.of(parameters),
entries
);
updatePacket = PacketAccessors.TEAM_PACKET_CONSTRUCTOR.invoke(
teamName,
TeamConstants.MODE_UPDATE,
Optional.of(parameters),
entries
);
} else {
assert PacketAccessors.TEAM_NAME_FIELD != null;
assert PacketAccessors.TEAM_MODE_FIELD != null;
createPacket = PacketAccessors.TEAM_PACKET_CONSTRUCTOR.invoke();
PacketAccessors.TEAM_NAME_FIELD.set(createPacket, teamName);
PacketAccessors.TEAM_MODE_FIELD.set(createPacket, TeamConstants.MODE_CREATE);
PacketAccessors.TEAM_ENTRIES_FIELD.set(createPacket, entries);
fillParameters(createPacket, null);
updatePacket = PacketAccessors.TEAM_PACKET_CONSTRUCTOR.invoke();
PacketAccessors.TEAM_NAME_FIELD.set(updatePacket, teamName);
PacketAccessors.TEAM_MODE_FIELD.set(updatePacket, TeamConstants.MODE_UPDATE);
PacketAccessors.TEAM_ENTRIES_FIELD.set(updatePacket, entries);
fillParameters(updatePacket, null);
}
}
switch (packetType) {
case CREATE:
sender.sendPacket(players, createPacket);
provider.packetSender().sendPacket(players, createPacket);
break;
case UPDATE:
sender.sendPacket(players, updatePacket);
provider.packetSender().sendPacket(players, updatePacket);
break;
}
}
@Override
protected void fillParameters(ClientboundSetPlayerTeamPacket.@NotNull Parameters parameters, @UnknownNullability Locale locale) {
protected void fillParameters(@NotNull Object parameters, @UnknownNullability Locale locale) {
super.fillParameters(parameters, locale);
if (properties.displayName() != displayName) {
net.minecraft.network.chat.Component vanilla = NativeAdventureUtil.fromAdventureComponent(properties.displayName());
PacketAccessors.DISPLAY_NAME_FIELD.set(parameters, vanilla);
displayName = properties.displayName();
}
if (properties.prefix() != prefix) {
net.minecraft.network.chat.Component vanilla = NativeAdventureUtil.fromAdventureComponent(properties.prefix());
PacketAccessors.PREFIX_FIELD.set(parameters, vanilla);
prefix = properties.prefix();
}
if (properties.suffix() != suffix) {
net.minecraft.network.chat.Component vanilla = NativeAdventureUtil.fromAdventureComponent(properties.suffix());
PacketAccessors.SUFFIX_FIELD.set(parameters, vanilla);
suffix = properties.suffix();
}
PacketAccessors.DISPLAY_NAME_FIELD.set(parameters, PacketAccessors.fromAdventureComponent(properties.displayName()));
PacketAccessors.PREFIX_FIELD.set(parameters, PacketAccessors.fromAdventureComponent(properties.prefix()));
PacketAccessors.SUFFIX_FIELD.set(parameters, PacketAccessors.fromAdventureComponent(properties.suffix()));
}
}
}
@@ -3,25 +3,24 @@ package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.team;
import com.google.common.collect.ImmutableList;
import net.kyori.adventure.text.Component;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.ImmutableTeamProperties;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender;
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.team.TeamConstants;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamDisplayPacketAdapter;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.ComponentProvider;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.LocalePacketUtil;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.UnknownNullability;
import java.util.Collection;
import java.util.Locale;
import java.util.Optional;
public class SpigotTeamsPacketAdapter extends AbstractTeamsPacketAdapterImpl {
public SpigotTeamsPacketAdapter(@NotNull PacketSender<Packet<?>> sender, @NotNull ComponentProvider componentProvider, @NotNull String teamName) {
super(sender, componentProvider, teamName);
public SpigotTeamsPacketAdapter(PacketAdapterProviderImpl provider, @NotNull String teamName) {
super(provider, teamName);
}
@Override
@@ -38,23 +37,44 @@ public class SpigotTeamsPacketAdapter extends AbstractTeamsPacketAdapterImpl {
public void sendProperties(@NotNull PropertiesPacketType packetType, @NotNull Collection<Player> players) {
Collection<String> entries = ImmutableList.copyOf(properties.syncedEntries());
LocalePacketUtil.sendLocalePackets(
sender,
provider.packetSender(),
players,
locale -> {
ClientboundSetPlayerTeamPacket.@NotNull Parameters parameters = PacketAccessors.PARAMETERS_CONSTRUCTOR.invoke();
fillParameters(parameters, locale);
return createTeamsPacket(TeamConstants.mode(packetType), teamName, parameters, entries);
if (PacketAccessors.IS_1_17_OR_ABOVE) {
assert PacketAccessors.PARAMETERS_CONSTRUCTOR != null;
Object parameters = PacketAccessors.PARAMETERS_CONSTRUCTOR.invoke();
fillParameters(parameters, locale);
return PacketAccessors.TEAM_PACKET_CONSTRUCTOR.invoke(
teamName,
TeamConstants.mode(packetType),
Optional.of(parameters),
entries
);
} else {
assert PacketAccessors.TEAM_NAME_FIELD != null;
assert PacketAccessors.TEAM_MODE_FIELD != null;
assert PacketAccessors.TEAM_ENTRIES_FIELD != null;
Object packet = PacketAccessors.TEAM_PACKET_CONSTRUCTOR.invoke();
PacketAccessors.TEAM_NAME_FIELD.set(packet, teamName);
PacketAccessors.TEAM_MODE_FIELD.set(packet, TeamConstants.mode(packetType));
PacketAccessors.TEAM_ENTRIES_FIELD.set(packet, entries);
fillParameters(packet, locale);
return packet;
}
}
);
}
@Override
protected void fillParameters(ClientboundSetPlayerTeamPacket.@NotNull Parameters parameters, @UnknownNullability Locale locale) {
protected void fillParameters(@NotNull Object parameters, @UnknownNullability Locale locale) {
super.fillParameters(parameters, locale);
PacketAccessors.DISPLAY_NAME_FIELD.set(parameters, componentProvider.fromAdventure(properties.displayName(), locale));
PacketAccessors.PREFIX_FIELD.set(parameters, componentProvider.fromAdventure(properties.prefix(), locale));
PacketAccessors.SUFFIX_FIELD.set(parameters, componentProvider.fromAdventure(properties.suffix(), locale));
PacketAccessors.DISPLAY_NAME_FIELD.set(parameters, ModernComponentProvider.fromAdventure(properties.displayName(), locale));
PacketAccessors.PREFIX_FIELD.set(parameters, ModernComponentProvider.fromAdventure(properties.prefix(), locale));
PacketAccessors.SUFFIX_FIELD.set(parameters, ModernComponentProvider.fromAdventure(properties.suffix(), locale));
}
}
}
@@ -0,0 +1,117 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util;
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;
import org.jetbrains.annotations.Nullable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Locale;
import java.util.Optional;
import static net.kyori.adventure.text.serializer.gson.GsonComponentSerializer.gson;
public final class ModernComponentProvider {
public static final boolean IS_NATIVE_ADVENTURE;
private static final Object MINECRAFT_REGISTRY;
private static final Object CODEC;
private static final MethodHandle FROM_JSON_METHOD;
private ModernComponentProvider() {
}
static {
// Hide from relocation checkers
String notRelocatedPackage = "net.ky".concat("ori.adventure.text");
// The native adventure optimisations only work when the adventure library isn't relocated
IS_NATIVE_ADVENTURE = PacketAccessors.ADVENTURE_COMPONENT_CLASS != null && Component.class.getPackage().getName().equals(notRelocatedPackage);
if (PacketAccessors.IS_1_20_5_OR_ABOVE) {
Class<?> craftRegistry;
try {
craftRegistry = Class.forName(MinecraftClasses.craftBukkit("CraftRegistry"));
} catch (ClassNotFoundException e) {
throw new ExceptionInInitializerError(e);
}
try {
Method method = craftRegistry.getMethod("getMinecraftRegistry");
MINECRAFT_REGISTRY = method.invoke(null);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
throw new RuntimeException(e);
}
} else {
MINECRAFT_REGISTRY = null;
}
if (PacketAccessors.IS_1_21_6_OR_ABOVE) {
CODEC = ReflectUtil.findFieldUnchecked(PacketAccessors.COMPONENT_SERIALIZATION_CLASS, 0, PacketAccessors.CODEC_CLASS, true).get(null);
FROM_JSON_METHOD = null;
} else {
MethodHandle handle = null;
for (Method method : PacketAccessors.CHAT_SERIALIZER_CLASS.getMethods()) {
if (method.getParameterCount() >= 1 &&
method.getParameterCount() <= 2 &&
method.getParameterTypes()[0].getName().equals(RelocatedGson.SERVER_GSON_PKG + ".JsonElement")
) {
try {
handle = MethodHandles.lookup().unreflect(method);
break;
} catch (IllegalAccessException e) {
throw new ExceptionInInitializerError(e);
}
}
}
if (handle == null) {
throw new ExceptionInInitializerError("failed to find chat component fromJson method");
}
FROM_JSON_METHOD = handle;
CODEC = null;
}
}
public static @NotNull Object fromAdventure(@NotNull Component adventure, @Nullable Locale locale) {
if (IS_NATIVE_ADVENTURE) {
return PacketAccessors.fromAdventureComponent(adventure);
}
Component translated = adventure;
if (locale != null) {
translated = GlobalTranslator.render(adventure, locale);
}
Object json = RelocatedGson.convertToServerGson(gson().serializeToTree(translated));
if (FROM_JSON_METHOD == null) {
// 1.21.6+
Object result = PacketAccessors.CODEC_PARSE.invoke(CODEC, PacketAccessors.JSON_OPS, json);
//noinspection OptionalGetWithoutIsPresent
return ((Optional<?>) PacketAccessors.RESULT_UNWRAP_METHOD.invoke(result)).get();
}
Object[] args;
if (MINECRAFT_REGISTRY != null) {
args = new Object[]{json, MINECRAFT_REGISTRY};
} else {
args = new Object[]{json};
}
try {
return FROM_JSON_METHOD.invokeWithArguments(args);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}
@@ -0,0 +1,106 @@
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;
import org.bukkit.entity.Player;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
public final class ModernPacketSender implements PacketSender<Object> {
private static final MethodHandle GET_HANDLE;
private static final MethodHandle PLAYER_CONNECTION;
private static final MethodHandle SEND_PACKET;
private final ViaAPI<Player> via;
public ModernPacketSender(final ViaAPI<Player> via) {
this.via = via;
}
static {
Class<?> craftPlayer;
try {
craftPlayer = Class.forName(MinecraftClasses.craftBukkit("entity.CraftPlayer"));
} catch (ClassNotFoundException e) {
throw new ExceptionInInitializerError(e);
}
MethodHandles.Lookup lookup = MethodHandles.publicLookup();
MethodType methodType = MethodType.methodType(PacketAccessors.SERVER_PLAYER_CLASS);
try {
GET_HANDLE = lookup.findVirtual(craftPlayer, "getHandle", methodType);
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new ExceptionInInitializerError(e);
}
MethodHandle playerConnection = null;
for (Field field : PacketAccessors.SERVER_PLAYER_CLASS.getFields()) {
if (field.getType() == PacketAccessors.PLAYER_CONNECTION_CLASS) {
try {
playerConnection = lookup.unreflectGetter(field);
} catch (IllegalAccessException e) {
throw new ExceptionInInitializerError(e);
}
}
}
if (playerConnection == null) {
throw new ExceptionInInitializerError("failed to find player connection field");
}
PLAYER_CONNECTION = playerConnection;
MethodType sendMethodType = MethodType.methodType(void.class, PacketAccessors.PKT_CLASS);
MethodHandle sendPacket = null;
String[] sendPacketNames = {"a", "sendPacket", "b", "send"};
for (String name : sendPacketNames) {
try {
sendPacket = lookup.findVirtual(PacketAccessors.PLAYER_CONNECTION_CLASS, name, sendMethodType);
} catch (NoSuchMethodException ignored) {
} catch (IllegalAccessException e) {
throw new ExceptionInInitializerError(e);
}
}
if (sendPacket == null) {
throw new ExceptionInInitializerError(new RuntimeException("Couldn't find send packet method"));
}
SEND_PACKET = sendPacket;
}
@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);
} catch (Throwable e) {
throw new IllegalStateException("couldn't send packet to player", e);
}
}
}
@@ -1,14 +0,0 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util;
import io.papermc.paper.adventure.AdventureComponent;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.NotNull;
public final class NativeAdventureUtil {
private NativeAdventureUtil() {
}
public static @NotNull AdventureComponent fromAdventureComponent(@NotNull Component component) {
return new AdventureComponent(component);
}
}
@@ -1,85 +0,0 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util;
import net.minecraft.network.protocol.Packet;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
public final class PacketUtil {
private static final MethodHandle GET_HANDLE;
private static final MethodHandle PLAYER_CONNECTION;
private static final MethodHandle SEND_PACKET;
private PacketUtil() {
}
static {
String cbPackage = Bukkit.getServer().getClass().getPackage().getName();
Class<?> craftPlayer;
try {
craftPlayer = Class.forName(cbPackage + ".entity.CraftPlayer");
} catch (ClassNotFoundException e) {
throw new ExceptionInInitializerError(e);
}
MethodHandles.Lookup lookup = MethodHandles.publicLookup();
MethodType methodType = MethodType.methodType(ServerPlayer.class);
try {
GET_HANDLE = lookup.findVirtual(craftPlayer, "getHandle", methodType);
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new ExceptionInInitializerError(e);
}
MethodHandle playerConnection = null;
for (Field field : ServerPlayer.class.getFields()) {
if (field.getType() == ServerGamePacketListenerImpl.class) {
try {
playerConnection = lookup.unreflectGetter(field);
} catch (IllegalAccessException e) {
throw new ExceptionInInitializerError(e);
}
}
}
if (playerConnection == null) {
throw new ExceptionInInitializerError("failed to find player connection field");
}
PLAYER_CONNECTION = playerConnection;
MethodType sendMethodType = MethodType.methodType(void.class, Packet.class);
MethodHandle sendPacket = null;
String[] sendPacketNames = {"a", "sendPacket", "b"};
for (String name : sendPacketNames) {
try {
sendPacket = lookup.findVirtual(ServerGamePacketListenerImpl.class, name, sendMethodType);
} catch (NoSuchMethodException ignored) {
} catch (IllegalAccessException e) {
throw new ExceptionInInitializerError(e);
}
}
if (sendPacket == null) {
throw new ExceptionInInitializerError(new RuntimeException("Couldn't find send packet method"));
}
SEND_PACKET = sendPacket;
}
public static void sendPacket(Player player, Packet<?> packet) {
try {
ServerPlayer handle = (ServerPlayer) GET_HANDLE.invoke(player);
ServerGamePacketListenerImpl connection = (ServerGamePacketListenerImpl) PLAYER_CONNECTION.invoke(handle);
SEND_PACKET.invoke(connection, packet);
} catch (Throwable e) {
throw new IllegalStateException("couldn't send packet to player", e);
}
}
}
@@ -1,31 +0,0 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util;
import net.minecraft.core.RegistryAccess;
import org.bukkit.Bukkit;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public final class RegistryUtil {
public static final RegistryAccess MINECRAFT_REGISTRY;
static {
String cbPackage = Bukkit.getServer().getClass().getPackage().getName();
Class<?> craftRegistry;
try {
craftRegistry = Class.forName(cbPackage + ".CraftRegistry");
} catch (ClassNotFoundException e) {
throw new ExceptionInInitializerError(e);
}
try {
Method method = craftRegistry.getDeclaredMethod("getMinecraftRegistry");
MINECRAFT_REGISTRY = (RegistryAccess) method.invoke(null);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
throw new ExceptionInInitializerError(e);
}
}
private RegistryUtil() {
}
}
@@ -5,4 +5,5 @@ plugins {
dependencies {
api(project(":scoreboard-library-api"))
api(project(":scoreboard-library-commons"))
compileOnly(libs.spigotApi)
}
@@ -12,6 +12,8 @@ import org.jetbrains.annotations.Nullable;
import java.util.Collection;
public interface ObjectivePacketAdapter {
@NotNull String objectiveName();
void display(@NotNull Collection<Player> players, @NotNull ObjectiveDisplaySlot slot);
void sendProperties(
@@ -4,7 +4,8 @@ import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPack
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,
@@ -7,6 +7,8 @@ import org.jetbrains.annotations.NotNull;
import java.util.Collection;
public interface TeamDisplayPacketAdapter {
void removeTeam(@NotNull Iterable<Player> players);
default void updateTeamPackets() {
}
@@ -6,8 +6,6 @@ import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
public interface TeamsPacketAdapter {
void removeTeam(@NotNull Iterable<Player> players);
@NotNull TeamDisplayPacketAdapter createTeamDisplayAdapter(@NotNull ImmutableTeamProperties<Component> properties);
default @NotNull TeamDisplayPacketAdapter createLegacyTeamDisplayAdapter(@NotNull ImmutableTeamProperties<String> properties) {
@@ -1,18 +1,35 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability;
import java.lang.invoke.MethodHandle;
public class FieldAccessor<T, V> {
private final MethodHandle setter;
private final MethodHandle getter, setter;
public FieldAccessor(@NotNull MethodHandle setter) {
public FieldAccessor(@NotNull MethodHandle getter, @Nullable MethodHandle setter) {
this.getter = getter;
this.setter = setter;
}
public void set(@NotNull T instance, @UnknownNullability V value) {
public Object get(@Nullable T instance) {
try {
if (instance == null) {
return getter.invoke();
} else {
return getter.invoke(instance);
}
} catch (Throwable e) {
throw new IllegalStateException("couldn't set value of field", e);
}
}
public void set(T instance, @UnknownNullability V value) {
if (this.setter == null) {
throw new IllegalStateException("cannot set static final field");
}
try {
setter.invokeExact(instance, value);
} catch (Throwable e) {
@@ -0,0 +1,22 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.UnknownNullability;
import java.lang.invoke.MethodHandle;
public class MethodAccessor {
private final MethodHandle handle;
public MethodAccessor(@NotNull MethodHandle handle) {
this.handle = handle;
}
public Object invoke(@UnknownNullability Object... args) {
try {
return handle.invokeExact(args);
} catch (Throwable e) {
throw new IllegalStateException("couldn't set value of field", e);
}
}
}
@@ -8,7 +8,6 @@ import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Optional;
public class ReflectUtil {
// Inspired by
@@ -55,42 +54,52 @@ public class ReflectUtil {
private ReflectUtil() {
}
public static @NotNull Class<?> getClassOrThrow(String name) {
Class<?> c = getOptionalClass(name);
if (c == null){
throw new IllegalStateException("Class " + name + " not found");
public static @NotNull Class<?> getClassOrThrow(String... names) {
Class<?> c = getOptionalClass(names);
if (c == null) {
throw new IllegalStateException("Class with names either of " + String.join(", ", names) + " not found");
}
return c;
}
public static @Nullable Class<?> getOptionalClass(String name) {
try {
return Class.forName(name);
} catch (ClassNotFoundException e) {
return null;
public static @Nullable Class<?> getOptionalClass(String... names) {
for (String name : names) {
if (name == null) continue;
try {
return Class.forName(name);
} catch (ClassNotFoundException ignored) {
}
}
return null;
}
public static @NotNull Object getEnumInstance(@NotNull Class<?> clazz, @NotNull String name) {
//noinspection unchecked,rawtypes
return Enum.valueOf((Class<? extends Enum>) clazz, name);
}
public static <T, V> @NotNull FieldAccessor<T, V> findField(@NotNull Class<T> clazz, int index, @NotNull Class<V> valueClass) {
return findFieldUnchecked(clazz, index, valueClass);
public static @NotNull Object getEnumInstance(@NotNull Class<?> clazz, @NotNull String... names) {
for (String name : names) {
try {
//noinspection unchecked,rawtypes
return Enum.valueOf((Class<? extends Enum>) clazz, name);
} catch (IllegalArgumentException ignored) {
}
}
throw new IllegalStateException("Enum " + clazz.getName() + " instance with names either of " + String.join(",", names) + " not found");
}
public static <T, V> @NotNull FieldAccessor<T, V> findFieldUnchecked(@NotNull Class<?> clazz, int index, @NotNull Class<?> valueClass) {
return findFieldUnchecked(clazz, index, valueClass, false);
}
public static <T, V> @NotNull FieldAccessor<T, V> findFieldUnchecked(@NotNull Class<?> clazz, int index, @NotNull Class<?> valueClass, boolean isStatic) {
int i = 0;
for (Field field : clazz.getDeclaredFields()) {
if (Modifier.isStatic(field.getModifiers()) || field.getType() != valueClass) {
if (Modifier.isStatic(field.getModifiers()) != isStatic || field.getType() != valueClass) {
continue;
}
if (i == index) {
try {
MethodHandle setter = LOOKUP.unreflectSetter(field);
return new FieldAccessor<>(setter.asType(VIRTUAL_FIELD_SETTER));
MethodHandle getter = LOOKUP.unreflectGetter(field);
MethodHandle setter = !isStatic || !Modifier.isFinal(field.getModifiers()) ? LOOKUP.unreflectSetter(field).asType(VIRTUAL_FIELD_SETTER) : null;
return new FieldAccessor<>(getter, setter);
} catch (IllegalAccessException e) {
throw new RuntimeException("failed to unreflect field setter", e);
}
@@ -145,6 +154,35 @@ public class ReflectUtil {
};
}
public static @NotNull MethodAccessor findMethod(@NotNull Class<?> clazz, boolean isStatic, @NotNull MethodType methodType, @NotNull String... names) {
MethodAccessor accessor = findOptionalMethod(clazz, isStatic, methodType, names);
if (accessor == null) {
throw new RuntimeException("Method with the names either of " + String.join(",", names) + " for class " + clazz.getName() + " not found");
}
return accessor;
}
public static <T> @Nullable MethodAccessor findOptionalMethod(@NotNull Class<T> clazz, boolean isStatic, @NotNull MethodType methodType, @NotNull String... names) {
if (names.length == 0) {
throw new IllegalArgumentException("no method names passed");
}
for (String name : names) {
MethodHandle handle;
try {
if (isStatic) {
handle = LOOKUP.findStatic(clazz, name, methodType);
} else {
handle = LOOKUP.findVirtual(clazz, name, methodType);
}
return new MethodAccessor(convertToGeneric(handle));
} catch (NoSuchMethodException ignored) {
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
return null;
}
private static @NotNull MethodHandle convertToGeneric(@NotNull MethodHandle handle) {
MethodHandle target = handle.asFixedArity();
MethodType methodType = MethodType.genericMethodType(0, true);
+1 -8
View File
@@ -2,11 +2,4 @@ plugins {
id("net.megavex.scoreboardlibrary.base-conventions")
}
repositories {
maven("https://repo.codemc.io/repository/maven-releases/")
}
dependencies {
compileOnly(project(":scoreboard-library-packet-adapter-base"))
compileOnly(libs.packetEvents)
}
// packetevents adapter is gone, this module is kept for compatibility
@@ -1,157 +0,0 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.packetevents;
import com.github.retrooper.packetevents.PacketEventsAPI;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerDisplayScoreboard;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerResetScore;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerScoreboardObjective;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerUpdateScore;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.translation.GlobalTranslator;
import net.megavex.scoreboardlibrary.api.objective.ObjectiveDisplaySlot;
import net.megavex.scoreboardlibrary.api.objective.ObjectiveRenderType;
import net.megavex.scoreboardlibrary.api.objective.ScoreFormat;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectiveConstants;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectivePacketAdapter;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.LocalePacketUtil;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Locale;
import java.util.Optional;
public class ObjectivePacketAdapterImpl implements ObjectivePacketAdapter {
private final PacketSender<PacketWrapper<?>> provider;
private final PacketEventsAPI<?> packetEvents;
private final String objectiveName;
private WrapperPlayServerScoreboardObjective removePacket;
public ObjectivePacketAdapterImpl(@NotNull PacketSender<PacketWrapper<?>> provider, @NotNull PacketEventsAPI<?> packetEvents, @NotNull String objectiveName) {
this.provider = provider;
this.packetEvents = packetEvents;
this.objectiveName = objectiveName;
}
@Override
public void display(@NotNull Collection<Player> players, @NotNull ObjectiveDisplaySlot slot) {
WrapperPlayServerDisplayScoreboard packet = new WrapperPlayServerDisplayScoreboard(ObjectiveConstants.displaySlotIndex(slot), objectiveName);
provider.sendPacket(players, packet);
}
@Override
public void sendProperties(
@NotNull Collection<Player> players,
@NotNull PropertiesPacketType packetType,
@NotNull Component value,
@NotNull ObjectiveRenderType renderType,
@Nullable ScoreFormat scoreFormat
) {
LocalePacketUtil.sendLocalePackets(
provider,
players,
locale -> createObjectivePacket(locale, packetType, value, renderType, scoreFormat)
);
}
@Override
public void remove(@NotNull Collection<Player> players) {
if (removePacket == null) {
removePacket = new WrapperPlayServerScoreboardObjective(
objectiveName,
WrapperPlayServerScoreboardObjective.ObjectiveMode.REMOVE,
null,
null
);
}
provider.sendPacket(players, removePacket);
}
@Override
public void sendScore(
@NotNull Collection<Player> players,
@NotNull String entry,
int value,
@Nullable Component display,
@Nullable ScoreFormat scoreFormat
) {
LocalePacketUtil.sendLocalePackets(
provider,
players,
locale -> new WrapperPlayServerUpdateScore(
entry,
WrapperPlayServerUpdateScore.Action.CREATE_OR_UPDATE_ITEM,
objectiveName,
value,
display != null ? GlobalTranslator.render(display, locale) : null,
ScoreFormatConverter.convert(locale, scoreFormat)
)
);
}
@Override
public void removeScore(@NotNull Collection<Player> players, @NotNull String entry) {
WrapperPlayServerUpdateScore oldPacket = new WrapperPlayServerUpdateScore(
entry,
WrapperPlayServerUpdateScore.Action.REMOVE_ITEM,
objectiveName,
Optional.empty()
);
WrapperPlayServerResetScore newPacket = new WrapperPlayServerResetScore(
entry,
objectiveName
);
if (packetEvents.getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_20_3)) {
provider.sendPacket(players, newPacket);
} else {
provider.sendPacket(players, oldPacket);
}
}
private WrapperPlayServerScoreboardObjective createObjectivePacket(
@NotNull Locale locale,
@NotNull PropertiesPacketType packetType,
@NotNull Component value,
@NotNull ObjectiveRenderType renderType,
@Nullable ScoreFormat format
) {
WrapperPlayServerScoreboardObjective.ObjectiveMode peMode;
switch (packetType) {
case CREATE:
peMode = WrapperPlayServerScoreboardObjective.ObjectiveMode.CREATE;
break;
case UPDATE:
peMode = WrapperPlayServerScoreboardObjective.ObjectiveMode.UPDATE;
break;
default:
throw new IllegalStateException();
}
WrapperPlayServerScoreboardObjective.RenderType peRenderType;
switch (renderType) {
case INTEGER:
peRenderType = WrapperPlayServerScoreboardObjective.RenderType.INTEGER;
break;
case HEARTS:
peRenderType = WrapperPlayServerScoreboardObjective.RenderType.HEARTS;
break;
default:
throw new IllegalStateException();
}
return new WrapperPlayServerScoreboardObjective(
objectiveName,
peMode,
GlobalTranslator.render(value, locale),
peRenderType,
ScoreFormatConverter.convert(locale, format)
);
}
}
@@ -1,56 +0,0 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.packetevents;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.PacketEventsAPI;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import net.megavex.scoreboardlibrary.implementation.commons.LineRenderingStrategy;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketAdapterProvider;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectivePacketAdapter;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.packetevents.team.TeamsPacketAdapterImpl;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamsPacketAdapter;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@SuppressWarnings("unused")
public class PacketAdapterProviderImpl implements PacketAdapterProvider {
private final PacketEventsAPI<?> packetEvents;
private final PacketEventsSender packetSender;
public PacketAdapterProviderImpl() {
this.packetEvents = PacketEvents.getAPI();
if (this.packetEvents == null) {
throw new IllegalStateException("PacketEvents exists in classpath but isn't loaded");
}
this.packetSender = new PacketEventsSender(this.packetEvents);
}
@Override
public @NotNull TeamsPacketAdapter createTeamPacketAdapter(@NotNull String teamName) {
return new TeamsPacketAdapterImpl(packetSender, teamName);
}
@Override
public @NotNull ObjectivePacketAdapter createObjectiveAdapter(@NotNull String objectiveName) {
return new ObjectivePacketAdapterImpl(packetSender, packetEvents, objectiveName);
}
@Override
public @NotNull LineRenderingStrategy lineRenderingStrategy(@NotNull Player player) {
ServerVersion serverVer = packetEvents.getServerManager().getVersion();
return serverVer.isNewerThanOrEquals(ServerVersion.V_1_13) ? LineRenderingStrategy.MODERN : LineRenderingStrategy.LEGACY;
// Waiting for packetevents to fix https://github.com/retrooper/packetevents/issues/1024
/*
if (serverVer.isOlderThan(ServerVersion.V_1_13)) {
return LineRenderingStrategy.LEGACY;
}
if (Bukkit.getServer().getPluginManager().isPluginEnabled("ProtocolSupport")) {
return LineRenderingStrategy.MODERN;
}
ClientVersion clientVer = packetEvents.getPlayerManager().getClientVersion(player);
return clientVer.isNewerThanOrEquals(ClientVersion.V_1_13) ? LineRenderingStrategy.MODERN : LineRenderingStrategy.LEGACY;
*/
}
}
@@ -1,65 +0,0 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.packetevents;
import com.github.retrooper.packetevents.PacketEventsAPI;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper;
import com.github.retrooper.packetevents.netty.channel.ChannelHelper;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.User;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
public class PacketEventsSender implements PacketSender<PacketWrapper<?>> {
private final PacketEventsAPI<?> packetEvents;
public PacketEventsSender(@NotNull PacketEventsAPI<?> packetEvents) {
this.packetEvents = packetEvents;
}
//@SuppressWarnings("UnstableApiUsage, deprecation")
@Override
public void sendPacket(Player player, PacketWrapper<?> packet) {
packetEvents.getPlayerManager().sendPacket(player, packet);
/*
ServerVersion serverVer = packetEvents.getServerManager().getVersion();
ClientVersion clientVer = packetEvents.getPlayerManager().getClientVersion(player);
if (serverVer.isOlderThan(ServerVersion.V_1_13) || clientVer.isNewerThanOrEquals(ClientVersion.V_1_13)) {
packetEvents.getPlayerManager().sendPacket(player, packet);
return;
}
// This is a hack to send packets directly skipping plugins such as ViaVersion and ViaRewind
// Based on https://discord.com/channels/721686193061888071/755472595096174873/1193239879560212520
// (PacketEvents Discord)
User user = packetEvents.getPlayerManager().getUser(player);
if (user == null) {
return;
}
Object channel = user.getChannel();
if (!ChannelHelper.isOpen(channel)) {
return;
}
String viaEncoder = "via-encoder";
if (ChannelHelper.getPipelineHandler(channel, viaEncoder) == null) {
packetEvents.getPlayerManager().sendPacket(player, packet);
return;
}
packet.buffer = ChannelHelper.pooledByteBuf(channel);
int id = Objects.requireNonNull(packet.getPacketTypeData().getPacketType()).getId(user.getClientVersion());
ByteBufHelper.writeVarInt(packet.buffer, id);
packet.setServerVersion(clientVer.toServerVersion());
packet.write();
ChannelHelper.writeAndFlushInContext(channel, viaEncoder, packet.buffer);
*/
}
}
@@ -1,31 +0,0 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.packetevents;
import net.kyori.adventure.translation.GlobalTranslator;
import net.megavex.scoreboardlibrary.api.objective.ScoreFormat;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Locale;
import static com.github.retrooper.packetevents.protocol.score.ScoreFormat.*;
public final class ScoreFormatConverter {
private ScoreFormatConverter() {
}
public static @Nullable com.github.retrooper.packetevents.protocol.score.ScoreFormat convert(@NotNull Locale locale, @Nullable ScoreFormat format) {
if (format == null) {
return null;
}
if (format == ScoreFormat.blank()) {
return blankScore();
} else if (format instanceof ScoreFormat.Styled) {
return styledScore(((ScoreFormat.Styled) format).style());
} else if (format instanceof ScoreFormat.Fixed) {
return fixedScore(GlobalTranslator.render(((ScoreFormat.Fixed) format).content(), locale));
} else {
throw new IllegalArgumentException("Invalid score format: " + format);
}
}
}
Loaded 100 of 105 files, more files were not shown because too many files have changed in this diff. Show more