Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9fad798af8 | ||
|
|
f3cbbfb2b5 | ||
|
|
4d1d5ca25e | ||
|
|
cd53aaf8e3 | ||
|
|
36a0ef0dfd | ||
|
|
232ed9be84 | ||
|
|
7e3d7d7918 | ||
|
|
f277ee4de2 | ||
|
|
1613c8d2c5 | ||
|
|
91d74ddf4e | ||
|
|
45544a71c7 | ||
|
|
a454dff208 | ||
|
|
6075333d7b | ||
|
|
9d16e5fe32 | ||
|
|
b8babc7542 | ||
|
|
80cb76e98b | ||
|
|
1e6c8a033a | ||
|
|
cd09080ced | ||
|
|
bf01337fcf | ||
|
|
ef3998bd3b | ||
|
|
38d006cf6e | ||
|
|
d19f997846 | ||
|
|
92d3281fad | ||
|
|
538edffa1a | ||
|
|
1c82d3025f | ||
|
|
d7558778bb | ||
|
|
b16dd7cbac | ||
|
|
ce1533ded5 | ||
|
|
6158c71cc9 | ||
|
|
d9fcd8013c | ||
|
|
a4b9c13bef | ||
|
|
b5bd7a543d | ||
|
|
33144908eb | ||
|
|
2c84532f79 | ||
|
|
eeff691c95 | ||
|
|
97e027c2d3 | ||
|
|
59dbad8311 | ||
|
|
c15e8c2b9d | ||
|
|
8066116d7e | ||
|
|
96677233af | ||
|
|
c57aa1ee22 | ||
|
|
8de8502b63 | ||
|
|
206bd2e56a | ||
|
|
54135c73b8 | ||
|
|
f23d2e2804 | ||
|
|
904b7e5475 | ||
|
|
7b9d274683 | ||
|
|
9e6e1d506d | ||
|
|
96a18d1632 | ||
|
|
2092517215 | ||
|
|
dd735360ce | ||
|
|
bb74f217f2 | ||
|
|
b0a69da47c | ||
|
|
d845cca062 | ||
|
|
d71e029052 | ||
|
|
4eb7ed3cd8 | ||
|
|
f8fb6c2a8c | ||
|
|
a3e0c6795c | ||
|
|
9e37c3e627 | ||
|
|
8fdf24e07a | ||
|
|
1227b28189 | ||
|
|
6fe6200cda | ||
|
|
fb4863a9e3 | ||
|
|
a298842bce | ||
|
|
16314a7d14 | ||
|
|
c1286520d3 | ||
|
|
bc8e3c2d2e | ||
|
|
032349b190 | ||
|
|
d0d5eaa706 | ||
|
|
ac334838ee | ||
|
|
162de14e0e | ||
|
|
5f5b3efc5f | ||
|
|
41c0a4b3c9 | ||
|
|
a276708162 | ||
|
|
793e78284f | ||
|
|
38de97ba01 | ||
|
|
a535f37214 | ||
|
|
48351b6c81 | ||
|
|
7dcf1e30eb | ||
|
|
2ca874346d | ||
|
|
cb08e5d3d9 | ||
|
|
eecde934a8 | ||
|
|
2a070cdc3d | ||
|
|
589ab095b2 | ||
|
|
41fb8aa4aa | ||
|
|
da69391930 | ||
|
|
47ef9a8143 | ||
|
|
ccf9aad96d | ||
|
|
facb3a4d1c | ||
|
|
f41c1ce0b1 | ||
|
|
e72d442f9b | ||
|
|
9ef53fc36c | ||
|
|
774ab670c9 | ||
|
|
30641a8c15 | ||
|
|
01e1374db6 | ||
|
|
db58aa35c5 | ||
|
|
35a0565cd1 | ||
|
|
6fab408824 | ||
|
|
b0d6903262 | ||
|
|
acd64b884e | ||
|
|
70eb07c25d | ||
|
|
50d567a4af | ||
|
|
97cd00b598 | ||
|
|
bf0529a11e | ||
|
|
1b752a0694 | ||
|
|
5f093db220 | ||
|
|
651f5b24b7 | ||
|
|
e35fb3503c | ||
|
|
14e16b95a0 | ||
|
|
b30ea7dc44 | ||
|
|
8e09c6d1c1 | ||
|
|
8aa61448e4 | ||
|
|
3762419bc5 | ||
|
|
53bea167d7 | ||
|
|
5eaa7f3c07 | ||
|
|
699cbacf6e | ||
|
|
e6fdd5e513 | ||
|
|
a95c879d10 | ||
|
|
4f6d1dc5b1 | ||
|
|
5c0709a8c4 | ||
|
|
d5c61dc156 | ||
|
|
5466140dc4 | ||
|
|
cc13241968 | ||
|
|
16592c2815 | ||
|
|
8c09c0e97c | ||
|
|
0bbc487f88 | ||
|
|
0cbd54f8f1 | ||
|
|
0551a8f978 | ||
|
|
dd65c812d2 | ||
|
|
a88394223b | ||
|
|
0e579e603a | ||
|
|
71aaa7ac50 | ||
|
|
8521d1deec | ||
|
|
9c1a121e09 | ||
|
|
7f3651d4bf | ||
|
|
2e06fc1d34 | ||
|
|
cb391dd676 | ||
|
|
a36a8c3fd2 | ||
|
|
4f3cf55c01 | ||
|
|
422a36a284 | ||
|
|
a2e5df14e6 | ||
|
|
521bc47dd0 | ||
|
|
09dc3a653d | ||
|
|
b5181d0d81 | ||
|
|
f01f4fd831 | ||
|
|
778b479b91 | ||
|
|
7a1c47045b | ||
|
|
e2c8ff5eb8 | ||
|
|
af426bdfbc | ||
|
|
641223e1f5 | ||
|
|
cf8fa59c86 | ||
|
|
96595d0a41 | ||
|
|
ba6bff7ed1 | ||
|
|
0f96547b37 | ||
|
|
5d02fd625b | ||
|
|
fed9f4c64b | ||
|
|
0c3f942757 | ||
|
|
c1c9d7258b | ||
|
|
93cd55a626 | ||
|
|
0d979d6922 | ||
|
|
cf6b51be40 | ||
|
|
c1199432eb | ||
|
|
bccc777541 | ||
|
|
d14ca9470a | ||
|
|
1889b84373 | ||
|
|
bf629bd7de | ||
|
|
690905774e | ||
|
|
40a35272ff | ||
|
|
f9fdc13443 | ||
|
|
bdce2423b1 | ||
|
|
4955c839a8 | ||
|
|
9b198da189 | ||
|
|
32409d589c | ||
|
|
e016b43822 | ||
|
|
17137a88c4 | ||
|
|
4369acd756 | ||
|
|
4fa40ed032 | ||
|
|
3bf91896b0 | ||
|
|
b71d555391 | ||
|
|
25f236344f | ||
|
|
01d8ef9b29 | ||
|
|
98917376d0 | ||
|
|
cd0607a72c | ||
|
|
8ba9ef40da | ||
|
|
3be7b09631 | ||
|
|
079e867325 | ||
|
|
c9a3c6b3f5 | ||
|
|
f8f542e739 | ||
|
|
fca17dbda4 | ||
|
|
9a77454a9e | ||
|
|
2d30af76f3 | ||
|
|
59198fb117 | ||
|
|
022968489c | ||
|
|
afd64f4fd2 | ||
|
|
d4c553f93f | ||
|
|
3f37335bac | ||
|
|
deaf231965 | ||
|
|
d15ac8c2e8 | ||
|
|
8408a3e279 | ||
|
|
c2ce803f30 | ||
|
|
5f89a47798 | ||
|
|
4d23bc5cfd | ||
|
|
d26eab2110 | ||
|
|
c0a6ec0ec1 |
No files matched your search
+16
-16
@@ -1,24 +1,24 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
build:
|
||||
ci:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: gradle/wrapper-validation-action@v2
|
||||
- name: Set up JDK 8
|
||||
uses: actions/setup-java@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: cachix/install-nix-action@v31
|
||||
- name: Cache Gradle
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
java-version: 17
|
||||
distribution: 'temurin'
|
||||
- name: Build the project
|
||||
uses: gradle/gradle-build-action@v3.1.0
|
||||
with:
|
||||
arguments: build
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
- name: ./gradlew build
|
||||
run: nix develop .#jdk25 -i -c sh -c "./gradlew build"
|
||||
+9
-1
@@ -2,4 +2,12 @@
|
||||
*.iml
|
||||
|
||||
.gradle
|
||||
build/
|
||||
build/
|
||||
|
||||
.classpath
|
||||
.project
|
||||
.settings/
|
||||
bin/
|
||||
|
||||
test-env/servers
|
||||
test-env/cache
|
||||
+25
-81
@@ -1,116 +1,60 @@
|
||||
# Installation
|
||||
|
||||
Latest version: `2.1.3`
|
||||
Latest version: `2.7.0`
|
||||
|
||||
## Gradle
|
||||
|
||||
Make sure you have the Jitpack repository:
|
||||
|
||||
```kotlin
|
||||
repositories {
|
||||
maven("https://jitpack.io/")
|
||||
mavenCentral()
|
||||
}
|
||||
```
|
||||
|
||||
Then add the dependencies:
|
||||
|
||||
```kotlin
|
||||
dependencies {
|
||||
val scoreboardLibraryVersion = "{VERSION HERE}"
|
||||
implementation("com.github.megavexnetwork.scoreboard-library:scoreboard-library-api:$scoreboardLibraryVersion")
|
||||
runtimeOnly("com.github.megavexnetwork.scoreboard-library:scoreboard-library-implementation:$scoreboardLibraryVersion")
|
||||
implementation("com.github.megavexnetwork.scoreboard-library:scoreboard-library-extra-kotlin:$scoreboardLibraryVersion") // If using Kotlin
|
||||
implementation("net.megavex:scoreboard-library-api:2.7.0")
|
||||
runtimeOnly("net.megavex:scoreboard-library-implementation:2.7.0")
|
||||
|
||||
// Add packet adapter implementations you want:
|
||||
runtimeOnly("com.github.megavexnetwork.scoreboard-library:scoreboard-library-modern:$scoreboardLibraryVersion") // 1.17+
|
||||
runtimeOnly("com.github.megavexnetwork.scoreboard-library:scoreboard-library-packetevents:$scoreboardLibraryVersion") // 1.8+
|
||||
runtimeOnly("com.github.megavexnetwork.scoreboard-library:scoreboard-library-v1_8_R3:$scoreboardLibraryVersion") // 1.8
|
||||
|
||||
// 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 using the 1.8 version implementation, add Adventure as well:
|
||||
implementation("net.kyori:adventure-platform-bukkit:4.0.1")
|
||||
// If targeting a server version without native Adventure support, add it as well:
|
||||
implementation("net.kyori:adventure-platform-bukkit:4.4.1")
|
||||
}
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
Make sure you have the Jitpack repository:
|
||||
```xml
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>jitpack.io</id>
|
||||
<url>https://jitpack.io/</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
```
|
||||
|
||||
Then add the dependencies:
|
||||
```xml
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.github.megavexnetwork.scoreboard-library</groupId>
|
||||
<groupId>net.megavex</groupId>
|
||||
<artifactId>scoreboard-library-api</artifactId>
|
||||
<version>{VERSION HERE}</version>
|
||||
<version>2.7.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.megavexnetwork.scoreboard-library</groupId>
|
||||
<groupId>net.megavex</groupId>
|
||||
<artifactId>scoreboard-library-implementation</artifactId>
|
||||
<version>{VERSION HERE}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<!-- If using Kotlin: -->
|
||||
<dependency>
|
||||
<groupId>com.github.megavexnetwork.scoreboard-library</groupId>
|
||||
<artifactId>scoreboard-library-extra-kotlin</artifactId>
|
||||
<version>{VERSION HERE}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Add packet adapter implementations you want: -->
|
||||
<dependency>
|
||||
<groupId>com.github.megavexnetwork.scoreboard-library</groupId>
|
||||
<artifactId>scoreboard-library-modern</artifactId>
|
||||
<version>{VERSION HERE}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.megavexnetwork.scoreboard-library</groupId>
|
||||
<artifactId>scoreboard-library-packetevents</artifactId>
|
||||
<version>{VERSION HERE}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.megavexnetwork.scoreboard-library</groupId>
|
||||
<artifactId>scoreboard-library-v1_8_R3</artifactId>
|
||||
<version>{VERSION HERE}</version>
|
||||
<version>2.7.0</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 using the 1.8 version implementation, add Adventure as well: -->
|
||||
<!-- If targeting a server version without native Adventure support, add it as well: -->
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-platform-bukkit</artifactId>
|
||||
<version>4.0.1</version>
|
||||
<version>4.4.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
||||
|
||||
You will need to shade these dependencies and relocate them with something
|
||||
like [maven-shade-plugin](https://maven.apache.org/plugins/maven-shade-plugin/).
|
||||
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"]
|
||||
```
|
||||
@@ -8,27 +8,24 @@ Join the [Discord](https://discord.gg/v7nmTDTW8W) or create an issue for support
|
||||
|
||||
## Features
|
||||
|
||||
- Sidebars. Up to 42 characters (depends on the formatting) for 1.12.2 and below, no limit for newer versions
|
||||
- Sidebars. Up to 42 characters (depends on the formatting) for 1.12.2-, no limit for newer versions
|
||||
- Teams. Supports showing different properties (display name, prefix, entries etc.) of the same team to different
|
||||
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.20.4. Can take advantage of [Paper](https://papermc.io)'s native adventure support to be more efficient.
|
||||
- **PacketEvents.** Supports 1.8+. Requires [PacketEvents 2.0](https://github.com/retrooper/packetevents/tree/2.0) to be shaded or installed as a plugin. Can be less stable than ProtocolLib.
|
||||
- **1.8.8.**
|
||||
|
||||
> [!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 26.1 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:
|
||||
@@ -69,8 +66,7 @@ sidebar.line(3, Component.text("epicserver.net"));
|
||||
|
||||
sidebar.addPlayer(player); // Add the player to the sidebar
|
||||
|
||||
// After you've finished using the Sidebar, make sure to close it to prevent a memory leak:
|
||||
sidebar.close();
|
||||
// Don't forget to call sidebar.close() once you don't need it anymore!
|
||||
```
|
||||
|
||||
### Component sidebars
|
||||
@@ -265,8 +261,7 @@ newTeamDisplay.displayName(Component.text("Other Team Display Name"));
|
||||
// Change the TeamDisplay a player sees like this:
|
||||
team.display(player, newTeamDisplay);
|
||||
|
||||
// After you've finished using the TeamManager, make sure to close it to prevent a memory leak:
|
||||
teamManager.close();
|
||||
// Don't forget to call teamManager.close() once you don't need it anymore!
|
||||
```
|
||||
|
||||
### ObjectiveManager
|
||||
@@ -280,6 +275,5 @@ objectiveManager.display(ObjectiveDisplaySlot.belowName(), objective);
|
||||
|
||||
objectiveManager.addPlayer(player); // Make a player see the objectives
|
||||
|
||||
// After you've finished using the ObjectiveManager, make sure to close it to prevent a memory leak:
|
||||
objectiveManager.close();
|
||||
// Don't forget to call objectiveManager.close() once you don't need it anymore!
|
||||
```
|
||||
@@ -1,3 +1,8 @@
|
||||
plugins {
|
||||
id("net.megavex.scoreboardlibrary.publish-conventions")
|
||||
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;
|
||||
}
|
||||
|
||||
+3
@@ -24,6 +24,9 @@ public interface ObjectiveDisplaySlot {
|
||||
return BelowName.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since Minecraft 1.8
|
||||
*/
|
||||
static @NotNull TeamSidebar teamSidebar(@NotNull NamedTextColor teamColor) {
|
||||
Preconditions.checkNotNull(teamColor);
|
||||
return new TeamSidebar(teamColor);
|
||||
|
||||
@@ -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.
|
||||
|
||||
+12
@@ -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,
|
||||
@@ -43,7 +48,7 @@ public interface Sidebar {
|
||||
// Lines
|
||||
|
||||
/**
|
||||
* Gets a line's value.
|
||||
* Gets a line's value, or null if line isn't set.
|
||||
*
|
||||
* @param line line index
|
||||
* @return value of line, or null if unset
|
||||
@@ -52,6 +57,7 @@ public interface Sidebar {
|
||||
|
||||
/**
|
||||
* Sets a line's value.
|
||||
* A null value hides the line.
|
||||
*
|
||||
* @param index line index
|
||||
* @param value new value, or null to hide
|
||||
@@ -62,6 +68,7 @@ public interface Sidebar {
|
||||
|
||||
/**
|
||||
* Sets a line's value with a custom score format.
|
||||
* A null value hides the line.
|
||||
* Note that custom score formats are only supported in Minecraft 1.20.3+.
|
||||
*
|
||||
* @param index line index
|
||||
@@ -83,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
|
||||
|
||||
/**
|
||||
@@ -97,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
|
||||
|
||||
/**
|
||||
|
||||
+7
-1
@@ -32,6 +32,12 @@ public final class ComponentSidebarLayout {
|
||||
return linesComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies the title and line components to a sidebar.
|
||||
* Call this every time you want to update the sidebar.
|
||||
*
|
||||
* @param sidebar sidebar to apply title and line components to
|
||||
*/
|
||||
public void apply(@NotNull Sidebar sidebar) {
|
||||
Preconditions.checkNotNull(sidebar);
|
||||
|
||||
@@ -72,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);
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -7,12 +7,12 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Something lines can be drawn to. This is either the lines of a sidebar or the sidebar title.
|
||||
* Something lines can be drawn to line by line. This is either the lines of a sidebar or its title.
|
||||
*/
|
||||
@ApiStatus.NonExtendable
|
||||
public interface LineDrawable {
|
||||
/**
|
||||
* Draws a line, or does nothing if it has reached the limit.
|
||||
* Draws the next line, or does nothing if it has reached a limit.
|
||||
*
|
||||
* @param line line component
|
||||
*/
|
||||
@@ -21,7 +21,7 @@ public interface LineDrawable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws a line with a custom score format, or does nothing if it has reached the limit.
|
||||
* Draws the next line with a custom score format, or does nothing if it has reached a limit.
|
||||
*
|
||||
* @param line line component
|
||||
* @param scoreFormat score format
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ public interface FramedSidebarAnimation<T> extends SidebarAnimation<T> {
|
||||
int currentFrameIndex();
|
||||
|
||||
/**
|
||||
* Switches the current frame
|
||||
* Switches the current frame to specified index
|
||||
*
|
||||
* @param frameIndex the index of the new current frame
|
||||
* @throws ArrayIndexOutOfBoundsException if frame is out of bounds
|
||||
|
||||
@@ -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
|
||||
|
||||
/**
|
||||
@@ -127,6 +145,7 @@ public interface TeamDisplay {
|
||||
|
||||
/**
|
||||
* @return name tag visibility rule value
|
||||
* @since Minecraft 1.8
|
||||
*/
|
||||
@NotNull NameTagVisibility nameTagVisibility();
|
||||
|
||||
@@ -134,6 +153,7 @@ public interface TeamDisplay {
|
||||
* Updates the name tag visibility rule.
|
||||
*
|
||||
* @param nameTagVisibility new rule value
|
||||
* @since Minecraft 1.8
|
||||
*/
|
||||
@NotNull TeamDisplay nameTagVisibility(@NotNull NameTagVisibility nameTagVisibility);
|
||||
|
||||
@@ -156,6 +176,7 @@ public interface TeamDisplay {
|
||||
* It is also used for displaying team specific sidebars with the {@link ObjectiveDisplaySlot.TeamSidebar}} display slot.
|
||||
*
|
||||
* @return player color
|
||||
* @since Minecraft 1.8
|
||||
*/
|
||||
@Nullable NamedTextColor playerColor();
|
||||
|
||||
@@ -164,6 +185,12 @@ public interface TeamDisplay {
|
||||
*
|
||||
* @param playerColor new player color
|
||||
* @see #playerColor()
|
||||
* @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
-42
@@ -1,46 +1,5 @@
|
||||
plugins {
|
||||
`java-library`
|
||||
}
|
||||
|
||||
allprojects {
|
||||
version = "2.1.3"
|
||||
version = "2.7.1-SNAPSHOT"
|
||||
group = "net.megavex"
|
||||
description = "Powerful packet-level scoreboard library for Paper/Spigot servers"
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
maven("https://oss.sonatype.org/content/groups/public/")
|
||||
maven("https://repo.papermc.io/repository/maven-public/")
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
apply(plugin = "java-library")
|
||||
|
||||
dependencies {
|
||||
compileOnly("org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT")
|
||||
testImplementation("org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT")
|
||||
|
||||
val adventureVersion = "4.16.0"
|
||||
compileOnly("net.kyori:adventure-api:$adventureVersion")
|
||||
compileOnly("net.kyori:adventure-text-serializer-legacy:$adventureVersion")
|
||||
compileOnly("net.kyori:adventure-text-serializer-gson:$adventureVersion")
|
||||
testImplementation("net.kyori:adventure-api:$adventureVersion")
|
||||
|
||||
val jupiterVersion = "5.10.2"
|
||||
testImplementation("org.junit.jupiter:junit-jupiter-api:$jupiterVersion")
|
||||
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$jupiterVersion")
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8.toString()
|
||||
targetCompatibility = JavaVersion.VERSION_1_8.toString()
|
||||
options.encoding = "UTF-8"
|
||||
options.isIncremental = true
|
||||
options.compilerArgs = mutableListOf("-Xlint:-deprecation,-unchecked")
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
}
|
||||
}
|
||||
@@ -3,5 +3,12 @@ plugins {
|
||||
}
|
||||
|
||||
repositories {
|
||||
mavenCentral()
|
||||
gradlePluginPortal()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(libs.buildIndra)
|
||||
implementation(libs.buildNmcp)
|
||||
compileOnly(files(libs::class.java.protectionDomain.codeSource.location))
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
dependencyResolutionManagement {
|
||||
versionCatalogs {
|
||||
register("libs") {
|
||||
from(files("../gradle/libs.versions.toml")) // include from parent project
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
import org.gradle.api.tasks.compile.JavaCompile
|
||||
import org.gradle.kotlin.dsl.dependencies
|
||||
import org.gradle.kotlin.dsl.withType
|
||||
|
||||
plugins {
|
||||
`maven-publish`
|
||||
`java-library`
|
||||
id("net.kyori.indra")
|
||||
id("net.kyori.indra.publishing")
|
||||
id("com.gradleup.nmcp")
|
||||
}
|
||||
|
||||
val libs = extensions.getByType(org.gradle.accessors.dm.LibrariesForLibs::class)
|
||||
|
||||
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 {
|
||||
testImplementation(libs.spigotApi)
|
||||
testImplementation(libs.bundles.adventure)
|
||||
testImplementation(libs.junitJupiter)
|
||||
testRuntimeOnly(libs.junitPlatformLauncher)
|
||||
}
|
||||
|
||||
indra {
|
||||
github("MegavexNetwork", "scoreboard-library") {
|
||||
ci(true)
|
||||
}
|
||||
mitLicense()
|
||||
reproducibleBuilds(true)
|
||||
|
||||
javaVersions {
|
||||
target(8)
|
||||
minimumToolchain(21)
|
||||
}
|
||||
|
||||
publishSnapshotsTo("forgejo", "https://git.megavex.net/api/packages/MegavexPublic/maven")
|
||||
configurePublications {
|
||||
pom {
|
||||
url = "https://github.com/MegavexNetwork/scoreboard-library"
|
||||
developers {
|
||||
developer {
|
||||
id = "vytskalt"
|
||||
name = "vytskalt"
|
||||
email = "vytskalt@protonmail.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nmcp {
|
||||
val centralUsername = findProperty("centralUsername") as String?
|
||||
val centralPassword = findProperty("centralPassword") as String?
|
||||
|
||||
if (centralUsername != null && centralPassword != null) {
|
||||
publishAllPublications {
|
||||
username = centralUsername
|
||||
password = centralPassword
|
||||
publicationType = "USER_MANAGED"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
options.compilerArgs = mutableListOf("-Xlint:-deprecation,-unchecked")
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
plugins {
|
||||
`java-library`
|
||||
`maven-publish`
|
||||
}
|
||||
|
||||
publishing {
|
||||
publications.create<MavenPublication>("maven") {
|
||||
from(components["java"])
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
plugins {
|
||||
id("net.megavex.scoreboardlibrary.publish-conventions")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":scoreboard-library-api"))
|
||||
}
|
||||
-66
@@ -1,66 +0,0 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.commons;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodHandles.Lookup;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.*;
|
||||
|
||||
public final class CollectionProvider {
|
||||
private static final MethodHandle mapConstructor, setConstructor, listConstructor;
|
||||
|
||||
static {
|
||||
Lookup lookup = MethodHandles.publicLookup();
|
||||
MethodType parameters = MethodType.methodType(void.class, int.class);
|
||||
mapConstructor = getConstructor(lookup, "it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap", HashMap.class, parameters);
|
||||
setConstructor = getConstructor(lookup, "it.unimi.dsi.fastutil.objects.ObjectOpenHashSet", HashSet.class, parameters);
|
||||
listConstructor = getConstructor(lookup, "it.unimi.dsi.fastutil.objects.ObjectArrayList", ArrayList.class, parameters);
|
||||
}
|
||||
|
||||
private CollectionProvider() {
|
||||
}
|
||||
|
||||
private static MethodHandle getConstructor(Lookup lookup, String fastUtilClass, Class<?> fallback, MethodType parameters) {
|
||||
Class<?> clazz;
|
||||
try {
|
||||
clazz = Class.forName(fastUtilClass);
|
||||
} catch (ClassNotFoundException e) {
|
||||
clazz = fallback;
|
||||
}
|
||||
|
||||
try {
|
||||
return lookup.findConstructor(clazz, parameters);
|
||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||
throw new ExceptionInInitializerError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <K, V> @NotNull Map<K, V> map(int capacity) {
|
||||
try {
|
||||
// noinspection unchecked
|
||||
return (Map<K, V>) mapConstructor.invokeWithArguments(capacity);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <E> @NotNull Set<E> set(int capacity) {
|
||||
try {
|
||||
// noinspection unchecked
|
||||
return (Set<E>) setConstructor.invokeWithArguments(capacity);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <E> @NotNull List<E> list(int capacity) {
|
||||
try {
|
||||
// noinspection unchecked
|
||||
return (List<E>) listConstructor.invokeWithArguments(capacity);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,9 @@
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinJvmCompile
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "1.9.23"
|
||||
id("net.megavex.scoreboardlibrary.publish-conventions")
|
||||
id("net.megavex.scoreboardlibrary.base-conventions")
|
||||
alias(libs.plugins.kotlin)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
@@ -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
+27
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1770056022,
|
||||
"narHash": "sha256-yvCz+Qmci1bVucXEyac3TdoSPMtjqVJmVy5wro6j/70=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "d04d8548aed39902419f14a8537006426dc1e4fa",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-25.11",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.11";
|
||||
};
|
||||
|
||||
outputs = { nixpkgs, ... }: let
|
||||
forAllSystems = function:
|
||||
nixpkgs.lib.genAttrs [
|
||||
"x86_64-linux"
|
||||
"aarch64-linux"
|
||||
"x86_64-darwin"
|
||||
"aarch64-darwin"
|
||||
] (system:
|
||||
function (import nixpkgs {
|
||||
inherit system;
|
||||
}));
|
||||
in {
|
||||
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
|
||||
];
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
[versions]
|
||||
adventure = "4.25.0"
|
||||
|
||||
[libraries]
|
||||
spigotApi = "org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT" # do not update
|
||||
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" }
|
||||
|
||||
# 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.2.21"
|
||||
|
||||
[bundles]
|
||||
adventure = [ "adventureApi", "adventureTextSerializerGson", "adventureTextSerializerLegacy" ]
|
||||
Vendored
BIN
Binary file not shown.
+1
-1
@@ -1,6 +1,6 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
|
||||
@@ -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.
|
||||
@@ -15,6 +15,8 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
@@ -55,7 +57,7 @@
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
@@ -84,7 +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 "${APP_HOME:-./}" > /dev/null && pwd -P ) || 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
|
||||
@@ -112,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.
|
||||
@@ -170,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" )
|
||||
|
||||
@@ -203,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
+3
-2
@@ -13,6 +13,8 @@
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
@rem SPDX-License-Identifier: Apache-2.0
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%"=="" @echo off
|
||||
@rem ##########################################################################
|
||||
@@ -68,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
|
||||
|
||||
@@ -1,8 +1,17 @@
|
||||
plugins {
|
||||
id("net.megavex.scoreboardlibrary.publish-conventions")
|
||||
id("net.megavex.scoreboardlibrary.base-conventions")
|
||||
//id("io.papermc.paperweight.userdev") version "2.0.0-beta.19"
|
||||
}
|
||||
|
||||
repositories {
|
||||
maven("https://repo.viaversion.com")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":scoreboard-library-api"))
|
||||
implementation(project(":scoreboard-library-packet-adapter-base"))
|
||||
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")
|
||||
}
|
||||
+90
-43
@@ -3,6 +3,7 @@ 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;
|
||||
|
||||
@@ -12,14 +13,14 @@ public final class PacketAdapterLoader {
|
||||
private PacketAdapterLoader() {
|
||||
}
|
||||
|
||||
public static @NotNull PacketAdapterProvider loadPacketAdapter() throws NoPacketAdapterAvailableException {
|
||||
public static @NotNull PacketAdapterProvider loadPacketAdapter(Plugin plugin) throws NoPacketAdapterAvailableException {
|
||||
Class<?> nmsClass = findAndLoadImplementationClass();
|
||||
if (nmsClass == null) {
|
||||
throw new NoPacketAdapterAvailableException();
|
||||
}
|
||||
|
||||
try {
|
||||
return (PacketAdapterProvider) nmsClass.getConstructors()[0].newInstance();
|
||||
return (PacketAdapterProvider) nmsClass.getConstructors()[0].newInstance(plugin);
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException("couldn't initialize packet adapter", e);
|
||||
}
|
||||
@@ -27,58 +28,104 @@ public final class PacketAdapterLoader {
|
||||
|
||||
private static @Nullable Class<?> findAndLoadImplementationClass() {
|
||||
String version = Bukkit.getServer().getBukkitVersion();
|
||||
Class<?> nmsClass = tryLoadVersion(version);
|
||||
if (nmsClass != null) {
|
||||
return nmsClass;
|
||||
int dashIndex = version.indexOf('-');
|
||||
if (dashIndex != -1) {
|
||||
version = version.substring(0, dashIndex);
|
||||
}
|
||||
|
||||
return tryLoadPacketEvents();
|
||||
return tryLoadVersion(version);
|
||||
}
|
||||
|
||||
private static @Nullable Class<?> tryLoadVersion(@NotNull String version) {
|
||||
private static @Nullable Class<?> tryLoadVersion(@NotNull String serverVersion) {
|
||||
// https://www.spigotmc.org/wiki/spigot-nms-and-minecraft-versions-legacy/
|
||||
// https://www.spigotmc.org/wiki/spigot-nms-and-minecraft-versions-1-10-1-15/
|
||||
// https://www.spigotmc.org/wiki/spigot-nms-and-minecraft-versions-1-16/
|
||||
switch (version) {
|
||||
case "1.8.8-R0.1-SNAPSHOT":
|
||||
return tryLoadImplementationClass("v1_8_R3");
|
||||
case "1.17-R0.1-SNAPSHOT":
|
||||
case "1.17.1-R0.1-SNAPSHOT":
|
||||
case "1.18-R0.1-SNAPSHOT":
|
||||
case "1.18.1-R0.1-SNAPSHOT":
|
||||
case "1.18.2-R0.1-SNAPSHOT":
|
||||
case "1.19-R0.1-SNAPSHOT":
|
||||
case "1.19.1-R0.1-SNAPSHOT":
|
||||
case "1.19.2-R0.1-SNAPSHOT":
|
||||
case "1.19.3-R0.1-SNAPSHOT":
|
||||
case "1.19.4-R0.1-SNAPSHOT":
|
||||
case "1.20-R0.1-SNAPSHOT":
|
||||
case "1.20.1-R0.1-SNAPSHOT":
|
||||
case "1.20.2-R0.1-SNAPSHOT":
|
||||
case "1.20.3-R0.1-SNAPSHOT":
|
||||
case "1.20.4-R0.1-SNAPSHOT":
|
||||
return tryLoadImplementationClass("modern");
|
||||
// https://www.spigotmc.org/wiki/spigot-nms-and-minecraft-versions-1-21/
|
||||
switch (serverVersion) {
|
||||
case "1.7.10":
|
||||
case "1.8":
|
||||
case "1.8.3":
|
||||
case "1.8.4":
|
||||
case "1.8.5":
|
||||
case "1.8.6":
|
||||
case "1.8.7":
|
||||
case "1.8.8":
|
||||
case "1.9":
|
||||
case "1.9.2":
|
||||
case "1.9.4":
|
||||
case "1.10.2":
|
||||
case "1.11":
|
||||
case "1.11.2":
|
||||
case "1.12":
|
||||
case "1.12.1":
|
||||
case "1.12.2":
|
||||
try {
|
||||
return Class.forName("net.megavex.scoreboardlibrary.implementation.packetAdapter.legacy.PacketAdapterProviderImpl");
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
return null;
|
||||
}
|
||||
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":
|
||||
case "1.18.1":
|
||||
case "1.18.2":
|
||||
case "1.19":
|
||||
case "1.19.1":
|
||||
case "1.19.2":
|
||||
case "1.19.3":
|
||||
case "1.19.4":
|
||||
case "1.20":
|
||||
case "1.20.1":
|
||||
case "1.20.2":
|
||||
case "1.20.3":
|
||||
case "1.20.4":
|
||||
case "1.20.5":
|
||||
case "1.20.6":
|
||||
case "1.21":
|
||||
case "1.21.1":
|
||||
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":
|
||||
case "26.1":
|
||||
return tryLoadModern();
|
||||
default:
|
||||
// Hide from relocation checkers
|
||||
String property = "net.mega".concat("vex.scoreboardlibrary.forceModern");
|
||||
if (System.getProperty(property, "").equalsIgnoreCase("true")) {
|
||||
return tryLoadModern();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable Class<?> tryLoadPacketEvents() {
|
||||
Class<?> nmsClass = tryLoadImplementationClass("packetevents");
|
||||
if (nmsClass == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Class<?> tryLoadModern() {
|
||||
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";
|
||||
return Class.forName(path);
|
||||
return Class.forName("net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAdapterProviderImpl");
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
return null;
|
||||
}
|
||||
|
||||
+5
-5
@@ -30,7 +30,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ScoreboardLibraryImpl implements ScoreboardLibrary {
|
||||
public final class ScoreboardLibraryImpl implements ScoreboardLibrary {
|
||||
private final Plugin plugin;
|
||||
private final PacketAdapterProvider packetAdapter;
|
||||
private final TaskScheduler taskScheduler;
|
||||
@@ -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);
|
||||
|
||||
+12
-6
@@ -8,6 +8,7 @@ import net.kyori.adventure.translation.GlobalTranslator;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -17,17 +18,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 = new HashMap<>(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 +63,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();
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.commons;
|
||||
|
||||
/**
|
||||
* Represents different strategies of rendering sidebar lines.
|
||||
*/
|
||||
public enum LineRenderingStrategy {
|
||||
/**
|
||||
* For versions older than 1.13, where team properties are stored as strings and have a limit of 16 characters.
|
||||
*/
|
||||
LEGACY,
|
||||
/**
|
||||
* For versions 1.13+, where team properties are stored as components with no limits.
|
||||
*/
|
||||
MODERN,
|
||||
/**
|
||||
* For versions 1.20.3+, where the score display name can be used instead of team prefix team.
|
||||
* Not currently being used.
|
||||
*/
|
||||
POST_MODERN
|
||||
}
|
||||
+5
-3
@@ -12,12 +12,14 @@ import java.util.function.Function;
|
||||
|
||||
public final class LocaleProvider {
|
||||
private static final Locale DEFAULT_LOCALE = Locale.US;
|
||||
private static final Function<Player, Locale> provider = get();
|
||||
private static final Function<Player, Locale> PROVIDER = get();
|
||||
|
||||
public static @NotNull Locale locale(Player player) {
|
||||
return provider.apply(player);
|
||||
private LocaleProvider() {
|
||||
}
|
||||
|
||||
public static @NotNull Locale locale(Player player) {
|
||||
return PROVIDER.apply(player);
|
||||
}
|
||||
|
||||
private static @NotNull Function<Player, Locale> get() {
|
||||
MethodHandles.Lookup lookup = MethodHandles.publicLookup();
|
||||
+33
-9
@@ -6,7 +6,6 @@ import net.megavex.scoreboardlibrary.api.objective.ObjectiveManager;
|
||||
import net.megavex.scoreboardlibrary.api.objective.ObjectiveScore;
|
||||
import net.megavex.scoreboardlibrary.api.objective.ScoreboardObjective;
|
||||
import net.megavex.scoreboardlibrary.implementation.ScoreboardLibraryImpl;
|
||||
import net.megavex.scoreboardlibrary.implementation.commons.CollectionProvider;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectivePacketAdapter;
|
||||
import net.megavex.scoreboardlibrary.implementation.player.PlayerDisplayable;
|
||||
@@ -15,16 +14,17 @@ 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;
|
||||
|
||||
public class ObjectiveManagerImpl implements ObjectiveManager, PlayerDisplayable {
|
||||
public final class ObjectiveManagerImpl implements ObjectiveManager, PlayerDisplayable {
|
||||
private final ScoreboardLibraryImpl library;
|
||||
private final Map<String, ScoreboardObjectiveImpl> objectives = new ConcurrentHashMap<>();
|
||||
private final Map<ObjectiveDisplaySlot, ScoreboardObjectiveImpl> displaySlots = new HashMap<>();
|
||||
|
||||
private final Set<Player> players = CollectionProvider.set(8);
|
||||
private final Set<Player> displayingPlayers = CollectionProvider.set(8);
|
||||
private final Set<Player> players = new HashSet<>(8);
|
||||
private final Set<Player> displayingPlayers = new HashSet<>(8);
|
||||
private final Queue<ObjectiveManagerTask> taskQueue = new ConcurrentLinkedQueue<>();
|
||||
private boolean closed;
|
||||
|
||||
@@ -108,10 +108,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 +159,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 +184,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 +217,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 +229,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());
|
||||
}
|
||||
}
|
||||
|
||||
+9
-1
@@ -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 {
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.util.Iterator;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class ObjectiveUpdaterTask implements Runnable {
|
||||
public final class ObjectiveUpdaterTask implements Runnable {
|
||||
private final ScoreboardLibraryImpl scoreboardLibrary;
|
||||
private final RunningTask task;
|
||||
private final Object lock = new Object();
|
||||
|
||||
+25
-1
@@ -16,7 +16,7 @@ import java.util.*;
|
||||
|
||||
import static net.kyori.adventure.text.Component.empty;
|
||||
|
||||
public class ScoreboardObjectiveImpl implements ScoreboardObjective {
|
||||
public final class ScoreboardObjectiveImpl implements ScoreboardObjective {
|
||||
private final ObjectivePacketAdapter packetAdapter;
|
||||
private final Queue<ObjectiveManagerTask> taskQueue;
|
||||
private final String name;
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -1,5 +1,6 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.packetAdapter;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.megavex.scoreboardlibrary.api.team.enums.CollisionRule;
|
||||
import net.megavex.scoreboardlibrary.api.team.enums.NameTagVisibility;
|
||||
@@ -7,11 +8,10 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
public interface ImmutableTeamProperties<T> {
|
||||
default @NotNull Collection<String> entries() {
|
||||
return Collections.emptySet();
|
||||
default @NotNull Collection<String> syncedEntries() {
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
@NotNull T displayName();
|
||||
+2
-1
@@ -1,5 +1,6 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.packetAdapter;
|
||||
|
||||
import net.megavex.scoreboardlibrary.implementation.commons.LineRenderingStrategy;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectivePacketAdapter;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamsPacketAdapter;
|
||||
import org.bukkit.entity.Player;
|
||||
@@ -10,5 +11,5 @@ public interface PacketAdapterProvider {
|
||||
|
||||
@NotNull TeamsPacketAdapter createTeamPacketAdapter(@NotNull String teamName);
|
||||
|
||||
boolean isLegacy(@NotNull Player player);
|
||||
@NotNull LineRenderingStrategy lineRenderingStrategy(@NotNull Player player);
|
||||
}
|
||||
File renamed without changes.
File renamed without changes.
+41
@@ -0,0 +1,41 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.packetAdapter.legacy;
|
||||
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ReflectUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public final class ChatColorUtil {
|
||||
private static final MethodHandle FROM_NAME_METHOD, GET_INDEX_METHOD;
|
||||
|
||||
static {
|
||||
Class<?> enumChatFormatClass = ReflectUtil.getClassOrThrow(LegacyMinecraftClasses.server("EnumChatFormat"));
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
|
||||
try {
|
||||
Method fromNameMethod = enumChatFormatClass.getMethod("b", String.class);
|
||||
FROM_NAME_METHOD = lookup.unreflect(fromNameMethod);
|
||||
|
||||
Method getIndexMethod = enumChatFormatClass.getMethod("b");
|
||||
GET_INDEX_METHOD = lookup.unreflect(getIndexMethod);
|
||||
} catch (NoSuchMethodException | IllegalAccessException e) {
|
||||
throw new ExceptionInInitializerError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private ChatColorUtil() {
|
||||
}
|
||||
|
||||
public static int getColorIndex(@NotNull NamedTextColor color) {
|
||||
String name = NamedTextColor.NAMES.key(color);
|
||||
try {
|
||||
Object format = FROM_NAME_METHOD.invoke(name);
|
||||
return (int) GET_INDEX_METHOD.invoke(format);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.packetAdapter.legacy;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
// TODO: should be moved to MinecraftReflection at some point
|
||||
public final class LegacyMinecraftClasses {
|
||||
private static final String NMS_VERSION_STRING = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
|
||||
|
||||
private LegacyMinecraftClasses() {
|
||||
}
|
||||
|
||||
public static @NotNull String server(String path) {
|
||||
return "net.minecraft.server." + NMS_VERSION_STRING + "." + path;
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.packetAdapter.legacy;
|
||||
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.MinecraftClasses;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ReflectUtil;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public final class LegacyPacketSender implements PacketSender<Object> {
|
||||
public static final LegacyPacketSender INSTANCE = new LegacyPacketSender();
|
||||
|
||||
private static final MethodHandle GET_HANDLE_METHOD, PLAYER_CONNECTION_FIELD, SEND_PACKET_METHOD;
|
||||
|
||||
static {
|
||||
Class<?> packetClass = ReflectUtil.getClassOrThrow(LegacyMinecraftClasses.server("Packet"));
|
||||
Class<?> craftPlayerClass = ReflectUtil.getClassOrThrow(MinecraftClasses.craftBukkit("entity.CraftPlayer"));
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
|
||||
try {
|
||||
Method getHandleMethod = craftPlayerClass.getMethod("getHandle");
|
||||
GET_HANDLE_METHOD = lookup.unreflect(getHandleMethod);
|
||||
|
||||
Field playerConnectionField = getHandleMethod.getReturnType().getField("playerConnection");
|
||||
PLAYER_CONNECTION_FIELD = lookup.unreflectGetter(playerConnectionField);
|
||||
|
||||
Method sendPacketMethod = playerConnectionField.getType().getMethod("sendPacket", packetClass);
|
||||
SEND_PACKET_METHOD = lookup.unreflect(sendPacketMethod);
|
||||
} catch (NoSuchMethodException | IllegalAccessException | NoSuchFieldException e) {
|
||||
throw new ExceptionInInitializerError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private LegacyPacketSender() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPacket(Player player, Object packet) {
|
||||
try {
|
||||
Object handle = GET_HANDLE_METHOD.invoke(player);
|
||||
Object playerConnection = PLAYER_CONNECTION_FIELD.invoke(handle);
|
||||
SEND_PACKET_METHOD.invoke(playerConnection, packet);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+45
-35
@@ -1,40 +1,42 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.packetAdapter.v1_8_R3;
|
||||
package net.megavex.scoreboardlibrary.implementation.packetAdapter.legacy;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
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;
|
||||
import net.megavex.scoreboardlibrary.implementation.commons.LegacyFormatUtil;
|
||||
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 net.minecraft.server.v1_8_R3.*;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class ObjectivePacketAdapterImpl implements ObjectivePacketAdapter {
|
||||
private final PacketSender<Packet<?>> sender;
|
||||
private final String objectiveName;
|
||||
private PacketPlayOutScoreboardObjective removePacket;
|
||||
import static net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection;
|
||||
|
||||
public ObjectivePacketAdapterImpl(@NotNull PacketSender<Packet<?>> sender, @NotNull String objectiveName) {
|
||||
this.sender = sender;
|
||||
public final class ObjectivePacketAdapterImpl implements ObjectivePacketAdapter {
|
||||
private final String objectiveName;
|
||||
private Object removePacket;
|
||||
|
||||
public ObjectivePacketAdapterImpl(@NotNull String objectiveName) {
|
||||
this.objectiveName = objectiveName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String objectiveName() {
|
||||
return objectiveName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void display(@NotNull Collection<Player> players, @NotNull ObjectiveDisplaySlot slot) {
|
||||
PacketPlayOutScoreboardDisplayObjective packet = new PacketPlayOutScoreboardDisplayObjective();
|
||||
PacketAccessors.DISPLAY_OBJECTIVE_POSITION.set(packet, ObjectiveConstants.displaySlotIndex(slot));
|
||||
Object packet = PacketAccessors.DISPLAY_OBJECTIVE_CONSTRUCTOR.invoke();
|
||||
PacketAccessors.DISPLAY_OBJECTIVE_POSITION.set(packet, ObjectiveConstants.displaySlotIndex(slot, false));
|
||||
PacketAccessors.DISPLAY_OBJECTIVE_NAME.set(packet, objectiveName);
|
||||
sender.sendPacket(players, packet);
|
||||
LegacyPacketSender.INSTANCE.sendPacket(players, packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -46,7 +48,7 @@ public class ObjectivePacketAdapterImpl implements ObjectivePacketAdapter {
|
||||
@Nullable ScoreFormat scoreFormat
|
||||
) {
|
||||
LocalePacketUtil.sendLocalePackets(
|
||||
sender,
|
||||
LegacyPacketSender.INSTANCE,
|
||||
players,
|
||||
locale -> createPropertiesPacket(packetType, GlobalTranslator.render(value, locale), renderType)
|
||||
);
|
||||
@@ -55,11 +57,12 @@ public class ObjectivePacketAdapterImpl implements ObjectivePacketAdapter {
|
||||
@Override
|
||||
public void remove(@NotNull Collection<Player> players) {
|
||||
if (removePacket == null) {
|
||||
removePacket = new PacketPlayOutScoreboardObjective();
|
||||
removePacket = PacketAccessors.OBJECTIVE_CONSTRUCTOR.invoke();
|
||||
PacketAccessors.OBJECTIVE_NAME_FIELD.set(removePacket, objectiveName);
|
||||
PacketAccessors.OBJECTIVE_DISPLAY_NAME_FIELD.set(removePacket, "");
|
||||
PacketAccessors.OBJECTIVE_MODE_FIELD.set(removePacket, ObjectiveConstants.MODE_REMOVE);
|
||||
}
|
||||
sender.sendPacket(players, removePacket);
|
||||
LegacyPacketSender.INSTANCE.sendPacket(players, removePacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,45 +73,52 @@ public class ObjectivePacketAdapterImpl implements ObjectivePacketAdapter {
|
||||
@Nullable Component display,
|
||||
@Nullable ScoreFormat scoreFormat
|
||||
) {
|
||||
PacketPlayOutScoreboardScore packet = new PacketPlayOutScoreboardScore(entry);
|
||||
Object packet = PacketAccessors.SCORE_CONSTRUCTOR.invoke(entry);
|
||||
PacketAccessors.SCORE_OBJECTIVE_NAME_FIELD.set(packet, objectiveName);
|
||||
PacketAccessors.SCORE_VALUE_FIELD.set(packet, value);
|
||||
PacketAccessors.SCORE_ACTION_FIELD.set(packet, PacketPlayOutScoreboardScore.EnumScoreboardAction.CHANGE);
|
||||
sender.sendPacket(players, packet);
|
||||
if (PacketAccessors.SCORE_ACTION_FIELD_1_8 != null) {
|
||||
PacketAccessors.SCORE_ACTION_FIELD_1_8.set(packet, PacketAccessors.SCORE_ACTION_CHANGE_1_8);
|
||||
} else {
|
||||
PacketAccessors.SCORE_ACTION_FIELD_1_7.set(packet, 0);
|
||||
}
|
||||
|
||||
LegacyPacketSender.INSTANCE.sendPacket(players, packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeScore(@NotNull Collection<Player> players, @NotNull String entry) {
|
||||
PacketPlayOutScoreboardScore packet = new PacketPlayOutScoreboardScore(entry);
|
||||
Object packet = PacketAccessors.SCORE_CONSTRUCTOR.invoke(entry);
|
||||
PacketAccessors.SCORE_OBJECTIVE_NAME_FIELD.set(packet, objectiveName);
|
||||
sender.sendPacket(players, packet);
|
||||
LegacyPacketSender.INSTANCE.sendPacket(players, packet);
|
||||
}
|
||||
|
||||
private @NotNull PacketPlayOutScoreboardObjective createPropertiesPacket(
|
||||
private @NotNull Object createPropertiesPacket(
|
||||
@NotNull PropertiesPacketType packetType,
|
||||
@NotNull Component value,
|
||||
@NotNull ObjectiveRenderType renderType
|
||||
) {
|
||||
PacketPlayOutScoreboardObjective packet = new PacketPlayOutScoreboardObjective();
|
||||
Object packet = PacketAccessors.OBJECTIVE_CONSTRUCTOR.invoke();
|
||||
PacketAccessors.OBJECTIVE_NAME_FIELD.set(packet, objectiveName);
|
||||
PacketAccessors.OBJECTIVE_MODE_FIELD.set(packet, ObjectiveConstants.mode(packetType));
|
||||
|
||||
String legacyValue = LegacyFormatUtil.limitLegacyText(LegacyComponentSerializer.legacySection().serialize(value), ObjectiveConstants.LEGACY_VALUE_CHAR_LIMIT);
|
||||
String legacyValue = LegacyFormatUtil.limitLegacyText(legacySection().serialize(value), ObjectiveConstants.LEGACY_VALUE_CHAR_LIMIT);
|
||||
PacketAccessors.OBJECTIVE_DISPLAY_NAME_FIELD.set(packet, legacyValue);
|
||||
|
||||
IScoreboardCriteria.EnumScoreboardHealthDisplay nmsRenderType;
|
||||
switch (renderType) {
|
||||
case INTEGER:
|
||||
nmsRenderType = IScoreboardCriteria.EnumScoreboardHealthDisplay.INTEGER;
|
||||
break;
|
||||
case HEARTS:
|
||||
nmsRenderType = IScoreboardCriteria.EnumScoreboardHealthDisplay.HEARTS;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
if (PacketAccessors.OBJECTIVE_HEALTH_DISPLAY_FIELD != null) {
|
||||
Object nmsRenderType;
|
||||
switch (renderType) {
|
||||
case INTEGER:
|
||||
nmsRenderType = PacketAccessors.HEALTH_DISPLAY_INTEGER;
|
||||
break;
|
||||
case HEARTS:
|
||||
nmsRenderType = PacketAccessors.HEALTH_DISPLAY_HEARTS;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
PacketAccessors.OBJECTIVE_HEALTH_DISPLAY_FIELD.set(packet, nmsRenderType);
|
||||
}
|
||||
|
||||
PacketAccessors.OBJECTIVE_HEALTH_DISPLAY_FIELD.set(packet, nmsRenderType);
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.packetAdapter.legacy;
|
||||
|
||||
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.ReflectUtil;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public final class PacketAccessors {
|
||||
public static final Class<?> HEALTH_DISPLAY_CLASS =
|
||||
ReflectUtil.getOptionalClass(LegacyMinecraftClasses.server("IScoreboardCriteria$EnumScoreboardHealthDisplay"));
|
||||
public static final Object HEALTH_DISPLAY_INTEGER = HEALTH_DISPLAY_CLASS != null ? ReflectUtil.getEnumInstance(HEALTH_DISPLAY_CLASS, "INTEGER") : null;
|
||||
public static final Object HEALTH_DISPLAY_HEARTS = HEALTH_DISPLAY_CLASS != null ? ReflectUtil.getEnumInstance(HEALTH_DISPLAY_CLASS, "HEARTS") : null;
|
||||
|
||||
private static final boolean IS_AT_LEAST_1_8 = HEALTH_DISPLAY_INTEGER != null;
|
||||
private static final boolean IS_AT_LEAST_1_9 = ReflectUtil.getOptionalClass(LegacyMinecraftClasses.server("ScoreboardTeamBase$EnumTeamPush")) != null;
|
||||
|
||||
public static final Class<?> OBJECTIVE_CLASS =
|
||||
ReflectUtil.getClassOrThrow(LegacyMinecraftClasses.server("PacketPlayOutScoreboardObjective"));
|
||||
public static final ConstructorAccessor<?> OBJECTIVE_CONSTRUCTOR = ReflectUtil.findConstructor(OBJECTIVE_CLASS);
|
||||
public static final FieldAccessor<Object, Integer> OBJECTIVE_MODE_FIELD =
|
||||
ReflectUtil.findFieldUnchecked(OBJECTIVE_CLASS, 0, int.class);
|
||||
public static final FieldAccessor<Object, String> OBJECTIVE_NAME_FIELD =
|
||||
ReflectUtil.findFieldUnchecked(OBJECTIVE_CLASS, 0, String.class);
|
||||
public static final FieldAccessor<Object, String> OBJECTIVE_DISPLAY_NAME_FIELD =
|
||||
ReflectUtil.findFieldUnchecked(OBJECTIVE_CLASS, 1, String.class);
|
||||
public static final FieldAccessor<Object, Object> OBJECTIVE_HEALTH_DISPLAY_FIELD =
|
||||
HEALTH_DISPLAY_CLASS != null ? ReflectUtil.findFieldUnchecked(OBJECTIVE_CLASS, 0, HEALTH_DISPLAY_CLASS) : null;
|
||||
|
||||
public static final Class<?> DISPLAY_OBJECTIVE_CLASS =
|
||||
ReflectUtil.getClassOrThrow(LegacyMinecraftClasses.server("PacketPlayOutScoreboardDisplayObjective"));
|
||||
public static final ConstructorAccessor<?> DISPLAY_OBJECTIVE_CONSTRUCTOR = ReflectUtil.findConstructor(DISPLAY_OBJECTIVE_CLASS);
|
||||
public static final FieldAccessor<Object, Integer> DISPLAY_OBJECTIVE_POSITION =
|
||||
ReflectUtil.findFieldUnchecked(DISPLAY_OBJECTIVE_CLASS, 0, int.class);
|
||||
public static final FieldAccessor<Object, String> DISPLAY_OBJECTIVE_NAME =
|
||||
ReflectUtil.findFieldUnchecked(DISPLAY_OBJECTIVE_CLASS, 0, String.class);
|
||||
|
||||
public static final Class<?> SCORE_CLASS =
|
||||
ReflectUtil.getClassOrThrow(LegacyMinecraftClasses.server("PacketPlayOutScoreboardScore"));
|
||||
public static final ConstructorAccessor<?> SCORE_CONSTRUCTOR = ReflectUtil.findConstructor(SCORE_CLASS, String.class);
|
||||
public static final FieldAccessor<Object, String> SCORE_OBJECTIVE_NAME_FIELD =
|
||||
ReflectUtil.findFieldUnchecked(SCORE_CLASS, 1, String.class);
|
||||
public static final FieldAccessor<Object, Integer> SCORE_VALUE_FIELD =
|
||||
ReflectUtil.findFieldUnchecked(SCORE_CLASS, 0, int.class);
|
||||
public static final Class<?> SCORE_ACTION_CLASS =
|
||||
ReflectUtil.getOptionalClass(LegacyMinecraftClasses.server("PacketPlayOutScoreboardScore$EnumScoreboardAction"));
|
||||
public static final FieldAccessor<Object, Object> SCORE_ACTION_FIELD_1_8 =
|
||||
SCORE_ACTION_CLASS != null ? ReflectUtil.findFieldUnchecked(SCORE_CLASS, 0, SCORE_ACTION_CLASS) : null;
|
||||
public static final Object SCORE_ACTION_CHANGE_1_8 = SCORE_ACTION_FIELD_1_8 != null ? ReflectUtil.getEnumInstance(SCORE_ACTION_CLASS, "CHANGE") : null;
|
||||
public static final FieldAccessor<Object, Integer> SCORE_ACTION_FIELD_1_7 =
|
||||
SCORE_ACTION_CLASS == null ? ReflectUtil.findFieldUnchecked(SCORE_CLASS, 1, int.class) : null;
|
||||
|
||||
public static final Class<?> TEAM_CLASS =
|
||||
ReflectUtil.getClassOrThrow(LegacyMinecraftClasses.server("PacketPlayOutScoreboardTeam"));
|
||||
public static final ConstructorAccessor<?> TEAM_CONSTRUCTOR = ReflectUtil.findConstructor(TEAM_CLASS);
|
||||
public static final FieldAccessor<Object, String> TEAM_NAME_FIELD =
|
||||
ReflectUtil.findFieldUnchecked(TEAM_CLASS, 0, String.class);
|
||||
public static final FieldAccessor<Object, String> TEAM_DISPLAY_NAME_FIELD =
|
||||
ReflectUtil.findFieldUnchecked(TEAM_CLASS, 1, String.class);
|
||||
public static final FieldAccessor<Object, String> TEAM_PREFIX_FIELD =
|
||||
ReflectUtil.findFieldUnchecked(TEAM_CLASS, 2, String.class);
|
||||
public static final FieldAccessor<Object, String> TEAM_SUFFIX_FIELD =
|
||||
ReflectUtil.findFieldUnchecked(TEAM_CLASS, 3, String.class);
|
||||
public static final FieldAccessor<Object, String> TEAM_NAME_TAG_VISIBILITY_FIELD =
|
||||
IS_AT_LEAST_1_8 ? ReflectUtil.findFieldUnchecked(TEAM_CLASS, 4, String.class) : null;
|
||||
public static final FieldAccessor<Object, String> TEAM_COLLISION_RULE_FIELD =
|
||||
IS_AT_LEAST_1_9 ? ReflectUtil.findFieldUnchecked(TEAM_CLASS, 5, String.class) : null;
|
||||
public static final FieldAccessor<Object, Integer> TEAM_COLOR_FIELD =
|
||||
IS_AT_LEAST_1_8 ? ReflectUtil.findFieldUnchecked(TEAM_CLASS, 0, int.class) : null;
|
||||
@SuppressWarnings("rawtypes")
|
||||
public static final FieldAccessor<Object, Collection> TEAM_ENTRIES_FIELD =
|
||||
ReflectUtil.findFieldUnchecked(TEAM_CLASS, 0, Collection.class);
|
||||
public static final FieldAccessor<Object, Integer> TEAM_MODE_FIELD =
|
||||
ReflectUtil.findFieldUnchecked(TEAM_CLASS, IS_AT_LEAST_1_8 ? 1 : 0, int.class);
|
||||
public static final FieldAccessor<Object, Integer> TEAM_RULES_FIELD =
|
||||
ReflectUtil.findFieldUnchecked(TEAM_CLASS, IS_AT_LEAST_1_8 ? 2 : 1, int.class);
|
||||
|
||||
private PacketAccessors() {
|
||||
}
|
||||
}
|
||||
+10
-14
@@ -1,33 +1,29 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.packetAdapter.v1_8_R3;
|
||||
package net.megavex.scoreboardlibrary.implementation.packetAdapter.legacy;
|
||||
|
||||
import net.megavex.scoreboardlibrary.implementation.commons.LineRenderingStrategy;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectivePacketAdapter;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketAdapterProvider;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamsPacketAdapter;
|
||||
import net.minecraft.server.v1_8_R3.Packet;
|
||||
import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public class PacketAdapterProviderImpl implements PacketAdapterProvider, PacketSender<Packet<?>> {
|
||||
public final class PacketAdapterProviderImpl implements PacketAdapterProvider {
|
||||
public PacketAdapterProviderImpl(Plugin plugin) {}
|
||||
|
||||
@Override
|
||||
public @NotNull TeamsPacketAdapter createTeamPacketAdapter(@NotNull String teamName) {
|
||||
return new TeamsPacketAdapterImpl(this, teamName);
|
||||
return new TeamsPacketAdapterImpl(teamName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ObjectivePacketAdapter createObjectiveAdapter(@NotNull String objectiveName) {
|
||||
return new ObjectivePacketAdapterImpl(this, objectiveName);
|
||||
return new ObjectivePacketAdapterImpl(objectiveName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLegacy(@NotNull Player player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPacket(@NotNull Player player, @NotNull Packet<?> packet) {
|
||||
((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
|
||||
public @NotNull LineRenderingStrategy lineRenderingStrategy(@NotNull Player player) {
|
||||
return LineRenderingStrategy.LEGACY;
|
||||
}
|
||||
}
|
||||
+37
-37
@@ -1,49 +1,32 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.packetAdapter.v1_8_R3;
|
||||
package net.megavex.scoreboardlibrary.implementation.packetAdapter.legacy;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.megavex.scoreboardlibrary.implementation.commons.LegacyFormatUtil;
|
||||
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.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.megavex.scoreboardlibrary.implementation.packetAdapter.util.LocalePacketUtil;
|
||||
import net.minecraft.server.v1_8_R3.EnumChatFormat;
|
||||
import net.minecraft.server.v1_8_R3.Packet;
|
||||
import net.minecraft.server.v1_8_R3.PacketPlayOutScoreboardTeam;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
import static net.megavex.scoreboardlibrary.implementation.commons.LegacyFormatUtil.limitLegacyText;
|
||||
|
||||
public class TeamsPacketAdapterImpl implements TeamsPacketAdapter {
|
||||
private final PacketSender<Packet<?>> sender;
|
||||
public final class TeamsPacketAdapterImpl implements TeamsPacketAdapter {
|
||||
private final String teamName;
|
||||
private PacketPlayOutScoreboardTeam removePacket;
|
||||
private Object removePacket;
|
||||
|
||||
public TeamsPacketAdapterImpl(@NotNull PacketSender<Packet<?>> sender, @NotNull String teamName) {
|
||||
this.sender = sender;
|
||||
public TeamsPacketAdapterImpl(@NotNull String teamName) {
|
||||
this.teamName = teamName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeTeam(@NotNull Iterable<Player> players) {
|
||||
if (removePacket == null) {
|
||||
removePacket = new PacketPlayOutScoreboardTeam();
|
||||
PacketAccessors.TEAM_NAME_FIELD.set(removePacket, teamName);
|
||||
PacketAccessors.TEAM_MODE_FIELD.set(removePacket, TeamConstants.MODE_REMOVE);
|
||||
}
|
||||
|
||||
sender.sendPacket(players, removePacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TeamDisplayPacketAdapter createTeamDisplayAdapter(@NotNull ImmutableTeamProperties<Component> properties) {
|
||||
return new AdventureTeamDisplayPacketAdapter(properties);
|
||||
@@ -61,42 +44,59 @@ 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) {
|
||||
PacketPlayOutScoreboardTeam packet = new PacketPlayOutScoreboardTeam();
|
||||
Object packet = PacketAccessors.TEAM_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);
|
||||
sender.sendPacket(players, packet);
|
||||
LegacyPacketSender.INSTANCE.sendPacket(players, packet);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendProperties(@NotNull PropertiesPacketType packetType, @NotNull Collection<Player> players) {
|
||||
LocalePacketUtil.sendLocalePackets(
|
||||
sender,
|
||||
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);
|
||||
|
||||
PacketPlayOutScoreboardTeam packet = new PacketPlayOutScoreboardTeam();
|
||||
Object packet = PacketAccessors.TEAM_CONSTRUCTOR.invoke();
|
||||
PacketAccessors.TEAM_NAME_FIELD.set(packet, teamName);
|
||||
PacketAccessors.TEAM_MODE_FIELD.set(packet, TeamConstants.mode(packetType));
|
||||
PacketAccessors.TEAM_DISPLAY_NAME_FIELD.set(packet, displayName);
|
||||
PacketAccessors.TEAM_PREFIX_FIELD.set(packet, prefix);
|
||||
PacketAccessors.TEAM_SUFFIX_FIELD.set(packet, suffix);
|
||||
PacketAccessors.TEAM_NAME_TAG_VISIBILITY_FIELD.set(packet, properties.nameTagVisibility().key());
|
||||
|
||||
NamedTextColor color = properties.playerColor();
|
||||
if (color != null) {
|
||||
String name = NamedTextColor.NAMES.key(color);
|
||||
PacketAccessors.TEAM_COLOR_FIELD.set(packet, Objects.requireNonNull(EnumChatFormat.b(name)).b());
|
||||
PacketAccessors.TEAM_RULES_FIELD.set(packet, properties.packOptions());
|
||||
if (PacketAccessors.TEAM_NAME_TAG_VISIBILITY_FIELD != null) {
|
||||
PacketAccessors.TEAM_NAME_TAG_VISIBILITY_FIELD.set(packet, properties.nameTagVisibility().key());
|
||||
}
|
||||
|
||||
NamedTextColor color = properties.playerColor();
|
||||
if (PacketAccessors.TEAM_COLOR_FIELD != null && color != null) {
|
||||
int teamColorField = ChatColorUtil.getColorIndex(color);
|
||||
PacketAccessors.TEAM_COLOR_FIELD.set(packet, teamColorField);
|
||||
}
|
||||
|
||||
if (PacketAccessors.TEAM_COLLISION_RULE_FIELD != null) {
|
||||
PacketAccessors.TEAM_COLLISION_RULE_FIELD.set(packet, properties.collisionRule().key());
|
||||
}
|
||||
|
||||
PacketAccessors.TEAM_RULES_FIELD.set(packet, properties.packOptions());
|
||||
if (packetType == PropertiesPacketType.CREATE) {
|
||||
PacketAccessors.TEAM_ENTRIES_FIELD.set(packet, properties.entries());
|
||||
PacketAccessors.TEAM_ENTRIES_FIELD.set(packet, ImmutableList.copyOf(properties.syncedEntries()));
|
||||
}
|
||||
|
||||
return packet;
|
||||
+361
@@ -0,0 +1,361 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern;
|
||||
|
||||
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.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");
|
||||
is1_20_2OrAbove = true;
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
}
|
||||
IS_1_20_2_OR_ABOVE = is1_20_2OrAbove;
|
||||
|
||||
boolean is1_20_3OrAbove = false;
|
||||
try {
|
||||
Class.forName("net.minecraft.network.chat.numbers.NumberFormat");
|
||||
is1_20_3OrAbove = true;
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
}
|
||||
IS_1_20_3_OR_ABOVE = is1_20_3OrAbove;
|
||||
|
||||
boolean is1_20_5OrAbove = false;
|
||||
try {
|
||||
Class.forName("net.minecraft.network.protocol.common.ClientboundTransferPacket");
|
||||
is1_20_5OrAbove = true;
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
}
|
||||
IS_1_20_5_OR_ABOVE = is1_20_5OrAbove;
|
||||
|
||||
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;
|
||||
|
||||
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 MethodAccessor RESULT_UNWRAP_METHOD;
|
||||
public static final MethodAccessor CODEC_PARSE;
|
||||
public static final Object JSON_OPS;
|
||||
|
||||
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<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<?> 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;
|
||||
|
||||
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<?> RESET_SCORE_CONSTRUCTOR; // 1.20.3+
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern;
|
||||
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.ViaAPI;
|
||||
import com.viaversion.viaversion.api.protocol.version.ProtocolVersion;
|
||||
import net.megavex.scoreboardlibrary.implementation.commons.LineRenderingStrategy;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketAdapterProvider;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.objective.PaperObjectivePacketAdapter;
|
||||
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.ModernComponentProvider;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernPacketSender;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectivePacketAdapter;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamsPacketAdapter;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.WeakHashMap;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public final class PacketAdapterProviderImpl implements PacketAdapterProvider {
|
||||
private final ViaAPI<Player> via;
|
||||
private final ModernPacketSender packetSender;
|
||||
private final WeakHashMap<Player, Integer> viaTeamPacketIds = new WeakHashMap<>();
|
||||
|
||||
public PacketAdapterProviderImpl(Plugin plugin) {
|
||||
final String viaPlugin = "ViaVersion";
|
||||
boolean isViaEnabled = plugin.getServer().getPluginManager().isPluginEnabled(viaPlugin);
|
||||
boolean isViaAllowed = plugin.getDescription().getSoftDepend().contains(viaPlugin) || plugin.getDescription().getDepend().contains(viaPlugin);
|
||||
if (isViaEnabled && isViaAllowed) {
|
||||
//noinspection unchecked
|
||||
this.via = (ViaAPI<Player>) Via.getAPI();
|
||||
} else {
|
||||
this.via = null;
|
||||
}
|
||||
this.packetSender = new ModernPacketSender(this.via);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ObjectivePacketAdapter createObjectiveAdapter(@NotNull String objectiveName) {
|
||||
return ModernComponentProvider.IS_NATIVE_ADVENTURE
|
||||
? new PaperObjectivePacketAdapter(this, objectiveName)
|
||||
: new SpigotObjectivePacketAdapter(this, objectiveName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TeamsPacketAdapter createTeamPacketAdapter(@NotNull String 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;
|
||||
}
|
||||
|
||||
public @Nullable ViaAPI<Player> via() {
|
||||
return via;
|
||||
}
|
||||
|
||||
public @NotNull ModernPacketSender packetSender() {
|
||||
return packetSender;
|
||||
}
|
||||
|
||||
public @NotNull WeakHashMap<Player, Integer> viaTeamPacketIds() {
|
||||
return viaTeamPacketIds;
|
||||
}
|
||||
}
|
||||
+46
@@ -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;
|
||||
}
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.objective;
|
||||
|
||||
import net.megavex.scoreboardlibrary.api.objective.ObjectiveDisplaySlot;
|
||||
import net.megavex.scoreboardlibrary.api.objective.ObjectiveRenderType;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAdapterProviderImpl;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectiveConstants;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectivePacketAdapter;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public abstract class AbstractObjectivePacketAdapter implements ObjectivePacketAdapter {
|
||||
protected final PacketAdapterProviderImpl provider;
|
||||
protected final String objectiveName;
|
||||
private Object removePacket;
|
||||
|
||||
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) {
|
||||
provider.packetSender().sendPacket(players, createDisplayPacket(slot));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(@NotNull Collection<Player> players) {
|
||||
if (removePacket == null) {
|
||||
this.removePacket = PacketAccessors.OBJECTIVE_PACKET_CONSTRUCTOR.invoke();
|
||||
PacketAccessors.OBJECTIVE_NAME_FIELD.set(removePacket, objectiveName);
|
||||
PacketAccessors.OBJECTIVE_MODE_FIELD.set(removePacket, ObjectiveConstants.MODE_REMOVE);
|
||||
}
|
||||
provider.packetSender().sendPacket(players, removePacket);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeScore(@NotNull Collection<Player> players, @NotNull String entry) {
|
||||
Object packet;
|
||||
if (PacketAccessors.IS_1_20_3_OR_ABOVE) {
|
||||
packet = Objects.requireNonNull(PacketAccessors.RESET_SCORE_CONSTRUCTOR)
|
||||
.invoke(entry, objectiveName);
|
||||
} else {
|
||||
packet = Objects.requireNonNull(PacketAccessors.SCORE_CONSTRUCTOR)
|
||||
.invoke(PacketAccessors.SCORE_1_20_2_METHOD_REMOVE, objectiveName, entry, 0);
|
||||
}
|
||||
provider.packetSender().sendPacket(players, packet);
|
||||
}
|
||||
|
||||
protected @NotNull Object createDisplayPacket(@NotNull ObjectiveDisplaySlot displaySlot) {
|
||||
Object packet;
|
||||
if (PacketAccessors.IS_1_20_2_OR_ABOVE) {
|
||||
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 Object createScorePacket(
|
||||
@NotNull String entry,
|
||||
int value,
|
||||
@Nullable Object nmsDisplay,
|
||||
@Nullable Object numberFormat
|
||||
) {
|
||||
if (PacketAccessors.IS_1_20_5_OR_ABOVE) {
|
||||
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_CONSTRUCTOR)
|
||||
.invoke(entry, objectiveName, value, nmsDisplay, numberFormat);
|
||||
} else {
|
||||
return Objects.requireNonNull(PacketAccessors.SCORE_CONSTRUCTOR)
|
||||
.invoke(PacketAccessors.SCORE_1_20_2_METHOD_CHANGE, objectiveName, entry, value);
|
||||
}
|
||||
}
|
||||
|
||||
protected @NotNull Object createObjectivePacket(
|
||||
@NotNull PropertiesPacketType packetType,
|
||||
@NotNull Object nmsValue,
|
||||
@NotNull ObjectiveRenderType renderType,
|
||||
@Nullable Object numberFormat
|
||||
) {
|
||||
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);
|
||||
|
||||
if (PacketAccessors.IS_1_20_3_OR_ABOVE) {
|
||||
assert PacketAccessors.OBJECTIVE_NUMBER_FORMAT_FIELD != null;
|
||||
Object value;
|
||||
if (PacketAccessors.IS_1_20_5_OR_ABOVE) {
|
||||
value = Optional.ofNullable(numberFormat);
|
||||
} else {
|
||||
value = numberFormat;
|
||||
}
|
||||
PacketAccessors.OBJECTIVE_NUMBER_FORMAT_FIELD.set(packet, value);
|
||||
}
|
||||
|
||||
Object nmsRenderType;
|
||||
switch (renderType) {
|
||||
case INTEGER:
|
||||
nmsRenderType = PacketAccessors.RENDER_TYPE_INTEGER;
|
||||
break;
|
||||
case HEARTS:
|
||||
nmsRenderType = PacketAccessors.RENDER_TYPE_HEARTS;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("unknown render type " + renderType);
|
||||
}
|
||||
|
||||
PacketAccessors.OBJECTIVE_RENDER_TYPE_FIELD.set(packet, nmsRenderType);
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
+13
-16
@@ -1,32 +1,29 @@
|
||||
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;
|
||||
|
||||
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 final class PaperObjectivePacketAdapter extends AbstractObjectivePacketAdapter {
|
||||
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);
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.objective;
|
||||
|
||||
import net.megavex.scoreboardlibrary.api.objective.ScoreFormat;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.RelocatedGson;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.ModernComponentProvider;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ConstructorAccessor;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ReflectUtil;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
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 Object STYLE_CODEC;
|
||||
private static final Object BLANK;
|
||||
private static final ConstructorAccessor<?> STYLED_CONSTRUCTOR;
|
||||
private static final ConstructorAccessor<?> FIXED_CONSTRUCTOR;
|
||||
|
||||
static {
|
||||
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(@Nullable Locale locale, @Nullable ScoreFormat format) {
|
||||
if (format == null || !PacketAccessors.IS_1_20_3_OR_ABOVE) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (format == ScoreFormat.blank()) {
|
||||
return BLANK;
|
||||
} else if (format instanceof ScoreFormat.Styled) {
|
||||
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 FIXED_CONSTRUCTOR.invoke(ModernComponentProvider.fromAdventure(((ScoreFormat.Fixed) format).content(), locale));
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid score format: " + format);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
-10
@@ -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;
|
||||
@@ -13,16 +13,16 @@ 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 final class SpigotObjectivePacketAdapter extends AbstractObjectivePacketAdapter {
|
||||
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);
|
||||
}
|
||||
);
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.team;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.ImmutableTeamProperties;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.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 org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
|
||||
public abstract class AbstractTeamsPacketAdapterImpl implements TeamsPacketAdapter {
|
||||
protected final PacketAdapterProviderImpl provider;
|
||||
protected final String teamName;
|
||||
private Object removePacket;
|
||||
|
||||
public AbstractTeamsPacketAdapterImpl(@NotNull PacketAdapterProviderImpl provider, @NotNull String teamName) {
|
||||
this.provider = provider;
|
||||
this.teamName = teamName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TeamDisplayPacketAdapter createLegacyTeamDisplayAdapter(@NotNull ImmutableTeamProperties<String> properties) {
|
||||
return new LegacyTeamDisplayPacketAdapter(provider, teamName, properties);
|
||||
}
|
||||
|
||||
public abstract class TeamDisplayPacketAdapterImpl implements TeamDisplayPacketAdapter {
|
||||
protected final ImmutableTeamProperties<Component> properties;
|
||||
|
||||
public TeamDisplayPacketAdapterImpl(ImmutableTeamProperties<Component> properties) {
|
||||
this.properties = properties;
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
|
||||
@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;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
+162
@@ -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;
|
||||
}
|
||||
}
|
||||
+106
@@ -0,0 +1,106 @@
|
||||
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.PropertiesPacketType;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAdapterProviderImpl;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamConstants;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamDisplayPacketAdapter;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.UnknownNullability;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
|
||||
public final class PaperTeamsPacketAdapterImpl extends AbstractTeamsPacketAdapterImpl {
|
||||
public PaperTeamsPacketAdapterImpl(PacketAdapterProviderImpl provider, @NotNull String teamName) {
|
||||
super(provider, teamName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TeamDisplayPacketAdapter createTeamDisplayAdapter(@NotNull ImmutableTeamProperties<Component> properties) {
|
||||
return new TeamDisplayPacketAdapterImpl(properties);
|
||||
}
|
||||
|
||||
private class TeamDisplayPacketAdapterImpl extends AbstractTeamsPacketAdapterImpl.TeamDisplayPacketAdapterImpl {
|
||||
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() {
|
||||
if (parameters != null) {
|
||||
fillParameters(parameters, null);
|
||||
}
|
||||
createPacket = null;
|
||||
updatePacket = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendProperties(@NotNull PropertiesPacketType packetType, @NotNull Collection<Player> players) {
|
||||
if (createPacket == null || updatePacket == null) {
|
||||
Collection<String> entries = ImmutableList.copyOf(properties.syncedEntries());
|
||||
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:
|
||||
provider.packetSender().sendPacket(players, createPacket);
|
||||
break;
|
||||
case UPDATE:
|
||||
provider.packetSender().sendPacket(players, updatePacket);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void fillParameters(@NotNull Object parameters, @UnknownNullability Locale locale) {
|
||||
super.fillParameters(parameters, locale);
|
||||
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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
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.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.util.LocalePacketUtil;
|
||||
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 final class SpigotTeamsPacketAdapter extends AbstractTeamsPacketAdapterImpl {
|
||||
public SpigotTeamsPacketAdapter(PacketAdapterProviderImpl provider, @NotNull String teamName) {
|
||||
super(provider, teamName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TeamDisplayPacketAdapter createTeamDisplayAdapter(@NotNull ImmutableTeamProperties<Component> properties) {
|
||||
return new TeamDisplayPacketAdapterImpl(properties);
|
||||
}
|
||||
|
||||
private class TeamDisplayPacketAdapterImpl extends AbstractTeamsPacketAdapterImpl.TeamDisplayPacketAdapterImpl {
|
||||
public TeamDisplayPacketAdapterImpl(ImmutableTeamProperties<Component> properties) {
|
||||
super(properties);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendProperties(@NotNull PropertiesPacketType packetType, @NotNull Collection<Player> players) {
|
||||
Collection<String> entries = ImmutableList.copyOf(properties.syncedEntries());
|
||||
LocalePacketUtil.sendLocalePackets(
|
||||
provider.packetSender(),
|
||||
players,
|
||||
locale -> {
|
||||
|
||||
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(@NotNull Object parameters, @UnknownNullability Locale locale) {
|
||||
super.fillParameters(parameters, 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
+117
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+106
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -28,11 +28,15 @@ public final class ObjectiveConstants {
|
||||
}
|
||||
|
||||
public static int displaySlotIndex(@NotNull ObjectiveDisplaySlot slot) {
|
||||
return displaySlotIndex(slot, true);
|
||||
}
|
||||
|
||||
public static int displaySlotIndex(@NotNull ObjectiveDisplaySlot slot, boolean isAbove1_8) {
|
||||
if (slot instanceof ObjectiveDisplaySlot.PlayerList) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (slot instanceof ObjectiveDisplaySlot.Sidebar) {
|
||||
if (slot instanceof ObjectiveDisplaySlot.Sidebar || (!isAbove1_8 && slot instanceof ObjectiveDisplaySlot.TeamSidebar)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
+2
@@ -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(
|
||||
File renamed without changes.
+2
-1
@@ -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,
|
||||
+3
-1
@@ -7,7 +7,9 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.util.Collection;
|
||||
|
||||
public interface TeamDisplayPacketAdapter {
|
||||
default void updateTeamPackets(@NotNull Collection<String> entries) {
|
||||
void removeTeam(@NotNull Iterable<Player> players);
|
||||
|
||||
default void updateTeamPackets() {
|
||||
}
|
||||
|
||||
void sendEntries(@NotNull EntriesPacketType packetType, @NotNull Collection<Player> players, @NotNull Collection<String> entries);
|
||||
-2
@@ -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) {
|
||||
+2
-2
@@ -1,12 +1,12 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.packetAdapter.util;
|
||||
|
||||
import net.megavex.scoreboardlibrary.implementation.commons.CollectionProvider;
|
||||
import net.megavex.scoreboardlibrary.implementation.commons.LocaleProvider;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.Function;
|
||||
@@ -31,7 +31,7 @@ public final class LocalePacketUtil {
|
||||
return;
|
||||
}
|
||||
|
||||
Map<Locale, P> map = CollectionProvider.map(1);
|
||||
Map<Locale, P> map = new HashMap<>(1);
|
||||
for (Player player : players) {
|
||||
Locale locale = LocaleProvider.locale(player);
|
||||
P packet = map.computeIfAbsent(locale, i -> packetFunction.apply(locale));
|
||||
+1
-1
@@ -5,7 +5,7 @@ import org.jetbrains.annotations.UnknownNullability;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
|
||||
public class ConstructorAccessor<T> {
|
||||
public final class ConstructorAccessor<T> {
|
||||
private final MethodHandle handle;
|
||||
|
||||
public ConstructorAccessor(@NotNull MethodHandle handle) {
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
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 final class FieldAccessor<T, V> {
|
||||
private final MethodHandle getter, setter;
|
||||
|
||||
public FieldAccessor(@NotNull MethodHandle getter, @Nullable MethodHandle setter) {
|
||||
this.getter = getter;
|
||||
this.setter = setter;
|
||||
}
|
||||
|
||||
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) {
|
||||
throw new IllegalStateException("couldn't set value of field", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+6
-6
@@ -5,16 +5,16 @@ import org.jetbrains.annotations.UnknownNullability;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
|
||||
public class FieldAccessor<T, V> {
|
||||
private final MethodHandle setter;
|
||||
public final class MethodAccessor {
|
||||
private final MethodHandle handle;
|
||||
|
||||
public FieldAccessor(@NotNull MethodHandle setter) {
|
||||
this.setter = setter;
|
||||
public MethodAccessor(@NotNull MethodHandle handle) {
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
public void set(@NotNull T instance, @UnknownNullability V value) {
|
||||
public Object invoke(@UnknownNullability Object... args) {
|
||||
try {
|
||||
setter.invokeExact(instance, value);
|
||||
return handle.invokeExact(args);
|
||||
} catch (Throwable e) {
|
||||
throw new IllegalStateException("couldn't set value of field", e);
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect;
|
||||
|
||||
import org.bukkit.Bukkit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public final class MinecraftClasses {
|
||||
private static final String CB_PACKAGE = Bukkit.getServer().getClass().getPackage().getName();
|
||||
|
||||
private MinecraftClasses() {
|
||||
}
|
||||
|
||||
public static @NotNull String craftBukkit(String className) {
|
||||
return CB_PACKAGE + '.' + className;
|
||||
}
|
||||
}
|
||||
File renamed without changes.
+88
-19
@@ -7,9 +7,9 @@ import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Arrays;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
public class ReflectUtil {
|
||||
public final class ReflectUtil {
|
||||
// Inspired by
|
||||
// https://github.com/dmulloy2/ProtocolLib/blob/02e917cd08cf5b37a52052e22b223272a040e0df/src/main/java/com/comphenix/protocol/reflect/accessors/MethodHandleHelper.java
|
||||
|
||||
@@ -54,34 +54,66 @@ public class ReflectUtil {
|
||||
private ReflectUtil() {
|
||||
}
|
||||
|
||||
public static <T, V> @NotNull FieldAccessor<T, V> findField(@NotNull Class<T> clazz, @NotNull String name, @NotNull Class<V> valueClass) {
|
||||
return findField(clazz, new String[]{name}, valueClass);
|
||||
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 <T, V> @NotNull FieldAccessor<T, V> findField(@NotNull Class<T> clazz, @NotNull String[] names, @NotNull Class<V> valueClass) {
|
||||
public static @Nullable Class<?> getOptionalClass(String... names) {
|
||||
for (String name : names) {
|
||||
if (name == null) continue;
|
||||
try {
|
||||
MethodHandle setter = LOOKUP.findSetter(clazz, name, valueClass).asType(VIRTUAL_FIELD_SETTER);
|
||||
return new FieldAccessor<>(setter);
|
||||
} catch (NoSuchFieldException ignored) {
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalStateException("couldn't get field accessor", e);
|
||||
return Class.forName(name);
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
throw new IllegalStateException("couldn't find field on class " + clazz.getSimpleName() + " with names " + Arrays.toString(names));
|
||||
return null;
|
||||
}
|
||||
|
||||
public static <T> @NotNull ConstructorAccessor<T> findConstructorOrThrow(@NotNull Class<T> clazz, @NotNull Class<?>... args) {
|
||||
ConstructorAccessor<T> accessor = findConstructor(clazz, args);
|
||||
if (accessor == null) {
|
||||
throw new IllegalStateException("couldn't get constructor accessor for class " + clazz.getSimpleName());
|
||||
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()) != isStatic || field.getType() != valueClass) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i == index) {
|
||||
try {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
return accessor;
|
||||
throw new IllegalStateException(
|
||||
"couldn't find field with class " + valueClass.getSimpleName() + " on " + clazz.getSimpleName() + " at index " + index
|
||||
);
|
||||
}
|
||||
|
||||
public static <T> @Nullable ConstructorAccessor<T> findConstructor(@NotNull Class<T> clazz, @NotNull Class<?>... args) {
|
||||
public static <T> @Nullable ConstructorAccessor<T> findOptionalConstructor(@NotNull Class<T> clazz, @NotNull Class<?>... args) {
|
||||
try {
|
||||
MethodHandle handle = LOOKUP.findConstructor(clazz, MethodType.methodType(void.class, args));
|
||||
return new ConstructorAccessor<>(convertToGeneric(handle));
|
||||
@@ -90,7 +122,15 @@ public class ReflectUtil {
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> @NotNull PacketConstructor<T> findEmptyConstructor(@NotNull Class<T> packetClass) {
|
||||
public static <T> @NotNull ConstructorAccessor<T> findConstructor(@NotNull Class<T> clazz, @NotNull Class<?>... args) {
|
||||
ConstructorAccessor<T> accessor = findOptionalConstructor(clazz, args);
|
||||
if (accessor == null) {
|
||||
throw new RuntimeException("Constructor for class " + clazz.getName() + " not found");
|
||||
}
|
||||
return accessor;
|
||||
}
|
||||
|
||||
public static <T> @NotNull PacketConstructor<T> getEmptyConstructor(@NotNull Class<T> packetClass) {
|
||||
try {
|
||||
MethodHandle constructor = LOOKUP.findConstructor(packetClass, VOID_METHOD_TYPE);
|
||||
return () -> {
|
||||
@@ -114,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);
|
||||
+41
-35
@@ -1,60 +1,66 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.player;
|
||||
|
||||
import net.megavex.scoreboardlibrary.implementation.commons.CollectionProvider;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class DisplayableQueue<T extends PlayerDisplayable> {
|
||||
public final class DisplayableQueue<T extends PlayerDisplayable> {
|
||||
private final UUID playerUuid;
|
||||
private final List<T> queue = CollectionProvider.list(1);
|
||||
private final List<T> queue = new ArrayList<>(1);
|
||||
|
||||
public DisplayableQueue(@NotNull UUID playerUuid) {
|
||||
this.playerUuid = playerUuid;
|
||||
}
|
||||
|
||||
public synchronized @Nullable T current() {
|
||||
if (queue.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
return queue.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void add(@NotNull T displayable) {
|
||||
if (queue.contains(displayable)) {
|
||||
throw new IllegalStateException("displayable already registered");
|
||||
}
|
||||
|
||||
queue.add(displayable);
|
||||
|
||||
if (current() == displayable) {
|
||||
Player player = Bukkit.getPlayer(playerUuid);
|
||||
if (player != null) {
|
||||
displayable.display(player);
|
||||
public @Nullable T current() {
|
||||
synchronized (this) {
|
||||
if (queue.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
return queue.get(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void remove(@NotNull T displayable) {
|
||||
boolean wasCurrent = displayable == current();
|
||||
if (!queue.remove(displayable)) {
|
||||
throw new IllegalStateException("displayable not registered");
|
||||
}
|
||||
public void add(@NotNull T displayable) {
|
||||
synchronized (this) {
|
||||
if (queue.contains(displayable)) {
|
||||
throw new IllegalStateException("displayable already registered");
|
||||
}
|
||||
|
||||
if (!wasCurrent) {
|
||||
return;
|
||||
}
|
||||
queue.add(displayable);
|
||||
|
||||
T newDisplayable = current();
|
||||
if (newDisplayable != null) {
|
||||
Player player = Bukkit.getPlayer(playerUuid);
|
||||
if (player != null) {
|
||||
newDisplayable.display(player);
|
||||
if (current() == displayable) {
|
||||
Player player = Bukkit.getPlayer(playerUuid);
|
||||
if (player != null) {
|
||||
displayable.display(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void remove(@NotNull T displayable) {
|
||||
synchronized (this) {
|
||||
boolean wasCurrent = displayable == current();
|
||||
if (!queue.remove(displayable)) {
|
||||
throw new IllegalStateException("displayable not registered");
|
||||
}
|
||||
|
||||
if (!wasCurrent) {
|
||||
return;
|
||||
}
|
||||
|
||||
T newDisplayable = current();
|
||||
if (newDisplayable != null) {
|
||||
Player player = Bukkit.getPlayer(playerUuid);
|
||||
if (player != null) {
|
||||
newDisplayable.display(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@ import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerLocaleChangeEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class LocaleListener implements Listener {
|
||||
public final class LocaleListener implements Listener {
|
||||
private final ScoreboardLibraryImpl scoreboardLibrary;
|
||||
|
||||
public LocaleListener(@NotNull ScoreboardLibraryImpl scoreboardLibrary) {
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class ScoreboardLibraryPlayer {
|
||||
public final class ScoreboardLibraryPlayer {
|
||||
private final DisplayableQueue<TeamManagerImpl> teamManagerQueue;
|
||||
private final DisplayableQueue<ObjectiveManagerImpl> objectiveManagerQueue;
|
||||
private final DisplayableQueue<AbstractSidebar> sidebarQueue;
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@ package net.megavex.scoreboardlibrary.implementation.scheduler;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
class BukkitTaskScheduler implements TaskScheduler {
|
||||
final class BukkitTaskScheduler implements TaskScheduler {
|
||||
private final Plugin plugin;
|
||||
|
||||
public BukkitTaskScheduler(@NotNull Plugin plugin) {
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ import java.lang.invoke.MethodType;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
class FoliaTaskScheduler implements TaskScheduler {
|
||||
final class FoliaTaskScheduler implements TaskScheduler {
|
||||
private static final Class<?> asyncSchedulerClass;
|
||||
private static final Class<?> scheduledTaskClass;
|
||||
private static final Class<?> cancelledStateClass;
|
||||
|
||||
+44
-18
@@ -1,53 +1,48 @@
|
||||
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.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.api.sidebar.Sidebar;
|
||||
import net.megavex.scoreboardlibrary.implementation.ScoreboardLibraryImpl;
|
||||
import net.megavex.scoreboardlibrary.implementation.commons.CollectionProvider;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectivePacketAdapter;
|
||||
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 net.megavex.scoreboardlibrary.implementation.sidebar.line.PlayerNameProvider;
|
||||
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 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 static net.kyori.adventure.text.Component.empty;
|
||||
|
||||
public abstract class AbstractSidebar implements Sidebar, PlayerDisplayable {
|
||||
private final ScoreboardLibraryImpl scoreboardLibrary;
|
||||
private final ObjectivePacketAdapter packetAdapter;
|
||||
private final List<String> linePlayerNames;
|
||||
|
||||
private final GlobalLineInfo[] lines;
|
||||
private Component title = empty();
|
||||
|
||||
private final Set<Player> players = CollectionProvider.set(8);
|
||||
private final Set<Player> players = new HashSet<>(8);
|
||||
|
||||
private boolean closed;
|
||||
|
||||
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 = UUID.randomUUID().toString().substring(0, 5);
|
||||
this.packetAdapter = scoreboardLibrary.packetAdapter().createObjectiveAdapter(objectiveName);
|
||||
this.linePlayerNames = PlayerNameProvider.provideLinePlayerNames(maxLines);
|
||||
this.lines = new GlobalLineInfo[maxLines];
|
||||
}
|
||||
|
||||
@@ -108,6 +103,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();
|
||||
@@ -125,6 +125,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();
|
||||
@@ -150,6 +161,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;
|
||||
}
|
||||
@@ -168,12 +185,13 @@ public abstract class AbstractSidebar implements Sidebar, PlayerDisplayable {
|
||||
|
||||
@Override
|
||||
public final void display(@NotNull Player player) {
|
||||
packetAdapter.sendProperties(players, PropertiesPacketType.CREATE, title, ObjectiveRenderType.INTEGER, ScoreFormat.blank());
|
||||
Collection<Player> singleton = Collections.singleton(player);
|
||||
packetAdapter.sendProperties(singleton, PropertiesPacketType.CREATE, title, ObjectiveRenderType.INTEGER, ScoreFormat.blank());
|
||||
|
||||
LocaleLineHandler lineHandler = Objects.requireNonNull(addPlayer0(player));
|
||||
lineHandler.addPlayer(player);
|
||||
lineHandler.show(player);
|
||||
packetAdapter.display(Collections.singleton(player), ObjectiveDisplaySlot.sidebar());
|
||||
packetAdapter.display(singleton, ObjectiveDisplaySlot.sidebar());
|
||||
}
|
||||
|
||||
public final boolean tick() {
|
||||
@@ -234,6 +252,11 @@ public abstract class AbstractSidebar implements Sidebar, PlayerDisplayable {
|
||||
});
|
||||
} else if (task instanceof SidebarTask.UpdateScores) {
|
||||
forEachLineHandler(LocaleLineHandler::updateScores);
|
||||
for (GlobalLineInfo line : lines) {
|
||||
if (line != null) {
|
||||
line.updateScore(false);
|
||||
}
|
||||
}
|
||||
} else if (task instanceof SidebarTask.UpdateTitle) {
|
||||
packetAdapter.sendProperties(internalPlayers(), PropertiesPacketType.UPDATE, title, ObjectiveRenderType.INTEGER, ScoreFormat.blank());
|
||||
}
|
||||
@@ -273,7 +296,10 @@ public abstract class AbstractSidebar implements Sidebar, PlayerDisplayable {
|
||||
private @NotNull GlobalLineInfo getLineInfo(int line) {
|
||||
GlobalLineInfo globalLineInfo = lines[line];
|
||||
if (globalLineInfo == null) {
|
||||
globalLineInfo = lines[line] = new GlobalLineInfo(this, linePlayerNames.get(line), line);
|
||||
char c = Character.toChars(128 + line)[0];
|
||||
String playerName = "" + LegacyComponentSerializer.SECTION_CHAR + c;
|
||||
|
||||
globalLineInfo = lines[line] = new GlobalLineInfo(this, playerName, line);
|
||||
updateScores();
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -13,12 +13,12 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class PlayerDependantLocaleSidebar extends AbstractSidebar {
|
||||
public final 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
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import java.util.Iterator;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class SidebarUpdaterTask implements Runnable {
|
||||
public final class SidebarUpdaterTask implements Runnable {
|
||||
private final ScoreboardLibraryImpl scoreboardLibrary;
|
||||
private final RunningTask task;
|
||||
private final Object lock = new Object();
|
||||
|
||||
+6
-9
@@ -1,26 +1,25 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.sidebar;
|
||||
|
||||
import net.megavex.scoreboardlibrary.implementation.ScoreboardLibraryImpl;
|
||||
import net.megavex.scoreboardlibrary.implementation.commons.CollectionProvider;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.line.LocaleLineHandler;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class SingleLocaleSidebar extends AbstractSidebar {
|
||||
public final class SingleLocaleSidebar extends AbstractSidebar {
|
||||
private final Locale locale;
|
||||
private final LocaleLineHandler sidebar;
|
||||
private final Set<Player> internalPlayers;
|
||||
private final Set<Player> internalPlayers = new HashSet<>(8);
|
||||
|
||||
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);
|
||||
this.sidebar = new LocaleLineHandler(this, locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -36,14 +35,12 @@ public class SingleLocaleSidebar extends AbstractSidebar {
|
||||
@Override
|
||||
protected @Nullable LocaleLineHandler addPlayer0(@NotNull Player player) {
|
||||
if (!internalPlayers.add(player)) return null;
|
||||
|
||||
return sidebar;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable LocaleLineHandler removePlayer0(@NotNull Player player) {
|
||||
if (!internalPlayers.remove(player)) return null;
|
||||
|
||||
return sidebar;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import net.megavex.scoreboardlibrary.implementation.sidebar.AbstractSidebar;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class GlobalLineInfo {
|
||||
public final class GlobalLineInfo {
|
||||
private final String player;
|
||||
private final int line;
|
||||
private final TeamsPacketAdapter packetAdapter;
|
||||
|
||||
+50
-63
@@ -1,122 +1,109 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.sidebar.line;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.megavex.scoreboardlibrary.implementation.commons.LineRenderingStrategy;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.AbstractSidebar;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.line.locale.LineType;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
|
||||
public class LocaleLineHandler {
|
||||
public final class LocaleLineHandler {
|
||||
private final AbstractSidebar sidebar;
|
||||
private final Locale locale;
|
||||
private SidebarLineHandler modernLineHandler, legacyLineHandler;
|
||||
private final SidebarLineHandler[] lineHandlers;
|
||||
|
||||
public LocaleLineHandler(AbstractSidebar sidebar, Locale locale) {
|
||||
public LocaleLineHandler(@NotNull AbstractSidebar sidebar, @NotNull Locale locale) {
|
||||
this.sidebar = sidebar;
|
||||
this.locale = locale;
|
||||
this.lineHandlers = new SidebarLineHandler[LineRenderingStrategy.values().length];
|
||||
}
|
||||
|
||||
public AbstractSidebar sidebar() {
|
||||
public @NotNull AbstractSidebar sidebar() {
|
||||
return sidebar;
|
||||
}
|
||||
|
||||
public Locale locale() {
|
||||
public @NotNull Locale locale() {
|
||||
return locale;
|
||||
}
|
||||
|
||||
public boolean hasPlayers() {
|
||||
return (modernLineHandler != null && !modernLineHandler.players().isEmpty()) || (legacyLineHandler != null && !legacyLineHandler.players().isEmpty());
|
||||
for (SidebarLineHandler lineHandler : lineHandlers) {
|
||||
if (lineHandler != null && !lineHandler.players().isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addPlayer(@NotNull Player player) {
|
||||
boolean isLegacy = sidebar.scoreboardLibrary().packetAdapter().isLegacy(player);
|
||||
LineType lineType = isLegacy ? LineType.LEGACY : LineType.MODERN;
|
||||
lineHandler(lineType).players().add(player);
|
||||
LineRenderingStrategy strategy = sidebar.scoreboardLibrary().packetAdapter().lineRenderingStrategy(player);
|
||||
lineHandler(strategy).players().add(player);
|
||||
}
|
||||
|
||||
public void removePlayer(@NotNull Player player) {
|
||||
if (modernLineHandler != null && modernLineHandler.players().remove(player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (legacyLineHandler != null) {
|
||||
legacyLineHandler.players().remove(player);
|
||||
for (SidebarLineHandler lineHandler : lineHandlers) {
|
||||
if (lineHandler != null && lineHandler.players().remove(player)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public @NotNull SidebarLineHandler lineHandler(@NotNull LineType lineType) {
|
||||
switch (lineType) {
|
||||
case MODERN: {
|
||||
if (modernLineHandler == null) {
|
||||
modernLineHandler = new SidebarLineHandler(LineType.MODERN, this);
|
||||
}
|
||||
|
||||
return modernLineHandler;
|
||||
}
|
||||
case LEGACY: {
|
||||
if (legacyLineHandler == null) {
|
||||
legacyLineHandler = new SidebarLineHandler(LineType.LEGACY, this);
|
||||
}
|
||||
|
||||
return legacyLineHandler;
|
||||
}
|
||||
public @NotNull SidebarLineHandler lineHandler(@NotNull LineRenderingStrategy lineType) {
|
||||
SidebarLineHandler lineHandler = lineHandlers[lineType.ordinal()];
|
||||
if (lineHandler == null) {
|
||||
lineHandler = lineHandlers[lineType.ordinal()] = new SidebarLineHandler(lineType, this);
|
||||
}
|
||||
|
||||
throw new RuntimeException();
|
||||
return lineHandler;
|
||||
}
|
||||
|
||||
public void updateScoreFormat(int lineIndex) {
|
||||
modernLineHandler.updateScore(lineIndex);
|
||||
SidebarLineHandler lineHandler = lineHandlers[LineRenderingStrategy.MODERN.ordinal()];
|
||||
if (lineHandler != null) {
|
||||
lineHandler.updateScore(lineIndex);
|
||||
}
|
||||
}
|
||||
|
||||
public void updateLine(int lineIndex, Component renderedValue) {
|
||||
if (modernLineHandler != null) {
|
||||
modernLineHandler.setLine(lineIndex, renderedValue);
|
||||
modernLineHandler.updateLine(lineIndex);
|
||||
}
|
||||
|
||||
if (legacyLineHandler != null) {
|
||||
legacyLineHandler.setLine(lineIndex, renderedValue);
|
||||
legacyLineHandler.updateLine(lineIndex);
|
||||
for (SidebarLineHandler lineHandler : lineHandlers) {
|
||||
if (lineHandler != null) {
|
||||
lineHandler.setLine(lineIndex, renderedValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updateScores() {
|
||||
if (modernLineHandler != null) {
|
||||
modernLineHandler.updateScores();
|
||||
}
|
||||
|
||||
if (legacyLineHandler != null) {
|
||||
legacyLineHandler.updateScores();
|
||||
for (SidebarLineHandler lineHandler : lineHandlers) {
|
||||
if (lineHandler != null) {
|
||||
lineHandler.updateScores();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void show(Player player) {
|
||||
if (modernLineHandler != null && modernLineHandler.players().contains(player)) {
|
||||
modernLineHandler.show(Collections.singleton(player));
|
||||
} else if (legacyLineHandler != null && legacyLineHandler.players().contains(player)) {
|
||||
legacyLineHandler.show(Collections.singleton(player));
|
||||
for (SidebarLineHandler lineHandler : lineHandlers) {
|
||||
if (lineHandler != null && lineHandler.players().contains(player)) {
|
||||
lineHandler.show(Collections.singleton(player));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void hide(Player player) {
|
||||
if (modernLineHandler != null && modernLineHandler.players().contains(player)) {
|
||||
modernLineHandler.hide(Collections.singleton(player));
|
||||
} else if (legacyLineHandler != null && legacyLineHandler.players().contains(player)) {
|
||||
legacyLineHandler.hide(Collections.singleton(player));
|
||||
for (SidebarLineHandler lineHandler : lineHandlers) {
|
||||
if (lineHandler != null && lineHandler.players().contains(player)) {
|
||||
lineHandler.hide(Collections.singleton(player));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
if (modernLineHandler != null) {
|
||||
modernLineHandler.hide(modernLineHandler.players());
|
||||
}
|
||||
|
||||
if (legacyLineHandler != null) {
|
||||
legacyLineHandler.hide(legacyLineHandler.players());
|
||||
for (SidebarLineHandler lineHandler : lineHandlers) {
|
||||
if (lineHandler != null) {
|
||||
lineHandler.hide(lineHandler.players());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
-41
@@ -1,41 +0,0 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.sidebar.line;
|
||||
|
||||
import net.megavex.scoreboardlibrary.api.sidebar.Sidebar;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class PlayerNameProvider {
|
||||
private static final ChatColor[] CHAT_COLORS = ChatColor.values();
|
||||
private static final List<String> DEFAULT_NAMES = createLinePlayerNames(Sidebar.MAX_LINES);
|
||||
|
||||
private PlayerNameProvider() {
|
||||
}
|
||||
|
||||
public static @NotNull @Unmodifiable List<String> provideLinePlayerNames(int maxLines) {
|
||||
return maxLines <= Sidebar.MAX_LINES ? DEFAULT_NAMES : createLinePlayerNames(maxLines);
|
||||
}
|
||||
|
||||
private static @NotNull @Unmodifiable List<String> createLinePlayerNames(int maxLines) {
|
||||
List<String> result = new ArrayList<>(maxLines);
|
||||
|
||||
int i = 0;
|
||||
while (result.size() < maxLines) {
|
||||
for (ChatColor color : CHAT_COLORS) {
|
||||
String legacy = color.toString();
|
||||
String newStr = i == 0 ? legacy : result.get(i - 1) + legacy;
|
||||
|
||||
if (!result.contains(newStr)) {
|
||||
result.add(newStr);
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(result);
|
||||
}
|
||||
}
|
||||
+41
-34
@@ -2,23 +2,26 @@ package net.megavex.scoreboardlibrary.implementation.sidebar.line;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.translation.GlobalTranslator;
|
||||
import net.megavex.scoreboardlibrary.implementation.commons.CollectionProvider;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.line.locale.LineType;
|
||||
import net.megavex.scoreboardlibrary.implementation.commons.LineRenderingStrategy;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.line.locale.LegacyLocaleLine;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.line.locale.LocaleLine;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.line.locale.ModernLocaleLine;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.line.locale.PostModernLocaleLine;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class SidebarLineHandler {
|
||||
private final LineType lineType;
|
||||
public final class SidebarLineHandler {
|
||||
private final LineRenderingStrategy strategy;
|
||||
private final LocaleLineHandler localeLineHandler;
|
||||
private final Set<Player> players = CollectionProvider.set(1);
|
||||
private final LocaleLine<?>[] lines;
|
||||
private final Set<Player> players = new HashSet<>(1);
|
||||
private final LocaleLine[] lines;
|
||||
|
||||
public SidebarLineHandler(@NotNull LineType lineType, @NotNull LocaleLineHandler localeLineHandler) {
|
||||
this.lineType = lineType;
|
||||
public SidebarLineHandler(@NotNull LineRenderingStrategy strategy, @NotNull LocaleLineHandler localeLineHandler) {
|
||||
this.strategy = strategy;
|
||||
this.localeLineHandler = localeLineHandler;
|
||||
this.lines = new LocaleLine[localeLineHandler.sidebar().maxLines()];
|
||||
|
||||
@@ -26,7 +29,7 @@ public class SidebarLineHandler {
|
||||
if (line != null) {
|
||||
Component value = line.value();
|
||||
if (value != null) {
|
||||
setLine(line.line(), GlobalTranslator.render(value, localeLineHandler.locale()), false);
|
||||
setLine(line.line(), GlobalTranslator.render(value, localeLineHandler.locale()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -40,40 +43,30 @@ public class SidebarLineHandler {
|
||||
return players;
|
||||
}
|
||||
|
||||
public void updateLine(int lineIndex) {
|
||||
LocaleLine<?> line = lines[lineIndex];
|
||||
if (line != null) {
|
||||
line.updateTeam();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateScores() {
|
||||
for (LocaleLine<?> line : lines) {
|
||||
for (LocaleLine line : lines) {
|
||||
if (line != null && line.info().updateScore()) {
|
||||
line.sendScore(players);
|
||||
line.info().updateScore(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updateScore(int line) {
|
||||
LocaleLine<?> localeLine = lines[line];
|
||||
localeLine.sendScore(players);
|
||||
LocaleLine localeLine = lines[line];
|
||||
if (localeLine != null) {
|
||||
localeLine.sendScore(players);
|
||||
}
|
||||
}
|
||||
|
||||
public void setLine(int line, Component renderedLine) {
|
||||
setLine(line, renderedLine, true);
|
||||
}
|
||||
|
||||
private void setLine(int line, Component renderedLine, boolean sendPackets) {
|
||||
LocaleLine<?> localeLine = lines[line];
|
||||
LocaleLine localeLine = lines[line];
|
||||
if (renderedLine == null && localeLine == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
boolean newlyCreated = false;
|
||||
if (localeLine == null) {
|
||||
lines[line] = localeLine = lineType.create(localeLineHandler.sidebar().lines()[line], this);
|
||||
lines[line] = localeLine = createLine(strategy, localeLineHandler.sidebar().lines()[line]);
|
||||
newlyCreated = true;
|
||||
}
|
||||
|
||||
@@ -83,17 +76,18 @@ public class SidebarLineHandler {
|
||||
localeLine.value(renderedLine);
|
||||
}
|
||||
|
||||
if (sendPackets) {
|
||||
if (renderedLine == null) {
|
||||
localeLine.hide(players);
|
||||
} else if (newlyCreated) {
|
||||
localeLine.show(players);
|
||||
}
|
||||
if (renderedLine == null) {
|
||||
localeLine.hide(players);
|
||||
} else if (newlyCreated) {
|
||||
localeLine.resetOldPlayer();
|
||||
localeLine.show(players);
|
||||
} else {
|
||||
localeLine.updateTeam();
|
||||
}
|
||||
}
|
||||
|
||||
public void show(Collection<Player> players) {
|
||||
for (LocaleLine<?> line : lines) {
|
||||
for (LocaleLine line : lines) {
|
||||
if (line != null) {
|
||||
line.show(players);
|
||||
}
|
||||
@@ -101,10 +95,23 @@ public class SidebarLineHandler {
|
||||
}
|
||||
|
||||
public void hide(Collection<Player> players) {
|
||||
for (LocaleLine<?> line : lines) {
|
||||
for (LocaleLine line : lines) {
|
||||
if (line != null) {
|
||||
line.hide(players);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private @NotNull LocaleLine createLine(@NotNull LineRenderingStrategy strategy, @NotNull GlobalLineInfo line) {
|
||||
switch (strategy) {
|
||||
case LEGACY:
|
||||
return new LegacyLocaleLine(line, this);
|
||||
case MODERN:
|
||||
return new ModernLocaleLine(line, this);
|
||||
case POST_MODERN:
|
||||
return new PostModernLocaleLine(line, this);
|
||||
default:
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
-21
@@ -1,7 +1,9 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.sidebar.line.locale;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.ImmutableTeamProperties;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectivePacketAdapter;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.EntriesPacketType;
|
||||
@@ -18,7 +20,8 @@ import java.util.Set;
|
||||
|
||||
import static net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection;
|
||||
|
||||
class LegacyLocaleLine implements LocaleLine<String> {
|
||||
// Implementation for versions below 1.13
|
||||
public final class LegacyLocaleLine implements LocaleLine, ImmutableTeamProperties<String> {
|
||||
private final GlobalLineInfo info;
|
||||
private final SidebarLineHandler handler;
|
||||
private final TeamDisplayPacketAdapter packetAdapter;
|
||||
@@ -31,7 +34,7 @@ class LegacyLocaleLine implements LocaleLine<String> {
|
||||
this.handler = handler;
|
||||
this.player = info.player();
|
||||
this.packetAdapter = info.packetAdapter().createLegacyTeamDisplayAdapter(this);
|
||||
packetAdapter.updateTeamPackets(entries());
|
||||
packetAdapter.updateTeamPackets();
|
||||
}
|
||||
|
||||
public @NotNull GlobalLineInfo info() {
|
||||
@@ -54,17 +57,16 @@ class LegacyLocaleLine implements LocaleLine<String> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<String> entries() {
|
||||
return Collections.singleton(player);
|
||||
public @NotNull Collection<String> syncedEntries() {
|
||||
return ImmutableList.of(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void value(@NotNull Component renderedComponent) {
|
||||
// I really need to rewrite this
|
||||
String legacyValue = legacySection().serialize(renderedComponent);
|
||||
|
||||
@NotNull String legacyValue = legacySection().serialize(renderedComponent);
|
||||
|
||||
oldPlayer = player;
|
||||
String prevPlayer = player;
|
||||
|
||||
if (legacyValue.length() <= 16) {
|
||||
this.prefix = legacyValue;
|
||||
@@ -80,7 +82,7 @@ class LegacyLocaleLine implements LocaleLine<String> {
|
||||
this.prefix = legacyValue.substring(0, prefixEnd);
|
||||
|
||||
String last = prefix + LegacyComponentSerializer.SECTION_CHAR + (endsWithSection ? legacyValue.charAt(16) : "");
|
||||
this.player = info.player() + ChatColor.RESET + ChatColor.getLastColors(last);
|
||||
this.player = info.player() + ChatColor.getLastColors(last);
|
||||
|
||||
int playerEnd = prefixEnd;
|
||||
if (legacyValue.length() > 32) {
|
||||
@@ -91,11 +93,10 @@ class LegacyLocaleLine implements LocaleLine<String> {
|
||||
player += legacyValue.substring(prefixEnd, playerEnd);
|
||||
}
|
||||
|
||||
this.suffix = legacyValue.substring(playerEnd + (endsWithSection ? 2 : 0));
|
||||
this.suffix = ChatColor.getLastColors(this.player) + legacyValue.substring(playerEnd + (endsWithSection ? 2 : 0));
|
||||
if (suffix.length() > 16) {
|
||||
String newSuffix = suffix.substring(0, 16);
|
||||
if (newSuffix.endsWith(String.valueOf(LegacyComponentSerializer.SECTION_CHAR)) &&
|
||||
ChatColor.getByChar(suffix.charAt(16)) != null) {
|
||||
if (newSuffix.endsWith(String.valueOf(LegacyComponentSerializer.SECTION_CHAR))) {
|
||||
newSuffix = newSuffix.substring(0, 15);
|
||||
}
|
||||
|
||||
@@ -105,8 +106,8 @@ class LegacyLocaleLine implements LocaleLine<String> {
|
||||
|
||||
currentValue = legacyValue;
|
||||
|
||||
if (oldPlayer.equals(player)) {
|
||||
oldPlayer = null;
|
||||
if (!player.equals(prevPlayer)) {
|
||||
oldPlayer = prevPlayer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -114,19 +115,24 @@ class LegacyLocaleLine implements LocaleLine<String> {
|
||||
public void updateTeam() {
|
||||
Set<Player> players = handler.players();
|
||||
if (oldPlayer != null) {
|
||||
ObjectivePacketAdapter pd = handler.localeLineHandler().sidebar().packetAdapter();
|
||||
|
||||
packetAdapter.sendEntries(EntriesPacketType.REMOVE, players, Collections.singleton(oldPlayer));
|
||||
handler.localeLineHandler().sidebar().packetAdapter().removeScore(players, oldPlayer);
|
||||
pd.removeScore(players, oldPlayer);
|
||||
oldPlayer = null;
|
||||
|
||||
Collection<String> entries = entries();
|
||||
packetAdapter.updateTeamPackets(entries);
|
||||
packetAdapter.sendEntries(EntriesPacketType.ADD, players, entries);
|
||||
handler.localeLineHandler().sidebar().packetAdapter().sendScore(players, player, info.objectiveScore(), null, null);
|
||||
packetAdapter.sendEntries(EntriesPacketType.ADD, players, Collections.singleton(player));
|
||||
pd.sendScore(players, player, info.objectiveScore(), null, null);
|
||||
}
|
||||
|
||||
packetAdapter.sendProperties(PropertiesPacketType.UPDATE, players);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resetOldPlayer() {
|
||||
oldPlayer = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendScore(@NotNull Collection<Player> players) {
|
||||
ObjectivePacketAdapter pd = handler.localeLineHandler().sidebar().packetAdapter();
|
||||
@@ -145,12 +151,13 @@ class LegacyLocaleLine implements LocaleLine<String> {
|
||||
|
||||
@Override
|
||||
public void hide(@NotNull Collection<Player> players) {
|
||||
ObjectivePacketAdapter pd = handler.localeLineHandler().sidebar().packetAdapter();
|
||||
if (oldPlayer != null) {
|
||||
handler.localeLineHandler().sidebar().packetAdapter().removeScore(players, oldPlayer);
|
||||
pd.removeScore(players, oldPlayer);
|
||||
}
|
||||
|
||||
handler.localeLineHandler().sidebar().packetAdapter().removeScore(players, player);
|
||||
info.packetAdapter().removeTeam(players);
|
||||
pd.removeScore(players, player);
|
||||
packetAdapter.removeTeam(players);
|
||||
}
|
||||
}
|
||||
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.sidebar.line.locale;
|
||||
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.line.GlobalLineInfo;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.line.SidebarLineHandler;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
|
||||
public enum LineType {
|
||||
MODERN(ModernLocaleLine::new),
|
||||
LEGACY(LegacyLocaleLine::new);
|
||||
|
||||
private final BiFunction<GlobalLineInfo, SidebarLineHandler, LocaleLine<?>> constructor;
|
||||
|
||||
LineType(BiFunction<GlobalLineInfo, SidebarLineHandler, LocaleLine<?>> constructor) {
|
||||
this.constructor = constructor;
|
||||
}
|
||||
|
||||
public LocaleLine<?> create(GlobalLineInfo line, SidebarLineHandler localeLineHandler) {
|
||||
return constructor.apply(line, localeLineHandler);
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -1,20 +1,22 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.sidebar.line.locale;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.ImmutableTeamProperties;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.line.GlobalLineInfo;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public interface LocaleLine<C> extends ImmutableTeamProperties<C> {
|
||||
public interface LocaleLine {
|
||||
@NotNull GlobalLineInfo info();
|
||||
|
||||
void value(@NotNull Component renderedComponent);
|
||||
|
||||
void updateTeam();
|
||||
|
||||
default void resetOldPlayer() {
|
||||
}
|
||||
|
||||
void sendScore(@NotNull Collection<Player> players);
|
||||
|
||||
void show(@NotNull Collection<Player> players);
|
||||
|
||||
+9
-8
@@ -1,6 +1,8 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.sidebar.line.locale;
|
||||
|
||||
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.PropertiesPacketType;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamDisplayPacketAdapter;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.line.GlobalLineInfo;
|
||||
@@ -9,12 +11,11 @@ import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import static net.kyori.adventure.text.Component.empty;
|
||||
|
||||
// Implementation for versions above 1.12.2
|
||||
class ModernLocaleLine implements LocaleLine<Component> {
|
||||
// Implementation for versions above 1.13
|
||||
public final class ModernLocaleLine implements LocaleLine, ImmutableTeamProperties<Component> {
|
||||
private final GlobalLineInfo info;
|
||||
private final SidebarLineHandler handler;
|
||||
private final Collection<String> entries;
|
||||
@@ -23,9 +24,9 @@ class ModernLocaleLine implements LocaleLine<Component> {
|
||||
public ModernLocaleLine(GlobalLineInfo info, SidebarLineHandler handler) {
|
||||
this.info = info;
|
||||
this.handler = handler;
|
||||
this.entries = Collections.singleton(info.player());
|
||||
this.entries = ImmutableList.of(info.player());
|
||||
this.packetAdapter = info.packetAdapter().createTeamDisplayAdapter(this);
|
||||
packetAdapter.updateTeamPackets(entries);
|
||||
packetAdapter.updateTeamPackets();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -39,7 +40,7 @@ class ModernLocaleLine implements LocaleLine<Component> {
|
||||
|
||||
@Override
|
||||
public void updateTeam() {
|
||||
packetAdapter.updateTeamPackets(entries);
|
||||
packetAdapter.updateTeamPackets();
|
||||
packetAdapter.sendProperties(PropertiesPacketType.UPDATE, handler.players());
|
||||
}
|
||||
|
||||
@@ -60,11 +61,11 @@ class ModernLocaleLine implements LocaleLine<Component> {
|
||||
@Override
|
||||
public void hide(@NotNull Collection<Player> players) {
|
||||
handler.localeLineHandler().sidebar().packetAdapter().removeScore(players, info.player());
|
||||
info.packetAdapter().removeTeam(players);
|
||||
packetAdapter.removeTeam(players);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<String> entries() {
|
||||
public @NotNull Collection<String> syncedEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
|
||||
Loaded 100 of 150 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user