Compare commits

...
153 Commits
Author SHA1 Message Date
vytskalt 48351b6c81 release: 2.3.2 2025-06-23 01:11:13 +03:00
vytskalt 7dcf1e30eb add 1.21.6 support 2025-06-23 01:06:20 +03:00
vytskalt 2ca874346d release: 2.3.1 2025-05-04 19:29:04 +03:00
vytskalt cb08e5d3d9 Merge remote-tracking branch 'origin/main' 2025-04-26 23:53:16 +03:00
vytskalt eecde934a8 setup snapshot 2025-04-26 23:52:50 +03:00
vytskalt 2a070cdc3d remove transitive dependency on spigot-api 2025-04-26 23:52:11 +03:00
vytskalt 589ab095b2 [ci skip] Update INSTALLATION.md 2025-04-21 19:52:18 +03:00
vytskalt 41fb8aa4aa Update INSTALLATION.md 2025-04-21 19:43:22 +03:00
vytskalt da69391930 release: 2.3.0 2025-04-12 19:31:35 +03:00
vytskalt 47ef9a8143 update junit platform launcher 2025-04-12 19:27:25 +03:00
vytskalt ccf9aad96d update dependencies 2025-04-12 19:23:17 +03:00
vytskalt facb3a4d1c allow getting/setting sidebar objective name 2025-04-03 21:42:29 +03:00
vytskalt f41c1ce0b1 1.21.5 2025-03-27 19:59:49 +02:00
vytskalt e72d442f9b upgrade to gradle 8.13 2025-03-27 18:44:23 +02:00
vytskalt 9ef53fc36c release: 2.2.2 2024-12-08 19:58:38 +02:00
vytskalt 774ab670c9 1.21.4 2024-12-08 19:56:08 +02:00
vytskalt 30641a8c15 release: 2.2.1 2024-11-01 19:36:11 +02:00
vytskalt 01e1374db6 feat: add support for 1.21.2/1.21.3 2024-11-01 19:35:38 +02:00
vytskalt db58aa35c5 [ci skip] update maven docs 2024-10-13 17:45:21 +03:00
vytskalt 35a0565cd1 release 2.2.0 2024-10-13 17:11:05 +03:00
vytskalt 6fab408824 [ci skip] update docs for legacy adapter 2024-10-13 16:59:34 +03:00
vytskalt b0d6903262 fix removing score on packetevents 2024-10-13 16:35:14 +03:00
vytskalt acd64b884e update deps 2024-10-13 15:54:59 +03:00
vytskalt 70eb07c25d temp packetevents fix 2024-10-13 15:49:43 +03:00
vytskalt 50d567a4af fix 1.13+ client on 1.12.2- server sidebar line styling bug 2024-10-13 15:26:35 +03:00
vytskalt 97cd00b598 Merge pull request #59 from Nixuge/main
Legacy Packet Adapter
2024-10-13 14:55:07 +03:00
vytskalt bf0529a11e [ci skip] undo docs changes 2024-10-13 14:53:54 +03:00
vytskalt 1b752a0694 add safety check to packetevents legacy teams serializer 2024-10-13 14:46:54 +03:00
vytskalt 5f093db220 support collision rule for 1.9+ 2024-10-11 19:15:34 +03:00
vytskalt 651f5b24b7 fix 1.7.10 & 1.8.8 2024-10-11 18:50:41 +03:00
vytskalt e35fb3503c more cleanup 2024-10-11 16:03:02 +03:00
vytskalt 14e16b95a0 readd modeerrn 2024-10-11 11:48:45 +03:00
vytskalt b30ea7dc44 cleanup 2024-10-11 10:35:22 +03:00
vytskalt 8e09c6d1c1 ඞ 2024-10-08 23:25:33 +03:00
vytskalt 8aa61448e4 capital statics 2024-10-08 13:03:29 +03:00
vytskalt 3762419bc5 what the sigma 2024-10-08 13:00:11 +03:00
vytskalt 53bea167d7 docs and loader 2024-10-08 10:01:09 +03:00
vytskalt 5eaa7f3c07 legacyreflections -> legacy 2024-10-08 09:49:08 +03:00
vytskalt 699cbacf6e yeet 1.8 adapter 2024-10-08 09:44:18 +03:00
vytskalt e6fdd5e513 yeet tests 2024-10-08 09:41:45 +03:00
vytskalt a95c879d10 Merge remote-tracking branch 'upstream/main' 2024-10-08 09:18:18 +03:00
vytskalt 4f6d1dc5b1 fix: possible concurrent modification error in team manager 2024-09-09 20:05:08 +03:00
Nixuge 5c0709a8c4 [UNTESTED] Cached more reflections fields 2024-09-02 17:58:54 +02:00
Nixuge d5c61dc156 1.7 support, now 1.7 - 1.12.2, automatically grabbing NMS version 2024-08-25 20:12:30 +02:00
Nixuge 5466140dc4 Removed sneaky debug print i left there 2024-08-24 20:30:03 +02:00
Nixuge cc13241968 Added 1.7.10 case to PacketAdapterLoader using new LEGACY_REFLECTIONS 2024-08-24 20:28:01 +02:00
Nixuge 16592c2815 Fixed wrong thing I wrongfully copied 2024-08-24 20:00:03 +02:00
Nixuge 8c09c0e97c 1.8 folder back to actually 1.8 2024-08-24 19:08:24 +02:00
Nixuge 0bbc487f88 Merge branch 'MegavexNetwork:main' into main 2024-08-24 19:05:14 +02:00
Nixuge 0cbd54f8f1 Moved actual legacy reflections implementation to legacyreflections 2024-08-24 19:04:50 +02:00
Nixuge 0551a8f978 Base fully working with reflections 2024-08-24 18:42:40 +02:00
Nixuge dd65c812d2 Started removing v1_8_R3 imports in favor of reflections 2024-08-24 17:38:20 +02:00
vytskalt a88394223b fix: invalid packets in PacketEvents on 1.13+ servers with 1.12.2- clients and 1.12.2- servers with 1.13+ clients 2024-08-22 17:36:20 +03:00
vytskalt 0e579e603a release: 2.1.12 2024-08-14 21:03:18 +03:00
vytskalt 71aaa7ac50 1.21.1 2024-08-14 21:01:17 +03:00
vytskalt 8521d1deec ci: add caching for gradle 2024-08-10 11:57:23 +03:00
vytskalt 9c1a121e09 chore: make builds as reproducible as possible 2024-08-10 11:43:36 +03:00
vytskalt 7f3651d4bf chore: bump dependencies 2024-08-06 16:17:26 +03:00
vytskalt 2e06fc1d34 don't pass mutable entries lists to packets
fixes some invalid packets being sent
2024-08-06 16:11:33 +03:00
vytskalt cb391dd676 fix: sending create team packets with entries not yet synced 2024-08-06 13:45:46 +03:00
vytskalt a36a8c3fd2 use snapshot version 2024-08-05 23:53:37 +03:00
vytskalt 4f3cf55c01 fix ci failing due to maven repo being down 2024-07-12 15:14:06 +03:00
vytskalt 422a36a284 release: 2.1.11 2024-07-12 15:01:03 +03:00
vytskalt a2e5df14e6 fix possible CraftRegistry ClassNotFoundException 2024-07-04 22:57:59 +03:00
vytskalt 521bc47dd0 fix finding wrong fromJson method 2024-06-24 10:02:05 +03:00
vytskalt 09dc3a653d [ci skip] docs: mention 1.21 as supported 2024-06-16 18:09:03 +03:00
vytskalt b5181d0d81 [ci skip] add snapshots repo 2024-06-16 18:08:38 +03:00
vytskalt f01f4fd831 use thread safe collection for team entries 2024-06-16 18:03:26 +03:00
vytskalt 778b479b91 release: 2.1.10 2024-06-16 12:50:45 +03:00
vytskalt 7a1c47045b 1.21 2024-06-15 13:47:25 +03:00
vytskalt e2c8ff5eb8 release: 2.1.9 2024-05-20 16:36:42 +03:00
vytskalt af426bdfbc [ci skip] fix oopsie 2024-05-20 16:24:20 +03:00
vytskalt 641223e1f5 [ci skip] some docs improvements 2024-05-17 23:51:58 +03:00
vytskalt cf8fa59c86 fix adapter loading on customized bukkit version 2024-05-14 12:27:41 +03:00
vytskalt 96595d0a41 Merge pull request #40 from MegavexNetwork/renovate/com.github.retrooper.packetevents-spigot-2.x
fix(deps): update dependency com.github.retrooper.packetevents:spigot to v2.3.0
2024-05-12 21:02:20 +03:00
vytskalt ba6bff7ed1 Merge pull request #37 from MegavexNetwork/renovate/org.jetbrains.kotlin.jvm-1.x
chore(deps): update plugin kotlin to v1.9.24
2024-05-12 21:02:08 +03:00
vytskalt 0f96547b37 Merge pull request #38 from MegavexNetwork/renovate/adventure
fix(deps): update adventure to v4.17.0
2024-05-12 21:01:46 +03:00
renovate[bot] 5d02fd625b chore(deps): update plugin kotlin to v1.9.24 2024-05-12 18:01:44 +00:00
vytskalt fed9f4c64b Merge pull request #36 from MegavexNetwork/renovate/io.papermc.paperweight.userdev-1.x
chore(deps): update plugin paperweight to v1.7.1
2024-05-12 21:01:20 +03:00
renovate[bot] 0c3f942757 chore(deps): update plugin paperweight to v1.7.1 2024-05-11 21:24:49 +00:00
renovate[bot] c1c9d7258b fix(deps): update dependency com.github.retrooper.packetevents:spigot to v2.3.0 2024-05-09 03:46:27 +00:00
renovate[bot] 93cd55a626 fix(deps): update adventure to v4.17.0 2024-05-08 07:21:33 +00:00
vytskalt 0d979d6922 Mention 1.20.6 as supported in README 2024-05-01 15:07:01 +03:00
vytskalt cf6b51be40 Merge remote-tracking branch 'origin/main' 2024-04-30 17:37:28 +03:00
vytskalt c1199432eb Merge pull request #33 from MegavexNetwork/renovate/io.papermc.paperweight.userdev-1.x
chore(deps): update plugin paperweight to v1.6.3
2024-04-30 17:06:38 +03:00
vytskalt bccc777541 Merge pull request #32 from MegavexNetwork/renovate/gradle-gradle-build-action-3.x
chore(deps): update gradle/gradle-build-action action to v3.3.2
2024-04-30 17:06:28 +03:00
vytskalt d14ca9470a release 2.1.8 2024-04-30 17:02:15 +03:00
vytskalt 1889b84373 mark 1.20.6 as supported & add forceModern system property 2024-04-30 16:54:30 +03:00
renovate[bot] bf629bd7de chore(deps): update plugin paperweight to v1.6.3 2024-04-30 00:17:36 +00:00
vytskalt 690905774e release 2.1.7 2024-04-27 22:54:17 +03:00
renovate[bot] 40a35272ff chore(deps): update gradle/gradle-build-action action to v3.3.2 2024-04-27 19:48:44 +00:00
vytskalt f9fdc13443 Merge pull request #35 from MegavexNetwork/dev/1.20.5
Add support for 1.20.5
2024-04-27 22:48:19 +03:00
vytskalt bdce2423b1 finish up 2024-04-27 22:47:56 +03:00
vytskalt 4955c839a8 fix weird jvm issues 2024-04-27 22:25:54 +03:00
vytskalt 9b198da189 add mojmap artifact 2024-04-27 20:32:37 +03:00
vytskalt 32409d589c add backwards compat for older versions 2024-04-27 19:56:13 +03:00
vytskalt e016b43822 get it to work on 1.20.5 2024-04-27 14:32:45 +03:00
vytskalt 17137a88c4 update ci 2024-04-27 12:20:13 +03:00
vytskalt 4369acd756 get it to compile 2024-04-27 12:14:49 +03:00
vytskalt 4fa40ed032 [ci skip] remove jitpack.yml
it no longer works due to indra
2024-04-09 22:47:54 +03:00
vytskalt 3bf91896b0 attempt to fix jitpack build 2024-04-09 22:42:14 +03:00
vytskalt b71d555391 release: 2.1.6 2024-04-09 22:35:32 +03:00
vytskalt 25f236344f add missing null check for sidebar line 2024-04-09 22:33:33 +03:00
vytskalt 01d8ef9b29 update installation 2024-04-06 14:20:22 +03:00
vytskalt 98917376d0 release: 2.1.5 2024-04-06 14:20:22 +03:00
vytskalt cd0607a72c build: add nmcp plugin 2024-04-06 14:20:22 +03:00
vytskalt 8ba9ef40da that's the wrong url 2024-04-06 14:20:21 +03:00
vytskalt 3be7b09631 fix modern adapter build script 2024-04-06 14:19:16 +03:00
vytskalt 079e867325 Merge pull request #30 from MegavexNetwork/renovate/gradle-gradle-build-action-3.x
chore(deps): update gradle/gradle-build-action action to v3.2.0
2024-04-06 00:31:22 +03:00
vytskalt c9a3c6b3f5 Merge pull request #29 from MegavexNetwork/renovate/io.papermc.paperweight.userdev-1.x
chore(deps): update plugin paperweight to v1.5.13
2024-04-06 00:31:12 +03:00
vytskalt f8f542e739 Merge pull request #31 from MegavexNetwork/indra-publishing
Add indra publishing plugin
2024-04-06 00:31:00 +03:00
vytskalt fca17dbda4 get it to work 2024-04-06 00:30:20 +03:00
renovate[bot] 9a77454a9e chore(deps): update gradle/gradle-build-action action to v3.2.0 2024-04-04 23:35:39 +00:00
renovate[bot] 2d30af76f3 chore(deps): update plugin paperweight to v1.5.13 2024-04-04 23:35:35 +00:00
vytskalt 59198fb117 attempt to add indra-publishing 2024-04-04 21:24:55 +03:00
vytskalt 022968489c build: use indra 2024-04-04 20:19:51 +03:00
vytskalt afd64f4fd2 release: prepare for development on 2.1.5 2024-04-04 20:19:21 +03:00
vytskalt d4c553f93f release: 2.1.4 2024-04-02 20:26:07 +03:00
vytskalt 3f37335bac fix: rare bug in rendering sidebar scores 2024-04-02 20:02:10 +03:00
vytskalt deaf231965 [ci skip] docs: fix outdated comment 2024-04-02 16:55:23 +03:00
vytskalt d15ac8c2e8 move dependencies to libs.versions.toml 2024-03-28 19:01:50 +02:00
vytskalt 8408a3e279 move shared build logic to buildSrc 2024-03-26 20:57:28 +02:00
vytskalt c2ce803f30 Merge remote-tracking branch 'origin/main' 2024-03-26 19:52:04 +02:00
vytskalt 5f89a47798 slightly refactor sidebar line logic, fixes several instances of useless packets being sent & a rare NPE 2024-03-26 19:51:45 +02:00
vytskalt 4d23bc5cfd Update README.md 2024-03-23 18:50:46 +02:00
vytskalt d26eab2110 Merge pull request #27 from MegavexNetwork/renovate/gradle-8.x
Update dependency gradle to v8.7
2024-03-22 22:09:04 +02:00
renovate[bot] c0a6ec0ec1 Update dependency gradle to v8.7 2024-03-22 19:47:38 +00:00
vytskalt 20245b870f release 2.1.3 2024-03-16 11:54:26 +02:00
vytskalt 715c28732e slight build changes 2024-03-16 11:39:38 +02:00
vytskalt 408a379bfe fix incompatibility with old paper versions 2024-03-16 11:35:58 +02:00
vytskalt caeb7286a8 fix javadocs 2024-03-16 11:35:58 +02:00
vytskalt 0452126353 don't import Unsafe directly 2024-03-16 11:35:58 +02:00
vytskalt d8d6ba3a11 Merge pull request #20 from MegavexNetwork/renovate/org.jetbrains.kotlin.jvm-1.x
Update plugin org.jetbrains.kotlin.jvm to v1.9.23
2024-03-10 16:10:24 +02:00
vytskalt 30b52722cd Merge pull request #21 from MegavexNetwork/renovate/actions-checkout-4.x
Update actions/checkout action to v4
2024-03-10 16:10:14 +02:00
vytskalt 4ad96a9a79 Merge pull request #22 from MegavexNetwork/renovate/actions-setup-java-4.x
Update actions/setup-java action to v4
2024-03-10 16:10:03 +02:00
renovate[bot] 5524dd399f Update actions/checkout action to v4 2024-03-10 14:09:54 +00:00
vytskalt 17ba876a7a Merge pull request #23 from MegavexNetwork/renovate/gradle-gradle-build-action-3.x
Update gradle/gradle-build-action action to v3
2024-03-10 16:09:49 +02:00
vytskalt 4e7b21f0ca Merge pull request #24 from MegavexNetwork/renovate/gradle-wrapper-validation-action-2.x
Update gradle/wrapper-validation-action action to v2
2024-03-10 16:09:34 +02:00
renovate[bot] 25df9e7262 Update gradle/wrapper-validation-action action to v2 2024-03-10 14:02:55 +00:00
renovate[bot] d547387245 Update gradle/gradle-build-action action to v3 2024-03-10 14:02:51 +00:00
renovate[bot] 9fc2c23a9d Update actions/setup-java action to v4 2024-03-10 14:02:47 +00:00
renovate[bot] 2f07a0e68d Update plugin org.jetbrains.kotlin.jvm to v1.9.23 2024-03-10 14:02:39 +00:00
vytskalt 78442b652c Merge pull request #19 from MegavexNetwork/renovate/junit5-monorepo
Update junit5 monorepo to v5.10.2
2024-03-10 16:01:12 +02:00
vytskalt 57e8470cc7 Merge pull request #18 from MegavexNetwork/renovate/gradle-gradle-build-action-2.x
Update gradle/gradle-build-action action to v2.12.0
2024-03-10 16:00:54 +02:00
renovate[bot] ee16d324e1 Update junit5 monorepo to v5.10.2 2024-03-10 14:00:27 +00:00
renovate[bot] b0ec7d2d97 Update gradle/gradle-build-action action to v2.12.0 2024-03-10 14:00:22 +00:00
vytskalt 1c4ec63eea Merge pull request #16 from MegavexNetwork/renovate/gradle-8.x
Update dependency gradle to v8.6
2024-03-10 15:59:59 +02:00
renovate[bot] 8ec3e1082d Update dependency gradle to v8.6 2024-03-10 13:59:27 +00:00
vytskalt bdbbf155e7 Merge pull request #15 from MegavexNetwork/renovate/adventureversion
Update adventureVersion to v4.16.0
2024-03-10 15:55:47 +02:00
renovate[bot] 40c7fe49be Update adventureVersion to v4.16.0 2024-03-10 13:54:28 +00:00
vytskalt 3a0b0c1d94 Merge pull request #14 from MegavexNetwork/renovate/configure
Configure Renovate
2024-03-10 15:54:03 +02:00
vytskalt 226f39ee27 Update renovate.json 2024-03-10 15:52:17 +02:00
renovate[bot] 8c00d11070 Add renovate.json 2024-03-10 13:47:25 +00:00
89 changed files with 1601 additions and 861 deletions

No files matched your search

+16 -16
View File
@@ -1,24 +1,24 @@
name: CI
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
workflow_dispatch:
push:
branches:
- main
jobs:
build:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: gradle/wrapper-validation-action@v1
- name: Set up JDK 8
uses: actions/setup-java@v3
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v31
- name: Cache Gradle
uses: actions/cache@v4
with:
java-version: 17
distribution: 'temurin'
- name: Build the project
uses: gradle/gradle-build-action@v2.4.2
with:
arguments: build
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*') }}
restore-keys: |
${{ runner.os }}-gradle-
- name: ./gradlew build
run: nix develop -i -c sh -c "./gradlew build"
+6 -1
View File
@@ -2,4 +2,9 @@
*.iml
.gradle
build/
build/
.classpath
.project
.settings/
bin/
+24 -39
View File
@@ -1,30 +1,25 @@
# Installation
Latest version: `2.1.2`
Latest version: `2.3.2`
## Gradle
Make sure you have the Jitpack repository:
```kotlin
repositories {
maven("https://jitpack.io/")
mavenCentral()
}
```
Then add the dependencies:
```kotlin
dependencies {
val scoreboardLibraryVersion = "{VERSION HERE}"
implementation("com.github.megavexnetwork.scoreboard-library:scoreboard-library-api:$scoreboardLibraryVersion")
runtimeOnly("com.github.megavexnetwork.scoreboard-library:scoreboard-library-implementation:$scoreboardLibraryVersion")
implementation("com.github.megavexnetwork.scoreboard-library:scoreboard-library-extra-kotlin:$scoreboardLibraryVersion") // If using Kotlin
implementation("net.megavex:scoreboard-library-api:$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-modern:$scoreboardLibraryVersion") // 1.17+
runtimeOnly("com.github.megavexnetwork.scoreboard-library:scoreboard-library-packetevents:$scoreboardLibraryVersion") // 1.8+
runtimeOnly("com.github.megavexnetwork.scoreboard-library:scoreboard-library-v1_8_R3:$scoreboardLibraryVersion") // 1.8
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:
@@ -33,63 +28,54 @@ dependencies {
// 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
Make sure you have the Jitpack repository:
```xml
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io/</url>
</repository>
</repositories>
```
Then add the dependencies:
```xml
<dependencies>
<dependency>
<groupId>com.github.megavexnetwork.scoreboard-library</groupId>
<groupId>net.megavex</groupId>
<artifactId>scoreboard-library-api</artifactId>
<version>{VERSION HERE}</version>
</dependency>
<dependency>
<groupId>com.github.megavexnetwork.scoreboard-library</groupId>
<groupId>net.megavex</groupId>
<artifactId>scoreboard-library-implementation</artifactId>
<version>{VERSION HERE}</version>
<scope>runtime</scope>
</dependency>
<!-- If using Kotlin: -->
<!-- Kotlin specific extensions (optional) -->
<dependency>
<groupId>com.github.megavexnetwork.scoreboard-library</groupId>
<groupId>net.megavex</groupId>
<artifactId>scoreboard-library-extra-kotlin</artifactId>
<version>{VERSION HERE}</version>
</dependency>
<!-- Add packet adapter implementations you want: -->
<dependency>
<groupId>com.github.megavexnetwork.scoreboard-library</groupId>
<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>com.github.megavexnetwork.scoreboard-library</groupId>
<groupId>net.megavex</groupId>
<artifactId>scoreboard-library-packetevents</artifactId>
<version>{VERSION HERE}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.github.megavexnetwork.scoreboard-library</groupId>
<artifactId>scoreboard-library-v1_8_R3</artifactId>
<groupId>net.megavex</groupId>
<artifactId>scoreboard-library-legacy</artifactId>
<version>{VERSION HERE}</version>
<scope>runtime</scope>
</dependency>
@@ -103,7 +89,7 @@ Then add the dependencies:
https://github.com/retrooper/packetevents-example/blob/24f0c842d47362aef122b794dea29b8fee113fa3/thread-safe-listener/src/main/java/main/Main.java
-->
<!-- If using the 1.8 version implementation, add Adventure as well: -->
<!-- If targeting a Minecraft version without native Adventure support, add it as well: -->
<dependency>
<groupId>net.kyori</groupId>
<artifactId>adventure-platform-bukkit</artifactId>
@@ -112,5 +98,4 @@ Then add the dependencies:
</dependencies>
```
You will need to shade these dependencies and relocate them with something
like [maven-shade-plugin](https://maven.apache.org/plugins/maven-shade-plugin/).
You will need to shade these dependencies and relocate them with [maven-shade-plugin](https://maven.apache.org/plugins/maven-shade-plugin/).
+7 -10
View File
@@ -8,7 +8,7 @@ Join the [Discord](https://discord.gg/v7nmTDTW8W) or create an issue for support
## Features
- Sidebars. Up to 42 characters (depends on the formatting) for 1.12.2 and below, no limit for newer versions
- Sidebars. Up to 42 characters (depends on the formatting) for 1.12.2-, no limit for newer versions
- Teams. Supports showing different properties (display name, prefix, entries etc.) of the same team to different
players
- Objectives.
@@ -23,9 +23,9 @@ Join the [Discord](https://discord.gg/v7nmTDTW8W) or create an issue for support
## Available Packet Adapters
- **modern.** Supports 1.17-1.20.4. Can take advantage of [Paper](https://papermc.io)'s native adventure support to be more efficient.
- **PacketEvents.** Supports 1.8+. Requires [PacketEvents 2.0](https://github.com/retrooper/packetevents/tree/2.0) to be shaded or installed as a plugin. Can be less stable than ProtocolLib.
- **1.8.8.**
- **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.
@@ -69,8 +69,7 @@ sidebar.line(3, Component.text("epicserver.net"));
sidebar.addPlayer(player); // Add the player to the sidebar
// After you've finished using the Sidebar, make sure to close it to prevent a memory leak:
sidebar.close();
// Don't forget to call sidebar.close() once you don't need it anymore!
```
### Component sidebars
@@ -265,8 +264,7 @@ newTeamDisplay.displayName(Component.text("Other Team Display Name"));
// Change the TeamDisplay a player sees like this:
team.display(player, newTeamDisplay);
// After you've finished using the TeamManager, make sure to close it to prevent a memory leak:
teamManager.close();
// Don't forget to call teamManager.close() once you don't need it anymore!
```
### ObjectiveManager
@@ -280,6 +278,5 @@ objectiveManager.display(ObjectiveDisplaySlot.belowName(), objective);
objectiveManager.addPlayer(player); // Make a player see the objectives
// After you've finished using the ObjectiveManager, make sure to close it to prevent a memory leak:
objectiveManager.close();
// Don't forget to call objectiveManager.close() once you don't need it anymore!
```
+6 -1
View File
@@ -1,3 +1,8 @@
plugins {
id("net.megavex.scoreboardlibrary.publish-conventions")
id("net.megavex.scoreboardlibrary.base-conventions")
}
dependencies {
compileOnlyApi(libs.bundles.adventure)
compileOnly(libs.spigotApi)
}
@@ -5,6 +5,7 @@ import net.megavex.scoreboardlibrary.api.noop.NoopScoreboardLibrary;
import net.megavex.scoreboardlibrary.api.objective.ObjectiveManager;
import net.megavex.scoreboardlibrary.api.sidebar.Sidebar;
import net.megavex.scoreboardlibrary.api.team.TeamManager;
import org.apache.commons.lang.RandomStringUtils;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.ApiStatus;
@@ -56,7 +57,7 @@ public interface ScoreboardLibrary {
* @return newly created sidebar
*/
default @NotNull Sidebar createSidebar() {
return createSidebar(Sidebar.MAX_LINES, null);
return createSidebar(Sidebar.MAX_LINES);
}
/**
@@ -79,7 +80,21 @@ public interface ScoreboardLibrary {
* or null if the locale should depend on the player
* @return newly created sidebar
*/
@NotNull Sidebar createSidebar(@Range(from = 1, to = Integer.MAX_VALUE) int maxLines, @Nullable Locale locale);
default @NotNull Sidebar createSidebar(@Range(from = 1, to = Integer.MAX_VALUE) int maxLines, @Nullable Locale locale) {
return createSidebar(maxLines, locale, RandomStringUtils.randomAlphanumeric(16));
}
/**
* Creates a new {@link Sidebar}.
*
* @param maxLines max amount of lines the sidebar will have.
* Note that vanilla clients can only display at most {@value Sidebar#MAX_LINES}
* @param locale locale which will be used for translating {@link net.kyori.adventure.text.TranslatableComponent}s,
* or null if the locale should depend on the player
* @param objectiveName objective name the sidebar should use
* @return newly created sidebar
*/
@NotNull Sidebar createSidebar(@Range(from = 1, to = Integer.MAX_VALUE) int maxLines, @Nullable Locale locale, @NotNull String objectiveName);
/**
* Creates a new {@link TeamManager}.
@@ -19,9 +19,9 @@ public final class NoopScoreboardLibrary implements ScoreboardLibrary {
private boolean closed = false;
@Override
public @NotNull Sidebar createSidebar(@Range(from = 1, to = Sidebar.MAX_LINES) int maxLines, @Nullable Locale locale) {
public @NotNull Sidebar createSidebar(@Range(from = 1, to = Integer.MAX_VALUE) int maxLines, @Nullable Locale locale, @NotNull String objectiveName) {
checkClosed();
return new NoopSidebar(maxLines, locale);
return new NoopSidebar(maxLines, objectiveName, locale);
}
@Override
@@ -17,14 +17,16 @@ import static net.kyori.adventure.text.Component.empty;
class NoopSidebar implements Sidebar {
private final Set<Player> players = new HashSet<>();
private final int maxLines;
private final String objectiveName;
private final Locale locale;
private final Component[] lines;
private Component title = empty();
private boolean closed;
NoopSidebar(int maxLines, @Nullable Locale locale) {
NoopSidebar(int maxLines, String objectiveName, @Nullable Locale locale) {
this.maxLines = maxLines;
this.locale = locale;
this.objectiveName = objectiveName;
this.lines = new Component[maxLines];
}
@@ -62,6 +64,11 @@ class NoopSidebar implements Sidebar {
return maxLines;
}
@Override
public @NotNull String objectiveName() {
return objectiveName;
}
@Override
public @Nullable Locale locale() {
return locale;
@@ -24,6 +24,9 @@ public interface ObjectiveDisplaySlot {
return BelowName.INSTANCE;
}
/**
* @since Minecraft 1.8
*/
static @NotNull TeamSidebar teamSidebar(@NotNull NamedTextColor teamColor) {
Preconditions.checkNotNull(teamColor);
return new TeamSidebar(teamColor);
@@ -62,7 +62,7 @@ public interface ScoreboardObjective {
*
* @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).value()} and {@link ObjectiveScore#value()} instead
* @deprecated use {@link #scoreInfo(String)} and {@link ObjectiveScore#value()} instead
*/
@Deprecated
default @Nullable Integer score(@NotNull String entry) {
@@ -33,6 +33,11 @@ public interface Sidebar {
*/
@Range(from = 1, to = Integer.MAX_VALUE) int maxLines();
/**
* @return objective name used by this sidebar
*/
@NotNull String objectiveName();
/**
* @return locale which is used for translating {@link net.kyori.adventure.text.TranslatableComponent}s,
* or null if it depends on each player's client locale.
@@ -43,7 +48,7 @@ public interface Sidebar {
// Lines
/**
* Gets a line's value.
* Gets a line's value, or null if line isn't set.
*
* @param line line index
* @return value of line, or null if unset
@@ -52,6 +57,7 @@ public interface Sidebar {
/**
* Sets a line's value.
* A null value hides the line.
*
* @param index line index
* @param value new value, or null to hide
@@ -62,6 +68,7 @@ public interface Sidebar {
/**
* Sets a line's value with a custom score format.
* A null value hides the line.
* Note that custom score formats are only supported in Minecraft 1.20.3+.
*
* @param index line index
@@ -32,6 +32,12 @@ public final class ComponentSidebarLayout {
return linesComponent;
}
/**
* Applies the title and line components to a sidebar.
* Call this every time you want to update the sidebar.
*
* @param sidebar sidebar to apply title and line components to
*/
public void apply(@NotNull Sidebar sidebar) {
Preconditions.checkNotNull(sidebar);
@@ -7,12 +7,12 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* Something lines can be drawn to. This is either the lines of a sidebar or the sidebar title.
* Something lines can be drawn to line by line. This is either the lines of a sidebar or its title.
*/
@ApiStatus.NonExtendable
public interface LineDrawable {
/**
* Draws a line, or does nothing if it has reached the limit.
* Draws the next line, or does nothing if it has reached a limit.
*
* @param line line component
*/
@@ -21,7 +21,7 @@ public interface LineDrawable {
}
/**
* Draws a line with a custom score format, or does nothing if it has reached the limit.
* Draws the next line with a custom score format, or does nothing if it has reached a limit.
*
* @param line line component
* @param scoreFormat score format
@@ -22,7 +22,7 @@ public interface FramedSidebarAnimation<T> extends SidebarAnimation<T> {
int currentFrameIndex();
/**
* Switches the current frame
* Switches the current frame to specified index
*
* @param frameIndex the index of the new current frame
* @throws ArrayIndexOutOfBoundsException if frame is out of bounds
@@ -54,7 +54,7 @@ public interface TeamDisplay {
// Properties
/**
* @return display name, defaults to {@link Component#empty()
* @return display name, defaults to {@link Component#empty()}
*/
@NotNull Component displayName();
@@ -69,7 +69,7 @@ public interface TeamDisplay {
* 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()
* @return prefix, defaults to {@link Component#empty()}
*/
@NotNull Component prefix();
@@ -127,6 +127,7 @@ public interface TeamDisplay {
/**
* @return name tag visibility rule value
* @since Minecraft 1.8
*/
@NotNull NameTagVisibility nameTagVisibility();
@@ -134,6 +135,7 @@ public interface TeamDisplay {
* Updates the name tag visibility rule.
*
* @param nameTagVisibility new rule value
* @since Minecraft 1.8
*/
@NotNull TeamDisplay nameTagVisibility(@NotNull NameTagVisibility nameTagVisibility);
@@ -156,6 +158,7 @@ public interface TeamDisplay {
* It is also used for displaying team specific sidebars with the {@link ObjectiveDisplaySlot.TeamSidebar}} display slot.
*
* @return player color
* @since Minecraft 1.8
*/
@Nullable NamedTextColor playerColor();
@@ -164,6 +167,7 @@ public interface TeamDisplay {
*
* @param playerColor new player color
* @see #playerColor()
* @since Minecraft 1.8
*/
@NotNull TeamDisplay playerColor(@Nullable NamedTextColor playerColor);
}
+1 -42
View File
@@ -1,46 +1,5 @@
plugins {
`java-library`
}
allprojects {
version = "2.1.2"
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://repo.papermc.io/repository/maven-public/")
}
}
subprojects {
apply(plugin = "java-library")
dependencies {
compileOnly("org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT")
val adventureVersion = "4.14.0"
compileOnly("net.kyori:adventure-api:$adventureVersion")
compileOnly("net.kyori:adventure-text-serializer-legacy:$adventureVersion")
compileOnly("net.kyori:adventure-text-serializer-gson:$adventureVersion")
testImplementation("org.junit.jupiter:junit-jupiter-api:5.9.3")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:5.9.3")
testImplementation("org.spigotmc:spigot-api:1.12.2-R0.1-SNAPSHOT")
testImplementation("net.kyori:adventure-api:$adventureVersion")
}
tasks.withType<JavaCompile>().configureEach {
sourceCompatibility = JavaVersion.VERSION_1_8.toString()
targetCompatibility = JavaVersion.VERSION_1_8.toString()
options.encoding = "UTF-8"
options.isIncremental = true
options.compilerArgs = mutableListOf("-Xlint:-deprecation,-unchecked")
}
tasks.test {
useJUnitPlatform()
}
}
+7
View File
@@ -3,5 +3,12 @@ plugins {
}
repositories {
mavenCentral()
gradlePluginPortal()
}
dependencies {
implementation(libs.buildIndra)
implementation(libs.buildNmcp)
compileOnly(files(libs::class.java.protectionDomain.codeSource.location))
}
+7
View File
@@ -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"])
}
}
+2 -1
View File
@@ -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)
}
@@ -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
}
@@ -18,7 +18,6 @@ public final class LocaleProvider {
return provider.apply(player);
}
private static @NotNull Function<Player, Locale> get() {
MethodHandles.Lookup lookup = MethodHandles.publicLookup();
try {
+3 -2
View File
@@ -1,8 +1,8 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
kotlin("jvm") version "1.8.21"
id("net.megavex.scoreboardlibrary.publish-conventions")
id("net.megavex.scoreboardlibrary.base-conventions")
alias(libs.plugins.kotlin)
}
kotlin {
@@ -12,6 +12,7 @@ kotlin {
dependencies {
api(project(":scoreboard-library-api"))
testImplementation(kotlin("test"))
compileOnly(libs.spigotApi)
}
tasks.withType<KotlinCompile>().configureEach {
Generated
+27
View File
@@ -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
}
+26
View File
@@ -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
];
};
});
};
}
+23
View File
@@ -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" ]
Binary file not shown.
+3 -1
View File
@@ -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
Vendored
+30 -13
View File
@@ -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
View File
@@ -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
+2 -1
View File
@@ -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)
}
@@ -9,6 +9,10 @@ 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() {
}
@@ -27,6 +31,11 @@ public final class PacketAdapterLoader {
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;
@@ -35,34 +44,68 @@ public final class PacketAdapterLoader {
return tryLoadPacketEvents();
}
private static @Nullable Class<?> tryLoadVersion(@NotNull String version) {
private static @Nullable Class<?> tryLoadVersion(@NotNull String serverVersion) {
// https://www.spigotmc.org/wiki/spigot-nms-and-minecraft-versions-legacy/
// https://www.spigotmc.org/wiki/spigot-nms-and-minecraft-versions-1-10-1-15/
// https://www.spigotmc.org/wiki/spigot-nms-and-minecraft-versions-1-16/
switch (version) {
case "1.8.8-R0.1-SNAPSHOT":
return tryLoadImplementationClass("v1_8_R3");
case "1.17-R0.1-SNAPSHOT":
case "1.17.1-R0.1-SNAPSHOT":
case "1.18-R0.1-SNAPSHOT":
case "1.18.1-R0.1-SNAPSHOT":
case "1.18.2-R0.1-SNAPSHOT":
case "1.19-R0.1-SNAPSHOT":
case "1.19.1-R0.1-SNAPSHOT":
case "1.19.2-R0.1-SNAPSHOT":
case "1.19.3-R0.1-SNAPSHOT":
case "1.19.4-R0.1-SNAPSHOT":
case "1.20-R0.1-SNAPSHOT":
case "1.20.1-R0.1-SNAPSHOT":
case "1.20.2-R0.1-SNAPSHOT":
case "1.20.3-R0.1-SNAPSHOT":
case "1.20.4-R0.1-SNAPSHOT":
return tryLoadImplementationClass("modern");
// https://www.spigotmc.org/wiki/spigot-nms-and-minecraft-versions-1-21/
switch (serverVersion) {
case "1.7.10":
case "1.8":
case "1.8.3":
case "1.8.4":
case "1.8.5":
case "1.8.6":
case "1.8.7":
case "1.8.8":
case "1.9":
case "1.9.2":
case "1.9.4":
case "1.10.2":
case "1.11":
case "1.11.2":
case "1.12":
case "1.12.1":
case "1.12.2":
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("packetevents");
Class<?> nmsClass = tryLoadImplementationClass(PACKET_EVENTS);
if (nmsClass == null) {
return null;
}
@@ -90,7 +90,7 @@ public class ScoreboardLibraryImpl implements ScoreboardLibrary {
}
@Override
public @NotNull Sidebar createSidebar(int maxLines, @Nullable Locale locale) {
public @NotNull Sidebar createSidebar(int maxLines, @Nullable Locale locale, @NotNull String objectiveName) {
checkClosed();
if (maxLines <= 0) {
@@ -99,9 +99,9 @@ public class ScoreboardLibraryImpl implements ScoreboardLibrary {
AbstractSidebar sidebar;
if (locale == null) {
sidebar = new PlayerDependantLocaleSidebar(this, maxLines);
sidebar = new PlayerDependantLocaleSidebar(this, maxLines, objectiveName);
} else {
sidebar = new SingleLocaleSidebar(this, maxLines, locale);
sidebar = new SingleLocaleSidebar(this, maxLines, objectiveName, locale);
}
sidebars().add(sidebar);
@@ -3,6 +3,7 @@ package net.megavex.scoreboardlibrary.implementation.sidebar;
import com.google.common.base.Preconditions;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.ComponentLike;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.kyori.adventure.translation.GlobalTranslator;
import net.megavex.scoreboardlibrary.api.objective.ObjectiveDisplaySlot;
import net.megavex.scoreboardlibrary.api.objective.ObjectiveRenderType;
@@ -16,7 +17,6 @@ import net.megavex.scoreboardlibrary.implementation.player.PlayerDisplayable;
import net.megavex.scoreboardlibrary.implementation.player.ScoreboardLibraryPlayer;
import net.megavex.scoreboardlibrary.implementation.sidebar.line.GlobalLineInfo;
import net.megavex.scoreboardlibrary.implementation.sidebar.line.LocaleLineHandler;
import net.megavex.scoreboardlibrary.implementation.sidebar.line.PlayerNameProvider;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -31,7 +31,6 @@ import static net.kyori.adventure.text.Component.empty;
public abstract class AbstractSidebar implements Sidebar, PlayerDisplayable {
private final ScoreboardLibraryImpl scoreboardLibrary;
private final ObjectivePacketAdapter packetAdapter;
private final List<String> linePlayerNames;
private final GlobalLineInfo[] lines;
private Component title = empty();
@@ -42,12 +41,9 @@ public abstract class AbstractSidebar implements Sidebar, PlayerDisplayable {
private final Queue<SidebarTask> taskQueue = new ConcurrentLinkedQueue<>();
public AbstractSidebar(@NotNull ScoreboardLibraryImpl scoreboardLibrary, int maxLines) {
public AbstractSidebar(@NotNull ScoreboardLibraryImpl scoreboardLibrary, int maxLines, @NotNull String objectiveName) {
this.scoreboardLibrary = scoreboardLibrary;
String objectiveName = UUID.randomUUID().toString().substring(0, 5);
this.packetAdapter = scoreboardLibrary.packetAdapter().createObjectiveAdapter(objectiveName);
this.linePlayerNames = PlayerNameProvider.provideLinePlayerNames(maxLines);
this.lines = new GlobalLineInfo[maxLines];
}
@@ -108,6 +104,11 @@ public abstract class AbstractSidebar implements Sidebar, PlayerDisplayable {
return lines.length;
}
@Override
public @NotNull String objectiveName() {
return packetAdapter.objectiveName();
}
@Override
public final void line(@Range(from = 0, to = MAX_LINES - 1) int index, @Nullable ComponentLike value, @Nullable ScoreFormat scoreFormat) {
checkClosed();
@@ -168,12 +169,13 @@ public abstract class AbstractSidebar implements Sidebar, PlayerDisplayable {
@Override
public final void display(@NotNull Player player) {
packetAdapter.sendProperties(players, PropertiesPacketType.CREATE, title, ObjectiveRenderType.INTEGER, ScoreFormat.blank());
Collection<Player> singleton = Collections.singleton(player);
packetAdapter.sendProperties(singleton, PropertiesPacketType.CREATE, title, ObjectiveRenderType.INTEGER, ScoreFormat.blank());
LocaleLineHandler lineHandler = Objects.requireNonNull(addPlayer0(player));
lineHandler.addPlayer(player);
lineHandler.show(player);
packetAdapter.display(Collections.singleton(player), ObjectiveDisplaySlot.sidebar());
packetAdapter.display(singleton, ObjectiveDisplaySlot.sidebar());
}
public final boolean tick() {
@@ -234,6 +236,11 @@ public abstract class AbstractSidebar implements Sidebar, PlayerDisplayable {
});
} else if (task instanceof SidebarTask.UpdateScores) {
forEachLineHandler(LocaleLineHandler::updateScores);
for (GlobalLineInfo line : lines) {
if (line != null) {
line.updateScore(false);
}
}
} else if (task instanceof SidebarTask.UpdateTitle) {
packetAdapter.sendProperties(internalPlayers(), PropertiesPacketType.UPDATE, title, ObjectiveRenderType.INTEGER, ScoreFormat.blank());
}
@@ -273,7 +280,10 @@ public abstract class AbstractSidebar implements Sidebar, PlayerDisplayable {
private @NotNull GlobalLineInfo getLineInfo(int line) {
GlobalLineInfo globalLineInfo = lines[line];
if (globalLineInfo == null) {
globalLineInfo = lines[line] = new GlobalLineInfo(this, linePlayerNames.get(line), line);
char c = Character.toChars(128 + line)[0];
String playerName = "" + LegacyComponentSerializer.SECTION_CHAR + c;
globalLineInfo = lines[line] = new GlobalLineInfo(this, playerName, line);
updateScores();
}
@@ -17,8 +17,8 @@ public class PlayerDependantLocaleSidebar extends AbstractSidebar {
private final Map<Player, LocaleLineHandler> playerMap = new HashMap<>();
private final Map<Locale, LocaleLineHandler> localeMap = new HashMap<>();
public PlayerDependantLocaleSidebar(@NotNull ScoreboardLibraryImpl scoreboardLibrary, int maxLines) {
super(scoreboardLibrary, maxLines);
public PlayerDependantLocaleSidebar(@NotNull ScoreboardLibraryImpl scoreboardLibrary, int maxLines, @NotNull String objectiveName) {
super(scoreboardLibrary, maxLines, objectiveName);
}
@Override
@@ -16,10 +16,10 @@ public class SingleLocaleSidebar extends AbstractSidebar {
private final LocaleLineHandler sidebar;
private final Set<Player> internalPlayers;
public SingleLocaleSidebar(@NotNull ScoreboardLibraryImpl scoreboardLibrary, int size, @NotNull Locale locale) {
super(scoreboardLibrary, size);
public SingleLocaleSidebar(@NotNull ScoreboardLibraryImpl scoreboardLibrary, int size, @NotNull String objectiveName, @NotNull Locale locale) {
super(scoreboardLibrary, size, objectiveName);
this.locale = locale;
this.sidebar = new LocaleLineHandler(this, locale());
this.sidebar = new LocaleLineHandler(this, locale);
this.internalPlayers = CollectionProvider.set(8);
}
@@ -36,14 +36,12 @@ public class SingleLocaleSidebar extends AbstractSidebar {
@Override
protected @Nullable LocaleLineHandler addPlayer0(@NotNull Player player) {
if (!internalPlayers.add(player)) return null;
return sidebar;
}
@Override
protected @Nullable LocaleLineHandler removePlayer0(@NotNull Player player) {
if (!internalPlayers.remove(player)) return null;
return sidebar;
}
@@ -1,8 +1,8 @@
package net.megavex.scoreboardlibrary.implementation.sidebar.line;
import net.kyori.adventure.text.Component;
import net.megavex.scoreboardlibrary.implementation.commons.LineRenderingStrategy;
import net.megavex.scoreboardlibrary.implementation.sidebar.AbstractSidebar;
import net.megavex.scoreboardlibrary.implementation.sidebar.line.locale.LineType;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@@ -12,111 +12,98 @@ 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) {
boolean isLegacy = sidebar.scoreboardLibrary().packetAdapter().isLegacy(player);
LineType lineType = isLegacy ? LineType.LEGACY : LineType.MODERN;
lineHandler(lineType).players().add(player);
LineRenderingStrategy strategy = sidebar.scoreboardLibrary().packetAdapter().lineRenderingStrategy(player);
lineHandler(strategy).players().add(player);
}
public void removePlayer(@NotNull Player player) {
if (modernLineHandler != null && modernLineHandler.players().remove(player)) {
return;
}
if (legacyLineHandler != null) {
legacyLineHandler.players().remove(player);
for (SidebarLineHandler lineHandler : lineHandlers) {
if (lineHandler != null && lineHandler.players().remove(player)) {
return;
}
}
}
public @NotNull SidebarLineHandler lineHandler(@NotNull LineType lineType) {
switch (lineType) {
case MODERN: {
if (modernLineHandler == null) {
modernLineHandler = new SidebarLineHandler(LineType.MODERN, this);
}
return modernLineHandler;
}
case LEGACY: {
if (legacyLineHandler == null) {
legacyLineHandler = new SidebarLineHandler(LineType.LEGACY, this);
}
return legacyLineHandler;
}
public @NotNull SidebarLineHandler lineHandler(@NotNull LineRenderingStrategy lineType) {
SidebarLineHandler lineHandler = lineHandlers[lineType.ordinal()];
if (lineHandler == null) {
lineHandler = lineHandlers[lineType.ordinal()] = new SidebarLineHandler(lineType, this);
}
throw new RuntimeException();
return lineHandler;
}
public void updateScoreFormat(int lineIndex) {
modernLineHandler.updateScore(lineIndex);
SidebarLineHandler lineHandler = lineHandlers[LineRenderingStrategy.MODERN.ordinal()];
if (lineHandler != null) {
lineHandler.updateScore(lineIndex);
}
}
public void updateLine(int lineIndex, Component renderedValue) {
if (modernLineHandler != null) {
modernLineHandler.setLine(lineIndex, renderedValue);
modernLineHandler.updateLine(lineIndex);
}
if (legacyLineHandler != null) {
legacyLineHandler.setLine(lineIndex, renderedValue);
legacyLineHandler.updateLine(lineIndex);
for (SidebarLineHandler lineHandler : lineHandlers) {
if (lineHandler != null) {
lineHandler.setLine(lineIndex, renderedValue);
}
}
}
public void updateScores() {
if (modernLineHandler != null) {
modernLineHandler.updateScores();
}
if (legacyLineHandler != null) {
legacyLineHandler.updateScores();
for (SidebarLineHandler lineHandler : lineHandlers) {
if (lineHandler != null) {
lineHandler.updateScores();
}
}
}
public void show(Player player) {
if (modernLineHandler != null && modernLineHandler.players().contains(player)) {
modernLineHandler.show(Collections.singleton(player));
} else if (legacyLineHandler != null && legacyLineHandler.players().contains(player)) {
legacyLineHandler.show(Collections.singleton(player));
for (SidebarLineHandler lineHandler : lineHandlers) {
if (lineHandler != null && lineHandler.players().contains(player)) {
lineHandler.show(Collections.singleton(player));
return;
}
}
}
public void hide(Player player) {
if (modernLineHandler != null && modernLineHandler.players().contains(player)) {
modernLineHandler.hide(Collections.singleton(player));
} else if (legacyLineHandler != null && legacyLineHandler.players().contains(player)) {
legacyLineHandler.hide(Collections.singleton(player));
for (SidebarLineHandler lineHandler : lineHandlers) {
if (lineHandler != null && lineHandler.players().contains(player)) {
lineHandler.hide(Collections.singleton(player));
return;
}
}
}
public void hide() {
if (modernLineHandler != null) {
modernLineHandler.hide(modernLineHandler.players());
}
if (legacyLineHandler != null) {
legacyLineHandler.hide(legacyLineHandler.players());
for (SidebarLineHandler lineHandler : lineHandlers) {
if (lineHandler != null) {
lineHandler.hide(lineHandler.players());
}
}
}
}
@@ -1,41 +0,0 @@
package net.megavex.scoreboardlibrary.implementation.sidebar.line;
import net.megavex.scoreboardlibrary.api.sidebar.Sidebar;
import org.bukkit.ChatColor;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Unmodifiable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class PlayerNameProvider {
private static final ChatColor[] CHAT_COLORS = ChatColor.values();
private static final List<String> DEFAULT_NAMES = createLinePlayerNames(Sidebar.MAX_LINES);
private PlayerNameProvider() {
}
public static @NotNull @Unmodifiable List<String> provideLinePlayerNames(int maxLines) {
return maxLines <= Sidebar.MAX_LINES ? DEFAULT_NAMES : createLinePlayerNames(maxLines);
}
private static @NotNull @Unmodifiable List<String> createLinePlayerNames(int maxLines) {
List<String> result = new ArrayList<>(maxLines);
int i = 0;
while (result.size() < maxLines) {
for (ChatColor color : CHAT_COLORS) {
String legacy = color.toString();
String newStr = i == 0 ? legacy : result.get(i - 1) + legacy;
if (!result.contains(newStr)) {
result.add(newStr);
}
}
i++;
}
return Collections.unmodifiableList(result);
}
}
@@ -3,8 +3,11 @@ package net.megavex.scoreboardlibrary.implementation.sidebar.line;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.translation.GlobalTranslator;
import net.megavex.scoreboardlibrary.implementation.commons.CollectionProvider;
import net.megavex.scoreboardlibrary.implementation.sidebar.line.locale.LineType;
import net.megavex.scoreboardlibrary.implementation.commons.LineRenderingStrategy;
import net.megavex.scoreboardlibrary.implementation.sidebar.line.locale.LegacyLocaleLine;
import net.megavex.scoreboardlibrary.implementation.sidebar.line.locale.LocaleLine;
import net.megavex.scoreboardlibrary.implementation.sidebar.line.locale.ModernLocaleLine;
import net.megavex.scoreboardlibrary.implementation.sidebar.line.locale.PostModernLocaleLine;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@@ -12,13 +15,13 @@ 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()];
@@ -26,7 +29,7 @@ public class SidebarLineHandler {
if (line != null) {
Component value = line.value();
if (value != null) {
setLine(line.line(), GlobalTranslator.render(value, localeLineHandler.locale()), false);
setLine(line.line(), GlobalTranslator.render(value, localeLineHandler.locale()));
}
}
}
@@ -40,40 +43,28 @@ public class SidebarLineHandler {
return players;
}
public void updateLine(int lineIndex) {
LocaleLine<?> line = lines[lineIndex];
if (line != null) {
line.updateTeam();
}
}
public void updateScores() {
for (LocaleLine<?> line : lines) {
for (LocaleLine line : lines) {
if (line != null && line.info().updateScore()) {
line.sendScore(players);
line.info().updateScore(false);
}
}
}
public void updateScore(int line) {
LocaleLine<?> localeLine = lines[line];
LocaleLine localeLine = lines[line];
localeLine.sendScore(players);
}
public void setLine(int line, Component renderedLine) {
setLine(line, renderedLine, true);
}
private void setLine(int line, Component renderedLine, boolean sendPackets) {
LocaleLine<?> localeLine = lines[line];
LocaleLine localeLine = lines[line];
if (renderedLine == null && localeLine == null) {
return;
}
boolean newlyCreated = false;
if (localeLine == null) {
lines[line] = localeLine = lineType.create(localeLineHandler.sidebar().lines()[line], this);
lines[line] = localeLine = createLine(strategy, localeLineHandler.sidebar().lines()[line]);
newlyCreated = true;
}
@@ -83,17 +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 (LocaleLine<?> line : lines) {
for (LocaleLine line : lines) {
if (line != null) {
line.show(players);
}
@@ -101,10 +93,23 @@ public class SidebarLineHandler {
}
public void hide(Collection<Player> players) {
for (LocaleLine<?> line : lines) {
for (LocaleLine line : lines) {
if (line != null) {
line.hide(players);
}
}
}
private @NotNull LocaleLine createLine(@NotNull LineRenderingStrategy strategy, @NotNull GlobalLineInfo line) {
switch (strategy) {
case LEGACY:
return new LegacyLocaleLine(line, this);
case MODERN:
return new ModernLocaleLine(line, this);
case POST_MODERN:
return new PostModernLocaleLine(line, this);
default:
throw new IllegalArgumentException();
}
}
}
@@ -1,7 +1,9 @@
package net.megavex.scoreboardlibrary.implementation.sidebar.line.locale;
import com.google.common.collect.ImmutableList;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.ImmutableTeamProperties;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectivePacketAdapter;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.EntriesPacketType;
@@ -18,7 +20,8 @@ import java.util.Set;
import static net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection;
class LegacyLocaleLine implements LocaleLine<String> {
// Implementation for versions below 1.13
public class LegacyLocaleLine implements LocaleLine, ImmutableTeamProperties<String> {
private final GlobalLineInfo info;
private final SidebarLineHandler handler;
private final TeamDisplayPacketAdapter packetAdapter;
@@ -31,7 +34,7 @@ class LegacyLocaleLine implements LocaleLine<String> {
this.handler = handler;
this.player = info.player();
this.packetAdapter = info.packetAdapter().createLegacyTeamDisplayAdapter(this);
packetAdapter.updateTeamPackets(entries());
packetAdapter.updateTeamPackets();
}
public @NotNull GlobalLineInfo info() {
@@ -54,17 +57,16 @@ class LegacyLocaleLine implements LocaleLine<String> {
}
@Override
public @NotNull Collection<String> entries() {
return Collections.singleton(player);
public @NotNull Collection<String> syncedEntries() {
return ImmutableList.of(player);
}
@Override
public void value(@NotNull Component renderedComponent) {
// I really need to rewrite this
String legacyValue = legacySection().serialize(renderedComponent);
@NotNull String legacyValue = legacySection().serialize(renderedComponent);
oldPlayer = player;
String prevPlayer = player;
if (legacyValue.length() <= 16) {
this.prefix = legacyValue;
@@ -80,7 +82,7 @@ class LegacyLocaleLine implements LocaleLine<String> {
this.prefix = legacyValue.substring(0, prefixEnd);
String last = prefix + LegacyComponentSerializer.SECTION_CHAR + (endsWithSection ? legacyValue.charAt(16) : "");
this.player = info.player() + ChatColor.RESET + ChatColor.getLastColors(last);
this.player = info.player() + ChatColor.getLastColors(last);
int playerEnd = prefixEnd;
if (legacyValue.length() > 32) {
@@ -91,11 +93,10 @@ class LegacyLocaleLine implements LocaleLine<String> {
player += legacyValue.substring(prefixEnd, playerEnd);
}
this.suffix = legacyValue.substring(playerEnd + (endsWithSection ? 2 : 0));
this.suffix = ChatColor.getLastColors(this.player) + legacyValue.substring(playerEnd + (endsWithSection ? 2 : 0));
if (suffix.length() > 16) {
String newSuffix = suffix.substring(0, 16);
if (newSuffix.endsWith(String.valueOf(LegacyComponentSerializer.SECTION_CHAR)) &&
ChatColor.getByChar(suffix.charAt(16)) != null) {
if (newSuffix.endsWith(String.valueOf(LegacyComponentSerializer.SECTION_CHAR))) {
newSuffix = newSuffix.substring(0, 15);
}
@@ -105,8 +106,8 @@ class LegacyLocaleLine implements LocaleLine<String> {
currentValue = legacyValue;
if (oldPlayer.equals(player)) {
oldPlayer = null;
if (!player.equals(prevPlayer)) {
oldPlayer = prevPlayer;
}
}
@@ -114,19 +115,24 @@ class LegacyLocaleLine implements LocaleLine<String> {
public void updateTeam() {
Set<Player> players = handler.players();
if (oldPlayer != null) {
ObjectivePacketAdapter pd = handler.localeLineHandler().sidebar().packetAdapter();
packetAdapter.sendEntries(EntriesPacketType.REMOVE, players, Collections.singleton(oldPlayer));
handler.localeLineHandler().sidebar().packetAdapter().removeScore(players, oldPlayer);
pd.removeScore(players, oldPlayer);
oldPlayer = null;
Collection<String> entries = entries();
packetAdapter.updateTeamPackets(entries);
packetAdapter.sendEntries(EntriesPacketType.ADD, players, entries);
handler.localeLineHandler().sidebar().packetAdapter().sendScore(players, player, info.objectiveScore(), null, null);
packetAdapter.sendEntries(EntriesPacketType.ADD, players, Collections.singleton(player));
pd.sendScore(players, player, info.objectiveScore(), null, null);
}
packetAdapter.sendProperties(PropertiesPacketType.UPDATE, players);
}
@Override
public void resetOldPlayer() {
oldPlayer = null;
}
@Override
public void sendScore(@NotNull Collection<Player> players) {
ObjectivePacketAdapter pd = handler.localeLineHandler().sidebar().packetAdapter();
@@ -145,11 +151,12 @@ class LegacyLocaleLine implements LocaleLine<String> {
@Override
public void hide(@NotNull Collection<Player> players) {
ObjectivePacketAdapter pd = handler.localeLineHandler().sidebar().packetAdapter();
if (oldPlayer != null) {
handler.localeLineHandler().sidebar().packetAdapter().removeScore(players, oldPlayer);
pd.removeScore(players, oldPlayer);
}
handler.localeLineHandler().sidebar().packetAdapter().removeScore(players, player);
pd.removeScore(players, player);
info.packetAdapter().removeTeam(players);
}
}
@@ -1,21 +0,0 @@
package net.megavex.scoreboardlibrary.implementation.sidebar.line.locale;
import net.megavex.scoreboardlibrary.implementation.sidebar.line.GlobalLineInfo;
import net.megavex.scoreboardlibrary.implementation.sidebar.line.SidebarLineHandler;
import java.util.function.BiFunction;
public enum LineType {
MODERN(ModernLocaleLine::new),
LEGACY(LegacyLocaleLine::new);
private final BiFunction<GlobalLineInfo, SidebarLineHandler, LocaleLine<?>> constructor;
LineType(BiFunction<GlobalLineInfo, SidebarLineHandler, LocaleLine<?>> constructor) {
this.constructor = constructor;
}
public LocaleLine<?> create(GlobalLineInfo line, SidebarLineHandler localeLineHandler) {
return constructor.apply(line, localeLineHandler);
}
}
@@ -1,20 +1,22 @@
package net.megavex.scoreboardlibrary.implementation.sidebar.line.locale;
import net.kyori.adventure.text.Component;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.ImmutableTeamProperties;
import net.megavex.scoreboardlibrary.implementation.sidebar.line.GlobalLineInfo;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
public interface LocaleLine<C> extends ImmutableTeamProperties<C> {
public interface LocaleLine {
@NotNull GlobalLineInfo info();
void value(@NotNull Component renderedComponent);
void updateTeam();
default void resetOldPlayer() {
}
void sendScore(@NotNull Collection<Player> players);
void show(@NotNull Collection<Player> players);
@@ -1,6 +1,8 @@
package net.megavex.scoreboardlibrary.implementation.sidebar.line.locale;
import com.google.common.collect.ImmutableList;
import net.kyori.adventure.text.Component;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.ImmutableTeamProperties;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamDisplayPacketAdapter;
import net.megavex.scoreboardlibrary.implementation.sidebar.line.GlobalLineInfo;
@@ -9,12 +11,11 @@ import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Collections;
import static net.kyori.adventure.text.Component.empty;
// Implementation for versions above 1.12.2
class ModernLocaleLine implements LocaleLine<Component> {
// Implementation for versions above 1.13
public class ModernLocaleLine implements LocaleLine, ImmutableTeamProperties<Component> {
private final GlobalLineInfo info;
private final SidebarLineHandler handler;
private final Collection<String> entries;
@@ -23,9 +24,9 @@ class ModernLocaleLine implements LocaleLine<Component> {
public ModernLocaleLine(GlobalLineInfo info, SidebarLineHandler handler) {
this.info = info;
this.handler = handler;
this.entries = Collections.singleton(info.player());
this.entries = ImmutableList.of(info.player());
this.packetAdapter = info.packetAdapter().createTeamDisplayAdapter(this);
packetAdapter.updateTeamPackets(entries);
packetAdapter.updateTeamPackets();
}
@Override
@@ -39,7 +40,7 @@ class ModernLocaleLine implements LocaleLine<Component> {
@Override
public void updateTeam() {
packetAdapter.updateTeamPackets(entries);
packetAdapter.updateTeamPackets();
packetAdapter.sendProperties(PropertiesPacketType.UPDATE, handler.players());
}
@@ -64,7 +65,7 @@ class ModernLocaleLine implements LocaleLine<Component> {
}
@Override
public @NotNull Collection<String> entries() {
public @NotNull Collection<String> syncedEntries() {
return entries;
}
@@ -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());
}
}
@@ -111,8 +111,8 @@ public class ScoreboardTeamImpl implements ScoreboardTeam {
Collection<Player> singleton = Collections.singleton(player);
newTeamDisplay.packetAdapter().sendProperties(PropertiesPacketType.UPDATE, singleton);
Collection<String> oldEntries = oldTeamDisplay.entries();
Collection<String> newEntries = newTeamDisplay.entries();
Collection<String> oldEntries = oldTeamDisplay.syncedEntries();
Collection<String> newEntries = newTeamDisplay.syncedEntries();
if (oldEntries.isEmpty()) {
newTeamDisplay.packetAdapter().sendEntries(EntriesPacketType.ADD, singleton, newEntries);
@@ -10,6 +10,8 @@ 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.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;
@@ -23,7 +25,8 @@ public class TeamDisplayImpl implements TeamDisplay, ImmutableTeamProperties<Com
private final ScoreboardTeamImpl team;
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();
@@ -35,7 +38,7 @@ public class TeamDisplayImpl implements TeamDisplay, ImmutableTeamProperties<Com
public TeamDisplayImpl(@NotNull ScoreboardTeamImpl team) {
this.team = team;
this.packetAdapter = team.packetAdapter().createTeamDisplayAdapter(this);
updateTeamPackets();
packetAdapter.updateTeamPackets();
}
@Override
@@ -45,12 +48,18 @@ 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)) {
if (!entries.contains(entry)) {
entries.add(entry);
team.teamManager().taskQueue().add(new TeamManagerTask.AddEntries(this, Collections.singleton(entry)));
return true;
}
@@ -209,8 +218,21 @@ 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() {
@@ -7,7 +7,6 @@ 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.packetAdapter.team.EntriesPacketType;
import net.megavex.scoreboardlibrary.implementation.player.PlayerDisplayable;
import net.megavex.scoreboardlibrary.implementation.player.ScoreboardLibraryPlayer;
import org.bukkit.entity.Player;
@@ -24,6 +23,8 @@ 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 final Queue<TeamManagerTask> taskQueue = new ConcurrentLinkedQueue<>();
private boolean closed;
@@ -156,6 +157,7 @@ public class TeamManagerImpl implements TeamManager, PlayerDisplayable {
return taskQueue;
}
@Override
public void display(@NotNull Player player) {
for (ScoreboardTeamImpl team : teams.values()) {
team.addPlayer(player);
@@ -171,14 +173,14 @@ public class TeamManagerImpl implements TeamManager, PlayerDisplayable {
if (task instanceof TeamManagerTask.Close) {
for (ScoreboardTeamImpl team : teams.values()) {
Set<Player> removePlayers = CollectionProvider.set(players.size());
Set<Player> removePlayers = CollectionProvider.set(internalPlayers.size());
for (TeamDisplayImpl value : team.displayMap().values()) {
removePlayers.addAll(value.players());
}
team.packetAdapter().removeTeam(removePlayers);
}
for (Player player : players) {
for (Player player : internalPlayers) {
Objects.requireNonNull(scoreboardLibrary.getPlayer(player)).teamManagerQueue().remove(this);
}
return false;
@@ -186,12 +188,14 @@ public class TeamManagerImpl implements TeamManager, PlayerDisplayable {
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());
}
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;
@@ -225,16 +229,15 @@ public class TeamManagerImpl implements TeamManager, PlayerDisplayable {
} else if (task instanceof TeamManagerTask.UpdateTeamDisplay) {
TeamManagerTask.UpdateTeamDisplay updateTeamDisplayTask = (TeamManagerTask.UpdateTeamDisplay) task;
@NotNull TeamDisplayImpl teamDisplay = updateTeamDisplayTask.teamDisplay();
teamDisplay.updateTeamPackets();
teamDisplay.packetAdapter().sendProperties(PropertiesPacketType.UPDATE, teamDisplay.players());
teamDisplay.handleUpdateDisplay();
} else if (task instanceof TeamManagerTask.AddEntries) {
TeamManagerTask.AddEntries addEntriesTask = (TeamManagerTask.AddEntries) task;
@NotNull TeamDisplayImpl teamDisplay = addEntriesTask.teamDisplay();
teamDisplay.packetAdapter().sendEntries(EntriesPacketType.ADD, teamDisplay.players(), addEntriesTask.entries());
teamDisplay.handleAddEntries(addEntriesTask.entries());
} else if (task instanceof TeamManagerTask.RemoveEntries) {
TeamManagerTask.RemoveEntries removeEntriesTask = (TeamManagerTask.RemoveEntries) task;
@NotNull TeamDisplayImpl teamDisplay = removeEntriesTask.teamDisplay();
teamDisplay.packetAdapter().sendEntries(EntriesPacketType.REMOVE, teamDisplay.players(), removeEntriesTask.entries());
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());
@@ -1,20 +0,0 @@
package net.megavex.scoreboardlibrary.implementation.sidebar.line;
import org.junit.jupiter.api.Test;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.junit.jupiter.api.Assertions.assertTrue;
class PlayerNameProviderTest {
@Test
void duplicateTest() {
List<String> names = PlayerNameProvider.provideLinePlayerNames(100);
Set<String> uniqueNames = new HashSet<>(names.size());
for (String name : names) {
assertTrue(uniqueNames.add(name), "duplicate player name " + name);
}
}
}
-5
View File
@@ -1,5 +0,0 @@
jdk:
- openjdk17
install:
- "bash gradlew build publishToMavenLocal"
+9
View File
@@ -0,0 +1,9 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": [
"config:recommended"
],
"ignoreDeps": [
"org.spigotmc:spigot-api"
]
}
+5 -1
View File
@@ -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")
@@ -14,6 +14,10 @@ project(":packetevents").projectDir = file("versions/packetevents")
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")
+8
View File
@@ -0,0 +1,8 @@
plugins {
id("net.megavex.scoreboardlibrary.base-conventions")
}
dependencies {
compileOnly(project(":scoreboard-library-packet-adapter-base"))
compileOnly(libs.spigotApi)
}
@@ -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);
}
}
}
@@ -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;
}
}
@@ -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);
}
}
}
@@ -1,40 +1,42 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.v1_8_R3;
package net.megavex.scoreboardlibrary.implementation.packetAdapter.legacy;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
import net.kyori.adventure.translation.GlobalTranslator;
import net.megavex.scoreboardlibrary.api.objective.ObjectiveDisplaySlot;
import net.megavex.scoreboardlibrary.api.objective.ObjectiveRenderType;
import net.megavex.scoreboardlibrary.api.objective.ScoreFormat;
import net.megavex.scoreboardlibrary.implementation.commons.LegacyFormatUtil;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectiveConstants;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectivePacketAdapter;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.LocalePacketUtil;
import net.minecraft.server.v1_8_R3.*;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
public class ObjectivePacketAdapterImpl implements ObjectivePacketAdapter {
private final PacketSender<Packet<?>> sender;
private final String objectiveName;
private PacketPlayOutScoreboardObjective removePacket;
import static net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer.legacySection;
public ObjectivePacketAdapterImpl(@NotNull PacketSender<Packet<?>> sender, @NotNull String objectiveName) {
this.sender = sender;
public class ObjectivePacketAdapterImpl implements ObjectivePacketAdapter {
private final String objectiveName;
private Object removePacket;
public ObjectivePacketAdapterImpl(@NotNull String objectiveName) {
this.objectiveName = objectiveName;
}
@Override
public @NotNull String objectiveName() {
return objectiveName;
}
@Override
public void display(@NotNull Collection<Player> players, @NotNull ObjectiveDisplaySlot slot) {
PacketPlayOutScoreboardDisplayObjective packet = new PacketPlayOutScoreboardDisplayObjective();
PacketAccessors.DISPLAY_OBJECTIVE_POSITION.set(packet, ObjectiveConstants.displaySlotIndex(slot));
Object packet = PacketAccessors.DISPLAY_OBJECTIVE_CONSTRUCTOR.invoke();
PacketAccessors.DISPLAY_OBJECTIVE_POSITION.set(packet, ObjectiveConstants.displaySlotIndex(slot, false));
PacketAccessors.DISPLAY_OBJECTIVE_NAME.set(packet, objectiveName);
sender.sendPacket(players, packet);
LegacyPacketSender.INSTANCE.sendPacket(players, packet);
}
@Override
@@ -46,7 +48,7 @@ public class ObjectivePacketAdapterImpl implements ObjectivePacketAdapter {
@Nullable ScoreFormat scoreFormat
) {
LocalePacketUtil.sendLocalePackets(
sender,
LegacyPacketSender.INSTANCE,
players,
locale -> createPropertiesPacket(packetType, GlobalTranslator.render(value, locale), renderType)
);
@@ -55,11 +57,12 @@ public class ObjectivePacketAdapterImpl implements ObjectivePacketAdapter {
@Override
public void remove(@NotNull Collection<Player> players) {
if (removePacket == null) {
removePacket = new PacketPlayOutScoreboardObjective();
removePacket = PacketAccessors.OBJECTIVE_CONSTRUCTOR.invoke();
PacketAccessors.OBJECTIVE_NAME_FIELD.set(removePacket, objectiveName);
PacketAccessors.OBJECTIVE_DISPLAY_NAME_FIELD.set(removePacket, "");
PacketAccessors.OBJECTIVE_MODE_FIELD.set(removePacket, ObjectiveConstants.MODE_REMOVE);
}
sender.sendPacket(players, removePacket);
LegacyPacketSender.INSTANCE.sendPacket(players, removePacket);
}
@Override
@@ -70,45 +73,52 @@ public class ObjectivePacketAdapterImpl implements ObjectivePacketAdapter {
@Nullable Component display,
@Nullable ScoreFormat scoreFormat
) {
PacketPlayOutScoreboardScore packet = new PacketPlayOutScoreboardScore(entry);
Object packet = PacketAccessors.SCORE_CONSTRUCTOR.invoke(entry);
PacketAccessors.SCORE_OBJECTIVE_NAME_FIELD.set(packet, objectiveName);
PacketAccessors.SCORE_VALUE_FIELD.set(packet, value);
PacketAccessors.SCORE_ACTION_FIELD.set(packet, PacketPlayOutScoreboardScore.EnumScoreboardAction.CHANGE);
sender.sendPacket(players, packet);
if (PacketAccessors.SCORE_ACTION_FIELD_1_8 != null) {
PacketAccessors.SCORE_ACTION_FIELD_1_8.set(packet, PacketAccessors.SCORE_ACTION_CHANGE_1_8);
} else {
PacketAccessors.SCORE_ACTION_FIELD_1_7.set(packet, 0);
}
LegacyPacketSender.INSTANCE.sendPacket(players, packet);
}
@Override
public void removeScore(@NotNull Collection<Player> players, @NotNull String entry) {
PacketPlayOutScoreboardScore packet = new PacketPlayOutScoreboardScore(entry);
Object packet = PacketAccessors.SCORE_CONSTRUCTOR.invoke(entry);
PacketAccessors.SCORE_OBJECTIVE_NAME_FIELD.set(packet, objectiveName);
sender.sendPacket(players, packet);
LegacyPacketSender.INSTANCE.sendPacket(players, packet);
}
private @NotNull PacketPlayOutScoreboardObjective createPropertiesPacket(
private @NotNull Object createPropertiesPacket(
@NotNull PropertiesPacketType packetType,
@NotNull Component value,
@NotNull ObjectiveRenderType renderType
) {
PacketPlayOutScoreboardObjective packet = new PacketPlayOutScoreboardObjective();
Object packet = PacketAccessors.OBJECTIVE_CONSTRUCTOR.invoke();
PacketAccessors.OBJECTIVE_NAME_FIELD.set(packet, objectiveName);
PacketAccessors.OBJECTIVE_MODE_FIELD.set(packet, ObjectiveConstants.mode(packetType));
String legacyValue = LegacyFormatUtil.limitLegacyText(LegacyComponentSerializer.legacySection().serialize(value), ObjectiveConstants.LEGACY_VALUE_CHAR_LIMIT);
String legacyValue = LegacyFormatUtil.limitLegacyText(legacySection().serialize(value), ObjectiveConstants.LEGACY_VALUE_CHAR_LIMIT);
PacketAccessors.OBJECTIVE_DISPLAY_NAME_FIELD.set(packet, legacyValue);
IScoreboardCriteria.EnumScoreboardHealthDisplay nmsRenderType;
switch (renderType) {
case INTEGER:
nmsRenderType = IScoreboardCriteria.EnumScoreboardHealthDisplay.INTEGER;
break;
case HEARTS:
nmsRenderType = IScoreboardCriteria.EnumScoreboardHealthDisplay.HEARTS;
break;
default:
throw new IllegalStateException();
if (PacketAccessors.OBJECTIVE_HEALTH_DISPLAY_FIELD != null) {
Object nmsRenderType;
switch (renderType) {
case INTEGER:
nmsRenderType = PacketAccessors.HEALTH_DISPLAY_INTEGER;
break;
case HEARTS:
nmsRenderType = PacketAccessors.HEALTH_DISPLAY_HEARTS;
break;
default:
throw new IllegalStateException();
}
PacketAccessors.OBJECTIVE_HEALTH_DISPLAY_FIELD.set(packet, nmsRenderType);
}
PacketAccessors.OBJECTIVE_HEALTH_DISPLAY_FIELD.set(packet, nmsRenderType);
return packet;
}
}
@@ -0,0 +1,80 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.legacy;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ConstructorAccessor;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.FieldAccessor;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ReflectUtil;
import java.util.Collection;
public final class PacketAccessors {
public static final Class<?> HEALTH_DISPLAY_CLASS =
ReflectUtil.getOptionalClass(LegacyMinecraftClasses.server("IScoreboardCriteria$EnumScoreboardHealthDisplay"));
public static final Object HEALTH_DISPLAY_INTEGER = HEALTH_DISPLAY_CLASS != null ? ReflectUtil.getEnumInstance(HEALTH_DISPLAY_CLASS, "INTEGER") : null;
public static final Object HEALTH_DISPLAY_HEARTS = HEALTH_DISPLAY_CLASS != null ? ReflectUtil.getEnumInstance(HEALTH_DISPLAY_CLASS, "HEARTS") : null;
private static final boolean IS_AT_LEAST_1_8 = HEALTH_DISPLAY_INTEGER != null;
private static final boolean IS_AT_LEAST_1_9 = ReflectUtil.getOptionalClass(LegacyMinecraftClasses.server("ScoreboardTeamBase$EnumTeamPush")) != null;
public static final Class<?> OBJECTIVE_CLASS =
ReflectUtil.getClassOrThrow(LegacyMinecraftClasses.server("PacketPlayOutScoreboardObjective"));
public static final ConstructorAccessor<?> OBJECTIVE_CONSTRUCTOR = ReflectUtil.findConstructor(OBJECTIVE_CLASS);
public static final FieldAccessor<Object, Integer> OBJECTIVE_MODE_FIELD =
ReflectUtil.findFieldUnchecked(OBJECTIVE_CLASS, 0, int.class);
public static final FieldAccessor<Object, String> OBJECTIVE_NAME_FIELD =
ReflectUtil.findFieldUnchecked(OBJECTIVE_CLASS, 0, String.class);
public static final FieldAccessor<Object, String> OBJECTIVE_DISPLAY_NAME_FIELD =
ReflectUtil.findFieldUnchecked(OBJECTIVE_CLASS, 1, String.class);
public static final FieldAccessor<Object, Object> OBJECTIVE_HEALTH_DISPLAY_FIELD =
HEALTH_DISPLAY_CLASS != null ? ReflectUtil.findFieldUnchecked(OBJECTIVE_CLASS, 0, HEALTH_DISPLAY_CLASS) : null;
public static final Class<?> DISPLAY_OBJECTIVE_CLASS =
ReflectUtil.getClassOrThrow(LegacyMinecraftClasses.server("PacketPlayOutScoreboardDisplayObjective"));
public static final ConstructorAccessor<?> DISPLAY_OBJECTIVE_CONSTRUCTOR = ReflectUtil.findConstructor(DISPLAY_OBJECTIVE_CLASS);
public static final FieldAccessor<Object, Integer> DISPLAY_OBJECTIVE_POSITION =
ReflectUtil.findFieldUnchecked(DISPLAY_OBJECTIVE_CLASS, 0, int.class);
public static final FieldAccessor<Object, String> DISPLAY_OBJECTIVE_NAME =
ReflectUtil.findFieldUnchecked(DISPLAY_OBJECTIVE_CLASS, 0, String.class);
public static final Class<?> SCORE_CLASS =
ReflectUtil.getClassOrThrow(LegacyMinecraftClasses.server("PacketPlayOutScoreboardScore"));
public static final ConstructorAccessor<?> SCORE_CONSTRUCTOR = ReflectUtil.findConstructor(SCORE_CLASS, String.class);
public static final FieldAccessor<Object, String> SCORE_OBJECTIVE_NAME_FIELD =
ReflectUtil.findFieldUnchecked(SCORE_CLASS, 1, String.class);
public static final FieldAccessor<Object, Integer> SCORE_VALUE_FIELD =
ReflectUtil.findFieldUnchecked(SCORE_CLASS, 0, int.class);
public static final Class<?> SCORE_ACTION_CLASS =
ReflectUtil.getOptionalClass(LegacyMinecraftClasses.server("PacketPlayOutScoreboardScore$EnumScoreboardAction"));
public static final FieldAccessor<Object, Object> SCORE_ACTION_FIELD_1_8 =
SCORE_ACTION_CLASS != null ? ReflectUtil.findFieldUnchecked(SCORE_CLASS, 0, SCORE_ACTION_CLASS) : null;
public static final Object SCORE_ACTION_CHANGE_1_8 = SCORE_ACTION_FIELD_1_8 != null ? ReflectUtil.getEnumInstance(SCORE_ACTION_CLASS, "CHANGE") : null;
public static final FieldAccessor<Object, Integer> SCORE_ACTION_FIELD_1_7 =
SCORE_ACTION_CLASS == null ? ReflectUtil.findFieldUnchecked(SCORE_CLASS, 1, int.class) : null;
public static final Class<?> TEAM_CLASS =
ReflectUtil.getClassOrThrow(LegacyMinecraftClasses.server("PacketPlayOutScoreboardTeam"));
public static final ConstructorAccessor<?> TEAM_CONSTRUCTOR = ReflectUtil.findConstructor(TEAM_CLASS);
public static final FieldAccessor<Object, String> TEAM_NAME_FIELD =
ReflectUtil.findFieldUnchecked(TEAM_CLASS, 0, String.class);
public static final FieldAccessor<Object, String> TEAM_DISPLAY_NAME_FIELD =
ReflectUtil.findFieldUnchecked(TEAM_CLASS, 1, String.class);
public static final FieldAccessor<Object, String> TEAM_PREFIX_FIELD =
ReflectUtil.findFieldUnchecked(TEAM_CLASS, 2, String.class);
public static final FieldAccessor<Object, String> TEAM_SUFFIX_FIELD =
ReflectUtil.findFieldUnchecked(TEAM_CLASS, 3, String.class);
public static final FieldAccessor<Object, String> TEAM_NAME_TAG_VISIBILITY_FIELD =
IS_AT_LEAST_1_8 ? ReflectUtil.findFieldUnchecked(TEAM_CLASS, 4, String.class) : null;
public static final FieldAccessor<Object, String> TEAM_COLLISION_RULE_FIELD =
IS_AT_LEAST_1_9 ? ReflectUtil.findFieldUnchecked(TEAM_CLASS, 5, String.class) : null;
public static final FieldAccessor<Object, Integer> TEAM_COLOR_FIELD =
IS_AT_LEAST_1_8 ? ReflectUtil.findFieldUnchecked(TEAM_CLASS, 0, int.class) : null;
@SuppressWarnings("rawtypes")
public static final FieldAccessor<Object, Collection> TEAM_ENTRIES_FIELD =
ReflectUtil.findFieldUnchecked(TEAM_CLASS, 0, Collection.class);
public static final FieldAccessor<Object, Integer> TEAM_MODE_FIELD =
ReflectUtil.findFieldUnchecked(TEAM_CLASS, IS_AT_LEAST_1_8 ? 1 : 0, int.class);
public static final FieldAccessor<Object, Integer> TEAM_RULES_FIELD =
ReflectUtil.findFieldUnchecked(TEAM_CLASS, IS_AT_LEAST_1_8 ? 2 : 1, int.class);
private PacketAccessors() {
}
}
@@ -1,33 +1,26 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.v1_8_R3;
package net.megavex.scoreboardlibrary.implementation.packetAdapter.legacy;
import net.megavex.scoreboardlibrary.implementation.commons.LineRenderingStrategy;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectivePacketAdapter;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketAdapterProvider;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamsPacketAdapter;
import net.minecraft.server.v1_8_R3.Packet;
import org.bukkit.craftbukkit.v1_8_R3.entity.CraftPlayer;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@SuppressWarnings("unused")
public class PacketAdapterProviderImpl implements PacketAdapterProvider, PacketSender<Packet<?>> {
public class PacketAdapterProviderImpl implements PacketAdapterProvider {
@Override
public @NotNull TeamsPacketAdapter createTeamPacketAdapter(@NotNull String teamName) {
return new TeamsPacketAdapterImpl(this, teamName);
return new TeamsPacketAdapterImpl(teamName);
}
@Override
public @NotNull ObjectivePacketAdapter createObjectiveAdapter(@NotNull String objectiveName) {
return new ObjectivePacketAdapterImpl(this, objectiveName);
return new ObjectivePacketAdapterImpl(objectiveName);
}
@Override
public boolean isLegacy(@NotNull Player player) {
return true;
}
@Override
public void sendPacket(@NotNull Player player, @NotNull Packet<?> packet) {
((CraftPlayer) player).getHandle().playerConnection.sendPacket(packet);
public @NotNull LineRenderingStrategy lineRenderingStrategy(@NotNull Player player) {
return LineRenderingStrategy.LEGACY;
}
}
@@ -1,47 +1,41 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.v1_8_R3;
package net.megavex.scoreboardlibrary.implementation.packetAdapter.legacy;
import com.google.common.collect.ImmutableList;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.format.NamedTextColor;
import net.megavex.scoreboardlibrary.implementation.commons.LegacyFormatUtil;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.ImmutableTeamProperties;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PropertiesPacketType;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.EntriesPacketType;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamConstants;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamDisplayPacketAdapter;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamsPacketAdapter;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.LocalePacketUtil;
import net.minecraft.server.v1_8_R3.EnumChatFormat;
import net.minecraft.server.v1_8_R3.Packet;
import net.minecraft.server.v1_8_R3.PacketPlayOutScoreboardTeam;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Locale;
import java.util.Objects;
import static net.megavex.scoreboardlibrary.implementation.commons.LegacyFormatUtil.limitLegacyText;
public class TeamsPacketAdapterImpl implements TeamsPacketAdapter {
private final PacketSender<Packet<?>> sender;
private final String teamName;
private PacketPlayOutScoreboardTeam removePacket;
private Object removePacket;
public TeamsPacketAdapterImpl(@NotNull PacketSender<Packet<?>> sender, @NotNull String teamName) {
this.sender = sender;
public TeamsPacketAdapterImpl(@NotNull String teamName) {
this.teamName = teamName;
}
@Override
public void removeTeam(@NotNull Iterable<Player> players) {
if (removePacket == null) {
removePacket = new PacketPlayOutScoreboardTeam();
removePacket = PacketAccessors.TEAM_CONSTRUCTOR.invoke();
PacketAccessors.TEAM_NAME_FIELD.set(removePacket, teamName);
PacketAccessors.TEAM_MODE_FIELD.set(removePacket, TeamConstants.MODE_REMOVE);
}
sender.sendPacket(players, removePacket);
LegacyPacketSender.INSTANCE.sendPacket(players, removePacket);
}
@Override
@@ -63,40 +57,46 @@ public class TeamsPacketAdapterImpl implements TeamsPacketAdapter {
@Override
public void sendEntries(@NotNull EntriesPacketType packetType, @NotNull Collection<Player> players, @NotNull Collection<String> entries) {
PacketPlayOutScoreboardTeam packet = new PacketPlayOutScoreboardTeam();
Object packet = PacketAccessors.TEAM_CONSTRUCTOR.invoke();
PacketAccessors.TEAM_NAME_FIELD.set(packet, teamName);
PacketAccessors.TEAM_MODE_FIELD.set(packet, TeamConstants.mode(packetType));
PacketAccessors.TEAM_ENTRIES_FIELD.set(packet, entries);
sender.sendPacket(players, packet);
LegacyPacketSender.INSTANCE.sendPacket(players, packet);
}
@Override
public void sendProperties(@NotNull PropertiesPacketType packetType, @NotNull Collection<Player> players) {
LocalePacketUtil.sendLocalePackets(
sender,
LegacyPacketSender.INSTANCE,
players,
locale -> {
String displayName = limitLegacyText(toLegacy(properties.displayName(), locale), TeamConstants.LEGACY_CHAR_LIMIT);
String prefix = limitLegacyText(toLegacy(properties.prefix(), locale), TeamConstants.LEGACY_CHAR_LIMIT);
String suffix = limitLegacyText(toLegacy(properties.suffix(), locale), TeamConstants.LEGACY_CHAR_LIMIT);
PacketPlayOutScoreboardTeam packet = new PacketPlayOutScoreboardTeam();
Object packet = PacketAccessors.TEAM_CONSTRUCTOR.invoke();
PacketAccessors.TEAM_NAME_FIELD.set(packet, teamName);
PacketAccessors.TEAM_MODE_FIELD.set(packet, TeamConstants.mode(packetType));
PacketAccessors.TEAM_DISPLAY_NAME_FIELD.set(packet, displayName);
PacketAccessors.TEAM_PREFIX_FIELD.set(packet, prefix);
PacketAccessors.TEAM_SUFFIX_FIELD.set(packet, suffix);
PacketAccessors.TEAM_NAME_TAG_VISIBILITY_FIELD.set(packet, properties.nameTagVisibility().key());
NamedTextColor color = properties.playerColor();
if (color != null) {
String name = NamedTextColor.NAMES.key(color);
PacketAccessors.TEAM_COLOR_FIELD.set(packet, Objects.requireNonNull(EnumChatFormat.b(name)).b());
PacketAccessors.TEAM_RULES_FIELD.set(packet, properties.packOptions());
if (PacketAccessors.TEAM_NAME_TAG_VISIBILITY_FIELD != null) {
PacketAccessors.TEAM_NAME_TAG_VISIBILITY_FIELD.set(packet, properties.nameTagVisibility().key());
}
NamedTextColor color = properties.playerColor();
if (PacketAccessors.TEAM_COLOR_FIELD != null && color != null) {
int teamColorField = ChatColorUtil.getColorIndex(color);
PacketAccessors.TEAM_COLOR_FIELD.set(packet, teamColorField);
}
if (PacketAccessors.TEAM_COLLISION_RULE_FIELD != null) {
PacketAccessors.TEAM_COLLISION_RULE_FIELD.set(packet, properties.collisionRule().key());
}
PacketAccessors.TEAM_RULES_FIELD.set(packet, properties.packOptions());
if (packetType == PropertiesPacketType.CREATE) {
PacketAccessors.TEAM_ENTRIES_FIELD.set(packet, properties.entries());
PacketAccessors.TEAM_ENTRIES_FIELD.set(packet, ImmutableList.copyOf(properties.syncedEntries()));
}
return packet;
+35 -8
View File
@@ -1,25 +1,52 @@
plugins {
id("io.papermc.paperweight.userdev") version "1.5.11"
`maven-publish`
id("net.megavex.scoreboardlibrary.base-conventions")
alias(libs.plugins.paperweight)
}
dependencies {
compileOnly(project(":scoreboard-library-packet-adapter-base"))
paperweight.paperDevBundle("1.20.4-R0.1-SNAPSHOT")
compileOnly(project(":scoreboard-library-packet-adapter-base")) {
exclude(group = "org.spigotmc", module = "spigot-api")
}
paperweight.paperDevBundle(libs.versions.devBundle.get())
}
tasks {
compileJava {
// Workaround to get it to compile targeting Java 8 while using NMS which targets 17
// Why does this work? I have no idea
options.release = null
}
// https://github.com/unnamed/hephaestus-engine/blob/db6deabe1ddb0a549306b0c4b519c3e79e6f1ea8/runtime-bukkit/adapt-v1_20_R3/build.gradle.kts#L25
reobfJar {
outputJar = file("build/libs/scoreboard-library-modern-$version-reobf.jar")
}
assemble {
dependsOn(reobfJar)
}
}
publishing {
publications.create<MavenPublication>("maven") {
artifact(tasks.reobfJar)
javadoc {
exclude("**")
}
}
java {
disableAutoTargetJvm()
}
indra {
includeJavaSoftwareComponentInPublications(false)
}
publishing {
publications.getByName<MavenPublication>("maven") {
artifact(tasks.jar) {
classifier = "mojmap"
}
artifact(tasks.reobfJar)
artifact(tasks.javadocJar)
artifact(tasks.sourcesJar)
}
}
@@ -0,0 +1,90 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern;
import com.google.gson.JsonElement;
import com.mojang.serialization.JsonOps;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.translation.GlobalTranslator;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.NativeAdventureUtil;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.RegistryUtil;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ReflectUtil;
import net.minecraft.network.chat.ComponentSerialization;
import net.minecraft.network.chat.MutableComponent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Method;
import java.util.Locale;
import static net.kyori.adventure.text.serializer.gson.GsonComponentSerializer.gson;
public class ComponentProviderImpl implements ComponentProvider {
private static final MethodHandle FROM_JSON_METHOD;
static {
if (!PacketAccessors.IS_1_21_6_OR_ABOVE) {
Class<?> serializerClass = ReflectUtil.getClassOrThrow("net.minecraft.network.chat.Component$Serializer", "net.minecraft.network.chat.IChatBaseComponent$ChatSerializer");
MethodHandle handle = null;
for (Method method : serializerClass.getMethods()) {
if (method.getReturnType() == MutableComponent.class &&
method.getParameterCount() >= 1 &&
method.getParameterCount() <= 2 &&
method.getParameterTypes()[0] == JsonElement.class
) {
try {
handle = MethodHandles.lookup().unreflect(method);
break;
} catch (IllegalAccessException e) {
throw new ExceptionInInitializerError(e);
}
}
}
if (handle == null) {
throw new ExceptionInInitializerError("failed to find chat component fromJson method");
}
FROM_JSON_METHOD = handle;
} else {
FROM_JSON_METHOD = null;
}
}
private final boolean isNativeAdventure;
public ComponentProviderImpl(boolean isNativeAdventure) {
this.isNativeAdventure = isNativeAdventure;
}
@Override
public net.minecraft.network.chat.@NotNull Component fromAdventure(@NotNull Component adventure, @Nullable Locale locale) {
if (isNativeAdventure) {
return NativeAdventureUtil.fromAdventureComponent(adventure);
}
Component translated = adventure;
if (locale != null) {
translated = GlobalTranslator.render(adventure, locale);
}
JsonElement json = gson().serializeToTree(translated);
if (FROM_JSON_METHOD == null) {
// 1.21.6+
return ComponentSerialization.CODEC.parse(JsonOps.INSTANCE, json).getOrThrow();
}
Object[] args;
if (PacketAccessors.IS_1_20_5_OR_ABOVE) {
args = new Object[]{json, RegistryUtil.MINECRAFT_REGISTRY};
} else {
args = new Object[]{json};
}
try {
return (net.minecraft.network.chat.Component) FROM_JSON_METHOD.invokeWithArguments(args);
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
}
@@ -11,8 +11,8 @@ import net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket;
import net.minecraft.network.protocol.game.ClientboundSetObjectivePacket;
import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket;
import net.minecraft.network.protocol.game.ClientboundSetScorePacket;
import net.minecraft.server.ServerScoreboard;
import net.minecraft.world.scores.Objective;
import net.minecraft.world.scores.Team;
import net.minecraft.world.scores.criteria.ObjectiveCriteria;
import java.util.Collection;
@@ -20,62 +20,126 @@ import java.util.Optional;
public final class PacketAccessors {
static {
boolean hasNumberFormat = true;
boolean is1_20_2OrAbove = false;
try {
Class.forName("net.minecraft.world.scores.DisplaySlot");
is1_20_2OrAbove = true;
} catch (ClassNotFoundException ignored) {
}
IS_1_20_2_OR_ABOVE = is1_20_2OrAbove;
boolean is1_20_3OrAbove = false;
try {
Class.forName("net.minecraft.network.chat.numbers.NumberFormat");
} catch (ClassNotFoundException e) {
hasNumberFormat = false;
is1_20_3OrAbove = true;
} catch (ClassNotFoundException ignored) {
}
IS_1_20_3_OR_ABOVE = is1_20_3OrAbove;
boolean is1_20_5OrAbove = false;
try {
Class.forName("net.minecraft.network.protocol.common.ClientboundTransferPacket"); // Random 1.20.5 class
is1_20_5OrAbove = true;
} catch (ClassNotFoundException ignored) {
}
IS_1_20_5_OR_ABOVE = is1_20_5OrAbove;
boolean is1_21_5OrAbove = false;
try {
Class.forName("net.minecraft.world.item.component.BlocksAttacks"); // Random 1.21.5 class
is1_21_5OrAbove = true;
} catch (ClassNotFoundException ignored) {
}
IS_1_21_5_OR_ABOVE = is1_21_5OrAbove;
boolean is1_21_6OrAbove = false;
try {
Class.forName("net.minecraft.server.dialog.Dialog"); // Random 1.21.6 class
is1_21_6OrAbove = true;
} catch (ClassNotFoundException ignored) {
}
IS_1_21_6_OR_ABOVE = is1_21_6OrAbove;
if (is1_21_5OrAbove) {
NAME_TAG_VISIBILITY_FIELD_1_21_5 = ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 0, Team.Visibility.class);
COLLISION_RULE_FIELD_1_21_5 = ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 0, Team.CollisionRule.class);
NAME_TAG_VISIBILITY_FIELD_1_21_4 = null;
COLLISION_RULE_FIELD_1_21_4 = null;
} else {
NAME_TAG_VISIBILITY_FIELD_1_21_4 = ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 0, String.class);
COLLISION_RULE_FIELD_1_21_4 = ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 1, String.class);
NAME_TAG_VISIBILITY_FIELD_1_21_5 = null;
COLLISION_RULE_FIELD_1_21_5 = null;
}
HAS_NUMBER_FORMAT = hasNumberFormat;
if (hasNumberFormat) {
OBJECTIVE_NUMBER_FORMAT_FIELD = ReflectUtil.findField(ClientboundSetObjectivePacket.class, "g", NumberFormat.class);
if (is1_20_5OrAbove) {
OBJECTIVE_NUMBER_FORMAT_FIELD = ReflectUtil.findFieldUnchecked(ClientboundSetObjectivePacket.class, 0, Optional.class);
SCORE_1_20_2_CONSTRUCTOR = null;
SCORE_1_20_3_CONSTRUCTOR = null;
SCORE_1_20_2_METHOD_CHANGE = null;
SCORE_1_20_2_METHOD_REMOVE = null;
} else if (is1_20_3OrAbove) {
OBJECTIVE_NUMBER_FORMAT_FIELD = ReflectUtil.findFieldUnchecked(ClientboundSetObjectivePacket.class, 0, NumberFormat.class);
SCORE_1_20_3_CONSTRUCTOR = ReflectUtil.findConstructor(ClientboundSetScorePacket.class, String.class, String.class, int.class, Component.class, NumberFormat.class);
SCORE_1_20_2_METHOD_CHANGE = null;
SCORE_1_20_2_METHOD_REMOVE = null;
SCORE_1_20_2_CONSTRUCTOR = null;
} else {
OBJECTIVE_NUMBER_FORMAT_FIELD = null;
SCORE_1_20_3_CONSTRUCTOR = null;
Class<?> methodClass = ReflectUtil.getClassOrThrow("net.minecraft.server.ServerScoreboard$Method", "net.minecraft.server.ScoreboardServer$Action");
SCORE_1_20_2_METHOD_CHANGE = ReflectUtil.getEnumInstance(methodClass, "CHANGE", "a");
SCORE_1_20_2_METHOD_REMOVE = ReflectUtil.getEnumInstance(methodClass, "REMOVE", "b");
SCORE_1_20_2_CONSTRUCTOR = ReflectUtil.findConstructor(ClientboundSetScorePacket.class, methodClass, String.class, String.class, int.class);
}
}
public static final boolean HAS_NUMBER_FORMAT; // 1.20.3+
public static final boolean IS_1_20_2_OR_ABOVE, IS_1_20_3_OR_ABOVE, IS_1_20_5_OR_ABOVE, IS_1_21_5_OR_ABOVE, IS_1_21_6_OR_ABOVE;
public static final PacketConstructor<ClientboundSetObjectivePacket> OBJECTIVE_PACKET_CONSTRUCTOR =
ReflectUtil.findEmptyConstructor(ClientboundSetObjectivePacket.class);
ReflectUtil.getEmptyConstructor(ClientboundSetObjectivePacket.class);
public static final FieldAccessor<ClientboundSetObjectivePacket, String> OBJECTIVE_NAME_FIELD =
ReflectUtil.findField(ClientboundSetObjectivePacket.class, "d", String.class);
ReflectUtil.findField(ClientboundSetObjectivePacket.class, 0, String.class);
public static final FieldAccessor<ClientboundSetObjectivePacket, net.minecraft.network.chat.Component> OBJECTIVE_VALUE_FIELD =
ReflectUtil.findField(ClientboundSetObjectivePacket.class, "e", net.minecraft.network.chat.Component.class);
ReflectUtil.findField(ClientboundSetObjectivePacket.class, 0, net.minecraft.network.chat.Component.class);
public static final FieldAccessor<ClientboundSetObjectivePacket, ObjectiveCriteria.RenderType> OBJECTIVE_RENDER_TYPE_FIELD =
ReflectUtil.findField(ClientboundSetObjectivePacket.class, "f", ObjectiveCriteria.RenderType.class);
public static final FieldAccessor<ClientboundSetObjectivePacket, NumberFormat> OBJECTIVE_NUMBER_FORMAT_FIELD;
ReflectUtil.findField(ClientboundSetObjectivePacket.class, 0, ObjectiveCriteria.RenderType.class);
// Optional<NumberFormat> for 1.20.5+, NumberFormat for below
public static final FieldAccessor<ClientboundSetObjectivePacket, Object> OBJECTIVE_NUMBER_FORMAT_FIELD;
public static final FieldAccessor<ClientboundSetObjectivePacket, Integer> OBJECTIVE_MODE_FIELD =
ReflectUtil.findField(ClientboundSetObjectivePacket.class, new String[]{"g", "h"}, int.class);
ReflectUtil.findField(ClientboundSetObjectivePacket.class, 0, int.class);
public static final ConstructorAccessor<ClientboundSetDisplayObjectivePacket> DISPLAY_LEGACY_CONSTRUCTOR =
ReflectUtil.findConstructor(ClientboundSetDisplayObjectivePacket.class, int.class, Objective.class);
public static final ConstructorAccessor<ClientboundSetDisplayObjectivePacket> DISPLAY_1_20_1_CONSTRUCTOR =
ReflectUtil.findOptionalConstructor(ClientboundSetDisplayObjectivePacket.class, int.class, Objective.class);
public static final FieldAccessor<ClientboundSetDisplayObjectivePacket, String> DISPLAY_OBJECTIVE_NAME =
ReflectUtil.findField(ClientboundSetDisplayObjectivePacket.class, "b", String.class);
ReflectUtil.findField(ClientboundSetDisplayObjectivePacket.class, 0, String.class);
public static final ConstructorAccessor<ClientboundSetScorePacket> SCORE_LEGACY_CONSTRUCTOR =
ReflectUtil.findConstructor(ClientboundSetScorePacket.class, ServerScoreboard.Method.class, String.class, String.class, int.class);
public static final ConstructorAccessor<ClientboundSetScorePacket> SCORE_1_20_3_CONSTRUCTOR;
public static final Object SCORE_1_20_2_METHOD_CHANGE, SCORE_1_20_2_METHOD_REMOVE;
public static final ConstructorAccessor<ClientboundSetScorePacket> SCORE_1_20_2_CONSTRUCTOR;
public static final ConstructorAccessor<ClientboundSetPlayerTeamPacket> TEAM_PACKET_CONSTRUCTOR =
ReflectUtil.findConstructorOrThrow(ClientboundSetPlayerTeamPacket.class, String.class, int.class, Optional.class, Collection.class);
ReflectUtil.findConstructor(ClientboundSetPlayerTeamPacket.class, String.class, int.class, Optional.class, Collection.class);
public static final PacketConstructor<ClientboundSetPlayerTeamPacket.Parameters> PARAMETERS_CONSTRUCTOR =
ReflectUtil.getEmptyConstructor(ClientboundSetPlayerTeamPacket.Parameters.class);
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, Component> DISPLAY_NAME_FIELD =
ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, "a", net.minecraft.network.chat.Component.class);
ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 0, net.minecraft.network.chat.Component.class);
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, net.minecraft.network.chat.Component> PREFIX_FIELD =
ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, "b", net.minecraft.network.chat.Component.class);
ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 1, net.minecraft.network.chat.Component.class);
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, net.minecraft.network.chat.Component> SUFFIX_FIELD =
ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, "c", net.minecraft.network.chat.Component.class);
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, String> NAME_TAG_VISIBILITY_FIELD =
ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, "d", String.class);
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, String> COLLISION_RULE_FIELD =
ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, "e", String.class);
ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 2, net.minecraft.network.chat.Component.class);
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, String> NAME_TAG_VISIBILITY_FIELD_1_21_4;
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, String> COLLISION_RULE_FIELD_1_21_4;
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, Team.Visibility> NAME_TAG_VISIBILITY_FIELD_1_21_5;
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, Team.CollisionRule> COLLISION_RULE_FIELD_1_21_5;
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, ChatFormatting> COLOR_FIELD =
ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, "f", ChatFormatting.class);
ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 0, ChatFormatting.class);
public static final FieldAccessor<ClientboundSetPlayerTeamPacket.Parameters, Integer> OPTIONS_FIELD =
ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, "g", int.class);
@SuppressWarnings("rawtypes")
public static final FieldAccessor<ClientboundSetPlayerTeamPacket, Collection> ENTRIES_FIELD =
ReflectUtil.findField(ClientboundSetPlayerTeamPacket.class, "j", Collection.class);
ReflectUtil.findField(ClientboundSetPlayerTeamPacket.Parameters.class, 0, int.class);
private PacketAccessors() {
}
@@ -1,30 +1,23 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern;
import com.google.gson.JsonElement;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.translation.GlobalTranslator;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectivePacketAdapter;
import net.megavex.scoreboardlibrary.implementation.commons.LineRenderingStrategy;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketAdapterProvider;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamsPacketAdapter;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.objective.PaperObjectivePacketAdapter;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.objective.SpigotObjectivePacketAdapter;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.team.PaperTeamsPacketAdapterImpl;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.team.SpigotTeamsPacketAdapter;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.NativeAdventureUtil;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util.PacketUtil;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectivePacketAdapter;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamsPacketAdapter;
import net.minecraft.network.protocol.Packet;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Locale;
import java.util.Objects;
import static net.kyori.adventure.text.serializer.gson.GsonComponentSerializer.gson;
public class PacketAdapterProviderImpl implements PacketAdapterProvider, PacketSender<Packet<?>>, ComponentProvider {
public class PacketAdapterProviderImpl implements PacketAdapterProvider, PacketSender<Packet<?>> {
private boolean isNativeAdventure;
private final ComponentProvider componentProvider;
public PacketAdapterProviderImpl() {
try {
@@ -39,44 +32,31 @@ public class PacketAdapterProviderImpl implements PacketAdapterProvider, PacketS
}
} catch (ClassNotFoundException ignored) {
}
this.componentProvider = new ComponentProviderImpl(isNativeAdventure);
}
@Override
public @NotNull ObjectivePacketAdapter createObjectiveAdapter(@NotNull String objectiveName) {
return isNativeAdventure
? new PaperObjectivePacketAdapter(this, this, objectiveName)
: new SpigotObjectivePacketAdapter(this, this, objectiveName);
? new PaperObjectivePacketAdapter(this, componentProvider, objectiveName)
: new SpigotObjectivePacketAdapter(this, componentProvider, objectiveName);
}
@Override
public @NotNull TeamsPacketAdapter createTeamPacketAdapter(@NotNull String teamName) {
return isNativeAdventure
? new PaperTeamsPacketAdapterImpl(this, this, teamName)
: new SpigotTeamsPacketAdapter(this, this, teamName);
? new PaperTeamsPacketAdapterImpl(this, componentProvider, teamName)
: new SpigotTeamsPacketAdapter(this, componentProvider, teamName);
}
@Override
public boolean isLegacy(@NotNull Player player) {
return false;
public @NotNull LineRenderingStrategy lineRenderingStrategy(@NotNull Player player) {
return LineRenderingStrategy.MODERN;
}
@Override
public void sendPacket(@NotNull Player player, @NotNull Packet<?> packet) {
PacketUtil.sendPacket(player, packet);
}
@Override
public net.minecraft.network.chat.@NotNull Component fromAdventure(@NotNull Component adventure, @Nullable Locale locale) {
if (isNativeAdventure) {
return NativeAdventureUtil.fromAdventureComponent(adventure);
}
Component translated = adventure;
if (locale != null) {
translated = GlobalTranslator.render(adventure, locale);
}
JsonElement json = gson().serializeToTree(translated);
return Objects.requireNonNull(net.minecraft.network.chat.Component.Serializer.fromJson(json));
}
}
@@ -15,7 +15,6 @@ import net.minecraft.network.protocol.game.ClientboundResetScorePacket;
import net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket;
import net.minecraft.network.protocol.game.ClientboundSetObjectivePacket;
import net.minecraft.network.protocol.game.ClientboundSetScorePacket;
import net.minecraft.server.ServerScoreboard;
import net.minecraft.world.scores.criteria.ObjectiveCriteria;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@@ -23,6 +22,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Objects;
import java.util.Optional;
public abstract class AbstractObjectivePacketAdapter implements ObjectivePacketAdapter {
protected final PacketSender<Packet<?>> sender;
@@ -36,6 +36,11 @@ public abstract class AbstractObjectivePacketAdapter implements ObjectivePacketA
this.objectiveName = objectiveName;
}
@Override
public @NotNull String objectiveName() {
return objectiveName;
}
@Override
public void display(@NotNull Collection<Player> players, @NotNull ObjectiveDisplaySlot slot) {
sender.sendPacket(players, createDisplayPacket(slot));
@@ -44,7 +49,7 @@ public abstract class AbstractObjectivePacketAdapter implements ObjectivePacketA
@Override
public void remove(@NotNull Collection<Player> players) {
if (removePacket == null) {
this.removePacket = ReflectUtil.findEmptyConstructor(ClientboundSetObjectivePacket.class).invoke();
this.removePacket = ReflectUtil.getEmptyConstructor(ClientboundSetObjectivePacket.class).invoke();
PacketAccessors.OBJECTIVE_NAME_FIELD.set(removePacket, objectiveName);
PacketAccessors.OBJECTIVE_MODE_FIELD.set(removePacket, ObjectiveConstants.MODE_REMOVE);
}
@@ -54,26 +59,23 @@ public abstract class AbstractObjectivePacketAdapter implements ObjectivePacketA
@Override
public void removeScore(@NotNull Collection<Player> players, @NotNull String entry) {
Packet<?> packet;
try {
Class.forName("net.minecraft.network.protocol.game.ClientboundResetScorePacket"); // Added in 1.20.3
if (PacketAccessors.IS_1_20_3_OR_ABOVE) {
packet = new ClientboundResetScorePacket(entry, objectiveName);
} catch (ClassNotFoundException ignored) {
packet = Objects.requireNonNull(PacketAccessors.SCORE_LEGACY_CONSTRUCTOR)
.invoke(ServerScoreboard.Method.REMOVE, objectiveName, entry, 0);
} else {
packet = Objects.requireNonNull(PacketAccessors.SCORE_1_20_2_CONSTRUCTOR)
.invoke(PacketAccessors.SCORE_1_20_2_METHOD_REMOVE, objectiveName, entry, 0);
}
sender.sendPacket(players, packet);
}
protected @NotNull ClientboundSetDisplayObjectivePacket createDisplayPacket(@NotNull ObjectiveDisplaySlot displaySlot) {
ClientboundSetDisplayObjectivePacket packet;
try {
Class.forName("net.minecraft.world.scores.DisplaySlot"); // Added in 1.20.2
if (PacketAccessors.IS_1_20_2_OR_ABOVE) {
packet = new ClientboundSetDisplayObjectivePacket(DisplaySlotProvider.toNms(displaySlot), null);
} catch (ClassNotFoundException ignored) {
packet = Objects.requireNonNull(PacketAccessors.DISPLAY_LEGACY_CONSTRUCTOR)
} else {
packet = Objects.requireNonNull(PacketAccessors.DISPLAY_1_20_1_CONSTRUCTOR)
.invoke(ObjectiveConstants.displaySlotIndex(displaySlot), null);
}
PacketAccessors.DISPLAY_OBJECTIVE_NAME.set(packet, objectiveName);
return packet;
}
@@ -84,11 +86,14 @@ public abstract class AbstractObjectivePacketAdapter implements ObjectivePacketA
@Nullable net.minecraft.network.chat.Component nmsDisplay,
@Nullable Object numberFormat
) {
if (PacketAccessors.HAS_NUMBER_FORMAT) {
return new ClientboundSetScorePacket(entry, objectiveName, value, nmsDisplay, (NumberFormat) numberFormat);
if (PacketAccessors.IS_1_20_5_OR_ABOVE) {
return new ClientboundSetScorePacket(entry, objectiveName, value, Optional.ofNullable(nmsDisplay), Optional.ofNullable((NumberFormat) numberFormat));
} else if (PacketAccessors.IS_1_20_3_OR_ABOVE) {
return Objects.requireNonNull(PacketAccessors.SCORE_1_20_3_CONSTRUCTOR)
.invoke(entry, objectiveName, value, nmsDisplay, numberFormat);
} else {
return Objects.requireNonNull(PacketAccessors.SCORE_LEGACY_CONSTRUCTOR)
.invoke(ServerScoreboard.Method.CHANGE, objectiveName, entry, value);
return Objects.requireNonNull(PacketAccessors.SCORE_1_20_2_CONSTRUCTOR)
.invoke(PacketAccessors.SCORE_1_20_2_METHOD_CHANGE, objectiveName, entry, value);
}
}
@@ -103,9 +108,15 @@ public abstract class AbstractObjectivePacketAdapter implements ObjectivePacketA
PacketAccessors.OBJECTIVE_NAME_FIELD.set(packet, objectiveName);
PacketAccessors.OBJECTIVE_VALUE_FIELD.set(packet, nmsValue);
if (numberFormat != null) {
if (PacketAccessors.IS_1_20_3_OR_ABOVE) {
assert PacketAccessors.OBJECTIVE_NUMBER_FORMAT_FIELD != null;
PacketAccessors.OBJECTIVE_NUMBER_FORMAT_FIELD.set(packet, (NumberFormat) numberFormat);
Object value;
if (PacketAccessors.IS_1_20_5_OR_ABOVE) {
value = Optional.ofNullable(numberFormat);
} else {
value = numberFormat;
}
PacketAccessors.OBJECTIVE_NUMBER_FORMAT_FIELD.set(packet, value);
}
ObjectiveCriteria.RenderType nmsRenderType;
@@ -17,8 +17,6 @@ import org.jetbrains.annotations.Nullable;
import java.util.Collection;
public class PaperObjectivePacketAdapter extends AbstractObjectivePacketAdapter {
private AdventureComponent lastValue;
public PaperObjectivePacketAdapter(@NotNull PacketAdapterProviderImpl packetAdapter, @NotNull ComponentProvider componentProvider, @NotNull String objectiveName) {
super(packetAdapter, componentProvider, objectiveName);
}
@@ -39,14 +37,7 @@ public class PaperObjectivePacketAdapter extends AbstractObjectivePacketAdapter
@NotNull ObjectiveRenderType renderType,
@Nullable ScoreFormat scoreFormat
) {
AdventureComponent nmsValue;
if (this.lastValue != null && this.lastValue.adventure$component() == value) {
nmsValue = this.lastValue;
} else {
nmsValue = NativeAdventureUtil.fromAdventureComponent(value);
this.lastValue = nmsValue;
}
AdventureComponent nmsValue = NativeAdventureUtil.fromAdventureComponent(value);
Object numberFormat = ScoreFormatConverter.convert(componentProvider, null, scoreFormat);
ClientboundSetObjectivePacket packet = createObjectivePacket(packetType, nmsValue, renderType, numberFormat);
sender.sendPacket(players, packet);
@@ -1,11 +1,11 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.objective;
import com.google.gson.JsonElement;
import com.mojang.serialization.DataResult;
import com.mojang.serialization.JsonOps;
import net.megavex.scoreboardlibrary.api.objective.ScoreFormat;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.ComponentProvider;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.PacketAccessors;
import net.minecraft.Util;
import net.minecraft.network.chat.Style;
import net.minecraft.network.chat.numbers.BlankFormat;
import net.minecraft.network.chat.numbers.FixedFormat;
@@ -13,16 +13,30 @@ import net.minecraft.network.chat.numbers.StyledFormat;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Locale;
import java.util.Optional;
import static net.kyori.adventure.text.serializer.gson.GsonComponentSerializer.gson;
public final class ScoreFormatConverter {
private static final MethodHandle RESULT_UNWRAP_METHOD;
static {
try {
RESULT_UNWRAP_METHOD = MethodHandles.lookup().findVirtual(DataResult.class, "result", MethodType.methodType(Optional.class));
} catch (NoSuchMethodException | IllegalAccessException e) {
throw new RuntimeException(e);
}
}
private ScoreFormatConverter() {
}
public static @Nullable Object convert(@NotNull ComponentProvider componentProvider, @Nullable Locale locale, @Nullable ScoreFormat format) {
if (format == null || !PacketAccessors.HAS_NUMBER_FORMAT) {
if (format == null || !PacketAccessors.IS_1_20_3_OR_ABOVE) {
return null;
}
@@ -30,7 +44,14 @@ public final class ScoreFormatConverter {
return BlankFormat.INSTANCE;
} else if (format instanceof ScoreFormat.Styled) {
JsonElement json = gson().serializer().toJsonTree(((ScoreFormat.Styled) format).style());
Style style = Util.getOrThrow(Style.Serializer.CODEC.parse(JsonOps.INSTANCE, json), RuntimeException::new);
Object result = Style.Serializer.CODEC.parse(JsonOps.INSTANCE, json);
Style style;
try {
//noinspection unchecked,rawtypes
style = (Style) ((Optional) RESULT_UNWRAP_METHOD.invokeExact((DataResult) result)).orElseThrow(RuntimeException::new);
} catch (Throwable e) {
throw new RuntimeException(e);
}
return new StyledFormat(style);
} else if (format instanceof ScoreFormat.Fixed) {
return new FixedFormat(componentProvider.fromAdventure(((ScoreFormat.Fixed) format).content(), locale));
@@ -10,12 +10,11 @@ import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.EntriesPa
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamConstants;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamDisplayPacketAdapter;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamsPacketAdapter;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.PacketConstructor;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ReflectUtil;
import net.minecraft.ChatFormatting;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket;
import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket.Parameters;
import net.minecraft.world.scores.Team;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -24,9 +23,6 @@ import org.jetbrains.annotations.UnknownNullability;
import java.util.*;
public abstract class AbstractTeamsPacketAdapterImpl implements TeamsPacketAdapter {
static final PacketConstructor<Parameters> parametersConstructor =
ReflectUtil.findEmptyConstructor(Parameters.class);
protected final PacketSender<Packet<?>> sender;
protected final ComponentProvider componentProvider;
protected final String teamName;
@@ -72,16 +68,20 @@ public abstract class AbstractTeamsPacketAdapterImpl implements TeamsPacketAdapt
sender.sendPacket(players, createTeamsPacket(TeamConstants.mode(packetType), teamName, null, entries));
}
protected void fillTeamPacket(ClientboundSetPlayerTeamPacket packet, Collection<String> entries) {
PacketAccessors.ENTRIES_FIELD.set(packet, entries);
}
protected void fillParameters(@NotNull Parameters parameters, @UnknownNullability Locale locale) {
String nameTagVisibilityKey = properties.nameTagVisibility().key();
PacketAccessors.NAME_TAG_VISIBILITY_FIELD.set(parameters, nameTagVisibilityKey);
if (PacketAccessors.IS_1_21_5_OR_ABOVE) {
Objects.requireNonNull(PacketAccessors.NAME_TAG_VISIBILITY_FIELD_1_21_5)
.set(parameters, Team.Visibility.valueOf(properties.nameTagVisibility().name()));
String collisionRuleKey = properties.collisionRule().key();
PacketAccessors.COLLISION_RULE_FIELD.set(parameters, collisionRuleKey);
Objects.requireNonNull(PacketAccessors.COLLISION_RULE_FIELD_1_21_5)
.set(parameters, Team.CollisionRule.valueOf(properties.collisionRule().name()));
} else {
Objects.requireNonNull(PacketAccessors.NAME_TAG_VISIBILITY_FIELD_1_21_4)
.set(parameters, properties.nameTagVisibility().key());
Objects.requireNonNull(PacketAccessors.COLLISION_RULE_FIELD_1_21_4)
.set(parameters, properties.collisionRule().key());
}
char legacyChar = LegacyFormatUtil.getChar(properties.playerColor());
PacketAccessors.COLOR_FIELD.set(parameters, ChatFormatting.getByCode(legacyChar));
@@ -1,5 +1,6 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.team;
import com.google.common.collect.ImmutableList;
import net.kyori.adventure.text.Component;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.ImmutableTeamProperties;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender;
@@ -29,9 +30,9 @@ public class PaperTeamsPacketAdapterImpl extends AbstractTeamsPacketAdapterImpl
}
private class TeamDisplayPacketAdapterImpl extends AbstractTeamsPacketAdapterImpl.TeamDisplayPacketAdapterImpl {
private final ClientboundSetPlayerTeamPacket.Parameters parameters = parametersConstructor.invoke();
private final ClientboundSetPlayerTeamPacket createPacket = createTeamsPacket(TeamConstants.MODE_CREATE, teamName, parameters, null);
private final ClientboundSetPlayerTeamPacket updatePacket = createTeamsPacket(TeamConstants.MODE_UPDATE, teamName, parameters, null);
private final ClientboundSetPlayerTeamPacket.Parameters parameters = PacketAccessors.PARAMETERS_CONSTRUCTOR.invoke();
private ClientboundSetPlayerTeamPacket createPacket = null;
private ClientboundSetPlayerTeamPacket updatePacket = null;
private Component displayName, prefix, suffix;
public TeamDisplayPacketAdapterImpl(@NotNull ImmutableTeamProperties<Component> properties) {
@@ -39,14 +40,20 @@ public class PaperTeamsPacketAdapterImpl extends AbstractTeamsPacketAdapterImpl
}
@Override
public void updateTeamPackets(@NotNull Collection<String> entries) {
public void updateTeamPackets() {
fillParameters(parameters, null);
fillTeamPacket(createPacket, entries);
fillTeamPacket(updatePacket, entries);
createPacket = null;
updatePacket = null;
}
@Override
public void sendProperties(@NotNull PropertiesPacketType packetType, @NotNull Collection<Player> players) {
if (createPacket == null || updatePacket == null) {
Collection<String> entries = ImmutableList.copyOf(properties.syncedEntries());
createPacket = createTeamsPacket(TeamConstants.MODE_CREATE, teamName, parameters, entries);
updatePacket = createTeamsPacket(TeamConstants.MODE_UPDATE, teamName, parameters, entries);
}
switch (packetType) {
case CREATE:
sender.sendPacket(players, createPacket);
@@ -1,5 +1,6 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.team;
import com.google.common.collect.ImmutableList;
import net.kyori.adventure.text.Component;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.ImmutableTeamProperties;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender;
@@ -13,7 +14,6 @@ import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.UnknownNullability;
import java.util.Collection;
@@ -36,13 +36,14 @@ public class SpigotTeamsPacketAdapter extends AbstractTeamsPacketAdapterImpl {
@Override
public void sendProperties(@NotNull PropertiesPacketType packetType, @NotNull Collection<Player> players) {
Collection<String> entries = ImmutableList.copyOf(properties.syncedEntries());
LocalePacketUtil.sendLocalePackets(
sender,
players,
locale -> {
ClientboundSetPlayerTeamPacket.@NotNull Parameters parameters = parametersConstructor.invoke();
ClientboundSetPlayerTeamPacket.@NotNull Parameters parameters = PacketAccessors.PARAMETERS_CONSTRUCTOR.invoke();
fillParameters(parameters, locale);
return createTeamsPacket(TeamConstants.mode(packetType), teamName, parameters, properties.entries());
return createTeamsPacket(TeamConstants.mode(packetType), teamName, parameters, entries);
}
);
}
@@ -9,6 +9,7 @@ import org.bukkit.entity.Player;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
public final class PacketUtil {
private static final MethodHandle GET_HANDLE;
@@ -36,19 +37,20 @@ public final class PacketUtil {
throw new ExceptionInInitializerError(e);
}
MethodHandle playerConnection;
try {
playerConnection = lookup.findGetter(ServerPlayer.class, "c", ServerGamePacketListenerImpl.class);
} catch (NoSuchFieldException e) {
try {
playerConnection = lookup.findGetter(ServerPlayer.class, "b", ServerGamePacketListenerImpl.class);
} catch (NoSuchFieldException | IllegalAccessException ex) {
throw new ExceptionInInitializerError(ex);
MethodHandle playerConnection = null;
for (Field field : ServerPlayer.class.getFields()) {
if (field.getType() == ServerGamePacketListenerImpl.class) {
try {
playerConnection = lookup.unreflectGetter(field);
} catch (IllegalAccessException e) {
throw new ExceptionInInitializerError(e);
}
}
} catch (IllegalAccessException e) {
throw new ExceptionInInitializerError(e);
}
if (playerConnection == null) {
throw new ExceptionInInitializerError("failed to find player connection field");
}
PLAYER_CONNECTION = playerConnection;
MethodType sendMethodType = MethodType.methodType(void.class, Packet.class);
@@ -0,0 +1,31 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.modern.util;
import net.minecraft.core.RegistryAccess;
import org.bukkit.Bukkit;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public final class RegistryUtil {
public static final RegistryAccess MINECRAFT_REGISTRY;
static {
String cbPackage = Bukkit.getServer().getClass().getPackage().getName();
Class<?> craftRegistry;
try {
craftRegistry = Class.forName(cbPackage + ".CraftRegistry");
} catch (ClassNotFoundException e) {
throw new ExceptionInInitializerError(e);
}
try {
Method method = craftRegistry.getDeclaredMethod("getMinecraftRegistry");
MINECRAFT_REGISTRY = (RegistryAccess) method.invoke(null);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
throw new ExceptionInInitializerError(e);
}
}
private RegistryUtil() {
}
}
@@ -1,8 +1,9 @@
plugins {
id("net.megavex.scoreboardlibrary.publish-conventions")
id("net.megavex.scoreboardlibrary.base-conventions")
}
dependencies {
api(project(":scoreboard-library-api"))
api(project(":scoreboard-library-commons"))
compileOnly(libs.spigotApi)
}
@@ -1,5 +1,6 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter;
import com.google.common.collect.ImmutableList;
import net.kyori.adventure.text.format.NamedTextColor;
import net.megavex.scoreboardlibrary.api.team.enums.CollisionRule;
import net.megavex.scoreboardlibrary.api.team.enums.NameTagVisibility;
@@ -7,11 +8,10 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.Collections;
public interface ImmutableTeamProperties<T> {
default @NotNull Collection<String> entries() {
return Collections.emptySet();
default @NotNull Collection<String> syncedEntries() {
return ImmutableList.of();
}
@NotNull T displayName();
@@ -1,5 +1,6 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter;
import net.megavex.scoreboardlibrary.implementation.commons.LineRenderingStrategy;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectivePacketAdapter;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamsPacketAdapter;
import org.bukkit.entity.Player;
@@ -10,5 +11,5 @@ public interface PacketAdapterProvider {
@NotNull TeamsPacketAdapter createTeamPacketAdapter(@NotNull String teamName);
boolean isLegacy(@NotNull Player player);
@NotNull LineRenderingStrategy lineRenderingStrategy(@NotNull Player player);
}
@@ -28,11 +28,15 @@ public final class ObjectiveConstants {
}
public static int displaySlotIndex(@NotNull ObjectiveDisplaySlot slot) {
return displaySlotIndex(slot, true);
}
public static int displaySlotIndex(@NotNull ObjectiveDisplaySlot slot, boolean isAbove1_8) {
if (slot instanceof ObjectiveDisplaySlot.PlayerList) {
return 0;
}
if (slot instanceof ObjectiveDisplaySlot.Sidebar) {
if (slot instanceof ObjectiveDisplaySlot.Sidebar || (!isAbove1_8 && slot instanceof ObjectiveDisplaySlot.TeamSidebar)) {
return 1;
}
@@ -12,6 +12,8 @@ import org.jetbrains.annotations.Nullable;
import java.util.Collection;
public interface ObjectivePacketAdapter {
@NotNull String objectiveName();
void display(@NotNull Collection<Player> players, @NotNull ObjectiveDisplaySlot slot);
void sendProperties(
@@ -7,7 +7,7 @@ import org.jetbrains.annotations.NotNull;
import java.util.Collection;
public interface TeamDisplayPacketAdapter {
default void updateTeamPackets(@NotNull Collection<String> entries) {
default void updateTeamPackets() {
}
void sendEntries(@NotNull EntriesPacketType packetType, @NotNull Collection<Player> players, @NotNull Collection<String> entries);
@@ -0,0 +1,15 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect;
import org.bukkit.Bukkit;
import org.jetbrains.annotations.NotNull;
public final class MinecraftClasses {
private static final String CB_PACKAGE = Bukkit.getServer().getClass().getPackage().getName();
private MinecraftClasses() {
}
public static @NotNull String craftBukkit(String className) {
return CB_PACKAGE + '.' + className;
}
}
@@ -2,77 +2,116 @@ package net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import sun.misc.Unsafe;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.lang.reflect.Modifier;
public class ReflectUtil {
// Inspired by
// https://github.com/dmulloy2/ProtocolLib/blob/02e917cd08cf5b37a52052e22b223272a040e0df/src/main/java/com/comphenix/protocol/reflect/accessors/MethodHandleHelper.java
private static final Unsafe UNSAFE;
private static final Object UNSAFE;
private static final MethodHandles.Lookup LOOKUP;
private static final MethodHandle ALLOCATE_INSTANCE_HANDLE;
private static final MethodType VOID_METHOD_TYPE = MethodType.methodType(void.class);
private static final MethodType VIRTUAL_FIELD_SETTER = MethodType.methodType(void.class, Object.class, Object.class);
static {
MethodHandles.Lookup normalLookup = MethodHandles.lookup();
try {
Class<?> unsafeClass = Class.forName("sun.misc.Unsafe");
Field theUnsafe = unsafeClass.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
UNSAFE = (Unsafe) theUnsafe.get(null);
} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
UNSAFE = theUnsafe.get(null);
ALLOCATE_INSTANCE_HANDLE = normalLookup.findVirtual(UNSAFE.getClass(), "allocateInstance", MethodType.methodType(Object.class, Class.class));
} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException | NoSuchMethodException e) {
throw new ExceptionInInitializerError(e);
}
MethodHandles.Lookup lookup;
try {
MethodHandle staticFieldOffset = normalLookup.findVirtual(UNSAFE.getClass(), "staticFieldOffset", MethodType.methodType(long.class, Field.class));
MethodHandle staticFieldBase = normalLookup.findVirtual(UNSAFE.getClass(), "staticFieldBase", MethodType.methodType(Object.class, Field.class));
MethodHandle getObject = normalLookup.findVirtual(UNSAFE.getClass(), "getObject", MethodType.methodType(Object.class, Object.class, long.class));
Field trustedLookup = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
long offset = UNSAFE.staticFieldOffset(trustedLookup);
Object baseValue = UNSAFE.staticFieldBase(trustedLookup);
lookup = (MethodHandles.Lookup) UNSAFE.getObject(baseValue, offset);
} catch (NoSuchFieldException e) {
long offset = (long) staticFieldOffset.invoke(UNSAFE, trustedLookup);
Object baseValue = staticFieldBase.invoke(UNSAFE, trustedLookup);
lookup = (MethodHandles.Lookup) getObject.invoke(UNSAFE, baseValue, offset);
} catch (Throwable e) {
e.printStackTrace();
lookup = MethodHandles.lookup();
lookup = normalLookup;
}
LOOKUP = lookup;
}
private ReflectUtil() {
}
public static <T, V> @NotNull FieldAccessor<T, V> findField(@NotNull Class<T> clazz, @NotNull String name, @NotNull Class<V> valueClass) {
return findField(clazz, new String[]{name}, valueClass);
public static @NotNull Class<?> getClassOrThrow(String... names) {
Class<?> c = getOptionalClass(names);
if (c == null) {
throw new IllegalStateException("Class with names either of " + String.join(", ", names) + " not found");
}
return c;
}
public static <T, V> @NotNull FieldAccessor<T, V> findField(@NotNull Class<T> clazz, @NotNull String[] names, @NotNull Class<V> valueClass) {
public static @Nullable Class<?> getOptionalClass(String... names) {
for (String name : names) {
try {
MethodHandle setter = LOOKUP.findSetter(clazz, name, valueClass).asType(VIRTUAL_FIELD_SETTER);
return new FieldAccessor<>(setter);
} catch (NoSuchFieldException ignored) {
} catch (IllegalAccessException e) {
throw new IllegalStateException("couldn't get field accessor", e);
return Class.forName(name);
} catch (ClassNotFoundException ignored) {
}
}
throw new IllegalStateException("couldn't find field on class " + clazz.getSimpleName() + " with names " + Arrays.toString(names));
return null;
}
public static <T> @NotNull ConstructorAccessor<T> findConstructorOrThrow(@NotNull Class<T> clazz, @NotNull Class<?>... args) {
ConstructorAccessor<T> accessor = findConstructor(clazz, args);
if (accessor == null) {
throw new IllegalStateException("couldn't get constructor accessor for class " + clazz.getSimpleName());
public static @NotNull Object getEnumInstance(@NotNull Class<?> clazz, @NotNull String... names) {
for (String name : names) {
try {
//noinspection unchecked,rawtypes
return Enum.valueOf((Class<? extends Enum>) clazz, name);
} catch (IllegalArgumentException ignored){
}
}
throw new IllegalStateException("Enum " + clazz.getName() + " instance with names either of " + String.join(",", names) + " not found");
}
public static <T, V> @NotNull FieldAccessor<T, V> findField(@NotNull Class<T> clazz, int index, @NotNull Class<V> valueClass) {
return findFieldUnchecked(clazz, index, valueClass);
}
public static <T, V> @NotNull FieldAccessor<T, V> findFieldUnchecked(@NotNull Class<?> clazz, int index, @NotNull Class<?> valueClass) {
int i = 0;
for (Field field : clazz.getDeclaredFields()) {
if (Modifier.isStatic(field.getModifiers()) || field.getType() != valueClass) {
continue;
}
if (i == index) {
try {
MethodHandle setter = LOOKUP.unreflectSetter(field);
return new FieldAccessor<>(setter.asType(VIRTUAL_FIELD_SETTER));
} catch (IllegalAccessException e) {
throw new RuntimeException("failed to unreflect field setter", e);
}
}
i++;
}
return accessor;
throw new IllegalStateException(
"couldn't find field with class " + valueClass.getSimpleName() + " on " + clazz.getSimpleName() + " at index " + index
);
}
public static <T> @Nullable ConstructorAccessor<T> findConstructor(@NotNull Class<T> clazz, @NotNull Class<?>... args) {
public static <T> @Nullable ConstructorAccessor<T> findOptionalConstructor(@NotNull Class<T> clazz, @NotNull Class<?>... args) {
try {
MethodHandle handle = LOOKUP.findConstructor(clazz, MethodType.methodType(void.class, args));
return new ConstructorAccessor<>(convertToGeneric(handle));
@@ -81,7 +120,15 @@ public class ReflectUtil {
}
}
public static <T> @NotNull PacketConstructor<T> findEmptyConstructor(@NotNull Class<T> packetClass) {
public static <T> @NotNull ConstructorAccessor<T> findConstructor(@NotNull Class<T> clazz, @NotNull Class<?>... args) {
ConstructorAccessor<T> accessor = findOptionalConstructor(clazz, args);
if (accessor == null) {
throw new RuntimeException("Constructor for class " + clazz.getName() + " not found");
}
return accessor;
}
public static <T> @NotNull PacketConstructor<T> getEmptyConstructor(@NotNull Class<T> packetClass) {
try {
MethodHandle constructor = LOOKUP.findConstructor(packetClass, VOID_METHOD_TYPE);
return () -> {
@@ -98,7 +145,7 @@ public class ReflectUtil {
return () -> {
try {
// noinspection unchecked
return (T) UNSAFE.allocateInstance(packetClass);
return (T) ALLOCATE_INSTANCE_HANDLE.invoke(UNSAFE, packetClass);
} catch (Throwable e) {
throw new IllegalStateException("couldn't allocate packet instance using Unsafe", e);
}
+3 -3
View File
@@ -1,13 +1,13 @@
plugins {
id("net.megavex.scoreboardlibrary.publish-conventions")
id("net.megavex.scoreboardlibrary.base-conventions")
}
repositories {
maven("https://repo.papermc.io/repository/maven-public/")
maven("https://repo.codemc.io/repository/maven-releases/")
}
dependencies {
compileOnly(project(":scoreboard-library-packet-adapter-base"))
compileOnly("com.github.retrooper.packetevents:spigot:2.2.1")
compileOnly(libs.packetEvents)
compileOnly(libs.spigotApi)
}
@@ -1,7 +1,7 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.packetevents;
import com.github.retrooper.packetevents.PacketEventsAPI;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerDisplayScoreboard;
import com.github.retrooper.packetevents.wrapper.play.server.WrapperPlayServerResetScore;
@@ -37,6 +37,11 @@ public class ObjectivePacketAdapterImpl implements ObjectivePacketAdapter {
this.objectiveName = objectiveName;
}
@Override
public @NotNull String objectiveName() {
return objectiveName;
}
@Override
public void display(@NotNull Collection<Player> players, @NotNull ObjectiveDisplaySlot slot) {
WrapperPlayServerDisplayScoreboard packet = new WrapperPlayServerDisplayScoreboard(ObjectiveConstants.displaySlotIndex(slot), objectiveName);
@@ -107,12 +112,10 @@ public class ObjectivePacketAdapterImpl implements ObjectivePacketAdapter {
objectiveName
);
for (Player player : players) {
if (packetEvents.getPlayerManager().getClientVersion(player).isNewerThanOrEquals(ClientVersion.V_1_20_3)) {
provider.sendPacket(players, newPacket);
} else {
provider.sendPacket(players, oldPacket);
}
if (packetEvents.getServerManager().getVersion().isNewerThanOrEquals(ServerVersion.V_1_20_3)) {
provider.sendPacket(players, newPacket);
} else {
provider.sendPacket(players, oldPacket);
}
}
@@ -2,52 +2,55 @@ package net.megavex.scoreboardlibrary.implementation.packetAdapter.packetevents;
import com.github.retrooper.packetevents.PacketEvents;
import com.github.retrooper.packetevents.PacketEventsAPI;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectivePacketAdapter;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import net.megavex.scoreboardlibrary.implementation.commons.LineRenderingStrategy;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketAdapterProvider;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamsPacketAdapter;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.objective.ObjectivePacketAdapter;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.packetevents.team.TeamsPacketAdapterImpl;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.team.TeamsPacketAdapter;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@SuppressWarnings("unused")
public class PacketAdapterProviderImpl implements PacketAdapterProvider, PacketSender<PacketWrapper<?>> {
public class PacketAdapterProviderImpl implements PacketAdapterProvider {
private final PacketEventsAPI<?> packetEvents;
private final PacketEventsSender packetSender;
public PacketAdapterProviderImpl() {
this.packetEvents = PacketEvents.getAPI();
if (this.packetEvents == null) {
throw new IllegalStateException("PacketEvents exists in classpath but isn't loaded");
}
this.packetSender = new PacketEventsSender(this.packetEvents);
}
@Override
public @NotNull TeamsPacketAdapter createTeamPacketAdapter(@NotNull String teamName) {
return new TeamsPacketAdapterImpl(this, teamName);
return new TeamsPacketAdapterImpl(packetSender, teamName);
}
@Override
public @NotNull ObjectivePacketAdapter createObjectiveAdapter(@NotNull String objectiveName) {
return new ObjectivePacketAdapterImpl(this, packetEvents, objectiveName);
return new ObjectivePacketAdapterImpl(packetSender, packetEvents, objectiveName);
}
@Override
public boolean isLegacy(@NotNull Player player) {
return packetEvents
.getPlayerManager()
.getClientVersion(player)
.isOlderThanOrEquals(ClientVersion.V_1_12_2);
}
public @NotNull LineRenderingStrategy lineRenderingStrategy(@NotNull Player player) {
ServerVersion serverVer = packetEvents.getServerManager().getVersion();
return serverVer.isNewerThanOrEquals(ServerVersion.V_1_13) ? LineRenderingStrategy.MODERN : LineRenderingStrategy.LEGACY;
public void sendPacket(@NotNull Player player, @NotNull PacketWrapper<?> packet) {
packetEvents.getPlayerManager().sendPacket(player, packet);
}
public void sendPacket(@NotNull Iterable<Player> players, @NotNull PacketWrapper<?> packet) {
for (Player player : players) {
sendPacket(player, packet);
// Waiting for packetevents to fix https://github.com/retrooper/packetevents/issues/1024
/*
if (serverVer.isOlderThan(ServerVersion.V_1_13)) {
return LineRenderingStrategy.LEGACY;
}
if (Bukkit.getServer().getPluginManager().isPluginEnabled("ProtocolSupport")) {
return LineRenderingStrategy.MODERN;
}
ClientVersion clientVer = packetEvents.getPlayerManager().getClientVersion(player);
return clientVer.isNewerThanOrEquals(ClientVersion.V_1_13) ? LineRenderingStrategy.MODERN : LineRenderingStrategy.LEGACY;
*/
}
}
@@ -0,0 +1,65 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.packetevents;
import com.github.retrooper.packetevents.PacketEventsAPI;
import com.github.retrooper.packetevents.manager.server.ServerVersion;
import com.github.retrooper.packetevents.netty.buffer.ByteBufHelper;
import com.github.retrooper.packetevents.netty.channel.ChannelHelper;
import com.github.retrooper.packetevents.protocol.player.ClientVersion;
import com.github.retrooper.packetevents.protocol.player.User;
import com.github.retrooper.packetevents.wrapper.PacketWrapper;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.PacketSender;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
public class PacketEventsSender implements PacketSender<PacketWrapper<?>> {
private final PacketEventsAPI<?> packetEvents;
public PacketEventsSender(@NotNull PacketEventsAPI<?> packetEvents) {
this.packetEvents = packetEvents;
}
//@SuppressWarnings("UnstableApiUsage, deprecation")
@Override
public void sendPacket(Player player, PacketWrapper<?> packet) {
packetEvents.getPlayerManager().sendPacket(player, packet);
/*
ServerVersion serverVer = packetEvents.getServerManager().getVersion();
ClientVersion clientVer = packetEvents.getPlayerManager().getClientVersion(player);
if (serverVer.isOlderThan(ServerVersion.V_1_13) || clientVer.isNewerThanOrEquals(ClientVersion.V_1_13)) {
packetEvents.getPlayerManager().sendPacket(player, packet);
return;
}
// This is a hack to send packets directly skipping plugins such as ViaVersion and ViaRewind
// Based on https://discord.com/channels/721686193061888071/755472595096174873/1193239879560212520
// (PacketEvents Discord)
User user = packetEvents.getPlayerManager().getUser(player);
if (user == null) {
return;
}
Object channel = user.getChannel();
if (!ChannelHelper.isOpen(channel)) {
return;
}
String viaEncoder = "via-encoder";
if (ChannelHelper.getPipelineHandler(channel, viaEncoder) == null) {
packetEvents.getPlayerManager().sendPacket(player, packet);
return;
}
packet.buffer = ChannelHelper.pooledByteBuf(channel);
int id = Objects.requireNonNull(packet.getPacketTypeData().getPacketType()).getId(user.getClientVersion());
ByteBufHelper.writeVarInt(packet.buffer, id);
packet.setServerVersion(clientVer.toServerVersion());
packet.write();
ChannelHelper.writeAndFlushInContext(channel, viaEncoder, packet.buffer);
*/
}
}
@@ -47,7 +47,7 @@ public class AdventureTeamDisplayPacketAdapter extends AbstractTeamDisplayPacket
teamName,
mode(packetType),
info,
properties.entries()
properties.syncedEntries()
);
});
}
@@ -24,6 +24,10 @@ public class WrapperPlayServerTeamsLegacy extends PacketWrapper<WrapperPlayServe
@Override
public void write() {
if (serverVersion.isNewerThanOrEquals(ServerVersion.V_1_13)) {
throw new IllegalStateException("Tried to serialize legacy teams packet on 1.13+ server version");
}
writeString(teamName, 16);
writeByte(teamMode.ordinal());
writeString(properties.displayName());
@@ -43,12 +47,12 @@ public class WrapperPlayServerTeamsLegacy extends PacketWrapper<WrapperPlayServe
if (teamMode == WrapperPlayServerTeams.TeamMode.CREATE) {
if (serverVersion == ServerVersion.V_1_7_10) {
writeShort(properties.entries().size());
writeShort(properties.syncedEntries().size());
} else {
writeVarInt(properties.entries().size());
writeVarInt(properties.syncedEntries().size());
}
for (String entry : properties.entries()) {
for (String entry : properties.syncedEntries()) {
writeString(entry);
}
}
+3 -8
View File
@@ -1,13 +1,8 @@
plugins {
id("net.megavex.scoreboardlibrary.publish-conventions")
}
repositories {
maven("https://nexus.funkemunky.cc/content/repositories/releases/")
id("net.megavex.scoreboardlibrary.base-conventions")
}
dependencies {
compileOnly(project(":scoreboard-library-packet-adapter-base"))
compileOnly("org.github.spigot:1.8.8:1.8.8")
testImplementation("org.github.spigot:1.8.8:1.8.8")
// Alias to legacy for backwards compatibility
implementation(project(":scoreboard-library-legacy"))
}
@@ -1,53 +0,0 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.v1_8_R3;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.FieldAccessor;
import net.megavex.scoreboardlibrary.implementation.packetAdapter.util.reflect.ReflectUtil;
import net.minecraft.server.v1_8_R3.*;
import java.util.Collection;
public final class PacketAccessors {
public static final FieldAccessor<PacketPlayOutScoreboardObjective, String> OBJECTIVE_NAME_FIELD =
ReflectUtil.findField(PacketPlayOutScoreboardObjective.class, "a", String.class);
public static final FieldAccessor<PacketPlayOutScoreboardObjective, String> OBJECTIVE_DISPLAY_NAME_FIELD =
ReflectUtil.findField(PacketPlayOutScoreboardObjective.class, "b", String.class);
public static final FieldAccessor<PacketPlayOutScoreboardObjective, IScoreboardCriteria.EnumScoreboardHealthDisplay> OBJECTIVE_HEALTH_DISPLAY_FIELD =
ReflectUtil.findField(PacketPlayOutScoreboardObjective.class, "c", IScoreboardCriteria.EnumScoreboardHealthDisplay.class);
public static final FieldAccessor<PacketPlayOutScoreboardObjective, Integer> OBJECTIVE_MODE_FIELD =
ReflectUtil.findField(PacketPlayOutScoreboardObjective.class, "d", int.class);
public static final FieldAccessor<PacketPlayOutScoreboardDisplayObjective, Integer> DISPLAY_OBJECTIVE_POSITION =
ReflectUtil.findField(PacketPlayOutScoreboardDisplayObjective.class, "a", int.class);
public static final FieldAccessor<PacketPlayOutScoreboardDisplayObjective, String> DISPLAY_OBJECTIVE_NAME =
ReflectUtil.findField(PacketPlayOutScoreboardDisplayObjective.class, "b", String.class);
public static final FieldAccessor<PacketPlayOutScoreboardScore, String> SCORE_OBJECTIVE_NAME_FIELD =
ReflectUtil.findField(PacketPlayOutScoreboardScore.class, "b", String.class);
public static final FieldAccessor<PacketPlayOutScoreboardScore, Integer> SCORE_VALUE_FIELD =
ReflectUtil.findField(PacketPlayOutScoreboardScore.class, "c", int.class);
public static final FieldAccessor<PacketPlayOutScoreboardScore, PacketPlayOutScoreboardScore.EnumScoreboardAction> SCORE_ACTION_FIELD =
ReflectUtil.findField(PacketPlayOutScoreboardScore.class, "d", PacketPlayOutScoreboardScore.EnumScoreboardAction.class);
public static final FieldAccessor<PacketPlayOutScoreboardTeam, String> TEAM_NAME_FIELD =
ReflectUtil.findField(PacketPlayOutScoreboardTeam.class, "a", String.class);
public static final FieldAccessor<PacketPlayOutScoreboardTeam, String> TEAM_DISPLAY_NAME_FIELD =
ReflectUtil.findField(PacketPlayOutScoreboardTeam.class, "b", String.class);
public static final FieldAccessor<PacketPlayOutScoreboardTeam, String> TEAM_PREFIX_FIELD =
ReflectUtil.findField(PacketPlayOutScoreboardTeam.class, "c", String.class);
public static final FieldAccessor<PacketPlayOutScoreboardTeam, String> TEAM_SUFFIX_FIELD =
ReflectUtil.findField(PacketPlayOutScoreboardTeam.class, "d", String.class);
public static final FieldAccessor<PacketPlayOutScoreboardTeam, String> TEAM_NAME_TAG_VISIBILITY_FIELD =
ReflectUtil.findField(PacketPlayOutScoreboardTeam.class, "e", String.class);
public static final FieldAccessor<PacketPlayOutScoreboardTeam, Integer> TEAM_COLOR_FIELD =
ReflectUtil.findField(PacketPlayOutScoreboardTeam.class, "f", int.class);
@SuppressWarnings("rawtypes")
public static final FieldAccessor<PacketPlayOutScoreboardTeam, Collection> TEAM_ENTRIES_FIELD =
ReflectUtil.findField(PacketPlayOutScoreboardTeam.class, "g", Collection.class);
public static final FieldAccessor<PacketPlayOutScoreboardTeam, Integer> TEAM_MODE_FIELD =
ReflectUtil.findField(PacketPlayOutScoreboardTeam.class, "h", int.class);
public static final FieldAccessor<PacketPlayOutScoreboardTeam, Integer> TEAM_RULES_FIELD =
ReflectUtil.findField(PacketPlayOutScoreboardTeam.class, "i", int.class);
private PacketAccessors() {
}
}
@@ -1,16 +0,0 @@
package net.megavex.scoreboardlibrary.implementation.packetAdapter.v1_8_R3;
import net.kyori.adventure.text.format.NamedTextColor;
import net.minecraft.server.v1_8_R3.EnumChatFormat;
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.assertNotNull;
class ColorNameTest {
@Test
void colorNameTest() {
for (String key : NamedTextColor.NAMES.keys()) {
assertNotNull(EnumChatFormat.b(key));
}
}
}