Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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 | ||
|
|
20245b870f | ||
|
|
715c28732e | ||
|
|
408a379bfe | ||
|
|
caeb7286a8 | ||
|
|
0452126353 | ||
|
|
d8d6ba3a11 | ||
|
|
30b52722cd | ||
|
|
4ad96a9a79 | ||
|
|
5524dd399f | ||
|
|
17ba876a7a | ||
|
|
4e7b21f0ca | ||
|
|
25df9e7262 | ||
|
|
d547387245 | ||
|
|
9fc2c23a9d | ||
|
|
2f07a0e68d | ||
|
|
78442b652c | ||
|
|
57e8470cc7 | ||
|
|
ee16d324e1 | ||
|
|
b0ec7d2d97 | ||
|
|
1c4ec63eea | ||
|
|
8ec3e1082d | ||
|
|
bdbbf155e7 | ||
|
|
40c7fe49be | ||
|
|
3a0b0c1d94 | ||
|
|
226f39ee27 | ||
|
|
8c00d11070 | ||
|
|
f809416599 | ||
|
|
da176b8ea4 | ||
|
|
77dd1b8c65 | ||
|
|
736199d58f | ||
|
|
c2c1977885 | ||
|
|
045b558548 | ||
|
|
34f78377d9 | ||
|
|
f2bb69bd23 | ||
|
|
e2c4b6708e | ||
|
|
e957539c0d | ||
|
|
0f2f3ba432 | ||
|
|
7cf57c471f | ||
|
|
ceabb2edc8 | ||
|
|
7d12c7aa5d | ||
|
|
c3f398ae88 | ||
|
|
ebbb902343 | ||
|
|
ee418c87fd | ||
|
|
fcd8b81a8f | ||
|
|
fbe3d1c6cf | ||
|
|
ab7f574c14 | ||
|
|
b2c756c2cb | ||
|
|
8b930bfe13 | ||
|
|
07d868e180 | ||
|
|
b98e1f007b | ||
|
|
563f132528 | ||
|
|
eccc236d5d | ||
|
|
cc73eea58d | ||
|
|
8b88a58668 | ||
|
|
c2c30b33d7 | ||
|
|
3a497f7e0e | ||
|
|
f5ff418eff | ||
|
|
eadf470946 | ||
|
|
409d95b9a3 | ||
|
|
83f44eca4a | ||
|
|
67a8f209b0 | ||
|
|
07d8a377ae | ||
|
|
3bf265fe53 | ||
|
|
b747d4bfeb | ||
|
|
fe96150add | ||
|
|
541c349f41 | ||
|
|
7817ce08df | ||
|
|
087d086945 | ||
|
|
feff32dc05 | ||
|
|
d9db488aca | ||
|
|
a2cdd2a7ae | ||
|
|
1536aacb0a | ||
|
|
db9995dc47 | ||
|
|
de2d02b41f | ||
|
|
710612bbc7 | ||
|
|
3d4ccd9d2c | ||
|
|
fcca0b0507 | ||
|
|
cc6eae07ee | ||
|
|
73dd4033d4 | ||
|
|
61c59406cb | ||
|
|
74190b3e14 | ||
|
|
8712a3a435 | ||
|
|
78c3ee78cf | ||
|
|
daa32239b9 | ||
|
|
bab2448922 | ||
|
|
f82a3d2797 | ||
|
|
e744372afc | ||
|
|
36e63b4b54 | ||
|
|
1c471be8a2 | ||
|
|
a5f7cbaeec | ||
|
|
b45a5d5f2b | ||
|
|
7388ce5cb0 | ||
|
|
7991216122 | ||
|
|
b4d8394992 | ||
|
|
3ec2f4abf7 | ||
|
|
8d1c391c66 | ||
|
|
7dadc970ca | ||
|
|
e4aab20c2f | ||
|
|
7954038d75 | ||
|
|
9b1e21e4f7 | ||
|
|
92cbf54fc1 | ||
|
|
cc4a8fb4e5 | ||
|
|
2ca345c3f7 | ||
|
|
af58ce7423 | ||
|
|
c43966026c | ||
|
|
d9fcaf7f62 | ||
|
|
71c8985d64 | ||
|
|
46ab9d56f6 | ||
|
|
6142ce4c53 | ||
|
|
2f52aa52c4 | ||
|
|
d4e5bc8d6e | ||
|
|
a6a81da88d | ||
|
|
dc6bb4ef6c | ||
|
|
31d51d0ce9 | ||
|
|
a78cab78e1 | ||
|
|
6dbecb310d | ||
|
|
23173d1446 | ||
|
|
093950dc94 | ||
|
|
c8cc9c00ef | ||
|
|
863ba3951f | ||
|
|
b074ce8ea2 | ||
|
|
2cf6bf2ce7 | ||
|
|
247b55a04e | ||
|
|
2226a509a4 | ||
|
|
faccce273d | ||
|
|
1ff5b78098 | ||
|
|
682634de5c | ||
|
|
32469565e1 | ||
|
|
db269edff9 | ||
|
|
6ec3393aab | ||
|
|
079c774cc4 | ||
|
|
0ae845ea1a | ||
|
|
fc8e26af88 | ||
|
|
8fb5080916 | ||
|
|
5c023d7bc3 | ||
|
|
3a7a684d0b | ||
|
|
3f5099fdfb |
No files matched your search
@@ -0,0 +1,24 @@
|
||||
name: CI
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
ci:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: cachix/install-nix-action@v31
|
||||
- name: Cache Gradle
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: |
|
||||
~/.gradle/caches
|
||||
~/.gradle/wrapper
|
||||
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-gradle-
|
||||
- name: ./gradlew build
|
||||
run: nix develop -i -c sh -c "./gradlew build"
|
||||
+6
-1
@@ -2,4 +2,9 @@
|
||||
*.iml
|
||||
|
||||
.gradle
|
||||
build/
|
||||
build/
|
||||
|
||||
.classpath
|
||||
.project
|
||||
.settings/
|
||||
bin/
|
||||
+81
-18
@@ -1,38 +1,101 @@
|
||||
# Installation
|
||||
|
||||
I'm only going to show the installation for Gradle, however everything can be applied to Maven too.
|
||||
Latest version: `2.3.2`
|
||||
|
||||
First, add the Jitpack repository:
|
||||
## Gradle
|
||||
|
||||
```kotlin
|
||||
repositories {
|
||||
maven("https://jitpack.io/")
|
||||
mavenCentral()
|
||||
}
|
||||
```
|
||||
|
||||
Then add the dependencies:
|
||||
|
||||
```kotlin
|
||||
dependencies {
|
||||
val scoreboardLibraryVersion = "2.0.0-RC5"
|
||||
implementation("com.github.megavexnetwork.scoreboard-library:scoreboard-library-api:$scoreboardLibraryVersion")
|
||||
implementation("com.github.megavexnetwork.scoreboard-library:scoreboard-library-extra-kotlin:$scoreboardLibraryVersion") // If using Kotlin
|
||||
runtimeOnly("com.github.megavexnetwork.scoreboard-library:scoreboard-library-implementation:$scoreboardLibraryVersion")
|
||||
val scoreboardLibraryVersion = "{VERSION HERE}"
|
||||
implementation("net.megavex:scoreboard-library-api:$scoreboardLibraryVersion")
|
||||
runtimeOnly("net.megavex:scoreboard-library-implementation:$scoreboardLibraryVersion")
|
||||
implementation("net.megavex:scoreboard-library-extra-kotlin:$scoreboardLibraryVersion") // Kotlin specific extensions (optional)
|
||||
|
||||
// Add packet adapter implementations you want:
|
||||
runtimeOnly("com.github.megavexnetwork.scoreboard-library:scoreboard-library-v1_8_R3:$scoreboardLibraryVersion")
|
||||
runtimeOnly("com.github.megavexnetwork.scoreboard-library:scoreboard-library-v1_19_R2:$scoreboardLibraryVersion")
|
||||
runtimeOnly("com.github.megavexnetwork.scoreboard-library:scoreboard-library-packetevents:$scoreboardLibraryVersion")
|
||||
runtimeOnly("net.megavex:scoreboard-library-modern:$scoreboardLibraryVersion") // 1.17+
|
||||
runtimeOnly("net.megavex:scoreboard-library-modern:$scoreboardLibraryVersion:mojmap") // Mojang mapped variant (only use if you know what you're doing!)
|
||||
runtimeOnly("net.megavex:scoreboard-library-packetevents:$scoreboardLibraryVersion") // 1.8+
|
||||
runtimeOnly("net.megavex:scoreboard-library-legacy:$scoreboardLibraryVersion") // 1.7.10-1.12.2
|
||||
|
||||
// If using the PacketEvents implementation, scoreboard-library expects PacketEvents to be in the classpath.
|
||||
// Follow either of:
|
||||
// - https://github.com/retrooper/packetevents/wiki/Depending-on-pre-built-PacketEvents
|
||||
// - 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 Minecraft version without native Adventure support, add it as well:
|
||||
implementation("net.kyori:adventure-platform-bukkit:4.3.4")
|
||||
}
|
||||
```
|
||||
|
||||
You will need to shade these dependencies and relocate them with something
|
||||
like [Shadow](https://imperceptiblethoughts.com/shadow/).
|
||||
like [Shadow](https://gradleup.com/shadow/).
|
||||
|
||||
## Maven
|
||||
|
||||
```xml
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>net.megavex</groupId>
|
||||
<artifactId>scoreboard-library-api</artifactId>
|
||||
<version>{VERSION HERE}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.megavex</groupId>
|
||||
<artifactId>scoreboard-library-implementation</artifactId>
|
||||
<version>{VERSION HERE}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<!-- Kotlin specific extensions (optional) -->
|
||||
<dependency>
|
||||
<groupId>net.megavex</groupId>
|
||||
<artifactId>scoreboard-library-extra-kotlin</artifactId>
|
||||
<version>{VERSION HERE}</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Add packet adapter implementations you want: -->
|
||||
<dependency>
|
||||
<groupId>net.megavex</groupId>
|
||||
<artifactId>scoreboard-library-modern</artifactId>
|
||||
<version>{VERSION HERE}</version>
|
||||
<scope>runtime</scope>
|
||||
<!-- For a Mojang mapped variant, uncomment line below (only use if you know what you're doing!): -->
|
||||
<!-- <classifier>mojmap</classifier> -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.megavex</groupId>
|
||||
<artifactId>scoreboard-library-packetevents</artifactId>
|
||||
<version>{VERSION HERE}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.megavex</groupId>
|
||||
<artifactId>scoreboard-library-legacy</artifactId>
|
||||
<version>{VERSION HERE}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<!--
|
||||
If using the PacketEvents implementation, scoreboard-library expects PacketEvents to be in the classpath.
|
||||
Follow either of:
|
||||
- https://github.com/retrooper/packetevents/wiki/Depending-on-pre-built-PacketEvents
|
||||
- https://github.com/retrooper/packetevents/wiki/Shading-PacketEvents
|
||||
Example how to load PacketEvents in your plugin:
|
||||
https://github.com/retrooper/packetevents-example/blob/24f0c842d47362aef122b794dea29b8fee113fa3/thread-safe-listener/src/main/java/main/Main.java
|
||||
-->
|
||||
|
||||
<!-- If targeting a Minecraft version without native Adventure support, add it as well: -->
|
||||
<dependency>
|
||||
<groupId>net.kyori</groupId>
|
||||
<artifactId>adventure-platform-bukkit</artifactId>
|
||||
<version>4.0.1</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
```
|
||||
|
||||
You will need to shade these dependencies and relocate them with [maven-shade-plugin](https://maven.apache.org/plugins/maven-shade-plugin/).
|
||||
@@ -1,28 +1,34 @@
|
||||
# scoreboard-library
|
||||
|
||||
Powerful Scoreboard library for Paper/Spigot servers using the [adventure](https://github.com/KyoriPowered/adventure)
|
||||
Powerful scoreboard library for Minecraft Paper/Spigot servers using
|
||||
the [adventure](https://github.com/KyoriPowered/adventure)
|
||||
component library
|
||||
|
||||
Join the [Discord](https://discord.gg/v7nmTDTW8W) or create an issue for support
|
||||
|
||||
## Features
|
||||
|
||||
- Sidebars
|
||||
- Teams
|
||||
- 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)
|
||||
- Packet-level, meaning it works with other scoreboard plugins
|
||||
- Fully async. All packet work is done asynchronously so you can (but don't have to) use the library from the main
|
||||
- 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
|
||||
- Works with `TranslatableComponent`s, meaning all components are automatically translated using `GlobalTranslator` for
|
||||
each players client locale (and automatically update whenever the player changes it)
|
||||
- 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
|
||||
|
||||
## Packet Adapters
|
||||
## Available Packet Adapters
|
||||
|
||||
- **1.19.3.** [Spigot](https://www.spigotmc.org/) does work, but [Paper](https://papermc.io/) is recommended because
|
||||
scoreboard-library can take advantage of the native [Adventure](https://github.com/KyoriPowered/adventure) feature to
|
||||
improve performance
|
||||
- **1.8.8.** Note that you'll still need to use Java 17
|
||||
- **PacketEvents.** Requires [PacketEvents 2.0](https://github.com/retrooper/packetevents/tree/2.0) to be loaded in the
|
||||
classpath. Should work on all versions 1.8+
|
||||
- **modern.** Supports 1.17-1.21.6. Can take advantage of [Paper](https://papermc.io)'s native adventure support to be more efficient.
|
||||
- **legacy.** Supports 1.7.10-1.12.2.
|
||||
- **PacketEvents.** Supports 1.8+. Requires [PacketEvents 2.0](https://github.com/retrooper/packetevents/tree/2.0) to be shaded or installed as a plugin.
|
||||
|
||||
> [!NOTE]
|
||||
> You can add multiple packet adapters, the best one will automatically be picked depending on the server version.
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -37,14 +43,19 @@ try {
|
||||
} catch (NoPacketAdapterAvailableException e) {
|
||||
// If no packet adapter was found, you can fallback to the no-op implementation:
|
||||
scoreboardLibrary = new NoopScoreboardLibrary();
|
||||
plugin.getLogger().warning("No scoreboard packet adapter available!");
|
||||
}
|
||||
|
||||
// On plugin shutdown:
|
||||
scoreboardLibrary.close();
|
||||
```
|
||||
|
||||
### Sidebar
|
||||
### Thread safety warning (Folia)
|
||||
|
||||
`Sidebar`s and `TeamManager`s are not thread safe, so you will need to add some
|
||||
synchronisation to make sure you're using them from only one thread at a time.
|
||||
|
||||
### Sidebar (low-level)
|
||||
|
||||
```java
|
||||
Sidebar sidebar = scoreboardLibrary.createSidebar();
|
||||
@@ -58,73 +69,175 @@ 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!
|
||||
```
|
||||
|
||||
### Sidebar (Kotlin)
|
||||
### Component sidebars
|
||||
|
||||
```kotlin
|
||||
val sidebar = scoreboardLibrary.createSidebar(4)
|
||||
|
||||
val timerLine: LinesBuilder.DynamicLine
|
||||
var timer = 0
|
||||
|
||||
sidebar.title(Component.text("Timer Example", NamedTextColor.AQUA))
|
||||
sidebar.lines {
|
||||
emptyLine()
|
||||
timerLine = dynamicLine { Component.text("Timer: $timer") }
|
||||
emptyLine()
|
||||
line(Component.text("epicserver.net", NamedTextColor.AQUA))
|
||||
}
|
||||
|
||||
plugin.server.scheduler.runTaskTimerAsynchronously(
|
||||
plugin,
|
||||
Runnable {
|
||||
timer++
|
||||
timerLine.update()
|
||||
},
|
||||
20,
|
||||
20
|
||||
)
|
||||
|
||||
sidebar.addPlayer(player)
|
||||
```
|
||||
|
||||
### AbstractSidebar
|
||||
Component sidebars are an abstraction over the low-level `Sidebar`. They allow you to design sidebars in a clean way.
|
||||
Here's how you create a `SidebarComponent` with a static line:
|
||||
|
||||
```java
|
||||
public class TimerSidebar extends AbstractSidebar {
|
||||
private final DynamicLine timerLine;
|
||||
private int timer;
|
||||
SidebarComponent staticLine = SidebarComponent.staticLine(Component.text("A static line"));
|
||||
```
|
||||
|
||||
private final BukkitTask task;
|
||||
You can chain multiple `SidebarComponent`s together using `SidebarComponent.builder()`:
|
||||
|
||||
public TimerSidebar(@NotNull Plugin plugin, @NotNull ScoreboardLibrary scoreboardLibrary, @NotNull Player player) {
|
||||
super(scoreboardLibrary.createSidebar(4));
|
||||
```java
|
||||
SidebarComponent lines = SidebarComponent.builder()
|
||||
.addComponent(SidebarComponent.staticLine(Component.text("A static line")))
|
||||
.addStaticLine(Component.text("Another static line")) // Shorthand for line above
|
||||
.build();
|
||||
```
|
||||
|
||||
sidebar.title(text("Timer Example", NamedTextColor.AQUA));
|
||||
To use these components, create a `ComponentSidebarLayout`:
|
||||
|
||||
registerEmptyLine(0);
|
||||
timerLine = registerDynamicLine(1, () -> text("Timer: " + timer));
|
||||
registerEmptyLine(2);
|
||||
registerStaticLine(3, text("epicserver.net", NamedTextColor.AQUA));
|
||||
```java
|
||||
ComponentSidebarLayout layout = new ComponentSidebarLayout(
|
||||
SidebarComponent.staticLine(Component.text("Sidebar Title")),
|
||||
lines
|
||||
);
|
||||
|
||||
task = plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, () -> {
|
||||
timer++;
|
||||
timerLine.update();
|
||||
}, 20, 20);
|
||||
Sidebar sidebar = scoreboardLibrary.createSidebar();
|
||||
|
||||
sidebar.addPlayer(player);
|
||||
// Apply the title & lines components to the Sidebar
|
||||
// Do this every time the title or any line needs to be updated
|
||||
layout.apply(sidebar);
|
||||
```
|
||||
|
||||
You can make animations with `SidebarAnimation`:
|
||||
|
||||
```java
|
||||
Component lineComponent = Component.text("Line that changes colors");
|
||||
Set<NamedTextColor> colors = NamedTextColor.NAMES.values();
|
||||
List<Component> frames = new ArrayList<>(colors.size());
|
||||
for (NamedTextColor color : colors) {
|
||||
frames.add(lineComponent.color(color));
|
||||
}
|
||||
|
||||
SidebarAnimation<Component> animation = new CollectionSidebarAnimation<>(frames);
|
||||
// You can also implement SidebarAnimation yourself
|
||||
|
||||
SidebarComponent line = SidebarComponent.animatedLine(animation);
|
||||
|
||||
// Advance to the next frame of the animation
|
||||
animation.nextFrame();
|
||||
```
|
||||
|
||||
Animations can be used for pagination:
|
||||
|
||||
```java
|
||||
Player player = ...;
|
||||
|
||||
SidebarComponent firstPage = SidebarComponent.builder()
|
||||
.addStaticLine(Component.text("First page"))
|
||||
.addDynamicLine(() -> Component.text("Level: " + player.getLevel()))
|
||||
.build();
|
||||
|
||||
SidebarComponent secondPage = SidebarComponent.builder()
|
||||
.addStaticLine(Component.text("Second page"))
|
||||
.addDynamicLine(() -> Component.text("Health: " + player.getHealth()))
|
||||
.build();
|
||||
|
||||
List<SidebarComponent> pages = Arrays.asList(firstPage, secondPage);
|
||||
SidebarAnimation<SidebarComponent> pageAnimation = new CollectionSidebarAnimation<>(pages);
|
||||
SidebarComponent paginatedComponent = SidebarComponent.animatedComponent(pageAnimation);
|
||||
|
||||
// ...
|
||||
```
|
||||
|
||||
You can also create your own `SidebarComponent`s:
|
||||
|
||||
```java
|
||||
public class KeyValueSidebarComponent implements SidebarComponent {
|
||||
private final Component key;
|
||||
private final Supplier<Component> valueSupplier;
|
||||
|
||||
public KeyValueSidebarComponent(@NotNull Component key, @NotNull Supplier<Component> valueSupplier) {
|
||||
this.key = key;
|
||||
this.valueSupplier = valueSupplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onClosed() {
|
||||
task.cancel();
|
||||
public void draw(@NotNull LineDrawable drawable) {
|
||||
var value = valueSupplier.get();
|
||||
var line = Component.text()
|
||||
.append(key)
|
||||
.append(Component.text(": "))
|
||||
.append(value.colorIfAbsent(NamedTextColor.AQUA))
|
||||
.build();
|
||||
|
||||
drawable.drawLine(line);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here's a full sidebar example:
|
||||
|
||||
```java
|
||||
public class ExampleComponentSidebar {
|
||||
private final Sidebar sidebar;
|
||||
private final ComponentSidebarLayout componentSidebar;
|
||||
private final SidebarAnimation<Component> titleAnimation;
|
||||
|
||||
public ExampleComponentSidebar(@NotNull Plugin plugin, @NotNull Sidebar sidebar) {
|
||||
this.sidebar = sidebar;
|
||||
|
||||
this.titleAnimation = createGradientAnimation(Component.text("Sidebar Example", Style.style(TextDecoration.BOLD)));
|
||||
var title = SidebarComponent.animatedLine(titleAnimation);
|
||||
|
||||
SimpleDateFormat dtf = new SimpleDateFormat("HH:mm:ss");
|
||||
|
||||
// Custom SidebarComponent, see below for how an implementation might look like
|
||||
SidebarComponent onlinePlayers = new KeyValueSidebarComponent(
|
||||
Component.text("Online players"),
|
||||
() -> Component.text(plugin.getServer().getOnlinePlayers().size())
|
||||
);
|
||||
|
||||
SidebarComponent lines = SidebarComponent.builder()
|
||||
.addDynamicLine(() -> {
|
||||
var time = dtf.format(new Date());
|
||||
return Component.text(time, NamedTextColor.GRAY);
|
||||
})
|
||||
.addBlankLine()
|
||||
.addStaticLine(Component.text("A static line"))
|
||||
.addComponent(onlinePlayers)
|
||||
.addBlankLine()
|
||||
.addStaticLine(Component.text("epicserver.net", NamedTextColor.AQUA))
|
||||
.build();
|
||||
|
||||
|
||||
this.componentSidebar = new ComponentSidebarLayout(title, lines);
|
||||
}
|
||||
|
||||
// Called every tick
|
||||
public void tick() {
|
||||
// Advance title animation to the next frame
|
||||
titleAnimation.nextFrame();
|
||||
|
||||
// Update sidebar title & lines
|
||||
componentSidebar.apply(sidebar);
|
||||
}
|
||||
|
||||
private @NotNull SidebarAnimation<Component> createGradientAnimation(@NotNull Component text) {
|
||||
float step = 1f / 8f;
|
||||
|
||||
TagResolver.Single textPlaceholder = Placeholder.component("text", text);
|
||||
List<Component> frames = new ArrayList<>((int) (2f / step));
|
||||
|
||||
float phase = -1f;
|
||||
while (phase < 1) {
|
||||
frames.add(MiniMessage.miniMessage().deserialize("<gradient:yellow:gold:" + phase + "><text>", textPlaceholder));
|
||||
phase += step;
|
||||
}
|
||||
|
||||
return new CollectionSidebarAnimation<>(frames);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`
|
||||
|
||||
### TeamManager
|
||||
|
||||
```java
|
||||
@@ -140,6 +253,7 @@ teamDisplay.displayName(Component.text("Team Display Name"));
|
||||
teamDisplay.prefix(Component.text("[Prefix] "));
|
||||
teamDisplay.suffix(Component.text(" [Suffix]"));
|
||||
teamDisplay.playerColor(NamedTextColor.RED);
|
||||
teamDisplay.addEntry(player.getName());
|
||||
|
||||
teamManager.addPlayer(player); // Player will be added to the default TeamDisplay of each ScoreboardTeam
|
||||
|
||||
@@ -150,8 +264,19 @@ 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!
|
||||
```
|
||||
|
||||
For more examples, check out the [example plugin](https://github.com/MegavexNetwork/scoreboard-library-example)
|
||||
### ObjectiveManager
|
||||
|
||||
```java
|
||||
ObjectiveManager objectiveManager = scoreboardLibrary.createObjectiveManager();
|
||||
ScoreboardObjective objective = objectiveManager.create("coolobjective");
|
||||
objective.value(Component.text("Display name"));
|
||||
objective.score(player.getName(), 69420);
|
||||
objectiveManager.display(ObjectiveDisplaySlot.belowName(), objective);
|
||||
|
||||
objectiveManager.addPlayer(player); // Make a player see the objectives
|
||||
|
||||
// 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)
|
||||
}
|
||||
@@ -1,34 +1,49 @@
|
||||
package net.megavex.scoreboardlibrary.api;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Locale;
|
||||
import javax.annotation.concurrent.ThreadSafe;
|
||||
import net.megavex.scoreboardlibrary.api.exception.NoPacketAdapterAvailableException;
|
||||
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;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Entrypoint of the library. To create an instance of this interface, use {@link #loadScoreboardLibrary}.
|
||||
* For unit testing, take a look at {@link NoopScoreboardLibrary}.
|
||||
* Note: this class is thread-safe, meaning you can safely use it from multiple threads at a time.
|
||||
*/
|
||||
@ApiStatus.NonExtendable
|
||||
@ThreadSafe
|
||||
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
|
||||
*/
|
||||
static @NotNull ScoreboardLibrary loadScoreboardLibrary(@NotNull Plugin plugin) throws NoPacketAdapterAvailableException {
|
||||
Class<?> clazz;
|
||||
Class<?> implClass;
|
||||
try {
|
||||
clazz = Class.forName("net.megavex.scoreboardlibrary.implementation.ScoreboardLibraryImpl");
|
||||
implClass = Class.forName("net.megavex.scoreboardlibrary.implementation.ScoreboardLibraryImpl");
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("scoreboard-library implementation is not shaded into the classpath");
|
||||
throw new IllegalStateException("scoreboard-library implementation is not shaded into the classpath");
|
||||
}
|
||||
|
||||
try {
|
||||
return (ScoreboardLibrary) clazz.getDeclaredConstructor(Plugin.class).newInstance(plugin);
|
||||
return (ScoreboardLibrary) implClass.getDeclaredConstructor(Plugin.class).newInstance(plugin);
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
|
||||
if (e instanceof InvocationTargetException invocationTargetException) {
|
||||
if (invocationTargetException.getTargetException() instanceof NoPacketAdapterAvailableException noPacketAdapterAvailableException) {
|
||||
throw noPacketAdapterAvailableException;
|
||||
if (e instanceof InvocationTargetException) {
|
||||
Throwable targetException = ((InvocationTargetException) e).getTargetException();
|
||||
if (targetException instanceof NoPacketAdapterAvailableException) {
|
||||
throw (NoPacketAdapterAvailableException) targetException;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,49 +52,72 @@ public interface ScoreboardLibrary {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Sidebar}
|
||||
* Creates a new {@link Sidebar};
|
||||
*
|
||||
* @return Sidebar
|
||||
* @return newly created sidebar
|
||||
*/
|
||||
default @NotNull Sidebar createSidebar() {
|
||||
return createSidebar(Sidebar.MAX_LINES, null);
|
||||
return createSidebar(Sidebar.MAX_LINES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Sidebar}
|
||||
* Creates a new {@link Sidebar}.
|
||||
*
|
||||
* @param maxLines Max sidebar lines
|
||||
* @return Sidebar
|
||||
* @param maxLines max amount of lines the sidebar will have.
|
||||
* Note that vanilla clients can only display at most {@value Sidebar#MAX_LINES}
|
||||
* @return newly created sidebar
|
||||
*/
|
||||
default @NotNull Sidebar createSidebar(@Range(from = 1, to = Sidebar.MAX_LINES) int maxLines) {
|
||||
default @NotNull Sidebar createSidebar(@Range(from = 1, to = Integer.MAX_VALUE) int maxLines) {
|
||||
return createSidebar(maxLines, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link Sidebar}
|
||||
* Creates a new {@link Sidebar}.
|
||||
*
|
||||
* @param maxLines Max sidebar lines
|
||||
* @param locale Locale which will be used for translating {@link net.kyori.adventure.text.TranslatableComponent}s
|
||||
* @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
|
||||
* @return Sidebar
|
||||
* @return newly created sidebar
|
||||
*/
|
||||
@NotNull Sidebar createSidebar(@Range(from = 1, to = Sidebar.MAX_LINES) 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 {@link TeamManager}
|
||||
* Creates a new {@link Sidebar}.
|
||||
*
|
||||
* @return TeamManager
|
||||
* @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}.
|
||||
*
|
||||
* @return newly created team manager
|
||||
*/
|
||||
@NotNull TeamManager createTeamManager();
|
||||
|
||||
/**
|
||||
* Closes this ScoreboardLibrary instance.
|
||||
* Should always be called when the plugin is disabled
|
||||
* Creates a new {@link ObjectiveManager}.
|
||||
*
|
||||
* @return newly created objective manager
|
||||
*/
|
||||
@NotNull ObjectiveManager createObjectiveManager();
|
||||
|
||||
/**
|
||||
* Closes this scoreboard library instance.
|
||||
* You should call this in {@link JavaPlugin#onDisable()}.
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* @return Whether this ScoreboardLibrary instance is closed
|
||||
* @return whether this scoreboard library instance is closed
|
||||
*/
|
||||
boolean closed();
|
||||
}
|
||||
+4
@@ -1,5 +1,9 @@
|
||||
package net.megavex.scoreboardlibrary.api.exception;
|
||||
|
||||
/**
|
||||
* Exception indicating that there is no packet adapter available for the current server version.
|
||||
* As a fallback, consider using the {@link net.megavex.scoreboardlibrary.api.noop.NoopScoreboardLibrary} implementation.
|
||||
*/
|
||||
public final class NoPacketAdapterAvailableException extends Exception {
|
||||
public NoPacketAdapterAvailableException() {
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package net.megavex.scoreboardlibrary.api.noop;
|
||||
|
||||
import net.megavex.scoreboardlibrary.api.objective.ObjectiveDisplaySlot;
|
||||
import net.megavex.scoreboardlibrary.api.objective.ObjectiveManager;
|
||||
import net.megavex.scoreboardlibrary.api.objective.ScoreboardObjective;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
class NoopObjectiveManager implements ObjectiveManager {
|
||||
private final Map<String, ScoreboardObjective> objectives = new HashMap<>();
|
||||
private final Set<Player> players = new HashSet<>();
|
||||
private boolean isClosed = true;
|
||||
|
||||
@Override
|
||||
public @NotNull ScoreboardObjective create(@NotNull String name) {
|
||||
return objectives.computeIfAbsent(name, i -> new NoopScoreboardObjective());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(@NotNull ScoreboardObjective objective) {
|
||||
objectives.values().remove(objective);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void display(@NotNull ObjectiveDisplaySlot displaySlot, @NotNull ScoreboardObjective objective) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<Player> players() {
|
||||
return Collections.unmodifiableSet(players);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addPlayer(@NotNull Player player) {
|
||||
return players.add(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removePlayer(@NotNull Player player) {
|
||||
return players.remove(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
isClosed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean closed() {
|
||||
return isClosed;
|
||||
}
|
||||
}
|
||||
+13
-4
@@ -1,24 +1,27 @@
|
||||
package net.megavex.scoreboardlibrary.api.noop;
|
||||
|
||||
import java.util.Locale;
|
||||
import net.megavex.scoreboardlibrary.api.ScoreboardLibrary;
|
||||
import net.megavex.scoreboardlibrary.api.objective.ObjectiveManager;
|
||||
import net.megavex.scoreboardlibrary.api.sidebar.Sidebar;
|
||||
import net.megavex.scoreboardlibrary.api.team.TeamManager;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* no-op implementation of ScoreboardLibrary.
|
||||
* No-op implementation of {@link ScoreboardLibrary}.
|
||||
* Can be used as a fallback when there is no packet adapter available
|
||||
* and for unit testing.
|
||||
*/
|
||||
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
|
||||
@@ -27,6 +30,12 @@ public final class NoopScoreboardLibrary implements ScoreboardLibrary {
|
||||
return new NoopTeamManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ObjectiveManager createObjectiveManager() {
|
||||
checkClosed();
|
||||
return new NoopObjectiveManager();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
closed = true;
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
package net.megavex.scoreboardlibrary.api.noop;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import net.megavex.scoreboardlibrary.api.objective.ObjectiveRenderType;
|
||||
import net.megavex.scoreboardlibrary.api.objective.ObjectiveScore;
|
||||
import net.megavex.scoreboardlibrary.api.objective.ScoreFormat;
|
||||
import net.megavex.scoreboardlibrary.api.objective.ScoreboardObjective;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static net.kyori.adventure.text.Component.empty;
|
||||
|
||||
public class NoopScoreboardObjective implements ScoreboardObjective {
|
||||
private final Map<String, ObjectiveScore> scores = new HashMap<>();
|
||||
private Component value = empty();
|
||||
private ObjectiveRenderType renderType;
|
||||
private ScoreFormat defaultScoreFormat;
|
||||
|
||||
@Override
|
||||
public @NotNull Component value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ScoreboardObjective value(@NotNull ComponentLike value) {
|
||||
this.value = value.asComponent();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ObjectiveRenderType renderType() {
|
||||
return renderType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ScoreboardObjective renderType(@NotNull ObjectiveRenderType renderType) {
|
||||
this.renderType = renderType;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ScoreFormat defaultScoreFormat() {
|
||||
return defaultScoreFormat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void defaultScoreFormat(@Nullable ScoreFormat defaultScoreFormat) {
|
||||
this.defaultScoreFormat = defaultScoreFormat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ObjectiveScore scoreInfo(@NotNull String entry) {
|
||||
return scores.get(entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ScoreboardObjective score(@NotNull String entry, ObjectiveScore score) {
|
||||
scores.put(entry, score);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ScoreboardObjective removeScore(@NotNull String entry) {
|
||||
scores.remove(entry);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +1,16 @@
|
||||
package net.megavex.scoreboardlibrary.api.noop;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import net.megavex.scoreboardlibrary.api.team.ScoreboardTeam;
|
||||
import net.megavex.scoreboardlibrary.api.team.TeamDisplay;
|
||||
import net.megavex.scoreboardlibrary.api.team.TeamManager;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
class NoopScoreboardTeam implements ScoreboardTeam {
|
||||
private final NoopTeamManager teamManager;
|
||||
private final String name;
|
||||
|
||||
@@ -1,33 +1,32 @@
|
||||
package net.megavex.scoreboardlibrary.api.noop;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import net.megavex.scoreboardlibrary.api.objective.ScoreFormat;
|
||||
import net.megavex.scoreboardlibrary.api.sidebar.Sidebar;
|
||||
import net.megavex.scoreboardlibrary.api.util.SidebarUtilities;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
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];
|
||||
}
|
||||
|
||||
@@ -43,7 +42,7 @@ class NoopSidebar implements Sidebar {
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<Player> players() {
|
||||
return closed ? Set.of() : Collections.unmodifiableSet(players);
|
||||
return closed ? Collections.emptySet() : Collections.unmodifiableSet(players);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -65,21 +64,26 @@ class NoopSidebar implements Sidebar {
|
||||
return maxLines;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String objectiveName() {
|
||||
return objectiveName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Locale locale() {
|
||||
return locale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void line(int line, @Nullable Component value) {
|
||||
SidebarUtilities.checkLineBounds(maxLines, line);
|
||||
public void line(int index, @Nullable ComponentLike value, @Nullable ScoreFormat scoreFormat) {
|
||||
checkLineBounds(index);
|
||||
checkClosed();
|
||||
lines[line] = value;
|
||||
lines[index] = value == null ? null : value.asComponent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Component line(int line) {
|
||||
SidebarUtilities.checkLineBounds(maxLines, line);
|
||||
checkLineBounds(line);
|
||||
checkClosed();
|
||||
return lines[line];
|
||||
}
|
||||
@@ -90,10 +94,10 @@ class NoopSidebar implements Sidebar {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void title(@NotNull Component title) {
|
||||
public void title(@NotNull ComponentLike title) {
|
||||
Preconditions.checkNotNull(title);
|
||||
checkClosed();
|
||||
this.title = title;
|
||||
this.title = title.asComponent();
|
||||
}
|
||||
|
||||
private void checkClosed() {
|
||||
@@ -101,4 +105,10 @@ class NoopSidebar implements Sidebar {
|
||||
throw new IllegalStateException("NoopSidebar is closed");
|
||||
}
|
||||
}
|
||||
|
||||
private void checkLineBounds(int line) {
|
||||
if (line >= maxLines || line < 0) {
|
||||
throw new IndexOutOfBoundsException("invalid line " + line);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
package net.megavex.scoreboardlibrary.api.noop;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.megavex.scoreboardlibrary.api.team.ScoreboardTeam;
|
||||
import net.megavex.scoreboardlibrary.api.team.TeamDisplay;
|
||||
@@ -13,6 +10,10 @@ import net.megavex.scoreboardlibrary.api.team.enums.NameTagVisibility;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import static net.kyori.adventure.text.Component.empty;
|
||||
|
||||
@@ -58,8 +59,8 @@ class NoopTeamDisplay implements TeamDisplay {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TeamDisplay displayName(@NotNull Component displayName) {
|
||||
this.displayName = displayName;
|
||||
public @NotNull TeamDisplay displayName(@NotNull ComponentLike displayName) {
|
||||
this.displayName = displayName.asComponent();
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -69,8 +70,8 @@ class NoopTeamDisplay implements TeamDisplay {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TeamDisplay prefix(@NotNull Component prefix) {
|
||||
this.prefix = prefix;
|
||||
public @NotNull TeamDisplay prefix(@NotNull ComponentLike prefix) {
|
||||
this.prefix = prefix.asComponent();
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -80,8 +81,8 @@ class NoopTeamDisplay implements TeamDisplay {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TeamDisplay suffix(@NotNull Component suffix) {
|
||||
this.suffix = suffix;
|
||||
public @NotNull TeamDisplay suffix(@NotNull ComponentLike suffix) {
|
||||
this.suffix = suffix.asComponent();
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
package net.megavex.scoreboardlibrary.api.noop;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import net.megavex.scoreboardlibrary.api.team.ScoreboardTeam;
|
||||
import net.megavex.scoreboardlibrary.api.team.TeamDisplay;
|
||||
import net.megavex.scoreboardlibrary.api.team.TeamManager;
|
||||
@@ -16,6 +8,10 @@ import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
class NoopTeamManager implements TeamManager {
|
||||
private final Set<Player> players = new HashSet<>();
|
||||
private final Map<String, NoopScoreboardTeam> teams = new HashMap<>();
|
||||
@@ -33,7 +29,7 @@ class NoopTeamManager implements TeamManager {
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<Player> players() {
|
||||
return closed ? Set.of() : Collections.unmodifiableSet(players);
|
||||
return closed ? Collections.emptySet() : Collections.unmodifiableSet(players);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -45,7 +41,7 @@ class NoopTeamManager implements TeamManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var team : teams.values()) {
|
||||
for (NoopScoreboardTeam team : teams.values()) {
|
||||
team.displayMap().remove(player);
|
||||
}
|
||||
|
||||
@@ -54,7 +50,7 @@ class NoopTeamManager implements TeamManager {
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<ScoreboardTeam> teams() {
|
||||
return closed ? Set.of() : Collections.unmodifiableCollection(teams.values());
|
||||
return closed ? Collections.emptySet() : Collections.unmodifiableCollection(teams.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -73,14 +69,14 @@ class NoopTeamManager implements TeamManager {
|
||||
checkClosed();
|
||||
|
||||
name = name.toLowerCase();
|
||||
var team = teams.get(name);
|
||||
NoopScoreboardTeam team = teams.get(name);
|
||||
if (team != null) {
|
||||
return team;
|
||||
}
|
||||
|
||||
team = new NoopScoreboardTeam(this, name);
|
||||
for (var player : players) {
|
||||
var teamDisplay = teamDisplayFunction == null ? team.defaultDisplay() : teamDisplayFunction.apply(player, team);
|
||||
for (Player player : players) {
|
||||
TeamDisplay teamDisplay = teamDisplayFunction == null ? team.defaultDisplay() : teamDisplayFunction.apply(player, team);
|
||||
validateTeamDisplay(team, teamDisplay);
|
||||
team.displayMap().put(player, teamDisplay);
|
||||
}
|
||||
@@ -114,8 +110,8 @@ class NoopTeamManager implements TeamManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var team : teams.values()) {
|
||||
var teamDisplay = teamDisplayFunction == null ? team.defaultDisplay() : teamDisplayFunction.apply(team);
|
||||
for (NoopScoreboardTeam team : teams.values()) {
|
||||
TeamDisplay teamDisplay = teamDisplayFunction == null ? team.defaultDisplay() : teamDisplayFunction.apply(team);
|
||||
validateTeamDisplay(team, teamDisplay);
|
||||
team.displayMap().put(player, teamDisplay);
|
||||
}
|
||||
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
package net.megavex.scoreboardlibrary.api.objective;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* Represents all valid display slots that objectives can be shown in.
|
||||
*
|
||||
* @see <a href="https://minecraft.wiki/w/Scoreboard#Display_slots">Minecraft Wiki</a>
|
||||
*/
|
||||
@ApiStatus.NonExtendable
|
||||
public interface ObjectiveDisplaySlot {
|
||||
static @NotNull PlayerList playerList() {
|
||||
return PlayerList.INSTANCE;
|
||||
}
|
||||
|
||||
static @NotNull Sidebar sidebar() {
|
||||
return Sidebar.INSTANCE;
|
||||
}
|
||||
|
||||
static @NotNull BelowName belowName() {
|
||||
return BelowName.INSTANCE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since Minecraft 1.8
|
||||
*/
|
||||
static @NotNull TeamSidebar teamSidebar(@NotNull NamedTextColor teamColor) {
|
||||
Preconditions.checkNotNull(teamColor);
|
||||
return new TeamSidebar(teamColor);
|
||||
}
|
||||
|
||||
class PlayerList implements ObjectiveDisplaySlot {
|
||||
private static final PlayerList INSTANCE = new PlayerList();
|
||||
|
||||
private PlayerList() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "PlayerList";
|
||||
}
|
||||
}
|
||||
|
||||
class Sidebar implements ObjectiveDisplaySlot {
|
||||
private static final Sidebar INSTANCE = new Sidebar();
|
||||
|
||||
private Sidebar() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Sidebar";
|
||||
}
|
||||
}
|
||||
|
||||
class BelowName implements ObjectiveDisplaySlot {
|
||||
private static final BelowName INSTANCE = new BelowName();
|
||||
|
||||
private BelowName() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "BelowName";
|
||||
}
|
||||
}
|
||||
|
||||
class TeamSidebar implements ObjectiveDisplaySlot {
|
||||
private final NamedTextColor teamColor;
|
||||
|
||||
private TeamSidebar(@NotNull NamedTextColor teamColor) {
|
||||
this.teamColor = teamColor;
|
||||
}
|
||||
|
||||
public @NotNull NamedTextColor teamColor() {
|
||||
return teamColor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
TeamSidebar that = (TeamSidebar) o;
|
||||
return teamColor.equals(that.teamColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return teamColor.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "TeamSidebar{teamColor=" + teamColor + "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
package net.megavex.scoreboardlibrary.api.objective;
|
||||
|
||||
import net.megavex.scoreboardlibrary.api.ScoreboardLibrary;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Represents a group of {@link ScoreboardObjective}s.
|
||||
* To get an instance of this interface, use {@link ScoreboardLibrary#createObjectiveManager()}
|
||||
* Note: this interface is not thread-safe, meaning you can only use it from one thread at a time,
|
||||
* although it does not have to be the main thread.
|
||||
*/
|
||||
public interface ObjectiveManager {
|
||||
/**
|
||||
* Creates an objective with a name if one doesn't already exist and returns it.
|
||||
*
|
||||
* @param name name of the objective
|
||||
* @return objective
|
||||
*/
|
||||
@NotNull ScoreboardObjective create(@NotNull String name);
|
||||
|
||||
/**
|
||||
* Removes an objective.
|
||||
*
|
||||
* @param objective objective to remove
|
||||
*/
|
||||
void remove(@NotNull ScoreboardObjective objective);
|
||||
|
||||
/**
|
||||
* Updates which objective is shown in a display slot.
|
||||
*
|
||||
* @param displaySlot display slot value to show objective in
|
||||
* @param objective objective to display in that display slot
|
||||
*/
|
||||
void display(@NotNull ObjectiveDisplaySlot displaySlot, @NotNull ScoreboardObjective objective);
|
||||
|
||||
/**
|
||||
* @return unmodifiable collection of viewers in this ObjectiveManager
|
||||
* @see #addPlayer
|
||||
* @see #removePlayer
|
||||
*/
|
||||
@NotNull Collection<Player> players();
|
||||
|
||||
/**
|
||||
* Adds a viewer to this ObjectiveManager.
|
||||
* Note that a player can only see a single ObjectiveManager at a time.
|
||||
* The ObjectiveManager will internally be added to a queue for this player who
|
||||
* will start seeing it once they are removed from all previous ObjectiveManagers.
|
||||
*
|
||||
* @param player player to add
|
||||
* @return whether the player was added
|
||||
*/
|
||||
boolean addPlayer(@NotNull Player player);
|
||||
|
||||
/**
|
||||
* Adds multiple viewers to this ObjectiveManager.
|
||||
*
|
||||
* @param players viewers to add
|
||||
* @see #addPlayer
|
||||
*/
|
||||
default void addPlayers(@NotNull Collection<Player> players) {
|
||||
for (Player player : players) {
|
||||
addPlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a viewer from this ObjectiveManager.
|
||||
*
|
||||
* @param player viewer to remove
|
||||
* @return whether the viewer was removed
|
||||
*/
|
||||
boolean removePlayer(@NotNull Player player);
|
||||
|
||||
/**
|
||||
* Removes multiple viewers from this ObjectiveManager
|
||||
*
|
||||
* @param players viewers to remove
|
||||
*/
|
||||
default void removePlayers(@NotNull Collection<Player> players) {
|
||||
for (Player player : players) {
|
||||
removePlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this ObjectiveManager.
|
||||
* This must be called once you no longer need this ObjectiveManager to prevent a memory leak.
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* @return whether this ObjectiveManager is closed
|
||||
* @see #close
|
||||
*/
|
||||
boolean closed();
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package net.megavex.scoreboardlibrary.api.objective;
|
||||
|
||||
/**
|
||||
* Represents all possible render types of an objective.
|
||||
* The render type represents the way scores are displayed.
|
||||
*/
|
||||
public enum ObjectiveRenderType {
|
||||
INTEGER,
|
||||
HEARTS
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package net.megavex.scoreboardlibrary.api.objective;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents an objective score.
|
||||
*/
|
||||
public final class ObjectiveScore {
|
||||
private final int value;
|
||||
private final Component displayName;
|
||||
private final ScoreFormat format;
|
||||
|
||||
public ObjectiveScore(int value, @Nullable ComponentLike displayName, @Nullable ScoreFormat format) {
|
||||
this.value = value;
|
||||
this.displayName = displayName == null ? null : displayName.asComponent();
|
||||
this.format = format;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return score value
|
||||
*/
|
||||
public int value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the score display.
|
||||
*
|
||||
* @return score display
|
||||
* @since Minecraft 1.20.3
|
||||
*/
|
||||
public @Nullable Component displayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the score format, which determines how the scores are rendered in clients.
|
||||
*
|
||||
* @return score format
|
||||
* @since Minecraft 1.20.3
|
||||
*/
|
||||
public @Nullable ScoreFormat format() {
|
||||
return format;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ObjectiveScore{" +
|
||||
"value=" + value +
|
||||
", displayName=" + displayName +
|
||||
", format=" + format +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
ObjectiveScore that = (ObjectiveScore) o;
|
||||
|
||||
if (value != that.value) return false;
|
||||
if (!Objects.equals(displayName, that.displayName)) return false;
|
||||
return Objects.equals(format, that.format);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = value;
|
||||
result = 31 * result + (displayName != null ? displayName.hashCode() : 0);
|
||||
result = 31 * result + (format != null ? format.hashCode() : 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package net.megavex.scoreboardlibrary.api.objective;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import net.kyori.adventure.text.format.Style;
|
||||
import net.kyori.adventure.text.format.StyleBuilderApplicable;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static net.kyori.adventure.text.format.Style.style;
|
||||
|
||||
/**
|
||||
* Represents all valid objective score formats.
|
||||
*
|
||||
* @since Minecraft 1.20.3
|
||||
*/
|
||||
@ApiStatus.NonExtendable
|
||||
public interface ScoreFormat {
|
||||
static @NotNull Blank blank() {
|
||||
return Blank.INSTANCE;
|
||||
}
|
||||
|
||||
static @NotNull Fixed fixed(@NotNull ComponentLike content) {
|
||||
Preconditions.checkNotNull(content);
|
||||
return new Fixed(content.asComponent());
|
||||
}
|
||||
|
||||
static @NotNull Styled styled(@NotNull Style style) {
|
||||
Preconditions.checkNotNull(style);
|
||||
return new Styled(style);
|
||||
}
|
||||
|
||||
static @NotNull Styled styled(@NotNull StyleBuilderApplicable @NotNull ... styleBuilderApplicables) {
|
||||
Preconditions.checkNotNull(styleBuilderApplicables);
|
||||
return new Styled(style(styleBuilderApplicables));
|
||||
}
|
||||
|
||||
class Blank implements ScoreFormat {
|
||||
private static final Blank INSTANCE = new Blank();
|
||||
|
||||
private Blank() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Blank";
|
||||
}
|
||||
}
|
||||
|
||||
class Fixed implements ScoreFormat {
|
||||
private final Component content;
|
||||
|
||||
public Fixed(@NotNull Component content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public @NotNull Component content() {
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Fixed that = (Fixed) o;
|
||||
return content.equals(that.content);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return content.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Fixed{content=" + content + "}";
|
||||
}
|
||||
}
|
||||
|
||||
class Styled implements ScoreFormat {
|
||||
private final Style style;
|
||||
|
||||
public Styled(@NotNull Style style) {
|
||||
this.style = style;
|
||||
}
|
||||
|
||||
public @NotNull Style style() {
|
||||
return style;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
Styled that = (Styled) o;
|
||||
return style.equals(that.style);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return style.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Styled{style=" + style + "}";
|
||||
}
|
||||
}
|
||||
}
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
package net.megavex.scoreboardlibrary.api.objective;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* Represents a scoreboard objective.
|
||||
* To get an instance of this interface, use {@link ObjectiveManager#create}.
|
||||
* Note: this interface is not thread-safe, meaning you can only use it from one thread at a time,
|
||||
* although it does not have to be the main thread.
|
||||
*/
|
||||
public interface ScoreboardObjective {
|
||||
/**
|
||||
* @return the value (display name) of the objective, defaults to {@link Component#empty}
|
||||
*/
|
||||
@NotNull Component value();
|
||||
|
||||
/**
|
||||
* Sets the value (display name) of the objective.
|
||||
*
|
||||
* @param value new value
|
||||
*/
|
||||
@NotNull ScoreboardObjective value(@NotNull ComponentLike value);
|
||||
|
||||
/**
|
||||
* @return the render type of the objective, defaults to {@link ObjectiveRenderType#INTEGER}
|
||||
*/
|
||||
@NotNull ObjectiveRenderType renderType();
|
||||
|
||||
/**
|
||||
* Sets the render type of the objective.
|
||||
*
|
||||
* @param renderType new render type
|
||||
*/
|
||||
@NotNull ScoreboardObjective renderType(@NotNull ObjectiveRenderType renderType);
|
||||
|
||||
/**
|
||||
* @return the default score format for all scores in this objective, defaults to null
|
||||
*/
|
||||
@Nullable ScoreFormat defaultScoreFormat();
|
||||
|
||||
/**
|
||||
* Sets the default score format of this objective.
|
||||
*
|
||||
* @param defaultScoreFormat new default score format
|
||||
* @see #defaultScoreFormat()
|
||||
*/
|
||||
void defaultScoreFormat(@Nullable ScoreFormat defaultScoreFormat);
|
||||
|
||||
/**
|
||||
* Get the score for an entry, or null if the entry has no score.
|
||||
*
|
||||
* @param entry entry to get score of
|
||||
* @return the score value, or null if it doesn't exist for the entry
|
||||
*/
|
||||
@Nullable ObjectiveScore scoreInfo(@NotNull String entry);
|
||||
|
||||
/**
|
||||
* Gets the score value for an entry, or null if the entry has no score.
|
||||
*
|
||||
* @param entry entry to get score of
|
||||
* @return the score value, or null if it doesn't exist for the entry
|
||||
* @deprecated use {@link #scoreInfo(String)} and {@link ObjectiveScore#value()} instead
|
||||
*/
|
||||
@Deprecated
|
||||
default @Nullable Integer score(@NotNull String entry) {
|
||||
ObjectiveScore score = scoreInfo(entry);
|
||||
return score == null ? null : score.value();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the score of an entry.
|
||||
*
|
||||
* @param entry entry to update score of
|
||||
* @param score score
|
||||
*/
|
||||
@NotNull ScoreboardObjective score(@NotNull String entry, ObjectiveScore score);
|
||||
|
||||
/**
|
||||
* Updates the score of an entry.
|
||||
*
|
||||
* @param entry entry to update score of
|
||||
* @param scoreValue score value
|
||||
*/
|
||||
default @NotNull ScoreboardObjective score(@NotNull String entry, int scoreValue) {
|
||||
return score(entry, new ObjectiveScore(scoreValue, null, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the score of an entry.
|
||||
*
|
||||
* @param entry entry to update score of
|
||||
* @param scoreValue score value
|
||||
* @param displayName score display name
|
||||
* @param scoreFormat score format
|
||||
*/
|
||||
default @NotNull ScoreboardObjective score(
|
||||
@NotNull String entry,
|
||||
int scoreValue,
|
||||
@Nullable ComponentLike displayName,
|
||||
@Nullable ScoreFormat scoreFormat
|
||||
) {
|
||||
return score(entry, new ObjectiveScore(scoreValue, displayName, scoreFormat));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the score of an entry.
|
||||
*
|
||||
* @param entry entry to update score of
|
||||
* @param scoreValue score value
|
||||
* @param displayName score display name
|
||||
*/
|
||||
default @NotNull ScoreboardObjective score(
|
||||
@NotNull String entry,
|
||||
int scoreValue,
|
||||
@Nullable ComponentLike displayName
|
||||
) {
|
||||
return score(entry, new ObjectiveScore(scoreValue, displayName, null));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the score of an entry.
|
||||
*
|
||||
* @param entry entry to update score of
|
||||
* @param scoreValue score value
|
||||
* @param scoreFormat score format
|
||||
*/
|
||||
default @NotNull ScoreboardObjective score(
|
||||
@NotNull String entry,
|
||||
int scoreValue,
|
||||
@Nullable ScoreFormat scoreFormat
|
||||
) {
|
||||
return score(entry, new ObjectiveScore(scoreValue, null, scoreFormat));
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a score associated with an entry.
|
||||
*
|
||||
* @param entry entry to remove score of
|
||||
*/
|
||||
@NotNull ScoreboardObjective removeScore(@NotNull String entry);
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
package net.megavex.scoreboardlibrary.api.sidebar;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.util.function.Supplier;
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.megavex.scoreboardlibrary.api.util.SidebarUtilities;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
|
||||
import static net.kyori.adventure.text.Component.empty;
|
||||
|
||||
@NotThreadSafe
|
||||
public class AbstractSidebar {
|
||||
protected final Sidebar sidebar;
|
||||
private final Line[] lines;
|
||||
private boolean closed;
|
||||
|
||||
public AbstractSidebar(@NotNull Sidebar sidebar) {
|
||||
this.sidebar = sidebar;
|
||||
this.lines = new Line[sidebar.maxLines()];
|
||||
}
|
||||
|
||||
public final @NotNull Sidebar sidebar() {
|
||||
return sidebar;
|
||||
}
|
||||
|
||||
@ApiStatus.OverrideOnly
|
||||
protected void onClosed() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a static line for this AbstractSidebar
|
||||
*
|
||||
* @param line Line to register
|
||||
* @param value Static value
|
||||
*/
|
||||
protected final void registerStaticLine(@Range(from = 0, to = Sidebar.MAX_LINES - 1) int line, @NotNull Component value) {
|
||||
SidebarUtilities.checkLineBounds(sidebar.maxLines(), line);
|
||||
checkLineAvailable(line);
|
||||
Preconditions.checkNotNull(value);
|
||||
checkClosed();
|
||||
|
||||
lines[line] = new Line(line);
|
||||
sidebar.line(line, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a line that can change its value
|
||||
*
|
||||
* @param line Line to register
|
||||
* @param lineSupplier Line value supplier
|
||||
* @return The created LineSupplier
|
||||
*/
|
||||
protected final @NotNull AbstractSidebar.DynamicLine registerDynamicLine(@Range(from = 0, to = Sidebar.MAX_LINES - 1) int line, @NotNull Supplier<@Nullable Component> lineSupplier) {
|
||||
SidebarUtilities.checkLineBounds(sidebar.maxLines(), line);
|
||||
checkLineAvailable(line);
|
||||
Preconditions.checkNotNull(lineSupplier);
|
||||
checkClosed();
|
||||
|
||||
var sidebarLine = new DynamicLine(line, lineSupplier);
|
||||
lines[line] = sidebarLine;
|
||||
sidebar.line(line, lineSupplier.get());
|
||||
return sidebarLine;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers an empty line
|
||||
*
|
||||
* @param line Line to register
|
||||
*/
|
||||
protected final void registerEmptyLine(@Range(from = 0, to = Sidebar.MAX_LINES - 1) int line) {
|
||||
registerStaticLine(line, empty());
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a line
|
||||
*
|
||||
* @param line Line to unregister
|
||||
*/
|
||||
protected final void unregisterLine(@NotNull AbstractSidebar.Line line) {
|
||||
Preconditions.checkNotNull(line);
|
||||
checkClosed();
|
||||
|
||||
lines[line.line] = null;
|
||||
sidebar.line(line.line, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the {@link Line} of an index
|
||||
*
|
||||
* @param line Line
|
||||
* @return Supplier of line
|
||||
*/
|
||||
protected @Nullable AbstractSidebar.Line getLine(@Range(from = 0, to = Sidebar.MAX_LINES - 1) int line) {
|
||||
SidebarUtilities.checkLineBounds(sidebar.maxLines(), line);
|
||||
checkClosed();
|
||||
return lines[line];
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this AbstractSidebar
|
||||
*/
|
||||
public final void close() {
|
||||
close(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes this AbstractSidebar
|
||||
*
|
||||
* @param closeWrappedSidebar Whether to close the wrapped {@link Sidebar} or just clear all lines from it
|
||||
*/
|
||||
public final void close(boolean closeWrappedSidebar) {
|
||||
if (closed) return;
|
||||
closed = true;
|
||||
|
||||
if (closeWrappedSidebar) {
|
||||
sidebar.close();
|
||||
} else {
|
||||
for (var line : lines) {
|
||||
if (line != null) {
|
||||
sidebar.line(line.line, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onClosed();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Whether this AbstractSidebar is closed
|
||||
*/
|
||||
public final boolean closed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return sidebar.hashCode();
|
||||
}
|
||||
|
||||
protected final void checkClosed() {
|
||||
Preconditions.checkState(!closed, "AbstractSidebar is closed");
|
||||
}
|
||||
|
||||
private void checkLineAvailable(int line) {
|
||||
if (lines[line] != null) {
|
||||
throw new IllegalArgumentException("line " + line + " is already registered");
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class Line permits DynamicLine {
|
||||
protected final int line;
|
||||
|
||||
private Line(int line) {
|
||||
this.line = line;
|
||||
}
|
||||
}
|
||||
|
||||
public final class DynamicLine extends Line {
|
||||
private final Supplier<Component> function;
|
||||
|
||||
private DynamicLine(int line, @NotNull Supplier<Component> function) {
|
||||
super(line);
|
||||
this.function = function;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
sidebar.line(line, function.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,54 +1,88 @@
|
||||
package net.megavex.scoreboardlibrary.api.sidebar;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
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 org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
@ApiStatus.NonExtendable
|
||||
@NotThreadSafe
|
||||
public interface Sidebar {
|
||||
// Constants
|
||||
import java.util.Collection;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Represents a sidebar.
|
||||
* To get an instance of this interface, use {@link ScoreboardLibrary#createSidebar()}.
|
||||
* Note: this interface is not thread-safe, meaning you can only use it from one thread at a time,
|
||||
* although it does not have to be the main thread.
|
||||
*/
|
||||
@ApiStatus.NonExtendable
|
||||
public interface Sidebar {
|
||||
/**
|
||||
* The max amount of lines a vanilla client can display at once.
|
||||
*/
|
||||
int MAX_LINES = 15;
|
||||
|
||||
// Main
|
||||
|
||||
/**
|
||||
* @return Max amount of lines this sidebar can have
|
||||
* @return max amount of lines this sidebar can have
|
||||
*/
|
||||
@Range(from = 1, to = MAX_LINES) int maxLines();
|
||||
@Range(from = 1, to = Integer.MAX_VALUE) int maxLines();
|
||||
|
||||
/**
|
||||
* @return {@link Locale} which is used to translate {@link net.kyori.adventure.text.TranslatableComponent}s
|
||||
* @return objective name used by this sidebar
|
||||
*/
|
||||
@NotNull String objectiveName();
|
||||
|
||||
/**
|
||||
* @return locale which is used for translating {@link net.kyori.adventure.text.TranslatableComponent}s,
|
||||
* or null if it depends on each player's client locale.
|
||||
* @see ScoreboardLibrary#createSidebar(int, Locale)
|
||||
*/
|
||||
@Nullable Locale locale();
|
||||
|
||||
// Lines
|
||||
|
||||
/**
|
||||
* Gets a line's value
|
||||
* Gets a line's value, or null if line isn't set.
|
||||
*
|
||||
* @param line Line
|
||||
* @return Value of line
|
||||
* @param line line index
|
||||
* @return value of line, or null if unset
|
||||
*/
|
||||
@Nullable Component line(@Range(from = 0, to = MAX_LINES - 1) int line);
|
||||
@Nullable Component line(@Range(from = 0, to = Integer.MAX_VALUE - 1) int line);
|
||||
|
||||
/**
|
||||
* Sets a line's value
|
||||
* Sets a line's value.
|
||||
* A null value hides the line.
|
||||
*
|
||||
* @param line Line
|
||||
* @param value Value
|
||||
* @param index line index
|
||||
* @param value new value, or null to hide
|
||||
*/
|
||||
void line(@Range(from = 0, to = MAX_LINES - 1) int line, @Nullable Component value);
|
||||
default void line(@Range(from = 0, to = Integer.MAX_VALUE - 1) int index, @Nullable ComponentLike value) {
|
||||
line(index, value, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all lines
|
||||
* 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
|
||||
* @param value new value, or null to hide
|
||||
* @param scoreFormat score format
|
||||
*/
|
||||
void line(
|
||||
@Range(from = 0, to = Integer.MAX_VALUE - 1) int index,
|
||||
@Nullable ComponentLike value,
|
||||
@Nullable ScoreFormat scoreFormat
|
||||
);
|
||||
|
||||
/**
|
||||
* Clears all lines in this sidebar.
|
||||
*/
|
||||
default void clearLines() {
|
||||
for (int i = 0; i < maxLines(); i++) {
|
||||
@@ -59,46 +93,77 @@ public interface Sidebar {
|
||||
// Title
|
||||
|
||||
/**
|
||||
* @return Current title of this Sidebar
|
||||
* @return title of the sidebar, defaults to {@link Component#empty}
|
||||
*/
|
||||
@NotNull Component title();
|
||||
|
||||
/**
|
||||
* Sets the title
|
||||
* Sets the title of the sidebar.
|
||||
*
|
||||
* @param title Title
|
||||
* @param title title
|
||||
*/
|
||||
void title(@NotNull Component title);
|
||||
void title(@NotNull ComponentLike title);
|
||||
|
||||
// Players
|
||||
|
||||
/**
|
||||
* @return unmodifiable collection of viewers in this Sidebar
|
||||
* @see #addPlayer
|
||||
* @see #removePlayer
|
||||
*/
|
||||
@NotNull Collection<Player> players();
|
||||
|
||||
/**
|
||||
* Adds a viewer to this Sidebar.
|
||||
* Note that a player can only see a single Sidebar at a time.
|
||||
* The Sidebar will internally be added to a queue for this player who
|
||||
* will start seeing it once they are removed from all previous Sidebars.
|
||||
*
|
||||
* @param player player to add
|
||||
* @return whether the player was added
|
||||
*/
|
||||
boolean addPlayer(@NotNull Player player);
|
||||
|
||||
boolean removePlayer(@NotNull Player player);
|
||||
|
||||
/**
|
||||
* Adds multiple viewers to this Sidebar.
|
||||
*
|
||||
* @param players viewers to add
|
||||
* @see #addPlayer
|
||||
*/
|
||||
default void addPlayers(@NotNull Collection<Player> players) {
|
||||
for (var player : players) {
|
||||
for (Player player : players) {
|
||||
addPlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a viewer from this Sidebar.
|
||||
*
|
||||
* @param player viewer to remove
|
||||
* @return whether the viewer was removed
|
||||
*/
|
||||
boolean removePlayer(@NotNull Player player);
|
||||
|
||||
/**
|
||||
* Removes multiple viewers from this Sidebar
|
||||
*
|
||||
* @param players viewers to remove
|
||||
*/
|
||||
default void removePlayers(@NotNull Collection<Player> players) {
|
||||
for (var player : players) {
|
||||
for (Player player : players) {
|
||||
removePlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
// Close
|
||||
|
||||
/**
|
||||
* Closes this Sidebar
|
||||
* Closes this Sidebar.
|
||||
* This must be called once you no longer need this Sidebar to prevent a memory leak.
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* @return Whether this Sidebar is closed
|
||||
* @return Whether this sidebar is closed
|
||||
* @see #close
|
||||
*/
|
||||
boolean closed();
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
package net.megavex.scoreboardlibrary.api.sidebar.component;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import net.megavex.scoreboardlibrary.api.objective.ScoreFormat;
|
||||
import net.megavex.scoreboardlibrary.api.sidebar.Sidebar;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static net.kyori.adventure.text.Component.empty;
|
||||
|
||||
/**
|
||||
* Represents a component sidebar layout that can be applied to a {@link Sidebar}.
|
||||
*/
|
||||
public final class ComponentSidebarLayout {
|
||||
private final SidebarComponent titleComponent;
|
||||
private final SidebarComponent linesComponent;
|
||||
|
||||
public ComponentSidebarLayout(@NotNull SidebarComponent titleComponent, @NotNull SidebarComponent linesComponent) {
|
||||
Preconditions.checkNotNull(titleComponent);
|
||||
Preconditions.checkNotNull(linesComponent);
|
||||
this.titleComponent = titleComponent;
|
||||
this.linesComponent = linesComponent;
|
||||
}
|
||||
|
||||
public @NotNull SidebarComponent titleComponent() {
|
||||
return titleComponent;
|
||||
}
|
||||
|
||||
public @NotNull SidebarComponent linesComponent() {
|
||||
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);
|
||||
|
||||
SidebarTitleDrawable titleDrawable = new SidebarTitleDrawable();
|
||||
titleComponent.draw(titleDrawable);
|
||||
sidebar.title(titleDrawable.title == null ? empty() : titleDrawable.title);
|
||||
|
||||
SidebarLineDrawable linesDrawable = new SidebarLineDrawable(sidebar);
|
||||
linesComponent.draw(linesDrawable);
|
||||
|
||||
for (int i = linesDrawable.index; i < Sidebar.MAX_LINES; i++) {
|
||||
sidebar.line(i, null);
|
||||
}
|
||||
}
|
||||
|
||||
private static class SidebarTitleDrawable implements LineDrawable {
|
||||
private Component title;
|
||||
|
||||
@Override
|
||||
public void drawLine(@NotNull ComponentLike line, @Nullable ScoreFormat scoreFormat) {
|
||||
Preconditions.checkNotNull(line);
|
||||
|
||||
if (title == null) {
|
||||
title = line.asComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class SidebarLineDrawable implements LineDrawable {
|
||||
private final Sidebar sidebar;
|
||||
private int index = 0;
|
||||
|
||||
public SidebarLineDrawable(@NotNull Sidebar sidebar) {
|
||||
this.sidebar = sidebar;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawLine(@NotNull ComponentLike line, @Nullable ScoreFormat scoreFormat) {
|
||||
Preconditions.checkNotNull(line);
|
||||
|
||||
if (index < Sidebar.MAX_LINES) {
|
||||
sidebar.line(index++, line, scoreFormat);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package net.megavex.scoreboardlibrary.api.sidebar.component;
|
||||
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import net.megavex.scoreboardlibrary.api.objective.ScoreFormat;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* 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 the next line, or does nothing if it has reached a limit.
|
||||
*
|
||||
* @param line line component
|
||||
*/
|
||||
default void drawLine(@NotNull ComponentLike line) {
|
||||
drawLine(line, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
void drawLine(@NotNull ComponentLike line, @Nullable ScoreFormat scoreFormat);
|
||||
}
|
||||
+96
@@ -0,0 +1,96 @@
|
||||
package net.megavex.scoreboardlibrary.api.sidebar.component;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import net.megavex.scoreboardlibrary.api.objective.ScoreFormat;
|
||||
import net.megavex.scoreboardlibrary.api.sidebar.component.animation.SidebarAnimation;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static net.kyori.adventure.text.Component.empty;
|
||||
|
||||
public interface SidebarComponent {
|
||||
static @NotNull SidebarComponent staticLine(@NotNull ComponentLike line) {
|
||||
Preconditions.checkNotNull(line);
|
||||
return drawable -> drawable.drawLine(line);
|
||||
}
|
||||
|
||||
static @NotNull SidebarComponent staticLine(@NotNull ComponentLike line, @NotNull ScoreFormat scoreFormat) {
|
||||
Preconditions.checkNotNull(line);
|
||||
return drawable -> drawable.drawLine(line, scoreFormat);
|
||||
}
|
||||
|
||||
static @NotNull SidebarComponent blankLine() {
|
||||
return drawable -> drawable.drawLine(empty());
|
||||
}
|
||||
|
||||
static @NotNull <T extends ComponentLike> SidebarComponent dynamicLine(@NotNull Supplier<T> lineSupplier) {
|
||||
return drawable -> drawable.drawLine(lineSupplier.get());
|
||||
}
|
||||
|
||||
static @NotNull <T extends ComponentLike> SidebarComponent animatedLine(@NotNull SidebarAnimation<T> animation) {
|
||||
Preconditions.checkNotNull(animation);
|
||||
return drawable -> drawable.drawLine(animation.currentFrame());
|
||||
}
|
||||
|
||||
static @NotNull <T extends SidebarComponent> SidebarComponent animatedComponent(@NotNull SidebarAnimation<T> animation) {
|
||||
Preconditions.checkNotNull(animation);
|
||||
return drawable -> animation.currentFrame().draw(drawable);
|
||||
}
|
||||
|
||||
static @NotNull Builder builder() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
void draw(@NotNull LineDrawable drawable);
|
||||
|
||||
final class Builder {
|
||||
private final List<SidebarComponent> children = new ArrayList<>(4);
|
||||
|
||||
private Builder() {
|
||||
}
|
||||
|
||||
public @NotNull Builder addComponent(@NotNull SidebarComponent component) {
|
||||
Preconditions.checkNotNull(component);
|
||||
children.add(component);
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull Builder addStaticLine(@NotNull ComponentLike line) {
|
||||
return addComponent(SidebarComponent.staticLine(line));
|
||||
}
|
||||
|
||||
public @NotNull Builder addStaticLine(@NotNull ComponentLike line, @NotNull ScoreFormat scoreFormat) {
|
||||
return addComponent(SidebarComponent.staticLine(line, scoreFormat));
|
||||
}
|
||||
|
||||
public @NotNull Builder addBlankLine() {
|
||||
return addComponent(SidebarComponent.blankLine());
|
||||
}
|
||||
|
||||
public @NotNull <T extends ComponentLike> Builder addDynamicLine(@NotNull Supplier<T> lineSupplier) {
|
||||
return addComponent(SidebarComponent.dynamicLine(lineSupplier));
|
||||
}
|
||||
|
||||
public <T extends ComponentLike> @NotNull Builder addAnimatedLine(@NotNull SidebarAnimation<T> animation) {
|
||||
return addComponent(SidebarComponent.animatedLine(animation));
|
||||
}
|
||||
|
||||
public @NotNull <T extends SidebarComponent> Builder addAnimatedComponent(@NotNull SidebarAnimation<T> animation) {
|
||||
return addComponent(SidebarComponent.animatedComponent(animation));
|
||||
}
|
||||
|
||||
public @NotNull SidebarComponent build() {
|
||||
ImmutableList<SidebarComponent> children = ImmutableList.copyOf(this.children);
|
||||
return drawable -> {
|
||||
for (SidebarComponent child : children) {
|
||||
child.draw(drawable);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package net.megavex.scoreboardlibrary.api.sidebar.component.animation;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Implementation of {@link FramedSidebarAnimation} using a predefined collection of frames.
|
||||
*
|
||||
* @param <T> frame type
|
||||
*/
|
||||
public final class CollectionSidebarAnimation<T> implements FramedSidebarAnimation<T> {
|
||||
private final List<T> frames;
|
||||
private int currentFrameIndex;
|
||||
|
||||
public CollectionSidebarAnimation(@NotNull Collection<T> frames) {
|
||||
Preconditions.checkNotNull(frames);
|
||||
Preconditions.checkArgument(!frames.isEmpty());
|
||||
this.frames = ImmutableList.copyOf(frames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Unmodifiable @NotNull List<T> frames() {
|
||||
return frames;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int currentFrameIndex() {
|
||||
return currentFrameIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switchFrame(int frameIndex) {
|
||||
if (frameIndex < 0 || frameIndex >= frames.size()) {
|
||||
throw new ArrayIndexOutOfBoundsException();
|
||||
}
|
||||
|
||||
currentFrameIndex = frameIndex;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull T currentFrame() {
|
||||
return frames.get(currentFrameIndex);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void nextFrame() {
|
||||
if (++currentFrameIndex == frames.size()) {
|
||||
currentFrameIndex = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
package net.megavex.scoreboardlibrary.api.sidebar.component.animation;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Unmodifiable;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A {@link SidebarAnimation} with predetermined frames.
|
||||
*
|
||||
* @param <T> frame type
|
||||
*/
|
||||
public interface FramedSidebarAnimation<T> extends SidebarAnimation<T> {
|
||||
/**
|
||||
* @return all frames in this animation
|
||||
*/
|
||||
@Unmodifiable @NotNull List<T> frames();
|
||||
|
||||
/**
|
||||
* @return the index of the current frame
|
||||
*/
|
||||
int currentFrameIndex();
|
||||
|
||||
/**
|
||||
* Switches the current frame to specified index
|
||||
*
|
||||
* @param frameIndex the index of the new current frame
|
||||
* @throws ArrayIndexOutOfBoundsException if frame is out of bounds
|
||||
*/
|
||||
void switchFrame(int frameIndex);
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package net.megavex.scoreboardlibrary.api.sidebar.component.animation;
|
||||
|
||||
import net.megavex.scoreboardlibrary.api.sidebar.component.ComponentSidebarLayout;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* An animation that can be used with {@link ComponentSidebarLayout}s
|
||||
*
|
||||
* @param <T> frame type
|
||||
*/
|
||||
public interface SidebarAnimation<T> {
|
||||
/**
|
||||
* @return the current frame of the animation
|
||||
*/
|
||||
@NotNull T currentFrame();
|
||||
|
||||
/**
|
||||
* Advances to the next frame of the animation
|
||||
*/
|
||||
void nextFrame();
|
||||
}
|
||||
@@ -3,43 +3,56 @@ package net.megavex.scoreboardlibrary.api.team;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
@NotThreadSafe
|
||||
/**
|
||||
* Represents a scoreboard team.
|
||||
* To get an instance of this interface, use {@link TeamManager#createIfAbsent}.
|
||||
* Note: this interface is not thread-safe, meaning you can only use it from one thread at a time,
|
||||
* although it does not have to be the main thread.
|
||||
*
|
||||
* @see <a href="https://minecraft.fandom.com/wiki/Scoreboard#Teams">Minecraft Wiki</a>
|
||||
*/
|
||||
public interface ScoreboardTeam {
|
||||
/**
|
||||
* @return {@link TeamManager} of this team
|
||||
* @return TeamManager of this team
|
||||
*/
|
||||
@NotNull TeamManager teamManager();
|
||||
|
||||
/**
|
||||
* @return Name of this team
|
||||
* @return name of this team
|
||||
*/
|
||||
@NotNull String name();
|
||||
|
||||
/**
|
||||
* @return Default {@link TeamDisplay} of this team
|
||||
* Gets the default {@link TeamDisplay} of this team.
|
||||
* All players are added to this display by default.
|
||||
*
|
||||
* @return default team display
|
||||
*/
|
||||
@NotNull TeamDisplay defaultDisplay();
|
||||
|
||||
/**
|
||||
* Gets the {@link TeamDisplay} of a player
|
||||
* Gets the {@link TeamDisplay} that a player sees.
|
||||
*
|
||||
* @param player Player
|
||||
* @return {@link TeamDisplay} of this player
|
||||
* @param player player
|
||||
* @return team display the player sees
|
||||
* @throws IllegalArgumentException if player is not in the TeamManager
|
||||
*/
|
||||
@NotNull TeamDisplay display(@NotNull Player player);
|
||||
|
||||
/**
|
||||
* Changes a player's visible {@link TeamDisplay}
|
||||
* Updates the team {@link TeamDisplay} that a player sees for this team.
|
||||
*
|
||||
* @param player Player
|
||||
* @param teamDisplay New {@link TeamDisplay} of Player
|
||||
* @param player player
|
||||
* @param teamDisplay new {@link TeamDisplay} of player
|
||||
* @throws IllegalArgumentException if player is not in the TeamManager
|
||||
*/
|
||||
void display(@NotNull Player player, @NotNull TeamDisplay teamDisplay);
|
||||
|
||||
/**
|
||||
* @return Newly created {@link TeamDisplay} which is assigned to this team
|
||||
* Creates a new {@link TeamDisplay}.
|
||||
* To show it to players, use {@link #display(Player, TeamDisplay)}.
|
||||
*
|
||||
* @return newly created team display
|
||||
*/
|
||||
@NotNull TeamDisplay createDisplay();
|
||||
}
|
||||
@@ -1,139 +1,173 @@
|
||||
package net.megavex.scoreboardlibrary.api.team;
|
||||
|
||||
import java.util.Collection;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.megavex.scoreboardlibrary.api.objective.ObjectiveDisplaySlot;
|
||||
import net.megavex.scoreboardlibrary.api.team.enums.CollisionRule;
|
||||
import net.megavex.scoreboardlibrary.api.team.enums.NameTagVisibility;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Represents the display properties of a scoreboard team.
|
||||
* Note: this interface is not thread-safe, meaning you can only use it from one thread at a time,
|
||||
* although it does not have to be the main thread.
|
||||
*/
|
||||
@ApiStatus.NonExtendable
|
||||
@NotThreadSafe
|
||||
public interface TeamDisplay {
|
||||
/**
|
||||
* @return Team which is assigned to this {@link TeamDisplay}
|
||||
* @return team which this team display is assigned to
|
||||
*/
|
||||
@NotNull ScoreboardTeam team();
|
||||
|
||||
// Entries
|
||||
|
||||
/**
|
||||
* Gets the entries. The entries specify which players are members of the team.
|
||||
*
|
||||
* @return entries of the team.
|
||||
*/
|
||||
@NotNull Collection<String> entries();
|
||||
|
||||
/**
|
||||
* Adds an entry
|
||||
* Adds an entry.
|
||||
*
|
||||
* @param entry Entry to add
|
||||
* @return Whether the entry was added
|
||||
* @param entry entry to add
|
||||
* @return whether the entry was added
|
||||
* @see #entries
|
||||
*/
|
||||
boolean addEntry(@NotNull String entry);
|
||||
|
||||
/**
|
||||
* Removes an entry
|
||||
* Removes an entry.
|
||||
*
|
||||
* @param entry Entry to remove
|
||||
* @return Whether the entry was removed
|
||||
* @param entry entry to remove
|
||||
* @return whether the entry was removed
|
||||
* @see #entries
|
||||
*/
|
||||
boolean removeEntry(@NotNull String entry);
|
||||
|
||||
// Properties
|
||||
|
||||
/**
|
||||
* @return Display name
|
||||
* @return display name, defaults to {@link Component#empty()}
|
||||
*/
|
||||
@NotNull Component displayName();
|
||||
|
||||
/**
|
||||
* Sets the display name
|
||||
* Updates the display name.
|
||||
*
|
||||
* @param displayName Display name
|
||||
* @param displayName new display name
|
||||
*/
|
||||
@NotNull TeamDisplay displayName(@NotNull Component displayName);
|
||||
@NotNull TeamDisplay displayName(@NotNull ComponentLike displayName);
|
||||
|
||||
/**
|
||||
* @return Prefix
|
||||
* Gets the prefix, which defaults to {@link Component#empty()}.
|
||||
* The suffix is prepended to all team members' names.
|
||||
*
|
||||
* @return prefix, defaults to {@link Component#empty()}
|
||||
*/
|
||||
@NotNull Component prefix();
|
||||
|
||||
/**
|
||||
* Sets the prefix
|
||||
* Updates the prefix.
|
||||
*
|
||||
* @param prefix Prefix
|
||||
* @param prefix new prefix
|
||||
* @see #prefix()
|
||||
*/
|
||||
@NotNull TeamDisplay prefix(@NotNull Component prefix);
|
||||
@NotNull TeamDisplay prefix(@NotNull ComponentLike prefix);
|
||||
|
||||
/**
|
||||
* @return Suffix
|
||||
* Gets the suffix, which defaults to {@link Component#empty()}.
|
||||
* The suffix is appended to all team members' names.
|
||||
*
|
||||
* @return suffix
|
||||
*/
|
||||
@NotNull Component suffix();
|
||||
|
||||
/**
|
||||
* Sets the suffix
|
||||
* Updates the suffix.
|
||||
*
|
||||
* @param suffix Suffix
|
||||
* @param suffix new suffix
|
||||
* @see #suffix()
|
||||
*/
|
||||
@NotNull TeamDisplay suffix(@NotNull Component suffix);
|
||||
@NotNull TeamDisplay suffix(@NotNull ComponentLike suffix);
|
||||
|
||||
/**
|
||||
* @return Friendly fire rule
|
||||
* @return friendly fire rule value, defaults to false
|
||||
*/
|
||||
boolean friendlyFire();
|
||||
|
||||
/**
|
||||
* Sets whether friendly fire is allowed
|
||||
* Updates the friendly fire rule.
|
||||
*
|
||||
* @param friendlyFire whether friendly fire is allowed
|
||||
* @param friendlyFire new rule value
|
||||
*/
|
||||
@NotNull TeamDisplay friendlyFire(boolean friendlyFire);
|
||||
|
||||
/**
|
||||
* @return Can see friendly invisibles rule
|
||||
* Gets the can see friendly invisibles rule value, which is false by default.
|
||||
* If true, team members can see other members with the invisibility effect.
|
||||
*
|
||||
* @return rule value
|
||||
*/
|
||||
boolean canSeeFriendlyInvisibles();
|
||||
|
||||
/**
|
||||
* Sets whether players can see friendly invisibles
|
||||
* Updates the can see friendly invisibles rule.
|
||||
*
|
||||
* @param canSeeFriendlyInvisibles whether players can see friendly invisibles
|
||||
* @param canSeeFriendlyInvisibles new rule value
|
||||
* @see #canSeeFriendlyInvisibles()
|
||||
*/
|
||||
@NotNull TeamDisplay canSeeFriendlyInvisibles(boolean canSeeFriendlyInvisibles);
|
||||
|
||||
/**
|
||||
* @return Name tag visibility rule
|
||||
* @return name tag visibility rule value
|
||||
* @since Minecraft 1.8
|
||||
*/
|
||||
@NotNull NameTagVisibility nameTagVisibility();
|
||||
|
||||
/**
|
||||
* Sets the {@link NameTagVisibility}
|
||||
* Updates the name tag visibility rule.
|
||||
*
|
||||
* @param nameTagVisibility Name tag visibility rule
|
||||
* @param nameTagVisibility new rule value
|
||||
* @since Minecraft 1.8
|
||||
*/
|
||||
@NotNull TeamDisplay nameTagVisibility(@NotNull NameTagVisibility nameTagVisibility);
|
||||
|
||||
/**
|
||||
* @return Collision rule
|
||||
* @return collision rule
|
||||
* @since Minecraft 1.9
|
||||
*/
|
||||
@NotNull CollisionRule collisionRule();
|
||||
|
||||
/**
|
||||
* Sets the {@link CollisionRule}.
|
||||
* Updates the collision rule.
|
||||
*
|
||||
* @param collisionRule Collision rule
|
||||
* @param collisionRule new rule value
|
||||
* @since Minecraft 1.9
|
||||
*/
|
||||
@NotNull TeamDisplay collisionRule(@NotNull CollisionRule collisionRule);
|
||||
|
||||
/**
|
||||
* @return Player color
|
||||
* Gets the player color. The player color is the color player name tags are displayed in.
|
||||
* 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();
|
||||
|
||||
/**
|
||||
* Sets the player color
|
||||
* Updates the player color.
|
||||
*
|
||||
* @param playerColor Player color
|
||||
* @param playerColor new player color
|
||||
* @see #playerColor()
|
||||
* @since Minecraft 1.8
|
||||
*/
|
||||
@NotNull TeamDisplay playerColor(@Nullable NamedTextColor playerColor);
|
||||
}
|
||||
@@ -1,139 +1,153 @@
|
||||
package net.megavex.scoreboardlibrary.api.team;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
import net.megavex.scoreboardlibrary.api.ScoreboardLibrary;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Represents a group of {@link ScoreboardTeam}s.
|
||||
* To get an instance of this interface, use {@link ScoreboardLibrary#createTeamManager()}.
|
||||
* Note: this interface is not thread-safe, meaning you can only use it from one thread at a time,
|
||||
* although it does not have to be the main thread.
|
||||
*/
|
||||
@ApiStatus.NonExtendable
|
||||
@NotThreadSafe
|
||||
public interface TeamManager {
|
||||
// Teams
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable set of teams in this team manager
|
||||
*
|
||||
* @return Teams
|
||||
* @return unmodifiable collection of teams in this TeamManager
|
||||
*/
|
||||
@NotNull Collection<ScoreboardTeam> teams();
|
||||
|
||||
/**
|
||||
* Returns team based on its name
|
||||
* Gets a team by its name.
|
||||
*
|
||||
* @param name Name of team
|
||||
* @return Team with this name
|
||||
* @param name name of team
|
||||
* @return team with the name, or null if one doesn't exist
|
||||
*/
|
||||
@Nullable ScoreboardTeam team(@NotNull String name);
|
||||
|
||||
/**
|
||||
* Returns whether a team by the name exists
|
||||
* Check whether a team with a name exists.
|
||||
*
|
||||
* @param name Name of team
|
||||
* @return Whether a team by the name exists
|
||||
* @param name name of team
|
||||
* @return whether a team by the name exists
|
||||
*/
|
||||
boolean teamExists(@NotNull String name);
|
||||
|
||||
/**
|
||||
* Returns team based on its name. If it doesn't already exist, creates it
|
||||
* Creates a team with a name if one doesn't already exist and returns it.
|
||||
*
|
||||
* @param name Name of team
|
||||
* @return Existing or newly created team with the name
|
||||
* @param name name of team
|
||||
* @return team
|
||||
*/
|
||||
default @NotNull ScoreboardTeam createIfAbsent(@NotNull String name) {
|
||||
return createIfAbsent(name, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a team based on its name. If it doesn't already exist, it creates it
|
||||
* Creates a team with a name if one doesn't already exist and returns it.
|
||||
*
|
||||
* @param name Name of team
|
||||
* @param teamDisplayFunction A function that provides the team display to set for each player
|
||||
* @return Team with this name
|
||||
* @param name name of team
|
||||
* @param teamDisplayFunction a function that provides the team display to set for each player
|
||||
* @return team
|
||||
*/
|
||||
@NotNull ScoreboardTeam createIfAbsent(@NotNull String name, @Nullable BiFunction<Player, ScoreboardTeam, TeamDisplay> teamDisplayFunction);
|
||||
|
||||
/**
|
||||
* Removes a team
|
||||
* Removes a team by its name.
|
||||
*
|
||||
* @param name Name of team
|
||||
* @return If there was a team with than name
|
||||
* @param name name of team
|
||||
* @return if there was a team with than name
|
||||
*/
|
||||
boolean removeTeam(@NotNull String name);
|
||||
|
||||
/**
|
||||
* Removes a team
|
||||
* Removes a team.
|
||||
*
|
||||
* @param team Team
|
||||
* @throws IllegalArgumentException If the provided team is not owned by this TeamManager
|
||||
* @param team team to remove
|
||||
* @throws IllegalArgumentException if the provided team is not owned by this TeamManager
|
||||
*/
|
||||
void removeTeam(@NotNull ScoreboardTeam team);
|
||||
|
||||
// Players
|
||||
|
||||
/**
|
||||
* @return Players in this TeamManager
|
||||
* @return unmodifiable collection of viewers in this TeamManager
|
||||
* @see #addPlayer
|
||||
* @see #removePlayer
|
||||
*/
|
||||
@NotNull Collection<Player> players();
|
||||
|
||||
/**
|
||||
* Adds a player to this TeamManager
|
||||
* Adds a viewer to this TeamManager.
|
||||
* Note that a player can only see a single TeamManager at a time.
|
||||
* 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 to TeamManager
|
||||
* @return Whether the player was added
|
||||
* @param player player to add
|
||||
* @return whether the player was added
|
||||
*/
|
||||
default boolean addPlayer(@NotNull Player player) {
|
||||
return addPlayer(player, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a player to this TeamManager
|
||||
* Adds a viewer to this TeamManager.
|
||||
* Note that a player can only see a single TeamManager at a time.
|
||||
* 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 teamDisplayFunction A function that provides the team display to set for each team
|
||||
* @return Whether the player was added
|
||||
* @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
|
||||
*/
|
||||
boolean addPlayer(@NotNull Player player, @Nullable Function<ScoreboardTeam, TeamDisplay> teamDisplayFunction);
|
||||
|
||||
/**
|
||||
* Adds a player to this TeamManager
|
||||
* Adds multiple viewers to this TeamManager.
|
||||
*
|
||||
* @param players Players to add
|
||||
* @param players viewers to add
|
||||
* @see #addPlayer
|
||||
*/
|
||||
default void addPlayers(@NotNull Collection<Player> players) {
|
||||
addPlayers(players, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a collection of players to this TeamManager
|
||||
* Adds multiple viewers to this TeamManager.
|
||||
*
|
||||
* @param players Players to add
|
||||
* @param teamDisplayFunction A function that provides the team display to set for each team
|
||||
* @param players viewers to add
|
||||
* @param teamDisplayFunction a function that provides the team display to set for each team
|
||||
* @see #addPlayer
|
||||
*/
|
||||
default void addPlayers(@NotNull Collection<Player> players, @Nullable Function<ScoreboardTeam, TeamDisplay> teamDisplayFunction) {
|
||||
for (var player : players) {
|
||||
for (Player player : players) {
|
||||
addPlayer(player, teamDisplayFunction);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a player from this TeamManager
|
||||
* Removes a viewer from this TeamManager.
|
||||
*
|
||||
* @param player Player to remove
|
||||
* @return Whether the player wasn't already in this TeamManager
|
||||
* @param player viewer to remove
|
||||
* @return whether the viewer was removed
|
||||
*/
|
||||
boolean removePlayer(@NotNull Player player);
|
||||
|
||||
/**
|
||||
* Removes a collection of players from this TeamManager
|
||||
* Removes multiple viewers from this TeamManager.
|
||||
*
|
||||
* @param players Players to remove
|
||||
* @param players viewers to remove
|
||||
*/
|
||||
default void removePlayers(@NotNull Collection<Player> players) {
|
||||
for (var player : players) {
|
||||
for (Player player : players) {
|
||||
removePlayer(player);
|
||||
}
|
||||
}
|
||||
@@ -141,12 +155,14 @@ public interface TeamManager {
|
||||
// Close
|
||||
|
||||
/**
|
||||
* Closes this TeamManager
|
||||
* Closes this TeamManager.
|
||||
* This must be called once you no longer need this TeamManager to prevent a memory leak.
|
||||
*/
|
||||
void close();
|
||||
|
||||
/**
|
||||
* @return Whether this TeamManager is closed
|
||||
* @see #close
|
||||
*/
|
||||
boolean closed();
|
||||
}
|
||||
@@ -2,29 +2,23 @@ package net.megavex.scoreboardlibrary.api.team.enums;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* Represents all possible collision rule values of a team.
|
||||
*
|
||||
* @since Minecraft 1.9
|
||||
*/
|
||||
public enum CollisionRule {
|
||||
ALWAYS("always"),
|
||||
NEVER("never"),
|
||||
PUSH_OTHER_TEAMS("pushOtherTeams"),
|
||||
PUSH_OWN_TEAM("pushOwnTeam");
|
||||
|
||||
private static final CollisionRule[] values = values();
|
||||
private final String key;
|
||||
|
||||
CollisionRule(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static CollisionRule of(byte id) {
|
||||
return values[id];
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public byte id() {
|
||||
return (byte) ordinal();
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public String key() {
|
||||
return this.key;
|
||||
|
||||
+3
-11
@@ -2,29 +2,21 @@ package net.megavex.scoreboardlibrary.api.team.enums;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
/**
|
||||
* Represents all possible name tag visibility rule values of a team.
|
||||
*/
|
||||
public enum NameTagVisibility {
|
||||
ALWAYS("always"),
|
||||
NEVER("never"),
|
||||
HIDE_FOR_OTHER_TEAMS("hideForOtherTeams"),
|
||||
HIDE_FOR_OWN_TEAM("hideForOwnTeam");
|
||||
|
||||
private static final NameTagVisibility[] values = values();
|
||||
private final String key;
|
||||
|
||||
NameTagVisibility(String key) {
|
||||
this.key = key;
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static NameTagVisibility of(byte id) {
|
||||
return values[id];
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public byte id() {
|
||||
return (byte) ordinal();
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
public String key() {
|
||||
return this.key;
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
package net.megavex.scoreboardlibrary.api.util;
|
||||
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
|
||||
@ApiStatus.Internal
|
||||
public final class SidebarUtilities {
|
||||
private SidebarUtilities() {
|
||||
}
|
||||
|
||||
public static void checkLineBounds(int maxLines, int line) {
|
||||
if (line >= maxLines || line < 0) {
|
||||
throw new IndexOutOfBoundsException("invalid line " + line);
|
||||
}
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package net.megavex.scoreboardlibrary.api.animation;
|
||||
|
||||
import net.megavex.scoreboardlibrary.api.sidebar.component.animation.CollectionSidebarAnimation;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class CollectionAnimationTest {
|
||||
@Test
|
||||
void loopTest() {
|
||||
List<Integer> frames = Arrays.asList(0, 1, 2);
|
||||
CollectionSidebarAnimation<Integer> animation = new CollectionSidebarAnimation<>(frames);
|
||||
assertEquals(0, animation.currentFrame());
|
||||
animation.nextFrame();
|
||||
assertEquals(1, animation.currentFrame());
|
||||
animation.nextFrame();
|
||||
assertEquals(2, animation.currentFrame());
|
||||
animation.nextFrame();
|
||||
assertEquals(0, animation.currentFrame());
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
package net.megavex.scoreboardlibrary.api.sidebar.component;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.megavex.scoreboardlibrary.api.noop.NoopScoreboardLibrary;
|
||||
import net.megavex.scoreboardlibrary.api.sidebar.Sidebar;
|
||||
import net.megavex.scoreboardlibrary.api.sidebar.component.animation.CollectionSidebarAnimation;
|
||||
import net.megavex.scoreboardlibrary.api.sidebar.component.animation.SidebarAnimation;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import static net.kyori.adventure.text.Component.text;
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class ComponentSidebarLayoutTest {
|
||||
private final Sidebar sidebar = new NoopScoreboardLibrary().createSidebar();
|
||||
|
||||
@Test
|
||||
void maxLines() {
|
||||
SidebarComponent.Builder builder = SidebarComponent.builder();
|
||||
for (int i = 0; i < Sidebar.MAX_LINES + 1; i++) {
|
||||
builder.addComponent(SidebarComponent.staticLine(text(i)));
|
||||
}
|
||||
|
||||
ComponentSidebarLayout componentSidebar = new ComponentSidebarLayout(drawable -> {
|
||||
}, builder.build());
|
||||
|
||||
componentSidebar.apply(sidebar);
|
||||
|
||||
for (int i = 0; i < Sidebar.MAX_LINES; i++) {
|
||||
assertNotNull(sidebar.line(i));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void titleComponent() {
|
||||
Component title = text("title");
|
||||
ComponentSidebarLayout componentSidebar = new ComponentSidebarLayout(SidebarComponent.staticLine(title), drawable -> {
|
||||
});
|
||||
componentSidebar.apply(sidebar);
|
||||
assertEquals(title, sidebar.title());
|
||||
}
|
||||
|
||||
@Test
|
||||
void animatedLines() {
|
||||
SidebarAnimation<Component> animation = new CollectionSidebarAnimation<>(Arrays.asList(text("frame 1"), text("frame 2")));
|
||||
SidebarComponent lines = SidebarComponent.builder().addAnimatedLine(animation).build();
|
||||
ComponentSidebarLayout componentSidebar = new ComponentSidebarLayout(drawable -> {
|
||||
}, lines);
|
||||
|
||||
componentSidebar.apply(sidebar);
|
||||
assertEquals(animation.currentFrame(), sidebar.line(0));
|
||||
|
||||
animation.nextFrame();
|
||||
componentSidebar.apply(sidebar);
|
||||
assertEquals(animation.currentFrame(), sidebar.line(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void animatedComponents() {
|
||||
Component frame1Line = text("frame with one line");
|
||||
Component frame2Line1 = text("frame with");
|
||||
Component frame2Line2 = text("two lines");
|
||||
|
||||
SidebarComponent frame1 = SidebarComponent.staticLine(frame1Line);
|
||||
SidebarComponent frame2 = drawable -> {
|
||||
drawable.drawLine(frame2Line1);
|
||||
drawable.drawLine(frame2Line2);
|
||||
};
|
||||
|
||||
SidebarAnimation<SidebarComponent> animation = new CollectionSidebarAnimation<>(Arrays.asList(frame1, frame2));
|
||||
SidebarComponent lines = SidebarComponent.builder().addAnimatedComponent(animation).build();
|
||||
ComponentSidebarLayout componentSidebar = new ComponentSidebarLayout(drawable -> {
|
||||
}, lines);
|
||||
|
||||
componentSidebar.apply(sidebar);
|
||||
assertEquals(frame1Line, sidebar.line(0));
|
||||
assertNull(sidebar.line(1));
|
||||
|
||||
animation.nextFrame();
|
||||
componentSidebar.apply(sidebar);
|
||||
assertEquals(frame2Line1, sidebar.line(0));
|
||||
assertEquals(frame2Line2, sidebar.line(1));
|
||||
|
||||
animation.nextFrame();
|
||||
componentSidebar.apply(sidebar);
|
||||
assertEquals(frame1Line, sidebar.line(0));
|
||||
assertNull(sidebar.line(1));
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 931 KiB |
+2
-37
@@ -1,40 +1,5 @@
|
||||
plugins {
|
||||
`java-library`
|
||||
}
|
||||
|
||||
allprojects {
|
||||
version = "2.0.0-RC5"
|
||||
version = "2.3.2"
|
||||
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://papermc.io/repo/repository/maven-public/")
|
||||
}
|
||||
}
|
||||
|
||||
subprojects {
|
||||
apply(plugin = "java-library")
|
||||
|
||||
dependencies {
|
||||
compileOnly("io.papermc.paper:paper-api:1.19.3-R0.1-SNAPSHOT")
|
||||
|
||||
val adventureVersion = "4.12.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")
|
||||
testImplementation("net.kyori:adventure-text-serializer-legacy:$adventureVersion")
|
||||
testImplementation("net.kyori:adventure-text-serializer-gson:$adventureVersion")
|
||||
}
|
||||
|
||||
tasks.withType<JavaCompile>().configureEach {
|
||||
sourceCompatibility = JavaVersion.VERSION_17.toString()
|
||||
targetCompatibility = JavaVersion.VERSION_17.toString()
|
||||
options.encoding = "UTF-8"
|
||||
options.isIncremental = true
|
||||
options.compilerArgs = mutableListOf("-Xlint:-deprecation,-unchecked")
|
||||
}
|
||||
description = "Powerful packet-level scoreboard library for Paper/Spigot servers"
|
||||
}
|
||||
@@ -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,70 @@
|
||||
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/")
|
||||
}
|
||||
|
||||
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 +1,8 @@
|
||||
plugins {
|
||||
id("net.megavex.scoreboardlibrary.publish-conventions")
|
||||
id("net.megavex.scoreboardlibrary.base-conventions")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":scoreboard-library-api"))
|
||||
compileOnly(libs.spigotApi)
|
||||
}
|
||||
+4
-8
@@ -1,23 +1,19 @@
|
||||
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.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import java.util.*;
|
||||
|
||||
public final class CollectionProvider {
|
||||
private static final MethodHandle mapConstructor, setConstructor, listConstructor;
|
||||
|
||||
static {
|
||||
Lookup lookup = MethodHandles.publicLookup();
|
||||
var parameters = MethodType.methodType(void.class, int.class);
|
||||
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);
|
||||
|
||||
+20
-11
@@ -1,14 +1,16 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.commons;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyFormat;
|
||||
import net.kyori.adventure.translation.GlobalTranslator;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import static net.kyori.adventure.text.Component.empty;
|
||||
import static net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection;
|
||||
@@ -18,13 +20,13 @@ public final class LegacyFormatUtil {
|
||||
private static final Map<NamedTextColor, Character> legacyMap;
|
||||
|
||||
static {
|
||||
var values = ChatColor.values();
|
||||
ChatColor[] values = ChatColor.values();
|
||||
legacyMap = CollectionProvider.map(values.length);
|
||||
for (var value : values) {
|
||||
for (ChatColor value : values) {
|
||||
if (!value.isColor()) continue;
|
||||
|
||||
char c = value.getChar();
|
||||
var format = Objects.requireNonNull(parseChar(c));
|
||||
LegacyFormat format = Objects.requireNonNull(parseChar(c));
|
||||
legacyMap.put((NamedTextColor) format.color(), c);
|
||||
}
|
||||
}
|
||||
@@ -37,21 +39,28 @@ public final class LegacyFormatUtil {
|
||||
return text;
|
||||
}
|
||||
|
||||
var lastNotColorCharIndex = limit - 1;
|
||||
while (text.charAt(lastNotColorCharIndex) == ChatColor.COLOR_CHAR) {
|
||||
int lastNotColorCharIndex = limit - 1;
|
||||
while (text.charAt(lastNotColorCharIndex) == LegacyComponentSerializer.SECTION_CHAR) {
|
||||
lastNotColorCharIndex--;
|
||||
}
|
||||
|
||||
return text.substring(0, lastNotColorCharIndex + 1);
|
||||
}
|
||||
|
||||
public static String serialize(@Nullable Component component, Locale locale) {
|
||||
public static String serialize(@Nullable Component component, @Nullable Locale locale) {
|
||||
if (component == null || component == empty()) return "";
|
||||
|
||||
return legacySection().serialize(GlobalTranslator.render(component, locale));
|
||||
Component translated;
|
||||
if (locale != null) {
|
||||
translated = GlobalTranslator.render(component, locale);
|
||||
} else {
|
||||
translated = component;
|
||||
}
|
||||
|
||||
return legacySection().serialize(translated);
|
||||
}
|
||||
|
||||
public static char getChar(NamedTextColor color) {
|
||||
public static char getChar(@Nullable NamedTextColor color) {
|
||||
if (color == null) return 'r';
|
||||
|
||||
return legacyMap.getOrDefault(color, '\0');
|
||||
|
||||
+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-1.20.2, 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
|
||||
}
|
||||
+22
-15
@@ -1,19 +1,28 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.commons;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.Locale;
|
||||
import net.kyori.adventure.translation.Translator;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface LocaleProvider {
|
||||
Locale DEFAULT_LOCALE = Locale.US;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Function;
|
||||
|
||||
static @NotNull LocaleProvider localeProvider() {
|
||||
var lookup = MethodHandles.publicLookup();
|
||||
public final class LocaleProvider {
|
||||
private static final Locale DEFAULT_LOCALE = Locale.US;
|
||||
private static final Function<Player, Locale> provider = get();
|
||||
|
||||
public static @NotNull Locale locale(Player player) {
|
||||
return provider.apply(player);
|
||||
}
|
||||
|
||||
private static @NotNull Function<Player, Locale> get() {
|
||||
MethodHandles.Lookup lookup = MethodHandles.publicLookup();
|
||||
try {
|
||||
var adventureMethod = lookup.findVirtual(Player.class, "locale", MethodType.methodType(Locale.class));
|
||||
@SuppressWarnings("JavaLangInvokeHandleSignature")
|
||||
MethodHandle adventureMethod = lookup.findVirtual(Player.class, "locale", MethodType.methodType(Locale.class));
|
||||
return player -> {
|
||||
try {
|
||||
return (Locale) adventureMethod.invokeExact(player);
|
||||
@@ -24,12 +33,12 @@ public interface LocaleProvider {
|
||||
} catch (IllegalAccessException | NoSuchMethodException ignored) {
|
||||
}
|
||||
|
||||
var methodType = MethodType.methodType(String.class);
|
||||
MethodType methodType = MethodType.methodType(String.class);
|
||||
try {
|
||||
var legacySpigotMethod = lookup.findVirtual(Player.Spigot.class, "getLocale", methodType);
|
||||
MethodHandle legacySpigotMethod = lookup.findVirtual(Player.Spigot.class, "getLocale", methodType);
|
||||
return player -> {
|
||||
try {
|
||||
var locale = Translator.parseLocale((String) legacySpigotMethod.invokeExact(player.spigot()));
|
||||
Locale locale = Translator.parseLocale((String) legacySpigotMethod.invokeExact(player.spigot()));
|
||||
return locale == null ? DEFAULT_LOCALE : locale;
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
@@ -39,10 +48,10 @@ public interface LocaleProvider {
|
||||
}
|
||||
|
||||
try {
|
||||
var legacyMethod = lookup.findVirtual(Player.class, "getLocale", methodType);
|
||||
MethodHandle legacyMethod = lookup.findVirtual(Player.class, "getLocale", methodType);
|
||||
return player -> {
|
||||
try {
|
||||
var locale = Translator.parseLocale((String) legacyMethod.invokeExact(player));
|
||||
@org.jetbrains.annotations.Nullable Locale locale = Translator.parseLocale((String) legacyMethod.invokeExact(player));
|
||||
return locale == null ? DEFAULT_LOCALE : locale;
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
@@ -52,6 +61,4 @@ public interface LocaleProvider {
|
||||
throw new RuntimeException("No way to get players locale found");
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull Locale locale(@NotNull Player player);
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
||||
|
||||
plugins {
|
||||
kotlin("jvm") version "1.7.21"
|
||||
id("net.megavex.scoreboardlibrary.publish-conventions")
|
||||
id("net.megavex.scoreboardlibrary.base-conventions")
|
||||
alias(libs.plugins.kotlin)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
@@ -10,8 +12,11 @@ kotlin {
|
||||
dependencies {
|
||||
api(project(":scoreboard-library-api"))
|
||||
testImplementation(kotlin("test"))
|
||||
compileOnly(libs.spigotApi)
|
||||
}
|
||||
|
||||
tasks.test {
|
||||
useJUnitPlatform()
|
||||
tasks.withType<KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package net.megavex.scoreboardlibrary.extra.kotlin
|
||||
|
||||
import net.megavex.scoreboardlibrary.api.sidebar.component.ComponentSidebarLayout
|
||||
import net.megavex.scoreboardlibrary.api.sidebar.component.SidebarComponent
|
||||
|
||||
public inline val ComponentSidebarLayout.titleComponent: SidebarComponent
|
||||
get() = titleComponent()
|
||||
|
||||
public inline val ComponentSidebarLayout.linesComponent: SidebarComponent
|
||||
get() = linesComponent()
|
||||
@@ -1,93 +0,0 @@
|
||||
package net.megavex.scoreboardlibrary.extra.kotlin
|
||||
|
||||
import net.kyori.adventure.text.Component
|
||||
import net.kyori.adventure.text.Component.empty
|
||||
import net.megavex.scoreboardlibrary.api.sidebar.Sidebar
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
public fun Sidebar.lines(
|
||||
direction: LinesBuilder.Direction = LinesBuilder.Direction.TOP_TO_BOTTOM,
|
||||
block: LinesBuilder.() -> Unit
|
||||
) {
|
||||
contract {
|
||||
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||
}
|
||||
|
||||
val range = when (direction) {
|
||||
LinesBuilder.Direction.TOP_TO_BOTTOM -> IntProgression.fromClosedRange(0, maxLines - 1, 1)
|
||||
LinesBuilder.Direction.BOTTOM_TO_TOP -> IntProgression.fromClosedRange(maxLines - 1, 0, -1)
|
||||
}
|
||||
|
||||
block(LinesBuilder(this, range))
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
public fun Sidebar.lines(
|
||||
progression: IntProgression,
|
||||
block: LinesBuilder.() -> Unit
|
||||
) {
|
||||
contract {
|
||||
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
|
||||
}
|
||||
|
||||
require(progression.first in 0 until maxLines) { "invalid range" }
|
||||
require(progression.last in 0 until maxLines) { "invalid range" }
|
||||
|
||||
block(LinesBuilder(this, progression))
|
||||
}
|
||||
|
||||
public class LinesBuilder internal constructor(private val sidebar: Sidebar, progression: IntProgression) {
|
||||
public enum class Direction {
|
||||
TOP_TO_BOTTOM,
|
||||
BOTTOM_TO_TOP
|
||||
}
|
||||
|
||||
public interface DynamicLine {
|
||||
public fun update()
|
||||
}
|
||||
|
||||
private var iterator = progression.iterator()
|
||||
|
||||
public fun line(value: Component) {
|
||||
sidebar.line(nextLine(), value)
|
||||
}
|
||||
|
||||
public fun emptyLine() {
|
||||
line(empty())
|
||||
}
|
||||
|
||||
public fun dynamicLine(valueProvider: () -> Component): DynamicLine {
|
||||
val lineIndex = nextLine()
|
||||
val dynamicLine = object : DynamicLine {
|
||||
override fun update() {
|
||||
sidebar.line(lineIndex, valueProvider())
|
||||
}
|
||||
}
|
||||
dynamicLine.update()
|
||||
return dynamicLine
|
||||
}
|
||||
|
||||
public fun skip(lines: Int = 1) {
|
||||
require(lines >= 1) { "lines cannot be less than 1" }
|
||||
repeat(lines) {
|
||||
if (!iterator.hasNext()) {
|
||||
error("reached end of range")
|
||||
}
|
||||
iterator.nextInt()
|
||||
}
|
||||
}
|
||||
|
||||
public fun nextLine(): Int {
|
||||
while (iterator.hasNext()) {
|
||||
val line = iterator.nextInt()
|
||||
if (sidebar.line(line) == null) {
|
||||
return line
|
||||
}
|
||||
}
|
||||
|
||||
error("reached end of range")
|
||||
}
|
||||
}
|
||||
-136
@@ -1,136 +0,0 @@
|
||||
package net.megavex.scoreboardlibrary.extra.kotlin
|
||||
|
||||
import net.kyori.adventure.text.Component.empty
|
||||
import net.kyori.adventure.text.Component.text
|
||||
import net.megavex.scoreboardlibrary.api.noop.NoopScoreboardLibrary
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.assertThrows
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
internal class SidebarDslTest {
|
||||
private val scoreboardLibrary = NoopScoreboardLibrary()
|
||||
|
||||
@Test
|
||||
fun `puts lines in correct places`() {
|
||||
val sidebar = scoreboardLibrary.createSidebar()
|
||||
val line = text("Hello")
|
||||
sidebar.lines {
|
||||
emptyLine()
|
||||
line(line)
|
||||
emptyLine()
|
||||
}
|
||||
|
||||
assertEquals(empty(), sidebar.line(0))
|
||||
assertEquals(line, sidebar.line(1))
|
||||
assertEquals(empty(), sidebar.line(2))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `respects maxLines property`() {
|
||||
val sidebar = scoreboardLibrary.createSidebar()
|
||||
assertThrows<IllegalStateException> {
|
||||
sidebar.lines {
|
||||
repeat(sidebar.maxLines + 1) {
|
||||
emptyLine()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `skips existing lines`() {
|
||||
val sidebar = scoreboardLibrary.createSidebar()
|
||||
sidebar.lines {
|
||||
line(text("1"))
|
||||
}
|
||||
|
||||
sidebar.lines {
|
||||
line(text("2"))
|
||||
}
|
||||
|
||||
sidebar.lines {
|
||||
line(text("3"))
|
||||
}
|
||||
|
||||
assertEquals(text("1"), sidebar.line(0))
|
||||
assertEquals(text("2"), sidebar.line(1))
|
||||
assertEquals(text("3"), sidebar.line(2))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `throws if there are no available lines`() {
|
||||
val sidebar = scoreboardLibrary.createSidebar()
|
||||
sidebar.lines {
|
||||
repeat(sidebar.maxLines) {
|
||||
emptyLine()
|
||||
}
|
||||
}
|
||||
|
||||
assertThrows<IllegalStateException> {
|
||||
sidebar.lines {
|
||||
line(text("this shouldn't work"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun directions() {
|
||||
val sidebar = scoreboardLibrary.createSidebar(5)
|
||||
sidebar.lines(direction = LinesBuilder.Direction.BOTTOM_TO_TOP) {
|
||||
line(text("1"))
|
||||
line(text("2"))
|
||||
line(text("3"))
|
||||
}
|
||||
|
||||
assertEquals(text("1"), sidebar.line(4))
|
||||
assertEquals(text("2"), sidebar.line(3))
|
||||
assertEquals(text("3"), sidebar.line(2))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun skip() {
|
||||
val sidebar = scoreboardLibrary.createSidebar()
|
||||
sidebar.lines {
|
||||
line(text("1"))
|
||||
skip(lines = 2)
|
||||
line(text("3"))
|
||||
}
|
||||
|
||||
sidebar.lines {
|
||||
line(text("2"))
|
||||
}
|
||||
|
||||
assertEquals(text("1"), sidebar.line(0))
|
||||
assertEquals(text("2"), sidebar.line(1))
|
||||
assertEquals(null, sidebar.line(2))
|
||||
assertEquals(text("3"), sidebar.line(3))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `detects invalid progression`() {
|
||||
val sidebar = scoreboardLibrary.createSidebar()
|
||||
assertThrows<IllegalArgumentException> {
|
||||
sidebar.lines(progression = -1..5) {}
|
||||
}
|
||||
|
||||
assertThrows<IllegalArgumentException> {
|
||||
sidebar.lines(progression = 0..sidebar.maxLines) {}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `dynamic line`() {
|
||||
val sidebar = scoreboardLibrary.createSidebar()
|
||||
var test = 0
|
||||
var dynamicLine: LinesBuilder.DynamicLine
|
||||
|
||||
sidebar.lines {
|
||||
dynamicLine = dynamicLine { text(test) }
|
||||
}
|
||||
|
||||
assertEquals(text(test), sidebar.line(0))
|
||||
test++
|
||||
dynamicLine.update()
|
||||
assertEquals(text(test), sidebar.line(0))
|
||||
}
|
||||
}
|
||||
Generated
+27
@@ -0,0 +1,27 @@
|
||||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1722987190,
|
||||
"narHash": "sha256-68hmex5efCiM2aZlAAEcQgmFI4ZwWt8a80vOeB/5w3A=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "21cc704b5e918c5fbf4f9fff22b4ac2681706d90",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-24.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
|
||||
};
|
||||
|
||||
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: {
|
||||
default = pkgs.mkShell {
|
||||
buildInputs = [
|
||||
pkgs.temurin-bin-21
|
||||
];
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
[versions]
|
||||
adventure = "4.23.0"
|
||||
devBundle = "1.21.6-R0.1-20250622.015433-31" # find latest here: https://repo.papermc.io/repository/maven-snapshots/io/papermc/paper/dev-bundle/1.21.6-R0.1-SNAPSHOT/maven-metadata.xml
|
||||
|
||||
[libraries]
|
||||
spigotApi = "org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT" # do not update
|
||||
packetEvents = "com.github.retrooper:packetevents-spigot:2.8.0"
|
||||
buildIndra = { module = "net.kyori:indra-common", version = "3.1.3" }
|
||||
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" }
|
||||
|
||||
junitJupiter = "org.junit.jupiter:junit-jupiter:5.13.1"
|
||||
junitPlatformLauncher = "org.junit.platform:junit-platform-launcher:1.13.1"
|
||||
|
||||
[plugins]
|
||||
kotlin = "org.jetbrains.kotlin.jvm:2.1.21"
|
||||
paperweight = "io.papermc.paperweight.userdev:2.0.0-beta.17"
|
||||
|
||||
[bundles]
|
||||
adventure = [ "adventureApi", "adventureTextSerializerGson", "adventureTextSerializerLegacy" ]
|
||||
Vendored
BIN
Binary file not shown.
+3
-1
@@ -1,5 +1,7 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.2-bin.zip
|
||||
networkTimeout=10000
|
||||
validateDistributionUrl=true
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
@@ -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/master/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/.
|
||||
@@ -80,13 +82,11 @@ do
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
# This is normally unused
|
||||
# shellcheck disable=SC2034
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
|
||||
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
@@ -133,22 +133,29 @@ location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
if ! command -v java >/dev/null 2>&1
|
||||
then
|
||||
die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
# In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
# In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
|
||||
# shellcheck disable=SC2039,SC3045
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
@@ -193,11 +200,15 @@ if "$cygwin" || "$msys" ; then
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Collect all arguments for the java command:
|
||||
# * 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" \
|
||||
@@ -205,6 +216,12 @@ set -- \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Stop when "xargs" is not available.
|
||||
if ! command -v xargs >/dev/null 2>&1
|
||||
then
|
||||
die "xargs is not available"
|
||||
fi
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
|
||||
Vendored
+94
-89
@@ -1,89 +1,94 @@
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
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 %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@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 ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%"=="" set DIRNAME=.
|
||||
@rem This is normally unused
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if %ERRORLEVEL% equ 0 goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo. 1>&2
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
|
||||
echo. 1>&2
|
||||
echo Please set the JAVA_HOME variable in your environment to match the 1>&2
|
||||
echo location of your Java installation. 1>&2
|
||||
|
||||
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 %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if %ERRORLEVEL% equ 0 goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
set EXIT_CODE=%ERRORLEVEL%
|
||||
if %EXIT_CODE% equ 0 set EXIT_CODE=1
|
||||
if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
|
||||
exit /b %EXIT_CODE%
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
||||
@@ -1,8 +1,9 @@
|
||||
plugins {
|
||||
id("net.megavex.scoreboardlibrary.publish-conventions")
|
||||
id("net.megavex.scoreboardlibrary.base-conventions")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":scoreboard-library-api"))
|
||||
implementation(project(":scoreboard-library-packet-adapter-base"))
|
||||
compileOnly(libs.spigotApi)
|
||||
}
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
package net.megavex.scoreboardlibrary.implementation;
|
||||
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.PlayerDependantLocaleSidebar;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.SidebarTask;
|
||||
import net.megavex.scoreboardlibrary.implementation.team.TeamManagerTask;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerLocaleChangeEvent;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
|
||||
public record LocaleListener(ScoreboardLibraryImpl scoreboardLibrary) implements Listener {
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||
public void onPlayerLocaleChanged(PlayerLocaleChangeEvent event) {
|
||||
var player = event.getPlayer();
|
||||
// Need to wait a tick because the locale didn't update yet
|
||||
new BukkitRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
var slPlayer = scoreboardLibrary.getPlayer(player);
|
||||
if (slPlayer != null) {
|
||||
var teamManager = slPlayer.teamManager();
|
||||
if (teamManager != null) {
|
||||
teamManager.taskQueue().add(new TeamManagerTask.ReloadPlayer(player));
|
||||
}
|
||||
|
||||
var sidebar = slPlayer.sidebar();
|
||||
if (sidebar instanceof PlayerDependantLocaleSidebar) {
|
||||
sidebar.taskQueue().add(new SidebarTask.ReloadPlayer(player));
|
||||
}
|
||||
}
|
||||
}
|
||||
}.runTaskLaterAsynchronously(scoreboardLibrary.plugin(), 1);
|
||||
}
|
||||
}
|
||||
+102
-18
@@ -1,43 +1,127 @@
|
||||
package net.megavex.scoreboardlibrary.implementation;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import net.megavex.scoreboardlibrary.api.exception.NoPacketAdapterAvailableException;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.ScoreboardLibraryPacketAdapter;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketAdapterProvider;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
|
||||
public final class PacketAdapterLoader {
|
||||
private static final String MODERN = "modern",
|
||||
LEGACY = "legacy",
|
||||
PACKET_EVENTS = "packetevents";
|
||||
|
||||
private PacketAdapterLoader() {
|
||||
}
|
||||
|
||||
public static @NotNull ScoreboardLibraryPacketAdapter<?> loadPacketAdapter() throws NoPacketAdapterAvailableException {
|
||||
var versionName = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
|
||||
Class<?> nmsClass = tryLoadImplementationClass(versionName);
|
||||
|
||||
public static @NotNull PacketAdapterProvider loadPacketAdapter() throws NoPacketAdapterAvailableException {
|
||||
Class<?> nmsClass = findAndLoadImplementationClass();
|
||||
if (nmsClass == null) {
|
||||
nmsClass = tryLoadImplementationClass("packetevents");
|
||||
if (nmsClass == null) {
|
||||
throw new NoPacketAdapterAvailableException();
|
||||
}
|
||||
|
||||
try {
|
||||
Class.forName("com.github.retrooper.packetevents.PacketEvents");
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
throw new NoPacketAdapterAvailableException();
|
||||
}
|
||||
throw new NoPacketAdapterAvailableException();
|
||||
}
|
||||
|
||||
try {
|
||||
return (ScoreboardLibraryPacketAdapter<?>) nmsClass.getConstructors()[0].newInstance();
|
||||
return (PacketAdapterProvider) nmsClass.getConstructors()[0].newInstance();
|
||||
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
|
||||
throw new RuntimeException("couldn't initialize packet adapter", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable Class<?> findAndLoadImplementationClass() {
|
||||
String version = Bukkit.getServer().getBukkitVersion();
|
||||
int dashIndex = version.indexOf('-');
|
||||
if (dashIndex != -1) {
|
||||
version = version.substring(0, dashIndex);
|
||||
}
|
||||
|
||||
Class<?> nmsClass = tryLoadVersion(version);
|
||||
if (nmsClass != null) {
|
||||
return nmsClass;
|
||||
}
|
||||
|
||||
return tryLoadPacketEvents();
|
||||
}
|
||||
|
||||
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/
|
||||
// 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":
|
||||
return tryLoadImplementationClass(LEGACY);
|
||||
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":
|
||||
return tryLoadImplementationClass(MODERN);
|
||||
default:
|
||||
// Hide from relocation checkers
|
||||
String property = "net.mega".concat("vex.scoreboardlibrary.forceModern");
|
||||
if (System.getProperty(property, "").equalsIgnoreCase("true")) {
|
||||
return tryLoadImplementationClass(MODERN);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable Class<?> tryLoadPacketEvents() {
|
||||
Class<?> nmsClass = tryLoadImplementationClass(PACKET_EVENTS);
|
||||
if (nmsClass == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
Class.forName("com.github.retrooper.packetevents.PacketEvents");
|
||||
return nmsClass;
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable Class<?> tryLoadImplementationClass(@NotNull String name) {
|
||||
try {
|
||||
return Class.forName("net.megavex.scoreboardlibrary.implementation.packetAdapter." + name + ".PacketAdapterImpl");
|
||||
String path = "net.megavex.scoreboardlibrary.implementation.packetAdapter." + name + ".PacketAdapterProviderImpl";
|
||||
return Class.forName(path);
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
return null;
|
||||
}
|
||||
|
||||
+64
-24
@@ -2,16 +2,16 @@ package net.megavex.scoreboardlibrary.implementation;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.collect.MapMaker;
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import net.megavex.scoreboardlibrary.api.ScoreboardLibrary;
|
||||
import net.megavex.scoreboardlibrary.api.exception.NoPacketAdapterAvailableException;
|
||||
import net.megavex.scoreboardlibrary.api.objective.ObjectiveManager;
|
||||
import net.megavex.scoreboardlibrary.api.sidebar.Sidebar;
|
||||
import net.megavex.scoreboardlibrary.implementation.commons.LocaleProvider;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.ScoreboardLibraryPacketAdapter;
|
||||
import net.megavex.scoreboardlibrary.implementation.objective.ObjectiveManagerImpl;
|
||||
import net.megavex.scoreboardlibrary.implementation.objective.ObjectiveUpdaterTask;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketAdapterProvider;
|
||||
import net.megavex.scoreboardlibrary.implementation.player.LocaleListener;
|
||||
import net.megavex.scoreboardlibrary.implementation.player.ScoreboardLibraryPlayer;
|
||||
import net.megavex.scoreboardlibrary.implementation.scheduler.TaskScheduler;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.AbstractSidebar;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.PlayerDependantLocaleSidebar;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.SidebarUpdaterTask;
|
||||
@@ -24,18 +24,26 @@ import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ScoreboardLibraryImpl implements ScoreboardLibrary {
|
||||
private final Plugin plugin;
|
||||
private final ScoreboardLibraryPacketAdapter<?> packetAdapter;
|
||||
private final LocaleProvider localeProvider;
|
||||
private final PacketAdapterProvider packetAdapter;
|
||||
private final TaskScheduler taskScheduler;
|
||||
|
||||
private final Map<Player, ScoreboardLibraryPlayer> playerMap = new MapMaker().weakKeys().makeMap();
|
||||
|
||||
private volatile Set<TeamManagerImpl> teamManagers;
|
||||
private volatile Set<ObjectiveManagerImpl> objectiveManagers;
|
||||
private volatile Set<AbstractSidebar> sidebars;
|
||||
|
||||
private final LocaleListener localeListener;
|
||||
private TeamUpdaterTask teamTask;
|
||||
private ObjectiveUpdaterTask objectiveTask;
|
||||
private SidebarUpdaterTask sidebarTask;
|
||||
|
||||
private final Object lock = new Object();
|
||||
@@ -47,12 +55,12 @@ public class ScoreboardLibraryImpl implements ScoreboardLibrary {
|
||||
try {
|
||||
Class.forName("net.kyori.adventure.Adventure");
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new RuntimeException("Adventure is not in the classpath");
|
||||
throw new IllegalStateException("Adventure is not in the classpath");
|
||||
}
|
||||
|
||||
this.plugin = plugin;
|
||||
this.packetAdapter = PacketAdapterLoader.loadPacketAdapter();
|
||||
this.localeProvider = this.packetAdapter.localeProvider;
|
||||
this.taskScheduler = TaskScheduler.create(plugin);
|
||||
|
||||
boolean localeEventExists = false;
|
||||
try {
|
||||
@@ -73,27 +81,27 @@ public class ScoreboardLibraryImpl implements ScoreboardLibrary {
|
||||
return plugin;
|
||||
}
|
||||
|
||||
public @NotNull ScoreboardLibraryPacketAdapter<?> packetAdapter() {
|
||||
public @NotNull PacketAdapterProvider packetAdapter() {
|
||||
return packetAdapter;
|
||||
}
|
||||
|
||||
public @NotNull LocaleProvider localeProvider() {
|
||||
return localeProvider;
|
||||
public @NotNull TaskScheduler taskScheduler() {
|
||||
return taskScheduler;
|
||||
}
|
||||
|
||||
@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 || maxLines > Sidebar.MAX_LINES) {
|
||||
throw new IllegalArgumentException("maxLines");
|
||||
if (maxLines <= 0) {
|
||||
throw new IllegalArgumentException("invalid maxLines value: " + maxLines);
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -104,11 +112,20 @@ public class ScoreboardLibraryImpl implements ScoreboardLibrary {
|
||||
public @NotNull TeamManagerImpl createTeamManager() {
|
||||
checkClosed();
|
||||
|
||||
var teamManager = new TeamManagerImpl(this);
|
||||
TeamManagerImpl teamManager = new TeamManagerImpl(this);
|
||||
teamManagers().add(teamManager);
|
||||
return teamManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ObjectiveManager createObjectiveManager() {
|
||||
checkClosed();
|
||||
|
||||
ObjectiveManagerImpl objectiveManager = new ObjectiveManagerImpl(this);
|
||||
objectiveManagers().add(objectiveManager);
|
||||
return objectiveManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (closed) {
|
||||
@@ -126,19 +143,29 @@ public class ScoreboardLibraryImpl implements ScoreboardLibrary {
|
||||
HandlerList.unregisterAll(localeListener);
|
||||
|
||||
if (teamManagers != null) {
|
||||
teamTask.cancel();
|
||||
teamTask.task().cancel();
|
||||
synchronized (teamTask.lock()) {
|
||||
for (var teamManager : teamManagers) {
|
||||
for (TeamManagerImpl teamManager : teamManagers) {
|
||||
teamManager.close();
|
||||
teamManager.tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (objectiveManagers != null) {
|
||||
objectiveTask.task().cancel();
|
||||
synchronized (objectiveTask.lock()) {
|
||||
for (ObjectiveManagerImpl objectiveManager : objectiveManagers) {
|
||||
objectiveManager.close();
|
||||
objectiveManager.tick();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sidebars != null) {
|
||||
sidebarTask.cancel();
|
||||
sidebarTask.task().cancel();
|
||||
synchronized (sidebarTask.lock()) {
|
||||
for (var sidebar : sidebars) {
|
||||
for (AbstractSidebar sidebar : sidebars) {
|
||||
sidebar.close();
|
||||
sidebar.tick();
|
||||
}
|
||||
@@ -164,6 +191,19 @@ public class ScoreboardLibraryImpl implements ScoreboardLibrary {
|
||||
return teamManagers;
|
||||
}
|
||||
|
||||
public Set<ObjectiveManagerImpl> objectiveManagers() {
|
||||
if (objectiveManagers == null) {
|
||||
synchronized (lock) {
|
||||
if (objectiveManagers == null) {
|
||||
objectiveManagers = Collections.newSetFromMap(new ConcurrentHashMap<>(4, 0.75f, 2));
|
||||
objectiveTask = new ObjectiveUpdaterTask(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return objectiveManagers;
|
||||
}
|
||||
|
||||
public Set<AbstractSidebar> sidebars() {
|
||||
if (sidebars == null) {
|
||||
synchronized (lock) {
|
||||
|
||||
-95
@@ -1,95 +0,0 @@
|
||||
package net.megavex.scoreboardlibrary.implementation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import net.megavex.scoreboardlibrary.implementation.commons.CollectionProvider;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.AbstractSidebar;
|
||||
import net.megavex.scoreboardlibrary.implementation.team.TeamManagerImpl;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ScoreboardLibraryPlayer {
|
||||
private final UUID playerUuid;
|
||||
private final List<TeamManagerImpl> teamManagers = CollectionProvider.list(1);
|
||||
private final List<AbstractSidebar> sidebars = CollectionProvider.list(1);
|
||||
|
||||
public ScoreboardLibraryPlayer(@NotNull Player player) {
|
||||
this.playerUuid = player.getUniqueId();
|
||||
}
|
||||
|
||||
public synchronized @Nullable TeamManagerImpl teamManager() {
|
||||
if (teamManagers.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
return teamManagers.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void addTeamManager(@NotNull TeamManagerImpl teamManager) {
|
||||
if (teamManagers.contains(teamManager)) {
|
||||
throw new RuntimeException("TeamManager already registered");
|
||||
}
|
||||
|
||||
teamManagers.add(teamManager);
|
||||
|
||||
if (teamManager() == teamManager) {
|
||||
var player = Bukkit.getPlayer(playerUuid);
|
||||
if (player != null) {
|
||||
teamManager.show(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void removeTeamManager(@NotNull TeamManagerImpl teamManager) {
|
||||
if (!teamManagers.remove(teamManager)) {
|
||||
throw new RuntimeException("TeamManager not registered");
|
||||
}
|
||||
|
||||
var newTeamManager = teamManager();
|
||||
if (newTeamManager != null) {
|
||||
var player = Bukkit.getPlayer(playerUuid);
|
||||
if (player != null) {
|
||||
newTeamManager.show(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public @Nullable AbstractSidebar sidebar() {
|
||||
if (sidebars.isEmpty()) {
|
||||
return null;
|
||||
} else {
|
||||
return sidebars.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void addSidebar(@NotNull AbstractSidebar sidebar) {
|
||||
if (sidebars.contains(sidebar)) {
|
||||
throw new RuntimeException("Sidebar already registered");
|
||||
}
|
||||
|
||||
sidebars.add(sidebar);
|
||||
|
||||
if (sidebar() == sidebar) {
|
||||
var player = Bukkit.getPlayer(playerUuid);
|
||||
if (player != null) {
|
||||
sidebar.show(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void removeSidebar(@NotNull AbstractSidebar sidebar) {
|
||||
if (!sidebars.remove(sidebar)) {
|
||||
throw new RuntimeException("Sidebar not registered");
|
||||
}
|
||||
|
||||
var newSidebar = sidebar();
|
||||
if (newSidebar != null) {
|
||||
var player = Bukkit.getPlayer(playerUuid);
|
||||
if (player != null) {
|
||||
newSidebar.show(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+220
@@ -0,0 +1,220 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.objective;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import net.megavex.scoreboardlibrary.api.objective.ObjectiveDisplaySlot;
|
||||
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;
|
||||
import net.megavex.scoreboardlibrary.implementation.player.ScoreboardLibraryPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
public 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 Queue<ObjectiveManagerTask> taskQueue = new ConcurrentLinkedQueue<>();
|
||||
private boolean closed;
|
||||
|
||||
public ObjectiveManagerImpl(@NotNull ScoreboardLibraryImpl library) {
|
||||
this.library = library;
|
||||
}
|
||||
|
||||
public @NotNull Queue<ObjectiveManagerTask> taskQueue() {
|
||||
return taskQueue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ScoreboardObjective create(@NotNull String name) {
|
||||
Preconditions.checkNotNull(name);
|
||||
checkClosed();
|
||||
return objectives.computeIfAbsent(name, i -> {
|
||||
ObjectivePacketAdapter packetAdapter = library.packetAdapter().createObjectiveAdapter(name);
|
||||
ScoreboardObjectiveImpl objective = new ScoreboardObjectiveImpl(packetAdapter, taskQueue, name);
|
||||
taskQueue.add(new ObjectiveManagerTask.AddObjective(objective));
|
||||
return objective;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(@NotNull ScoreboardObjective objective) {
|
||||
Preconditions.checkNotNull(objective);
|
||||
checkClosed();
|
||||
if (!(objective instanceof ScoreboardObjectiveImpl)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ScoreboardObjectiveImpl impl = (ScoreboardObjectiveImpl) objective;
|
||||
if (objectives.values().remove(impl)) {
|
||||
displaySlots.values().removeIf(e -> e == impl);
|
||||
impl.close();
|
||||
taskQueue.add(new ObjectiveManagerTask.RemoveObjective(impl));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void display(@NotNull ObjectiveDisplaySlot displaySlot, @NotNull ScoreboardObjective objective) {
|
||||
Preconditions.checkNotNull(displaySlot);
|
||||
Preconditions.checkNotNull(objective);
|
||||
checkClosed();
|
||||
|
||||
if (!(objective instanceof ScoreboardObjectiveImpl)) {
|
||||
throw new IllegalArgumentException("Invalid objective implementation");
|
||||
}
|
||||
|
||||
ScoreboardObjectiveImpl impl = (ScoreboardObjectiveImpl) objective;
|
||||
if (objectives.get(impl.name()) != impl) {
|
||||
throw new IllegalArgumentException("Objective is from a different manager");
|
||||
}
|
||||
|
||||
displaySlots.put(displaySlot, impl);
|
||||
taskQueue.add(new ObjectiveManagerTask.UpdateDisplaySlot(displaySlot, impl));
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<Player> players() {
|
||||
return Collections.unmodifiableSet(players);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addPlayer(@NotNull Player player) {
|
||||
Preconditions.checkNotNull(player);
|
||||
checkClosed();
|
||||
if (!players.add(player)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
taskQueue.add(new ObjectiveManagerTask.AddPlayer(player));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removePlayer(@NotNull Player player) {
|
||||
Preconditions.checkNotNull(player);
|
||||
checkClosed();
|
||||
if (!players.remove(player)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
taskQueue.add(new ObjectiveManagerTask.RemovePlayer(player));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (!closed) {
|
||||
closed = true;
|
||||
taskQueue.add(ObjectiveManagerTask.Close.INSTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean closed() {
|
||||
return closed;
|
||||
}
|
||||
|
||||
public boolean tick() {
|
||||
while (true) {
|
||||
ObjectiveManagerTask task = taskQueue.poll();
|
||||
if (task == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (task instanceof ObjectiveManagerTask.Close) {
|
||||
for (ScoreboardObjectiveImpl objective : objectives.values()) {
|
||||
objective.packetAdapter().remove(displayingPlayers);
|
||||
}
|
||||
|
||||
for (Player player : players) {
|
||||
Objects.requireNonNull(library.getPlayer(player)).objectiveManagerQueue().remove(this);
|
||||
}
|
||||
return false;
|
||||
} else if (task instanceof ObjectiveManagerTask.AddPlayer) {
|
||||
Player player = ((ObjectiveManagerTask.AddPlayer) task).player();
|
||||
@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);
|
||||
for (ScoreboardObjectiveImpl objective : objectives.values()) {
|
||||
objective.packetAdapter().remove(singleton);
|
||||
}
|
||||
|
||||
displayingPlayers.remove(player);
|
||||
Objects.requireNonNull(library.getPlayer(player)).objectiveManagerQueue().remove(this);
|
||||
} else if (task instanceof ObjectiveManagerTask.ReloadPlayer) {
|
||||
Player player = ((ObjectiveManagerTask.ReloadPlayer) task).player();
|
||||
Collection<Player> singleton = Collections.singleton(player);
|
||||
for (ScoreboardObjectiveImpl objective : objectives.values()) {
|
||||
objective.sendProperties(singleton, PropertiesPacketType.UPDATE);
|
||||
|
||||
for (Map.Entry<String, ObjectiveScore> entry : objective.scores().entrySet()) {
|
||||
ObjectiveScore score = entry.getValue();
|
||||
objective.packetAdapter().sendScore(singleton, entry.getKey(), score.value(), score.displayName(), score.format());
|
||||
}
|
||||
}
|
||||
} else if (task instanceof ObjectiveManagerTask.AddObjective) {
|
||||
ScoreboardObjectiveImpl objective = ((ObjectiveManagerTask.AddObjective) task).objective();
|
||||
objective.sendProperties(displayingPlayers, PropertiesPacketType.CREATE);
|
||||
} else if (task instanceof ObjectiveManagerTask.RemoveObjective) {
|
||||
ScoreboardObjectiveImpl objective = ((ObjectiveManagerTask.RemoveObjective) task).objective();
|
||||
objective.packetAdapter().remove(displayingPlayers);
|
||||
} else if (task instanceof ObjectiveManagerTask.UpdateObjective) {
|
||||
ScoreboardObjectiveImpl objective = ((ObjectiveManagerTask.UpdateObjective) task).objective();
|
||||
objective.sendProperties(displayingPlayers, PropertiesPacketType.UPDATE);
|
||||
} else if (task instanceof ObjectiveManagerTask.UpdateScore) {
|
||||
ObjectiveManagerTask.UpdateScore updateScoreTask = ((ObjectiveManagerTask.UpdateScore) task);
|
||||
ScoreboardObjectiveImpl objective = updateScoreTask.objective();
|
||||
String entry = updateScoreTask.entry();
|
||||
ObjectiveScore score = updateScoreTask.score();
|
||||
if (score != null) {
|
||||
objective.packetAdapter().sendScore(displayingPlayers, entry, score.value(), score.displayName(), score.format());
|
||||
} else {
|
||||
objective.packetAdapter().removeScore(displayingPlayers, entry);
|
||||
}
|
||||
} else if (task instanceof ObjectiveManagerTask.UpdateDisplaySlot) {
|
||||
ObjectiveManagerTask.UpdateDisplaySlot updateDisplaySlotTask = (ObjectiveManagerTask.UpdateDisplaySlot) task;
|
||||
ObjectiveDisplaySlot slot = updateDisplaySlotTask.displaySlot();
|
||||
ScoreboardObjectiveImpl objective = updateDisplaySlotTask.objective();
|
||||
objective.packetAdapter().display(displayingPlayers, slot);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void display(@NotNull Player player) {
|
||||
displayingPlayers.add(player);
|
||||
Collection<Player> singleton = Collections.singleton(player);
|
||||
|
||||
for (ScoreboardObjectiveImpl objective : objectives.values()) {
|
||||
objective.sendProperties(singleton, PropertiesPacketType.CREATE);
|
||||
for (Map.Entry<String, ObjectiveScore> entry : objective.scores().entrySet()) {
|
||||
ObjectiveScore score = entry.getValue();
|
||||
objective.packetAdapter().sendScore(singleton, entry.getKey(), score.value(), score.displayName(), score.format());
|
||||
}
|
||||
}
|
||||
|
||||
for (Map.Entry<ObjectiveDisplaySlot, ScoreboardObjectiveImpl> entry : displaySlots.entrySet()) {
|
||||
entry.getValue().packetAdapter().display(singleton, entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
private void checkClosed() {
|
||||
if (closed) {
|
||||
throw new IllegalStateException("ObjectiveManager is closed");
|
||||
}
|
||||
}
|
||||
}
|
||||
+133
@@ -0,0 +1,133 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.objective;
|
||||
|
||||
import net.megavex.scoreboardlibrary.api.objective.ObjectiveDisplaySlot;
|
||||
import net.megavex.scoreboardlibrary.api.objective.ObjectiveScore;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class ObjectiveManagerTask {
|
||||
private ObjectiveManagerTask() {
|
||||
}
|
||||
|
||||
public static final class Close extends ObjectiveManagerTask {
|
||||
public static final Close INSTANCE = new Close();
|
||||
|
||||
private Close() {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class AddPlayer extends ObjectiveManagerTask {
|
||||
private final Player player;
|
||||
|
||||
public AddPlayer(@NotNull Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public @NotNull Player player() {
|
||||
return player;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class RemovePlayer extends ObjectiveManagerTask {
|
||||
private final Player player;
|
||||
|
||||
public RemovePlayer(@NotNull Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public @NotNull Player player() {
|
||||
return player;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ReloadPlayer extends ObjectiveManagerTask {
|
||||
private final Player player;
|
||||
|
||||
public ReloadPlayer(@NotNull Player player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
public @NotNull Player player() {
|
||||
return player;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class AddObjective extends ObjectiveManagerTask {
|
||||
private final ScoreboardObjectiveImpl objective;
|
||||
|
||||
public AddObjective(@NotNull ScoreboardObjectiveImpl team) {
|
||||
this.objective = team;
|
||||
}
|
||||
|
||||
public @NotNull ScoreboardObjectiveImpl objective() {
|
||||
return objective;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class RemoveObjective extends ObjectiveManagerTask {
|
||||
private final ScoreboardObjectiveImpl objective;
|
||||
|
||||
public RemoveObjective(@NotNull ScoreboardObjectiveImpl team) {
|
||||
this.objective = team;
|
||||
}
|
||||
|
||||
public @NotNull ScoreboardObjectiveImpl objective() {
|
||||
return objective;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class UpdateObjective extends ObjectiveManagerTask {
|
||||
private final ScoreboardObjectiveImpl objective;
|
||||
|
||||
public UpdateObjective(@NotNull ScoreboardObjectiveImpl objective) {
|
||||
this.objective = objective;
|
||||
}
|
||||
|
||||
public @NotNull ScoreboardObjectiveImpl objective() {
|
||||
return objective;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class UpdateScore extends ObjectiveManagerTask {
|
||||
private final ScoreboardObjectiveImpl objective;
|
||||
private final String entry;
|
||||
private final ObjectiveScore score;
|
||||
|
||||
public UpdateScore(@NotNull ScoreboardObjectiveImpl objective, @NotNull String entry, @Nullable ObjectiveScore score) {
|
||||
this.objective = objective;
|
||||
this.entry = entry;
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
public @NotNull ScoreboardObjectiveImpl objective() {
|
||||
return objective;
|
||||
}
|
||||
|
||||
public @NotNull String entry() {
|
||||
return entry;
|
||||
}
|
||||
|
||||
public @Nullable ObjectiveScore score() {
|
||||
return score;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class UpdateDisplaySlot extends ObjectiveManagerTask {
|
||||
private final ObjectiveDisplaySlot displaySlot;
|
||||
private final ScoreboardObjectiveImpl objective;
|
||||
|
||||
public UpdateDisplaySlot(@NotNull ObjectiveDisplaySlot displaySlot, @NotNull ScoreboardObjectiveImpl objective) {
|
||||
this.displaySlot = displaySlot;
|
||||
this.objective = objective;
|
||||
}
|
||||
|
||||
public @NotNull ObjectiveDisplaySlot displaySlot() {
|
||||
return displaySlot;
|
||||
}
|
||||
|
||||
public @NotNull ScoreboardObjectiveImpl objective() {
|
||||
return objective;
|
||||
}
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.objective;
|
||||
|
||||
import net.megavex.scoreboardlibrary.implementation.ScoreboardLibraryImpl;
|
||||
import net.megavex.scoreboardlibrary.implementation.scheduler.RunningTask;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class ObjectiveUpdaterTask implements Runnable {
|
||||
private final ScoreboardLibraryImpl scoreboardLibrary;
|
||||
private final RunningTask task;
|
||||
private final Object lock = new Object();
|
||||
|
||||
public ObjectiveUpdaterTask(@NotNull ScoreboardLibraryImpl scoreboardLibrary) {
|
||||
this.scoreboardLibrary = scoreboardLibrary;
|
||||
this.task = scoreboardLibrary.taskScheduler().runEveryTick(this);
|
||||
}
|
||||
|
||||
public @NotNull RunningTask task() {
|
||||
return task;
|
||||
}
|
||||
|
||||
public @NotNull Object lock() {
|
||||
return lock;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (lock) {
|
||||
Iterator<ObjectiveManagerImpl> iterator = scoreboardLibrary.objectiveManagers().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
ObjectiveManagerImpl objectiveManager = iterator.next();
|
||||
boolean result;
|
||||
try {
|
||||
result = objectiveManager.tick();
|
||||
} catch (Exception e) {
|
||||
scoreboardLibrary.plugin().getLogger().log(Level.WARNING, "an error occurred while updating an ObjectiveManager instance", e);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.objective;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import net.megavex.scoreboardlibrary.api.objective.ObjectiveRenderType;
|
||||
import net.megavex.scoreboardlibrary.api.objective.ObjectiveScore;
|
||||
import net.megavex.scoreboardlibrary.api.objective.ScoreFormat;
|
||||
import net.megavex.scoreboardlibrary.api.objective.ScoreboardObjective;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType;
|
||||
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.*;
|
||||
|
||||
import static net.kyori.adventure.text.Component.empty;
|
||||
|
||||
public class ScoreboardObjectiveImpl implements ScoreboardObjective {
|
||||
private final ObjectivePacketAdapter packetAdapter;
|
||||
private final Queue<ObjectiveManagerTask> taskQueue;
|
||||
private final String name;
|
||||
|
||||
private final Map<String, ObjectiveScore> scores = new HashMap<>();
|
||||
private Component value = empty();
|
||||
private ObjectiveRenderType renderType = ObjectiveRenderType.INTEGER;
|
||||
private ScoreFormat defaultScoreFormat;
|
||||
private boolean closed;
|
||||
|
||||
public ScoreboardObjectiveImpl(@NotNull ObjectivePacketAdapter packetAdapter, @NotNull Queue<ObjectiveManagerTask> taskQueue, @NotNull String name) {
|
||||
this.packetAdapter = packetAdapter;
|
||||
this.taskQueue = taskQueue;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public ObjectivePacketAdapter packetAdapter() {
|
||||
return packetAdapter;
|
||||
}
|
||||
|
||||
public @NotNull Map<String, ObjectiveScore> scores() {
|
||||
return scores;
|
||||
}
|
||||
|
||||
public @NotNull String name() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
closed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Component value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ScoreboardObjective value(@NotNull ComponentLike value) {
|
||||
Component component = value.asComponent();
|
||||
if (!this.value.equals(component)) {
|
||||
this.value = component;
|
||||
if (!closed) {
|
||||
taskQueue.add(new ObjectiveManagerTask.UpdateObjective(this));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ObjectiveRenderType renderType() {
|
||||
return renderType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ScoreboardObjective renderType(@NotNull ObjectiveRenderType renderType) {
|
||||
if (this.renderType != renderType) {
|
||||
this.renderType = renderType;
|
||||
if (!closed) {
|
||||
taskQueue.add(new ObjectiveManagerTask.UpdateObjective(this));
|
||||
}
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ScoreFormat defaultScoreFormat() {
|
||||
return defaultScoreFormat;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void defaultScoreFormat(@Nullable ScoreFormat defaultScoreFormat) {
|
||||
if (!Objects.equals(this.defaultScoreFormat, defaultScoreFormat)) {
|
||||
this.defaultScoreFormat = defaultScoreFormat;
|
||||
taskQueue.add(new ObjectiveManagerTask.UpdateObjective(this));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable ObjectiveScore scoreInfo(@NotNull String entry) {
|
||||
return scores.get(entry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ScoreboardObjective score(@NotNull String entry, ObjectiveScore score) {
|
||||
ObjectiveScore oldScore = scores.put(entry, score);
|
||||
if (!Objects.equals(oldScore, score)) {
|
||||
taskQueue.add(new ObjectiveManagerTask.UpdateScore(this, entry, score));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ScoreboardObjective removeScore(@NotNull String entry) {
|
||||
if (scores.remove(entry) != null) {
|
||||
taskQueue.add(new ObjectiveManagerTask.UpdateScore(this, entry, null));
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public void sendProperties(@NotNull Collection<Player> players, @NotNull PropertiesPacketType packetType) {
|
||||
packetAdapter.sendProperties(players, packetType, value, renderType, defaultScoreFormat);
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
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.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class DisplayableQueue<T extends PlayerDisplayable> {
|
||||
private final UUID playerUuid;
|
||||
private final List<T> queue = CollectionProvider.list(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 synchronized void remove(@NotNull T displayable) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.player;
|
||||
|
||||
import net.megavex.scoreboardlibrary.implementation.ScoreboardLibraryImpl;
|
||||
import net.megavex.scoreboardlibrary.implementation.objective.ObjectiveManagerImpl;
|
||||
import net.megavex.scoreboardlibrary.implementation.objective.ObjectiveManagerTask;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.AbstractSidebar;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.PlayerDependantLocaleSidebar;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.SidebarTask;
|
||||
import net.megavex.scoreboardlibrary.implementation.team.TeamManagerImpl;
|
||||
import net.megavex.scoreboardlibrary.implementation.team.TeamManagerTask;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.event.player.PlayerLocaleChangeEvent;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class LocaleListener implements Listener {
|
||||
private final ScoreboardLibraryImpl scoreboardLibrary;
|
||||
|
||||
public LocaleListener(@NotNull ScoreboardLibraryImpl scoreboardLibrary) {
|
||||
this.scoreboardLibrary = scoreboardLibrary;
|
||||
}
|
||||
|
||||
@EventHandler(ignoreCancelled = true, priority = EventPriority.MONITOR)
|
||||
public void onPlayerLocaleChanged(PlayerLocaleChangeEvent event) {
|
||||
Player player = event.getPlayer();
|
||||
|
||||
// Need to wait a tick because the locale didn't update yet
|
||||
scoreboardLibrary.taskScheduler().runNextTick(() -> {
|
||||
ScoreboardLibraryPlayer slPlayer = scoreboardLibrary.getPlayer(player);
|
||||
if (slPlayer != null) {
|
||||
TeamManagerImpl teamManager = slPlayer.teamManagerQueue().current();
|
||||
if (teamManager != null) {
|
||||
teamManager.taskQueue().add(new TeamManagerTask.ReloadPlayer(player));
|
||||
}
|
||||
|
||||
ObjectiveManagerImpl objectiveManager = slPlayer.objectiveManagerQueue().current();
|
||||
if (objectiveManager != null) {
|
||||
objectiveManager.taskQueue().add(new ObjectiveManagerTask.ReloadPlayer(player));
|
||||
}
|
||||
|
||||
AbstractSidebar sidebar = slPlayer.sidebarQueue().current();
|
||||
if (sidebar instanceof PlayerDependantLocaleSidebar) {
|
||||
sidebar.taskQueue().add(new SidebarTask.ReloadPlayer(player));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.player;
|
||||
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface PlayerDisplayable {
|
||||
void display(@NotNull Player player);
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.player;
|
||||
|
||||
import net.megavex.scoreboardlibrary.implementation.objective.ObjectiveManagerImpl;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.AbstractSidebar;
|
||||
import net.megavex.scoreboardlibrary.implementation.team.TeamManagerImpl;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class ScoreboardLibraryPlayer {
|
||||
private final DisplayableQueue<TeamManagerImpl> teamManagerQueue;
|
||||
private final DisplayableQueue<ObjectiveManagerImpl> objectiveManagerQueue;
|
||||
private final DisplayableQueue<AbstractSidebar> sidebarQueue;
|
||||
|
||||
public ScoreboardLibraryPlayer(@NotNull Player player) {
|
||||
UUID uuid = player.getUniqueId();
|
||||
this.teamManagerQueue = new DisplayableQueue<>(uuid);
|
||||
this.objectiveManagerQueue = new DisplayableQueue<>(uuid);
|
||||
this.sidebarQueue = new DisplayableQueue<>(uuid);
|
||||
}
|
||||
|
||||
public @NotNull DisplayableQueue<TeamManagerImpl> teamManagerQueue() {
|
||||
return teamManagerQueue;
|
||||
}
|
||||
|
||||
public @NotNull DisplayableQueue<ObjectiveManagerImpl> objectiveManagerQueue() {
|
||||
return objectiveManagerQueue;
|
||||
}
|
||||
|
||||
public @NotNull DisplayableQueue<AbstractSidebar> sidebarQueue() {
|
||||
return sidebarQueue;
|
||||
}
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.scheduler;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
class BukkitTaskScheduler implements TaskScheduler {
|
||||
private final Plugin plugin;
|
||||
|
||||
public BukkitTaskScheduler(@NotNull Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull RunningTask runEveryTick(@NotNull Runnable runnable) {
|
||||
org.bukkit.scheduler.BukkitTask task = plugin.getServer().getScheduler().runTaskTimerAsynchronously(plugin, runnable, 1, 1);
|
||||
return task::cancel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runNextTick(@NotNull Runnable runnable) {
|
||||
plugin.getServer().getScheduler().runTaskLaterAsynchronously(plugin, runnable, 1);
|
||||
}
|
||||
}
|
||||
+114
@@ -0,0 +1,114 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.scheduler;
|
||||
|
||||
import net.kyori.adventure.util.Ticks;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
class FoliaTaskScheduler implements TaskScheduler {
|
||||
private static final Class<?> asyncSchedulerClass;
|
||||
private static final Class<?> scheduledTaskClass;
|
||||
private static final Class<?> cancelledStateClass;
|
||||
private static final MethodHandle getAsyncSchedulerMethod;
|
||||
private static final MethodHandle cancelScheduledTaskMethod;
|
||||
private static final MethodHandle runAtFixedRateMethod;
|
||||
private static final MethodHandle runDelayedMethod;
|
||||
|
||||
static {
|
||||
try {
|
||||
asyncSchedulerClass = Class.forName("io.papermc.paper.threadedregions.scheduler.AsyncScheduler");
|
||||
scheduledTaskClass = Class.forName("io.papermc.paper.threadedregions.scheduler.ScheduledTask");
|
||||
cancelledStateClass = Class.forName("io.papermc.paper.threadedregions.scheduler.ScheduledTask$CancelledState");
|
||||
|
||||
MethodHandles.Lookup lookup = MethodHandles.lookup();
|
||||
getAsyncSchedulerMethod = lookup.findVirtual(
|
||||
Server.class,
|
||||
"getAsyncScheduler",
|
||||
MethodType.methodType(asyncSchedulerClass)
|
||||
);
|
||||
|
||||
cancelScheduledTaskMethod = lookup.findVirtual(scheduledTaskClass, "cancel", MethodType.methodType(cancelledStateClass));
|
||||
|
||||
runAtFixedRateMethod = lookup.findVirtual(
|
||||
asyncSchedulerClass,
|
||||
"runAtFixedRate",
|
||||
MethodType.methodType(
|
||||
scheduledTaskClass,
|
||||
Plugin.class,
|
||||
Consumer.class,
|
||||
long.class,
|
||||
long.class,
|
||||
TimeUnit.class
|
||||
)
|
||||
);
|
||||
|
||||
runDelayedMethod = lookup.findVirtual(
|
||||
asyncSchedulerClass,
|
||||
"runDelayed",
|
||||
MethodType.methodType(
|
||||
scheduledTaskClass,
|
||||
Plugin.class,
|
||||
Consumer.class,
|
||||
long.class,
|
||||
TimeUnit.class
|
||||
)
|
||||
);
|
||||
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException e) {
|
||||
throw new ExceptionInInitializerError(e);
|
||||
}
|
||||
}
|
||||
|
||||
private final Plugin plugin;
|
||||
private final Object asyncScheduler;
|
||||
|
||||
public FoliaTaskScheduler(@NotNull Plugin plugin) {
|
||||
this.plugin = plugin;
|
||||
try {
|
||||
this.asyncScheduler = getAsyncSchedulerMethod.invoke(plugin.getServer());
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("couldn't get async scheduler", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull RunningTask runEveryTick(@NotNull Runnable runnable) {
|
||||
Object scheduledTask;
|
||||
try {
|
||||
Consumer<Object> task = t -> runnable.run();
|
||||
scheduledTask = runAtFixedRateMethod.invoke(
|
||||
asyncScheduler,
|
||||
plugin,
|
||||
task,
|
||||
Ticks.SINGLE_TICK_DURATION_MS,
|
||||
Ticks.SINGLE_TICK_DURATION_MS,
|
||||
TimeUnit.MILLISECONDS
|
||||
);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("couldn't schedule repeating task", e);
|
||||
}
|
||||
|
||||
return () -> {
|
||||
try {
|
||||
cancelScheduledTaskMethod.invoke(scheduledTask);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("couldn't cancel scheduled task", e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void runNextTick(@NotNull Runnable runnable) {
|
||||
Consumer<Object> task = t -> runnable.run();
|
||||
try {
|
||||
runDelayedMethod.invoke(asyncScheduler, plugin, task, Ticks.SINGLE_TICK_DURATION_MS, TimeUnit.MILLISECONDS);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException("couldn't schedule delayed task", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.scheduler;
|
||||
|
||||
public interface RunningTask {
|
||||
void cancel();
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.scheduler;
|
||||
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public interface TaskScheduler {
|
||||
static @NotNull TaskScheduler create(@NotNull Plugin plugin) {
|
||||
try {
|
||||
Class.forName("io.papermc.paper.threadedregions.RegionizedServer");
|
||||
return new FoliaTaskScheduler(plugin);
|
||||
} catch (ClassNotFoundException ignored) {
|
||||
return new BukkitTaskScheduler(plugin);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull RunningTask runEveryTick(@NotNull Runnable runnable);
|
||||
|
||||
void runNextTick(@NotNull Runnable runnable);
|
||||
}
|
||||
+101
-66
@@ -1,19 +1,20 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.sidebar;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.function.Consumer;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
|
||||
import net.kyori.adventure.translation.GlobalTranslator;
|
||||
import 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.SidebarPacketAdapter;
|
||||
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 org.bukkit.entity.Player;
|
||||
@@ -21,12 +22,15 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.Range;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static net.kyori.adventure.text.Component.empty;
|
||||
|
||||
public abstract class AbstractSidebar implements Sidebar {
|
||||
public abstract class AbstractSidebar implements Sidebar, PlayerDisplayable {
|
||||
private final ScoreboardLibraryImpl scoreboardLibrary;
|
||||
private final SidebarPacketAdapter<?, ?> packetAdapter;
|
||||
private final ObjectivePacketAdapter packetAdapter;
|
||||
|
||||
private final GlobalLineInfo[] lines;
|
||||
private Component title = empty();
|
||||
@@ -37,15 +41,15 @@ public abstract class AbstractSidebar implements Sidebar {
|
||||
|
||||
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;
|
||||
this.packetAdapter = scoreboardLibrary.packetAdapter().createSidebarPacketAdapter(this);
|
||||
this.packetAdapter = scoreboardLibrary.packetAdapter().createObjectiveAdapter(objectiveName);
|
||||
this.lines = new GlobalLineInfo[maxLines];
|
||||
}
|
||||
|
||||
protected abstract @NotNull Set<Player> internalPlayers();
|
||||
|
||||
protected abstract void forEachSidebar(@NotNull Consumer<LocaleLineHandler> consumer);
|
||||
protected abstract void forEachLineHandler(@NotNull Consumer<LocaleLineHandler> consumer);
|
||||
|
||||
protected abstract @Nullable LocaleLineHandler addPlayer0(@NotNull Player player);
|
||||
|
||||
@@ -66,7 +70,7 @@ public abstract class AbstractSidebar implements Sidebar {
|
||||
|
||||
@Override
|
||||
public final @NotNull Collection<Player> players() {
|
||||
return closed ? Set.of() : Collections.unmodifiableSet(players);
|
||||
return closed ? Collections.emptySet() : Collections.unmodifiableSet(players);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -101,13 +105,23 @@ public abstract class AbstractSidebar implements Sidebar {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void line(@Range(from = 0, to = MAX_LINES - 1) int line, @Nullable Component value) {
|
||||
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();
|
||||
|
||||
var globalLineInfo = getLineInfo(line);
|
||||
if (!Objects.equals(globalLineInfo.value(), value)) {
|
||||
globalLineInfo.value(value);
|
||||
taskQueue.add(new SidebarTask.UpdateLine(line));
|
||||
Component component = value == null ? null : value.asComponent();
|
||||
GlobalLineInfo globalLineInfo = getLineInfo(index);
|
||||
boolean updateValue = !Objects.equals(globalLineInfo.value(), component);
|
||||
boolean updateScore = !Objects.equals(globalLineInfo.scoreFormat(), scoreFormat);
|
||||
|
||||
if (updateValue || updateScore) {
|
||||
globalLineInfo.value(component);
|
||||
globalLineInfo.scoreFormat(scoreFormat);
|
||||
taskQueue.add(new SidebarTask.UpdateLine(index, updateValue, updateScore));
|
||||
updateScores();
|
||||
}
|
||||
}
|
||||
@@ -116,7 +130,7 @@ public abstract class AbstractSidebar implements Sidebar {
|
||||
public final @Nullable Component line(@Range(from = 0, to = MAX_LINES - 1) int line) {
|
||||
checkClosed();
|
||||
|
||||
var globalLineInfo = lines[line];
|
||||
GlobalLineInfo globalLineInfo = lines[line];
|
||||
return globalLineInfo == null ? null : globalLineInfo.value();
|
||||
}
|
||||
|
||||
@@ -126,12 +140,13 @@ public abstract class AbstractSidebar implements Sidebar {
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void title(@NotNull Component title) {
|
||||
public final void title(@NotNull ComponentLike title) {
|
||||
Preconditions.checkNotNull(title);
|
||||
checkClosed();
|
||||
|
||||
if (!Objects.equals(this.title, title)) {
|
||||
this.title = title;
|
||||
Component component = title.asComponent();
|
||||
if (!Objects.equals(this.title, component)) {
|
||||
this.title = component;
|
||||
taskQueue.add(SidebarTask.UpdateTitle.INSTANCE);
|
||||
}
|
||||
}
|
||||
@@ -140,7 +155,7 @@ public abstract class AbstractSidebar implements Sidebar {
|
||||
return scoreboardLibrary;
|
||||
}
|
||||
|
||||
public final @NotNull SidebarPacketAdapter<?, ?> packetAdapter() {
|
||||
public final ObjectivePacketAdapter packetAdapter() {
|
||||
return packetAdapter;
|
||||
}
|
||||
|
||||
@@ -152,73 +167,90 @@ public abstract class AbstractSidebar implements Sidebar {
|
||||
return taskQueue;
|
||||
}
|
||||
|
||||
public final void show(@NotNull Player player) {
|
||||
packetAdapter.sendObjectivePacket(Set.of(player), SidebarPacketAdapter.ObjectivePacket.CREATE);
|
||||
@Override
|
||||
public final void display(@NotNull Player player) {
|
||||
Collection<Player> singleton = Collections.singleton(player);
|
||||
packetAdapter.sendProperties(singleton, PropertiesPacketType.CREATE, title, ObjectiveRenderType.INTEGER, ScoreFormat.blank());
|
||||
|
||||
var lineHandler = Objects.requireNonNull(addPlayer0(player));
|
||||
LocaleLineHandler lineHandler = Objects.requireNonNull(addPlayer0(player));
|
||||
lineHandler.addPlayer(player);
|
||||
lineHandler.show(player);
|
||||
scoreboardLibrary.packetAdapter().displaySidebar(Set.of(player));
|
||||
packetAdapter.display(singleton, ObjectiveDisplaySlot.sidebar());
|
||||
}
|
||||
|
||||
public final void tick() {
|
||||
public final boolean tick() {
|
||||
while (true) {
|
||||
var task = taskQueue.poll();
|
||||
SidebarTask task = taskQueue.poll();
|
||||
if (task == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (task instanceof SidebarTask.Close) {
|
||||
forEachSidebar(LocaleLineHandler::hide);
|
||||
scoreboardLibrary.packetAdapter().removeSidebar(internalPlayers());
|
||||
forEachLineHandler(LocaleLineHandler::hide);
|
||||
packetAdapter.remove(internalPlayers());
|
||||
|
||||
for (var player : internalPlayers()) {
|
||||
Objects.requireNonNull(scoreboardLibrary.getPlayer(player)).removeSidebar(this);
|
||||
for (Player player : internalPlayers()) {
|
||||
Objects.requireNonNull(scoreboardLibrary.getPlayer(player)).sidebarQueue().remove(this);
|
||||
}
|
||||
|
||||
return;
|
||||
} else if (task instanceof SidebarTask.AddPlayer addPlayerTask) {
|
||||
var slPlayer = scoreboardLibrary.getOrCreatePlayer(addPlayerTask.player());
|
||||
slPlayer.addSidebar(this);
|
||||
} else if (task instanceof SidebarTask.RemovePlayer removePlayerTask) {
|
||||
var lineHandler = removePlayer0(removePlayerTask.player());
|
||||
return false;
|
||||
} else if (task instanceof SidebarTask.AddPlayer) {
|
||||
SidebarTask.AddPlayer addPlayerTask = (SidebarTask.AddPlayer) task;
|
||||
ScoreboardLibraryPlayer slPlayer = scoreboardLibrary.getOrCreatePlayer(addPlayerTask.player());
|
||||
slPlayer.sidebarQueue().add(this);
|
||||
} else if (task instanceof SidebarTask.RemovePlayer) {
|
||||
SidebarTask.RemovePlayer removePlayerTask = (SidebarTask.RemovePlayer) task;
|
||||
|
||||
LocaleLineHandler lineHandler = removePlayer0(removePlayerTask.player());
|
||||
if (lineHandler != null) {
|
||||
lineHandler.hide(removePlayerTask.player());
|
||||
lineHandler.removePlayer(removePlayerTask.player());
|
||||
packetAdapter.remove(Collections.singleton(removePlayerTask.player()));
|
||||
}
|
||||
|
||||
Objects.requireNonNull(scoreboardLibrary.getPlayer(removePlayerTask.player())).sidebarQueue().remove(this);
|
||||
} else if (task instanceof SidebarTask.ReloadPlayer) {
|
||||
SidebarTask.ReloadPlayer reloadPlayerTask = (SidebarTask.ReloadPlayer) task;
|
||||
LocaleLineHandler lineHandler = removePlayer0(reloadPlayerTask.player());
|
||||
if (lineHandler == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
lineHandler.hide(removePlayerTask.player());
|
||||
lineHandler.removePlayer(removePlayerTask.player());
|
||||
scoreboardLibrary.packetAdapter().removeSidebar(Set.of(removePlayerTask.player()));
|
||||
|
||||
Objects.requireNonNull(scoreboardLibrary.getPlayer(removePlayerTask.player())).removeSidebar(this);
|
||||
} else if (task instanceof SidebarTask.ReloadPlayer reloadPlayerTask) {
|
||||
var lineHandler = removePlayer0(reloadPlayerTask.player());
|
||||
if (lineHandler == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
lineHandler.removePlayer(reloadPlayerTask.player());
|
||||
packetAdapter.remove(Collections.singleton(reloadPlayerTask.player()));
|
||||
lineHandler.hide(reloadPlayerTask.player());
|
||||
lineHandler.removePlayer(reloadPlayerTask.player());
|
||||
|
||||
show(reloadPlayerTask.player());
|
||||
} else if (task instanceof SidebarTask.UpdateLine updateLineTask) {
|
||||
forEachSidebar(sidebar -> {
|
||||
var value = lines[updateLineTask.line()].value();
|
||||
var renderedValue = value == null ? null : GlobalTranslator.render(value, sidebar.locale());
|
||||
sidebar.updateLine(updateLineTask.line(), renderedValue);
|
||||
display(reloadPlayerTask.player());
|
||||
} else if (task instanceof SidebarTask.UpdateLine) {
|
||||
SidebarTask.UpdateLine updateLineTask = (SidebarTask.UpdateLine) task;
|
||||
forEachLineHandler(sidebar -> {
|
||||
if (updateLineTask.updateValue()) {
|
||||
Component value = lines[updateLineTask.line()].value();
|
||||
Component renderedValue = value == null ? null : GlobalTranslator.render(value, sidebar.locale());
|
||||
sidebar.updateLine(updateLineTask.line(), renderedValue);
|
||||
}
|
||||
|
||||
if (updateLineTask.updateScore()) {
|
||||
sidebar.updateScoreFormat(updateLineTask.line());
|
||||
}
|
||||
});
|
||||
} else if (task instanceof SidebarTask.UpdateScores) {
|
||||
forEachSidebar(LocaleLineHandler::updateScores);
|
||||
forEachLineHandler(LocaleLineHandler::updateScores);
|
||||
for (GlobalLineInfo line : lines) {
|
||||
if (line != null) {
|
||||
line.updateScore(false);
|
||||
}
|
||||
}
|
||||
} else if (task instanceof SidebarTask.UpdateTitle) {
|
||||
packetAdapter.updateTitle(title);
|
||||
packetAdapter.sendObjectivePacket(internalPlayers(), SidebarPacketAdapter.ObjectivePacket.UPDATE);
|
||||
packetAdapter.sendProperties(internalPlayers(), PropertiesPacketType.UPDATE, title, ObjectiveRenderType.INTEGER, ScoreFormat.blank());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updateScores() {
|
||||
int size = 0;
|
||||
for (var line : lines) {
|
||||
for (GlobalLineInfo line : lines) {
|
||||
if (line != null && line.value() != null) {
|
||||
size++;
|
||||
}
|
||||
@@ -227,9 +259,9 @@ public abstract class AbstractSidebar implements Sidebar {
|
||||
boolean changed = false;
|
||||
int i = 0;
|
||||
|
||||
for (var line : lines) {
|
||||
for (GlobalLineInfo line : lines) {
|
||||
if (line != null && line.value() != null) {
|
||||
var newScore = size - i - 1;
|
||||
int newScore = size - i - 1;
|
||||
if (line.objectiveScore() != newScore) {
|
||||
changed = true;
|
||||
line.updateScore(true);
|
||||
@@ -246,9 +278,12 @@ public abstract class AbstractSidebar implements Sidebar {
|
||||
}
|
||||
|
||||
private @NotNull GlobalLineInfo getLineInfo(int line) {
|
||||
var globalLineInfo = lines[line];
|
||||
GlobalLineInfo globalLineInfo = lines[line];
|
||||
if (globalLineInfo == null) {
|
||||
globalLineInfo = lines[line] = new GlobalLineInfo(this, line);
|
||||
char c = Character.toChars(128 + line)[0];
|
||||
String playerName = "" + LegacyComponentSerializer.SECTION_CHAR + c;
|
||||
|
||||
globalLineInfo = lines[line] = new GlobalLineInfo(this, playerName, line);
|
||||
updateScores();
|
||||
}
|
||||
|
||||
|
||||
+14
-12
@@ -1,22 +1,24 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.sidebar;
|
||||
|
||||
import net.megavex.scoreboardlibrary.implementation.ScoreboardLibraryImpl;
|
||||
import net.megavex.scoreboardlibrary.implementation.commons.LocaleProvider;
|
||||
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.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import net.megavex.scoreboardlibrary.implementation.ScoreboardLibraryImpl;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.line.LocaleLineHandler;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class PlayerDependantLocaleSidebar extends AbstractSidebar {
|
||||
private final Map<Player, LocaleLineHandler> playerMap = new HashMap<>();
|
||||
private final Map<Locale, LocaleLineHandler> localeMap = new HashMap<>();
|
||||
|
||||
public PlayerDependantLocaleSidebar(ScoreboardLibraryImpl scoreboardLibrary, int maxLines) {
|
||||
super(scoreboardLibrary, maxLines);
|
||||
public PlayerDependantLocaleSidebar(@NotNull ScoreboardLibraryImpl scoreboardLibrary, int maxLines, @NotNull String objectiveName) {
|
||||
super(scoreboardLibrary, maxLines, objectiveName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -30,9 +32,9 @@ public class PlayerDependantLocaleSidebar extends AbstractSidebar {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void forEachSidebar(@NotNull Consumer<LocaleLineHandler> consumer) {
|
||||
protected void forEachLineHandler(@NotNull Consumer<LocaleLineHandler> consumer) {
|
||||
if (localeMap != null) {
|
||||
for (var value : localeMap.values()) {
|
||||
for (LocaleLineHandler value : localeMap.values()) {
|
||||
consumer.accept(value);
|
||||
}
|
||||
}
|
||||
@@ -40,12 +42,12 @@ public class PlayerDependantLocaleSidebar extends AbstractSidebar {
|
||||
|
||||
@Override
|
||||
protected @Nullable LocaleLineHandler addPlayer0(@NotNull Player player) {
|
||||
var sidebar = playerMap.get(player);
|
||||
LocaleLineHandler sidebar = playerMap.get(player);
|
||||
if (sidebar != null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var locale = scoreboardLibrary().localeProvider().locale(player);
|
||||
Locale locale = LocaleProvider.locale(player);
|
||||
|
||||
sidebar = localeMap.get(locale);
|
||||
if (sidebar != null) {
|
||||
@@ -65,7 +67,7 @@ public class PlayerDependantLocaleSidebar extends AbstractSidebar {
|
||||
protected @Nullable LocaleLineHandler removePlayer0(@NotNull Player player) {
|
||||
if (playerMap == null) return null;
|
||||
|
||||
var lineHandler = playerMap.remove(player);
|
||||
LocaleLineHandler lineHandler = playerMap.remove(player);
|
||||
if (lineHandler == null) return null;
|
||||
|
||||
if (!lineHandler.hasPlayers() && localeMap != null) {
|
||||
|
||||
+16
-2
@@ -3,7 +3,10 @@ package net.megavex.scoreboardlibrary.implementation.sidebar;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public sealed class SidebarTask {
|
||||
public class SidebarTask {
|
||||
private SidebarTask() {
|
||||
}
|
||||
|
||||
public static final class Close extends SidebarTask {
|
||||
public static final Close INSTANCE = new Close();
|
||||
|
||||
@@ -49,14 +52,25 @@ public sealed class SidebarTask {
|
||||
|
||||
public static final class UpdateLine extends SidebarTask {
|
||||
private final int line;
|
||||
private final boolean updateValue, updateScore;
|
||||
|
||||
public UpdateLine(int line) {
|
||||
public UpdateLine(int line, boolean updateValue, boolean updateScore) {
|
||||
this.line = line;
|
||||
this.updateValue = updateValue;
|
||||
this.updateScore = updateScore;
|
||||
}
|
||||
|
||||
public int line() {
|
||||
return line;
|
||||
}
|
||||
|
||||
public boolean updateValue() {
|
||||
return updateValue;
|
||||
}
|
||||
|
||||
public boolean updateScore() {
|
||||
return updateScore;
|
||||
}
|
||||
}
|
||||
|
||||
public static final class UpdateScores extends SidebarTask {
|
||||
|
||||
+21
-6
@@ -1,17 +1,24 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.sidebar;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import net.megavex.scoreboardlibrary.implementation.ScoreboardLibraryImpl;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import net.megavex.scoreboardlibrary.implementation.scheduler.RunningTask;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class SidebarUpdaterTask extends BukkitRunnable {
|
||||
import java.util.Iterator;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class SidebarUpdaterTask implements Runnable {
|
||||
private final ScoreboardLibraryImpl scoreboardLibrary;
|
||||
private final RunningTask task;
|
||||
private final Object lock = new Object();
|
||||
|
||||
public SidebarUpdaterTask(@NotNull ScoreboardLibraryImpl scoreboardLibrary) {
|
||||
this.scoreboardLibrary = scoreboardLibrary;
|
||||
runTaskTimerAsynchronously(scoreboardLibrary.plugin(), 1, 1);
|
||||
this.task = scoreboardLibrary.taskScheduler().runEveryTick(this);
|
||||
}
|
||||
|
||||
public @NotNull RunningTask task() {
|
||||
return task;
|
||||
}
|
||||
|
||||
public @NotNull Object lock() {
|
||||
@@ -21,11 +28,19 @@ public class SidebarUpdaterTask extends BukkitRunnable {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (lock) {
|
||||
for (var sidebar : scoreboardLibrary.sidebars()) {
|
||||
Iterator<AbstractSidebar> iterator = scoreboardLibrary.sidebars().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
AbstractSidebar sidebar = iterator.next();
|
||||
boolean result;
|
||||
try {
|
||||
sidebar.tick();
|
||||
result = sidebar.tick();
|
||||
} catch (Exception e) {
|
||||
scoreboardLibrary.plugin().getLogger().log(Level.WARNING, "an error occurred while updating a Sidebar instance", e);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
-10
@@ -1,8 +1,5 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.sidebar;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import net.megavex.scoreboardlibrary.implementation.ScoreboardLibraryImpl;
|
||||
import net.megavex.scoreboardlibrary.implementation.commons.CollectionProvider;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.line.LocaleLineHandler;
|
||||
@@ -10,16 +7,20 @@ import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class SingleLocaleSidebar extends AbstractSidebar {
|
||||
private final Locale locale;
|
||||
private final LocaleLineHandler sidebar;
|
||||
private final Set<Player> internalPlayers;
|
||||
|
||||
public SingleLocaleSidebar(@NotNull ScoreboardLibraryImpl scoreboardLibrary, int size, 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(1);
|
||||
this.sidebar = new LocaleLineHandler(this, locale);
|
||||
this.internalPlayers = CollectionProvider.set(8);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -35,19 +36,17 @@ 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;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void forEachSidebar(@NotNull Consumer<LocaleLineHandler> consumer) {
|
||||
protected void forEachLineHandler(@NotNull Consumer<LocaleLineHandler> consumer) {
|
||||
consumer.accept(sidebar);
|
||||
}
|
||||
}
|
||||
+21
-16
@@ -1,42 +1,39 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.sidebar.line;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.TeamsPacketAdapter;
|
||||
import net.megavex.scoreboardlibrary.api.objective.ScoreFormat;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamsPacketAdapter;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.AbstractSidebar;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class GlobalLineInfo {
|
||||
private static final String[] lineColors = new String[15];
|
||||
|
||||
static {
|
||||
var values = ChatColor.values();
|
||||
for (int i = 0; i < lineColors.length; i++) {
|
||||
lineColors[i] = values[i].toString();
|
||||
}
|
||||
}
|
||||
|
||||
private final String player;
|
||||
private final int line;
|
||||
private final TeamsPacketAdapter<?, ?> packetAdapter;
|
||||
private final TeamsPacketAdapter packetAdapter;
|
||||
private Component value;
|
||||
private ScoreFormat scoreFormat;
|
||||
private int objectiveScore;
|
||||
private boolean updateScore;
|
||||
|
||||
public GlobalLineInfo(@NotNull AbstractSidebar sidebar, int line) {
|
||||
public GlobalLineInfo(@NotNull AbstractSidebar sidebar, @NotNull String player, int line) {
|
||||
this.player = player;
|
||||
this.line = line;
|
||||
this.packetAdapter = sidebar.scoreboardLibrary().packetAdapter().createTeamPacketAdapter("sidebar_line_" + line);
|
||||
this.packetAdapter = sidebar
|
||||
.scoreboardLibrary()
|
||||
.packetAdapter()
|
||||
.createTeamPacketAdapter("sidebar_line_" + line);
|
||||
}
|
||||
|
||||
public @NotNull String player() {
|
||||
return lineColors[line];
|
||||
return player;
|
||||
}
|
||||
|
||||
public int line() {
|
||||
return line;
|
||||
}
|
||||
|
||||
public @NotNull TeamsPacketAdapter<?, ?> packetAdapter() {
|
||||
public @NotNull TeamsPacketAdapter packetAdapter() {
|
||||
return packetAdapter;
|
||||
}
|
||||
|
||||
@@ -48,6 +45,14 @@ public class GlobalLineInfo {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public @Nullable ScoreFormat scoreFormat() {
|
||||
return scoreFormat;
|
||||
}
|
||||
|
||||
public void scoreFormat(@Nullable ScoreFormat scoreFormat) {
|
||||
this.scoreFormat = scoreFormat;
|
||||
}
|
||||
|
||||
public int objectiveScore() {
|
||||
return objectiveScore;
|
||||
}
|
||||
|
||||
+54
-62
@@ -1,117 +1,109 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.sidebar.line;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
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 {
|
||||
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) {
|
||||
var isLegacy = sidebar.scoreboardLibrary().packetAdapter().isLegacy(player);
|
||||
var 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);
|
||||
}
|
||||
return lineHandler;
|
||||
}
|
||||
|
||||
throw new RuntimeException();
|
||||
public void updateScoreFormat(int 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(Set.of(player));
|
||||
} else if (legacyLineHandler != null && legacyLineHandler.players().contains(player)) {
|
||||
legacyLineHandler.show(Set.of(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(Set.of(player));
|
||||
} else if (legacyLineHandler != null && legacyLineHandler.players().contains(player)) {
|
||||
legacyLineHandler.hide(Set.of(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());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+44
-33
@@ -1,31 +1,35 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.sidebar.line;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
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.Set;
|
||||
|
||||
public class SidebarLineHandler {
|
||||
private final LineType lineType;
|
||||
private final LineRenderingStrategy strategy;
|
||||
private final LocaleLineHandler localeLineHandler;
|
||||
private final Set<Player> players = CollectionProvider.set(1);
|
||||
private final LocaleLine<?>[] lines;
|
||||
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()];
|
||||
|
||||
for (var line : localeLineHandler.sidebar().lines()) {
|
||||
for (GlobalLineInfo line : localeLineHandler.sidebar().lines()) {
|
||||
if (line != null) {
|
||||
var value = line.value();
|
||||
Component value = line.value();
|
||||
if (value != null) {
|
||||
setLine(line.line(), GlobalTranslator.render(value, localeLineHandler.locale()), false);
|
||||
setLine(line.line(), GlobalTranslator.render(value, localeLineHandler.locale()));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,35 +43,28 @@ public class SidebarLineHandler {
|
||||
return players;
|
||||
}
|
||||
|
||||
public void updateLine(int lineIndex) {
|
||||
var line = lines[lineIndex];
|
||||
if (line != null) {
|
||||
line.updateTeam();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateScores() {
|
||||
for (var line : lines) {
|
||||
for (LocaleLine line : lines) {
|
||||
if (line != null && line.info().updateScore()) {
|
||||
line.sendScore(players);
|
||||
line.info().updateScore(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setLine(int line, Component renderedLine) {
|
||||
setLine(line, renderedLine, true);
|
||||
public void updateScore(int line) {
|
||||
LocaleLine localeLine = lines[line];
|
||||
localeLine.sendScore(players);
|
||||
}
|
||||
|
||||
private void setLine(int line, Component renderedLine, boolean sendPackets) {
|
||||
var localeLine = lines[line];
|
||||
public void setLine(int line, Component renderedLine) {
|
||||
LocaleLine localeLine = lines[line];
|
||||
if (renderedLine == null && localeLine == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var newlyCreated = false;
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -77,17 +74,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 (var line : lines) {
|
||||
for (LocaleLine line : lines) {
|
||||
if (line != null) {
|
||||
line.show(players);
|
||||
}
|
||||
@@ -95,10 +93,23 @@ public class SidebarLineHandler {
|
||||
}
|
||||
|
||||
public void hide(Collection<Player> players) {
|
||||
for (var 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
+50
-36
@@ -1,22 +1,30 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.sidebar.line.locale;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.TeamsPacketAdapter;
|
||||
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;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamDisplayPacketAdapter;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.line.GlobalLineInfo;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.line.SidebarLineHandler;
|
||||
import org.bukkit.ChatColor;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
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 class LegacyLocaleLine implements LocaleLine, ImmutableTeamProperties<String> {
|
||||
private final GlobalLineInfo info;
|
||||
private final SidebarLineHandler handler;
|
||||
private final TeamsPacketAdapter.TeamDisplayPacketAdapter<String> packetAdapter;
|
||||
private final TeamDisplayPacketAdapter packetAdapter;
|
||||
private String player, oldPlayer;
|
||||
private String prefix, suffix;
|
||||
private String currentValue;
|
||||
@@ -26,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() {
|
||||
@@ -49,15 +57,16 @@ class LegacyLocaleLine implements LocaleLine<String> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<String> entries() {
|
||||
return Set.of(player);
|
||||
public @NotNull Collection<String> syncedEntries() {
|
||||
return ImmutableList.of(player);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void value(@NotNull Component renderedComponent) {
|
||||
var legacyValue = legacySection().serialize(renderedComponent);
|
||||
// I really need to rewrite this
|
||||
String legacyValue = legacySection().serialize(renderedComponent);
|
||||
|
||||
oldPlayer = player;
|
||||
String prevPlayer = player;
|
||||
|
||||
if (legacyValue.length() <= 16) {
|
||||
this.prefix = legacyValue;
|
||||
@@ -67,29 +76,27 @@ class LegacyLocaleLine implements LocaleLine<String> {
|
||||
this.player = info.player();
|
||||
}
|
||||
} else {
|
||||
var color = legacyValue.charAt(15) == ChatColor.COLOR_CHAR;
|
||||
boolean endsWithSection = legacyValue.charAt(15) == LegacyComponentSerializer.SECTION_CHAR;
|
||||
|
||||
var prefixEnd = color ? 15 : 16;
|
||||
int prefixEnd = endsWithSection ? 15 : 16;
|
||||
this.prefix = legacyValue.substring(0, prefixEnd);
|
||||
|
||||
this.player = info.player() + ChatColor.RESET
|
||||
+ ChatColor.getLastColors(prefix +
|
||||
ChatColor.COLOR_CHAR + (color ? legacyValue.charAt(16) : ""));
|
||||
String last = prefix + LegacyComponentSerializer.SECTION_CHAR + (endsWithSection ? legacyValue.charAt(16) : "");
|
||||
this.player = info.player() + ChatColor.getLastColors(last);
|
||||
|
||||
var playerEnd = prefixEnd;
|
||||
int playerEnd = prefixEnd;
|
||||
if (legacyValue.length() > 32) {
|
||||
var remaining = 16 - player.length();
|
||||
int remaining = 16 - player.length();
|
||||
assert remaining > 0;
|
||||
|
||||
playerEnd += remaining;
|
||||
player += legacyValue.substring(prefixEnd, playerEnd);
|
||||
}
|
||||
|
||||
this.suffix = legacyValue.substring(playerEnd + (color ? 2 : 0));
|
||||
this.suffix = ChatColor.getLastColors(this.player) + legacyValue.substring(playerEnd + (endsWithSection ? 2 : 0));
|
||||
if (suffix.length() > 16) {
|
||||
var newSuffix = suffix.substring(0, 16);
|
||||
if (newSuffix.endsWith(String.valueOf(ChatColor.COLOR_CHAR)) &&
|
||||
ChatColor.getByChar(suffix.charAt(16)) != null) {
|
||||
String newSuffix = suffix.substring(0, 16);
|
||||
if (newSuffix.endsWith(String.valueOf(LegacyComponentSerializer.SECTION_CHAR))) {
|
||||
newSuffix = newSuffix.substring(0, 15);
|
||||
}
|
||||
|
||||
@@ -99,50 +106,57 @@ class LegacyLocaleLine implements LocaleLine<String> {
|
||||
|
||||
currentValue = legacyValue;
|
||||
|
||||
if (oldPlayer.equals(player)) {
|
||||
oldPlayer = null;
|
||||
if (!player.equals(prevPlayer)) {
|
||||
oldPlayer = prevPlayer;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTeam() {
|
||||
var players = handler.players();
|
||||
Set<Player> players = handler.players();
|
||||
if (oldPlayer != null) {
|
||||
packetAdapter.removeEntries(players, Set.of(oldPlayer));
|
||||
handler.localeLineHandler().sidebar().packetAdapter().removeLine(players, oldPlayer);
|
||||
ObjectivePacketAdapter pd = handler.localeLineHandler().sidebar().packetAdapter();
|
||||
|
||||
packetAdapter.sendEntries(EntriesPacketType.REMOVE, players, Collections.singleton(oldPlayer));
|
||||
pd.removeScore(players, oldPlayer);
|
||||
oldPlayer = null;
|
||||
|
||||
var entries = entries();
|
||||
packetAdapter.updateTeamPackets(entries);
|
||||
packetAdapter.addEntries(players, entries);
|
||||
handler.localeLineHandler().sidebar().packetAdapter().score(players, info.objectiveScore(), player);
|
||||
packetAdapter.sendEntries(EntriesPacketType.ADD, players, Collections.singleton(player));
|
||||
pd.sendScore(players, player, info.objectiveScore(), null, null);
|
||||
}
|
||||
|
||||
packetAdapter.updateTeam(players);
|
||||
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();
|
||||
if (oldPlayer != null) {
|
||||
handler.localeLineHandler().sidebar().packetAdapter().removeLine(players, oldPlayer);
|
||||
pd.removeScore(players, oldPlayer);
|
||||
}
|
||||
|
||||
handler.localeLineHandler().sidebar().packetAdapter().score(players, info.objectiveScore(), player);
|
||||
pd.sendScore(players, player, info.objectiveScore(), null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show(@NotNull Collection<Player> players) {
|
||||
sendScore(players);
|
||||
packetAdapter.createTeam(players);
|
||||
packetAdapter.sendProperties(PropertiesPacketType.CREATE, players);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide(@NotNull Collection<Player> players) {
|
||||
ObjectivePacketAdapter pd = handler.localeLineHandler().sidebar().packetAdapter();
|
||||
if (oldPlayer != null) {
|
||||
handler.localeLineHandler().sidebar().packetAdapter().removeLine(players, oldPlayer);
|
||||
pd.removeScore(players, oldPlayer);
|
||||
}
|
||||
|
||||
handler.localeLineHandler().sidebar().packetAdapter().removeLine(players, player);
|
||||
pd.removeScore(players, player);
|
||||
info.packetAdapter().removeTeam(players);
|
||||
}
|
||||
}
|
||||
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.sidebar.line.locale;
|
||||
|
||||
import java.util.function.BiFunction;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.AbstractSidebar;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.line.GlobalLineInfo;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.line.SidebarLineHandler;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
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 static LineType getType(AbstractSidebar sidebar, Player player) {
|
||||
return sidebar.scoreboardLibrary().packetAdapter().isLegacy(player) ? LEGACY : MODERN;
|
||||
}
|
||||
|
||||
public LocaleLine<?> create(GlobalLineInfo line, SidebarLineHandler localeLineHandler) {
|
||||
return constructor.apply(line, localeLineHandler);
|
||||
}
|
||||
}
|
||||
+6
-3
@@ -1,19 +1,22 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.sidebar.line.locale;
|
||||
|
||||
import java.util.Collection;
|
||||
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;
|
||||
|
||||
public interface LocaleLine<C> extends ImmutableTeamProperties<C> {
|
||||
import java.util.Collection;
|
||||
|
||||
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);
|
||||
|
||||
+20
-15
@@ -1,30 +1,32 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.sidebar.line.locale;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Set;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.TeamsPacketAdapter;
|
||||
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;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.line.SidebarLineHandler;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
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 class ModernLocaleLine implements LocaleLine, ImmutableTeamProperties<Component> {
|
||||
private final GlobalLineInfo info;
|
||||
private final SidebarLineHandler handler;
|
||||
private final Collection<String> entries;
|
||||
private final TeamsPacketAdapter.TeamDisplayPacketAdapter<Component> packetAdapter;
|
||||
private final TeamDisplayPacketAdapter packetAdapter;
|
||||
|
||||
public ModernLocaleLine(GlobalLineInfo info, SidebarLineHandler handler) {
|
||||
this.info = info;
|
||||
this.handler = handler;
|
||||
this.entries = Set.of(info.player());
|
||||
this.entries = ImmutableList.of(info.player());
|
||||
this.packetAdapter = info.packetAdapter().createTeamDisplayAdapter(this);
|
||||
packetAdapter.updateTeamPackets(entries);
|
||||
packetAdapter.updateTeamPackets();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -38,29 +40,32 @@ class ModernLocaleLine implements LocaleLine<Component> {
|
||||
|
||||
@Override
|
||||
public void updateTeam() {
|
||||
packetAdapter.updateTeamPackets(entries);
|
||||
packetAdapter.updateTeam(handler.players());
|
||||
packetAdapter.updateTeamPackets();
|
||||
packetAdapter.sendProperties(PropertiesPacketType.UPDATE, handler.players());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendScore(@NotNull Collection<Player> players) {
|
||||
handler.localeLineHandler().sidebar().packetAdapter().score(players, info.objectiveScore(), info.player());
|
||||
handler.localeLineHandler()
|
||||
.sidebar()
|
||||
.packetAdapter()
|
||||
.sendScore(players, info.player(), info.objectiveScore(), null, info.scoreFormat());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show(@NotNull Collection<Player> players) {
|
||||
sendScore(players);
|
||||
packetAdapter.createTeam(players);
|
||||
packetAdapter.sendProperties(PropertiesPacketType.CREATE, players);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide(@NotNull Collection<Player> players) {
|
||||
handler.localeLineHandler().sidebar().packetAdapter().removeLine(players, info.player());
|
||||
handler.localeLineHandler().sidebar().packetAdapter().removeScore(players, info.player());
|
||||
info.packetAdapter().removeTeam(players);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<String> entries() {
|
||||
public @NotNull Collection<String> syncedEntries() {
|
||||
return entries;
|
||||
}
|
||||
|
||||
@@ -71,7 +76,7 @@ class ModernLocaleLine implements LocaleLine<Component> {
|
||||
|
||||
@Override
|
||||
public @NotNull Component prefix() {
|
||||
var value = info.value();
|
||||
Component value = info.value();
|
||||
return value == null ? empty() : value;
|
||||
}
|
||||
|
||||
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.sidebar.line.locale;
|
||||
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.line.GlobalLineInfo;
|
||||
import net.megavex.scoreboardlibrary.implementation.sidebar.line.SidebarLineHandler;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
// Implementation for versions 1.20.3+
|
||||
// This is not yet being used as it might break on servers with plugins such as ViaBackwards,
|
||||
// however it will once Mojang decides to remove legacy chat format support
|
||||
public class PostModernLocaleLine implements LocaleLine {
|
||||
private final GlobalLineInfo info;
|
||||
private final SidebarLineHandler handler;
|
||||
|
||||
public PostModernLocaleLine(GlobalLineInfo info, SidebarLineHandler handler) {
|
||||
this.info = info;
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull GlobalLineInfo info() {
|
||||
return info;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void value(@NotNull Component renderedComponent) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTeam() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendScore(@NotNull Collection<Player> players) {
|
||||
handler.localeLineHandler()
|
||||
.sidebar()
|
||||
.packetAdapter()
|
||||
.sendScore(players, info.player(), info.objectiveScore(), info.value(), info.scoreFormat());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void show(@NotNull Collection<Player> players) {
|
||||
sendScore(players);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hide(@NotNull Collection<Player> players) {
|
||||
handler.localeLineHandler().sidebar().packetAdapter().removeScore(players, info.player());
|
||||
}
|
||||
}
|
||||
+22
-22
@@ -1,21 +1,21 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.team;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import net.megavex.scoreboardlibrary.api.team.ScoreboardTeam;
|
||||
import net.megavex.scoreboardlibrary.api.team.TeamDisplay;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.TeamsPacketAdapter;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.EntriesPacketType;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamsPacketAdapter;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class ScoreboardTeamImpl implements ScoreboardTeam {
|
||||
private final TeamManagerImpl teamManager;
|
||||
private final String name;
|
||||
private final TeamsPacketAdapter<?, ?> packetAdapter;
|
||||
private final TeamsPacketAdapter packetAdapter;
|
||||
|
||||
private final TeamDisplayImpl defaultDisplay;
|
||||
private final Map<Player, TeamDisplayImpl> displayMap = new ConcurrentHashMap<>();
|
||||
@@ -66,12 +66,12 @@ public class ScoreboardTeamImpl implements ScoreboardTeam {
|
||||
throw new IllegalArgumentException("invalid TeamDisplay");
|
||||
}
|
||||
|
||||
var oldTeamDisplay = Objects.requireNonNull(displayMap.put(player, (TeamDisplayImpl) teamDisplay));
|
||||
TeamDisplayImpl oldTeamDisplay = Objects.requireNonNull(displayMap.put(player, (TeamDisplayImpl) teamDisplay));
|
||||
if (oldTeamDisplay == teamDisplay) {
|
||||
return;
|
||||
}
|
||||
|
||||
teamManager.taskQueue().add(new TeamManagerTask.ChangeTeamDisplayTask(player, this, oldTeamDisplay, (TeamDisplayImpl) teamDisplay));
|
||||
teamManager.taskQueue().add(new TeamManagerTask.ChangeTeamDisplay(player, this, oldTeamDisplay, (TeamDisplayImpl) teamDisplay));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -79,7 +79,7 @@ public class ScoreboardTeamImpl implements ScoreboardTeam {
|
||||
return new TeamDisplayImpl(this);
|
||||
}
|
||||
|
||||
public @NotNull TeamsPacketAdapter<?, ?> packetAdapter() {
|
||||
public @NotNull TeamsPacketAdapter packetAdapter() {
|
||||
return packetAdapter;
|
||||
}
|
||||
|
||||
@@ -88,16 +88,16 @@ public class ScoreboardTeamImpl implements ScoreboardTeam {
|
||||
}
|
||||
|
||||
public void addPlayer(@NotNull Player player) {
|
||||
var teamDisplay = Objects.requireNonNull(displayMap.get(player));
|
||||
TeamDisplayImpl teamDisplay = Objects.requireNonNull(displayMap.get(player));
|
||||
if (teamDisplay.players().add(player)) {
|
||||
teamDisplay.packetAdapter().createTeam(Set.of(player));
|
||||
teamDisplay.packetAdapter().sendProperties(PropertiesPacketType.CREATE, Collections.singleton(player));
|
||||
}
|
||||
}
|
||||
|
||||
public void removePlayer(@NotNull Player player) {
|
||||
var teamDisplay = Objects.requireNonNull(displayMap.remove(player));
|
||||
TeamDisplayImpl teamDisplay = Objects.requireNonNull(displayMap.remove(player));
|
||||
if (teamDisplay.players().remove(player)) {
|
||||
packetAdapter.removeTeam(Set.of(player));
|
||||
packetAdapter.removeTeam(Collections.singleton(player));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,27 +108,27 @@ public class ScoreboardTeamImpl implements ScoreboardTeam {
|
||||
|
||||
newTeamDisplay.players().add(player);
|
||||
|
||||
var singleton = Set.of(player);
|
||||
newTeamDisplay.packetAdapter().updateTeam(singleton);
|
||||
Collection<Player> singleton = Collections.singleton(player);
|
||||
newTeamDisplay.packetAdapter().sendProperties(PropertiesPacketType.UPDATE, singleton);
|
||||
|
||||
var oldEntries = oldTeamDisplay.entries();
|
||||
var newEntries = newTeamDisplay.entries();
|
||||
Collection<String> oldEntries = oldTeamDisplay.syncedEntries();
|
||||
Collection<String> newEntries = newTeamDisplay.syncedEntries();
|
||||
|
||||
if (oldEntries.isEmpty()) {
|
||||
newTeamDisplay.packetAdapter().addEntries(singleton, newEntries);
|
||||
newTeamDisplay.packetAdapter().sendEntries(EntriesPacketType.ADD, singleton, newEntries);
|
||||
return;
|
||||
}
|
||||
|
||||
var entries = new ArrayList<>(oldEntries);
|
||||
List<String> entries = new ArrayList<>(oldEntries);
|
||||
entries.removeAll(newEntries);
|
||||
if (!entries.isEmpty()) {
|
||||
newTeamDisplay.packetAdapter().removeEntries(singleton, entries);
|
||||
newTeamDisplay.packetAdapter().sendEntries(EntriesPacketType.REMOVE, singleton, entries);
|
||||
}
|
||||
|
||||
entries = new ArrayList<>(newEntries);
|
||||
entries.removeAll(oldEntries);
|
||||
if (!entries.isEmpty()) {
|
||||
newTeamDisplay.packetAdapter().addEntries(singleton, entries);
|
||||
newTeamDisplay.packetAdapter().sendEntries(EntriesPacketType.ADD, singleton, entries);
|
||||
}
|
||||
}
|
||||
}
|
||||
+51
-27
@@ -1,11 +1,8 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.team;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import net.kyori.adventure.text.Component;
|
||||
import net.kyori.adventure.text.ComponentLike;
|
||||
import net.kyori.adventure.text.format.NamedTextColor;
|
||||
import net.megavex.scoreboardlibrary.api.team.ScoreboardTeam;
|
||||
import net.megavex.scoreboardlibrary.api.team.TeamDisplay;
|
||||
@@ -13,19 +10,23 @@ import net.megavex.scoreboardlibrary.api.team.enums.CollisionRule;
|
||||
import net.megavex.scoreboardlibrary.api.team.enums.NameTagVisibility;
|
||||
import net.megavex.scoreboardlibrary.implementation.commons.CollectionProvider;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.ImmutableTeamProperties;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.TeamsPacketAdapter;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.EntriesPacketType;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamDisplayPacketAdapter;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static net.kyori.adventure.text.Component.empty;
|
||||
|
||||
public class TeamDisplayImpl implements TeamDisplay, ImmutableTeamProperties<Component> {
|
||||
private final ScoreboardTeamImpl team;
|
||||
private final TeamsPacketAdapter.TeamDisplayPacketAdapter<?> packetAdapter;
|
||||
private final TeamDisplayPacketAdapter packetAdapter;
|
||||
private final Set<Player> players = CollectionProvider.set(4);
|
||||
private final Set<String> entries = CollectionProvider.set(4);
|
||||
private final List<String> entries = CollectionProvider.list(4);
|
||||
private final List<String> syncedEntries = CollectionProvider.list(4);
|
||||
private Component displayName = empty(),
|
||||
prefix = empty(),
|
||||
suffix = empty();
|
||||
@@ -34,10 +35,10 @@ public class TeamDisplayImpl implements TeamDisplay, ImmutableTeamProperties<Com
|
||||
private CollisionRule collisionRule = CollisionRule.ALWAYS;
|
||||
private NamedTextColor playerColor = null;
|
||||
|
||||
public TeamDisplayImpl(ScoreboardTeamImpl team) {
|
||||
public TeamDisplayImpl(@NotNull ScoreboardTeamImpl team) {
|
||||
this.team = team;
|
||||
this.packetAdapter = team.packetAdapter().createTeamDisplayAdapter(this);
|
||||
updateTeamPackets();
|
||||
packetAdapter.updateTeamPackets();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -47,13 +48,19 @@ public class TeamDisplayImpl implements TeamDisplay, ImmutableTeamProperties<Com
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<String> entries() {
|
||||
return Collections.unmodifiableSet(entries);
|
||||
return Collections.unmodifiableCollection(entries);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<String> syncedEntries() {
|
||||
return syncedEntries;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean addEntry(@NotNull String entry) {
|
||||
if (entries.add(entry)) {
|
||||
team.teamManager().taskQueue().add(new TeamManagerTask.AddEntries(this, Set.of(entry)));
|
||||
if (!entries.contains(entry)) {
|
||||
entries.add(entry);
|
||||
team.teamManager().taskQueue().add(new TeamManagerTask.AddEntries(this, Collections.singleton(entry)));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -63,7 +70,7 @@ public class TeamDisplayImpl implements TeamDisplay, ImmutableTeamProperties<Com
|
||||
@Override
|
||||
public boolean removeEntry(@NotNull String entry) {
|
||||
if (entries.remove(entry)) {
|
||||
team.teamManager().taskQueue().add(new TeamManagerTask.RemoveEntries(this, Set.of(entry)));
|
||||
team.teamManager().taskQueue().add(new TeamManagerTask.RemoveEntries(this, Collections.singleton(entry)));
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -76,11 +83,12 @@ public class TeamDisplayImpl implements TeamDisplay, ImmutableTeamProperties<Com
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TeamDisplay displayName(@NotNull Component displayName) {
|
||||
public @NotNull TeamDisplay displayName(@NotNull ComponentLike displayName) {
|
||||
Preconditions.checkNotNull(displayName);
|
||||
|
||||
if (!Objects.equals(this.displayName, displayName)) {
|
||||
this.displayName = displayName;
|
||||
Component component = displayName.asComponent();
|
||||
if (!Objects.equals(this.displayName, component)) {
|
||||
this.displayName = component;
|
||||
scheduleUpdate();
|
||||
}
|
||||
|
||||
@@ -93,11 +101,12 @@ public class TeamDisplayImpl implements TeamDisplay, ImmutableTeamProperties<Com
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TeamDisplay prefix(@NotNull Component prefix) {
|
||||
public @NotNull TeamDisplay prefix(@NotNull ComponentLike prefix) {
|
||||
Preconditions.checkNotNull(prefix);
|
||||
|
||||
if (!Objects.equals(this.prefix, prefix)) {
|
||||
this.prefix = prefix;
|
||||
Component component = prefix.asComponent();
|
||||
if (!Objects.equals(this.prefix, component)) {
|
||||
this.prefix = component;
|
||||
scheduleUpdate();
|
||||
}
|
||||
|
||||
@@ -110,11 +119,12 @@ public class TeamDisplayImpl implements TeamDisplay, ImmutableTeamProperties<Com
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull TeamDisplay suffix(@NotNull Component suffix) {
|
||||
public @NotNull TeamDisplay suffix(@NotNull ComponentLike suffix) {
|
||||
Preconditions.checkNotNull(suffix);
|
||||
|
||||
if (!Objects.equals(this.suffix, suffix)) {
|
||||
this.suffix = suffix;
|
||||
Component component = suffix.asComponent();
|
||||
if (!Objects.equals(this.suffix, component)) {
|
||||
this.suffix = component;
|
||||
scheduleUpdate();
|
||||
}
|
||||
|
||||
@@ -200,7 +210,7 @@ public class TeamDisplayImpl implements TeamDisplay, ImmutableTeamProperties<Com
|
||||
return this;
|
||||
}
|
||||
|
||||
public @NotNull TeamsPacketAdapter.TeamDisplayPacketAdapter<?> packetAdapter() {
|
||||
public @NotNull TeamDisplayPacketAdapter packetAdapter() {
|
||||
return packetAdapter;
|
||||
}
|
||||
|
||||
@@ -208,13 +218,27 @@ public class TeamDisplayImpl implements TeamDisplay, ImmutableTeamProperties<Com
|
||||
return players;
|
||||
}
|
||||
|
||||
public void updateTeamPackets() {
|
||||
packetAdapter.updateTeamPackets(entries);
|
||||
public void handleUpdateDisplay() {
|
||||
packetAdapter.updateTeamPackets();
|
||||
packetAdapter().sendProperties(PropertiesPacketType.UPDATE, players());
|
||||
}
|
||||
|
||||
public void handleAddEntries(@NotNull Collection<String> newEntries) {
|
||||
packetAdapter.sendEntries(EntriesPacketType.ADD, players(), newEntries);
|
||||
syncedEntries.addAll(newEntries);
|
||||
packetAdapter.updateTeamPackets();
|
||||
}
|
||||
|
||||
public void handleRemoveEntries(@NotNull Collection<String> oldEntries) {
|
||||
packetAdapter.sendEntries(EntriesPacketType.REMOVE, players(), oldEntries);
|
||||
syncedEntries.removeAll(oldEntries);
|
||||
packetAdapter.updateTeamPackets();
|
||||
}
|
||||
|
||||
private void scheduleUpdate() {
|
||||
var taskQueue = team.teamManager().taskQueue();
|
||||
if (taskQueue.peek() instanceof TeamManagerTask.UpdateTeamDisplay updateTeamTask && updateTeamTask.teamDisplay() == this) {
|
||||
Queue<TeamManagerTask> taskQueue = team.teamManager().taskQueue();
|
||||
TeamManagerTask lastTask = taskQueue.peek();
|
||||
if (lastTask instanceof TeamManagerTask.UpdateTeamDisplay && ((TeamManagerTask.UpdateTeamDisplay) lastTask).teamDisplay() == this) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+73
-65
@@ -1,35 +1,33 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.team;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Queue;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import net.megavex.scoreboardlibrary.api.team.ScoreboardTeam;
|
||||
import net.megavex.scoreboardlibrary.api.team.TeamDisplay;
|
||||
import net.megavex.scoreboardlibrary.api.team.TeamManager;
|
||||
import net.megavex.scoreboardlibrary.implementation.ScoreboardLibraryImpl;
|
||||
import net.megavex.scoreboardlibrary.implementation.commons.CollectionProvider;
|
||||
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType;
|
||||
import net.megavex.scoreboardlibrary.implementation.player.PlayerDisplayable;
|
||||
import net.megavex.scoreboardlibrary.implementation.player.ScoreboardLibraryPlayer;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class TeamManagerImpl implements TeamManager {
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class TeamManagerImpl implements TeamManager, PlayerDisplayable {
|
||||
private final ScoreboardLibraryImpl scoreboardLibrary;
|
||||
|
||||
private final Set<Player> players = CollectionProvider.set(8);
|
||||
private final List<Player> internalPlayers = CollectionProvider.list(8);
|
||||
|
||||
private final Map<String, ScoreboardTeamImpl> teams = new ConcurrentHashMap<>();
|
||||
|
||||
private boolean closed;
|
||||
|
||||
private final Queue<TeamManagerTask> taskQueue = new ConcurrentLinkedQueue<>();
|
||||
private boolean closed;
|
||||
|
||||
public TeamManagerImpl(@NotNull ScoreboardLibraryImpl scoreboardLibrary) {
|
||||
this.scoreboardLibrary = scoreboardLibrary;
|
||||
@@ -74,7 +72,7 @@ public class TeamManagerImpl implements TeamManager {
|
||||
checkClosed();
|
||||
name = name.toLowerCase();
|
||||
|
||||
var team = teams.get(name);
|
||||
ScoreboardTeamImpl team = teams.get(name);
|
||||
if (team != null) {
|
||||
return team;
|
||||
}
|
||||
@@ -82,13 +80,13 @@ public class TeamManagerImpl implements TeamManager {
|
||||
team = new ScoreboardTeamImpl(this, name);
|
||||
teams.put(name, team);
|
||||
|
||||
for (var player : players) {
|
||||
var teamDisplay = teamDisplayFunction == null ? team.defaultDisplay() : teamDisplayFunction.apply(player, team);
|
||||
for (Player player : players) {
|
||||
TeamDisplay teamDisplay = teamDisplayFunction == null ? team.defaultDisplay() : teamDisplayFunction.apply(player, team);
|
||||
validateTeamDisplay(team, teamDisplay);
|
||||
team.displayMap().put(player, (TeamDisplayImpl) teamDisplay);
|
||||
}
|
||||
|
||||
var task = new TeamManagerTask.AddTeam(team);
|
||||
TeamManagerTask.AddTeam task = new TeamManagerTask.AddTeam(team);
|
||||
taskQueue.add(task);
|
||||
return team;
|
||||
}
|
||||
@@ -98,7 +96,7 @@ public class TeamManagerImpl implements TeamManager {
|
||||
Preconditions.checkNotNull(name);
|
||||
checkClosed();
|
||||
|
||||
var team = teams.remove(name.toLowerCase());
|
||||
ScoreboardTeamImpl team = teams.remove(name.toLowerCase());
|
||||
if (team != null) {
|
||||
taskQueue.add(new TeamManagerTask.RemoveTeam(team));
|
||||
return true;
|
||||
@@ -127,8 +125,8 @@ public class TeamManagerImpl implements TeamManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var team : teams.values()) {
|
||||
var teamDisplay = teamDisplayFunction == null ? team.defaultDisplay() : teamDisplayFunction.apply(team);
|
||||
for (ScoreboardTeamImpl team : teams.values()) {
|
||||
TeamDisplay teamDisplay = teamDisplayFunction == null ? team.defaultDisplay() : teamDisplayFunction.apply(team);
|
||||
validateTeamDisplay(team, teamDisplay);
|
||||
team.displayMap().put(player, (TeamDisplayImpl) teamDisplay);
|
||||
}
|
||||
@@ -146,7 +144,7 @@ public class TeamManagerImpl implements TeamManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
var task = new TeamManagerTask.RemovePlayer(player);
|
||||
TeamManagerTask.RemovePlayer task = new TeamManagerTask.RemovePlayer(player);
|
||||
taskQueue.add(task);
|
||||
return true;
|
||||
}
|
||||
@@ -159,83 +157,93 @@ public class TeamManagerImpl implements TeamManager {
|
||||
return taskQueue;
|
||||
}
|
||||
|
||||
public void show(@NotNull Player player) {
|
||||
for (var team : teams.values()) {
|
||||
@Override
|
||||
public void display(@NotNull Player player) {
|
||||
for (ScoreboardTeamImpl team : teams.values()) {
|
||||
team.addPlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
public void tick() {
|
||||
public boolean tick() {
|
||||
while (true) {
|
||||
var task = taskQueue.poll();
|
||||
TeamManagerTask task = taskQueue.poll();
|
||||
if (task == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (task instanceof TeamManagerTask.Close) {
|
||||
scoreboardLibrary.teamManagers().remove(this);
|
||||
|
||||
for (var team : teams.values()) {
|
||||
Set<Player> removePlayers = CollectionProvider.set(players.size());
|
||||
for (ScoreboardTeamImpl team : teams.values()) {
|
||||
Set<Player> removePlayers = CollectionProvider.set(internalPlayers.size());
|
||||
for (TeamDisplayImpl value : team.displayMap().values()) {
|
||||
removePlayers.addAll(value.players());
|
||||
}
|
||||
team.packetAdapter().removeTeam(removePlayers);
|
||||
}
|
||||
|
||||
for (var player : players) {
|
||||
Objects.requireNonNull(scoreboardLibrary.getPlayer(player)).removeTeamManager(this);
|
||||
for (Player player : internalPlayers) {
|
||||
Objects.requireNonNull(scoreboardLibrary.getPlayer(player)).teamManagerQueue().remove(this);
|
||||
}
|
||||
return;
|
||||
} else if (task instanceof TeamManagerTask.AddPlayer addPlayerTask) {
|
||||
var slPlayer = scoreboardLibrary.getOrCreatePlayer(addPlayerTask.player());
|
||||
slPlayer.addTeamManager(this);
|
||||
} else if (task instanceof TeamManagerTask.RemovePlayer removePlayerTask) {
|
||||
for (var team : teams.values()) {
|
||||
return false;
|
||||
} else if (task instanceof TeamManagerTask.AddPlayer) {
|
||||
TeamManagerTask.AddPlayer addPlayerTask = (TeamManagerTask.AddPlayer) task;
|
||||
@NotNull ScoreboardLibraryPlayer slPlayer = scoreboardLibrary.getOrCreatePlayer(addPlayerTask.player());
|
||||
slPlayer.teamManagerQueue().add(this);
|
||||
internalPlayers.add(addPlayerTask.player());
|
||||
} else if (task instanceof TeamManagerTask.RemovePlayer) {
|
||||
TeamManagerTask.RemovePlayer removePlayerTask = (TeamManagerTask.RemovePlayer) task;
|
||||
for (ScoreboardTeamImpl team : teams.values()) {
|
||||
team.removePlayer(removePlayerTask.player());
|
||||
}
|
||||
|
||||
Objects.requireNonNull(scoreboardLibrary.getPlayer(removePlayerTask.player())).removeTeamManager(this);
|
||||
} else if (task instanceof TeamManagerTask.ReloadPlayer reloadPlayerTask) {
|
||||
for (var team : teams.values()) {
|
||||
var teamDisplay = team.displayMap().get(reloadPlayerTask.player());
|
||||
internalPlayers.remove(removePlayerTask.player());
|
||||
Objects.requireNonNull(scoreboardLibrary.getPlayer(removePlayerTask.player())).teamManagerQueue().remove(this);
|
||||
} else if (task instanceof TeamManagerTask.ReloadPlayer) {
|
||||
TeamManagerTask.ReloadPlayer reloadPlayerTask = (TeamManagerTask.ReloadPlayer) task;
|
||||
for (ScoreboardTeamImpl team : teams.values()) {
|
||||
TeamDisplayImpl teamDisplay = team.displayMap().get(reloadPlayerTask.player());
|
||||
if (teamDisplay != null) {
|
||||
teamDisplay.packetAdapter().updateTeam(Set.of(reloadPlayerTask.player()));
|
||||
teamDisplay.packetAdapter().sendProperties(PropertiesPacketType.UPDATE, Collections.singleton(reloadPlayerTask.player()));
|
||||
}
|
||||
}
|
||||
} else if (task instanceof TeamManagerTask.AddTeam addTeamTask) {
|
||||
var team = addTeamTask.team();
|
||||
for (var player : team.displayMap().keySet()) {
|
||||
var slPlayer = scoreboardLibrary.getPlayer(player);
|
||||
if (slPlayer != null && slPlayer.teamManager() == this) {
|
||||
} else if (task instanceof TeamManagerTask.AddTeam) {
|
||||
TeamManagerTask.AddTeam addTeamTask = (TeamManagerTask.AddTeam) task;
|
||||
ScoreboardTeamImpl team = addTeamTask.team();
|
||||
for (Player player : team.displayMap().keySet()) {
|
||||
ScoreboardLibraryPlayer slPlayer = scoreboardLibrary.getPlayer(player);
|
||||
if (slPlayer != null && slPlayer.teamManagerQueue().current() == this) {
|
||||
team.addPlayer(player);
|
||||
}
|
||||
}
|
||||
} else if (task instanceof TeamManagerTask.RemoveTeam removeTeamTask) {
|
||||
} else if (task instanceof TeamManagerTask.RemoveTeam) {
|
||||
TeamManagerTask.RemoveTeam removeTeamTask = (TeamManagerTask.RemoveTeam) task;
|
||||
List<Player> playersInTeam = CollectionProvider.list(removeTeamTask.team().displayMap().size());
|
||||
for (var entry : removeTeamTask.team().displayMap().entrySet()) {
|
||||
var player = entry.getKey();
|
||||
var teamDisplay = entry.getValue();
|
||||
for (Map.Entry<Player, TeamDisplayImpl> entry : removeTeamTask.team().displayMap().entrySet()) {
|
||||
Player player = entry.getKey();
|
||||
TeamDisplayImpl teamDisplay = entry.getValue();
|
||||
if (teamDisplay.players().contains(player)) {
|
||||
playersInTeam.add(player);
|
||||
}
|
||||
}
|
||||
|
||||
removeTeamTask.team().packetAdapter().removeTeam(playersInTeam);
|
||||
} else if (task instanceof TeamManagerTask.UpdateTeamDisplay updateTeamDisplayTask) {
|
||||
var teamDisplay = updateTeamDisplayTask.teamDisplay();
|
||||
teamDisplay.updateTeamPackets();
|
||||
teamDisplay.packetAdapter().updateTeam(teamDisplay.players());
|
||||
} else if (task instanceof TeamManagerTask.AddEntries addEntriesTask) {
|
||||
var teamDisplay = addEntriesTask.teamDisplay();
|
||||
teamDisplay.packetAdapter().addEntries(teamDisplay.players(), addEntriesTask.entries());
|
||||
} else if (task instanceof TeamManagerTask.RemoveEntries removeEntriesTask) {
|
||||
var teamDisplay = removeEntriesTask.teamDisplay();
|
||||
teamDisplay.packetAdapter().removeEntries(teamDisplay.players(), removeEntriesTask.entries());
|
||||
} else if (task instanceof TeamManagerTask.ChangeTeamDisplayTask changeTeamDisplayTaskTask) {
|
||||
changeTeamDisplayTaskTask.team().changeTeamDisplay(changeTeamDisplayTaskTask.player(), changeTeamDisplayTaskTask.oldTeamDisplay(), changeTeamDisplayTaskTask.newTeamDisplay());
|
||||
} else if (task instanceof TeamManagerTask.UpdateTeamDisplay) {
|
||||
TeamManagerTask.UpdateTeamDisplay updateTeamDisplayTask = (TeamManagerTask.UpdateTeamDisplay) task;
|
||||
@NotNull TeamDisplayImpl teamDisplay = updateTeamDisplayTask.teamDisplay();
|
||||
teamDisplay.handleUpdateDisplay();
|
||||
} else if (task instanceof TeamManagerTask.AddEntries) {
|
||||
TeamManagerTask.AddEntries addEntriesTask = (TeamManagerTask.AddEntries) task;
|
||||
@NotNull TeamDisplayImpl teamDisplay = addEntriesTask.teamDisplay();
|
||||
teamDisplay.handleAddEntries(addEntriesTask.entries());
|
||||
} else if (task instanceof TeamManagerTask.RemoveEntries) {
|
||||
TeamManagerTask.RemoveEntries removeEntriesTask = (TeamManagerTask.RemoveEntries) task;
|
||||
@NotNull TeamDisplayImpl teamDisplay = removeEntriesTask.teamDisplay();
|
||||
teamDisplay.handleRemoveEntries(removeEntriesTask.entries());
|
||||
} else if (task instanceof TeamManagerTask.ChangeTeamDisplay) {
|
||||
TeamManagerTask.ChangeTeamDisplay changeTeamDisplayTask = (TeamManagerTask.ChangeTeamDisplay) task;
|
||||
changeTeamDisplayTask.team().changeTeamDisplay(changeTeamDisplayTask.player(), changeTeamDisplayTask.oldTeamDisplay(), changeTeamDisplayTask.newTeamDisplay());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void checkClosed() {
|
||||
|
||||
+8
-5
@@ -1,10 +1,14 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.team;
|
||||
|
||||
import java.util.Collection;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public sealed class TeamManagerTask {
|
||||
import java.util.Collection;
|
||||
|
||||
public class TeamManagerTask {
|
||||
private TeamManagerTask() {
|
||||
}
|
||||
|
||||
public static final class Close extends TeamManagerTask {
|
||||
public static final Close INSTANCE = new Close();
|
||||
|
||||
@@ -48,7 +52,6 @@ public sealed class TeamManagerTask {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static final class AddTeam extends TeamManagerTask {
|
||||
private final ScoreboardTeamImpl team;
|
||||
|
||||
@@ -121,13 +124,13 @@ public sealed class TeamManagerTask {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ChangeTeamDisplayTask extends TeamManagerTask {
|
||||
public static final class ChangeTeamDisplay extends TeamManagerTask {
|
||||
private final Player player;
|
||||
private final ScoreboardTeamImpl team;
|
||||
private final TeamDisplayImpl oldTeamDisplay;
|
||||
private final TeamDisplayImpl newTeamDisplay;
|
||||
|
||||
public ChangeTeamDisplayTask(@NotNull Player player, @NotNull ScoreboardTeamImpl team, @NotNull TeamDisplayImpl oldTeamDisplay, @NotNull TeamDisplayImpl newTeamDisplay) {
|
||||
public ChangeTeamDisplay(@NotNull Player player, @NotNull ScoreboardTeamImpl team, @NotNull TeamDisplayImpl oldTeamDisplay, @NotNull TeamDisplayImpl newTeamDisplay) {
|
||||
this.player = player;
|
||||
this.team = team;
|
||||
this.oldTeamDisplay = oldTeamDisplay;
|
||||
|
||||
+22
-7
@@ -1,17 +1,24 @@
|
||||
package net.megavex.scoreboardlibrary.implementation.team;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import net.megavex.scoreboardlibrary.implementation.ScoreboardLibraryImpl;
|
||||
import org.bukkit.scheduler.BukkitRunnable;
|
||||
import net.megavex.scoreboardlibrary.implementation.scheduler.RunningTask;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class TeamUpdaterTask extends BukkitRunnable {
|
||||
import java.util.Iterator;
|
||||
import java.util.logging.Level;
|
||||
|
||||
public class TeamUpdaterTask implements Runnable {
|
||||
private final ScoreboardLibraryImpl scoreboardLibrary;
|
||||
private final RunningTask task;
|
||||
private final Object lock = new Object();
|
||||
|
||||
public TeamUpdaterTask(@NotNull ScoreboardLibraryImpl scoreboardLibrary) {
|
||||
this.scoreboardLibrary = scoreboardLibrary;
|
||||
runTaskTimerAsynchronously(scoreboardLibrary.plugin(), 1, 1);
|
||||
this.task = scoreboardLibrary.taskScheduler().runEveryTick(this);
|
||||
}
|
||||
|
||||
public @NotNull RunningTask task() {
|
||||
return task;
|
||||
}
|
||||
|
||||
public @NotNull Object lock() {
|
||||
@@ -21,11 +28,19 @@ public class TeamUpdaterTask extends BukkitRunnable {
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (lock) {
|
||||
for (var teamManager : scoreboardLibrary.teamManagers()) {
|
||||
Iterator<TeamManagerImpl> iterator = scoreboardLibrary.teamManagers().iterator();
|
||||
while (iterator.hasNext()) {
|
||||
TeamManagerImpl teamManager = iterator.next();
|
||||
boolean result;
|
||||
try {
|
||||
teamManager.tick();
|
||||
result = teamManager.tick();
|
||||
} catch (Exception e) {
|
||||
scoreboardLibrary.plugin().getLogger().log(Level.SEVERE, "an error occurred while updating a TeamManager instance", e);
|
||||
scoreboardLibrary.plugin().getLogger().log(Level.WARNING, "an error occurred while updating a TeamManager instance", e);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
jdk:
|
||||
- openjdk17
|
||||
|
||||
install:
|
||||
- "bash gradlew build publishToMavenLocal"
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
|
||||
"extends": [
|
||||
"config:recommended"
|
||||
],
|
||||
"ignoreDeps": [
|
||||
"org.spigotmc:spigot-api"
|
||||
]
|
||||
}
|
||||
+7
-3
@@ -3,7 +3,7 @@ rootProject.name = "scoreboard-library"
|
||||
include(":api")
|
||||
include(":commons")
|
||||
include(":implementation")
|
||||
include("extra-kotlin")
|
||||
include(":extra-kotlin")
|
||||
|
||||
include(":packet-adapter-base")
|
||||
project(":packet-adapter-base").projectDir = file("versions/packet-adapter-base")
|
||||
@@ -11,9 +11,13 @@ project(":packet-adapter-base").projectDir = file("versions/packet-adapter-base"
|
||||
include(":packetevents")
|
||||
project(":packetevents").projectDir = file("versions/packetevents")
|
||||
|
||||
include(":v1_19_R2")
|
||||
project(":v1_19_R2").projectDir = file("versions/v1_19_R2")
|
||||
include(":modern")
|
||||
project(":modern").projectDir = file("versions/modern")
|
||||
|
||||
include(":legacy")
|
||||
project(":legacy").projectDir = file("versions/legacy")
|
||||
|
||||
// For backwards compatibility
|
||||
include(":v1_8_R3")
|
||||
project(":v1_8_R3").projectDir = file("versions/v1_8_R3")
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
plugins {
|
||||
id("net.megavex.scoreboardlibrary.base-conventions")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(":scoreboard-library-packet-adapter-base"))
|
||||
compileOnly(libs.spigotApi)
|
||||
}
|
||||
+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);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loaded 100 of 166 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user