Compare commits

...
149 Commits
Author SHA1 Message Date
NEZNAMY 189797aa73 Bukkit - Use the other constructor for player info packet which is cleaner (also #797) 2023-01-21 00:18:19 +01:00
NEZNAMY 1ae2ef7cad Fix compilation 2023-01-21 00:17:02 +01:00
NEZNAMY de14e14d33 Revert "Placeholders - Add timeout for placeholders to return value, causing TAB to freeze"
This reverts commit 37caed3123.
2023-01-19 22:30:09 +01:00
NEZNAMY 19892c14c1 Revert "Placeholders - Increase placeholder retrieve timeout to 500ms (#792)"
This reverts commit 6d85875fde.
2023-01-19 22:29:59 +01:00
NEZNAMY 2e00c4c002 Revert "NameTags - Fix error that doesn't make sense (#794)"
This reverts commit 12f5dd52b4.
2023-01-19 22:29:52 +01:00
NEZNAMY 97aa367629 Revert "Fix thread pool not being single threaded (maybe #781?)"
This reverts commit 87cc9a030b.
2023-01-19 22:28:22 +01:00
NEZNAMY 87cc9a030b Fix thread pool not being single threaded (maybe #781?) 2023-01-19 22:01:11 +01:00
NEZNAMY 8ba83d1cb1 BossBar - Fix errors when progress is below 0 or above 100 2023-01-19 16:59:05 +01:00
NEZNAMY 9f0e60bd47 Per world PlayerList - Remove redundant task schedule to main thread 2023-01-19 16:58:45 +01:00
NEZNAMY 93b2a25b2e Merge pull request #795 from Chris6ix/update-actions
Update Actions
2023-01-17 22:01:37 +01:00
Chris 4242d7a839 Remove Test Build Trigger 2023-01-17 21:33:33 +01:00
Chris 0ddb99bd46 Trigger Test Build 2023-01-17 21:29:34 +01:00
Chris 969672e3ad Update Actions 2023-01-17 21:26:40 +01:00
NEZNAMY 2423fbb150 Update README.md 2023-01-17 20:28:47 +01:00
NEZNAMY a99928dbf5 Placeholders - Colorize output of relational placeholders as well 2023-01-17 20:25:06 +01:00
NEZNAMY 12f5dd52b4 NameTags - Fix error that doesn't make sense (#794) 2023-01-17 15:38:20 +01:00
NEZNAMY 2bd105446e Bukkit - Properly send RGB messages into console (only for Paper+, Spigot doesn't support RGB at all) 2023-01-17 14:11:51 +01:00
NEZNAMY 6d85875fde Placeholders - Increase placeholder retrieve timeout to 500ms (#792) 2023-01-16 18:49:23 +01:00
NEZNAMY 42810aa490 3.2.3-SNAPSHOT 2023-01-16 18:47:42 +01:00
NEZNAMY bd31521bd3 3.2.2 2023-01-15 12:02:05 +01:00
NEZNAMY ec0deb8378 Yellow Number - Disable the feature for Bedrock players 2023-01-12 21:57:56 +01:00
NEZNAMY ae66b6e9a1 RGB - Fix MiniMessage->Bukkit conversion being after Bukkit->TAB, making it not translate anymore 2023-01-12 14:55:41 +01:00
NEZNAMY 3b86becebd Header/Footer - Do not add &r after \n anymore 2023-01-12 14:53:38 +01:00
NEZNAMY 4a8d86825b Configuration - Small changes to default configuration files 2023-01-10 18:56:34 +01:00
NEZNAMY 54a3bd81b4 Global PlayerList - Increase player removal prevention delay to 2000ms from 500ms 2023-01-10 16:49:16 +01:00
NEZNAMY b6768cca99 CPU manager - Add try/catch for task scheduling (#781) 2023-01-10 16:48:28 +01:00
NEZNAMY 60c414deed Bukkit - Remove redundant methods in nms loader 2023-01-05 16:48:16 +01:00
NEZNAMY 009283a83f Update README.md 2023-01-05 16:04:38 +01:00
Callum Seabrook 4a430b97df Merge pull request #779 from JT122406/master
Updates
2023-01-04 22:50:08 +00:00
NEZNAMY 722333da2e Fix RejectedExecutionException (#769) 2023-01-04 19:15:56 +01:00
NEZNAMY 1215af5262 Bukkit - Add 1.5.0 support 2023-01-04 18:28:24 +01:00
NEZNAMY 254088c37b Bukkit - Repackage nms loaders (also fix startup on Thermos) 2023-01-04 18:14:39 +01:00
J.T. McQuiggandCallum Seabrook cb2ce4acca Update gradle/libs.versions.toml
Co-authored-by: Callum Seabrook <callum.seabrook@prevarinite.com>
2023-01-04 11:20:55 -05:00
J.T.McQuigg 872adca8fd Updates 2023-01-03 23:09:35 -05:00
J.T.McQuigg f37cc0bdd8 Gradle 7.6 2023-01-03 23:02:04 -05:00
NEZNAMY 4063578ac3 Code style - Remove duplicate .toLowerCase() 2023-01-02 20:45:23 +01:00
NEZNAMY 2528de7eeb Code style - Use built-in String#trim function 2023-01-02 20:25:11 +01:00
NEZNAMY 37caed3123 Placeholders - Add timeout for placeholders to return value, causing TAB to freeze 2022-12-23 14:49:31 +01:00
NEZNAMY 27fe9dfb12 Commands - Make player command update player instantly if uuid is used instead of player name 2022-12-21 22:00:58 +01:00
NEZNAMY 0169cbd4d2 Bukkit - Fix wrong method taken sometimes, causing player kick with unlimited nametag mode 2022-12-20 15:08:18 +01:00
NEZNAMY ed760aec08 Aligned PlayerList - Fix rare error that makes no sense 2022-12-20 15:04:48 +01:00
NEZNAMY 858cbcefeb Code style - Add toString override 2022-12-20 15:04:21 +01:00
NEZNAMY 8a87d581a9 Scoreboard - Do not send packets to players who cannot see that specific scoreboard 2022-12-18 17:16:26 +01:00
NEZNAMY 6d642f4e51 NameTags - Fix players getting unregistered with wrong team name 2022-12-17 19:12:00 +01:00
NEZNAMY aca63640bc Attempt to fix rare NPE 2022-12-17 19:08:32 +01:00
NEZNAMY 1845a35315 3.2.2-SNAPSHOT 2022-12-16 22:25:39 +01:00
NEZNAMY cab5f89a95 Remove redundant check 2022-12-16 22:03:21 +01:00
NEZNAMY 1777d8bf01 Remove redundant try/catch 2022-12-16 21:14:47 +01:00
NEZNAMY 91b0bfaa9d Configuration - Simplify file copying, since default files already are encoded with UTF-8 encoding 2022-12-16 15:04:52 +01:00
NEZNAMY f1cf994d10 3.2.1 2022-12-16 11:27:48 +01:00
NEZNAMY 69957be933 Pet name fix - Fix excessive CPU usage caused by heavy operation and not caching the result 2022-12-15 00:22:23 +01:00
NEZNAMY b78eb5efb6 Bukkit - Remove forgotten debug stack trace printing 2022-12-15 00:21:28 +01:00
NEZNAMY 3577ad1964 Bukkit - Fix NPE in packet creation on <1.13 (#763) 2022-12-14 18:02:04 +01:00
NEZNAMY 4510491177 3.2.0 2022-12-12 09:46:21 +01:00
NEZNAMY 01b3a12377 1.19.3 support 2022-12-12 09:46:12 +01:00
NEZNAMY 64ae9863fb Bukkit - Drop old event API system 2022-12-10 12:27:51 +01:00
NEZNAMY ecd315a407 Sorting - Fix error from previous commit (.toLowerCase made string longer) 2022-12-08 11:20:58 +01:00
NEZNAMY 678eed288e Conditions - Add OR type for anonymous conditions using "|" symbol 2022-12-06 10:32:41 +01:00
NEZNAMY 9510455cb1 Conditions - Fix anonymous conditions not updating from previous commit 2022-12-06 10:31:29 +01:00
NEZNAMY e925ce23d0 Conditions - Fix nested conditions not working if they are defined below the condition using them 2022-12-06 09:51:16 +01:00
NEZNAMY c68cad6a1e Global PlayerList - Fix feature breaking nick/skin changes (minecraft mechanic changed in 1.19) (#752) 2022-12-06 09:50:00 +01:00
NEZNAMY 01d6eda4fb Configuration - Use luckperms placeholders instead of vault in default configuration (and make them return empty value if LP is not found) 2022-12-04 14:19:21 +01:00
NEZNAMY 73e7119bbc Configuration - add hidden server-name option for %server% value (useful with MySQL) 2022-12-04 11:49:59 +01:00
NEZNAMY cea781382a Sorting - Fix wrong team name being unregistered (#727) 2022-12-03 21:18:38 +01:00
NEZNAMY cf25b84e86 Commands - Fix debug command showing sorting is disabled even if layout is enabled for sorting 2022-12-03 21:16:29 +01:00
NEZNAMY 86e1d22390 Sorting - Only use 1 character for sorting by groups, permissions and predefined values; Improve debug output of sorting results 2022-12-03 12:16:38 +01:00
NEZNAMY 1625be111f Global PlayerList - Make server names in server groups and spy-servers not case-sensitive 2022-12-02 23:18:40 +01:00
NEZNAMY 00cda3b2aa Placeholders - Load all used placeholders on join in placeholder manager to make cpu usage appear under that "feature" instead of a feature actually using the placeholders 2022-12-01 22:46:40 +01:00
NEZNAMY 85a00a4436 Code style - Extract minimum placeholder refresh interval into an editable constant 2022-12-01 22:36:33 +01:00
NEZNAMY 37aec1a497 Global PlayerList - Add %playerlist-group_<group>% placeholder for online count in each group 2022-12-01 22:32:43 +01:00
NEZNAMY ef637616ee Placeholders - Improve error message 2022-11-27 17:13:24 +01:00
NEZNAMY af3b5067fb BossBar - Ignore players in disabled worlds/servers in announce command (#748) 2022-11-25 11:23:48 +01:00
NEZNAMY 53e5b15f96 Tab Expansion - Fix error in nested placeholder parsing 2022-11-24 11:01:55 +01:00
NEZNAMY 6a3afae607 Implement empty method 2022-11-16 12:24:03 +01:00
NEZNAMY 2e05c7d9d1 Another attempt for #727 2022-11-15 19:15:49 +01:00
NEZNAMY fb698a04b7 Configuration - Merge default config.yml files into one 2022-11-12 19:11:36 +01:00
NEZNAMY 52bdf30ddb Merge remote-tracking branch 'origin/master' 2022-11-09 22:34:22 +01:00
NEZNAMY a92f07aa40 Tab Expansion - Resend values to bridge on server switch (#740) 2022-11-09 22:34:09 +01:00
NEZNAMY 003ff7f99d Merge pull request #739 from CoachYT1/patch-1
bump org.yaml:snakeyaml to 1.33
2022-11-09 21:59:08 +01:00
NEZNAMY 47b9806ae7 Tab Expansion - Parse nested placeholders in replace function 2022-11-07 22:26:17 +01:00
CoachYT d2a7c65e08 bump org.yaml:snakeyaml to 1.33
snakeyaml 1.30 is affected by 5 vulnerabilities and should be upgraded to the latest version
2022-11-07 17:53:24 +01:00
NEZNAMY e232336a5f Add check for modifying non-existing teams (#727) 2022-11-06 22:42:23 +01:00
NEZNAMY 8816c75212 BungeeCord - Fix crash on 1.7 caused by unregistering non-existing objectives (login packet cleared them) (#727) 2022-11-04 13:31:34 +01:00
NEZNAMY f9d54751ba Fix rare ConcurrentModificationException 2022-11-04 12:53:00 +01:00
NEZNAMY 43d41ee761 BungeeCord - Improve unsupported BungeeCord build message 2022-11-01 17:55:43 +01:00
NEZNAMY 4512ba8d5e NameTags - Print a console warn if both unlimited nametags and invisible nametags are enabled 2022-10-30 17:56:00 +01:00
NEZNAMY 8b55bca564 Remove redundant variable 2022-10-30 13:40:32 +01:00
NEZNAMY d8d54970d3 Code style - Clean up vanish check code 2022-10-30 13:29:45 +01:00
NEZNAMY 15ea4482f9 Bukkit - Drop direct Essentials hook for vanishing as it supports metadata 2022-10-30 13:27:24 +01:00
NEZNAMY 87f9f38eac Update issue templates 2022-10-29 18:35:11 +02:00
NEZNAMY dba559f041 Layout - Fix startup error caused by invalid number as slot 2022-10-29 12:17:04 +02:00
NEZNAMY c610b6f323 PlayerList - Fix name applying on server switch when pipeline injection is disabled 2022-10-26 17:36:08 +02:00
NEZNAMY a5fb900e19 API - Add back TabPlayer#getTeamName() 2022-10-24 14:05:39 +02:00
NEZNAMY 09d6e455f5 Layout - Implement a fix for compatibility with plugins which add empty player name into their 2022-10-21 19:55:59 +02:00
NEZNAMY e68097b0f4 Layout - Fix startup error if slots is a single number instead of interval 2022-10-16 15:32:26 +02:00
NEZNAMY 0744fed953 Merge branch 'master' of https://github.com/NEZNAMY/TAB 2022-10-16 14:40:42 +02:00
NEZNAMY de0adf4067 MySQL - Attempt to fix startup error (#717) 2022-10-16 13:12:54 +02:00
NEZNAMY 76bf2b3efc Update links in README 2022-10-16 13:11:56 +02:00
NEZNAMY c089badeb4 Configuration - Remove owner and admin settings from default configuration 2022-10-15 19:15:38 +02:00
NEZNAMY b3b99d366a Placeholders - Fix relational placeholders updating causing normal placeholders to not refresh 2022-10-14 19:28:05 +02:00
NEZNAMY 4a36486f65 Bukkit - Fix startup on 1.8 2022-10-08 12:41:05 +02:00
NEZNAMY 75126d00d9 BossBar - Fix countdown placeholder not activating on usage in PlaceholderAPI 2022-10-06 16:43:06 +02:00
NEZNAMY 17ebf95d5b BossBar - Fix announcement bars disappearing on refresh 2022-10-06 16:42:32 +02:00
NEZNAMY c764420cfe Code Style - Simplify field value assignment 2022-10-06 15:33:50 +02:00
Callum Seabrook 28f05fc2af Changed publishing artifact IDs to begin with "tab-" 2022-10-06 12:02:28 +01:00
Callum Seabrook a420892887 Fixed publishing ID 2022-10-06 11:54:06 +01:00
NEZNAMY d910dc5913 Header / Footer - Prioritize world over server in config.yml as well 2022-10-06 11:11:00 +02:00
NEZNAMY c9c1cbc349 Layout - Fix anti-override for playerlist names being disabled even when no layout is visible 2022-09-26 11:02:14 +02:00
NEZNAMY fd4704f75e 3.1.5 2022-09-20 15:46:28 +02:00
NEZNAMY 5400ca637f Global PlayerList - Do not fill display name for 1.7 players 2022-09-19 18:23:16 +02:00
NEZNAMY 79d2610117 Global PlayerList - Fix server switch when pipeline injection is disabled 2022-09-17 18:38:06 +02:00
NEZNAMY 6a51f4cb9d BungeeCord - Fix 1.7 players not being processed fully on server switch (reapplying scoreboards) 2022-09-13 18:33:52 +02:00
NEZNAMY c190776665 Scoreboard - Fix show command enabling scoreboard for players who disabled it using toggle command 2022-09-13 18:32:54 +02:00
NEZNAMY 3765a1232e Merge pull request #697 from JT122406/master
Updates and Velocity Deprecation Fix
2022-08-27 19:48:00 +02:00
J.T. McQuigg 28866eba21 More Updates and Build Fix 2022-08-27 13:07:28 -04:00
J.T. McQuigg 08adee3dc3 Recomment this out for PR 2022-08-27 11:57:34 -04:00
J.T. McQuigg 8a1adaf2cd More Updates 2022-08-27 11:57:08 -04:00
J.T. McQuigg fdb9accaa6 Updates and don't use Deprecation in Velocity module 2022-08-27 11:42:15 -04:00
NEZNAMY 4f7656748b Merge pull request #695 from Tanguygab/master
added StartWith `|-` and EndsWith `-|` condition types
2022-08-25 22:25:48 +02:00
Tanguygab 03e4495ac7 added |- and -| conditions 2022-08-25 15:15:21 +02:00
NEZNAMY 01af5cb9e1 Placeholders - Fix nested placeholders not being applied when updating from nested?!! (#694) 2022-08-24 16:52:01 +02:00
NEZNAMY 8d49f32df2 Code style - Change the way API instance is set 2022-08-22 20:30:16 +02:00
NEZNAMY 8a955b3bc8 Javadoc - Further improve documentation 2022-08-22 20:27:20 +02:00
NEZNAMY ead40cb413 Merge pull request #691 from Tanguygab/master
Commands - Added target and -s arguments for /tab nametag preview/toggle (#690)
2022-08-22 16:22:32 +02:00
Tanguygab 5e07f78d4f updated messages.yml 2022-08-22 16:20:43 +02:00
Tanguygab c7d98795bc Revert "* for all players in /tab scoreboard show"
This reverts commit e4f33b58f7.
2022-08-22 16:13:46 +02:00
Tanguygab 9f637e3d6a added target and -s arguments for /tab nametag preview/toggle 2022-08-22 16:09:23 +02:00
Tanguygab 2d5f074c78 Merge remote-tracking branch 'upstream/master' 2022-08-22 15:35:23 +02:00
NEZNAMY 3be6ba484d API - Fix show_item hover event on 1.16+ 2022-08-18 22:36:43 +02:00
NEZNAMY d9d74e7a32 Placeholders - Fix NPE in %essentialsnick% event listener due to Essentials bug (#684) 2022-08-18 12:46:54 +02:00
NEZNAMY 887306a5ed Bukkit - Include 1.19.2 in supported version range 2022-08-16 01:25:15 +02:00
NEZNAMY 4b2542eb4f Global Playerlist - Add config option to disable (and enable) profile key filling (#680) 2022-08-15 13:28:56 +02:00
NEZNAMY 66bd3baa3e Layout - Add config option to show vanished players in layout 2022-08-14 15:41:19 +02:00
NEZNAMY bf32004f8f Layout - Do not format original entries in any way (can cause spaces) 2022-08-13 17:50:28 +02:00
NEZNAMY bfb2df9c95 Commands - Show storage type in debug output 2022-08-13 17:44:46 +02:00
Callum Seabrook ddeb05e408 Merge pull request #662 from JT122406/Updates
Update Gradle Wrapper to 7.5
2022-08-12 16:58:38 +01:00
Callum Seabrook 9312d61258 Fix some more BS 2022-08-12 16:49:30 +01:00
Callum Seabrook d44909bc42 Fixed some BS 2022-08-12 16:41:26 +01:00
Callum Seabrook 1d77df335c Switched to Indra for publishing and improved code reuse in build scripts 2022-08-12 16:29:12 +01:00
NEZNAMY a268dc25f4 BungeeCord - Use direct code for proxy module again 2022-08-12 15:46:14 +02:00
NEZNAMY 8c5696faa0 Bukkit - Register universal placeholders after bukkit ones to fix reference issue with nested placeholders 2022-08-12 15:05:40 +02:00
NEZNAMY 94f3ccfb7d Javadoc - Further improve documentation 2022-08-12 15:04:49 +02:00
NEZNAMY 576329214d Add 1.19.2 to known versions 2022-08-11 17:57:49 +02:00
NEZNAMY 5c13c2e225 Velocity - Remove join processing delay since the required delay is already included elsewhere 2022-08-11 17:49:42 +02:00
NEZNAMY 1070f85617 Placeholders - Retry updateValue if player is not loaded yet (might fix #564) 2022-08-11 17:48:26 +02:00
NEZNAMY 7c9dc87fcb NameTags - Make canSeeFriendlyInvisibles flag configurable 2022-08-09 14:12:14 +02:00
J.T. McQuigg dc09e77190 Update Gradle Wrapper to 7.5 2022-07-26 17:16:56 -04:00
Tanguygab d5b0ff7f40 Merge remote-tracking branch 'upstream/master' 2022-07-24 13:11:11 +02:00
Tanguygab e4f33b58f7 * for all players in /tab scoreboard show 2022-07-04 13:34:04 +02:00
152 changed files with 2485 additions and 1886 deletions

No files matched your search

+2
View File
@@ -57,5 +57,7 @@ body:
required: true
- label: I have read the wiki to make sure it's not an issue with configuration
required: true
- label: I ticked all of the boxes without actually reading them
required: false
- label: I have read the Compatibility wiki page and am not trying to run the plugin on an unsupported server version / platform
required: true
+2
View File
@@ -51,5 +51,7 @@ body:
required: true
- label: I have included a paste of the error
required: true
- label: I ticked all of the boxes without actually reading them
required: false
- label: I have read the Compatibility wiki page and am not trying to run the plugin on an unsupported server version / platform
required: true
+5 -5
View File
@@ -11,22 +11,22 @@ jobs:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v3.3.0 #https://github.com/actions/checkout/releases
with:
fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis
- name: Set up JDK 11
uses: actions/setup-java@v3
uses: actions/setup-java@v3.9.0 #https://github.com/actions/setup-java/releases
with:
distribution: 'temurin'
java-version: 11
- name: Cache SonarCloud packages
uses: actions/cache@v2
uses: actions/cache@v3.2.3 #https://github.com/actions/cache/releases
with:
path: ~/.sonar/cache
key: ${{ runner.os }}-sonar
restore-keys: ${{ runner.os }}-sonar
- name: Cache Gradle
uses: actions/cache@v2
uses: actions/cache@v3.2.3 #https://github.com/actions/cache/releases
with:
path: |
~/.gradle/caches
@@ -39,7 +39,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any
run: ./gradlew clean build
- name: Upload a Build Artifact
uses: actions/upload-artifact@v3
uses: actions/upload-artifact@v3.1.2 #https://github.com/actions/upload-artifact/releases
with:
name: Jar
path: jar/build/libs/TAB-**.jar
+12 -16
View File
@@ -6,8 +6,8 @@ More information can be found at [Why TAB?](https://github.com/NEZNAMY/TAB/wiki/
# Distribution
As of TAB 3.0.0, released with MC 1.18 (November 30th, 2021), there is no free / premium
version anymore. Only one version of the plugin exists now, which contains all implemented features
and is available to everyone for free. However, official support on discord is only provided to
those who have purchased the plugin.
and is available to everyone for free. However, customer support is only provided to
customers who have purchased the plugin.
# Compiling
Compilation requires JDK 8 and up.
@@ -15,17 +15,10 @@ To compile the plugin, run `./gradlew build` from the terminal.
Once the plugin compiles, grab the jar from `/jar/build/libs/` folder.
# Links
The precompiled JAR can be downloaded for free from:
* GitHub releases: https://github.com/NEZNAMY/TAB/releases
* MC-Market: https://www.mc-market.org/resources/20631/
Supporting my work:
* MC-Market: https://www.mc-market.org/resources/14009/
* Polymart: https://polymart.org/resource/484
Other links:
* Discord support for buyers: https://discord.gg/EaSvdk6
* Wiki: https://github.com/NEZNAMY/TAB/wiki
Download the plugin for free: [GitHub releases](https://github.com/NEZNAMY/TAB/releases),
[BuiltByBit](https://builtbybit.com/resources/20631/), [Modrinth](https://modrinth.com/plugin/tab-was-taken)
Support my work & receive customer support: [BuiltByBit](https://builtbybit.com/resources/14009/), [Polymart](https://polymart.org/resource/484)
Other links: [Discord support for customers](https://discord.gg/EaSvdk6), [Wiki](https://github.com/NEZNAMY/TAB/wiki)
# Issues
The [Issues](https://github.com/NEZNAMY/TAB/issues) section is **only** available for cases included in templates.
@@ -33,6 +26,9 @@ In other words, they **are not available for**:
* **Support** - support is provided on discord for buyers, where roles are given to verified users
allowing us to know who bought it and who didn't. It also serves as a much faster way of communication.
* **Feature requests** - closed indefinitely.
**If you're a customer, please use discord for reporting bugs, especially if you aren't good at making bug reports.
You will be provided with a much faster and higher quality service.**
# Contributing
If this plugin has helped you in any way, and you would like to return something back
@@ -40,8 +36,8 @@ to make the plugin even better, there is a lot of ways to contribute:
* Open a **pull request** containing a new feature or a bug fix, which you believe many users will benefit from.
* Make detailed high-quality **bug reports**. The difference between a bug getting fixed in 1 week vs 1 hour
is in quality of the report (typically providing correct steps to reproduce that actually work).
* Test **experimental builds**, which can be found in [Github Actions](https://github.com/NEZNAMY/TAB/actions) or in a form of pre-releases on discord
for problems of any kind including bugs. This way you will help make the actual releases more stable / help get stable releases sooner.
* Test **experimental builds**, which can be found in [Github Actions](https://github.com/NEZNAMY/TAB/actions) for problems of any kind including bugs.
This way you will help make the actual releases more stable / help get stable releases sooner.
* Help improve the wiki by opening a [Wiki change issue](https://github.com/NEZNAMY/TAB/issues/new?assignees=&labels=Wiki&template=wiki_change.yml),
where you can improve existing descriptions, add information you found missing, fix typos / grammar mistakes or add more examples of usage of functions.
* [Buy a copy](https://www.mc-market.org/resources/14009/) of the plugin or [send a donation](https://paypal.me/neznamy1) to keep the plugin active.
* [Buy a copy](https://builtbybit.com/resources/14009/) of the plugin or [send a donation](https://paypal.me/neznamy1) to keep the plugin active.
@@ -1,5 +1,9 @@
package me.neznamy.tab.api;
/**
* Interface representing an armor stand attached to player
* for displaying text in game.
*/
public interface ArmorStand {
/**
@@ -92,6 +96,13 @@ public interface ArmorStand {
* player to spawn for
*/
void spawn(TabPlayer viewer);
/**
* Sends respawn packets to specified player to make position
* change instant instead of teleport animation
*
* @param viewer
* Player to send armor stand respawn packets to
*/
void respawn(TabPlayer viewer);
}
@@ -10,5 +10,11 @@ public interface ArmorStandManager {
*/
void destroy();
/**
* Refreshes text of all armor stands for all nearby players
*
* @param force
* If refresh should be force despite no update seemingly being needed
*/
void refresh(boolean force);
}
@@ -91,9 +91,26 @@ public interface FeatureManager {
*/
boolean onCommand(TabPlayer sender, String command);
/**
* Internal function that marks objective packet deserialization
* when at least 1 feature uses it.
* <p>
* Internal use only.
*/
void markObjective();
/**
* Internal function that marks display objective packet deserialization
* when at least 1 feature uses it.
* <p>
* Internal use only.
*/
void markDisplayObjective();
/**
* Returns array of all currently enabled features
*
* @return array of all currently enabled features
*/
TabFeature[] getValues();
}
@@ -1,16 +1,82 @@
package me.neznamy.tab.api;
/**
* Interface allowing to send header/footer to players.
* <p>
* Instance can be obtained using {@link TabAPI#getHeaderFooterManager()}.
* This requires the Header/footer feature to be enabled in config, otherwise
* the method will return {@code null}.
*/
public interface HeaderFooterManager {
/**
* Sets raw value of header to specified value. This overrides plugin
* configuration until reset is called. Has full placeholder support
* and will send update packet whenever a placeholder used inside
* changes its value.
*
* @param player
* Player to set header for
* @param header
* Raw header value
*/
void setHeader(TabPlayer player, String header);
/**
* Sets raw value of footer to specified value. This overrides plugin
* configuration until reset is called. Has full placeholder support
* and will send update packet whenever a placeholder used inside
* changes its value.
*
* @param player
* Player to set header for
* @param footer
* Raw footer value
*/
void setFooter(TabPlayer player, String footer);
/**
* Sets raw value of header and footer to specified values. This overrides plugin
* configuration until reset is called. Has full placeholder support
* and will send update packet whenever a placeholder used inside
* changes its value.
*
* @param player
* Player to set header for
* @param header
* Raw header value
* @param footer
* Raw footer value
*/
void setHeaderAndFooter(TabPlayer player, String header, String footer);
/**
* Resets header value previously set using {@link #setHeader(TabPlayer, String)}
* or {@link #setHeaderAndFooter(TabPlayer, String, String)} and restores internal
* logic using configuration files.
*
* @param player
* Player to reset header for
*/
void resetHeader(TabPlayer player);
/**
* Resets footer value previously set using {@link #setFooter(TabPlayer, String)}
* or {@link #setHeaderAndFooter(TabPlayer, String, String)} and restores internal
* logic using configuration files.
*
* @param player
* Player to reset header for
*/
void resetFooter(TabPlayer player);
/**
* Resets values previously set using {@link #setHeader(TabPlayer, String)},
* {@link #setFooter(TabPlayer, String)} or {@link #setHeaderAndFooter(TabPlayer, String, String)}
* and restores internal logic using configuration files.
*
* @param player
* Player to reset header and footer for
*/
void resetHeaderAndFooter(TabPlayer player);
}
@@ -8,6 +8,8 @@ public enum ProtocolVersion {
UNKNOWN_SERVER_VERSION ("Unknown"),
UNKNOWN_CLIENT_VERSION ("Unknown"),
PROXY ("Proxy"),
V1_19_3 (761),
V1_19_2 (760),
V1_19_1 (760),
V1_19 (759),
V1_18_2 (758),
@@ -18,6 +18,13 @@ public abstract class TabAPI {
/** Instance of the API */
private static TabAPI instance;
/**
* Constructs new instance and sets it as new API instance
*/
public TabAPI() {
instance = this;
}
/**
* Returns API instance. If instance was not set by the plugin, throws
* {@code IllegalStateException}. This is usually caused by shading the API
@@ -34,16 +41,6 @@ public abstract class TabAPI {
return instance;
}
/**
* Instance setter for internal use by the plugin only.
*
* @param instance
* API instance
*/
public static void setInstance(TabAPI instance) {
TabAPI.instance = instance;
}
/**
* Returns player object from given UUID
*
@@ -83,6 +83,8 @@ public class TabConstants {
public static final String NAMETAGS_TEAM_UNREGISTER = "NameTags (Team unregister)";
public static final String NAMETAGS_TEAM_UPDATE = "NameTags (Team update)";
public static final String SORTING_CHANGING_TEAM_NAME = "Sorting (Changing team name)";
public static final String SCOREBOARD_TITLE = "Scoreboard (Title)";
public static final String SCOREBOARD_LINES = "Scoreboard (Lines)";
@@ -116,7 +118,11 @@ public class TabConstants {
public static final String COMMAND_MYSQL_DOWNLOAD = "tab.mysql.download";
public static final String COMMAND_MYSQL_UPLOAD = "tab.mysql.upload";
public static final String COMMAND_NAMETAG_TOGGLE = "tab.nametag.toggle";
public static final String COMMAND_NAMETAG_TOGGLE_OTHER = "tab.nametag.toggle.other";
public static final String COMMAND_NAMETAG_PREVIEW = "tab.nametag.preview";
public static final String COMMAND_NAMETAG_PREVIEW_OTHER = "tab.nametag.preview.other";
public static final String COMMAND_PARSE = "tab.parse";
public static final String COMMAND_SCOREBOARD_ANNOUNCE = "tab.announce.scoreboard";
public static final String COMMAND_SCOREBOARD_TOGGLE = "tab.scoreboard.toggle";
@@ -329,6 +335,9 @@ public class TabConstants {
public static final String VAULT_SUFFIX = "%vault-suffix%";
public static final String HEALTH = "%health%";
public static final int MINIMUM_REFRESH_INTERVAL = 50;
public static String condition(String name) {
return "%condition:" + name + "%";
}
@@ -336,6 +345,10 @@ public class TabConstants {
public static String animation(String name) {
return "%animation:" + name + "%";
}
public static String globalPlayerListGroup(String group) {
return "%playerlist-group_" + group + "%";
}
}
/**
@@ -212,13 +212,6 @@ public interface TabPlayer {
*/
String getTeamName();
/**
* Returns user-friendly explanation of team name
*
* @return explanation behind team name
*/
String getTeamNameNote();
/**
* Returns {@code true} if player is disguised using LibsDisguises, {@code false} if not
*
@@ -318,4 +311,10 @@ public interface TabPlayer {
* @return Player's direct chat message signing key
*/
Object getProfilePublicKey();
/**
* Return player's chat session ID
* @return player's chat session ID
*/
UUID getChatSessionId();
}
@@ -2,21 +2,23 @@ package me.neznamy.tab.api.chat;
import java.util.UUID;
import com.google.gson.JsonObject;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.util.Preconditions;
public class ChatComponentEntity extends IChatBaseComponent {
private final String type;
private final UUID id;
private final String name;
private final String CONTENTS;
private final String VALUE_1_13;
private final String VALUE_1_12;
private final String VALUE_1_8;
public ChatComponentEntity(String type, UUID id, String name) {
Preconditions.checkNotNull(type, "type");
Preconditions.checkNotNull(id, "id");
this.type = type;
this.id = id;
this.name = name;
this.CONTENTS = String.format("{\"type\":\"%s\",\"id\":\"%s\",\"name\":{\"text\":\"%s\"}}", type, id, name);
this.VALUE_1_13 = String.format("{type:\"%s\",id:\"%s\",name:\"{\\\"text\\\":\\\"%s\\\"}\"}", type, id, name);
this.VALUE_1_12 = String.format("{type:\"%s\",id:\"%s\",name:\"%s\"}", type, id, name);
this.VALUE_1_8 = String.format("{type:%s,id:%s,name:%s}", type, id, name);
}
@Override
@@ -26,16 +28,10 @@ public class ChatComponentEntity extends IChatBaseComponent {
@Override
public String toString() {
return String.format("{\"type\":\"%s\",\"id\":\"%s\",\"name\":{\"text\":\"%s\"}}", type, id, name);
}
public JsonObject toJson() {
JsonObject json = new JsonObject();
json.addProperty("type", type);
json.addProperty("id", id.toString());
JsonObject json2 = new JsonObject();
json2.addProperty("text", name);
json.add("name", json2);
return json;
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 16) return CONTENTS;
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 13) return VALUE_1_13;
if (TabAPI.getInstance().getServerVersion().getMinorVersion() == 12) return VALUE_1_12;
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 8) return VALUE_1_8;
throw new IllegalStateException("show_entity hover action is not supported on <1.8");
}
}
@@ -53,8 +53,18 @@ public class ChatHoverable {
*/
public enum EnumHoverAction {
SHOW_TEXT,
SHOW_ITEM,
SHOW_ENTITY
SHOW_TEXT("contents"),
SHOW_ITEM("value"),
SHOW_ENTITY("contents");
private final String preferredKey;
EnumHoverAction(String preferredKey) {
this.preferredKey = preferredKey;
}
public String getPreferredKey() {
return preferredKey;
}
}
}
@@ -249,17 +249,7 @@ public class ChatModifier {
public void onHoverShowEntity(String type, UUID id, String name) {
Preconditions.checkNotNull(type, "type");
Preconditions.checkNotNull(id, "id");
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 16) {
hoverEvent = new ChatHoverable(EnumHoverAction.SHOW_ENTITY, new ChatComponentEntity(type, id, name));
} else if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 13) {
hoverEvent = new ChatHoverable(EnumHoverAction.SHOW_ENTITY, new IChatBaseComponent(String.format("{type:\"%s\",id:\"%s\",name:\"{\\\"text\\\":\\\"%s\\\"}\"}", type, id, name)));
} else if (TabAPI.getInstance().getServerVersion().getMinorVersion() == 12) {
hoverEvent = new ChatHoverable(EnumHoverAction.SHOW_ENTITY, new IChatBaseComponent(String.format("{type:\"%s\",id:\"%s\",name:\"%s\"}", type, id, name)));
} else if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 8) {
hoverEvent = new ChatHoverable(EnumHoverAction.SHOW_ENTITY, new IChatBaseComponent(String.format("{type:%s,id:%s,name:%s}", type, id, name)));
} else {
throw new IllegalStateException("show_entity hover action is not supported on <1.8");
}
hoverEvent = new ChatHoverable(EnumHoverAction.SHOW_ENTITY, new ChatComponentEntity(type, id, name));
}
public void onHover(EnumHoverAction action, IChatBaseComponent value) {
@@ -295,7 +285,7 @@ public class ChatModifier {
JSONObject hover = new JSONObject();
hover.put("action", hoverEvent.getAction().toString().toLowerCase());
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 16) {
hover.put("contents", hoverEvent.getValue());
hover.put(hoverEvent.getAction().getPreferredKey(), hoverEvent.getValue());
} else {
hover.put("value", TabAPI.getInstance().getServerVersion().getMinorVersion() >= 9 ?
hoverEvent.getValue() : hoverEvent.getValue().toRawText());
@@ -39,15 +39,15 @@ public class RGBUtils {
*/
public RGBUtils() {
List<RGBFormatter> list = new ArrayList<>();
try {
Class.forName("net.kyori.adventure.text.minimessage.MiniMessage");
list.add(new MiniMessageFormat());
} catch (ClassNotFoundException ignored) {}
list.add(new BukkitFormat());
list.add(new CMIFormat());
list.add(new UnnamedFormat1());
list.add(new HtmlFormat());
list.add(new KyoriFormat());
try {
Class.forName("net.kyori.adventure.text.minimessage.MiniMessage");
list.add(new MiniMessageFormat());
} catch (ClassNotFoundException ignored) {}
formats = list.toArray(new RGBFormatter[0]);
gradients = new GradientPattern[] {
@@ -4,7 +4,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Formatter for #<RRGGBB>
* Formatter for #&lt;RRGGBB&gt;
*/
public class HtmlFormat implements RGBFormatter {
@@ -4,7 +4,7 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Formatter for &lt;#RRGGBB>
* Formatter for &lt;#RRGGBB&gt;
*/
public class KyoriFormat implements RGBFormatter {
@@ -1,20 +1,18 @@
package me.neznamy.tab.api.chat.rgb.format;
import me.neznamy.tab.api.chat.EnumChatFormat;
import net.kyori.adventure.text.minimessage.MiniMessage;
import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer;
/**
* Formatter for &lt;#RRGGBB>
* Call to MiniMessage API to reformat text to &amp;x&amp;R&amp;R&amp;G&amp;G&amp;B&amp;B
*/
public class MiniMessageFormat implements RGBFormatter {
@Override
public String reformat(String text) {
if (!text.contains("<")) return text;
String format = text.replace(EnumChatFormat.RESET.getFormat(), ""); //remove &r from header/footer newline inserts
try {
return LegacyComponentSerializer.legacySection().serialize(MiniMessage.miniMessage().deserialize(format));
return LegacyComponentSerializer.legacySection().serialize(MiniMessage.miniMessage().deserialize(text));
} catch (Throwable ignored) {
return text;
}
@@ -7,8 +7,8 @@ import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.chat.TextColor;
/**
* Gradient applier for &lt;gradient:#RRGGBB:#RRGGBB>Text&lt;/gradient>
* and &lt;gradient:#RRGGBB|L:#RRGGBB>Text&lt;/gradient>
* Gradient applier for &lt;gradient:#RRGGBB:#RRGGBB&gt;Text&lt;/gradient&gt;
* and &lt;gradient:#RRGGBB|L:#RRGGBB&gt;Text&lt;/gradient&gt;
*/
public class KyoriGradient implements GradientPattern {
@@ -1,14 +1,11 @@
package me.neznamy.tab.api.config;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.util.Preconditions;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.util.ArrayList;
import java.util.LinkedHashMap;
@@ -16,9 +13,6 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.util.Preconditions;
/**
* Abstract class for configuration file
*/
@@ -53,18 +47,9 @@ public abstract class ConfigurationFile {
Preconditions.checkNotNull(destination, "destination");
this.file = destination;
if (file.getParentFile() != null && !file.getParentFile().exists()) Files.createDirectories(file.getParentFile().toPath());
if (!file.exists() && source == null) throw new IllegalStateException("File does not exist and source is null");
if (file.createNewFile()) {
// Files.copy(source, file.toPath());
//avoiding MalformedInputException thrown on fabric due to file not having UTF8 encoding by default
//also automatically using the encoding so users don't have to worry about it anymore
try (BufferedReader reader = new BufferedReader(new InputStreamReader(source, StandardCharsets.UTF_8));
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), StandardCharsets.UTF_8))){
String line;
while ((line = reader.readLine()) != null) {
writer.write(line + "\n");
}
}
if (!file.exists()) {
if (source == null) throw new IllegalStateException("File does not exist and source is null");
Files.copy(source, file.toPath());
}
detectHeader();
}
@@ -202,7 +202,7 @@ public class PacketBuilder {
}
/**
* If {@code clientVersion} is >= 1.13, creates a component from given text and returns
* If {@code clientVersion} is &gt;= 1.13, creates a component from given text and returns
* it as a serialized component, which BungeeCord uses.
* <p>
* If {@code clientVersion} is &lt; 1.12, the text is cut to {@code length} characters if
@@ -1,6 +1,7 @@
package me.neznamy.tab.api.protocol;
import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.UUID;
@@ -12,8 +13,8 @@ import me.neznamy.tab.api.util.Preconditions;
*/
public class PacketPlayOutPlayerInfo implements TabPacket {
/** Packet action */
private final EnumPlayerInfoAction action;
/** Packet actions */
private final EnumSet<EnumPlayerInfoAction> actions;
/** List of affected entries */
private final List<PlayerInfoData> entries;
@@ -27,10 +28,7 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
* Affected entries
*/
public PacketPlayOutPlayerInfo(EnumPlayerInfoAction action, PlayerInfoData... entries) {
Preconditions.checkNotNull(action, "action");
Preconditions.checkNotNull(entries, "entries");
this.action = action;
this.entries = Arrays.asList(entries);
this(action, Arrays.asList(entries));
}
/**
@@ -44,22 +42,61 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
public PacketPlayOutPlayerInfo(EnumPlayerInfoAction action, List<PlayerInfoData> entries) {
Preconditions.checkNotNull(action, "action");
Preconditions.checkNotNull(entries, "entries");
this.action = action;
if (action == EnumPlayerInfoAction.ADD_PLAYER) {
actions = EnumSet.of(EnumPlayerInfoAction.ADD_PLAYER,
EnumPlayerInfoAction.INITIALIZE_CHAT,
EnumPlayerInfoAction.UPDATE_GAME_MODE,
EnumPlayerInfoAction.UPDATE_LISTED,
EnumPlayerInfoAction.UPDATE_LATENCY,
EnumPlayerInfoAction.UPDATE_DISPLAY_NAME);
} else {
this.actions = EnumSet.of(action);
}
this.entries = entries;
}
/**
* Constructs new instance with given parameters.
*
* @param actions
* Packet actions
* @param entries
* Affected entries
*/
public PacketPlayOutPlayerInfo(EnumSet<EnumPlayerInfoAction> actions, PlayerInfoData... entries) {
Preconditions.checkNotNull(actions, "action");
Preconditions.checkNotNull(entries, "entries");
this.actions = actions;
this.entries = Arrays.asList(entries);
}
/**
* Constructs new instance with given parameters.
*
* @param actions
* Packet actions
* @param entries
* Affected entries
*/
public PacketPlayOutPlayerInfo(EnumSet<EnumPlayerInfoAction> actions, List<PlayerInfoData> entries) {
Preconditions.checkNotNull(actions, "action");
Preconditions.checkNotNull(entries, "entries");
this.actions = actions;
this.entries = entries;
}
@Override
public String toString() {
return String.format("PacketPlayOutPlayerInfo{action=%s,entries=%s}", action, entries);
return String.format("PacketPlayOutPlayerInfo{actions=%s,entries=%s}", actions, entries);
}
/**
* Returns {@link #action}
* Returns {@link #actions}
*
* @return packet action
*/
public EnumPlayerInfoAction getAction() {
return action;
public EnumSet<EnumPlayerInfoAction> getActions() {
return actions;
}
/**
@@ -79,6 +116,9 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/** Latency */
private int latency;
/** I have no idea what this is */
private boolean listed;
/** GameMode */
private EnumGamemode gameMode = EnumGamemode.SURVIVAL; //ProtocolLib causes NPE even when action does not use GameMode
@@ -97,12 +137,15 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/** Player's skin, null for empty skin */
private Skin skin;
/** Chat session ID */
private UUID chatSessionId;
/** Player's chat signing key */
private Object profilePublicKey;
/**
* Constructs new instance with given parameters. Suitable for
* {@link EnumPlayerInfoAction}.ADD_PLAYER action
* {@link EnumPlayerInfoAction#ADD_PLAYER} action
*
* @param name
* Player's name
@@ -119,20 +162,22 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
* @param profilePublicKey
* Player's chat signing key
*/
public PlayerInfoData(String name, UUID uniqueId, Skin skin, int latency, EnumGamemode gameMode, IChatBaseComponent displayName, Object profilePublicKey) {
public PlayerInfoData(String name, UUID uniqueId, Skin skin, boolean listed, int latency, EnumGamemode gameMode, IChatBaseComponent displayName, UUID chatSessionId, Object profilePublicKey) {
Preconditions.checkNotNull(uniqueId, "uuid");
this.name = name;
this.uniqueId = uniqueId;
this.skin = skin;
this.listed = listed;
this.latency = latency;
this.gameMode = gameMode;
this.displayName = displayName;
this.chatSessionId = chatSessionId;
this.profilePublicKey = profilePublicKey;
}
/**
* Constructs new instance with given parameters. Suitable for
* {@link EnumPlayerInfoAction}.UPDATE_GAME_MODE action
* {@link EnumPlayerInfoAction#UPDATE_GAME_MODE} action
*
* @param uniqueId
* Player's uuid
@@ -147,7 +192,7 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* Constructs new instance with given parameters. Suitable for
* {@link EnumPlayerInfoAction}.UPDATE_LATENCY action
* {@link EnumPlayerInfoAction#UPDATE_LATENCY} action
*
* @param uniqueId
* Player's uuid
@@ -162,7 +207,7 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* Constructs new instance with given parameters. Suitable for
* {@link EnumPlayerInfoAction}.UPDATE_DISPLAY_NAME action
* {@link EnumPlayerInfoAction#UPDATE_DISPLAY_NAME} action
*
* @param uniqueId
* Player's uuid
@@ -177,7 +222,7 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* Constructs new instance with given parameter. Suitable for
* {@link EnumPlayerInfoAction}.REMOVE_PLAYER action
* {@link EnumPlayerInfoAction#REMOVE_PLAYER} action
*
* @param uniqueId
* Player's uuid
@@ -211,6 +256,25 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
this.latency = latency;
}
/**
* Returns {@link #listed}
*
* @return listed
*/
public boolean isListed() {
return listed;
}
/**
* Sets {@link #listed} to specified value
*
* @param listed
* Listed flag
*/
public void setListed(boolean listed) {
this.listed = listed;
}
/**
* Returns {@link #displayName}
*
@@ -306,6 +370,25 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
this.skin = skin;
}
/**
* Returns {@link #chatSessionId}
*
* @return chatSessionId
*/
public UUID getChatSessionId() {
return chatSessionId;
}
/**
* Sets {@link #chatSessionId} to specified value
*
* @param chatSessionId
* chatSessionId to use
*/
public void setChatSessionId(UUID chatSessionId) {
this.chatSessionId = chatSessionId;
}
/**
* Returns {@link #profilePublicKey}
*
@@ -328,12 +411,13 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
/**
* En enum representing packet action
* Calling ordinal() will return action's network ID.
*/
public enum EnumPlayerInfoAction {
ADD_PLAYER,
INITIALIZE_CHAT,
UPDATE_GAME_MODE,
UPDATE_LISTED,
UPDATE_LATENCY,
UPDATE_DISPLAY_NAME,
REMOVE_PLAYER
@@ -20,10 +20,10 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
*/
private final String name;
/** Team prefix, limited to 16 characters, on <1.13 */
/** Team prefix, limited to 16 characters, on &lt;1.13 */
private String playerPrefix;
/** Team suffix, limited to 16 characters, on <1.13 */
/** Team suffix, limited to 16 characters, on &lt;1.13 */
private String playerSuffix;
/**
@@ -46,7 +46,7 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
/**
* Affected entities. For players, it is their name, for other entities their UUID
* Player name length is limited to 16 characters for <1.8, 40 characters since 1.8.
* Player name length is limited to 16 characters for &lt;1.8, 40 characters since 1.8.
* Entity UUID option was added in 1.8.
*/
private Collection<String> players = Collections.emptyList();
@@ -74,7 +74,7 @@ public interface Scoreboard {
* @param index
* Index of line to remove, starting at 0
* @throws IndexOutOfBoundsException
* if the index is out of range (index &lt; 0 || index >= {@link #getLines()}.size())
* if the index is out of range (index &lt; 0 || index &gt;= {@link #getLines()}.size())
* @see #getLines()
* @see #addLine(String)
*/
@@ -1,10 +0,0 @@
package me.neznamy.tab.api.task;
public interface RepeatingTask {
int getInterval();
void setInterval(int paramInt);
void cancel();
}
@@ -4,6 +4,10 @@ import java.util.concurrent.Future;
import me.neznamy.tab.api.TabFeature;
/**
* Manager for scheduling and executing tasks in TAB's
* main thread to avoid concurrency issues.
*/
public interface ThreadManager {
/**
@@ -2,7 +2,7 @@ package me.neznamy.tab.api.util;
/**
* An interface for BiFunction which can throw an exception.
* Used in {@link me.neznamy.tab.api.protocol.PacketBuilder} to
* Used in {@link me.neznamy.tab.api.protocol.PacketBuilder} to
* call build method for packet classes, which may throw an
* exception due to reflection.
* @param <A>
@@ -14,5 +14,16 @@ package me.neznamy.tab.api.util;
*/
public interface BiFunctionWithException<A, B, C> {
/**
* Runs the function and returns the result
*
* @param a
* First argument
* @param b
* Second argument
* @return Output from function
* @throws ReflectiveOperationException
* If reflective operation fails
*/
C apply(A a, B b) throws ReflectiveOperationException;
}
@@ -2,24 +2,76 @@ package me.neznamy.tab.api.util;
import me.neznamy.tab.api.TabPlayer;
/**
* Class with utility methods used in the plugin
*/
public class Preconditions {
private Preconditions(){}
/**
* Private constructor preventing the class from being instantiated
*/
private Preconditions() {}
public static void checkNotNull(Object obj, String name){
/**
* Checks object and throws {@code IllegalArgumentException} if it is {@code null}.
*
* @param obj
* Object to check
* @param name
* Name of the variable to use in exception message
* @throws IllegalArgumentException
* if {@code obj} is {@code null}
*/
public static void checkNotNull(Object obj, String name) {
if (obj == null) throw new IllegalArgumentException(name + " cannot be null");
}
/**
* Checks length of String and throws {@code IllegalArgumentException} if it is
* longer than limit.
*
* @param string
* String to check
* @param maxLength
* Maximum length of the string
* @param name
* Name of the variable to use in exception message
* @throws IllegalArgumentException
* if {@code string} is {@code null} or longer than {@code maxLength} characters
*/
public static void checkMaxLength(String string, int maxLength, String name) {
checkNotNull(string, name);
if (string.length() > maxLength) throw new IllegalArgumentException(name + " is longer than " + maxLength + " characters (" + string.length() + ")");
}
public static void checkRange(Number number, Number min, Number max, String variable){
/**
* Checks number range and throws {@code IllegalArgumentException} if number
* is out of range.
*
* @param number
* Number to check
* @param min
* Minimum allowed value
* @param max
* Maximum allowed value
* @param variable
* Variable name to use in exception message
* @throws IllegalArgumentException
* if {@code number} is out of range ({@code number < min || number > max})
*/
public static void checkRange(Number number, Number min, Number max, String variable) {
if (number.doubleValue() < min.doubleValue() || number.doubleValue() > max.doubleValue())
throw new IllegalArgumentException(variable + " index out of range (" + min + " - " + max + ")");
throw new IllegalArgumentException(variable + " index out of range (" + min + " - " + max + "), was " + number);
}
/**
* Checks if player is loaded and throws {@code IllegalStateException} if not.
*
* @param player
* Player to check
* @throws IllegalStateException
* If player is not loaded
*/
public static void checkLoaded(TabPlayer player) {
if (!player.isLoaded()) throw new IllegalStateException("Player is not loaded yet. Try again later.");
}
@@ -0,0 +1,12 @@
package me.neznamy.tab.api.util;
/**
* Supplier that can throw an exception
*
* @param <T>
* Return class type
*/
public interface SupplierWithException<T> {
T get() throws Exception;
}
+9 -4
View File
@@ -2,11 +2,16 @@ plugins {
`kotlin-dsl`
}
repositories {
gradlePluginPortal()
mavenCentral()
dependencies {
implementation(libs.plugin.indra)
implementation(libs.plugin.shadow)
}
dependencies {
implementation("gradle.plugin.com.github.johnrengelman", "shadow", "7.1.0")
compileOnly(files(libs::class.java.protectionDomain.codeSource.location))
}
java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
+16
View File
@@ -0,0 +1,16 @@
rootProject.name = "build-logic"
dependencyResolutionManagement {
repositories {
gradlePluginPortal()
mavenCentral()
}
// You're a great IDE IntelliJ, but sometimes you really annoy me. >:(
// repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
versionCatalogs {
register("libs") {
from(files("../gradle/libs.versions.toml"))
}
}
}
@@ -1,7 +1,16 @@
import org.apache.tools.ant.filters.ReplaceTokens
plugins {
`java-library`
id("net.kyori.indra")
}
indra {
javaVersions {
target(8)
}
github("NEZNAMY", "TAB") {
ci(true)
}
}
tasks {
@@ -18,15 +27,4 @@ tasks {
options.encoding = Charsets.UTF_8.name()
(options as StandardJavadocDocletOptions).addStringOption("Xdoclint:none", "-quiet")
}
compileJava {
options.encoding = Charsets.UTF_8.name()
options.release.set(8)
options.compilerArgs.addAll(listOf("-nowarn", "-Xlint:-unchecked", "-Xlint:-deprecation"))
}
}
java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(17))
}
}
@@ -1,31 +1,12 @@
import gradle.kotlin.dsl.accessors._4cbb0020bd75d48acc42cabded5fdbb7.publishing
import org.gradle.api.artifacts.repositories.PasswordCredentials
import org.gradle.api.publish.maven.MavenPublication
import org.gradle.kotlin.dsl.get
import org.gradle.kotlin.dsl.repositories
plugins {
id("tab.base-conventions")
`maven-publish`
id("net.kyori.indra.publishing")
}
java {
withSourcesJar()
withJavadocJar()
}
publishing {
repositories {
maven {
name = "krypton"
url = uri("https://repo.kryptonmc.org/releases")
credentials(PasswordCredentials::class)
}
}
publications.create<MavenPublication>("mavenJava") {
groupId = rootProject.group as String
artifactId = "tab-${project.name}"
version = rootProject.version as String
from(components["java"])
indra {
publishReleasesTo("krypton", "https://repo.kryptonmc.org/releases")
publishSnapshotsTo("krypton", "https://repo.kryptonmc.org/snapshots")
configurePublications {
artifactId = "tab-$artifactId"
}
}
@@ -48,7 +48,8 @@ public class BukkitEventListener implements Listener {
@EventHandler(priority = EventPriority.LOWEST)
public void onJoin(PlayerJoinEvent e) {
if (TabAPI.getInstance().isPluginDisabled()) return;
TabAPI.getInstance().getThreadManager().runTask(() -> TabAPI.getInstance().getFeatureManager().onJoin(new BukkitTabPlayer(e.getPlayer(), platform.getProtocolVersion(e.getPlayer()))));
TabAPI.getInstance().getThreadManager().runTask(() ->
TabAPI.getInstance().getFeatureManager().onJoin(new BukkitTabPlayer(e.getPlayer(), platform.getProtocolVersion(e.getPlayer()))));
}
/**
@@ -60,7 +61,8 @@ public class BukkitEventListener implements Listener {
@EventHandler(priority = EventPriority.LOWEST)
public void onWorldChange(PlayerChangedWorldEvent e){
if (TabAPI.getInstance().isPluginDisabled()) return;
TabAPI.getInstance().getThreadManager().runTask(() -> TabAPI.getInstance().getFeatureManager().onWorldChange(e.getPlayer().getUniqueId(), e.getPlayer().getWorld().getName()));
TabAPI.getInstance().getThreadManager().runTask(() ->
TabAPI.getInstance().getFeatureManager().onWorldChange(e.getPlayer().getUniqueId(), e.getPlayer().getWorld().getName()));
}
/**
@@ -72,6 +74,7 @@ public class BukkitEventListener implements Listener {
@EventHandler
public void onCommand(PlayerCommandPreprocessEvent e) {
if (TabAPI.getInstance().isPluginDisabled()) return;
if (TabAPI.getInstance().getFeatureManager().onCommand(TabAPI.getInstance().getPlayer(e.getPlayer().getUniqueId()), e.getMessage())) e.setCancelled(true);
if (TabAPI.getInstance().getFeatureManager().onCommand(
TabAPI.getInstance().getPlayer(e.getPlayer().getUniqueId()), e.getMessage())) e.setCancelled(true);
}
}
@@ -12,13 +12,18 @@ import me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumGamemode;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.EnumPlayerInfoAction;
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo.PlayerInfoData;
import me.neznamy.tab.platforms.bukkit.nms.*;
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityDestroy;
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityMetadata;
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutEntityTeleport;
import me.neznamy.tab.platforms.bukkit.nms.PacketPlayOutSpawnEntityLiving;
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
import org.bukkit.Location;
import org.bukkit.entity.EntityType;
import java.lang.reflect.Array;
import java.util.*;
import java.util.stream.Collectors;
@SuppressWarnings({"unchecked", "rawtypes"})
public class BukkitPacketBuilder extends PacketBuilder {
@@ -76,9 +81,9 @@ public class BukkitPacketBuilder extends PacketBuilder {
return nms.newPacketPlayOutChat.newInstance(component, packet.getType().ordinal());
}
if (nms.getMinorVersion() >= 16)
return nms.newPacketPlayOutChat.newInstance(component, nms.ChatMessageType_values[packet.getType().ordinal()], UUID.randomUUID());
return nms.newPacketPlayOutChat.newInstance(component, Enum.valueOf(nms.ChatMessageType, packet.getType().toString()), UUID.randomUUID());
if (nms.getMinorVersion() >= 12)
return nms.newPacketPlayOutChat.newInstance(component, Enum.valueOf((Class<Enum>) nms.ChatMessageType, packet.getType().toString()));
return nms.newPacketPlayOutChat.newInstance(component, Enum.valueOf(nms.ChatMessageType, packet.getType().toString()));
if (nms.getMinorVersion() >= 8)
return nms.newPacketPlayOutChat.newInstance(component, (byte) packet.getType().ordinal());
if (nms.getMinorVersion() == 7)
@@ -89,24 +94,55 @@ public class BukkitPacketBuilder extends PacketBuilder {
@Override
public Object build(PacketPlayOutPlayerInfo packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
if (nms.getMinorVersion() < 8) return null;
Object nmsPacket = nms.newPacketPlayOutPlayerInfo.newInstance(nms.EnumPlayerInfoAction_values[packet.getAction().ordinal()], Array.newInstance(nms.EntityPlayer, 0));
List<Object> items = new ArrayList<>();
for (PlayerInfoData data : packet.getEntries()) {
GameProfile profile = new GameProfile(data.getUniqueId(), data.getName());
if (data.getSkin() != null) profile.getProperties().put("textures", new Property("textures", data.getSkin().getValue(), data.getSkin().getSignature()));
List<Object> parameters = new ArrayList<>();
if (nms.newPlayerInfoData.getParameterTypes()[0] == nms.PacketPlayOutPlayerInfo) {
parameters.add(nmsPacket);
if (nms.ClientboundPlayerInfoRemovePacket != null) {
//1.19.3+
if (packet.getActions().contains(EnumPlayerInfoAction.REMOVE_PLAYER)) {
return nms.newClientboundPlayerInfoRemovePacket.newInstance(
packet.getEntries().stream().map(PlayerInfoData::getUniqueId).collect(Collectors.toList())
);
}
parameters.add(profile);
parameters.add(data.getLatency());
parameters.add(data.getGameMode() == null ? null : nms.EnumGamemode_values[nms.EnumGamemode_values.length-EnumGamemode.VALUES.length+data.getGameMode().ordinal()]); //not_set was removed in 1.17
parameters.add(data.getDisplayName() == null ? null : toNMSComponent(data.getDisplayName(), clientVersion));
if (nms.getMinorVersion() >= 19) parameters.add(data.getProfilePublicKey());
items.add(nms.newPlayerInfoData.newInstance(parameters.toArray()));
Enum[] array = packet.getActions().stream().map(action -> Enum.valueOf(nms.EnumPlayerInfoAction, action.toString())).toArray(Enum[]::new);
Object nmsPacket = nms.newPacketPlayOutPlayerInfo.newInstance(EnumSet.of(array[0], array), Collections.emptyList());
List<Object> items = new ArrayList<>();
for (PlayerInfoData data : packet.getEntries()) {
GameProfile profile = new GameProfile(data.getUniqueId(), data.getName());
if (data.getSkin() != null) profile.getProperties().put("textures",
new Property("textures", data.getSkin().getValue(), data.getSkin().getSignature()));
Object obj = nms.newPlayerInfoData.newInstance(
data.getUniqueId(),
profile,
data.isListed(),
data.getLatency(),
data.getGameMode() == null ? null : Enum.valueOf(nms.EnumGamemode, data.getGameMode().toString()),
data.getDisplayName() == null ? null : toNMSComponent(data.getDisplayName(), clientVersion),
data.getProfilePublicKey() == null ? null : nms.newRemoteChatSession$Data.newInstance(data.getChatSessionId(), data.getProfilePublicKey()));
items.add(obj);
}
nms.setField(nmsPacket, nms.PacketPlayOutPlayerInfo_PLAYERS, items);
return nmsPacket;
} else {
//1.19.2-
EnumPlayerInfoAction action = packet.getActions().contains(EnumPlayerInfoAction.ADD_PLAYER) ?
EnumPlayerInfoAction.ADD_PLAYER : packet.getActions().iterator().next();
Object nmsPacket = nms.newPacketPlayOutPlayerInfo.newInstance(Enum.valueOf(nms.EnumPlayerInfoAction, action.toString()), Array.newInstance(nms.EntityPlayer, 0));
List<Object> items = new ArrayList<>();
for (PlayerInfoData data : packet.getEntries()) {
GameProfile profile = new GameProfile(data.getUniqueId(), data.getName());
if (data.getSkin() != null) profile.getProperties().put("textures", new Property("textures", data.getSkin().getValue(), data.getSkin().getSignature()));
List<Object> parameters = new ArrayList<>();
if (nms.newPlayerInfoData.getParameterTypes()[0] == nms.PacketPlayOutPlayerInfo) {
parameters.add(nmsPacket);
}
parameters.add(profile);
parameters.add(data.getLatency());
parameters.add(data.getGameMode() == null ? null : Enum.valueOf(nms.EnumGamemode, data.getGameMode().toString()));
parameters.add(data.getDisplayName() == null ? null : toNMSComponent(data.getDisplayName(), clientVersion));
if (nms.getMinorVersion() >= 19) parameters.add(data.getProfilePublicKey());
items.add(nms.newPlayerInfoData.newInstance(parameters.toArray()));
}
nms.setField(nmsPacket, nms.PacketPlayOutPlayerInfo_PLAYERS, items);
return nmsPacket;
}
nms.setField(nmsPacket, nms.PacketPlayOutPlayerInfo_PLAYERS, items);
return nmsPacket;
}
@Override
@@ -132,7 +168,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
if (nms.getMinorVersion() >= 13) {
return nms.newPacketPlayOutScoreboardObjective.newInstance(nms.newScoreboardObjective.newInstance(null, packet.getObjectiveName(), null,
toNMSComponent(IChatBaseComponent.optimizedComponent(displayName), clientVersion),
packet.getRenderType() == null ? null : nms.EnumScoreboardHealthDisplay_values[packet.getRenderType().ordinal()]),
packet.getRenderType() == null ? null : Enum.valueOf(nms.EnumScoreboardHealthDisplay, packet.getRenderType().toString())),
packet.getAction());
}
@@ -140,7 +176,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
nms.setField(nmsPacket, nms.PacketPlayOutScoreboardObjective_OBJECTIVENAME, packet.getObjectiveName());
nms.setField(nmsPacket, nms.PacketPlayOutScoreboardObjective_DISPLAYNAME, displayName);
if (nms.getMinorVersion() >= 8 && packet.getRenderType() != null) {
nms.setField(nmsPacket, nms.PacketPlayOutScoreboardObjective_RENDERTYPE, Enum.valueOf((Class<Enum>) nms.EnumScoreboardHealthDisplay, packet.getRenderType().toString()));
nms.setField(nmsPacket, nms.PacketPlayOutScoreboardObjective_RENDERTYPE, Enum.valueOf(nms.EnumScoreboardHealthDisplay, packet.getRenderType().toString()));
}
nms.setField(nmsPacket, nms.PacketPlayOutScoreboardObjective_METHOD, packet.getAction());
return nmsPacket;
@@ -149,7 +185,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
@Override
public Object build(PacketPlayOutScoreboardScore packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
if (nms.getMinorVersion() >= 13) {
return nms.newPacketPlayOutScoreboardScore_1_13.newInstance(nms.EnumScoreboardAction_values[packet.getAction().ordinal()], packet.getObjectiveName(), packet.getPlayer(), packet.getScore());
return nms.newPacketPlayOutScoreboardScore_1_13.newInstance(Enum.valueOf(nms.EnumScoreboardAction, packet.getAction().toString()), packet.getObjectiveName(), packet.getPlayer(), packet.getScore());
}
if (packet.getAction() == PacketPlayOutScoreboardScore.Action.REMOVE) {
return nms.newPacketPlayOutScoreboardScore_String.newInstance(packet.getPlayer());
@@ -189,9 +225,9 @@ public class BukkitPacketBuilder extends PacketBuilder {
case 2:
return nms.PacketPlayOutScoreboardTeam_ofBoolean.invoke(null, team, false);
case 3:
return nms.PacketPlayOutScoreboardTeam_ofString.invoke(null, team, packet.getPlayers().toArray(new String[0])[0], nms.PacketPlayOutScoreboardTeam_PlayerAction_values[0]);
return nms.PacketPlayOutScoreboardTeam_ofString.invoke(null, team, packet.getPlayers().iterator().next(), Enum.valueOf(nms.PacketPlayOutScoreboardTeam_PlayerAction, "ADD"));
case 4:
return nms.PacketPlayOutScoreboardTeam_ofString.invoke(null, team, packet.getPlayers().toArray(new String[0])[0], nms.PacketPlayOutScoreboardTeam_PlayerAction_values[1]);
return nms.PacketPlayOutScoreboardTeam_ofString.invoke(null, team, packet.getPlayers().iterator().next(), Enum.valueOf(nms.PacketPlayOutScoreboardTeam_PlayerAction, "REMOVE"));
default:
throw new IllegalArgumentException("Invalid action: " + packet.getAction());
}
@@ -227,7 +263,12 @@ public class BukkitPacketBuilder extends PacketBuilder {
* if thrown by reflective operation
*/
public Object build(PacketPlayOutEntityMetadata packet) throws ReflectiveOperationException {
return nms.newPacketPlayOutEntityMetadata.newInstance(packet.getEntityId(), packet.getDataWatcher().toNMS(), true);
if (nms.newPacketPlayOutEntityMetadata.getParameterCount() == 2) {
//1.19.3+
return nms.newPacketPlayOutEntityMetadata.newInstance(packet.getEntityId(), nms.DataWatcher_b.invoke(packet.getDataWatcher().toNMS()));
} else {
return nms.newPacketPlayOutEntityMetadata.newInstance(packet.getEntityId(), packet.getDataWatcher().toNMS(), true);
}
}
/**
@@ -264,7 +305,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
}
int id = entityIds.get(packet.getEntityType());
if (nms.getMinorVersion() >= 19) {
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_ENTITYTYPE, nms.Registry_a.invoke(nms.IRegistry_X, id));
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_ENTITYTYPE, nms.EntityTypes_ARMOR_STAND); // :(
} else {
nms.setField(nmsPacket, nms.PacketPlayOutSpawnEntityLiving_ENTITYTYPE, id);
}
@@ -322,9 +363,9 @@ public class BukkitPacketBuilder extends PacketBuilder {
if (prefix != null) nms.ScoreboardTeam_setPrefix.invoke(team, toNMSComponent(IChatBaseComponent.optimizedComponent(prefix), clientVersion));
if (suffix != null) nms.ScoreboardTeam_setSuffix.invoke(team, toNMSComponent(IChatBaseComponent.optimizedComponent(suffix), clientVersion));
EnumChatFormat format = packet.getColor() != null ? packet.getColor() : EnumChatFormat.lastColorsOf(prefix);
nms.ScoreboardTeam_setColor.invoke(team, nms.EnumChatFormat_values[format.ordinal()]);
nms.ScoreboardTeam_setNameTagVisibility.invoke(team, String.valueOf(packet.getNameTagVisibility()).equals("always") ? nms.EnumNameTagVisibility_values[0] : nms.EnumNameTagVisibility_values[1]);
nms.ScoreboardTeam_setCollisionRule.invoke(team, String.valueOf(packet.getCollisionRule()).equals("always") ? nms.EnumTeamPush_values[0] : nms.EnumTeamPush_values[1]);
nms.ScoreboardTeam_setColor.invoke(team, Enum.valueOf(nms.EnumChatFormat, format.toString()));
nms.ScoreboardTeam_setNameTagVisibility.invoke(team, Enum.valueOf(nms.EnumNameTagVisibility, String.valueOf(packet.getNameTagVisibility()).equals("always") ? "ALWAYS" : "NEVER"));
nms.ScoreboardTeam_setCollisionRule.invoke(team, Enum.valueOf(nms.EnumTeamPush, String.valueOf(packet.getCollisionRule()).equals("always") ? "ALWAYS" : "NEVER"));
}
/**
@@ -344,31 +385,71 @@ public class BukkitPacketBuilder extends PacketBuilder {
private void createTeamLegacy(PacketPlayOutScoreboardTeam packet, Object team, String prefix, String suffix) throws ReflectiveOperationException {
if (prefix != null) nms.ScoreboardTeam_setPrefix.invoke(team, prefix);
if (suffix != null) nms.ScoreboardTeam_setSuffix.invoke(team, suffix);
if (nms.getMinorVersion() >= 8) nms.ScoreboardTeam_setNameTagVisibility.invoke(team, String.valueOf(packet.getNameTagVisibility()).equals("always") ? nms.EnumNameTagVisibility_values[0] : nms.EnumNameTagVisibility_values[1]);
if (nms.getMinorVersion() >= 9) nms.ScoreboardTeam_setCollisionRule.invoke(team, String.valueOf(packet.getCollisionRule()).equals("always") ? nms.EnumTeamPush_values[0] : nms.EnumTeamPush_values[1]);
if (nms.getMinorVersion() >= 8) nms.ScoreboardTeam_setNameTagVisibility.invoke(team, Enum.valueOf(nms.EnumNameTagVisibility, String.valueOf(packet.getNameTagVisibility()).equals("always") ? "ALWAYS" : "NEVER"));
if (nms.getMinorVersion() >= 9) nms.ScoreboardTeam_setCollisionRule.invoke(team, Enum.valueOf(nms.EnumTeamPush, String.valueOf(packet.getCollisionRule()).equals("always") ? "ALWAYS" : "NEVER"));
}
@Override
public PacketPlayOutPlayerInfo readPlayerInfo(Object nmsPacket, ProtocolVersion clientVersion) throws ReflectiveOperationException {
if (nms.getMinorVersion() < 8) return null;
EnumPlayerInfoAction action = EnumPlayerInfoAction.valueOf(nms.PacketPlayOutPlayerInfo_ACTION.get(nmsPacket).toString());
List<PlayerInfoData> listData = new ArrayList<>();
for (Object nmsData : (List<?>) nms.PacketPlayOutPlayerInfo_PLAYERS.get(nmsPacket)) {
Object nmsGameMode = nms.PlayerInfoData_getGamemode.invoke(nmsData);
EnumGamemode gameMode = (nmsGameMode == null) ? null : EnumGamemode.valueOf(nmsGameMode.toString());
GameProfile profile = (GameProfile) nms.PlayerInfoData_getProfile.invoke(nmsData);
Object nmsComponent = nms.PlayerInfoData_getDisplayName.invoke(nmsData);
IChatBaseComponent listName = nmsComponent == null ? null : new WrappedChatComponent(nmsComponent);
Skin skin = null;
if (!profile.getProperties().get("textures").isEmpty()) {
Property pr = profile.getProperties().get("textures").iterator().next();
skin = new Skin(pr.getValue(), pr.getSignature());
if (nms.ClientboundPlayerInfoRemovePacket != null) {
//1.19.3+
if (nms.ClientboundPlayerInfoRemovePacket.isInstance(nmsPacket)) {
List<UUID> entries = (List<UUID>) nms.ClientboundPlayerInfoRemovePacket_getEntries.invoke(nmsPacket);
return new PacketPlayOutPlayerInfo(
EnumPlayerInfoAction.REMOVE_PLAYER,
entries.stream().map(PlayerInfoData::new).collect(Collectors.toList())
);
}
Object profilePublicKey = nms.getMinorVersion() >= 19 ? nms.PlayerInfoData_getProfilePublicKeyRecord.invoke(nmsData) : null;
listData.add(new PlayerInfoData(profile.getName(), profile.getId(), skin,
(int) nms.PlayerInfoData_getLatency.invoke(nmsData), gameMode, listName, profilePublicKey));
EnumSet<?> set = (EnumSet<?>) nms.PacketPlayOutPlayerInfo_ACTION.get(nmsPacket);
EnumPlayerInfoAction[] array = set.stream().map(action -> EnumPlayerInfoAction.valueOf(action.toString())).toArray(EnumPlayerInfoAction[]::new);
EnumSet<EnumPlayerInfoAction> actions = EnumSet.of(array[0], array);
List<PlayerInfoData> listData = new ArrayList<>();
for (Object nmsData : (List<?>) nms.PacketPlayOutPlayerInfo_PLAYERS.get(nmsPacket)) {
Object nmsGameMode = nms.PlayerInfoData_getGamemode.invoke(nmsData);
EnumGamemode gameMode = (nmsGameMode == null) ? null : EnumGamemode.valueOf(nmsGameMode.toString());
GameProfile profile = (GameProfile) nms.PlayerInfoData_getProfile.invoke(nmsData);
Object nmsComponent = nms.PlayerInfoData_getDisplayName.invoke(nmsData);
Skin skin = null;
if (!profile.getProperties().get("textures").isEmpty()) {
Property pr = profile.getProperties().get("textures").iterator().next();
skin = new Skin(pr.getValue(), pr.getSignature());
}
Object remoteChatSession = nms.PlayerInfoData_getProfilePublicKeyRecord.invoke(nmsData);
listData.add(
new PlayerInfoData(
profile.getName(),
profile.getId(),
skin,
(boolean) nms.PlayerInfoData_isListed.invoke(nmsData),
(int) nms.PlayerInfoData_getLatency.invoke(nmsData),
gameMode,
nmsComponent == null ? null : new WrappedChatComponent(nmsComponent),
remoteChatSession == null ? null : (UUID) nms.RemoteChatSession$Data_getSessionId.invoke(remoteChatSession),
remoteChatSession == null ? null : nms.RemoteChatSession$Data_getProfilePublicKey.invoke(remoteChatSession)));
}
return new PacketPlayOutPlayerInfo(actions, listData);
} else {
//1.19.2-
EnumPlayerInfoAction action = EnumPlayerInfoAction.valueOf(nms.PacketPlayOutPlayerInfo_ACTION.get(nmsPacket).toString());
List<PlayerInfoData> listData = new ArrayList<>();
for (Object nmsData : (List<?>) nms.PacketPlayOutPlayerInfo_PLAYERS.get(nmsPacket)) {
Object nmsGameMode = nms.PlayerInfoData_getGamemode.invoke(nmsData);
EnumGamemode gameMode = (nmsGameMode == null) ? null : EnumGamemode.valueOf(nmsGameMode.toString());
GameProfile profile = (GameProfile) nms.PlayerInfoData_getProfile.invoke(nmsData);
Object nmsComponent = nms.PlayerInfoData_getDisplayName.invoke(nmsData);
IChatBaseComponent listName = nmsComponent == null ? null : new WrappedChatComponent(nmsComponent);
Skin skin = null;
if (!profile.getProperties().get("textures").isEmpty()) {
Property pr = profile.getProperties().get("textures").iterator().next();
skin = new Skin(pr.getValue(), pr.getSignature());
}
Object profilePublicKey = nms.getMinorVersion() >= 19 ? nms.PlayerInfoData_getProfilePublicKeyRecord.invoke(nmsData) : null;
listData.add(new PlayerInfoData(profile.getName(), profile.getId(), skin, true,
(int) nms.PlayerInfoData_getLatency.invoke(nmsData), gameMode, listName, null, profilePublicKey));
}
return new PacketPlayOutPlayerInfo(action, listData);
}
return new PacketPlayOutPlayerInfo(action, listData);
}
@Override
@@ -388,7 +469,7 @@ public class BukkitPacketBuilder extends PacketBuilder {
}
/**
* Builds entity BossBar packet
* Builds entity packet representing requested BossBar packet using Wither on 1.8- clients.
*
* @param packet
* packet to build
@@ -436,13 +517,15 @@ public class BukkitPacketBuilder extends PacketBuilder {
}
/**
* Converts TAB's IChatBaseComponent into minecraft's component.
* Converts TAB's IChatBaseComponent into minecraft's component using String deserialization.
* If the requested component is found in cache, it is returned. If not, it is created, added into cache and returned.
* If {@code component} is {@code null}, returns {@code null}
*
* @param component
* component to convert
* @param clientVersion
* client version used to decide RGB conversion
* @return converted component
* @return converted component or {@code null} if {@code component} is {@code null}
* @throws ReflectiveOperationException
* if thrown by reflective operation
*/
@@ -9,10 +9,11 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.PipelineInjector;
import me.neznamy.tab.shared.features.sorting.Sorting;
import org.jetbrains.annotations.NotNull;
/**
@@ -66,7 +67,8 @@ public class BukkitPipelineInjector extends PipelineInjector {
@Override
public void write(ChannelHandlerContext context, Object packet, ChannelPromise channelPromise) {
try {
if (nms.PacketPlayOutPlayerInfo.isInstance(packet)) {
if (nms.PacketPlayOutPlayerInfo.isInstance(packet) ||
(nms.ClientboundPlayerInfoRemovePacket != null && nms.ClientboundPlayerInfoRemovePacket.isInstance(packet))) {
super.write(context, TAB.getInstance().getFeatureManager().onPacketPlayOutPlayerInfo(player, packet), channelPromise);
return;
}
@@ -117,9 +119,10 @@ public class BukkitPipelineInjector extends PipelineInjector {
newList.add(entry);
continue;
}
Sorting sorting = (Sorting) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
if (!((TabFeature)TAB.getInstance().getTeamManager()).isDisabledPlayer(p) &&
!TAB.getInstance().getTeamManager().hasTeamHandlingPaused(p) && !teamName.equals(p.getTeamName())) {
logTeamOverride(teamName, p.getName(), p.getTeamName());
!TAB.getInstance().getTeamManager().hasTeamHandlingPaused(p) && !teamName.equals(sorting.getShortTeamName(p))) {
logTeamOverride(teamName, p.getName(), sorting.getShortTeamName(p));
} else {
newList.add(entry);
}
@@ -33,15 +33,33 @@ public class BukkitPlaceholderRegistry extends UniversalPlaceholderRegistry {
/** Number formatter for 2 decimal places */
private final NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.US);
/** JavaPlugin reference for registering events */
private final JavaPlugin plugin;
/** Vault Chat hook */
private Chat chat;
private final Plugin essentials = Bukkit.getPluginManager().getPlugin(TabConstants.Plugin.ESSENTIALS);
/** NMS server to get TPS from on spigot */
private Object server;
/** TPS field*/
private Field recentTps;
/** Detection for presence of Paper's TPS getter */
private Method paperTps;
/** Detection for presence of Paper's MSPT getter */
private Method paperMspt;
/** Detection for presence of Purpur's AFK getter */
private Method purpurIsAfk;
/**
* Constructs new instance with given parameter and loads hooks
*
* @param plugin
* reference to the plugin's main class
*/
public BukkitPlaceholderRegistry(JavaPlugin plugin) {
this.plugin = plugin;
numberFormat.setMaximumFractionDigits(2);
@@ -63,7 +81,6 @@ public class BukkitPlaceholderRegistry extends UniversalPlaceholderRegistry {
@SuppressWarnings("deprecation")
@Override
public void registerPlaceholders(PlaceholderManager manager) {
super.registerPlaceholders(manager);
manager.registerPlayerPlaceholder(TabConstants.Placeholder.DISPLAY_NAME, 500, p -> ((Player) p.getPlayer()).getDisplayName());
if (paperTps != null) {
manager.registerServerPlaceholder(TabConstants.Placeholder.TPS, 1000, () -> formatTPS(Bukkit.getTPS()[0]));
@@ -81,6 +98,7 @@ public class BukkitPlaceholderRegistry extends UniversalPlaceholderRegistry {
if (paperMspt != null) {
manager.registerServerPlaceholder(TabConstants.Placeholder.MSPT, 1000, () -> numberFormat.format(Bukkit.getAverageTickTime()));
}
Plugin essentials = Bukkit.getPluginManager().getPlugin(TabConstants.Plugin.ESSENTIALS);
manager.registerPlayerPlaceholder(TabConstants.Placeholder.AFK, 500, p -> {
if (essentials != null && ((Essentials)essentials).getUser(p.getUniqueId()).isAfk()) return true;
return purpurIsAfk != null && ((Player)p.getPlayer()).isAfk();
@@ -93,8 +111,8 @@ public class BukkitPlaceholderRegistry extends UniversalPlaceholderRegistry {
Listener nickListener = new Listener() {
@EventHandler
public void onNickChange(NickChangeEvent e) {
String name = e.getValue() == null ? e.getAffected().getName() : e.getValue();
TabPlayer player = TAB.getInstance().getPlayer(e.getAffected().getUUID());
String name = e.getValue() == null ? e.getController().getName() : e.getValue();
TabPlayer player = TAB.getInstance().getPlayer(e.getController().getUUID());
if (player == null) return;
nick.updateValue(player, name);
}
@@ -113,8 +131,16 @@ public class BukkitPlaceholderRegistry extends UniversalPlaceholderRegistry {
manager.registerServerPlaceholder(TabConstants.Placeholder.VAULT_SUFFIX, -1, () -> "");
}
manager.registerPlayerPlaceholder(TabConstants.Placeholder.HEALTH, 100, p -> (int) Math.ceil(((Player) p.getPlayer()).getHealth()));
super.registerPlaceholders(manager);
}
/**
* Formats TPS using number formatter with 2 decimal places.
*
* @param tps
* TPS to format
* @return Formatted TPS as a String
*/
private String formatTPS(double tps) {
return numberFormat.format(Math.min(20, tps));
}
@@ -1,12 +1,11 @@
package me.neznamy.tab.platforms.bukkit;
import com.earth2me.essentials.Essentials;
import com.viaversion.viaversion.api.Via;
import me.clip.placeholderapi.PlaceholderAPI;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.platforms.bukkit.event.TabLoadEvent;
import me.neznamy.tab.platforms.bukkit.event.TabPlayerLoadEvent;
import me.neznamy.tab.api.chat.rgb.RGBUtils;
import me.neznamy.tab.platforms.bukkit.features.BukkitTabExpansion;
import me.neznamy.tab.platforms.bukkit.features.PerWorldPlayerList;
import me.neznamy.tab.platforms.bukkit.features.PetFix;
@@ -15,10 +14,10 @@ import me.neznamy.tab.platforms.bukkit.features.unlimitedtags.BukkitNameTagX;
import me.neznamy.tab.platforms.bukkit.permission.Vault;
import me.neznamy.tab.shared.Platform;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.shared.features.PlaceholderManagerImpl;
import me.neznamy.tab.shared.features.bossbar.BossBarManagerImpl;
import me.neznamy.tab.shared.features.nametags.NameTag;
import me.neznamy.tab.shared.features.sorting.Sorting;
import me.neznamy.tab.shared.permission.LuckPerms;
import me.neznamy.tab.shared.permission.None;
import me.neznamy.tab.shared.permission.PermissionPlugin;
@@ -43,7 +42,6 @@ public class BukkitPlatform extends Platform {
/** Variables checking presence of other plugins to hook into */
private final boolean placeholderAPI = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.PLACEHOLDER_API);
private boolean libsDisguises = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.LIBS_DISGUISES);
private final Plugin essentials = Bukkit.getPluginManager().getPlugin(TabConstants.Plugin.ESSENTIALS);
private Plugin viaVersion;
private final boolean protocolSupport = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.PROTOCOL_SUPPORT);
@@ -88,6 +86,7 @@ public class BukkitPlatform extends Platform {
tab.getFeatureManager().registerFeature(TabConstants.Feature.PIPELINE_INJECTION, new BukkitPipelineInjector());
new BukkitPlaceholderRegistry(plugin).registerPlaceholders(tab.getPlaceholderManager());
if (tab.getConfiguration().getConfig().getBoolean("scoreboard-teams.enabled", true)) {
tab.getFeatureManager().registerFeature(TabConstants.Feature.SORTING, new Sorting());
if (tab.getConfiguration().getConfig().getBoolean("scoreboard-teams.unlimited-nametag-mode.enabled", false) && tab.getServerVersion().getMinorVersion() >= 8) {
tab.getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS, new BukkitNameTagX(plugin));
} else {
@@ -182,16 +181,6 @@ public class BukkitPlatform extends Platform {
}
}
@Override
public void callLoadEvent() {
Bukkit.getPluginManager().callEvent(new TabLoadEvent());
}
@Override
public void callLoadEvent(TabPlayer player) {
Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, () -> Bukkit.getPluginManager().callEvent(new TabPlayerLoadEvent(player)));
}
/**
* Returns status of LibsDisguises plugin presence
*
@@ -213,20 +202,6 @@ public class BukkitPlatform extends Platform {
libsDisguises = enabled;
}
/**
* Returns Essentials' main class if the plugin is installed, {@code null} if not
*
* @return Essentials instance or {@code null} if plugin is not installed
*/
public Essentials getEssentials() {
return (Essentials) essentials;
}
@Override
public String getConfigName() {
return "bukkitconfig.yml";
}
/**
* Gets protocol version of requested player and returns it.
*
@@ -298,6 +273,7 @@ public class BukkitPlatform extends Platform {
/**
* Sends console message using ConsoleCommandSender, due to
* Paper not translating colors correctly in Logger messages
* and to allow RGB (at least on Paper, doesn't work on spigot)
*
* @param message
* Message to send
@@ -306,6 +282,9 @@ public class BukkitPlatform extends Platform {
*/
@Override
public void sendConsoleMessage(String message, boolean translateColors) {
Bukkit.getConsoleSender().sendMessage("[TAB] " + (translateColors ? EnumChatFormat.color(message) : message));
Bukkit.getConsoleSender().sendMessage("[TAB] " + (translateColors ?
EnumChatFormat.color(RGBUtils.getInstance().convertToBukkitFormat(message,
TAB.getInstance().getServerVersion().getMinorVersion() >= 16))
: message));
}
}
@@ -16,7 +16,6 @@ import org.bukkit.entity.Player;
import org.bukkit.metadata.MetadataValue;
import org.bukkit.potion.PotionEffectType;
import com.earth2me.essentials.Essentials;
import com.mojang.authlib.GameProfile;
import com.viaversion.viaversion.api.Via;
import com.viaversion.viaversion.api.legacy.bossbar.BossColor;
@@ -26,7 +25,7 @@ import com.viaversion.viaversion.api.legacy.bossbar.BossStyle;
import io.netty.channel.Channel;
import me.neznamy.tab.api.chat.rgb.RGBUtils;
import me.neznamy.tab.api.protocol.PacketPlayOutBoss;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
import me.neznamy.tab.shared.ITabPlayer;
import me.neznamy.tab.shared.TAB;
@@ -56,7 +55,7 @@ public class BukkitTabPlayer extends ITabPlayer {
* Player's protocol network id
*/
public BukkitTabPlayer(Player p, int protocolVersion){
super(p, p.getUniqueId(), p.getName(), "N/A", p.getWorld().getName(), protocolVersion, true);
super(p, p.getUniqueId(), p.getName(), TAB.getInstance().getConfiguration().getServerName(), p.getWorld().getName(), protocolVersion, true);
try {
handle = NMSStorage.getInstance().getHandle.invoke(player);
playerConnection = NMSStorage.getInstance().PLAYER_CONNECTION.get(handle);
@@ -277,10 +276,7 @@ public class BukkitTabPlayer extends ITabPlayer {
@Override
public boolean isVanished() {
Essentials essentials = ((BukkitPlatform)TAB.getInstance().getPlatform()).getEssentials();
if (essentials != null && essentials.getUser(getUniqueId()).isVanished()) return true;
List<MetadataValue> metadata = getPlayer().getMetadata("vanished");
return !metadata.isEmpty() && metadata.get(0).asBoolean();
return getPlayer().getMetadata("vanished").stream().anyMatch(MetadataValue::asBoolean);
}
@SuppressWarnings("deprecation")
@@ -293,15 +289,36 @@ public class BukkitTabPlayer extends ITabPlayer {
public Object getProfilePublicKey() {
if (NMSStorage.getInstance().getMinorVersion() < 19) return null;
try {
Object key = NMSStorage.getInstance().EntityHuman_ProfilePublicKey.get(handle);
if (key == null) return null;
return NMSStorage.getInstance().ProfilePublicKey_getRecord.invoke(key);
if (NMSStorage.getInstance().RemoteChatSession != null) {
//1.19.3+
Object session = NMSStorage.getInstance().EntityPlayer_RemoteChatSession.get(handle);
if (session == null) return null;
Object key = NMSStorage.getInstance().RemoteChatSession_getProfilePublicKey.invoke(session);
return NMSStorage.getInstance().ProfilePublicKey_getRecord.invoke(key);
} else {
Object key = NMSStorage.getInstance().EntityHuman_ProfilePublicKey.get(handle);
if (key == null) return null;
return NMSStorage.getInstance().ProfilePublicKey_getRecord.invoke(key);
}
} catch (ReflectiveOperationException e) {
TAB.getInstance().getErrorManager().printError("Failed to get profile key of " + getName(), e);
return null;
}
}
@Override
public UUID getChatSessionId() {
if (NMSStorage.getInstance().RemoteChatSession != null) return null;
try {
Object session = NMSStorage.getInstance().EntityPlayer_RemoteChatSession.get(handle);
if (session == null) return null;
return (UUID) NMSStorage.getInstance().RemoteChatSession_getSessionId.invoke(session);
} catch (ReflectiveOperationException e) {
TAB.getInstance().getErrorManager().printError("Failed to get chat session of " + getName(), e);
return null;
}
}
@Override
public Channel getChannel() {
try {
@@ -1,25 +1,25 @@
package me.neznamy.tab.platforms.bukkit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Level;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.platforms.bukkit.nms.*;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.util.SupplierWithException;
import me.neznamy.tab.platforms.bukkit.nms.storage.*;
import me.neznamy.tab.shared.TAB;
import org.bstats.bukkit.Metrics;
import org.bstats.charts.SimplePie;
import org.bukkit.Bukkit;
import org.bukkit.command.*;
import org.bukkit.entity.Player;
import org.bukkit.plugin.java.JavaPlugin;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.api.TabConstants;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Main class for Bukkit platform
*/
@@ -55,7 +55,7 @@ public class Main extends JavaPlugin {
@Override
public void onDisable() {
//null check due to L34 return making L37 not run
//null check due to compatibility check making instance not get set on unsupported versions
if (TAB.getInstance() != null) TAB.getInstance().unload();
}
@@ -72,37 +72,42 @@ public class Main extends JavaPlugin {
"v1_7_R1", "v1_7_R2", "v1_7_R3", "v1_7_R4", "v1_8_R1", "v1_8_R2", "v1_8_R3",
"v1_9_R1", "v1_9_R2", "v1_10_R1", "v1_11_R1", "v1_12_R1", "v1_13_R1", "v1_13_R2",
"v1_14_R1", "v1_15_R1", "v1_16_R1", "v1_16_R2", "v1_16_R3", "v1_17_R1", "v1_18_R1",
"v1_18_R2", "v1_19_R1");
"v1_18_R2", "v1_19_R1", "v1_19_R2");
String serverPackage = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
try {
long time = System.currentTimeMillis();
int minorVersion = Integer.parseInt(serverPackage.split("_")[1]);
if (minorVersion >= 17) {
try {
NMSStorage.setInstance(new BukkitModernNMSStorage());
} catch (ClassNotFoundException e) {
NMSStorage.setInstance(new MojangModernNMSStorage());
}
} else {
NMSStorage.setInstance(new LegacyNMSStorage());
}
NMSStorage.setInstance(getNMSLoader());
if (supportedVersions.contains(serverPackage)) {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("[TAB] Loaded NMS hook in " + (System.currentTimeMillis()-time) + "ms"));
return true;
} else {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] No compatibility issue was found, but this plugin version does not claim to support your server package (" + serverPackage + "). This jar has only been tested on 1.5.x - 1.19. Disabling just to stay safe."));
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] No compatibility issue was found, but this plugin version does not claim to support your server package (" + serverPackage + "). This jar has only been tested on 1.5.2 - 1.19.3. Disabling just to stay safe."));
}
} catch (Exception ex) {
} catch (IllegalStateException ex) {
if (supportedVersions.contains(serverPackage)) {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Your server version is marked as compatible, but a compatibility issue was found. Please report the error below (include your server version & fork too)"));
getLogger().log(Level.SEVERE, "", ex);
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Your server version is marked as compatible, but a compatibility issue was found. Please report this issue (include your server version & fork too)"));
} else {
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Your server version is completely unsupported. This plugin version only supports 1.5.x - 1.19. Disabling."));
Bukkit.getConsoleSender().sendMessage(EnumChatFormat.color("&c[TAB] Your server version is completely unsupported. This plugin version only supports 1.5.2 - 1.19.3. Disabling."));
}
}
return false;
}
private NMSStorage getNMSLoader() {
List<SupplierWithException<NMSStorage>> loaders = new ArrayList<>();
loaders.add(BukkitLegacyNMSStorage::new);
loaders.add(BukkitModernNMSStorage::new);
loaders.add(MojangModernNMSStorage::new);
loaders.add(ThermosNMSStorage::new);
for (SupplierWithException<NMSStorage> loader : loaders) {
try {
return loader.get();
} catch (Exception ignored){}
}
throw new IllegalStateException("Unsupported server version");
}
/**
* Command handler for /tab command
*/
@@ -1,25 +0,0 @@
package me.neznamy.tab.platforms.bukkit.event;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
/**
* Bukkit event that is called each time TAB fully loads. This includes server startup, reload,
* /tab reload and /plugman reload tab
*
* @deprecated for removal, use {@link me.neznamy.tab.api.event.plugin.TabLoadEvent}
*/
@Deprecated
public class TabLoadEvent extends Event {
private static final HandlerList handlers = new HandlerList();
@Override
public HandlerList getHandlers(){
return getHandlerList();
}
public static HandlerList getHandlerList() {
return handlers;
}
}
@@ -1,36 +0,0 @@
package me.neznamy.tab.platforms.bukkit.event;
import org.bukkit.event.Event;
import org.bukkit.event.HandlerList;
import me.neznamy.tab.api.TabPlayer;
/**
* Bukkit event that is called when player is successfully loaded after joining. This also includes plugin reloading.
*
* @deprecated for removal, use {@link me.neznamy.tab.api.event.player.PlayerLoadEvent}
*/
@Deprecated
public class TabPlayerLoadEvent extends Event {
private static final HandlerList handlers = new HandlerList();
private final TabPlayer player;
public TabPlayerLoadEvent(TabPlayer player) {
this.player = player;
}
@Override
public HandlerList getHandlers(){
return getHandlerList();
}
public static HandlerList getHandlerList() {
return handlers;
}
public TabPlayer getPlayer() {
return player;
}
}
@@ -6,6 +6,7 @@ import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.features.TabExpansion;
import org.bukkit.entity.Player;
import org.jetbrains.annotations.NotNull;
@@ -19,7 +20,7 @@ import java.util.WeakHashMap;
*/
public class BukkitTabExpansion extends PlaceholderExpansion implements TabExpansion {
/** Map holding all placeholder values for all players */
/** Map holding all values for all players for easy and high-performance access */
private final Map<TabPlayer, Map<String, String>> values = new WeakHashMap<>();
@Override
@@ -45,9 +46,16 @@ public class BukkitTabExpansion extends PlaceholderExpansion implements TabExpan
@Override
public String onPlaceholderRequest(Player player, @NotNull String identifier){
if (identifier.startsWith("replace_")) {
String placeholder = "%" + identifier.substring(8) + "%";
String output = PlaceholderAPI.setPlaceholders(player, placeholder);
return TabAPI.getInstance().getPlaceholderManager().findReplacement(placeholder, output);
String text = "%" + identifier.substring(8) + "%";
String textBefore;
do {
textBefore = text;
for (String placeholder : TAB.getInstance().getPlaceholderManager().detectPlaceholders(text)) {
text = text.replace(placeholder, TabAPI.getInstance().getPlaceholderManager().findReplacement(placeholder,
PlaceholderAPI.setPlaceholders(player, placeholder)));
}
} while (!textBefore.equals(text));
return text;
}
if (identifier.startsWith("placeholder_")) {
TabAPI.getInstance().getPlaceholderManager().addUsedPlaceholder("%" + identifier.substring(12) + "%", (TabFeature) TabAPI.getInstance().getPlaceholderManager());
@@ -25,10 +25,7 @@ import me.neznamy.tab.shared.TAB;
@SuppressWarnings("deprecation")
public class PerWorldPlayerList extends TabFeature implements Listener {
//plugin instance
private final JavaPlugin plugin;
//config options
/** Config options */
private final boolean allowBypass = TabAPI.getInstance().getConfig().getBoolean("per-world-playerlist.allow-bypass-permission", false);
private final List<String> ignoredWorlds = TabAPI.getInstance().getConfig().getStringList("per-world-playerlist.ignore-effect-in-worlds", Arrays.asList("ignoredworld", "build"));
private final Map<String, List<String>> sharedWorlds = TabAPI.getInstance().getConfig().getConfigurationSection("per-world-playerlist.shared-playerlist-world-groups");
@@ -41,14 +38,13 @@ public class PerWorldPlayerList extends TabFeature implements Listener {
*/
public PerWorldPlayerList(JavaPlugin plugin) {
super("Per world PlayerList", null);
this.plugin = plugin;
TabAPI.getInstance().debug(String.format("Loaded PerWorldPlayerList feature with parameters allowBypass=%s, ignoredWorlds=%s, sharedWorlds=%s", allowBypass, ignoredWorlds, sharedWorlds));
Bukkit.getPluginManager().registerEvents(this, plugin);
TabAPI.getInstance().debug(String.format("Loaded PerWorldPlayerList feature with parameters allowBypass=%s, ignoredWorlds=%s, sharedWorlds=%s", allowBypass, ignoredWorlds, sharedWorlds));
}
@Override
public void load(){
Bukkit.getScheduler().runTask(plugin, () -> Bukkit.getOnlinePlayers().forEach(this::checkPlayer));
Bukkit.getOnlinePlayers().forEach(this::checkPlayer);
}
@Override
@@ -75,6 +71,14 @@ public class PerWorldPlayerList extends TabFeature implements Listener {
TAB.getInstance().getCPUManager().addTime(getFeatureName(), TabConstants.CpuUsageCategory.WORLD_SWITCH, System.nanoTime()-time);
}
/**
* Performs visibility check on the player. Shows players this player should see and does not,
* hides players the player should not see, shows the player to those who should see player and hides
* from those, who should not see.
*
* @param p
* Player to update
*/
private void checkPlayer(Player p) {
for (Player all : Bukkit.getOnlinePlayers()){
if (all == p) continue;
@@ -85,6 +89,14 @@ public class PerWorldPlayerList extends TabFeature implements Listener {
}
}
/**
* Returns {@code true} if viewer should see target player, {@code false} if not.
* @param viewer
* Player viewing the TabList
* @param target
* Target displayed in the TabList
* @return {@code true} if viewer should see target, {@code false} if not.
*/
private boolean shouldSee(Player viewer, Player target) {
if (target == viewer) return true;
if ((allowBypass && viewer.hasPermission(TabConstants.Permission.PER_WORLD_PLAYERLIST_BYPASS)) || ignoredWorlds.contains(viewer.getWorld().getName())) return true;
@@ -3,7 +3,7 @@ package me.neznamy.tab.platforms.bukkit.features;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherItem;
@@ -22,13 +22,13 @@ import java.util.WeakHashMap;
*/
public class PetFix extends TabFeature {
//nms storage
/** NMS Storage reference for quick access */
private final NMSStorage nms = NMSStorage.getInstance();
//DataWatcher position of pet owner field
/** DataWatcher position of pet owner field */
private final int petOwnerPosition = getPetOwnerPosition();
//logger of last interacts to prevent feature not working on 1.16
/** Logger of last interacts to prevent feature not working on 1.16 */
private final WeakHashMap<TabPlayer, Long> lastInteractFix = new WeakHashMap<>();
/**
@@ -94,6 +94,13 @@ public class PetFix extends TabFeature {
return false;
}
/**
* Checks if the provided entity use action is INTERACT or not.
*
* @param action
* Action to check
* @return {@code true} if action is INTERACT, {@code false} if not.
*/
private boolean isInteract(Object action) {
if (nms.getMinorVersion() >= 17) {
return nms.PacketPlayInUseEntity$d.isInstance(action);
@@ -118,8 +125,16 @@ public class PetFix extends TabFeature {
try {
for (Object item : items) {
if (item == null) continue;
if (nms.DataWatcherObject_SLOT.getInt(nms.DataWatcherItem_TYPE.get(item)) == petOwnerPosition) {
Object value = nms.DataWatcherItem_VALUE.get(item);
int slot;
Object value;
if (nms.is1_19_3Plus()) {
slot = nms.DataWatcher$DataValue_POSITION.getInt(item);
value = nms.DataWatcher$DataValue_VALUE.get(item);
} else {
slot = nms.DataWatcherObject_SLOT.getInt(nms.DataWatcherItem_TYPE.get(item));
value = nms.DataWatcherItem_VALUE.get(item);
}
if (slot == petOwnerPosition) {
if (value instanceof Optional || value instanceof com.google.common.base.Optional) {
removedEntry = item;
}
@@ -21,11 +21,11 @@ import me.neznamy.tab.shared.features.bossbar.BossBarManagerImpl;
*/
public class WitherBossBar extends BossBarManagerImpl implements Listener {
//distance of wither in blocks
/** Distance of the Wither in blocks */
private static final int WITHER_DISTANCE = 60;
/**
* Constructs a new instance of the class
* Constructs a new instance of the class and registers events and task
*
* @param plugin
* plugin instance
@@ -44,6 +44,9 @@ public class WitherBossBar extends BossBarManagerImpl implements Listener {
teleport();
}
/**
* Updates Wither's location for all online players
*/
private void teleport() {
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
if (p.getVersion().getMinorVersion() > 8) continue; //sending VV packets to those
@@ -18,48 +18,63 @@ import org.bukkit.entity.Pose;
import java.util.UUID;
/**
* A class representing an armor stand attached to a player (if the feature is enabled)
* A class representing an armor stand attached to a player
*/
public class BukkitArmorStand implements ArmorStand {
//entity id counter to pick unique entity IDs
/** Entity id counter to pick unique entity ID for each armor stand */
private static int idCounter = 2000000000;
//NameTag feature
/** NameTag feature */
private final BukkitNameTagX manager = (BukkitNameTagX) TabAPI.getInstance().getFeatureManager().getFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS);
/** Armor stand manager which this armor stand belongs to */
private final BukkitArmorStandManager asm;
//owner of the armor stand
/** Owner of the armor stand */
private final TabPlayer owner;
//instance of Bukkit player
/** Owner as a Bukkit player */
private final Player player;
//offset in blocks, 0 for original height
/** Offset in blocks, 0 for original height */
private double yOffset;
//entity ID of this armor stand
private final int entityId = idCounter++;
//unique ID of this armor stand
private final UUID uuid = UUID.randomUUID();
//sneaking flag of armor stands
private boolean sneaking;
//armor stand visibility
private boolean visible;
//property dedicated to this armor stand
private final Property property;
//if offset is static or dynamic based on other armor stands
/** If offset is static, or dynamic based on other armor stands */
private final boolean staticOffset;
//entity destroy packet
/** Entity ID of this armor stand */
private final int entityId = idCounter++;
/** Unique ID of this armor stand */
private final UUID uuid = UUID.randomUUID();
/** Sneaking flag of armor stands */
private boolean sneaking;
/** Armor stand visibility */
private boolean visible;
/** Refresh property dedicated to this armor stand */
private final Property property;
/** Entity destroy packet */
private final PacketPlayOutEntityDestroy destroyPacket = new PacketPlayOutEntityDestroy(entityId);
/**
* Constructs new instance with given parameters.
*
* @param asm
* Armor stand manager which this armor stand belongs to
* @param owner
* Owner of the armor stand
* @param propertyName
* Name of refresh property to use
* @param yOffset
* Offset in blocks
* @param staticOffset
* {@code true} if offset is static, {@code false} if not
*/
public BukkitArmorStand(BukkitArmorStandManager asm, TabPlayer owner, String propertyName, double yOffset, boolean staticOffset) {
this.asm = asm;
this.owner = owner;
@@ -189,7 +204,7 @@ public class BukkitArmorStand implements ArmorStand {
/**
* Returns general visibility rule for everyone with limited info
*
* @return true if armor stand should be visible, false if not
* @return {@code true} if armor stand should be visible, {@code false} if not
*/
public boolean getVisibility() {
if (manager.isArmorStandsAlwaysVisible()) return true;
@@ -198,11 +213,13 @@ public class BukkitArmorStand implements ArmorStand {
}
/**
* Returns general location where armor stand should be at time of calling
* Returns location where armor stand should be at time of calling.
* This takes into account everything that affects height, including
* viewer's game version.
*
* @param viewer
* Player looking at the armor stand
* @return Location where armor stand should be for everyone
* @return Location where armor stand should be for specified viewer
*/
public Location getLocation(TabPlayer viewer) {
double x = player.getLocation().getX();
@@ -255,6 +272,10 @@ public class BukkitArmorStand implements ArmorStand {
return player.getLocation().getY();
}
/**
* Returns {@code true} if owner is swimming, {@code false} if not
* @return {@code true} if owner is swimming, {@code false} if not
*/
private boolean isSwimming() {
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 14 && player.getPose() == Pose.SWIMMING) return true;
return TabAPI.getInstance().getServerVersion().getMinorVersion() == 13 && player.isSwimming();
@@ -291,11 +312,12 @@ public class BukkitArmorStand implements ArmorStand {
}
/**
* Returns true if display name is in fact empty, for example only containing color codes
* Returns {@code true} if display name is in fact empty,
* for example only containing color codes, {@code false} if not.
*
* @param displayName
* string to check
* @return true if it's empty, false if not
* @return {@code true} if it's empty, {@code false} if not
*/
private boolean isNameVisiblyEmpty(String displayName) {
if (displayName.length() == 0) return true;
@@ -16,17 +16,26 @@ import java.util.Map;
*/
public class BukkitArmorStandManager implements ArmorStandManager {
//map of registered armor stands
/** Map of registered armor stands by name */
private final Map<String, ArmorStand> armorStands = new LinkedHashMap<>();
//players in entity tracking range
private final List<TabPlayer> nearbyPlayers = new ArrayList<>();
//array to iterate over to avoid concurrent modification and slightly boost performance & memory
/** Armor stands in an array for speed while iterating */
private ArmorStand[] armorStandArray = new ArmorStand[0];
/** Players in entity tracking range of owner */
private final List<TabPlayer> nearbyPlayers = new ArrayList<>();
/** Nearby players in an array for speed while iterating */
private TabPlayer[] nearbyPlayerArray = new TabPlayer[0];
/**
* Constructs new instance with given parameters and loads armor stands.
*
* @param nameTagX
* Main feature
* @param owner
* Owner of this armor stand manager
*/
public BukkitArmorStandManager(NameTagX nameTagX, TabPlayer owner) {
owner.setProperty(nameTagX, TabConstants.Property.NAMETAG, owner.getProperty(TabConstants.Property.TAGPREFIX).getCurrentRawValue()
+ owner.getProperty(TabConstants.Property.CUSTOMTAGNAME).getCurrentRawValue()
@@ -61,7 +70,7 @@ public class BukkitArmorStandManager implements ArmorStandManager {
/**
* Returns array of nearby players
* @return array of nearby players
* @return array of nearby players
*/
public TabPlayer[] getNearbyPlayers(){
return nearbyPlayerArray;
@@ -79,11 +88,11 @@ public class BukkitArmorStandManager implements ArmorStandManager {
}
/**
* Returns true if manager contains armor stand with specified entity id, false if not
* Returns {@code true} if manager contains armor stand with specified entity id, {@code false} if not
*
* @param entityId
* entity id
* @return true if armor stand with specified entity id exists, false if not
* @return {@code true} if armor stand with specified entity id exists, {@code false} if not
*/
public boolean hasArmorStandWithID(int entityId) {
for (ArmorStand a : armorStandArray) {
@@ -11,17 +11,19 @@ import org.bukkit.event.HandlerList;
import org.bukkit.plugin.java.JavaPlugin;
/**
* The core class for unlimited NameTag mode
* The core class for unlimited NameTag mode on Bukkit
*/
public class BukkitNameTagX extends NameTagX {
//bukkit event listener
/** Bukkit event listener */
private final EventListener eventListener = new EventListener(this);
/** Vehicle manager reference */
private final VehicleRefresher vehicleManager = new VehicleRefresher(this);
/**
* Constructs new instance with given parameters and loads config options
* Constructs new instance with given parameter, loads config options, registers events
* and registers sub-features.
*
* @param plugin
* plugin instance
@@ -45,6 +47,9 @@ public class BukkitNameTagX extends NameTagX {
startVisibilityRefreshTask();
}
/**
* Starts task checking for player visibility to hide armor stands of invisible players.
*/
private void startVisibilityRefreshTask() {
TabAPI.getInstance().getThreadManager().startRepeatingMeasuredTask(500, this, TabConstants.CpuUsageCategory.REFRESHING_NAME_TAG_VISIBILITY, () -> {
@@ -76,6 +81,16 @@ public class BukkitNameTagX extends NameTagX {
return vehicleManager != null && vehicleManager.isOnBoat(player);
}
/**
* Spawns armor stands of target player to viewer if all requirements are met.
* These include players being in the same world, distance being less than 48 blocks
* and target player being visible to viewer.
*
* @param viewer
* Player viewing armor stands
* @param target
* Target player with armor stands
*/
private void spawnArmorStands(TabPlayer viewer, TabPlayer target) {
if (viewer.getVersion().getMinorVersion() < 8) return;
if (target == viewer || isPlayerDisabled(target)) return;
@@ -163,6 +178,10 @@ public class BukkitNameTagX extends NameTagX {
return (BukkitArmorStandManager) armorStandManagerMap.get(player);
}
/**
* Returns {@link #vehicleManager}
* @return {@link #vehicleManager}
*/
public VehicleRefresher getVehicleManager() {
return vehicleManager;
}
@@ -10,15 +10,16 @@ import org.bukkit.event.player.PlayerRespawnEvent;
import org.bukkit.event.player.PlayerToggleSneakEvent;
/**
* The event listener part for securing proper functionality of armor stands
* Class listening to Bukkit events which the feature requires and are
* sufficient, without packets being requires.
*/
public class EventListener implements Listener {
//the NameTag feature handler
/** Reference to the main feature */
private final BukkitNameTagX feature;
/**
* Constructs new instance with given parameters
* Constructs new instance with given parameter
*
* @param feature
* NameTag feature handler
@@ -4,7 +4,7 @@ import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
@@ -13,22 +13,25 @@ import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* The packet listening part for securing proper functionality of armor stands
* The packet listening part for securing proper functionality of armor stands.
* Bukkit events are too unreliable and delayed/ahead which causes de-sync
* if trying to listen to move event.
* For entering/leaving tracking range there are no events and
* periodic / move-triggered distance checks would cause high CPU usage.
*/
public class PacketListener extends TabFeature {
//main feature
/** Reference to the main feature */
private final BukkitNameTagX nameTagX;
//player data by entityId, used for better performance
/** A player map by entity id, used for better performance */
private final Map<Integer, TabPlayer> entityIdMap = new ConcurrentHashMap<>();
//nms storage
/** Reference to NMS storage for quick access */
private final NMSStorage nms = NMSStorage.getInstance();
/**
* Constructs new instance with given parameters and loads config options
* Constructs new instance with given parameter
*
* @param nameTagX
* main feature
@@ -100,7 +103,10 @@ public class PacketListener extends TabFeature {
}
/**
* Processes entity move packet
* Processes entity move packet. If entity ID belongs to a player,
* armor stands of that player are teleported to player who received the packet.
* If it belongs to a vehicle carrying a player, that player's armor stands are
* teleported as well.
*
* @param receiver
* packet receiver
@@ -126,7 +132,16 @@ public class PacketListener extends TabFeature {
}
}
}
/**
* Processes named entity spawn packet and spawns armor stands if
* entity ID belongs to an online player.
*
* @param receiver
* packet receiver
* @param entityId
* spawned entity
*/
private void onEntitySpawn(TabPlayer receiver, int entityId) {
TabPlayer spawnedPlayer = entityIdMap.get(entityId);
if (spawnedPlayer != null && spawnedPlayer.isLoaded() && !nameTagX.isPlayerDisabled(spawnedPlayer)) {
@@ -135,18 +150,45 @@ public class PacketListener extends TabFeature {
}
}
/**
* Processes entity destroy packet and destroys armor stands if
* entity ID belongs to an online player.
*
* @param receiver
* packet receiver
* @param entities
* de-spawned entities
*/
private void onEntityDestroy(TabPlayer receiver, List<Integer> entities) {
for (int entity : entities) {
onEntityDestroy(receiver, entity);
}
}
/**
* Processes entity destroy packet and destroys armor stands if
* entity ID belongs to an online player.
*
* @param receiver
* packet receiver
* @param entities
* de-spawned entities
*/
private void onEntityDestroy(TabPlayer receiver, int... entities) {
for (int entity : entities) {
onEntityDestroy(receiver, entity);
}
}
/**
* Processes entity destroy packet and destroys armor stands if
* entity ID belongs to an online player.
*
* @param receiver
* packet receiver
* @param entity
* de-spawned entity
*/
private void onEntityDestroy(TabPlayer receiver, int entity) {
TabPlayer deSpawnedPlayer = entityIdMap.get(entity);
if (deSpawnedPlayer != null && deSpawnedPlayer.isLoaded() && !nameTagX.isPlayerDisabled(deSpawnedPlayer)) {
@@ -10,19 +10,35 @@ import org.bukkit.entity.Player;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
* Sub-feature for unlimited name tag mode to secure
* proper functionality when using vehicles.
* A config option allows to disable the feature on boats,
* where it would cause a massive de-sync due to boat movement
* animation.
* Additionally, when entering a vehicle, no move packet is sent
* and therefore manual teleporting of armor stands is required.
*/
public class VehicleRefresher extends TabFeature {
//map of players currently in a vehicle
/** Map of players currently in a vehicle */
private final WeakHashMap<TabPlayer, Entity> playersInVehicle = new WeakHashMap<>();
//map of vehicles carrying players
/** Map of vehicles carrying players */
private final Map<Integer, List<Entity>> vehicles = new ConcurrentHashMap<>();
//set of players currently on boats
/** set of players currently on boats */
private final Set<TabPlayer> playersOnBoats = Collections.newSetFromMap(new WeakHashMap<>());
/** Reference to the main feature */
private final BukkitNameTagX feature;
/**
* Constructs new instance with given parameter and starts tasks.
*
* @param feature
* Main feature
*/
public VehicleRefresher(BukkitNameTagX feature) {
super(feature.getFeatureName(), "Refreshing vehicles");
this.feature = feature;
@@ -96,10 +112,22 @@ public class VehicleRefresher extends TabFeature {
}
}
/**
* Returns {@code true} if the player is in a boat, {@code false} if not
*
* @param p
* Player to check
* @return {@code true} if in a boat, {@code false} if not
*/
public boolean isOnBoat(TabPlayer p) {
return playersOnBoats.contains(p);
}
/**
* Returns vehicle map
*
* @return vehicle map
*/
public Map<Integer, List<Entity>> getVehicles() {
return vehicles;
}
@@ -119,7 +147,7 @@ public class VehicleRefresher extends TabFeature {
if (vehicle.getPassenger() != null) {
return Collections.singletonList(vehicle.getPassenger());
} else {
return new ArrayList<>();
return Collections.emptyList();
}
}
}
@@ -1,41 +0,0 @@
package me.neznamy.tab.platforms.bukkit.nms;
import java.util.Arrays;
/**
* NMS loader for minecraft 1.17+ using Mojang packaging.
*/
public abstract class ModernNMSStorage extends NMSStorage {
protected Class<?> IRegistry;
protected Class<?> Registry;
/**
* Creates new instance, initializes required NMS classes and fields
*
* @throws ReflectiveOperationException
* If any class, field or method fails to load
*/
public ModernNMSStorage() throws ReflectiveOperationException {
}
/**
* Returns class with given potential names in same order
*
* @param names
* possible class names
* @return class for specified names
* @throws ClassNotFoundException
* if class does not exist
*/
protected Class<?> getModernClass(String... names) throws ClassNotFoundException {
for (String name : names) {
try {
return Class.forName(name);
} catch (ClassNotFoundException e) {
//not the first class name in array
}
}
throw new ClassNotFoundException("No class found with possible names " + Arrays.toString(names));
}
}
@@ -4,10 +4,20 @@ import java.util.Arrays;
import me.neznamy.tab.api.protocol.TabPacket;
/**
* Custom class for holding data used in PacketPlayOutEntityDestroy minecraft packet.
*/
public class PacketPlayOutEntityDestroy implements TabPacket {
/** Destroyed entities */
private final int[] entities;
/**
* Constructs new instance with given parameter
*
* @param entities
* Destroyed entities
*/
public PacketPlayOutEntityDestroy(int... entities) {
this.entities = entities;
}
@@ -17,6 +27,10 @@ public class PacketPlayOutEntityDestroy implements TabPacket {
return String.format("PacketPlayOutEntityDestroy{entities=%s}", Arrays.toString(entities));
}
/**
* Returns {@link #entities}
* @return {@link #entities}
*/
public int[] getEntities() {
return entities;
}
@@ -4,12 +4,25 @@ import me.neznamy.tab.api.protocol.TabPacket;
import me.neznamy.tab.api.util.Preconditions;
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
/**
* Custom class for holding data used in PacketPlayOutEntityMetadata minecraft packet.
*/
public class PacketPlayOutEntityMetadata implements TabPacket {
/** Entity's id */
private final int entityId;
/** Entity's data that changed */
private final DataWatcher dataWatcher;
/**
* Constructs new instance with given parameters
*
* @param entityId
* Entity's id
* @param dataWatcher
* Entity's data that changed
*/
public PacketPlayOutEntityMetadata(int entityId, DataWatcher dataWatcher) {
Preconditions.checkNotNull(dataWatcher, "data watcher");
this.entityId = entityId;
@@ -21,10 +34,18 @@ public class PacketPlayOutEntityMetadata implements TabPacket {
return String.format("PacketPlayOutEntityMetadata{entityId=%s,dataWatcher=%s}", entityId, dataWatcher);
}
/**
* Returns {@link #entityId}
* @return {@link #entityId}
*/
public int getEntityId() {
return entityId;
}
/**
* Returns {@link #dataWatcher}
* @return {@link #dataWatcher}
*/
public DataWatcher getDataWatcher() {
return dataWatcher;
}
@@ -5,12 +5,25 @@ import org.bukkit.Location;
import me.neznamy.tab.api.protocol.TabPacket;
/**
* Custom class for holding data used in PacketPlayOutEntityTeleport minecraft packet.
*/
public class PacketPlayOutEntityTeleport implements TabPacket {
/** Entity's id */
private final int entityId;
/** Entity's new location */
private final Location location;
/**
* Constructs new instance with given parameters
*
* @param entityId
* Entity's id
* @param location
* Entity's spawn location
*/
public PacketPlayOutEntityTeleport(int entityId, Location location) {
Preconditions.checkNotNull(location, "location");
this.entityId = entityId;
@@ -22,10 +35,18 @@ public class PacketPlayOutEntityTeleport implements TabPacket {
return String.format("PacketPlayOutEntityTeleport{entityId=%s,location=%s}", entityId, location);
}
/**
* Returns {@link #entityId}
* @return {@link #entityId}
*/
public int getEntityId() {
return entityId;
}
/**
* Returns {@link #location}
* @return {@link #location}
*/
public Location getLocation() {
return location;
}
@@ -9,18 +9,40 @@ import org.bukkit.entity.EntityType;
import me.neznamy.tab.api.protocol.TabPacket;
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
/**
* Custom class for holding data used in PacketPlayOutSpawnEntityLiving minecraft packet.
*/
public class PacketPlayOutSpawnEntityLiving implements TabPacket {
/** Entity's id */
private final int entityId;
/** Entity's UUID (for 1.9+ servers) */
private final UUID uuid;
/** Type of entity */
private final EntityType entityType;
/** Entity's spawn location */
private final Location location;
/** Entity's data for 1.14- servers */
private final DataWatcher dataWatcher;
/**
* Constructs new instance with given parameters
*
* @param entityId
* Entity's id
* @param uuid
* Entity's UUID (for 1.9+ servers)
* @param entityType
* Type of entity
* @param location
* Entity's spawn location
* @param dataWatcher
* Entity's data for 1.14- servers
*/
public PacketPlayOutSpawnEntityLiving(int entityId, UUID uuid, EntityType entityType, Location location, DataWatcher dataWatcher) {
Preconditions.checkNotNull(uuid, "uuid");
Preconditions.checkNotNull(entityType, "entityType");
@@ -38,22 +60,42 @@ public class PacketPlayOutSpawnEntityLiving implements TabPacket {
entityId, uuid, entityType, location, dataWatcher);
}
/**
* Returns {@link #entityId}
* @return {@link #entityId}
*/
public int getEntityId() {
return entityId;
}
/**
* Returns {@link #uuid}
* @return {@link #uuid}
*/
public UUID getUniqueId() {
return uuid;
}
/**
* Returns {@link #entityType}
* @return {@link #entityType}
*/
public EntityType getEntityType() {
return entityType;
}
/**
* Returns {@link #location}
* @return {@link #location}
*/
public Location getLocation() {
return location;
}
/**
* Returns {@link #dataWatcher}
* @return {@link #dataWatcher}
*/
public DataWatcher getDataWatcher() {
return dataWatcher;
}
@@ -5,17 +5,17 @@ import java.util.List;
import java.util.Map;
import me.neznamy.tab.api.util.Preconditions;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
/**
* A class representing the n.m.s.DataWatcher class to make work with it much easier
*/
public class DataWatcher {
//DataWatcher data
/** Watched data */
private final Map<Integer, DataWatcherItem> dataValues = new HashMap<>();
//a helper for easier data write
/** Helper for easier data write */
private final DataWatcherHelper helper = new DataWatcherHelper(this);
/**
@@ -73,19 +73,19 @@ public class DataWatcher {
NMSStorage nms = NMSStorage.getInstance();
Object nmsWatcher;
if (nms.newDataWatcher.getParameterCount() == 1) { //1.7+
Object[] args = new Object[] {null};
nmsWatcher = nms.newDataWatcher.newInstance(args);
nmsWatcher = nms.newDataWatcher.newInstance(new Object[] {null});
} else {
nmsWatcher = nms.newDataWatcher.newInstance();
}
for (DataWatcherItem item : dataValues.values()) {
Object position;
if (nms.getMinorVersion() >= 9) {
position = nms.newDataWatcherObject.newInstance(item.getType().getPosition(), item.getType().getClassType());
position = nms.newDataWatcherObject.newInstance(item.getType().getPosition(), item.getType().getSerializer());
} else {
position = item.getType().getPosition();
}
nms.DataWatcher_REGISTER.invoke(nmsWatcher, position, item.getValue());
if (nms.is1_19_3Plus()) nms.DataWatcher_markDirty.invoke(nmsWatcher, position);
}
return nmsWatcher;
}
@@ -112,4 +112,9 @@ public class DataWatcher {
}
return watcher;
}
@Override
public String toString() {
return String.format("DataWatcher{values=%s}", dataValues);
}
}
@@ -4,7 +4,7 @@ import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.chat.IChatBaseComponent;
import me.neznamy.tab.platforms.bukkit.BukkitPacketBuilder;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
import me.neznamy.tab.shared.TAB;
import java.util.Optional;
@@ -14,13 +14,13 @@ import java.util.Optional;
*/
public class DataWatcherHelper {
//position of armor stand flags
/** Position of armor stand flags */
private final int armorStandFlagsPosition = getArmorStandFlagsPosition();
//original DataWatcher to write to
/** DataWatcher to write to */
private final DataWatcher data;
//data watcher registry
/** Data Watcher registry reference */
private final DataWatcherRegistry registry;
/**
@@ -2,14 +2,17 @@ package me.neznamy.tab.platforms.bukkit.nms.datawatcher;
import me.neznamy.tab.api.TabAPI;
import me.neznamy.tab.api.util.Preconditions;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
/**
* Class representing NMS Data Watcher Item
*/
public class DataWatcherItem {
//type of value (position + data type (1.9+))
/** Value type */
private final DataWatcherObject type;
//actual data value
/** Data value */
private final Object value;
/**
@@ -38,19 +41,35 @@ public class DataWatcherItem {
*/
public static DataWatcherItem fromNMS(Object nmsItem) throws ReflectiveOperationException {
NMSStorage nms = NMSStorage.getInstance();
Object value = nms.DataWatcherItem_VALUE.get(nmsItem);
DataWatcherObject object;
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 9) {
Object nmsObject = nms.DataWatcherItem_TYPE.get(nmsItem);
return new DataWatcherItem(new DataWatcherObject(nms.DataWatcherObject_SLOT.getInt(nmsObject), nms.DataWatcherObject_SERIALIZER.get(nmsObject)), nms.DataWatcherItem_VALUE.get(nmsItem));
object = new DataWatcherObject(nms.DataWatcherObject_SLOT.getInt(nmsObject), nms.DataWatcherObject_SERIALIZER.get(nmsObject));
} else {
return new DataWatcherItem(new DataWatcherObject(nms.DataWatcherItem_TYPE.getInt(nmsItem), null), nms.DataWatcherItem_VALUE.get(nmsItem));
object = new DataWatcherObject(nms.DataWatcherItem_TYPE.getInt(nmsItem), null);
}
return new DataWatcherItem(object, value);
}
/**
* Returns {@link #type}
* @return {@link #type}
*/
public DataWatcherObject getType() {
return type;
}
/**
* Returns {@link #value}
* @return {@link #value}
*/
public Object getValue() {
return value;
}
@Override
public String toString() {
return String.format("DataWatcherItem{type=%s,value=%s}", type, value);
}
}
@@ -5,30 +5,43 @@ package me.neznamy.tab.platforms.bukkit.nms.datawatcher;
*/
public class DataWatcherObject {
//position in DataWatcher
/** Object's position in DataWatcher */
private final int position;
//value class type used since 1.9
private final Object classType;
/** Value encoder for 1.9+ */
private final Object serializer;
/**
* Constructs a new instance of this class with given parameters
*
* @param position
* position in DataWatcher
* @param classType
* value class type
* @param serializer
* Serializer for value in this slot
*/
public DataWatcherObject(int position, Object classType){
public DataWatcherObject(int position, Object serializer){
this.position = position;
this.classType = classType;
this.serializer = serializer;
}
/**
* Returns {@link #position}
* @return {@link #position}
*/
public int getPosition() {
return position;
}
public Object getClassType() {
return classType;
/**
* Returns {@link #serializer}
* @return {@link #serializer}
*/
public Object getSerializer() {
return serializer;
}
@Override
public String toString() {
return String.format("DataWatcherObject{position=%d,serializer=%s}", position, serializer);
}
}
@@ -1,6 +1,6 @@
package me.neznamy.tab.platforms.bukkit.nms.datawatcher;
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
import java.util.Map;
@@ -9,10 +9,19 @@ import java.util.Map;
*/
public class DataWatcherRegistry {
/** Byte encoder */
private Object registryByte;
/** Float encoder */
private Object registryFloat;
/** String encoder */
private Object registryString;
/** Encoder for optional component */
private Object registryOptionalIChatBaseComponent;
/** Boolean encoder */
private Object registryBoolean;
/**
@@ -27,8 +36,13 @@ public class DataWatcherRegistry {
registryFloat = fields.get("c");
registryString = fields.get("d");
if (nms.getMinorVersion() >= 13) {
registryOptionalIChatBaseComponent = fields.get("f");
registryBoolean = fields.get("i");
if (nms.is1_19_3Plus()) {
registryOptionalIChatBaseComponent = fields.get("g");
registryBoolean = fields.get("j");
} else {
registryOptionalIChatBaseComponent = fields.get("f");
registryBoolean = fields.get("i");
}
} else {
registryBoolean = fields.get("h");
}
@@ -43,22 +57,42 @@ public class DataWatcherRegistry {
}
}
/**
* Returns {@link #registryByte}
* @return {@link #registryByte}
*/
public Object getByte() {
return registryByte;
}
/**
* Returns {@link #registryFloat}
* @return {@link #registryFloat}
*/
public Object getFloat() {
return registryFloat;
}
/**
* Returns {@link #registryString}
* @return {@link #registryString}
*/
public Object getString() {
return registryString;
}
/**
* Returns {@link #registryOptionalIChatBaseComponent}
* @return {@link #registryOptionalIChatBaseComponent}
*/
public Object getOptionalComponent() {
return registryOptionalIChatBaseComponent;
}
/**
* Returns {@link #registryBoolean}
* @return {@link #registryBoolean}
*/
public Object getBoolean() {
return registryBoolean;
}
@@ -1,13 +1,12 @@
package me.neznamy.tab.platforms.bukkit.nms;
import me.neznamy.tab.platforms.bukkit.Main;
package me.neznamy.tab.platforms.bukkit.nms.storage;
import java.util.Arrays;
/**
* NMS loader for Minecraft 1.16.5 and lower using old NMS class structure.
* NMS loader for Minecraft 1.5.2 - 1.16.5 using Bukkit mapping.
*/
public class LegacyNMSStorage extends NMSStorage {
@SuppressWarnings({"unchecked", "rawtypes"})
public class BukkitLegacyNMSStorage extends NMSStorage {
/**
* Creates new instance, initializes required NMS classes and fields
@@ -15,7 +14,7 @@ public class LegacyNMSStorage extends NMSStorage {
* @throws ReflectiveOperationException
* If any class, field or method fails to load
*/
public LegacyNMSStorage() throws ReflectiveOperationException {
public BukkitLegacyNMSStorage() throws ReflectiveOperationException {
}
/**
@@ -47,44 +46,33 @@ public class LegacyNMSStorage extends NMSStorage {
* @throws ClassNotFoundException
* if class was not found
*/
private Class<?> getLegacyClass(String name) throws ClassNotFoundException {
try {
return Class.forName("net.minecraft.server." + serverPackage + "." + name);
} catch (ClassNotFoundException | NullPointerException e) {
try {
//modded server?
Class<?> clazz = Main.class.getClassLoader().loadClass("net.minecraft.server." + serverPackage + "." + name);
if (clazz != null) return clazz;
} catch (NullPointerException ignored) {}
throw new ClassNotFoundException(name);
}
public Class<?> getLegacyClass(String name) throws ClassNotFoundException {
return Class.forName("net.minecraft.server." + serverPackage + "." + name);
}
@Override
public void loadNamedFieldsAndMethods() throws ReflectiveOperationException {
PING = getField(EntityPlayer, "ping", "field_71138_i"); // {Bukkit, Thermos}
ScoreboardScore_setScore = getMethod(ScoreboardScore, new String[]{"setScore", "func_96647_c"}, int.class); // {Bukkit, Thermos}
ScoreboardTeam_setAllowFriendlyFire = getMethod(ScoreboardTeam,
new String[]{"setAllowFriendlyFire", "func_96660_a"}, boolean.class); // {Bukkit, Thermos}
ScoreboardTeam_setCanSeeFriendlyInvisibles = getMethod(ScoreboardTeam,
new String[]{"setCanSeeFriendlyInvisibles", "func_98300_b"}, boolean.class); // {Bukkit, Thermos}
PING = getField(EntityPlayer, "ping");
ScoreboardScore_setScore = getMethod(ScoreboardScore, new String[] {"setScore", "c"}, int.class); // 1.5.1+, 1.5
ScoreboardTeam_setAllowFriendlyFire = getMethod(ScoreboardTeam, new String[] {"setAllowFriendlyFire", "a"}, boolean.class); // 1.5.1+, 1.5
ScoreboardTeam_setCanSeeFriendlyInvisibles = getMethod(ScoreboardTeam, new String[] {"setCanSeeFriendlyInvisibles", "b"}, boolean.class); // 1.5.1+, 1.5
if (minorVersion >= 7) {
ChatSerializer_DESERIALIZE = getMethod(ChatSerializer, new String[]{"a", "func_150699_a"}, String.class); // {Bukkit, Thermos}
ChatSerializer_DESERIALIZE = ChatSerializer.getMethod("a", String.class);
}
if (minorVersion >= 8) {
ScoreboardTeam_setNameTagVisibility = getMethod(ScoreboardTeam, "setNameTagVisibility", EnumNameTagVisibility);
ScoreboardTeam_setNameTagVisibility = getMethod(ScoreboardTeam, new String[] {"setNameTagVisibility", "a"}, EnumNameTagVisibility); // {1.8.1+, 1.8}
}
if (minorVersion >= 9) {
DataWatcher_REGISTER = getMethod(DataWatcher, "register", DataWatcherObject, Object.class);
DataWatcher_REGISTER = DataWatcher.getMethod("register", DataWatcherObject, Object.class);
} else {
DataWatcher_REGISTER = getMethod(DataWatcher, new String[]{"a", "func_75682_a"}, int.class, Object.class); // {Bukkit, Thermos}
DataWatcher_REGISTER = DataWatcher.getMethod("a", int.class, Object.class);
}
if (minorVersion >= 13) {
ScoreboardTeam_setPrefix = getMethod(ScoreboardTeam, "setPrefix", IChatBaseComponent);
ScoreboardTeam_setSuffix = getMethod(ScoreboardTeam, "setSuffix", IChatBaseComponent);
ScoreboardTeam_setPrefix = ScoreboardTeam.getMethod("setPrefix", IChatBaseComponent);
ScoreboardTeam_setSuffix = ScoreboardTeam.getMethod("setSuffix", IChatBaseComponent);
} else {
ScoreboardTeam_setPrefix = getMethod(ScoreboardTeam, new String[]{"setPrefix", "func_96666_b"}, String.class); // {Bukkit, Thermos}
ScoreboardTeam_setSuffix = getMethod(ScoreboardTeam, new String[]{"setSuffix", "func_96662_c"}, String.class); // {Bukkit, Thermos}
ScoreboardTeam_setPrefix = getMethod(ScoreboardTeam, new String[] {"setPrefix", "b"}, String.class); // 1.5.1+, 1.5
ScoreboardTeam_setSuffix = getMethod(ScoreboardTeam, new String[] {"setSuffix", "c"}, String.class); // 1.5.1+, 1.5
}
}
@@ -93,7 +81,7 @@ public class LegacyNMSStorage extends NMSStorage {
EntityHuman = getLegacyClass("EntityHuman");
World = getLegacyClass("World");
Packet = getLegacyClass("Packet");
EnumChatFormat = getLegacyClass("EnumChatFormat");
EnumChatFormat = (Class<Enum>) getLegacyClass("EnumChatFormat");
EntityPlayer = getLegacyClass("EntityPlayer");
Entity = getLegacyClass("Entity");
EntityLiving = getLegacyClass("EntityLiving");
@@ -109,7 +97,7 @@ public class LegacyNMSStorage extends NMSStorage {
EntityArmorStand = getLegacyClass("EntityArmorStand");
}
if (minorVersion >= 12) {
ChatMessageType = getLegacyClass("ChatMessageType");
ChatMessageType = (Class<Enum>) getLegacyClass("ChatMessageType");
}
// DataWatcher
@@ -131,15 +119,15 @@ public class LegacyNMSStorage extends NMSStorage {
PacketPlayOutEntityMetadata = getLegacyClass("PacketPlayOutEntityMetadata", "Packet40EntityMetadata");
PacketPlayOutNamedEntitySpawn = getLegacyClass("PacketPlayOutNamedEntitySpawn", "Packet20NamedEntitySpawn");
if (minorVersion >= 7) {
EnumEntityUseAction = getLegacyClass("PacketPlayInUseEntity$EnumEntityUseAction", "EnumEntityUseAction");
EnumEntityUseAction = (Class<Enum>) getLegacyClass("PacketPlayInUseEntity$EnumEntityUseAction", "EnumEntityUseAction");
}
// Player Info
if (minorVersion >= 8) {
PacketPlayOutPlayerInfo = getLegacyClass("PacketPlayOutPlayerInfo");
EnumPlayerInfoAction = getLegacyClass("PacketPlayOutPlayerInfo$EnumPlayerInfoAction", "EnumPlayerInfoAction");
EnumPlayerInfoAction = (Class<Enum>) getLegacyClass("PacketPlayOutPlayerInfo$EnumPlayerInfoAction", "EnumPlayerInfoAction");
PlayerInfoData = getLegacyClass("PacketPlayOutPlayerInfo$PlayerInfoData", "PlayerInfoData");
EnumGamemode = getLegacyClass("EnumGamemode", "WorldSettings$EnumGamemode");
EnumGamemode = (Class<Enum>) getLegacyClass("EnumGamemode", "WorldSettings$EnumGamemode");
}
// Scoreboard
@@ -150,15 +138,15 @@ public class LegacyNMSStorage extends NMSStorage {
Scoreboard = getLegacyClass("Scoreboard");
ScoreboardObjective = getLegacyClass("ScoreboardObjective");
ScoreboardScore = getLegacyClass("ScoreboardScore");
IScoreboardCriteria = getLegacyClass("IScoreboardCriteria");
IScoreboardCriteria = getLegacyClass("IScoreboardCriteria", "IObjective"); // 1.5.1+, 1.5
ScoreboardTeam = getLegacyClass("ScoreboardTeam");
if (minorVersion >= 8) {
EnumScoreboardHealthDisplay = getLegacyClass("IScoreboardCriteria$EnumScoreboardHealthDisplay", "EnumScoreboardHealthDisplay");
EnumScoreboardAction = getLegacyClass("ScoreboardServer$Action", "PacketPlayOutScoreboardScore$EnumScoreboardAction", "EnumScoreboardAction");
EnumNameTagVisibility = getLegacyClass("ScoreboardTeamBase$EnumNameTagVisibility", "EnumNameTagVisibility");
EnumScoreboardHealthDisplay = (Class<Enum>) getLegacyClass("IScoreboardCriteria$EnumScoreboardHealthDisplay", "EnumScoreboardHealthDisplay");
EnumScoreboardAction = (Class<Enum>) getLegacyClass("ScoreboardServer$Action", "PacketPlayOutScoreboardScore$EnumScoreboardAction", "EnumScoreboardAction");
EnumNameTagVisibility = (Class<Enum>) getLegacyClass("ScoreboardTeamBase$EnumNameTagVisibility", "EnumNameTagVisibility");
}
if (minorVersion >= 9) {
EnumTeamPush = getLegacyClass("ScoreboardTeamBase$EnumTeamPush");
EnumTeamPush = (Class<Enum>) getLegacyClass("ScoreboardTeamBase$EnumTeamPush");
}
}
}
@@ -1,9 +1,10 @@
package me.neznamy.tab.platforms.bukkit.nms;
package me.neznamy.tab.platforms.bukkit.nms.storage;
/**
* NMS loader for minecraft 1.17+ using Mojang packaging and bukkit names.
*/
public class BukkitModernNMSStorage extends ModernNMSStorage {
@SuppressWarnings({"unchecked", "rawtypes"})
public class BukkitModernNMSStorage extends NMSStorage {
/**
* Creates new instance, initializes required NMS classes and fields
@@ -17,7 +18,7 @@ public class BukkitModernNMSStorage extends ModernNMSStorage {
@Override
public void loadNamedFieldsAndMethods() throws ReflectiveOperationException {
PING = getField(EntityPlayer, "e");
ChatSerializer_DESERIALIZE = getMethod(ChatSerializer, "a", String.class);
ChatSerializer_DESERIALIZE = ChatSerializer.getMethod("a", String.class);
DataWatcher_REGISTER = getMethod(DataWatcher, new String[]{"register", "a"}, DataWatcherObject, Object.class); // {Bukkit, Bukkit 1.18+}
ScoreboardScore_setScore = getMethod(ScoreboardScore, new String[]{"setScore", "b"}, int.class); // {Bukkit, Bukkit 1.18+}
ScoreboardTeam_setAllowFriendlyFire = getMethod(ScoreboardTeam, new String[]{"setAllowFriendlyFire", "a"}, boolean.class); // {Bukkit, Bukkit 1.18+}
@@ -26,9 +27,9 @@ public class BukkitModernNMSStorage extends ModernNMSStorage {
ScoreboardTeam_setSuffix = getMethod(ScoreboardTeam, new String[]{"setSuffix", "c"}, IChatBaseComponent); // {Bukkit, Bukkit 1.18+}
ScoreboardTeam_setNameTagVisibility = getMethod(ScoreboardTeam, new String[]{"setNameTagVisibility", "a"}, EnumNameTagVisibility); // {Bukkit, Bukkit 1.18+}
if (minorVersion >= 19) {
Registry_a = Registry.getMethod("a", int.class);
IRegistry_X = IRegistry.getDeclaredField("X").get(null);
EntityTypes_ARMOR_STAND = EntityTypes.getDeclaredField("d").get(null);
PacketPlayOutSpawnEntityLiving_ENTITYTYPE = getField(PacketPlayOutSpawnEntityLiving, "e");
DataWatcher_b = DataWatcher.getMethod("b");
}
}
@@ -41,16 +42,18 @@ public class BukkitModernNMSStorage extends ModernNMSStorage {
NetworkManager = Class.forName("net.minecraft.network.NetworkManager");
IChatBaseComponent = Class.forName("net.minecraft.network.chat.IChatBaseComponent");
Packet = Class.forName("net.minecraft.network.protocol.Packet");
EnumChatFormat = Class.forName("net.minecraft.EnumChatFormat");
EnumChatFormat = (Class<Enum>) Class.forName("net.minecraft.EnumChatFormat");
EntityPlayer = Class.forName("net.minecraft.server.level.EntityPlayer");
Entity = Class.forName("net.minecraft.world.entity.Entity");
EntityLiving = Class.forName("net.minecraft.world.entity.EntityLiving");
PlayerConnection = Class.forName("net.minecraft.server.network.PlayerConnection");
PacketPlayOutPlayerListHeaderFooter = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutPlayerListHeaderFooter");
PacketPlayOutChat = getModernClass("net.minecraft.network.protocol.game.ClientboundSystemChatPacket",
"net.minecraft.network.protocol.game.PacketPlayOutChat");
if (minorVersion < 19) {
ChatMessageType = Class.forName("net.minecraft.network.chat.ChatMessageType");
if (minorVersion >= 19) {
PacketPlayOutChat = Class.forName("net.minecraft.network.protocol.game.ClientboundSystemChatPacket");
} else {
PacketPlayOutChat = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutChat");
ChatMessageType = (Class<Enum>) Class.forName("net.minecraft.network.chat.ChatMessageType");
}
// DataWatcher
@@ -59,10 +62,16 @@ public class BukkitModernNMSStorage extends ModernNMSStorage {
DataWatcherObject = Class.forName("net.minecraft.network.syncher.DataWatcherObject");
DataWatcherRegistry = Class.forName("net.minecraft.network.syncher.DataWatcherRegistry");
DataWatcherSerializer = Class.forName("net.minecraft.network.syncher.DataWatcherSerializer");
if (is1_19_3Plus()) {
DataWatcher$DataValue = Class.forName("net.minecraft.network.syncher.DataWatcher$b");
}
// Entities
PacketPlayOutSpawnEntityLiving = getModernClass("net.minecraft.network.protocol.game.PacketPlayOutSpawnEntityLiving",
"net.minecraft.network.protocol.game.PacketPlayOutSpawnEntity");
if (minorVersion >= 19) {
PacketPlayOutSpawnEntityLiving = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutSpawnEntity");
} else {
PacketPlayOutSpawnEntityLiving = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutSpawnEntityLiving");
}
PacketPlayOutEntityTeleport = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutEntityTeleport");
PacketPlayInUseEntity = Class.forName("net.minecraft.network.protocol.game.PacketPlayInUseEntity");
PacketPlayInUseEntity$d = Class.forName("net.minecraft.network.protocol.game.PacketPlayInUseEntity$d");
@@ -71,19 +80,30 @@ public class BukkitModernNMSStorage extends ModernNMSStorage {
PacketPlayOutEntityLook = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutEntity$PacketPlayOutEntityLook");
PacketPlayOutEntityMetadata = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutEntityMetadata");
PacketPlayOutNamedEntitySpawn = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutNamedEntitySpawn");
EnumEntityUseAction = Class.forName("net.minecraft.network.protocol.game.PacketPlayInUseEntity$EnumEntityUseAction");
EnumEntityUseAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.PacketPlayInUseEntity$EnumEntityUseAction");
if (minorVersion >= 19) {
Registry = Class.forName("net.minecraft.core.Registry");
IRegistry = Class.forName("net.minecraft.core.IRegistry");
ProfilePublicKey = Class.forName("net.minecraft.world.entity.player.ProfilePublicKey");
ProfilePublicKey$a = Class.forName("net.minecraft.world.entity.player.ProfilePublicKey$a");
EntityTypes = Class.forName("net.minecraft.world.entity.EntityTypes");
}
// Player Info
PacketPlayOutPlayerInfo = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo");
EnumPlayerInfoAction = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo$EnumPlayerInfoAction");
PlayerInfoData = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo$PlayerInfoData");
EnumGamemode = Class.forName("net.minecraft.world.level.EnumGamemode");
if (minorVersion >= 19) {
ProfilePublicKey = Class.forName("net.minecraft.world.entity.player.ProfilePublicKey");
ProfilePublicKey$a = Class.forName("net.minecraft.world.entity.player.ProfilePublicKey$a");
}
if (is1_19_3Plus()) {
//1.19.3+
ClientboundPlayerInfoRemovePacket = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket");
PacketPlayOutPlayerInfo = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket");
EnumPlayerInfoAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket$a");
PlayerInfoData = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket$b");
RemoteChatSession = Class.forName("net.minecraft.network.chat.RemoteChatSession");
RemoteChatSession$Data = Class.forName("net.minecraft.network.chat.RemoteChatSession$a");
} else {
PacketPlayOutPlayerInfo = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo");
EnumPlayerInfoAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo$EnumPlayerInfoAction");
PlayerInfoData = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutPlayerInfo$PlayerInfoData");
}
EnumGamemode = (Class<Enum>) Class.forName("net.minecraft.world.level.EnumGamemode");
// Scoreboard
PacketPlayOutScoreboardDisplayObjective = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutScoreboardDisplayObjective");
@@ -93,12 +113,12 @@ public class BukkitModernNMSStorage extends ModernNMSStorage {
ScoreboardObjective = Class.forName("net.minecraft.world.scores.ScoreboardObjective");
ScoreboardScore = Class.forName("net.minecraft.world.scores.ScoreboardScore");
IScoreboardCriteria = Class.forName("net.minecraft.world.scores.criteria.IScoreboardCriteria");
EnumScoreboardHealthDisplay = Class.forName("net.minecraft.world.scores.criteria.IScoreboardCriteria$EnumScoreboardHealthDisplay");
EnumScoreboardAction = Class.forName("net.minecraft.server.ScoreboardServer$Action");
EnumScoreboardHealthDisplay = (Class<Enum>) Class.forName("net.minecraft.world.scores.criteria.IScoreboardCriteria$EnumScoreboardHealthDisplay");
EnumScoreboardAction = (Class<Enum>) Class.forName("net.minecraft.server.ScoreboardServer$Action");
PacketPlayOutScoreboardTeam = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam");
ScoreboardTeam = Class.forName("net.minecraft.world.scores.ScoreboardTeam");
EnumNameTagVisibility = Class.forName("net.minecraft.world.scores.ScoreboardTeamBase$EnumNameTagVisibility");
EnumTeamPush = Class.forName("net.minecraft.world.scores.ScoreboardTeamBase$EnumTeamPush");
PacketPlayOutScoreboardTeam_PlayerAction = Class.forName("net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam$a");
EnumNameTagVisibility = (Class<Enum>) Class.forName("net.minecraft.world.scores.ScoreboardTeamBase$EnumNameTagVisibility");
EnumTeamPush = (Class<Enum>) Class.forName("net.minecraft.world.scores.ScoreboardTeamBase$EnumTeamPush");
PacketPlayOutScoreboardTeam_PlayerAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.PacketPlayOutScoreboardTeam$a");
}
}
@@ -1,9 +1,10 @@
package me.neznamy.tab.platforms.bukkit.nms;
package me.neznamy.tab.platforms.bukkit.nms.storage;
/**
* NMS loader for minecraft 1.17+ using Mojang packaging and names.
*/
public class MojangModernNMSStorage extends ModernNMSStorage {
@SuppressWarnings({"unchecked", "rawtypes"})
public class MojangModernNMSStorage extends NMSStorage {
/**
* Creates new instance, initializes required NMS classes and fields
@@ -17,18 +18,18 @@ public class MojangModernNMSStorage extends ModernNMSStorage {
@Override
public void loadNamedFieldsAndMethods() throws ReflectiveOperationException {
PING = getField(EntityPlayer, "latency");
ChatSerializer_DESERIALIZE = getMethod(ChatSerializer, "fromJson", String.class);
DataWatcher_REGISTER = getMethod(DataWatcher, "define", DataWatcherObject, Object.class);
ScoreboardScore_setScore = getMethod(ScoreboardScore, "setScore", int.class);
ScoreboardTeam_setAllowFriendlyFire = getMethod(ScoreboardTeam, "setAllowFriendlyFire", boolean.class);
ScoreboardTeam_setCanSeeFriendlyInvisibles = getMethod(ScoreboardTeam, "setSeeFriendlyInvisibles", boolean.class);
ScoreboardTeam_setPrefix = getMethod(ScoreboardTeam, "setPlayerPrefix", IChatBaseComponent);
ScoreboardTeam_setSuffix = getMethod(ScoreboardTeam, "setPlayerSuffix", IChatBaseComponent);
ScoreboardTeam_setNameTagVisibility = getMethod(ScoreboardTeam, "setNameTagVisibility", EnumNameTagVisibility);
ChatSerializer_DESERIALIZE = ChatSerializer.getMethod("fromJson", String.class);
DataWatcher_REGISTER = DataWatcher.getMethod("define", DataWatcherObject, Object.class);
ScoreboardScore_setScore = ScoreboardScore.getMethod("setScore", int.class);
ScoreboardTeam_setAllowFriendlyFire = ScoreboardTeam.getMethod("setAllowFriendlyFire", boolean.class);
ScoreboardTeam_setCanSeeFriendlyInvisibles = ScoreboardTeam.getMethod("setSeeFriendlyInvisibles", boolean.class);
ScoreboardTeam_setPrefix = ScoreboardTeam.getMethod("setPlayerPrefix", IChatBaseComponent);
ScoreboardTeam_setSuffix = ScoreboardTeam.getMethod("setPlayerSuffix", IChatBaseComponent);
ScoreboardTeam_setNameTagVisibility = ScoreboardTeam.getMethod("setNameTagVisibility", EnumNameTagVisibility);
if (minorVersion >= 19) {
Registry_a = Registry.getMethod("byId", int.class);
IRegistry_X = IRegistry.getDeclaredField("ENTITY_TYPE").get(null);
EntityTypes_ARMOR_STAND = EntityTypes.getDeclaredField("ARMOR_STAND").get(null);
PacketPlayOutSpawnEntityLiving_ENTITYTYPE = getField(PacketPlayOutSpawnEntityLiving, "type");
DataWatcher_b = DataWatcher.getMethod("packDirty");
}
}
@@ -41,16 +42,17 @@ public class MojangModernNMSStorage extends ModernNMSStorage {
EntityHuman = Class.forName("net.minecraft.world.entity.player.Player");
NetworkManager = Class.forName("net.minecraft.network.Connection");
Packet = Class.forName("net.minecraft.network.protocol.Packet");
EnumChatFormat = Class.forName("net.minecraft.ChatFormatting");
EnumChatFormat = (Class<Enum>) Class.forName("net.minecraft.ChatFormatting");
EntityPlayer = Class.forName("net.minecraft.server.level.ServerPlayer");
Entity = Class.forName("net.minecraft.world.entity.Entity");
EntityLiving = Class.forName("net.minecraft.world.entity.LivingEntity");
PlayerConnection = Class.forName("net.minecraft.server.network.ServerGamePacketListenerImpl");
PacketPlayOutPlayerListHeaderFooter = Class.forName("net.minecraft.network.protocol.game.ClientboundTabListPacket");
PacketPlayOutChat = getModernClass("net.minecraft.network.protocol.game.ClientboundSystemChatPacket",
"net.minecraft.network.protocol.game.ClientboundChatPacket");
if (minorVersion < 19) {
ChatMessageType = Class.forName("net.minecraft.network.chat.ChatType");
if (minorVersion >= 19) {
PacketPlayOutChat = Class.forName("net.minecraft.network.protocol.game.ClientboundSystemChatPacket");
} else {
PacketPlayOutChat = Class.forName("net.minecraft.network.protocol.game.ClientboundChatPacket");
ChatMessageType = (Class<Enum>) Class.forName("net.minecraft.network.chat.ChatType");
}
// DataWatcher
@@ -59,10 +61,16 @@ public class MojangModernNMSStorage extends ModernNMSStorage {
DataWatcherObject = Class.forName("net.minecraft.network.syncher.EntityDataAccessor");
DataWatcherRegistry = Class.forName("net.minecraft.network.syncher.EntityDataSerializers");
DataWatcherSerializer = Class.forName("net.minecraft.network.syncher.EntityDataSerializer");
if (is1_19_3Plus()) {
DataWatcher$DataValue = Class.forName("net.minecraft.network.syncher.SynchedEntityData$DataValue");
}
// Entities
PacketPlayOutSpawnEntityLiving = getModernClass("net.minecraft.network.protocol.game.ClientboundAddMobPacket",
"net.minecraft.network.protocol.game.ClientboundAddEntityPacket");
if (minorVersion >= 19) {
PacketPlayOutSpawnEntityLiving = Class.forName("net.minecraft.network.protocol.game.ClientboundAddEntityPacket");
} else {
PacketPlayOutSpawnEntityLiving = Class.forName("net.minecraft.network.protocol.game.ClientboundAddMobPacket");
}
PacketPlayOutEntityTeleport = Class.forName("net.minecraft.network.protocol.game.ClientboundTeleportEntityPacket");
PacketPlayInUseEntity = Class.forName("net.minecraft.network.protocol.game.ServerboundInteractPacket");
PacketPlayInUseEntity$d = Class.forName("net.minecraft.network.protocol.game.ServerboundInteractPacket$InteractionAction");
@@ -71,19 +79,29 @@ public class MojangModernNMSStorage extends ModernNMSStorage {
PacketPlayOutEntityLook = Class.forName("net.minecraft.network.protocol.game.ClientboundMoveEntityPacket$Rot");
PacketPlayOutEntityMetadata = Class.forName("net.minecraft.network.protocol.game.ClientboundSetEntityDataPacket");
PacketPlayOutNamedEntitySpawn = Class.forName("net.minecraft.network.protocol.game.ClientboundAddPlayerPacket");
EnumEntityUseAction = Class.forName("net.minecraft.network.protocol.game.ServerboundInteractPacket$Action");
EnumEntityUseAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.ServerboundInteractPacket$Action");
if (minorVersion >= 19) {
Registry = Class.forName("net.minecraft.core.IdMap");
IRegistry = Class.forName("net.minecraft.core.Registry");
ProfilePublicKey = Class.forName("net.minecraft.world.entity.player.ProfilePublicKey");
ProfilePublicKey$a = Class.forName("net.minecraft.world.entity.player.ProfilePublicKey$Data");
EntityTypes = Class.forName("net.minecraft.world.entity.EntityType");
}
// Player Info
PacketPlayOutPlayerInfo = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket");
EnumPlayerInfoAction = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket$Action");
PlayerInfoData = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket$PlayerUpdate");
EnumGamemode = Class.forName("net.minecraft.world.level.GameType");
if (minorVersion >= 19) {
ProfilePublicKey = Class.forName("net.minecraft.world.entity.player.ProfilePublicKey");
ProfilePublicKey$a = Class.forName("net.minecraft.world.entity.player.ProfilePublicKey$Data");
}
if (is1_19_3Plus()) {
ClientboundPlayerInfoRemovePacket = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoRemovePacket");
PacketPlayOutPlayerInfo = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket");
EnumPlayerInfoAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket$Action");
PlayerInfoData = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket$Entry");
RemoteChatSession = Class.forName("net.minecraft.network.chat.RemoteChatSession");
RemoteChatSession$Data = Class.forName("net.minecraft.network.chat.RemoteChatSession$Data");
} else {
PacketPlayOutPlayerInfo = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket");
EnumPlayerInfoAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket$Action");
PlayerInfoData = Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoPacket$PlayerUpdate");
}
EnumGamemode = (Class<Enum>) Class.forName("net.minecraft.world.level.GameType");
// Scoreboard
PacketPlayOutScoreboardDisplayObjective = Class.forName("net.minecraft.network.protocol.game.ClientboundSetDisplayObjectivePacket");
@@ -93,12 +111,12 @@ public class MojangModernNMSStorage extends ModernNMSStorage {
ScoreboardObjective = Class.forName("net.minecraft.world.scores.Objective");
ScoreboardScore = Class.forName("net.minecraft.world.scores.Score");
IScoreboardCriteria = Class.forName("net.minecraft.world.scores.criteria.ObjectiveCriteria");
EnumScoreboardHealthDisplay = Class.forName("net.minecraft.world.scores.criteria.ObjectiveCriteria$RenderType");
EnumScoreboardAction = Class.forName("net.minecraft.server.ServerScoreboard$Method");
EnumScoreboardHealthDisplay = (Class<Enum>) Class.forName("net.minecraft.world.scores.criteria.ObjectiveCriteria$RenderType");
EnumScoreboardAction = (Class<Enum>) Class.forName("net.minecraft.server.ServerScoreboard$Method");
PacketPlayOutScoreboardTeam = Class.forName("net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket");
ScoreboardTeam = Class.forName("net.minecraft.world.scores.PlayerTeam");
EnumNameTagVisibility = Class.forName("net.minecraft.world.scores.Team$Visibility");
EnumTeamPush = Class.forName("net.minecraft.world.scores.Team$CollisionRule");
PacketPlayOutScoreboardTeam_PlayerAction = Class.forName("net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket$Action");
EnumNameTagVisibility = (Class<Enum>) Class.forName("net.minecraft.world.scores.Team$Visibility");
EnumTeamPush = (Class<Enum>) Class.forName("net.minecraft.world.scores.Team$CollisionRule");
PacketPlayOutScoreboardTeam_PlayerAction = (Class<Enum>) Class.forName("net.minecraft.network.protocol.game.ClientboundSetPlayerTeamPacket$Action");
}
}
@@ -1,4 +1,4 @@
package me.neznamy.tab.platforms.bukkit.nms;
package me.neznamy.tab.platforms.bukkit.nms.storage;
import com.mojang.authlib.GameProfile;
import io.netty.channel.Channel;
@@ -9,27 +9,30 @@ import org.bukkit.Bukkit;
import java.lang.reflect.*;
import java.util.*;
/**
* Class holding all NMS classes, methods, fields and constructors used by TAB.
*/
@SuppressWarnings("rawtypes")
public abstract class NMSStorage {
//instance of this class
/** Instance of this class */
private static NMSStorage instance;
//server package, such as "v1_16_R3"
/** Server's NMS/CraftBukkit package */
protected final String serverPackage = Bukkit.getServer().getClass().getPackage().getName().split("\\.")[3];
//server minor version such as "16"
/** Server's minor version */
protected final int minorVersion = Integer.parseInt(serverPackage.split("_")[1]);
//another field due to fabric which uses 1.16 package on 1.17 server
private final ProtocolVersion serverVersion = ProtocolVersion.fromFriendlyName(Bukkit.getBukkitVersion().split("-")[0]);
/** Flag determining whether the server version is at least 1.19.3 or not */
private boolean is1_19_3Plus;
//base
/** Basic universal values */
protected Class<?> Packet;
protected Class<?> NetworkManager;
protected Class<?> EntityArmorStand;
protected Class<?> World;
public Class<?> EnumChatFormat;
public Class<Enum> EnumChatFormat;
public Class<?> EntityPlayer;
protected Class<?> EntityHuman;
protected Class<?> Entity;
@@ -40,23 +43,22 @@ public abstract class NMSStorage {
public Field NETWORK_MANAGER;
public Field CHANNEL;
public Field EntityHuman_ProfilePublicKey;
public Field EntityPlayer_RemoteChatSession;
public Method getHandle;
public Method sendPacket;
public Method getProfile;
public Enum[] EnumChatFormat_values;
//chat
/** Chat components */
protected Class<?> IChatBaseComponent;
protected Class<?> ChatSerializer;
public Method ChatSerializer_DESERIALIZE;
//PacketPlayOutChat
/** PacketPlayOutChat */
protected Class<?> PacketPlayOutChat;
public Class<?> ChatMessageType;
public Class<Enum> ChatMessageType;
public Constructor<?> newPacketPlayOutChat;
public Enum[] ChatMessageType_values;
//DataWatcher
/** DataWatcher */
protected Class<?> DataWatcher;
protected Class<?> DataWatcherItem;
protected Class<?> DataWatcherObject;
@@ -70,9 +72,16 @@ public abstract class NMSStorage {
public Field DataWatcherObject_SERIALIZER;
public Method DataWatcher_REGISTER;
private final DataWatcherRegistry registry;
//1.19.3+
protected Class<?> DataWatcher$DataValue;
public Field DataWatcher$DataValue_POSITION;
public Field DataWatcher$DataValue_VALUE;
public Method DataWatcher_markDirty;
public Method DataWatcher_b;
//PacketPlayOutSpawnEntityLiving
/** PacketPlayOutSpawnEntityLiving */
public Class<?> PacketPlayOutSpawnEntityLiving;
public Class<?> EntityTypes;
public Constructor<?> newPacketPlayOutSpawnEntityLiving;
public Field PacketPlayOutSpawnEntityLiving_ENTITYID;
public Field PacketPlayOutSpawnEntityLiving_ENTITYTYPE;
@@ -83,10 +92,9 @@ public abstract class NMSStorage {
public Field PacketPlayOutSpawnEntityLiving_Y;
public Field PacketPlayOutSpawnEntityLiving_Z;
public Field PacketPlayOutSpawnEntityLiving_DATAWATCHER;
public Method Registry_a;
public Object IRegistry_X;
public Object EntityTypes_ARMOR_STAND;
//PacketPlayOutEntityTeleport
/** PacketPlayOutEntityTeleport */
public Class<?> PacketPlayOutEntityTeleport;
public Constructor<?> newPacketPlayOutEntityTeleport;
public Field PacketPlayOutEntityTeleport_ENTITYID;
@@ -96,16 +104,16 @@ public abstract class NMSStorage {
public Field PacketPlayOutEntityTeleport_YAW;
public Field PacketPlayOutEntityTeleport_PITCH;
//PacketPlayOutPlayerListHeaderFooter
/** PacketPlayOutPlayerListHeaderFooter */
protected Class<?> PacketPlayOutPlayerListHeaderFooter;
public Constructor<?> newPacketPlayOutPlayerListHeaderFooter;
public Field PacketPlayOutPlayerListHeaderFooter_HEADER;
public Field PacketPlayOutPlayerListHeaderFooter_FOOTER;
//other entity packets
/** Other entity packets */
public Class<?> PacketPlayInUseEntity;
public Class<?> PacketPlayInUseEntity$d;
protected Class<?> EnumEntityUseAction;
protected Class<Enum> EnumEntityUseAction;
public Field PacketPlayInUseEntity_ENTITY;
public Field PacketPlayInUseEntity_ACTION;
@@ -125,11 +133,11 @@ public abstract class NMSStorage {
public Class<?> PacketPlayOutNamedEntitySpawn;
public Field PacketPlayOutNamedEntitySpawn_ENTITYID;
//PacketPlayOutPlayerInfo
/** PacketPlayOutPlayerInfo */
public Class<?> PacketPlayOutPlayerInfo;
protected Class<?> EnumPlayerInfoAction;
public Class<Enum> EnumPlayerInfoAction;
protected Class<?> PlayerInfoData;
protected Class<?> EnumGamemode;
public Class<Enum> EnumGamemode;
protected Class<?> ProfilePublicKey;
protected Class<?> ProfilePublicKey$a;
public Constructor<?> newPacketPlayOutPlayerInfo;
@@ -137,15 +145,27 @@ public abstract class NMSStorage {
public Field PacketPlayOutPlayerInfo_ACTION;
public Field PacketPlayOutPlayerInfo_PLAYERS;
public Method PlayerInfoData_getProfile;
public Method PlayerInfoData_isListed;
public Method PlayerInfoData_getLatency;
public Method PlayerInfoData_getGamemode;
public Method PlayerInfoData_getDisplayName;
public Method PlayerInfoData_getProfilePublicKeyRecord;
public Method ProfilePublicKey_getRecord;
public Enum[] EnumPlayerInfoAction_values;
public Enum[] EnumGamemode_values;
//1.19.3+
public Class<?> ClientboundPlayerInfoRemovePacket;
public Class<?> RemoteChatSession;
public Class<?> RemoteChatSession$Data;
public Constructor<?> newClientboundPlayerInfoRemovePacket;
public Constructor<?> newRemoteChatSession;
public Constructor<?> newRemoteChatSession$Data;
public Method ClientboundPlayerInfoRemovePacket_getEntries;
public Method RemoteChatSession_getSessionId;
public Method RemoteChatSession_getProfilePublicKey;
public Method RemoteChatSession$Data_getSessionId;
public Method RemoteChatSession$Data_getProfilePublicKey;
//scoreboard objectives
/** Scoreboard objectives */
public Class<?> PacketPlayOutScoreboardDisplayObjective;
public Class<?> PacketPlayOutScoreboardObjective;
protected Class<?> Scoreboard;
@@ -153,8 +173,8 @@ public abstract class NMSStorage {
protected Class<?> ScoreboardObjective;
protected Class<?> ScoreboardScore;
protected Class<?> IScoreboardCriteria;
public Class<?> EnumScoreboardHealthDisplay;
protected Class<?> EnumScoreboardAction;
public Class<Enum> EnumScoreboardHealthDisplay;
public Class<Enum> EnumScoreboardAction;
public Constructor<?> newScoreboardObjective;
protected Constructor<?> newScoreboard;
public Constructor<?> newScoreboardScore;
@@ -171,15 +191,13 @@ public abstract class NMSStorage {
public Field PacketPlayOutScoreboardObjective_RENDERTYPE;
public Field PacketPlayOutScoreboardObjective_DISPLAYNAME;
public Method ScoreboardScore_setScore;
public Enum[] EnumScoreboardHealthDisplay_values;
public Enum[] EnumScoreboardAction_values;
//PacketPlayOutScoreboardTeam
/** PacketPlayOutScoreboardTeam */
public Class<?> PacketPlayOutScoreboardTeam;
protected Class<?> ScoreboardTeam;
protected Class<?> EnumNameTagVisibility;
protected Class<?> EnumTeamPush;
protected Class<?> PacketPlayOutScoreboardTeam_PlayerAction;
public Class<Enum> EnumNameTagVisibility;
public Class<Enum> EnumTeamPush;
public Class<Enum> PacketPlayOutScoreboardTeam_PlayerAction;
public Constructor<?> newScoreboardTeam;
public Constructor<?> newPacketPlayOutScoreboardTeam;
public Field PacketPlayOutScoreboardTeam_NAME;
@@ -196,15 +214,22 @@ public abstract class NMSStorage {
public Method PacketPlayOutScoreboardTeam_of;
public Method PacketPlayOutScoreboardTeam_ofBoolean;
public Method PacketPlayOutScoreboardTeam_ofString;
public Enum[] EnumNameTagVisibility_values;
public Enum[] EnumTeamPush_values;
public Enum[] PacketPlayOutScoreboardTeam_PlayerAction_values;
//other
/** Other */
public Object emptyScoreboard;
public Object dummyEntity;
/**
* Constructs new instance and attempts to load all fields, methods and constructors.
*
* @throws ReflectiveOperationException
* If some field, constructor or method was not found
*/
public NMSStorage() throws ReflectiveOperationException {
try {
Class.forName("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket");
is1_19_3Plus = true;
} catch (ClassNotFoundException | NullPointerException ignored) {}
ProtocolVersion.UNKNOWN_SERVER_VERSION.setMinorVersion(minorVersion); //fixing compatibility with forks that set version field value to "Unknown"
loadClasses();
if (minorVersion >= 7) {
@@ -220,7 +245,6 @@ public abstract class NMSStorage {
PLAYER_CONNECTION = getFields(EntityPlayer, PlayerConnection).get(0);
getHandle = Class.forName("org.bukkit.craftbukkit." + serverPackage + ".entity.CraftPlayer").getMethod("getHandle");
sendPacket = getMethods(PlayerConnection, void.class, Packet).get(0);
EnumChatFormat_values = getEnumValues(EnumChatFormat);
registry = new DataWatcherRegistry(this);
dataWatcher();
entityTeleport();
@@ -253,10 +277,13 @@ public abstract class NMSStorage {
return instance;
}
/**
* Loads PacketPlayOutChat's methods, fields and constructors
*
* @throws ReflectiveOperationException
* If some method, field or constructor was not found
*/
protected void chat() throws ReflectiveOperationException {
if (minorVersion >= 12 && minorVersion <= 18) {
ChatMessageType_values = getEnumValues(ChatMessageType);
}
if (minorVersion >= 19) {
try {
newPacketPlayOutChat = PacketPlayOutChat.getConstructor(IChatBaseComponent, boolean.class);
@@ -275,28 +302,60 @@ public abstract class NMSStorage {
}
}
/**
* Loads PacketPlayOutPlayerInfo's methods, fields and constructors
*
* @throws ReflectiveOperationException
* If some method, field or constructor was not found
*/
protected void playerInfo() throws ReflectiveOperationException {
if (minorVersion < 8) return;
newPacketPlayOutPlayerInfo = PacketPlayOutPlayerInfo.getConstructor(EnumPlayerInfoAction, Array.newInstance(EntityPlayer, 0).getClass());
if (is1_19_3Plus()) {
newClientboundPlayerInfoRemovePacket = ClientboundPlayerInfoRemovePacket.getConstructor(List.class);
newPacketPlayOutPlayerInfo = PacketPlayOutPlayerInfo.getConstructor(EnumSet.class, Collection.class);
ClientboundPlayerInfoRemovePacket_getEntries = getMethods(ClientboundPlayerInfoRemovePacket, List.class).get(0);
PacketPlayOutPlayerInfo_ACTION = getFields(PacketPlayOutPlayerInfo, EnumSet.class).get(0);
newRemoteChatSession$Data = RemoteChatSession$Data.getConstructor(UUID.class, ProfilePublicKey$a);
RemoteChatSession$Data_getSessionId = getMethods(RemoteChatSession$Data, UUID.class).get(0);
RemoteChatSession$Data_getProfilePublicKey = getMethods(RemoteChatSession$Data, ProfilePublicKey$a).get(0);
PlayerInfoData_getProfilePublicKeyRecord = getMethods(PlayerInfoData, RemoteChatSession$Data).get(0);
EntityPlayer_RemoteChatSession = getFields(EntityPlayer, RemoteChatSession).get(0);
PlayerInfoData_isListed = getMethods(PlayerInfoData, boolean.class).get(0);
} else {
newPacketPlayOutPlayerInfo = PacketPlayOutPlayerInfo.getConstructor(EnumPlayerInfoAction, Array.newInstance(EntityPlayer, 0).getClass());
PacketPlayOutPlayerInfo_ACTION = getFields(PacketPlayOutPlayerInfo, EnumPlayerInfoAction).get(0);
if (minorVersion >= 19) {
PlayerInfoData_getProfilePublicKeyRecord = getMethods(PlayerInfoData, ProfilePublicKey$a).get(0);
EntityHuman_ProfilePublicKey = getFields(EntityHuman, ProfilePublicKey).get(0);
}
}
newPlayerInfoData = PlayerInfoData.getConstructors()[0];
PacketPlayOutPlayerInfo_ACTION = getFields(PacketPlayOutPlayerInfo, EnumPlayerInfoAction).get(0);
PacketPlayOutPlayerInfo_PLAYERS = getFields(PacketPlayOutPlayerInfo, List.class).get(0);
PlayerInfoData_getProfile = getMethods(PlayerInfoData, GameProfile.class).get(0);
PlayerInfoData_getLatency = getMethods(PlayerInfoData, int.class).get(0);
for (Method m: getMethods(PlayerInfoData, int.class)) {
// do not take .hashCode(), which is final
if (!Modifier.isFinal(m.getModifiers())) PlayerInfoData_getLatency = m;
}
PlayerInfoData_getGamemode = getMethods(PlayerInfoData, EnumGamemode).get(0);
PlayerInfoData_getDisplayName = getMethods(PlayerInfoData, IChatBaseComponent).get(0);
EnumPlayerInfoAction_values = getEnumValues(EnumPlayerInfoAction);
EnumGamemode_values = getEnumValues(EnumGamemode);
if (minorVersion >= 19) {
EntityHuman_ProfilePublicKey = getFields(EntityHuman, ProfilePublicKey).get(0);
ProfilePublicKey_getRecord = getMethods(ProfilePublicKey, ProfilePublicKey$a).get(0);
PlayerInfoData_getProfilePublicKeyRecord = getMethods(PlayerInfoData, ProfilePublicKey$a).get(0);
}
}
/**
* Loads PacketPlayOutSpawnEntityLiving's methods, fields and constructors
*
* @throws ReflectiveOperationException
* If some method, field or constructor was not found
*/
protected void spawnEntityLiving() throws ReflectiveOperationException {
if (minorVersion >= 17 || serverVersion.getMinorVersion() >= 17) {
newPacketPlayOutSpawnEntityLiving = PacketPlayOutSpawnEntityLiving.getConstructor(EntityLiving);
if (minorVersion >= 17) {
if (is1_19_3Plus()) {
newPacketPlayOutSpawnEntityLiving = PacketPlayOutSpawnEntityLiving.getConstructor(Entity);
} else {
newPacketPlayOutSpawnEntityLiving = PacketPlayOutSpawnEntityLiving.getConstructor(EntityLiving);
}
} else {
newPacketPlayOutSpawnEntityLiving = PacketPlayOutSpawnEntityLiving.getConstructor();
}
@@ -327,11 +386,17 @@ public abstract class NMSStorage {
}
}
/**
* Loads PacketPlayOutEntityTeleport's methods, fields and constructors
*
* @throws ReflectiveOperationException
* If some method, field or constructor was not found
*/
protected void entityTeleport() throws ReflectiveOperationException {
PacketPlayOutEntityTeleport_ENTITYID = getFields(PacketPlayOutEntityTeleport, int.class).get(0);
PacketPlayOutEntityTeleport_YAW = getFields(PacketPlayOutEntityTeleport, byte.class).get(0);
PacketPlayOutEntityTeleport_PITCH = getFields(PacketPlayOutEntityTeleport, byte.class).get(1);
if (minorVersion >= 17 || serverVersion.getMinorVersion() >= 17) {
if (minorVersion >= 17) {
newPacketPlayOutEntityTeleport = PacketPlayOutEntityTeleport.getConstructor(Entity);
} else {
newPacketPlayOutEntityTeleport = PacketPlayOutEntityTeleport.getConstructor();
@@ -347,21 +412,37 @@ public abstract class NMSStorage {
}
}
/**
* Loads PacketPlayOutPlayerListHeaderFooter's methods, fields and constructors
*
* @throws ReflectiveOperationException
* If some method, field or constructor was not found
*/
protected void playerListHeaderFooter() throws ReflectiveOperationException {
if (minorVersion < 8) return;
PacketPlayOutPlayerListHeaderFooter_HEADER = getFields(PacketPlayOutPlayerListHeaderFooter, IChatBaseComponent).get(0);
PacketPlayOutPlayerListHeaderFooter_FOOTER = getFields(PacketPlayOutPlayerListHeaderFooter, IChatBaseComponent).get(1);
if (minorVersion >= 17 || serverVersion.getMinorVersion() >= 17) {
if (minorVersion >= 17) {
newPacketPlayOutPlayerListHeaderFooter = PacketPlayOutPlayerListHeaderFooter.getConstructor(IChatBaseComponent, IChatBaseComponent);
} else {
newPacketPlayOutPlayerListHeaderFooter = PacketPlayOutPlayerListHeaderFooter.getConstructor();
}
}
/**
* Loads methods, fields and constructors for other entity packets
*
* @throws ReflectiveOperationException
* If some method, field or constructor was not found
*/
protected void otherEntity() throws ReflectiveOperationException {
PacketPlayOutEntity_ENTITYID = getFields(PacketPlayOutEntity, int.class).get(0);
PacketPlayOutEntityDestroy_ENTITIES = setAccessible(PacketPlayOutEntityDestroy.getDeclaredFields()[0]);
newPacketPlayOutEntityMetadata = PacketPlayOutEntityMetadata.getConstructor(int.class, DataWatcher, boolean.class);
if (is1_19_3Plus()) {
newPacketPlayOutEntityMetadata = PacketPlayOutEntityMetadata.getConstructor(int.class, List.class);
} else {
newPacketPlayOutEntityMetadata = PacketPlayOutEntityMetadata.getConstructor(int.class, DataWatcher, boolean.class);
}
PacketPlayOutEntityMetadata_LIST = getFields(PacketPlayOutEntityMetadata, List.class).get(0);
PacketPlayOutNamedEntitySpawn_ENTITYID = getFields(PacketPlayOutNamedEntitySpawn, int.class).get(0);
if (minorVersion >= 7) {
@@ -376,6 +457,12 @@ public abstract class NMSStorage {
}
}
/**
* Loads DataWatcher's methods, fields and constructors
*
* @throws ReflectiveOperationException
* If some method, field or constructor was not found
*/
protected void dataWatcher() throws ReflectiveOperationException {
newDataWatcher = DataWatcher.getConstructors()[0];
DataWatcherItem_VALUE = getFields(DataWatcherItem, Object.class).get(0);
@@ -384,11 +471,22 @@ public abstract class NMSStorage {
DataWatcherItem_TYPE = getFields(DataWatcherItem, DataWatcherObject).get(0);
DataWatcherObject_SLOT = getFields(DataWatcherObject, int.class).get(0);
DataWatcherObject_SERIALIZER = getFields(DataWatcherObject, DataWatcherSerializer).get(0);
if (is1_19_3Plus()) {
DataWatcher$DataValue_POSITION = getFields(DataWatcher$DataValue, int.class).get(0);
DataWatcher$DataValue_VALUE = getFields(DataWatcher$DataValue, Object.class).get(0);
DataWatcher_markDirty = getMethods(DataWatcher, void.class, DataWatcherObject).get(0);
}
} else {
DataWatcherItem_TYPE = getFields(DataWatcherItem, int.class).get(1);
}
}
/**
* Loads Scoreboard methods, fields and constructors
*
* @throws ReflectiveOperationException
* If some method, field or constructor was not found
*/
protected void scoreboard() throws ReflectiveOperationException {
newScoreboardObjective = ScoreboardObjective.getConstructors()[0];
newScoreboard = Scoreboard.getConstructor();
@@ -402,8 +500,6 @@ public abstract class NMSStorage {
List<Field> list = getFields(PacketPlayOutScoreboardObjective, int.class);
PacketPlayOutScoreboardObjective_METHOD = list.get(list.size()-1);
if (minorVersion >= 8) {
EnumScoreboardHealthDisplay_values = getEnumValues(EnumScoreboardHealthDisplay);
EnumScoreboardAction_values = getEnumValues(EnumScoreboardAction);
PacketPlayOutScoreboardObjective_RENDERTYPE = getFields(PacketPlayOutScoreboardObjective, EnumScoreboardHealthDisplay).get(0);
}
if (minorVersion >= 13) {
@@ -422,17 +518,19 @@ public abstract class NMSStorage {
}
}
/**
* Loads PacketPlayOutScoreboardTeam's methods, fields and constructors
*
* @throws ReflectiveOperationException
* If some method, field or constructor was not found
*/
protected void scoreboardTeam() throws ReflectiveOperationException {
newScoreboardTeam = ScoreboardTeam.getConstructor(Scoreboard, String.class);
PacketPlayOutScoreboardTeam_NAME = getFields(PacketPlayOutScoreboardTeam, String.class).get(0);
PacketPlayOutScoreboardTeam_ACTION = getInstanceFields(PacketPlayOutScoreboardTeam, int.class).get(0);
PacketPlayOutScoreboardTeam_PLAYERS = getFields(PacketPlayOutScoreboardTeam, Collection.class).get(0);
ScoreboardTeam_getPlayerNameSet = getMethods(ScoreboardTeam, Collection.class).get(0);
if (minorVersion >= 8) {
EnumNameTagVisibility_values = getEnumValues(EnumNameTagVisibility);
}
if (minorVersion >= 9) {
EnumTeamPush_values = getEnumValues(EnumTeamPush);
ScoreboardTeam_setCollisionRule = getMethods(ScoreboardTeam, void.class, EnumTeamPush).get(0);
}
if (minorVersion >= 13) {
@@ -442,14 +540,26 @@ public abstract class NMSStorage {
PacketPlayOutScoreboardTeam_of = getMethods(PacketPlayOutScoreboardTeam, PacketPlayOutScoreboardTeam, ScoreboardTeam).get(0);
PacketPlayOutScoreboardTeam_ofBoolean = getMethods(PacketPlayOutScoreboardTeam, PacketPlayOutScoreboardTeam, ScoreboardTeam, boolean.class).get(0);
PacketPlayOutScoreboardTeam_ofString = getMethods(PacketPlayOutScoreboardTeam, PacketPlayOutScoreboardTeam, ScoreboardTeam, String.class, PacketPlayOutScoreboardTeam_PlayerAction).get(0);
PacketPlayOutScoreboardTeam_PlayerAction_values = getEnumValues(PacketPlayOutScoreboardTeam_PlayerAction);
} else {
newPacketPlayOutScoreboardTeam = PacketPlayOutScoreboardTeam.getConstructor(ScoreboardTeam, int.class);
}
}
/**
* Loads all classes used by the plugin
*
* @throws ClassNotFoundException
* If a class was not found
*/
public abstract void loadClasses() throws ClassNotFoundException;
/**
* Loads all fields and methods which must be loaded by their name due to
* lack of other methods of reliably retrieving them.
*
* @throws ReflectiveOperationException
* If a field or method was not found
*/
public abstract void loadNamedFieldsAndMethods() throws ReflectiveOperationException;
/**
@@ -468,7 +578,7 @@ public abstract class NMSStorage {
protected Method getMethod(Class<?> clazz, String[] names, Class<?>... parameterTypes) throws NoSuchMethodException {
for (String name : names) {
try {
return getMethod(clazz, name, parameterTypes);
return clazz.getMethod(name, parameterTypes);
} catch (NoSuchMethodException e) {
//not the first method in array
}
@@ -490,25 +600,18 @@ public abstract class NMSStorage {
Arrays.toString(parameterTypes) + " in class " + clazz.getName() + ". Methods with matching parameters: " + list);
}
protected Method getMethod(Class<?> clazz, String name, Class<?>... parameterTypes) throws NoSuchMethodException {
List<Method> list = new ArrayList<>();
for (Method m : clazz.getMethods()) {
if (!m.getName().equals(name) || m.getParameterCount() != parameterTypes.length) continue;
Class<?>[] types = m.getParameterTypes();
boolean valid = true;
for (int i=0; i<types.length; i++) {
if (types[i] != parameterTypes[i]) {
valid = false;
break;
}
}
if (valid) list.add(m);
}
if (!list.isEmpty()) return list.get(0);
throw new NoSuchMethodException("No method found with name " + name + " in class " + clazz.getName() + " with parameters " + Arrays.toString(parameterTypes));
}
protected List<Method> getMethods(Class<?> clazz, Class<?> returnType, Class<?>... parameterTypes){
/**
* Returns all methods from class which return specified class type and have specified parameter types.
*
* @param clazz
* Class to get methods from
* @param returnType
* Return type of methods
* @param parameterTypes
* Parameter types of methods
* @return List of found methods matching requirements. If nothing is found, empty list is returned.
*/
protected List<Method> getMethods(Class<?> clazz, Class<?> returnType, Class<?>... parameterTypes) {
List<Method> list = new ArrayList<>();
for (Method m : clazz.getDeclaredMethods()) {
if (m.getReturnType() != returnType || m.getParameterCount() != parameterTypes.length || !Modifier.isPublic(m.getModifiers())) continue;
@@ -534,8 +637,7 @@ public abstract class NMSStorage {
* field type to check for
* @return list of all fields with specified class type
*/
protected List<Field> getFields(Class<?> clazz, Class<?> type){
if (clazz == null) throw new IllegalArgumentException("Source class cannot be null");
protected List<Field> getFields(Class<?> clazz, Class<?> type) {
List<Field> list = new ArrayList<>();
for (Field field : clazz.getDeclaredFields()) {
if (field.getType() == type) {
@@ -554,8 +656,7 @@ public abstract class NMSStorage {
* field type to check for
* @return list of all fields with specified class type
*/
public List<Field> getInstanceFields(Class<?> clazz, Class<?> type){
if (clazz == null) throw new IllegalArgumentException("Source class cannot be null");
public List<Field> getInstanceFields(Class<?> clazz, Class<?> type) {
List<Field> list = new ArrayList<>();
for (Field field : clazz.getDeclaredFields()) {
if (field.getType() == type && !Modifier.isStatic(field.getModifiers())) {
@@ -585,40 +686,45 @@ public abstract class NMSStorage {
throw new NoSuchFieldException("Field \"" + name + "\" was not found in class " + clazz.getName());
}
protected Field getField(Class<?> clazz, String... potentialNames) throws NoSuchFieldException {
for (String name : potentialNames) {
try {
return getField(clazz, name);
} catch (NoSuchFieldException e) {
//not the first field name from array
}
}
throw new NoSuchFieldException("No field found in class " + clazz.getName() + " with potential names " + Arrays.toString(potentialNames));
}
protected Enum[] getEnumValues(Class<?> enumClass) {
if (enumClass == null) throw new IllegalArgumentException("Class cannot be null");
if (!enumClass.isEnum()) throw new IllegalArgumentException(enumClass.getName() + " is not an enum class");
try {
return (Enum[]) enumClass.getMethod("values").invoke(null);
} catch (ReflectiveOperationException e) {
//this should never happen
return new Enum[0];
}
}
/**
* Returns server's minor version
* @return server's minor version
*/
public int getMinorVersion() {
return minorVersion;
}
/**
* Sets value of a field in object
*
* @param obj
* Instance of object where field should be set
* @param field
* Field to set value of
* @param value
* Value to set field to
* @throws IllegalAccessException
* If thrown by reflective operation
*/
public void setField(Object obj, Field field, Object value) throws IllegalAccessException {
field.set(obj, value);
}
/**
* Returns data watcher registry
* @return data watcher registry
*/
public DataWatcherRegistry getDataWatcherRegistry() {
return registry;
}
/**
* Makes object accessible and returns it for chaining.
*
* @param o
* Object to make accessible
* @return Entered object, for chaining
*/
public <T extends AccessibleObject> T setAccessible(T o) {
o.setAccessible(true);
return o;
@@ -645,4 +751,8 @@ public abstract class NMSStorage {
}
return fields;
}
public boolean is1_19_3Plus() {
return is1_19_3Plus;
}
}
@@ -0,0 +1,49 @@
package me.neznamy.tab.platforms.bukkit.nms.storage;
import me.neznamy.tab.platforms.bukkit.Main;
/**
* NMS loader for Thermos 1.7.10.
*/
public class ThermosNMSStorage extends BukkitLegacyNMSStorage {
/**
* Creates new instance, initializes required NMS classes and fields
*
* @throws ReflectiveOperationException
* If any class, field or method fails to load
*/
public ThermosNMSStorage() throws ReflectiveOperationException {
}
/**
* Returns class from given name
*
* @param name
* class name
* @return class from given name
* @throws ClassNotFoundException
* if class was not found
*/
@Override
public Class<?> getLegacyClass(String name) throws ClassNotFoundException {
try {
return Main.class.getClassLoader().loadClass("net.minecraft.server." + serverPackage + "." + name);
} catch (NullPointerException e) {
// nested class not found
throw new ClassNotFoundException(name);
}
}
@Override
public void loadNamedFieldsAndMethods() throws ReflectiveOperationException {
PING = getField(EntityPlayer, "field_71138_i");
ScoreboardScore_setScore = ScoreboardScore.getMethod("func_96647_c", int.class);
ScoreboardTeam_setAllowFriendlyFire = ScoreboardTeam.getMethod("func_96660_a", boolean.class);
ScoreboardTeam_setCanSeeFriendlyInvisibles = ScoreboardTeam.getMethod("func_98300_b", boolean.class);
ChatSerializer_DESERIALIZE = ChatSerializer.getMethod("func_150699_a", String.class);
DataWatcher_REGISTER = DataWatcher.getMethod("func_75682_a", int.class, Object.class);
ScoreboardTeam_setPrefix = ScoreboardTeam.getMethod("func_96666_b", String.class);
ScoreboardTeam_setSuffix = ScoreboardTeam.getMethod("func_96662_c", String.class);
}
}
-231
View File
@@ -1,231 +0,0 @@
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Header-&-Footer
header-footer:
enabled: true
disable-in-worlds:
- disabledworld
header:
- "&3&lServer name"
- "&7&l>> %animation:Welcome%&3 &l%player%&7&l! &7&l<<"
- "&7Online players: &f%online%"
- "&6Online staff: &e%staffonline%"
- ""
- "%animation:MyAnimation1%"
footer:
- "%animation:MyAnimation1%"
- "%animation:time%"
- "%animation:ping-tps%"
- "&7&l Used memory: %memory-used% MB / %memory-max% MB"
- ""
- "&7Visit our webpage %animation:web%"
per-world:
world1:
header:
- "an example of world with custom"
footer:
- "header/footer and prefix/suffix"
world2;world3:
header:
- "This is a shared header for"
- "world2 and world3"
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Tablist-name-formatting
tablist-name-formatting:
enabled: true
align-tabsuffix-on-the-right: false
character-width-overrides: {}
anti-override: true
disable-in-worlds:
- disabledworld
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Nametags
scoreboard-teams:
enabled: true
enable-collision: true
invisible-nametags: false
anti-override: true
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Sorting-players-in-tablist
sorting-types:
- "GROUPS:owner,admin,mod,helper,builder,vip,default"
- "PLACEHOLDER_A_TO_Z:%player%"
case-sensitive-sorting: true
disable-in-worlds:
- disabledworld
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Unlimited-nametag-mode
unlimited-nametag-mode:
enabled: false
use-marker-tag-for-1-8-x-clients: false
disable-on-boats: true
disable-in-worlds:
- disabledworld
dynamic-lines:
- abovename
- nametag #the original one, combination of tagprefix + customtagname + tagsuffix
- belowname
- another
static-lines:
myCustomLine: 0.66
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Yellow-number
yellow-number-in-tablist:
enabled: true
value: "%ping%"
disable-in-worlds:
- disabledworld
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Belowname
belowname-objective:
enabled: false
number: "%health%"
text: "&cHealth"
disable-in-worlds:
- disabledworld
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Spectator-fix
prevent-spectator-effect:
enabled: false
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Bossbar
bossbar:
enabled: false
toggle-command: /bossbar
remember-toggle-choice: false
hidden-by-default: false
disable-in-worlds:
- disabledworld
bars:
ServerInfo:
style: "PROGRESS" # for 1.9+: PROGRESS, NOTCHED_6, NOTCHED_10, NOTCHED_12, NOTCHED_20
color: "%animation:barcolors%" # for 1.9+: BLUE, GREEN, PINK, PURPLE, RED, WHITE, YELLOW
progress: "100" # in %
text: "&fWebsite: &bwww.domain.com"
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Per-world-playerlist
per-world-playerlist:
enabled: false
# players with tab.staff will always see all players
allow-bypass-permission: false
# players in these worlds will always see all players
ignore-effect-in-worlds:
- ignoredworld
- build
shared-playerlist-world-groups:
lobby:
- lobby1
- lobby2
minigames:
- paintball
- bedwars
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Scoreboard
scoreboard:
enabled: false
toggle-command: /sb
remember-toggle-choice: false
hidden-by-default: false
use-numbers: false
static-number: 0
delay-on-join-milliseconds: 0
respect-other-plugins: true
disable-in-worlds:
- disabledworld
scoreboards:
admin:
# Only players with tab.scoreboard.admin can see this scoreboard, others will see scoreboard1
display-condition: "permission:tab.scoreboard.admin"
title: "Admin scoreboard"
lines:
- "%animation:MyAnimation1%"
- "&6Online:"
- "* &eOnline&7: &f%online%"
- "* &eCurrent World&7: &f%worldonline%"
- "* &eStaff&7: &f%staffonline%"
- " "
- "&6Server Info:"
- "* &bTPS&7: %tps%"
- "* &bUptime&7: &f%server_uptime%"
- "* &bMemory&7: &f%memory-used%&7/&4%memory-max%"
- "%animation:MyAnimation1%"
scoreboard1:
title: "Default"
lines:
- "%animation:MyAnimation1%"
- "&6My Stats:"
- "* &eKills&7: &f%statistic_player_kills%"
- "* &eDeaths&7: &f%statistic_deaths%"
- "* &eHealth&7: &f%health%"
- " "
- "&6Personal Info:"
- "* &bRank&7: &f%group%"
- "* &bPing&7: &f%ping%&7ms"
- "* &bWorld&7: &f%world%"
- "%animation:MyAnimation1%"
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Ping-Spoof
ping-spoof:
enabled: false
value: 0
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Pet-name-fix
fix-pet-names:
enabled: false
placeholders:
date-format: "dd.MM.yyyy"
time-format: "[HH:mm:ss / h:mm a]"
time-offset: 0
register-tab-expansion: false
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Placeholder-output-replacements
placeholder-output-replacements:
"%essentials_vanished%":
"yes": "&7| Vanished"
"no": ""
"%afk%":
true: " &o[AFK]&r"
false: ""
"%essentials_nickname%":
"%essentials_nickname%": "%player%" #placeholder did not parse correctly, show real name instead
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Conditional-placeholders
conditions:
nick: # use it with %condition:nick%
conditions:
- "%player%=%essentials_nickname%"
yes: "%player%"
no: "~%essentials_nickname%"
placeholderapi-refresh-intervals:
default-refresh-interval: 500
server:
"%server_uptime%": 1000
"%server_tps_1_colored%": 1000
"%server_unique_joins%": 5000
player:
"%player_health%": 200
"%player_ping%": 1000
"%vault_prefix%": 1000
relational:
"%rel_factionsuuid_relation_color%": 1000
# assigning groups by permission nodes instead of taking them from permission plugin
assign-groups-by-permissions: false
# if the option above is true, all groups are taken based on permissions and the one higher in this list is used as primary
# Warning! This is not sorting list and has nothing to do with sorting players in tablist!
primary-group-finding-list:
- Owner
- Admin
- Mod
- Helper
- default
debug: false
# https://github.com/NEZNAMY/TAB/wiki/MySQL
mysql:
enabled: false
host: 127.0.0.1
port: 3306
database: tab
username: user
password: password
@@ -15,8 +15,15 @@ import net.md_5.bungee.event.EventHandler;
*/
public class BungeeEventListener implements Listener {
/** Reference to BungeeCord platform implementation */
private final BungeePlatform platform;
/**
* Constructs new instance with given parameter
*
* @param platform
* BungeeCord implementation of platform
*/
public BungeeEventListener(BungeePlatform platform) {
this.platform = platform;
}
@@ -1,7 +1,7 @@
package me.neznamy.tab.platforms.bungeecord;
import java.util.ArrayList;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.chat.EnumChatFormat;
@@ -44,6 +44,37 @@ public class BungeePacketBuilder extends PacketBuilder {
@Override
public Object build(PacketPlayOutPlayerInfo packet, ProtocolVersion clientVersion) {
if (clientVersion.getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId()) {
if (packet.getActions().contains(EnumPlayerInfoAction.REMOVE_PLAYER)) {
PlayerListItemRemove remove = new PlayerListItemRemove();
remove.setUuids(packet.getEntries().stream().map(PlayerInfoData::getUniqueId).toArray(UUID[]::new));
return remove;
}
List<Item> items = new ArrayList<>();
for (PlayerInfoData data : packet.getEntries()) {
Item item = new Item();
if (data.getDisplayName() != null) item.setDisplayName(data.getDisplayName().toString(clientVersion));
if (data.getGameMode() != null) item.setGamemode(data.getGameMode().ordinal()-1);
item.setListed(data.isListed());
item.setPing(data.getLatency());
if (data.getSkin() != null) {
item.setProperties(new Property[]{new Property("textures", data.getSkin().getValue(), data.getSkin().getSignature())});
} else {
item.setProperties(new Property[0]);
}
item.setUsername(data.getName());
item.setUuid(data.getUniqueId());
item.setChatSessionId(data.getChatSessionId());
item.setPublicKey((PlayerPublicKey) data.getProfilePublicKey());
items.add(item);
}
PlayerListItemUpdate bungeePacket = new PlayerListItemUpdate();
PlayerListItemUpdate.Action[] array = packet.getActions().stream().map(action -> PlayerListItemUpdate.Action.valueOf(
action.toString().replace("GAME_MODE", "GAMEMODE"))).toArray(PlayerListItemUpdate.Action[]::new);
bungeePacket.setActions(EnumSet.of(array[0], array));
bungeePacket.setItems(items.toArray(new Item[0]));
return bungeePacket;
}
List<Item> items = new ArrayList<>();
for (PlayerInfoData data : packet.getEntries()) {
Item item = new Item();
@@ -69,7 +100,9 @@ public class BungeePacketBuilder extends PacketBuilder {
items.add(item);
}
PlayerListItem bungeePacket = new PlayerListItem();
bungeePacket.setAction(PlayerListItem.Action.valueOf(packet.getAction().toString().replace("GAME_MODE", "GAMEMODE")));
EnumPlayerInfoAction action = packet.getActions().contains(EnumPlayerInfoAction.ADD_PLAYER) ?
EnumPlayerInfoAction.ADD_PLAYER : packet.getActions().iterator().next();
bungeePacket.setAction(PlayerListItem.Action.valueOf(action.toString().replace("GAME_MODE", "GAMEMODE")));
bungeePacket.setItems(items.toArray(new Item[0]));
return bungeePacket;
}
@@ -106,13 +139,50 @@ public class BungeePacketBuilder extends PacketBuilder {
@Override
public PacketPlayOutPlayerInfo readPlayerInfo(Object bungeePacket, ProtocolVersion clientVersion) {
PlayerListItem item = (PlayerListItem) bungeePacket;
List<PlayerInfoData> listData = new ArrayList<>();
for (Item i : item.getItems()) {
Skin skin = i.getProperties() == null || i.getProperties().length == 0 ? null : new Skin(i.getProperties()[0].getValue(), i.getProperties()[0].getSignature());
listData.add(new PlayerInfoData(i.getUsername(), i.getUuid(), skin, i.getPing(), EnumGamemode.VALUES[i.getGamemode()+1], IChatBaseComponent.deserialize(i.getDisplayName()), i.getPublicKey()));
if (clientVersion.getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId()) {
if (bungeePacket instanceof PlayerListItemRemove) {
return new PacketPlayOutPlayerInfo(
EnumPlayerInfoAction.REMOVE_PLAYER,
Arrays.stream(((PlayerListItemRemove) bungeePacket).getUuids()).map(PlayerInfoData::new).collect(Collectors.toList())
);
}
PlayerListItemUpdate item = (PlayerListItemUpdate) bungeePacket;
List<PlayerInfoData> listData = new ArrayList<>();
for (Item i : item.getItems()) {
Skin skin = i.getProperties() == null || i.getProperties().length == 0 ? null : new Skin(i.getProperties()[0].getValue(), i.getProperties()[0].getSignature());
listData.add(new PlayerInfoData(
i.getUsername(),
i.getUuid(),
skin,
Boolean.TRUE.equals(i.getListed()),
i.getPing() == null ? 0 : i.getPing(),
i.getGamemode() == null ? null : EnumGamemode.VALUES[i.getGamemode()+1],
IChatBaseComponent.deserialize(i.getDisplayName()),
i.getChatSessionId(),
i.getPublicKey()));
}
EnumPlayerInfoAction[] array = item.getActions().stream().map(action ->
EnumPlayerInfoAction.valueOf(action.toString().replace("GAMEMODE", "GAME_MODE"))).toArray(EnumPlayerInfoAction[]::new);
EnumSet<EnumPlayerInfoAction> actions = EnumSet.of(array[0], array);
return new PacketPlayOutPlayerInfo(actions, listData);
} else {
PlayerListItem item = (PlayerListItem) bungeePacket;
List<PlayerInfoData> listData = new ArrayList<>();
for (Item i : item.getItems()) {
Skin skin = i.getProperties() == null || i.getProperties().length == 0 ? null : new Skin(i.getProperties()[0].getValue(), i.getProperties()[0].getSignature());
listData.add(new PlayerInfoData(
i.getUsername(),
i.getUuid(),
skin,
true,
i.getPing() == null ? 0 : i.getPing(),
i.getGamemode() == null ? null : EnumGamemode.VALUES[i.getGamemode()+1],
IChatBaseComponent.deserialize(i.getDisplayName()),
null,
i.getPublicKey()));
}
return new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.valueOf(item.getAction().toString().replace("GAMEMODE", "GAME_MODE")), listData);
}
return new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.valueOf(item.getAction().toString().replace("GAMEMODE", "GAME_MODE")), listData);
}
@Override
@@ -11,6 +11,7 @@ import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.shared.features.PipelineInjector;
import me.neznamy.tab.shared.features.redis.RedisPlayer;
import me.neznamy.tab.shared.features.sorting.Sorting;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.packet.ScoreboardDisplay;
@@ -68,6 +69,8 @@ public class BungeePipelineInjector extends PipelineInjector {
try {
switch(packet.getClass().getSimpleName()) {
case "PlayerListItem":
case "PlayerListItemUpdate":
case "PlayerListItemRemove":
super.write(context, TAB.getInstance().getFeatureManager().onPacketPlayOutPlayerInfo(player, packet), channelPromise);
return;
case "Team":
@@ -91,7 +94,7 @@ public class BungeePipelineInjector extends PipelineInjector {
default:
break;
}
} catch (Exception e){
} catch (Exception | NoSuchMethodError e){
TAB.getInstance().getErrorManager().printError("An error occurred when analyzing packets for player " + player.getName() + " with client version " + player.getVersion().getFriendlyName(), e);
}
try {
@@ -111,9 +114,10 @@ public class BungeePipelineInjector extends PipelineInjector {
if (packet.getMode() == 1 || packet.getMode() == 2 || packet.getMode() == 4) return;
Collection<String> col = Lists.newArrayList(packet.getPlayers());
for (TabPlayer p : TAB.getInstance().getOnlinePlayers()) {
Sorting sorting = (Sorting) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
if (col.contains(p.getNickname()) && !((TabFeature)TAB.getInstance().getTeamManager()).isDisabledPlayer(p) &&
!TAB.getInstance().getTeamManager().hasTeamHandlingPaused(p) && !packet.getName().equals(p.getTeamName())) {
logTeamOverride(packet.getName(), p.getName(), p.getTeamName());
!TAB.getInstance().getTeamManager().hasTeamHandlingPaused(p) && !packet.getName().equals(sorting.getShortTeamName(p))) {
logTeamOverride(packet.getName(), p.getName(), sorting.getShortTeamName(p));
col.remove(p.getNickname());
}
}
@@ -14,7 +14,7 @@ import net.md_5.bungee.api.plugin.Plugin;
public class BungeePlatform extends ProxyPlatform {
/**
* Constructs new instance
* Constructs new instance with BungeeCord packet builder
*/
public BungeePlatform() {
super(new BungeePacketBuilder());
@@ -1,22 +1,25 @@
package me.neznamy.tab.platforms.bungeecord;
import de.myzelyam.api.vanish.BungeeVanishAPI;
import io.netty.channel.Channel;
import me.neznamy.tab.api.ProtocolVersion;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.api.protocol.Skin;
import me.neznamy.tab.api.util.Preconditions;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.shared.proxy.ProxyTabPlayer;
import net.md_5.bungee.UserConnection;
import net.md_5.bungee.api.ProxyServer;
import net.md_5.bungee.api.connection.ProxiedPlayer;
import net.md_5.bungee.connection.InitialHandler;
import net.md_5.bungee.connection.LoginResult;
import net.md_5.bungee.netty.ChannelWrapper;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.Property;
import net.md_5.bungee.protocol.Protocol;
import net.md_5.bungee.protocol.packet.LoginRequest;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.UUID;
/**
* TabPlayer implementation for BungeeCord
@@ -24,37 +27,18 @@ import java.lang.reflect.Method;
public class BungeeTabPlayer extends ProxyTabPlayer {
/** Inaccessible bungee internals */
private static Method InitialHandler_getLoginProfile;
private static Method InitialHandler_getLoginRequest;
private static Method ChannelWrapper_getHandle;
private static Method LoginResult_Property_getValue;
private static Method LoginResult_Property_getSignature;
private static Method LoginResult_getProperties;
private static Method UserConnection_getGamemode;
private static Field wrapperField;
private static Object directionData;
private static Method getId;
static {
try {
Class<?> initialHandler = Class.forName("net.md_5.bungee.connection.InitialHandler");
InitialHandler_getLoginProfile = initialHandler.getMethod("getLoginProfile");
InitialHandler_getLoginRequest = initialHandler.getMethod("getLoginRequest");
Class<?> channelWrapper = Class.forName("net.md_5.bungee.netty.ChannelWrapper");
ChannelWrapper_getHandle = channelWrapper.getMethod("getHandle");
Class<?> loginResult = Class.forName("net.md_5.bungee.connection.LoginResult");
Class<?> loginResult_Property = Class.forName("net.md_5.bungee.protocol.Property");
LoginResult_Property_getValue = loginResult_Property.getMethod("getValue");
LoginResult_Property_getSignature = loginResult_Property.getMethod("getSignature");
LoginResult_getProperties = loginResult.getMethod("getProperties");
Class<?> userConnection = Class.forName("net.md_5.bungee.UserConnection");
UserConnection_getGamemode = userConnection.getMethod("getGamemode");
Field f = Protocol.class.getDeclaredField("TO_CLIENT");
f.setAccessible(true);
directionData = f.get(Protocol.GAME);
getId = directionData.getClass().getDeclaredMethod("getId", Class.class, int.class);
getId.setAccessible(true);
wrapperField = initialHandler.getDeclaredField("ch");
wrapperField = InitialHandler.class.getDeclaredField("ch");
wrapperField.setAccessible(true);
} catch (ReflectiveOperationException e) {
TAB.getInstance().getErrorManager().criticalError("Failed to initialize bungee internal fields", e);
@@ -70,8 +54,8 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
public BungeeTabPlayer(ProxiedPlayer p) {
super(p, p.getUniqueId(), p.getName(), p.getServer() != null ? p.getServer().getInfo().getName() : "-", -1, true);
try {
channel = (Channel) ChannelWrapper_getHandle.invoke(wrapperField.get(getPlayer().getPendingConnection()));
} catch (IllegalAccessException | InvocationTargetException e) {
channel = ((ChannelWrapper)wrapperField.get(getPlayer().getPendingConnection())).getHandle();
} catch (IllegalAccessException e) {
TAB.getInstance().getErrorManager().criticalError("Failed to get channel of " + getPlayer().getName(), e);
}
}
@@ -99,17 +83,11 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
@Override
public Skin getSkin() {
try {
Object loginResult = InitialHandler_getLoginProfile.invoke(getPlayer().getPendingConnection());
if (loginResult == null) return null;
Object[] properties = (Object[]) LoginResult_getProperties.invoke(loginResult);
if (properties == null || properties.length == 0) return null;
return new Skin((String) LoginResult_Property_getValue.invoke(properties[0]),
(String) LoginResult_Property_getSignature.invoke(properties[0]));
} catch (ReflectiveOperationException e) {
TAB.getInstance().getErrorManager().printError("Failed to get skin of " + getName(), e);
return null;
}
LoginResult loginResult = ((InitialHandler)getPlayer().getPendingConnection()).getLoginProfile();
if (loginResult == null) return null;
Property[] properties = loginResult.getProperties();
if (properties == null || properties.length == 0) return null;
return new Skin(properties[0].getValue(), properties[0].getSignature());
}
@Override
@@ -157,22 +135,17 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
@Override
public int getGamemode() {
try {
return (int) UserConnection_getGamemode.invoke(player);
} catch (ReflectiveOperationException e) {
TAB.getInstance().getErrorManager().printError("Failed to get gamemode of " + getPlayer().getName(), e);
return 0;
}
return ((UserConnection)player).getGamemode();
}
@Override
public Object getProfilePublicKey() {
try {
return ((LoginRequest) InitialHandler_getLoginRequest.invoke(getPlayer().getPendingConnection())).getPublicKey();
} catch (ReflectiveOperationException e) {
TAB.getInstance().getErrorManager().printError("Failed to get profile key of " + getPlayer().getName(), e);
return null;
}
return ((InitialHandler)getPlayer().getPendingConnection()).getLoginRequest().getPublicKey();
}
@Override
public UUID getChatSessionId() {
throw new UnsupportedOperationException("Not implemented yet");
}
@Override
@@ -26,7 +26,7 @@ public class Main extends Plugin {
@Override
public void onEnable(){
if (!isVersionSupported()) {
getLogger().info(EnumChatFormat.color("&cThe plugin requires BungeeCord build #1637 and up to work. Get it at https://ci.md-5.net/job/BungeeCord/"));
getLogger().info(EnumChatFormat.color("&cThe plugin requires BungeeCord build #1671 and up (or an equivalent fork) to work."));
return;
}
BungeePlatform platform = new BungeePlatform();
@@ -41,13 +41,13 @@ public class Main extends Plugin {
}
/**
* Checks for compatibility and returns true if version is supported, false if not
* Checks for compatibility and returns {@code true} if version is supported, {@code false} if not
*
* @return true if version is compatible, false if not
* @return {@code true} if version is compatible, {@code false} if not
*/
private boolean isVersionSupported() {
try {
Class.forName("net.md_5.bungee.protocol.Property");
Class.forName("net.md_5.bungee.protocol.packet.PlayerListItemUpdate");
return true;
} catch (ClassNotFoundException e) {
return false;
+1 -1
View File
@@ -1,5 +1,5 @@
# Project properties - we put these here so they can be modified without causing a recompile of the build scripts
projectVersion=3.1.4
projectVersion=3.2.3-SNAPSHOT
# Gradle properties
org.gradle.daemon=true
+21 -13
View File
@@ -3,19 +3,19 @@ metadata.format.version = "1.1"
[versions]
# API
netty = "4.1.78.Final"
netty = "4.1.86.Final"
jsonSimple = "1.1.1"
snakeyaml = "1.30"
snakeyaml = "1.33"
yamlAssist = "1.0.5"
gson = "2.9.0"
slf4j = "1.7.36"
minimessage = "4.11.0"
gson = "2.10"
slf4j = "2.0.6"
minimessage = "4.12.0"
# General
luckperms = "5.4"
guava = "31.1-jre"
floodgate = "2.0-SNAPSHOT"
kotlin = "1.6.10"
floodgate = "2.2.0-SNAPSHOT"
kotlin = "1.8.0"
event = "3.0.0"
# Platforms
@@ -25,14 +25,18 @@ krypton = "0.59.2"
velocity = "3.1.2-SNAPSHOT"
# Platform extras
placeholderapi = "2.11.1"
placeholderapi = "2.11.2"
vault = "1.7"
via = "4.3.1"
via = "4.5.1"
authlib = "1.5.25"
bstats = "3.0.0"
essentials = "2.19.4"
premiumVanish = "2.0.3"
redisBungee = "0.7.2"
essentials = "2.19.7"
premiumVanish = "2.7.11-2"
redisBungee = "0.10.1"
# Plugins
indra = "2.1.1"
shadow = "7.1.2"
[libraries]
@@ -56,7 +60,7 @@ event = { group = "net.kyori", name = "event-method", version.ref = "event" }
# Platforms
bukkit = { group = "org.purpurmc.purpur", name = "purpur-api", version.ref = "bukkit" }
bungeecord = { group = "net.md-5", name = "bungeecord-api", version.ref = "bungeecord" }
bungeecord = { group = "net.md-5", name = "bungeecord-proxy", version.ref = "bungeecord" }
krypton-api = { group = "org.kryptonmc", name = "krypton-api", version.ref = "krypton" }
krypton-server = { group = "org.kryptonmc", name = "krypton-server", version.ref = "krypton" }
krypton-annotationProcessor = { group = "org.kryptonmc", name = "krypton-annotation-processor", version.ref = "krypton" }
@@ -75,3 +79,7 @@ redisBungee = { group = "com.github.limework.redisbungee", name = "RedisBungee-B
bstats-bukkit = { group = "org.bstats", name = "bstats-bukkit", version.ref = "bstats" }
bstats-bungeecord = { group = "org.bstats", name = "bstats-bungeecord", version.ref = "bstats" }
bstats-velocity = { group = "org.bstats", name = "bstats-velocity", version.ref = "bstats" }
# Plugins
plugin-indra = { group = "net.kyori", name = "indra-common", version.ref = "indra" }
plugin-shadow = { group = "gradle.plugin.com.github.johnrengelman", name = "shadow", version.ref = "shadow" }
Binary file not shown.
+1 -1
View File
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
Vendored
+6
View File
@@ -205,6 +205,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
+8 -6
View File
@@ -14,7 +14,7 @@
@rem limitations under the License.
@rem
@if "%DEBUG%" == "" @echo off
@if "%DEBUG%"=="" @echo off
@rem ##########################################################################
@rem
@rem Gradle startup script for Windows
@@ -25,7 +25,7 @@
if "%OS%"=="Windows_NT" setlocal
set DIRNAME=%~dp0
if "%DIRNAME%" == "" set DIRNAME=.
if "%DIRNAME%"=="" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@@ -40,7 +40,7 @@ if defined JAVA_HOME goto findJavaFromJavaHome
set JAVA_EXE=java.exe
%JAVA_EXE% -version >NUL 2>&1
if "%ERRORLEVEL%" == "0" goto execute
if %ERRORLEVEL% equ 0 goto execute
echo.
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
@@ -75,13 +75,15 @@ set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
:end
@rem End local scope for the variables with windows NT shell
if "%ERRORLEVEL%"=="0" goto mainEnd
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!
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
exit /b 1
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
@@ -95,8 +95,6 @@ class KryptonPlatform(
override fun isPluginEnabled(plugin: String): Boolean = server.pluginManager.isLoaded(plugin)
override fun getConfigName(): String = "kryptonconfig.yml"
private fun loadNametagFeature() {
if (!TAB.getInstance().config.getBoolean("scoreboard-teams.enabled", true)) return
if (TAB.getInstance().config.getBoolean("scoreboard-teams.unlimited-nametag-mode.enabled", false)) {
@@ -1,214 +0,0 @@
# Detailed explanation of all options available at https://github.com/NEZNAMY/TAB/wiki/config.yml
scoreboard-teams:
enabled: true
enable-collision: true
invisible-nametags: false
anti-override: true
# types: GROUPS:<group1>,<group2>... , PERMISSIONS:<permission1>,<perm2>... , PLACEHOLDER:<output1>,<output2>... , PLACEHOLDER_A_TO_Z, PLACEHOLDER_Z_TO_A, PLACEHOLDER_HIGH_TO_LOW, PLACEHOLDER_LOW_TO_HIGH
sorting-types:
- "GROUPS:owner,admin,mod,helper,builder,vip,default"
- "PLACEHOLDER_A_TO_Z:%player%"
case-sensitive-sorting: true
disable-in-worlds:
- disabledworld
unlimited-nametag-mode:
enabled: false
use-marker-tag-for-1-8-x-clients: false
disable-on-boats: true
space-between-lines: 0.22
disable-in-worlds:
- disabledworld
dynamic-lines:
- abovename
- nametag #the original one, combination of tagprefix + customtagname + tagsuffix
- belowname
- another
static-lines:
myCustomLine: 0.66
tablist-name-formatting:
enabled: true
align-tabsuffix-on-the-right: false
character-width-overrides: {}
anti-override: true
disable-in-worlds:
- disabledworld
header-footer:
enabled: true
disable-in-worlds:
- disabledworld
header:
- "%animation:ServerName%"
- "&7&l>> %animation:Welcome%&3 &l%player%&7&l! &7&l<<"
- "&7Online players: &f%online%"
- "&6Online staff: &e%staffonline%"
- ""
- "%animation:MyAnimation1%"
footer:
- "%animation:MyAnimation1%"
- "%animation:time%"
- "%animation:ping-tps%"
- "&7&l Used memory: %memory-used% MB / %memory-max% MB"
- ""
- "&7Visit our webpage %animation:web%"
per-world:
world1:
header:
- "an example of world with custom"
footer:
- "header/footer and prefix/suffix"
world2-world3:
header:
- "This is a shared header for"
- "world2 and world3"
yellow-number-in-tablist:
enabled: true
value: "%ping%"
disable-in-worlds:
- disabledworld
belowname-objective:
enabled: false
number: "%health%"
text: "&c❤"
disable-in-worlds:
- disabledworld
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Spectator-fix
prevent-spectator-effect:
enabled: false
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Bossbar
bossbar:
enabled: false
toggle-command: /bossbar
remember-toggle-choice: false
hidden-by-default: false
disable-in-worlds:
- disabledworld
bars:
ServerInfo:
style: "PROGRESS" # can be one of: PROGRESS, NOTCHED_6, NOTCHED_10, NOTCHED_12, NOTCHED_20
color: "%animation:barcolors%" # can be one of: BLUE, GREEN, PINK, PURPLE, RED, WHITE, YELLOW
progress: "100" # in %
text: "&fWebsite: &bwww.domain.com"
tpsbar:
announcement-bar: true #marks this bossbar as announcement one and doesn't send it always
style: NOTCHED_20
color: GREEN
progress: "%mspt_mean_10s%"
text: "&7TPS&e: %condition:tps_5s% &7MSPT&e: %condition:mspt_mean_10s% &7Ping&e: %condition:ping%&7ms"
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Per-world-playerlist
per-world-playerlist:
enabled: false
# players with tab.staff will always see all players
allow-bypass-permission: false
# players in those worlds will always see all players
ignore-effect-in-worlds:
- ignoredworld
- build
shared-playerlist-world-groups:
lobby:
- lobby1
- lobby2
minigames:
- paintball
- bedwars
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Scoreboard-(premium-only)
scoreboard:
enabled: false
# show/hide the scoreboard
toggle-command: /sb
remember-toggle-choice: false
hidden-by-default: false
# when enabled, will show 0-14 in red next to lines
use-numbers: false
# if use-numbers is disabled, this number will show in red next to all lines
static-number: 0
delay-on-join-milliseconds: 0
respect-other-plugins: true
disable-in-worlds:
- disabledworld
scoreboards:
admin:
# Only players with tab.scoreboard.admin can see this scoreboard, others will see scoreboard1
display-condition: "permission:tab.scoreboard.admin"
title: "Admin scoreboard"
lines:
- "%animation:MyAnimation1%"
- "&6Online:"
- "* &eOnline&7: &f%online%&7/&4%maxplayers%"
- "* &eCurrent World&7: &f%worldonline%"
- "* &eStaff&7: &f%staffonline%"
- ""
- "&6Server Info:"
- "* &bTPS&7: %tps_5s%"
- "* &bUptime&7: &f%server_uptime%"
- "* &bMemory&7: &f%memory-used%&7/&4%memory-max%"
- "%animation:MyAnimation1%"
scoreboard1:
title: "Default"
lines:
- "%animation:MyAnimation1%"
- "&6My Stats:"
- "* &eKills&7: &f%statistic_player_kills%"
- "* &eDeaths&7: &f%statistic_deaths%"
- "* &eHealth&7: &f%health%"
- ""
- "&6Personal Info:"
- "* &bRank&7: &f%rank%"
- "* &bPing&7: &f%ping%&7ms"
- "* &bWorld&7: &f%world%"
- "%animation:MyAnimation1%"
ping-spoof:
enabled: false
# when enabled, all players will have this value as their ping
value: 0
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Pet-name-fix
fix-pet-names:
enabled: false
# https://github.com/NEZNAMY/TAB/wiki/config.yml#placeholders
placeholders:
date-format: "dd.MM.yyyy"
time-format: "[HH:mm:ss / h:mm a]"
time-offset: 0
# https://github.com/NEZNAMY/TAB/wiki/Feature-guide:-Placeholder-output-replacements-(premium-only)
placeholder-output-replacements:
"%essentials_vanished%":
"yes": "&7| Vanished"
"no": ""
"%afk%":
true: " &o[AFK]&r"
false: ""
#if none of the above fits your needs, you can enable this to give groups using tab.group.<group name from the list below> permission
assign-groups-by-permissions: false
#if the option above is true, all groups are taken based on permissions and the one higher in this list is used as primary
primary-group-finding-list:
- Owner
- Admin
- Mod
- Helper
- default
debug: false
mysql:
enabled: false
host: 127.0.0.1
port: 3306
database: tab
username: user
password: password
+7 -1
View File
@@ -8,8 +8,10 @@ dependencyResolutionManagement {
maven("https://repo.extendedclip.com/content/repositories/placeholderapi/") // PlaceholderAPI
maven("https://repo.viaversion.com/") // ViaVersion
maven("https://repo.essentialsx.net/releases/") // Essentials
maven("https://repo.codemc.org/repository/maven-public/") // RedisBungee, BungeeCord
maven("https://repo.codemc.org/repository/maven-public/") // RedisBungee
maven("https://repo.codemc.io/repository/maven-snapshots/") // Fixing compilation
maven("https://repo.kryptonmc.org/releases") // YamlAssist, LuckPerms
maven("https://repo.kryptonmc.org/snapshots") // BungeeCord
maven("https://repo.opencollab.dev/maven-snapshots/") // Floodgate
maven("https://repo.purpurmc.org/snapshots") // Purpur, Vault
maven("https://repo.papermc.io/repository/maven-public/") // Velocity
@@ -19,6 +21,10 @@ dependencyResolutionManagement {
pluginManagement {
includeBuild("build-logic")
repositories {
gradlePluginPortal()
mavenCentral()
}
}
rootProject.name = "TAB"
@@ -44,10 +44,10 @@ public class CpuManager implements ThreadManager {
private Map<String, AtomicInteger> packetsPrevious = new ConcurrentHashMap<>();
/** TAB's main thread where all tasks are executed */
private ExecutorService thread = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("TAB Processing Thread").build());
private final ExecutorService thread = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("TAB Processing Thread").build());
/** Thread pool for delayed and repeating tasks to perform sleep before submitting task to main thread */
private final ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(20,
private final ScheduledThreadPoolExecutor threadPool = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(20,
new ThreadFactoryBuilder().setNameFormat("TAB Repeating / Delayed Thread %d").build());
/** Tasks submitted to main thread before plugin was fully enabled */
@@ -74,13 +74,10 @@ public class CpuManager implements ThreadManager {
}
/**
* Cancels all tasks, new instance is set to avoid errors when starting tasks on shutdown (such as packet readers)
* Cancels all tasks and shuts down thread pools
*/
public void cancelAllTasks() {
//preventing errors when tasks are inserted while shutting down
ExecutorService old = thread;
thread = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat("TAB Processing Thread").build());
old.shutdownNow();
thread.shutdownNow();
threadPool.shutdownNow();
}
@@ -89,7 +86,7 @@ public class CpuManager implements ThreadManager {
*/
public void enable() {
enabled = true;
taskQueue.forEach(this::submit);
new ArrayList<>(taskQueue).forEach(this::submit);
taskQueue.clear();
}
@@ -107,13 +104,20 @@ public class CpuManager implements ThreadManager {
taskQueue.add(task);
return null;
}
return (Future<Void>) thread.submit(() -> {
try {
task.run();
} catch (Exception | LinkageError | StackOverflowError e) {
TAB.getInstance().getErrorManager().printError("An error was thrown when executing task", e);
}
});
if (thread.isShutdown()) return null;
try {
return (Future<Void>) thread.submit(() -> {
try {
task.run();
} catch (Exception | LinkageError | StackOverflowError e) {
TAB.getInstance().getErrorManager().printError("An error was thrown when executing task", e);
}
});
} catch (OutOfMemoryError e) {
TAB.getInstance().getErrorManager().criticalError("Failed to schedule task due to " + e.getClass().getName() +
": " + e.getMessage() + ". Threads created by TAB: " + (threadPool.getActiveCount()+1), null);
return null;
}
}
/**
@@ -359,21 +363,25 @@ public class CpuManager implements ThreadManager {
@Override
public Future<?> startRepeatingMeasuredTask(int intervalMilliseconds, TabFeature feature, String type, Runnable task) {
if (threadPool.isShutdown()) return null;
return threadPool.scheduleAtFixedRate(() -> runMeasuredTask(feature, type, task), 0, intervalMilliseconds, TimeUnit.MILLISECONDS);
}
@Override
public Future<?> startRepeatingTask(int intervalMilliseconds, Runnable task) {
if (threadPool.isShutdown()) return null;
return threadPool.scheduleAtFixedRate(() -> runTask(task), 0, intervalMilliseconds, TimeUnit.MILLISECONDS);
}
@Override
public Future<?> runTaskLater(int delayMilliseconds, TabFeature feature, String type, Runnable task) {
if (threadPool.isShutdown()) return null;
return threadPool.schedule(() -> runMeasuredTask(feature, type, task), delayMilliseconds, TimeUnit.MILLISECONDS);
}
@Override
public Future<?> runTaskLater(int delayMilliseconds, Runnable task) {
if (threadPool.isShutdown()) return null;
return threadPool.schedule(() -> submit(task), delayMilliseconds, TimeUnit.MILLISECONDS);
}
}
@@ -225,6 +225,6 @@ public class DynamicText implements Property {
RelationalPlaceholderImpl pl = (RelationalPlaceholderImpl) TAB.getInstance().getPlaceholderManager().getPlaceholder(identifier);
format = format.replace(pl.getIdentifier(), viewer == null ? "" : pl.getLastValue(viewer, owner));
}
return format;
return EnumChatFormat.color(format);
}
}
@@ -227,7 +227,8 @@ public class ErrorManager {
}
/**
* Makes interval divisible by 50 and sends error message if it was not already or was 0 or less
* Makes interval divisible by {@link me.neznamy.tab.api.TabConstants.Placeholder#MINIMUM_REFRESH_INTERVAL}
* and sends error message if it was not already or was 0 or less
*
* @param name
* name of animation used in error message
@@ -244,10 +245,10 @@ public class ErrorManager {
startupWarn(String.format("Animation \"&e%s&c\" has refresh interval of %s. Refresh cannot be negative! &bUsing 1000.", name, interval));
return 1000;
}
if (interval % 50 != 0) {
int newInterval = interval - interval%50;
if (newInterval == 0) newInterval = 50;
startupWarn(String.format("Animation \"&e%s&c\" has refresh interval of %s which is not divisible by 50! &bUsing %s.", name, interval, newInterval));
if (interval % TabConstants.Placeholder.MINIMUM_REFRESH_INTERVAL != 0) {
int newInterval = interval - interval % TabConstants.Placeholder.MINIMUM_REFRESH_INTERVAL;
if (newInterval == 0) newInterval = TabConstants.Placeholder.MINIMUM_REFRESH_INTERVAL;
startupWarn(String.format("Animation \"&e%s&c\" has refresh interval of %s which is not divisible by " + TabConstants.Placeholder.MINIMUM_REFRESH_INTERVAL + "! &bUsing %s.", name, interval, newInterval));
return newInterval;
}
return interval;
@@ -119,15 +119,11 @@ public class FeatureManagerImpl implements FeatureManager {
long millis = System.currentTimeMillis();
TAB.getInstance().addPlayer(connectedPlayer);
for (TabFeature f : values) {
try {
if (!f.overridesMethod("onJoin")) continue;
long time = System.nanoTime();
f.onJoin(connectedPlayer);
TAB.getInstance().getCPUManager().addTime(f, TabConstants.CpuUsageCategory.PLAYER_JOIN, System.nanoTime()-time);
TAB.getInstance().debug("Feature " + f.getClass().getSimpleName() + " processed player join in " + (System.nanoTime()-time)/1000000 + "ms");
} catch (Throwable e) {
TAB.getInstance().getErrorManager().criticalError("Feature " + f.getClass().getSimpleName() + " failed to process player " + connectedPlayer.getName(), e);
}
if (!f.overridesMethod("onJoin")) continue;
long time = System.nanoTime();
f.onJoin(connectedPlayer);
TAB.getInstance().getCPUManager().addTime(f, TabConstants.CpuUsageCategory.PLAYER_JOIN, System.nanoTime()-time);
TAB.getInstance().debug("Feature " + f.getClass().getSimpleName() + " processed player join in " + (System.nanoTime()-time)/1000000 + "ms");
}
((ITabPlayer)connectedPlayer).markAsLoaded(true);
TAB.getInstance().debug("Player join of " + connectedPlayer.getName() + " processed in " + (System.currentTimeMillis()-millis) + "ms");
@@ -159,7 +155,8 @@ public class FeatureManagerImpl implements FeatureManager {
String from = changed.getServer();
((ITabPlayer)changed).setServer(to);
((ProxyTabPlayer)changed).sendJoinPluginMessage();
if (!isFeatureEnabled(TabConstants.Feature.PIPELINE_INJECTION)) onLoginPacket(changed);
if (!isFeatureEnabled(TabConstants.Feature.PIPELINE_INJECTION) || changed.getVersion().getMinorVersion() < 8)
onLoginPacket(changed);
for (TabFeature f : values) {
if (!f.overridesMethod("onServerChange")) continue;
long time = System.nanoTime();
@@ -9,9 +9,9 @@ import me.neznamy.tab.api.chat.IChatBaseComponent;
import me.neznamy.tab.api.placeholder.PlayerPlaceholder;
import me.neznamy.tab.api.protocol.*;
import me.neznamy.tab.api.protocol.PacketPlayOutChat.ChatMessageType;
import me.neznamy.tab.api.team.TeamManager;
import me.neznamy.tab.api.util.Preconditions;
import me.neznamy.tab.shared.event.impl.PlayerLoadEventImpl;
import me.neznamy.tab.shared.features.sorting.Sorting;
import org.geysermc.floodgate.api.FloodgateApi;
/**
@@ -47,12 +47,6 @@ public abstract class ITabPlayer implements TabPlayer {
/** Player's permission group override using API */
private String temporaryGroup;
/** Player's personal scoreboard team name */
private String teamName;
/** Note explaining the team name, used in debug command */
private String teamNameNote;
/** Player's game type, {@code true} for Bedrock, {@code false} for Java */
private final boolean bedrockPlayer;
@@ -72,10 +66,10 @@ public abstract class ITabPlayer implements TabPlayer {
private boolean onJoinFinished;
/** Scoreboard teams player has registered */
private final List<String> registeredTeams = new ArrayList<>();
private final Set<String> registeredTeams = new HashSet<>();
/** Scoreboard objectives player has registered */
private final List<String> registeredObjectives = new ArrayList<>();
private final Set<String> registeredObjectives = new HashSet<>();
/** Player's name as seen in GameProfile, can be altered by nick plugins */
private String nickname;
@@ -146,26 +140,6 @@ public abstract class ITabPlayer implements TabPlayer {
return setProperty(feature, identifier, rawValue, null, false);
}
/**
* Sets team name to given value
*
* @param name
* Team name to use
*/
public void setTeamName(String name) {
teamName = name;
}
/**
* Sets team name note to given value
*
* @param note
* New team name note
*/
public void setTeamNameNote(String note) {
teamNameNote = note;
}
/**
* Marks the player as loaded and calls PlayerLoadEvent
*
@@ -175,7 +149,6 @@ public abstract class ITabPlayer implements TabPlayer {
public void markAsLoaded(boolean join) {
onJoinFinished = true;
if (TAB.getInstance().getEventBus() != null) TAB.getInstance().getEventBus().fire(new PlayerLoadEventImpl(this, join));
TAB.getInstance().getPlatform().callLoadEvent(this);
}
/**
@@ -298,13 +271,20 @@ public abstract class ITabPlayer implements TabPlayer {
String team = ((PacketPlayOutScoreboardTeam) packet).getName();
int method = ((PacketPlayOutScoreboardTeam) packet).getAction();
if (method == 0) {
if (registeredTeams.contains(team)) {
if (!registeredTeams.add(team)) {
TAB.getInstance().getErrorManager().printError("Tried to register duplicated team " + team + " to player " + getName());
return;
}
registeredTeams.add(team);
} else if (method == 1) {
registeredTeams.remove(team);
if (!registeredTeams.remove(team)) {
TAB.getInstance().getErrorManager().printError("Tried to unregister non-existing team " + team + " for player " + getName());
return;
}
} else if (method == 2) {
if (!registeredTeams.contains(team)) {
TAB.getInstance().getErrorManager().printError("Tried to modify non-existing team " + team + " for player " + getName());
return;
}
}
}
//avoiding BungeeCord bug kicking all players
@@ -312,13 +292,20 @@ public abstract class ITabPlayer implements TabPlayer {
String objective = ((PacketPlayOutScoreboardObjective) packet).getObjectiveName();
int method = ((PacketPlayOutScoreboardObjective) packet).getAction();
if (method == 0) {
if (registeredObjectives.contains(objective)) {
if (!registeredObjectives.add(objective)) {
TAB.getInstance().getErrorManager().printError("Tried to register duplicated objective " + objective + " to player " + getName());
return;
}
registeredObjectives.add(objective);
} else if (method == 1) {
registeredObjectives.remove(objective);
if (!registeredObjectives.remove(objective)) {
TAB.getInstance().getErrorManager().printError("Tried to unregister non-existing objective " + objective + " for player " + getName());
return;
}
} else if (method == 2) {
if (!registeredObjectives.contains(objective)) {
TAB.getInstance().getErrorManager().printError("Tried to modify non-existing objective " + objective + " for player " + getName());
return;
}
}
}
//avoiding console spam from geyser
@@ -391,20 +378,15 @@ public abstract class ITabPlayer implements TabPlayer {
}
@Override
public String getTeamName() {
TeamManager teams = TAB.getInstance().getTeamManager();
if (teams != null && teams.getForcedTeamName(this) != null) return teams.getForcedTeamName(this);
return teamName;
public boolean isBedrockPlayer() {
return bedrockPlayer;
}
@Override
public String getTeamNameNote() {
return teamNameNote;
}
@Override
public boolean isBedrockPlayer() {
return bedrockPlayer;
public String getTeamName() {
Sorting sorting = (Sorting) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
if (sorting == null) return null;
return sorting.getShortTeamName(this);
}
@Override
@@ -1,6 +1,5 @@
package me.neznamy.tab.shared;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.protocol.PacketBuilder;
import me.neznamy.tab.shared.permission.PermissionPlugin;
@@ -25,22 +24,6 @@ public abstract class Platform {
this.packetBuilder = packetBuilder;
}
/**
* Calls platform-specific load event.
* This method is called when plugin is fully enabled.
*/
public void callLoadEvent(){
//do nothing by default, old event system will be removed
}
/**
* Calls platform-specific player load event.
* This method is called when player is fully loaded.
*/
public void callLoadEvent(TabPlayer player){
//do nothing by default, old event system will be removed
}
/**
* Returns platform-specific packet builder implementation
*
@@ -89,12 +72,4 @@ public abstract class Platform {
* @return Version string if plugin is installed, {@code null} if not
*/
public abstract String getPluginVersion(String plugin);
/**
* Returns name of default config file for this platform
* as it appears in the final jar in root directory.
*
* @return name of default config file for this platform
*/
public abstract String getConfigName();
}
@@ -114,7 +114,6 @@ public class TAB extends TabAPI {
this.serverVersionString = serverVersionString;
this.dataFolder = dataFolder;
this.logger = logger;
TabAPI.setInstance(this);
try {
Class.forName("org.geysermc.floodgate.api.FloodgateApi");
floodgate = true;
@@ -151,8 +150,8 @@ public class TAB extends TabAPI {
long time = System.currentTimeMillis();
errorManager = new ErrorManager();
cpu = new CpuManager();
featureManager = new FeatureManagerImpl();
configuration = new Configs();
featureManager = new FeatureManagerImpl();
featureManager.registerFeature(TabConstants.Feature.PLACEHOLDER_MANAGER, new PlaceholderManagerImpl());
featureManager.registerFeature(TabConstants.Feature.GROUP_MANAGER, new GroupManager(platform.detectPermissionPlugin()));
platform.loadFeatures();
@@ -161,7 +160,6 @@ public class TAB extends TabAPI {
for (TabPlayer p : players) ((ITabPlayer)p).markAsLoaded(false);
cpu.enable();
if (eventBus != null) eventBus.fire(TabLoadEventImpl.getInstance());
platform.callLoadEvent();
disabled = false;
sendConsoleMessage("&aEnabled in " + (System.currentTimeMillis()-time) + "ms", true);
return configuration.getMessages().getReloadSuccess();
@@ -233,7 +231,7 @@ public class TAB extends TabAPI {
if (serverVersion.getMinorVersion() >= 8 && configuration.getLayout().getBoolean("enabled", false)) {
if (getTeamManager() == null) {
//sorting is disabled, but layout needs team names
featureManager.registerFeature(TabConstants.Feature.SORTING, new Sorting(null));
featureManager.registerFeature(TabConstants.Feature.SORTING, new Sorting());
}
featureManager.registerFeature(TabConstants.Feature.LAYOUT, new LayoutManager());
}
@@ -7,11 +7,12 @@ import java.util.Map;
import me.neznamy.tab.api.TabFeature;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.api.chat.EnumChatFormat;
import me.neznamy.tab.api.config.ConfigurationFile;
import me.neznamy.tab.shared.DynamicText;
import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.shared.features.PlayerList;
import me.neznamy.tab.shared.features.nametags.NameTag;
import me.neznamy.tab.shared.features.sorting.Sorting;
/**
* Handler for "/tab debug" subcommand
@@ -59,6 +60,7 @@ public class DebugCommand extends SubCommand {
sendMessage(sender, "&6Permission plugin: &b" + TAB.getInstance().getGroupManager().getPlugin().getName());
sendMessage(sender, "&6Permission group choice logic: &b" + getGroupChoiceLogic());
sendMessage(sender, "&6Sorting system: &b" + getSortingType());
sendMessage(sender, "&6Storage type: &b" + (tab.getConfiguration().getGroups() instanceof ConfigurationFile ? "File" : "MySQL"));
sendMessage(sender, separator);
if (analyzed == null) return;
sendMessage(sender, "&ePlayer: &a" + analyzed.getName());
@@ -109,9 +111,9 @@ public class DebugCommand extends SubCommand {
* @return sorting type
*/
private String getSortingType() {
NameTag nametag = (NameTag) TAB.getInstance().getTeamManager();
if (nametag != null) {
return nametag.getSorting().typesToString();
Sorting sorting = (Sorting) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
if (sorting != null) {
return sorting.typesToString();
} else {
return "&cDISABLED";
}
@@ -139,17 +141,16 @@ public class DebugCommand extends SubCommand {
* @return team name of specified player
*/
private String getTeamName(TabPlayer analyzed) {
if (TAB.getInstance().getTeamManager() != null) {
if (((TabFeature) TAB.getInstance().getTeamManager()).isDisabled(analyzed.getServer(), analyzed.getWorld())) {
return "&eTeam name: &cSorting is disabled in player's world/server";
} else {
return "&eTeam name: &a" + analyzed.getTeamName();
}
Sorting sorting = (Sorting) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
if (sorting == null) return "";
if (TAB.getInstance().getTeamManager() != null &&
((TabFeature) TAB.getInstance().getTeamManager()).isDisabled(analyzed.getServer(), analyzed.getWorld())) {
return "&eTeam name: &cSorting is disabled in player's world/server";
}
return "";
return "&eTeam name: &a" + (TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.LAYOUT)
? sorting.getFullTeamName(analyzed) : sorting.getShortTeamName(analyzed));
}
/**
* Returns team name note of specified player
*
@@ -158,11 +159,13 @@ public class DebugCommand extends SubCommand {
* @return team name note of specified player
*/
private String getTeamNameNote(TabPlayer analyzed) {
Sorting sorting = (Sorting) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.SORTING);
if (sorting == null) return "";
if (TAB.getInstance().getTeamManager() != null &&
!((TabFeature) TAB.getInstance().getTeamManager()).isDisabled(analyzed.getServer(), analyzed.getWorld()) &&
analyzed.getTeamNameNote() != null)
return "&eTeam name note: &r" + analyzed.getTeamNameNote();
return "";
((TabFeature) TAB.getInstance().getTeamManager()).isDisabled(analyzed.getServer(), analyzed.getWorld())) {
return "";
}
return "&eSorting note: &r" + sorting.getTeamNameNote(analyzed);
}
/**
@@ -6,9 +6,7 @@ import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.api.TabConstants;
import me.neznamy.tab.shared.features.nametags.unlimited.NameTagX;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.*;
/**
* Handler for "/tab nametag" subcommand
@@ -24,16 +22,18 @@ public class NameTagCommand extends SubCommand {
@Override
public void execute(TabPlayer sender, String[] args) {
if (args.length != 1) {
if (args.length == 0 || args.length > 3) {
sendMessages(sender, getMessages().getNameTagHelpMenu());
return;
}
boolean silent = args.length >= 3 && args[2].equals("-s");
switch (args[0].toLowerCase(Locale.US)) {
case "preview":
preview(sender);
preview(sender,getTarget(sender, args, TabConstants.Permission.COMMAND_NAMETAG_PREVIEW_OTHER, TabConstants.Permission.COMMAND_NAMETAG_PREVIEW),silent);
break;
case "toggle":
toggle(sender);
toggle(sender,getTarget(sender, args, TabConstants.Permission.COMMAND_NAMETAG_TOGGLE_OTHER, TabConstants.Permission.COMMAND_NAMETAG_TOGGLE),silent);
break;
default:
sendMessages(sender, getMessages().getNameTagHelpMenu());
@@ -41,46 +41,54 @@ public class NameTagCommand extends SubCommand {
}
}
private void preview(TabPlayer sender) {
if (sender == null) {
sendMessage(null, getMessages().getCommandOnlyFromGame());
return;
}
if (!hasPermission(sender, TabConstants.Permission.COMMAND_NAMETAG_PREVIEW)) {
sendMessage(sender, getMessages().getNoPermission());
return;
}
private void preview(TabPlayer sender, TabPlayer target, boolean silent) {
if (target == null) return;
NameTagX nameTagX = (NameTagX) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS);
if (nameTagX == null) {
sendMessage(sender, getMessages().getUnlimitedNametagModeNotEnabled());
return;
}
if (nameTagX.hasDisabledArmorStands(sender)) {
if (nameTagX.hasDisabledArmorStands(target)) {
sendMessage(sender, getMessages().getArmorStandsDisabledCannotPreview());
return;
}
nameTagX.toggleNametagPreview(sender);
nameTagX.toggleNametagPreview(target, !silent);
}
private void toggle(TabPlayer sender) {
if (sender == null) {
sendMessage(null, getMessages().getCommandOnlyFromGame());
return;
}
if (!hasPermission(sender, TabConstants.Permission.COMMAND_NAMETAG_TOGGLE)) {
sendMessage(sender, getMessages().getNoPermission());
return;
}
private void toggle(TabPlayer sender, TabPlayer target, boolean silent) {
if (target == null) return;
TeamManager teams = TAB.getInstance().getTeamManager();
if (teams == null) {
sendMessage(sender, getMessages().getNameTagFeatureNotEnabled());
return;
}
teams.toggleNameTagVisibilityView(sender, true);
teams.toggleNameTagVisibilityView(target, !silent);
}
private TabPlayer getTarget(TabPlayer sender, String[] args, String permissionOther, String permission) {
if (args.length >= 2 && TAB.getInstance().getPlayer(args[1]) != null) {
if (hasPermission(sender, permissionOther)) {
return TAB.getInstance().getPlayer(args[1]);
} else {
sendMessage(sender, getMessages().getNoPermission());
}
} else {
if (hasPermission(sender, permission)) {
return sender;
} else {
sendMessage(sender, getMessages().getNoPermission());
}
}
return null;
}
@Override
public List<String> complete(TabPlayer sender, String[] arguments) {
return getStartingArgument(Arrays.asList("preview", "toggle"), arguments[0]);
if (arguments.length == 1) return getStartingArgument(Arrays.asList("toggle", "preview"), arguments[0]);
if (arguments.length == 2) return getOnlinePlayers(arguments[1]);
if (arguments.length == 3) return getStartingArgument(Collections.singletonList("-s"), arguments[2]);
return new ArrayList<>();
}
}
@@ -14,7 +14,7 @@ import me.neznamy.tab.shared.TAB;
import me.neznamy.tab.api.TabConstants;
/**
* Handler for "/tab parse <player> <placeholder>" subcommand
* Handler for "/tab parse &lt;player&gt; &lt;placeholder&gt;" subcommand
*/
public class ParseCommand extends SubCommand {
@@ -2,6 +2,7 @@ package me.neznamy.tab.shared.command;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import me.neznamy.tab.api.TabPlayer;
import me.neznamy.tab.shared.TAB;
@@ -93,6 +94,9 @@ public class PlayerCommand extends PropertyCommand {
if (property.length > 0 && String.valueOf(value.length() == 0 ? null : value).equals(String.valueOf(property[0]))) return;
TAB.getInstance().getConfiguration().getUsers().setProperty(player, type, server, world, value.length() == 0 ? null : value);
TabPlayer pl = TAB.getInstance().getPlayer(player);
try {
if (pl == null) pl = TAB.getInstance().getPlayer(UUID.fromString(player));
} catch (IllegalArgumentException ignored){} // not an uuid string
if (pl != null) {
pl.forceRefresh();
}
@@ -13,7 +13,7 @@ import me.neznamy.tab.shared.features.scoreboard.ScoreboardManagerImpl;
/**
* Handler for "/tab scoreboard [on/off/toggle] [player] [options]" subcommand
* and "/tab scoreboard show <name> [player]"
* and "/tab scoreboard show &lt;name&gt; [player]"
*/
public class ScoreboardCommand extends SubCommand {
@@ -21,13 +21,14 @@ import java.util.List;
public class Configs {
//config.yml file
private final ConfigurationFile config = new YamlConfigurationFile(getClass().getClassLoader().getResourceAsStream(TAB.getInstance().getPlatform().getConfigName()),
private final ConfigurationFile config = new YamlConfigurationFile(getClass().getClassLoader().getResourceAsStream("config.yml"),
new File(TAB.getInstance().getDataFolder(), "config.yml"));
private final boolean bukkitPermissions = TAB.getInstance().getServerVersion() == ProtocolVersion.PROXY && config.getBoolean("use-bukkit-permissions-manager", false);
private final boolean debugMode = config.getBoolean("debug", false);
private final boolean removeGhostPlayers = getSecretOption("remove-ghost-players", false);
private final boolean pipelineInjection = getSecretOption("pipeline-injection", true) && TAB.getInstance().getServerVersion().getMinorVersion() >= 8;
private final String serverName = getSecretOption("server-name", "N/A");
//animations.yml file
private final ConfigurationFile animation = new YamlConfigurationFile(getClass().getClassLoader().getResourceAsStream("animations.yml"),
@@ -64,12 +65,18 @@ public class Configs {
converter.convertAnimationFile(animation);
if (config.getBoolean("mysql.enabled", false)) {
try {
// Initialization to try to avoid java.sql.SQLException: No suitable driver found
try {
Class.forName("com.mysql.cj.jdbc.Driver");
} catch (ClassNotFoundException e) {
Class.forName("com.mysql.jdbc.Driver");
}
mysql = new MySQL(config.getString("mysql.host", "127.0.0.1"), config.getInt("mysql.port", 3306),
config.getString("mysql.database", "tab"), config.getString("mysql.username", "user"), config.getString("mysql.password", "password"));
groupFile = new MySQLGroupConfiguration(mysql);
userFile = new MySQLUserConfiguration(mysql);
return;
} catch (SQLException e) {
} catch (SQLException | ClassNotFoundException e) {
TAB.getInstance().getErrorManager().criticalError("Failed to connect to MySQL", e);
}
}
@@ -163,4 +170,8 @@ public class Configs {
public boolean isDebugMode() {
return debugMode;
}
public String getServerName() {
return serverName;
}
}
@@ -25,7 +25,7 @@ public class Converter {
* The method contains a check if the conversion is necessary, so it is always safe
* to call this method, even if conversion is not needed.
* <p>
* This change was made from 2.8.10 -> 2.9.0
* This change was made from 2.8.10 -&gt; 2.9.0
*
* @param animations
* animation file to convert
@@ -214,8 +214,8 @@ public class MessageFile extends YamlConfigurationFile {
public List<String> getNameTagHelpMenu() {
return getStringList("nametag-help-menu", Arrays.asList(
"/tab nametag preview - toggles armor stand preview mode",
"/tab nametag toggle - toggles nametags on all players for command sender"
"/tab nametag preview [player] - toggles armor stand preview mode",
"/tab nametag toggle [player] - toggles nametags on all players for command sender"
));
}
@@ -25,7 +25,7 @@ public final class EventBusImpl implements EventBus {
this.bus = new SimpleEventBus<TabEvent>(TabEvent.class) {
@Override
protected boolean shouldPost(@NonNull TabEvent event, @NonNull EventSubscriber subscriber) {
protected boolean shouldPost(@NonNull TabEvent event, @NonNull EventSubscriber<?> subscriber) {
return true;
}
};
Loaded 100 of 152 files, more files were not shown because too many files have changed in this diff. Show more