mirror of
https://github.com/NEZNAMY/TAB.git
synced 2026-11-13 12:42:06 -05:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
87e9a6defd | ||
|
|
3d896ff828 | ||
|
|
bf93240888 | ||
|
|
0b0e30c3c9 | ||
|
|
84d3816bd1 | ||
|
|
04ff606947 | ||
|
|
1cb8e6e238 | ||
|
|
4a2226170d | ||
|
|
1469124cfc | ||
|
|
0826095e73 | ||
|
|
1bd698adde | ||
|
|
1058d269ff | ||
|
|
bacd87b78e | ||
|
|
5befbe8780 | ||
|
|
e159b2adae | ||
|
|
52f7418853 | ||
|
|
76061f7ccd | ||
|
|
7d39e21eae | ||
|
|
fee3723474 | ||
|
|
2f0e4c6888 | ||
|
|
b04cb6d3c5 | ||
|
|
a9228a698a | ||
|
|
27102495d8 | ||
|
|
d1bf96ab33 | ||
|
|
3360dbb468 | ||
|
|
d224e7584e | ||
|
|
6373540076 | ||
|
|
5f78aca997 | ||
|
|
145d944d33 | ||
|
|
ac91940e3b | ||
|
|
ef8452be73 | ||
|
|
47e0810697 | ||
|
|
1f30f225c7 | ||
|
|
fcbfd60ddb | ||
|
|
46f9033713 | ||
|
|
7e65018a0e | ||
|
|
9ef3485968 | ||
|
|
257f8f1145 | ||
|
|
8d65cbb0e5 | ||
|
|
20d221e3ca | ||
|
|
94072acf10 | ||
|
|
0422cc2597 | ||
|
|
58f61e645b | ||
|
|
5f416bd9e5 | ||
|
|
64528c913c | ||
|
|
357e7c6a49 | ||
|
|
81dd202053 | ||
|
|
189797aa73 | ||
|
|
1ae2ef7cad | ||
|
|
de14e14d33 | ||
|
|
19892c14c1 | ||
|
|
2e00c4c002 | ||
|
|
97aa367629 | ||
|
|
87cc9a030b | ||
|
|
8ba83d1cb1 | ||
|
|
9f0e60bd47 | ||
|
|
93b2a25b2e | ||
|
|
4242d7a839 | ||
|
|
0ddb99bd46 | ||
|
|
969672e3ad | ||
|
|
2423fbb150 | ||
|
|
a99928dbf5 | ||
|
|
12f5dd52b4 | ||
|
|
2bd105446e | ||
|
|
6d85875fde | ||
|
|
42810aa490 | ||
|
|
bd31521bd3 | ||
|
|
ec0deb8378 | ||
|
|
ae66b6e9a1 | ||
|
|
3b86becebd | ||
|
|
4a8d86825b | ||
|
|
54a3bd81b4 | ||
|
|
b6768cca99 | ||
|
|
60c414deed | ||
|
|
009283a83f | ||
|
|
4a430b97df | ||
|
|
722333da2e | ||
|
|
1215af5262 | ||
|
|
254088c37b | ||
|
|
cb2ce4acca | ||
|
|
872adca8fd | ||
|
|
f37cc0bdd8 | ||
|
|
4063578ac3 | ||
|
|
2528de7eeb | ||
|
|
37caed3123 | ||
|
|
27fe9dfb12 | ||
|
|
0169cbd4d2 | ||
|
|
ed760aec08 | ||
|
|
858cbcefeb | ||
|
|
8a87d581a9 | ||
|
|
6d642f4e51 | ||
|
|
aca63640bc | ||
|
|
1845a35315 | ||
|
|
cab5f89a95 | ||
|
|
1777d8bf01 | ||
|
|
91b0bfaa9d | ||
|
|
f1cf994d10 | ||
|
|
69957be933 | ||
|
|
b78eb5efb6 | ||
|
|
3577ad1964 | ||
|
|
4510491177 | ||
|
|
01b3a12377 | ||
|
|
64ae9863fb | ||
|
|
ecd315a407 | ||
|
|
678eed288e | ||
|
|
9510455cb1 | ||
|
|
e925ce23d0 | ||
|
|
c68cad6a1e | ||
|
|
01d6eda4fb | ||
|
|
73e7119bbc | ||
|
|
cea781382a | ||
|
|
cf25b84e86 | ||
|
|
86e1d22390 | ||
|
|
1625be111f | ||
|
|
00cda3b2aa | ||
|
|
85a00a4436 | ||
|
|
37aec1a497 | ||
|
|
ef637616ee | ||
|
|
af3b5067fb | ||
|
|
53e5b15f96 | ||
|
|
6a3afae607 | ||
|
|
2e05c7d9d1 | ||
|
|
fb698a04b7 | ||
|
|
52bdf30ddb | ||
|
|
a92f07aa40 | ||
|
|
003ff7f99d | ||
|
|
47b9806ae7 | ||
|
|
d2a7c65e08 | ||
|
|
e232336a5f | ||
|
|
8816c75212 | ||
|
|
f9d54751ba | ||
|
|
43d41ee761 | ||
|
|
4512ba8d5e | ||
|
|
8b55bca564 | ||
|
|
d8d54970d3 | ||
|
|
15ea4482f9 | ||
|
|
87f9f38eac | ||
|
|
dba559f041 | ||
|
|
c610b6f323 | ||
|
|
a5fb900e19 | ||
|
|
09d6e455f5 | ||
|
|
e68097b0f4 | ||
|
|
0744fed953 | ||
|
|
de0adf4067 | ||
|
|
76bf2b3efc | ||
|
|
c089badeb4 | ||
|
|
b3b99d366a | ||
|
|
4a36486f65 | ||
|
|
75126d00d9 | ||
|
|
17ebf95d5b | ||
|
|
c764420cfe | ||
|
|
28f05fc2af | ||
|
|
a420892887 | ||
|
|
d910dc5913 | ||
|
|
c9c1cbc349 | ||
|
|
fd4704f75e | ||
|
|
5400ca637f | ||
|
|
79d2610117 | ||
|
|
6a51f4cb9d | ||
|
|
c190776665 | ||
|
|
3765a1232e | ||
|
|
28866eba21 | ||
|
|
08adee3dc3 | ||
|
|
8a1adaf2cd | ||
|
|
fdb9accaa6 | ||
|
|
4f7656748b | ||
|
|
03e4495ac7 | ||
|
|
01af5cb9e1 | ||
|
|
8d49f32df2 | ||
|
|
8a955b3bc8 | ||
|
|
ead40cb413 | ||
|
|
5e07f78d4f | ||
|
|
c7d98795bc | ||
|
|
9f637e3d6a | ||
|
|
2d5f074c78 | ||
|
|
3be6ba484d | ||
|
|
d9d74e7a32 | ||
|
|
887306a5ed | ||
|
|
4b2542eb4f | ||
|
|
66bd3baa3e | ||
|
|
bf32004f8f | ||
|
|
bfb2df9c95 | ||
|
|
ddeb05e408 | ||
|
|
9312d61258 | ||
|
|
d44909bc42 | ||
|
|
1d77df335c | ||
|
|
a268dc25f4 | ||
|
|
8c5696faa0 | ||
|
|
94f3ccfb7d | ||
|
|
576329214d | ||
|
|
5c13c2e225 | ||
|
|
1070f85617 | ||
|
|
7c9dc87fcb | ||
|
|
dc09e77190 | ||
|
|
d5b0ff7f40 | ||
|
|
e4f33b58f7 |
No files matched your search
@@ -1,6 +1,6 @@
|
||||
name: Bug Report
|
||||
description: Unwanted behavior that does not match configuration.
|
||||
labels: ["Bug"]
|
||||
description: Plugin does not work as described.
|
||||
labels: ["Type: Bug"]
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
@@ -45,7 +45,7 @@ body:
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional info
|
||||
description: Any other info you want to provide, such as screenshots.
|
||||
description: Anything else you want to provide that will help in resolving the issue faster, such as screenshots or videos.
|
||||
validations:
|
||||
required: false
|
||||
- type: checkboxes
|
||||
@@ -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
|
||||
@@ -1,6 +1,6 @@
|
||||
name: Error
|
||||
description: Error message with a stack trace in console or error file.
|
||||
labels: ["Error"]
|
||||
labels: ["Type: Error"]
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
@@ -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
|
||||
@@ -1,6 +1,6 @@
|
||||
name: Wiki change
|
||||
description: Change to text on a wiki page or adding a new section.
|
||||
labels: ["Wiki"]
|
||||
labels: ["Type: Wiki change"]
|
||||
body:
|
||||
- type: input
|
||||
attributes:
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,10 @@
|
||||
If this plugin has helped you in any way, and you would like to return something back
|
||||
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) 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://builtbybit.com/resources/14009/) of the plugin or [send a donation](https://paypal.me/neznamy1) to keep the plugin active.
|
||||
@@ -0,0 +1,174 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
@@ -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,15 +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
|
||||
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.
|
||||
* 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.
|
||||
See [CONTRIBUTING.md](https://github.com/NEZNAMY/TAB/blob/master/CONTRIBUTING.md)
|
||||
@@ -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 {
|
||||
|
||||
/**
|
||||
@@ -7,7 +11,7 @@ public interface ArmorStand {
|
||||
*
|
||||
* @return true if static, false if dynamic
|
||||
*/
|
||||
boolean hasStaticOffset();
|
||||
boolean isStaticOffset();
|
||||
|
||||
/**
|
||||
* Changes offset of the armor stand
|
||||
@@ -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);
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package me.neznamy.tab.api;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* Enum class representing all possibly used protocol versions
|
||||
*/
|
||||
@@ -8,6 +11,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),
|
||||
@@ -68,13 +73,16 @@ public enum ProtocolVersion {
|
||||
public static final ProtocolVersion[] VALUES = values();
|
||||
|
||||
/** Version's network id found at https://wiki.vg/Protocol_version_numbers */
|
||||
private final int networkId;
|
||||
@Getter private final int networkId;
|
||||
|
||||
/** Version's minor version, such as 16 for 1.16.x */
|
||||
private int minorVersion;
|
||||
/** Version's minor version, such as 16 for 1.16.x. Allowing override to
|
||||
* set minor version of UNKNOWN_SERVER_VERSION value to value from package to fix compatibility
|
||||
* with server forks that set bukkit version field value to "Unknown".
|
||||
*/
|
||||
@Getter @Setter private int minorVersion;
|
||||
|
||||
/** Version's friendly name displayed in %player-version% placeholder */
|
||||
private final String friendlyName;
|
||||
@Getter private final String friendlyName;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given network id
|
||||
@@ -100,45 +108,6 @@ public enum ProtocolVersion {
|
||||
this.friendlyName = friendlyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the version's network id
|
||||
*
|
||||
* @return version's network id
|
||||
*/
|
||||
public int getNetworkId() {
|
||||
return networkId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns user-friendly name of the version (such as 1.16.2 instead of V1_16_2)
|
||||
*
|
||||
* @return user-friendly name of the version
|
||||
*/
|
||||
public String getFriendlyName() {
|
||||
return friendlyName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns minor version of this release, such as 8 for 1.8.x or 13 for 1.13.x
|
||||
*
|
||||
* @return version's minor version
|
||||
*/
|
||||
public int getMinorVersion() {
|
||||
return minorVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Overrides minor version of the version instance. This is used internally to
|
||||
* set minor version of UNKNOWN_SERVER_VERSION value to value from package to fix compatibility
|
||||
* with server forks that set bukkit version field value to "Unknown".
|
||||
*
|
||||
* @param minorVersion
|
||||
* minor version to change value to
|
||||
*/
|
||||
public void setMinorVersion(int minorVersion) {
|
||||
this.minorVersion = minorVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns enum constant of entered version or UNKNOWN_SERVER_VERSION if unknown version
|
||||
*
|
||||
|
||||
@@ -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";
|
||||
@@ -238,6 +244,7 @@ public class TabConstants {
|
||||
public static final String UNLIMITED_NAME_TAGS = "NameTagX";
|
||||
public static final String UNLIMITED_NAME_TAGS_PACKET_LISTENER = "nametagx-packet";
|
||||
public static final String UNLIMITED_NAME_TAGS_VEHICLE_REFRESHER = "nametagx-vehicle";
|
||||
public static final String UNLIMITED_NAME_TAGS_GAMEMODE_LISTENER = "nametagx-gamemode";
|
||||
|
||||
//Bukkit only
|
||||
public static final String PER_WORLD_PLAYER_LIST = "PerWorldPlayerList";
|
||||
@@ -329,6 +336,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 +346,10 @@ public class TabConstants {
|
||||
public static String animation(String name) {
|
||||
return "%animation:" + name + "%";
|
||||
}
|
||||
|
||||
public static String globalPlayerListGroup(String group) {
|
||||
return "%playerlist-group_" + group + "%";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,6 +2,7 @@ package me.neznamy.tab.api;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutPlayerInfo;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardDisplayObjective;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardObjective;
|
||||
@@ -14,10 +15,10 @@ import me.neznamy.tab.api.protocol.PacketPlayOutScoreboardObjective;
|
||||
public abstract class TabFeature {
|
||||
|
||||
/** Feature's name displayed in /tab cpu */
|
||||
private final String featureName;
|
||||
@Getter private final String featureName;
|
||||
|
||||
/** Feature's function name displayed in place of refreshing in /tab cpu */
|
||||
private final String refreshDisplayName;
|
||||
@Getter private final String refreshDisplayName;
|
||||
|
||||
/** Servers where the feature is disabled (or enabled if using whitelist mode) */
|
||||
protected String[] disabledServers = new String[0];
|
||||
@@ -340,24 +341,6 @@ public abstract class TabFeature {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of the feature displayed in /tab cpu.
|
||||
*
|
||||
* @return name of the feature displayed in /tab cpu
|
||||
*/
|
||||
public String getFeatureName() {
|
||||
return featureName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns text to display in /tab cpu of this feature refreshing visuals
|
||||
*
|
||||
* @return Text to display as refreshing of this feature
|
||||
*/
|
||||
public String getRefreshDisplayName() {
|
||||
return refreshDisplayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@code true} if method with specified name is overridden
|
||||
* in the implementation, {@code false} if not
|
||||
|
||||
@@ -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
|
||||
*
|
||||
@@ -310,12 +303,4 @@ public interface TabPlayer {
|
||||
* @return player's game profile name as seen by other players
|
||||
*/
|
||||
String getNickname();
|
||||
|
||||
/**
|
||||
* Returns player's chat message signing key. Returned object is a direct
|
||||
* object from the server, which is different per platform.
|
||||
*
|
||||
* @return Player's direct chat message signing key
|
||||
*/
|
||||
Object getProfilePublicKey();
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
package me.neznamy.tab.api.bossbar;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
|
||||
/**
|
||||
* An enum representing all available BossBar styles.
|
||||
* Calling ordinal() will return style's network ID.
|
||||
* Style names are equal to NMS names.
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public enum BarStyle {
|
||||
|
||||
PROGRESS("SOLID"),
|
||||
@@ -14,23 +18,5 @@ public enum BarStyle {
|
||||
NOTCHED_20("SEGMENTED_20");
|
||||
|
||||
/** Style's name used in Bukkit API */
|
||||
private final String bukkitName;
|
||||
|
||||
/**
|
||||
* Initializes enum constant with given bukkit name
|
||||
*
|
||||
* @param bukkitName
|
||||
* Name of color to be returned in {@code getBukkitName()}
|
||||
*/
|
||||
BarStyle(String bukkitName){
|
||||
this.bukkitName = bukkitName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns name of this style in Bukkit API
|
||||
* @return name of this style in Bukkit API
|
||||
*/
|
||||
public String getBukkitName() {
|
||||
return bukkitName;
|
||||
}
|
||||
@Getter private final String bukkitName;
|
||||
}
|
||||
@@ -1,52 +1,19 @@
|
||||
package me.neznamy.tab.api.chat;
|
||||
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
|
||||
/**
|
||||
* Class for click event action in chat component
|
||||
*/
|
||||
@Data
|
||||
public class ChatClickable {
|
||||
|
||||
/** Click action */
|
||||
private final EnumClickAction action;
|
||||
@NonNull private final EnumClickAction action;
|
||||
|
||||
/** Click value */
|
||||
private final String value;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given action and value.
|
||||
*
|
||||
* @param action
|
||||
* click event action
|
||||
* @param value
|
||||
* click event value
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code action} is null or {@code value} is null
|
||||
*/
|
||||
public ChatClickable(EnumClickAction action, String value) {
|
||||
Preconditions.checkNotNull(action, "hover action");
|
||||
Preconditions.checkNotNull(value, "hover value");
|
||||
this.action = action;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns click action defined in constructor
|
||||
*
|
||||
* @return click action
|
||||
*/
|
||||
public EnumClickAction getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns click action defined in constructor
|
||||
*
|
||||
* @return click action
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
@NonNull private final String value;
|
||||
|
||||
/**
|
||||
* Enum for all possible click actions
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
package me.neznamy.tab.api.chat;
|
||||
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.google.gson.JsonObject;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
public class ChatComponentEntity extends IChatBaseComponent {
|
||||
|
||||
private final String type;
|
||||
private final UUID id;
|
||||
private final String name;
|
||||
|
||||
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;
|
||||
private final String CONTENTS;
|
||||
private final String VALUE_1_13;
|
||||
private final String VALUE_1_12;
|
||||
private final String VALUE_1_8;
|
||||
|
||||
public ChatComponentEntity(@NonNull String type, @NonNull UUID id, String 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 +26,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");
|
||||
}
|
||||
}
|
||||
@@ -1,60 +1,32 @@
|
||||
package me.neznamy.tab.api.chat;
|
||||
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
|
||||
/**
|
||||
* Class for hover event action in chat component
|
||||
*/
|
||||
@Data
|
||||
public class ChatHoverable {
|
||||
|
||||
/** Hover action */
|
||||
private final EnumHoverAction action;
|
||||
@NonNull private final EnumHoverAction action;
|
||||
|
||||
/** Hover value */
|
||||
private final IChatBaseComponent value;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given action and value.
|
||||
*
|
||||
* @param action
|
||||
* hover event action
|
||||
* @param value
|
||||
* hover event value
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code action} is null or {@code value} is null
|
||||
*/
|
||||
public ChatHoverable(EnumHoverAction action, IChatBaseComponent value) {
|
||||
Preconditions.checkNotNull(action, "hover action");
|
||||
Preconditions.checkNotNull(value, "hover value");
|
||||
this.action = action;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns hover action defined in constructor
|
||||
*
|
||||
* @return hover action
|
||||
*/
|
||||
public EnumHoverAction getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns hover value defined in constructor
|
||||
*
|
||||
* @return hover value
|
||||
*/
|
||||
public IChatBaseComponent getValue() {
|
||||
return value;
|
||||
}
|
||||
@NonNull private final IChatBaseComponent value;
|
||||
|
||||
/**
|
||||
* Enum for all possible hover actions
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public enum EnumHoverAction {
|
||||
|
||||
SHOW_TEXT,
|
||||
SHOW_ITEM,
|
||||
SHOW_ENTITY
|
||||
SHOW_TEXT("contents"),
|
||||
SHOW_ITEM("value"),
|
||||
SHOW_ENTITY("contents");
|
||||
|
||||
@Getter private final String preferredKey;
|
||||
}
|
||||
}
|
||||
@@ -1,37 +1,31 @@
|
||||
package me.neznamy.tab.api.chat;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.chat.ChatClickable.EnumClickAction;
|
||||
import me.neznamy.tab.api.chat.ChatHoverable.EnumHoverAction;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Data @NoArgsConstructor
|
||||
public class ChatModifier {
|
||||
|
||||
private TextColor color;
|
||||
|
||||
private Boolean bold;
|
||||
private Boolean italic;
|
||||
private Boolean underlined;
|
||||
private Boolean strikethrough;
|
||||
private Boolean obfuscated;
|
||||
|
||||
private ChatClickable clickEvent;
|
||||
private ChatHoverable hoverEvent;
|
||||
|
||||
private String font;
|
||||
|
||||
private ProtocolVersion targetVersion;
|
||||
|
||||
public ChatModifier() {
|
||||
}
|
||||
|
||||
public ChatModifier(ChatModifier modifier) {
|
||||
Preconditions.checkNotNull(modifier, "modifier");
|
||||
public ChatModifier(@NonNull ChatModifier modifier) {
|
||||
this.color = modifier.color == null ? null : new TextColor(modifier.color);
|
||||
this.bold = modifier.bold;
|
||||
this.italic = modifier.italic;
|
||||
@@ -44,53 +38,6 @@ public class ChatModifier {
|
||||
this.targetVersion = modifier.targetVersion;
|
||||
}
|
||||
|
||||
public TextColor getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
public void setColor(TextColor color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public Boolean getBold() {
|
||||
return bold;
|
||||
}
|
||||
|
||||
public void setBold(Boolean bold) {
|
||||
this.bold = bold;
|
||||
}
|
||||
|
||||
public Boolean getItalic() {
|
||||
return italic;
|
||||
}
|
||||
|
||||
public void setItalic(Boolean italic) {
|
||||
this.italic = italic;
|
||||
}
|
||||
|
||||
public Boolean getUnderlined() {
|
||||
return underlined;
|
||||
}
|
||||
|
||||
public void setUnderlined(Boolean underlined) {
|
||||
this.underlined = underlined;
|
||||
}
|
||||
|
||||
public Boolean getStrikethrough() {
|
||||
return strikethrough;
|
||||
}
|
||||
|
||||
public void setStrikethrough(Boolean strikethrough) {
|
||||
this.strikethrough = strikethrough;
|
||||
}
|
||||
|
||||
public Boolean getObfuscated() {
|
||||
return obfuscated;
|
||||
}
|
||||
|
||||
public void setObfuscated(Boolean obfuscated) {
|
||||
this.obfuscated = obfuscated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if bold is defined and set to true, false otherwise
|
||||
@@ -137,30 +84,13 @@ public class ChatModifier {
|
||||
return Boolean.TRUE.equals(obfuscated);
|
||||
}
|
||||
|
||||
public ChatClickable getClickEvent() {
|
||||
return clickEvent;
|
||||
}
|
||||
|
||||
public void setClickEvent(ChatClickable clickEvent) {
|
||||
this.clickEvent = clickEvent;
|
||||
}
|
||||
|
||||
public ChatHoverable getHoverEvent() {
|
||||
return hoverEvent;
|
||||
}
|
||||
|
||||
public void setHoverEvent(ChatHoverable hoverEvent) {
|
||||
this.hoverEvent = hoverEvent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets click action to OPEN_URL and url to given value
|
||||
*
|
||||
* @param url
|
||||
* url to open
|
||||
*/
|
||||
public void onClickOpenUrl(String url) {
|
||||
Preconditions.checkNotNull(url, "url");
|
||||
public void onClickOpenUrl(@NonNull String url) {
|
||||
clickEvent = new ChatClickable(EnumClickAction.OPEN_URL, url);
|
||||
}
|
||||
|
||||
@@ -170,8 +100,7 @@ public class ChatModifier {
|
||||
* @param command
|
||||
* command to perform, might be without / to send a chat message
|
||||
*/
|
||||
public void onClickRunCommand(String command) {
|
||||
Preconditions.checkNotNull(command, "command");
|
||||
public void onClickRunCommand(@NonNull String command) {
|
||||
clickEvent = new ChatClickable(EnumClickAction.RUN_COMMAND, command);
|
||||
}
|
||||
|
||||
@@ -181,8 +110,7 @@ public class ChatModifier {
|
||||
* @param command
|
||||
* command to suggest
|
||||
*/
|
||||
public void onClickSuggestCommand(String command) {
|
||||
Preconditions.checkNotNull(command, "command");
|
||||
public void onClickSuggestCommand(@NonNull String command) {
|
||||
clickEvent = new ChatClickable(EnumClickAction.SUGGEST_COMMAND, command);
|
||||
}
|
||||
|
||||
@@ -203,14 +131,11 @@ public class ChatModifier {
|
||||
* @param text
|
||||
* text to copy to clipboard on click
|
||||
*/
|
||||
public void onClickCopyToClipBoard(String text) {
|
||||
Preconditions.checkNotNull(text, "text");
|
||||
public void onClickCopyToClipBoard(@NonNull String text) {
|
||||
clickEvent = new ChatClickable(EnumClickAction.COPY_TO_CLIPBOARD, text);
|
||||
}
|
||||
|
||||
public void onClick(EnumClickAction action, String value) {
|
||||
Preconditions.checkNotNull(action, "action");
|
||||
Preconditions.checkNotNull(value, "value");
|
||||
public void onClick(@NonNull EnumClickAction action, @NonNull String value) {
|
||||
clickEvent = new ChatClickable(action, value);
|
||||
}
|
||||
|
||||
@@ -220,8 +145,7 @@ public class ChatModifier {
|
||||
* @param text
|
||||
* text to show
|
||||
*/
|
||||
public void onHoverShowText(IChatBaseComponent text) {
|
||||
Preconditions.checkNotNull(text, "text");
|
||||
public void onHoverShowText(@NonNull IChatBaseComponent text) {
|
||||
hoverEvent = new ChatHoverable(EnumHoverAction.SHOW_TEXT, text);
|
||||
}
|
||||
|
||||
@@ -231,8 +155,7 @@ public class ChatModifier {
|
||||
* @param serializedItem
|
||||
* item to show
|
||||
*/
|
||||
public void onHoverShowItem(String serializedItem) {
|
||||
Preconditions.checkNotNull(serializedItem, "item");
|
||||
public void onHoverShowItem(@NonNull String serializedItem) {
|
||||
hoverEvent = new ChatHoverable(EnumHoverAction.SHOW_ITEM, new IChatBaseComponent(serializedItem));
|
||||
}
|
||||
|
||||
@@ -246,36 +169,14 @@ public class ChatModifier {
|
||||
* @param name
|
||||
* entity custom name
|
||||
*/
|
||||
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");
|
||||
}
|
||||
public void onHoverShowEntity(@NonNull String type, @NonNull UUID id, String name) {
|
||||
hoverEvent = new ChatHoverable(EnumHoverAction.SHOW_ENTITY, new ChatComponentEntity(type, id, name));
|
||||
}
|
||||
|
||||
public void onHover(EnumHoverAction action, IChatBaseComponent value) {
|
||||
Preconditions.checkNotNull(action, "action");
|
||||
Preconditions.checkNotNull(value, "value");
|
||||
public void onHover(@NonNull EnumHoverAction action, @NonNull IChatBaseComponent value) {
|
||||
hoverEvent = new ChatHoverable(action, value);
|
||||
}
|
||||
|
||||
public String getFont() {
|
||||
return font;
|
||||
}
|
||||
|
||||
public void setFont(String font) {
|
||||
this.font = font;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public JSONObject serialize() {
|
||||
JSONObject json = new JSONObject();
|
||||
@@ -295,7 +196,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());
|
||||
@@ -315,12 +216,4 @@ public class ChatModifier {
|
||||
if (isObfuscated()) builder.append(EnumChatFormat.OBFUSCATED.getFormat());
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public void setTargetVersion(ProtocolVersion targetVersion) {
|
||||
this.targetVersion = targetVersion;
|
||||
}
|
||||
|
||||
public ProtocolVersion getTargetVersion() {
|
||||
return targetVersion;
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
package me.neznamy.tab.api.chat;
|
||||
|
||||
import lombok.NonNull;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.parser.JSONParser;
|
||||
import org.json.simple.parser.ParseException;
|
||||
@@ -23,10 +25,11 @@ import java.util.List;
|
||||
* will act like a normal chat component.
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@RequiredArgsConstructor
|
||||
public class DeserializedChatComponent extends IChatBaseComponent {
|
||||
|
||||
/** The original serialized component string */
|
||||
private final String json;
|
||||
@NonNull private final String json;
|
||||
|
||||
/**
|
||||
* Flag tracking whether this component was fully deserialized
|
||||
@@ -41,17 +44,6 @@ public class DeserializedChatComponent extends IChatBaseComponent {
|
||||
*/
|
||||
private boolean modified;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
*
|
||||
* @param json
|
||||
* serialized component as string
|
||||
*/
|
||||
public DeserializedChatComponent(String json) {
|
||||
Preconditions.checkNotNull(json, "json");
|
||||
this.json = json;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (modified) return super.toString();
|
||||
@@ -77,7 +69,7 @@ public class DeserializedChatComponent extends IChatBaseComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatModifier getModifier() {
|
||||
public @NotNull ChatModifier getModifier() {
|
||||
if (!deserialized) deserialize();
|
||||
return super.getModifier();
|
||||
}
|
||||
@@ -90,14 +82,14 @@ public class DeserializedChatComponent extends IChatBaseComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addExtra(IChatBaseComponent child) {
|
||||
public void addExtra(@NotNull IChatBaseComponent child) {
|
||||
if (!deserialized) deserialize();
|
||||
modified = true;
|
||||
super.addExtra(child);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setModifier(ChatModifier modifier) {
|
||||
public void setModifier(@NotNull ChatModifier modifier) {
|
||||
if (!deserialized) deserialize();
|
||||
modified = true;
|
||||
super.setModifier(modifier);
|
||||
@@ -162,9 +154,7 @@ public class DeserializedChatComponent extends IChatBaseComponent {
|
||||
* name of key
|
||||
* @return value from json object or null if not present
|
||||
*/
|
||||
private static Boolean getBoolean(JSONObject jsonObject, String key) {
|
||||
Preconditions.checkNotNull(jsonObject, "json object");
|
||||
Preconditions.checkNotNull(key, "key");
|
||||
private static Boolean getBoolean(@NonNull JSONObject jsonObject, @NonNull String key) {
|
||||
String value = String.valueOf(jsonObject.getOrDefault(key, null));
|
||||
return "null".equals(value) ? null : Boolean.parseBoolean(value);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.neznamy.tab.api.chat;
|
||||
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.api.chat.rgb.RGBUtils;
|
||||
|
||||
/**
|
||||
@@ -40,22 +41,22 @@ public enum EnumChatFormat {
|
||||
public static final String COLOR_STRING = String.valueOf(COLOR_CHAR);
|
||||
|
||||
/** Character representing the color or magic code */
|
||||
private final char character;
|
||||
@Getter private final char character;
|
||||
|
||||
/** Red value of this constant, 0 for magic codes */
|
||||
private final short red;
|
||||
@Getter private final short red;
|
||||
|
||||
/** Green value of this constant, 0 for magic codes */
|
||||
private final short green;
|
||||
@Getter private final short green;
|
||||
|
||||
/** Blue value of this constant, 0 for magic codes */
|
||||
private final short blue;
|
||||
@Getter private final short blue;
|
||||
|
||||
/** Color as 6-digit hex code, null for magic codes */
|
||||
private final String hexCode;
|
||||
@Getter private final String hexCode;
|
||||
|
||||
/** Color symbol followed by constant's character */
|
||||
private final String chatFormat;
|
||||
@Getter private final String format;
|
||||
|
||||
/**
|
||||
* Constructs new color instance with given character and hex code
|
||||
@@ -67,7 +68,7 @@ public enum EnumChatFormat {
|
||||
*/
|
||||
EnumChatFormat(char character, String hexCode) {
|
||||
this.character = character;
|
||||
this.chatFormat = String.valueOf(COLOR_CHAR) + character;
|
||||
this.format = String.valueOf(COLOR_CHAR) + character;
|
||||
this.hexCode = hexCode;
|
||||
int hexColor = Integer.parseInt(hexCode, 16);
|
||||
red = (short) ((hexColor >> 16) & 0xFF);
|
||||
@@ -83,40 +84,13 @@ public enum EnumChatFormat {
|
||||
*/
|
||||
EnumChatFormat(char character) {
|
||||
this.character = character;
|
||||
this.chatFormat = String.valueOf(COLOR_CHAR) + character;
|
||||
this.format = String.valueOf(COLOR_CHAR) + character;
|
||||
red = 0;
|
||||
green = 0;
|
||||
blue = 0;
|
||||
hexCode = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns red value of this color code
|
||||
*
|
||||
* @return red value
|
||||
*/
|
||||
public short getRed() {
|
||||
return red;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns green value of this color code
|
||||
*
|
||||
* @return green value
|
||||
*/
|
||||
public short getGreen() {
|
||||
return green;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns blue value of this color code
|
||||
*
|
||||
* @return blue value
|
||||
*/
|
||||
public short getBlue() {
|
||||
return blue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns enum value based on provided character or null if character is not valid
|
||||
*
|
||||
@@ -152,33 +126,6 @@ public enum EnumChatFormat {
|
||||
return EnumChatFormat.WHITE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns color char followed by color's character
|
||||
*
|
||||
* @return color char followed by color's character
|
||||
*/
|
||||
public String getFormat() {
|
||||
return chatFormat;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns character representing this color
|
||||
*
|
||||
* @return character representing this color
|
||||
*/
|
||||
public char getCharacter() {
|
||||
return character;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns hex code of this format, null if this is a magic code
|
||||
*
|
||||
* @return hex code of this format
|
||||
*/
|
||||
public String getHexCode() {
|
||||
return hexCode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns enum value with exact red, green and blue values or null if no match was found
|
||||
*
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package me.neznamy.tab.api.chat;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import lombok.Setter;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.chat.rgb.RGBUtils;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import me.neznamy.tab.api.util.ComponentCache;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import java.util.*;
|
||||
@@ -12,39 +16,37 @@ import java.util.*;
|
||||
* A class representing the n.m.s.IChatBaseComponent class to make work with it much easier
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
@NoArgsConstructor
|
||||
public class IChatBaseComponent {
|
||||
|
||||
/**
|
||||
* Component cache maps to avoid large memory allocations as well as
|
||||
* higher CPU usage when using animations which send the same text on repeat.
|
||||
*/
|
||||
private static final Map<String, IChatBaseComponent> componentCache = new HashMap<>();
|
||||
private static final Map<IChatBaseComponent, String> serializeCacheModern = new HashMap<>();
|
||||
private static final Map<IChatBaseComponent, String> serializeCacheLegacy = new HashMap<>();
|
||||
private static final ComponentCache<String, IChatBaseComponent> stringCache = new ComponentCache<>(10000, (text, clientVersion) -> {
|
||||
return text.contains("#") || text.contains("&x") || text.contains(EnumChatFormat.COLOR_CHAR + "x") || text.contains("<") ?
|
||||
IChatBaseComponent.fromColoredText(text) : //contains RGB colors
|
||||
new IChatBaseComponent(text); //no RGB
|
||||
});
|
||||
private static final ComponentCache<IChatBaseComponent, String> serializeCache = new ComponentCache<>(10000,
|
||||
(component, clientVersion) -> component.toString());
|
||||
|
||||
/** Text of the component */
|
||||
private String text;
|
||||
@Getter @Setter private String text;
|
||||
|
||||
/** Chat modifier containing color, magic codes, hover and click event */
|
||||
private ChatModifier modifier = new ChatModifier();
|
||||
@Getter @Setter @NonNull private ChatModifier modifier = new ChatModifier();
|
||||
|
||||
/** Extra components used in "extra" field */
|
||||
private List<IChatBaseComponent> extra;
|
||||
|
||||
/**
|
||||
* Constructs a new empty component
|
||||
*/
|
||||
public IChatBaseComponent() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new component which is a clone of provided component
|
||||
*
|
||||
* @param component
|
||||
* component to clone
|
||||
*/
|
||||
public IChatBaseComponent(IChatBaseComponent component) {
|
||||
Preconditions.checkNotNull(component, "component");
|
||||
public IChatBaseComponent(@NonNull IChatBaseComponent component) {
|
||||
this.text = component.text;
|
||||
this.modifier = new ChatModifier(component.modifier);
|
||||
for (IChatBaseComponent child : component.getExtra()) {
|
||||
@@ -93,53 +95,11 @@ public class IChatBaseComponent {
|
||||
* @param child
|
||||
* component to append
|
||||
*/
|
||||
public void addExtra(IChatBaseComponent child) {
|
||||
Preconditions.checkNotNull(child, "extra");
|
||||
public void addExtra(@NonNull IChatBaseComponent child) {
|
||||
if (extra == null) extra = new ArrayList<>();
|
||||
extra.add(child);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns text of this component
|
||||
*
|
||||
* @return text of this component
|
||||
*/
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns chat modifier of this component
|
||||
*
|
||||
* @return chat modifier of this component
|
||||
*/
|
||||
public ChatModifier getModifier() {
|
||||
return modifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets modifier to provided value
|
||||
*
|
||||
* @param modifier
|
||||
* modifier to set value to
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code modifier} is null
|
||||
*/
|
||||
public void setModifier(ChatModifier modifier) {
|
||||
Preconditions.checkNotNull(modifier, "modifier");
|
||||
this.modifier = modifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes text of this component
|
||||
*
|
||||
* @param text
|
||||
* text to show
|
||||
*/
|
||||
public void setText(String text) {
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deserializes string and returns created component. If provided string is null, returns null.
|
||||
*
|
||||
@@ -207,19 +167,7 @@ public class IChatBaseComponent {
|
||||
for (IChatBaseComponent child : getExtra()) {
|
||||
child.modifier.setTargetVersion(clientVersion);
|
||||
}
|
||||
String string;
|
||||
if (clientVersion.getMinorVersion() >= 16) {
|
||||
if (serializeCacheModern.containsKey(this)) return serializeCacheModern.get(this);
|
||||
string = toString();
|
||||
if (serializeCacheModern.size() > 10000) serializeCacheModern.clear();
|
||||
serializeCacheModern.put(this, string);
|
||||
} else {
|
||||
if (serializeCacheLegacy.containsKey(this)) return serializeCacheLegacy.get(this);
|
||||
string = toString();
|
||||
if (serializeCacheLegacy.size() > 10000) serializeCacheLegacy.clear();
|
||||
serializeCacheLegacy.put(this, string);
|
||||
}
|
||||
return string;
|
||||
return serializeCache.get(this, clientVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -229,8 +177,7 @@ public class IChatBaseComponent {
|
||||
* text to convert
|
||||
* @return organized component from colored text
|
||||
*/
|
||||
public static IChatBaseComponent fromColoredText(String originalText){
|
||||
Preconditions.checkNotNull(originalText, "text");
|
||||
public static IChatBaseComponent fromColoredText(@NonNull String originalText){
|
||||
String text = RGBUtils.getInstance().applyFormats(EnumChatFormat.color(originalText));
|
||||
List<IChatBaseComponent> components = new ArrayList<>();
|
||||
StringBuilder builder = new StringBuilder();
|
||||
@@ -421,18 +368,6 @@ public class IChatBaseComponent {
|
||||
* @return The most performance-optimized component based on text
|
||||
*/
|
||||
public static IChatBaseComponent optimizedComponent(String text){
|
||||
if (text == null) return null;
|
||||
if (componentCache.containsKey(text)) return componentCache.get(text);
|
||||
IChatBaseComponent component;
|
||||
if (text.contains("#") || text.contains("&x") || text.contains(EnumChatFormat.COLOR_CHAR + "x") || text.contains("<")){
|
||||
//contains RGB colors
|
||||
component = IChatBaseComponent.fromColoredText(text);
|
||||
} else {
|
||||
//no RGB
|
||||
component = new IChatBaseComponent(text);
|
||||
}
|
||||
if (componentCache.size() > 10000) componentCache.clear();
|
||||
componentCache.put(text, component);
|
||||
return component;
|
||||
return stringCache.get(text, null);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
package me.neznamy.tab.api.chat;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
/**
|
||||
@@ -24,7 +26,7 @@ public class TextColor {
|
||||
* Boolean value whether the legacy color was forced with constructor or should be
|
||||
* automatically assigned as closest color.
|
||||
* This value is used in gradients when converting text for legacy players. */
|
||||
private boolean legacyColorForced;
|
||||
@Getter private boolean legacyColorForced;
|
||||
|
||||
/**
|
||||
* Constructs new instance as a clone of the provided color.
|
||||
@@ -34,8 +36,7 @@ public class TextColor {
|
||||
* @throws IllegalArgumentException
|
||||
* if color is {@code null}
|
||||
*/
|
||||
public TextColor(TextColor color) {
|
||||
Preconditions.checkNotNull(color, "color");
|
||||
public TextColor(@NonNull TextColor color) {
|
||||
rgb = color.rgb;
|
||||
legacyColor = color.legacyColor;
|
||||
hexCode = color.hexCode;
|
||||
@@ -50,8 +51,7 @@ public class TextColor {
|
||||
* @throws IllegalArgumentException
|
||||
* if hexCode is {@code null}
|
||||
*/
|
||||
public TextColor(String hexCode) {
|
||||
Preconditions.checkNotNull(hexCode, "hex code");
|
||||
public TextColor(@NonNull String hexCode) {
|
||||
this.hexCode = hexCode;
|
||||
}
|
||||
|
||||
@@ -65,9 +65,7 @@ public class TextColor {
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code hexCode} is {@code null} or {@code legacyColor} is {@code null}
|
||||
*/
|
||||
public TextColor(String hexCode, EnumChatFormat legacyColor) {
|
||||
Preconditions.checkNotNull(hexCode, "hex code");
|
||||
Preconditions.checkNotNull(legacyColor, "legacy color");
|
||||
public TextColor(@NonNull String hexCode, EnumChatFormat legacyColor) {
|
||||
this.hexCode = hexCode;
|
||||
this.legacyColorForced = true;
|
||||
this.legacyColor = legacyColor;
|
||||
@@ -81,8 +79,7 @@ public class TextColor {
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code legacyColor} is {@code null}
|
||||
*/
|
||||
public TextColor(EnumChatFormat legacyColor) {
|
||||
Preconditions.checkNotNull(legacyColor, "legacy color");
|
||||
public TextColor(@NonNull EnumChatFormat legacyColor) {
|
||||
this.rgb = (legacyColor.getRed() << 16) + (legacyColor.getGreen() << 8) + legacyColor.getBlue();
|
||||
this.hexCode = legacyColor.getHexCode();
|
||||
}
|
||||
@@ -195,15 +192,6 @@ public class TextColor {
|
||||
return "#" + getHexCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if legacy color was forced with a constructor, false if not
|
||||
*
|
||||
* @return true if forced, false if not
|
||||
*/
|
||||
public boolean isLegacyColorForced() {
|
||||
return legacyColorForced;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the string as it appears in chat component and turns it into the color object.
|
||||
* If the entered string is null, returns null.
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package me.neznamy.tab.api.chat;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -10,21 +13,12 @@ import java.util.List;
|
||||
* slows down the performance, as well as makes code more complicated
|
||||
* and open to problems.
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class WrappedChatComponent extends IChatBaseComponent {
|
||||
|
||||
/** Original NMS component */
|
||||
private final Object originalComponent;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
*
|
||||
* @param originalComponent
|
||||
* Original NMS component
|
||||
*/
|
||||
public WrappedChatComponent(Object originalComponent) {
|
||||
this.originalComponent = originalComponent;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the original NMS component
|
||||
* @return the original NMS component
|
||||
@@ -54,7 +48,7 @@ public class WrappedChatComponent extends IChatBaseComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChatModifier getModifier() {
|
||||
public @NotNull ChatModifier getModifier() {
|
||||
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@@ -64,12 +58,12 @@ public class WrappedChatComponent extends IChatBaseComponent {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addExtra(IChatBaseComponent child) {
|
||||
public void addExtra(@NonNull IChatBaseComponent child) {
|
||||
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setModifier(ChatModifier modifier) {
|
||||
public void setModifier(@NotNull ChatModifier modifier) {
|
||||
throw new UnsupportedOperationException("Not supported for " + getClass().getSimpleName());
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,7 @@
|
||||
package me.neznamy.tab.api.chat.rgb;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.chat.TextColor;
|
||||
import me.neznamy.tab.api.chat.rgb.format.*;
|
||||
@@ -12,7 +9,12 @@ import me.neznamy.tab.api.chat.rgb.gradient.CMIGradient;
|
||||
import me.neznamy.tab.api.chat.rgb.gradient.CommonGradient;
|
||||
import me.neznamy.tab.api.chat.rgb.gradient.GradientPattern;
|
||||
import me.neznamy.tab.api.chat.rgb.gradient.KyoriGradient;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import me.neznamy.tab.api.util.ReflectionUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A helper class to reformat all RGB formats into the default #RRGGBB and apply gradients
|
||||
@@ -20,7 +22,7 @@ import me.neznamy.tab.api.util.Preconditions;
|
||||
public class RGBUtils {
|
||||
|
||||
/** Instance of the class */
|
||||
private static final RGBUtils instance = new RGBUtils();
|
||||
@Getter private static final RGBUtils instance = new RGBUtils();
|
||||
|
||||
/** Registered RGB formatters */
|
||||
private final RGBFormatter[] formats;
|
||||
@@ -39,15 +41,14 @@ public class RGBUtils {
|
||||
*/
|
||||
public RGBUtils() {
|
||||
List<RGBFormatter> list = new ArrayList<>();
|
||||
if (ReflectionUtils.classExists("net.kyori.adventure.text.minimessage.MiniMessage")) {
|
||||
list.add(new MiniMessageFormat());
|
||||
}
|
||||
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[] {
|
||||
@@ -65,15 +66,6 @@ public class RGBUtils {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns instance of this class
|
||||
*
|
||||
* @return instance
|
||||
*/
|
||||
public static RGBUtils getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies all RGB formats and gradients to text and returns it.
|
||||
*
|
||||
@@ -81,8 +73,7 @@ public class RGBUtils {
|
||||
* original text
|
||||
* @return text where everything is converted to #RRGGBB
|
||||
*/
|
||||
public String applyFormats(String text) {
|
||||
Preconditions.checkNotNull(text, "text");
|
||||
public String applyFormats(@NonNull String text) {
|
||||
String replaced = text;
|
||||
for (GradientPattern pattern : gradients) {
|
||||
replaced = pattern.applyPattern(replaced, false);
|
||||
@@ -101,8 +92,7 @@ public class RGBUtils {
|
||||
* original text
|
||||
* @return text where all gradients with static text are converted to #RRGGBB
|
||||
*/
|
||||
public String applyCleanGradients(String text) {
|
||||
Preconditions.checkNotNull(text, "text");
|
||||
public String applyCleanGradients(@NonNull String text) {
|
||||
String replaced = text;
|
||||
for (GradientPattern pattern : gradients) {
|
||||
replaced = pattern.applyPattern(replaced, true);
|
||||
@@ -183,8 +173,7 @@ public class RGBUtils {
|
||||
* string to check
|
||||
* @return {@code true} if valid, {@code false} if not
|
||||
*/
|
||||
public boolean isHexCode(String string) {
|
||||
Preconditions.checkNotNull(string, "string");
|
||||
public boolean isHexCode(@NonNull String string) {
|
||||
if (string.length() != 6) return false;
|
||||
for (int i=0; i<6; i++) {
|
||||
char c = string.charAt(i);
|
||||
|
||||
@@ -4,7 +4,7 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Formatter for #<RRGGBB>
|
||||
* Formatter for #<RRGGBB>
|
||||
*/
|
||||
public class HtmlFormat implements RGBFormatter {
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Formatter for <#RRGGBB>
|
||||
* Formatter for <#RRGGBB>
|
||||
*/
|
||||
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 <#RRGGBB>
|
||||
* Call to MiniMessage API to reformat text to &x&R&R&G&G&B&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;
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@ package me.neznamy.tab.api.chat.rgb.gradient;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.chat.TextColor;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class CommonGradient implements GradientPattern {
|
||||
|
||||
private final Pattern pattern;
|
||||
@@ -15,18 +17,7 @@ public class CommonGradient implements GradientPattern {
|
||||
private final int startColorStart;
|
||||
private final int messageStart;
|
||||
private final int endColorStartSub;
|
||||
|
||||
public CommonGradient(Pattern pattern, Pattern legacyPattern, String containCheck, int legacyCharPosition,
|
||||
int startColorStart, int messageStart, int endColorStartSub) {
|
||||
this.pattern = pattern;
|
||||
this.legacyPattern = legacyPattern;
|
||||
this.containCheck = containCheck;
|
||||
this.legacyCharPosition = legacyCharPosition;
|
||||
this.startColorStart = startColorStart;
|
||||
this.messageStart = messageStart;
|
||||
this.endColorStartSub = endColorStartSub;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String applyPattern(String text, boolean ignorePlaceholders) {
|
||||
if (!text.contains(containCheck)) return text;
|
||||
|
||||
@@ -7,8 +7,8 @@ import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.chat.TextColor;
|
||||
|
||||
/**
|
||||
* Gradient applier for <gradient:#RRGGBB:#RRGGBB>Text</gradient>
|
||||
* and <gradient:#RRGGBB|L:#RRGGBB>Text</gradient>
|
||||
* Gradient applier for <gradient:#RRGGBB:#RRGGBB>Text</gradient>
|
||||
* and <gradient:#RRGGBB|L:#RRGGBB>Text</gradient>
|
||||
*/
|
||||
public class KyoriGradient implements GradientPattern {
|
||||
|
||||
|
||||
@@ -1,14 +1,13 @@
|
||||
package me.neznamy.tab.api.config;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.Setter;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
|
||||
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 +15,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
|
||||
*/
|
||||
@@ -29,10 +25,10 @@ public abstract class ConfigurationFile {
|
||||
protected List<String> header;
|
||||
|
||||
/** Configuration file content */
|
||||
protected Map<String, Object> values;
|
||||
@Getter @Setter protected Map<String, Object> values;
|
||||
|
||||
/** File to use */
|
||||
protected final File file;
|
||||
@Getter protected final File file;
|
||||
|
||||
/**
|
||||
* Constructs new instance and attempts to load specified configuration file.
|
||||
@@ -49,22 +45,12 @@ public abstract class ConfigurationFile {
|
||||
* @throws IOException
|
||||
* if I/O operation with the file unexpectedly fails
|
||||
*/
|
||||
protected ConfigurationFile(InputStream source, File destination) throws IOException {
|
||||
Preconditions.checkNotNull(destination, "destination");
|
||||
protected ConfigurationFile(InputStream source, @NonNull File destination) throws IOException {
|
||||
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();
|
||||
}
|
||||
@@ -83,19 +69,6 @@ public abstract class ConfigurationFile {
|
||||
return file.getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the root value map
|
||||
*
|
||||
* @return the root value map
|
||||
*/
|
||||
public Map<String, Object> getValues(){
|
||||
return values;
|
||||
}
|
||||
|
||||
public void setValues(Map<String, Object> values) {
|
||||
this.values = values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets config option with specified path. If the option is not present and
|
||||
* {@code defaultValue} is not {@code null}, value is inserted, {@link #save()}
|
||||
@@ -423,8 +396,4 @@ public abstract class ConfigurationFile {
|
||||
Files.write(file.toPath(), content);
|
||||
}
|
||||
}
|
||||
|
||||
public File getFile() {
|
||||
return file;
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.api.chat.rgb.RGBUtils;
|
||||
import me.neznamy.tab.api.util.BiFunctionWithException;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A class for packet building, methods are overridden in
|
||||
@@ -48,9 +48,7 @@ public class PacketBuilder {
|
||||
* @throws ReflectiveOperationException
|
||||
* if reflection fails
|
||||
*/
|
||||
public Object build(TabPacket packet, ProtocolVersion clientVersion) throws ReflectiveOperationException {
|
||||
Preconditions.checkNotNull(packet, "packet");
|
||||
Preconditions.checkNotNull(clientVersion, "clientVersion");
|
||||
public Object build(@NonNull TabPacket packet, @NonNull ProtocolVersion clientVersion) throws ReflectiveOperationException {
|
||||
return buildMap.get(packet.getClass()).apply(packet, clientVersion);
|
||||
}
|
||||
|
||||
@@ -202,7 +200,7 @@ public class PacketBuilder {
|
||||
}
|
||||
|
||||
/**
|
||||
* If {@code clientVersion} is >= 1.13, creates a component from given text and returns
|
||||
* If {@code clientVersion} is >= 1.13, creates a component from given text and returns
|
||||
* it as a serialized component, which BungeeCord uses.
|
||||
* <p>
|
||||
* If {@code clientVersion} is < 1.12, the text is cut to {@code length} characters if
|
||||
|
||||
@@ -1,21 +1,23 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.bossbar.BarColor;
|
||||
import me.neznamy.tab.api.bossbar.BarStyle;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A class representing platform specific packet class
|
||||
*/
|
||||
@Data
|
||||
public class PacketPlayOutBoss implements TabPacket {
|
||||
|
||||
/** UUID of the BossBar */
|
||||
private final UUID id;
|
||||
|
||||
/** Action of this packet */
|
||||
private final Action action;
|
||||
private Action action;
|
||||
|
||||
/** BossBar title */
|
||||
private String name;
|
||||
@@ -53,11 +55,7 @@ public class PacketPlayOutBoss implements TabPacket {
|
||||
* @param overlay
|
||||
* BossBar style
|
||||
*/
|
||||
public PacketPlayOutBoss(UUID id, String name, float pct, BarColor color, BarStyle overlay) {
|
||||
Preconditions.checkNotNull(id, "uuid");
|
||||
Preconditions.checkNotNull(name, "title");
|
||||
Preconditions.checkNotNull(color, "color");
|
||||
Preconditions.checkNotNull(overlay, "style");
|
||||
public PacketPlayOutBoss(@NonNull UUID id, @NonNull String name, float pct, @NonNull BarColor color, @NonNull BarStyle overlay) {
|
||||
this.action = Action.ADD;
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
@@ -68,28 +66,26 @@ public class PacketPlayOutBoss implements TabPacket {
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.REMOVE action
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action#REMOVE} action
|
||||
*
|
||||
* @param id
|
||||
* BossBar uuid
|
||||
*/
|
||||
public PacketPlayOutBoss(UUID id) {
|
||||
Preconditions.checkNotNull(id, "uuid");
|
||||
public PacketPlayOutBoss(@NonNull UUID id) {
|
||||
this.action = Action.REMOVE;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.UPDATE_PCT action
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action#UPDATE_PCT} action
|
||||
*
|
||||
* @param id
|
||||
* BossBar uuid
|
||||
* @param pct
|
||||
* BossBar progress
|
||||
*/
|
||||
public PacketPlayOutBoss(UUID id, float pct) {
|
||||
Preconditions.checkNotNull(id, "uuid");
|
||||
public PacketPlayOutBoss(@NonNull UUID id, float pct) {
|
||||
this.action = Action.UPDATE_PCT;
|
||||
this.id = id;
|
||||
this.pct = pct;
|
||||
@@ -97,16 +93,14 @@ public class PacketPlayOutBoss implements TabPacket {
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.UPDATE_NAME action
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action#UPDATE_NAME} action
|
||||
*
|
||||
* @param id
|
||||
* BossBar uuid
|
||||
* @param name
|
||||
* BossBar title
|
||||
*/
|
||||
public PacketPlayOutBoss(UUID id, String name) {
|
||||
Preconditions.checkNotNull(id, "uuid");
|
||||
Preconditions.checkNotNull(name, "title");
|
||||
public PacketPlayOutBoss(@NonNull UUID id, @NonNull String name) {
|
||||
this.action = Action.UPDATE_NAME;
|
||||
this.id = id;
|
||||
this.name = name;
|
||||
@@ -114,7 +108,7 @@ public class PacketPlayOutBoss implements TabPacket {
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.UPDATE_STYLE action
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action#UPDATE_STYLE} action
|
||||
*
|
||||
* @param id
|
||||
* BossBar uuid
|
||||
@@ -123,10 +117,7 @@ public class PacketPlayOutBoss implements TabPacket {
|
||||
* @param overlay
|
||||
* BossBar style
|
||||
*/
|
||||
public PacketPlayOutBoss(UUID id, BarColor color, BarStyle overlay) {
|
||||
Preconditions.checkNotNull(id, "uuid");
|
||||
Preconditions.checkNotNull(color, "color");
|
||||
Preconditions.checkNotNull(overlay, "style");
|
||||
public PacketPlayOutBoss(@NonNull UUID id, @NonNull BarColor color, @NonNull BarStyle overlay) {
|
||||
this.action = Action.UPDATE_STYLE;
|
||||
this.id = id;
|
||||
this.color = color;
|
||||
@@ -135,7 +126,7 @@ public class PacketPlayOutBoss implements TabPacket {
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action}.UPDATE_PROPERTIES action
|
||||
* {@link me.neznamy.tab.api.protocol.PacketPlayOutBoss.Action#UPDATE_PROPERTIES} action
|
||||
*
|
||||
* @param darkenScreen
|
||||
* Darken screen flag
|
||||
@@ -144,8 +135,7 @@ public class PacketPlayOutBoss implements TabPacket {
|
||||
* @param createWorldFog
|
||||
* Create fog flag
|
||||
*/
|
||||
public PacketPlayOutBoss(UUID id, boolean darkenScreen, boolean playMusic, boolean createWorldFog) {
|
||||
Preconditions.checkNotNull(id, "uuid");
|
||||
public PacketPlayOutBoss(@NonNull UUID id, boolean darkenScreen, boolean playMusic, boolean createWorldFog) {
|
||||
this.action = Action.UPDATE_PROPERTIES;
|
||||
this.id = id;
|
||||
this.darkenScreen = darkenScreen;
|
||||
@@ -169,123 +159,6 @@ public class PacketPlayOutBoss implements TabPacket {
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutBoss{id=%s,operation=%s,name=%s,pct=%s,color=%s,overlay=%s,darkenScreen=%s,playMusic=%s,createWorldFog=%s}",
|
||||
id, action, name, pct, color, overlay, darkenScreen, playMusic, createWorldFog);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #color}
|
||||
*
|
||||
* @return color
|
||||
*/
|
||||
public BarColor getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #overlay}
|
||||
*
|
||||
* @return style
|
||||
*/
|
||||
public BarStyle getOverlay() {
|
||||
return overlay;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #name}
|
||||
*
|
||||
* @return name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #id}
|
||||
*
|
||||
* @return id
|
||||
*/
|
||||
public UUID getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #pct}
|
||||
*
|
||||
* @return progress
|
||||
*/
|
||||
public float getPct() {
|
||||
return pct;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #action}
|
||||
*
|
||||
* @return packet action
|
||||
*/
|
||||
public Action getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #darkenScreen}
|
||||
*
|
||||
* @return darkenScreen
|
||||
*/
|
||||
public boolean isDarkenScreen() {
|
||||
return darkenScreen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #createWorldFog}
|
||||
*
|
||||
* @return createWorldFog
|
||||
*/
|
||||
public boolean isCreateWorldFog() {
|
||||
return createWorldFog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #playMusic}
|
||||
*
|
||||
* @return playMusic
|
||||
*/
|
||||
public boolean isPlayMusic() {
|
||||
return playMusic;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #darkenScreen} to specified value
|
||||
*
|
||||
* @param darkenScreen
|
||||
* Darken screen flag
|
||||
*/
|
||||
public void setDarkenScreen(boolean darkenScreen) {
|
||||
this.darkenScreen = darkenScreen;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #createWorldFog} to specified value
|
||||
*
|
||||
* @param createWorldFog
|
||||
* Create fog flag
|
||||
*/
|
||||
public void setCreateWorldFog(boolean createWorldFog) {
|
||||
this.createWorldFog = createWorldFog;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #playMusic} to specified value
|
||||
*
|
||||
* @param playMusic
|
||||
* Play music flag
|
||||
*/
|
||||
public void setPlayMusic(boolean playMusic) {
|
||||
this.playMusic = playMusic;
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum representing all valid boss packet actions.
|
||||
* Calling ordinal() will return action's network ID.
|
||||
|
||||
@@ -1,56 +1,20 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
/**
|
||||
* A class representing platform specific packet class
|
||||
*/
|
||||
@Data
|
||||
public class PacketPlayOutChat implements TabPacket {
|
||||
|
||||
/** Message to be sent */
|
||||
private final IChatBaseComponent message;
|
||||
@NonNull private final IChatBaseComponent message;
|
||||
|
||||
/** Message position */
|
||||
private final ChatMessageType type;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters
|
||||
*
|
||||
* @param message
|
||||
* Chat message to be sent
|
||||
* @param type
|
||||
* Message position
|
||||
*/
|
||||
public PacketPlayOutChat(IChatBaseComponent message, ChatMessageType type) {
|
||||
Preconditions.checkNotNull(message, "message");
|
||||
Preconditions.checkNotNull(type, "message type");
|
||||
this.message = message;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutChat{message=%s,type=%s}", message, type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #message}
|
||||
*
|
||||
* @return message
|
||||
*/
|
||||
public IChatBaseComponent getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #type}
|
||||
*
|
||||
* @return type
|
||||
*/
|
||||
public ChatMessageType getType() {
|
||||
return type;
|
||||
}
|
||||
@NonNull private final ChatMessageType type;
|
||||
|
||||
/**
|
||||
* An enum representing positions of a chat message
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
/**
|
||||
* A class representing platform specific packet class
|
||||
*/
|
||||
@Data @AllArgsConstructor
|
||||
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 +31,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));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -41,41 +42,51 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
* @param entries
|
||||
* Affected entries
|
||||
*/
|
||||
public PacketPlayOutPlayerInfo(EnumPlayerInfoAction action, List<PlayerInfoData> entries) {
|
||||
Preconditions.checkNotNull(action, "action");
|
||||
Preconditions.checkNotNull(entries, "entries");
|
||||
this.action = action;
|
||||
public PacketPlayOutPlayerInfo(@NonNull EnumPlayerInfoAction action, @NonNull List<PlayerInfoData> entries) {
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutPlayerInfo{action=%s,entries=%s}", action, entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #action}
|
||||
* Constructs new instance with given parameters.
|
||||
*
|
||||
* @return packet action
|
||||
* @param actions
|
||||
* Packet actions
|
||||
* @param entries
|
||||
* Affected entries
|
||||
*/
|
||||
public EnumPlayerInfoAction getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #entries}
|
||||
*
|
||||
* @return affected entries
|
||||
*/
|
||||
public List<PlayerInfoData> getEntries() {
|
||||
return entries;
|
||||
public PacketPlayOutPlayerInfo(@NonNull EnumSet<EnumPlayerInfoAction> actions, @NonNull PlayerInfoData... entries) {
|
||||
this.actions = actions;
|
||||
this.entries = Arrays.asList(entries);
|
||||
}
|
||||
|
||||
/**
|
||||
* A subclass representing player list entry
|
||||
*/
|
||||
@Data @AllArgsConstructor
|
||||
public static class PlayerInfoData {
|
||||
|
||||
/** Real name of affected player */
|
||||
private String name;
|
||||
|
||||
/** Player UUID */
|
||||
@NonNull private UUID uniqueId;
|
||||
|
||||
/** Player's skin, null for empty skin */
|
||||
private Skin skin;
|
||||
|
||||
/** I have no idea what this is */
|
||||
private boolean listed;
|
||||
|
||||
/** Latency */
|
||||
private int latency;
|
||||
|
||||
@@ -88,252 +99,75 @@ public class PacketPlayOutPlayerInfo implements TabPacket {
|
||||
*/
|
||||
private IChatBaseComponent displayName;
|
||||
|
||||
/** Real name of affected player */
|
||||
private String name;
|
||||
|
||||
/** Player UUID */
|
||||
private UUID uniqueId;
|
||||
|
||||
/** 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
|
||||
*
|
||||
* @param name
|
||||
* Player's name
|
||||
* @param uniqueId
|
||||
* Player's uuid
|
||||
* @param skin
|
||||
* Player's platform-specific skin object
|
||||
* @param latency
|
||||
* Player's ping
|
||||
* @param gameMode
|
||||
* Player's GameMode
|
||||
* @param displayName
|
||||
* Player's display name
|
||||
* @param profilePublicKey
|
||||
* Player's chat signing key
|
||||
*/
|
||||
public PlayerInfoData(String name, UUID uniqueId, Skin skin, int latency, EnumGamemode gameMode, IChatBaseComponent displayName, Object profilePublicKey) {
|
||||
Preconditions.checkNotNull(uniqueId, "uuid");
|
||||
this.name = name;
|
||||
this.uniqueId = uniqueId;
|
||||
this.skin = skin;
|
||||
this.latency = latency;
|
||||
this.gameMode = gameMode;
|
||||
this.displayName = displayName;
|
||||
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
|
||||
* @param gameMode
|
||||
* Player's GameMode
|
||||
*/
|
||||
public PlayerInfoData(UUID uniqueId, EnumGamemode gameMode) {
|
||||
Preconditions.checkNotNull(uniqueId, "uuid");
|
||||
public PlayerInfoData(@NonNull UUID uniqueId, EnumGamemode gameMode) {
|
||||
this.uniqueId = uniqueId;
|
||||
this.gameMode = gameMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters. Suitable for
|
||||
* {@link EnumPlayerInfoAction}.UPDATE_LATENCY action
|
||||
* {@link EnumPlayerInfoAction#UPDATE_LATENCY} action
|
||||
*
|
||||
* @param uniqueId
|
||||
* Player's uuid
|
||||
* @param latency
|
||||
* Player's ping
|
||||
*/
|
||||
public PlayerInfoData(UUID uniqueId, int latency) {
|
||||
Preconditions.checkNotNull(uniqueId, "uuid");
|
||||
public PlayerInfoData(@NonNull UUID uniqueId, int latency) {
|
||||
this.uniqueId = uniqueId;
|
||||
this.latency = latency;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param displayName
|
||||
* Player's display name
|
||||
*/
|
||||
public PlayerInfoData(UUID uniqueId, IChatBaseComponent displayName) {
|
||||
Preconditions.checkNotNull(uniqueId, "uuid");
|
||||
public PlayerInfoData(@NonNull UUID uniqueId, IChatBaseComponent displayName) {
|
||||
this.uniqueId = uniqueId;
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter. Suitable for
|
||||
* {@link EnumPlayerInfoAction}.REMOVE_PLAYER action
|
||||
* {@link EnumPlayerInfoAction#REMOVE_PLAYER} action
|
||||
*
|
||||
* @param uniqueId
|
||||
* Player's uuid
|
||||
*/
|
||||
public PlayerInfoData(UUID uniqueId) {
|
||||
public PlayerInfoData(@NonNull UUID uniqueId) {
|
||||
this.uniqueId = uniqueId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PlayerInfoData{latency=%s,gameMode=%s,displayName=%s,name=%s,uniqueId=%s,skin=%s,profilePublicKey=%s}",
|
||||
latency, gameMode, displayName, name, uniqueId, skin, profilePublicKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #latency}
|
||||
*
|
||||
* @return latency
|
||||
*/
|
||||
public int getLatency() {
|
||||
return latency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #latency} to specified value
|
||||
*
|
||||
* @param latency
|
||||
* Latency to use
|
||||
*/
|
||||
public void setLatency(int latency) {
|
||||
this.latency = latency;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #displayName}
|
||||
*
|
||||
* @return displayName
|
||||
*/
|
||||
public IChatBaseComponent getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #displayName} to specified value
|
||||
*
|
||||
* @param displayName
|
||||
* Display name to use
|
||||
*/
|
||||
public void setDisplayName(IChatBaseComponent displayName) {
|
||||
this.displayName = displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #uniqueId}
|
||||
*
|
||||
* @return uniqueId
|
||||
*/
|
||||
public UUID getUniqueId() {
|
||||
return uniqueId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #uniqueId} to specified value
|
||||
*
|
||||
* @param uniqueId
|
||||
* UUID to use
|
||||
*/
|
||||
public void setUniqueId(UUID uniqueId) {
|
||||
this.uniqueId = uniqueId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #name}
|
||||
*
|
||||
* @return name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #name} to specified value
|
||||
*
|
||||
* @param name
|
||||
* name to use
|
||||
*/
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #gameMode}
|
||||
*
|
||||
* @return gameMode
|
||||
*/
|
||||
public EnumGamemode getGameMode() {
|
||||
return gameMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #gameMode} to specified value
|
||||
*
|
||||
* @param gameMode
|
||||
* GameMode to use
|
||||
*/
|
||||
public void setGameMode(EnumGamemode gameMode) {
|
||||
this.gameMode = gameMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #skin}
|
||||
*
|
||||
* @return skin
|
||||
*/
|
||||
public Skin getSkin() {
|
||||
return skin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #skin} to specified value
|
||||
*
|
||||
* @param skin
|
||||
* Skin to use
|
||||
*/
|
||||
public void setSkin(Skin skin) {
|
||||
this.skin = skin;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #profilePublicKey}
|
||||
*
|
||||
* @return profilePublicKey
|
||||
*/
|
||||
public Object getProfilePublicKey() {
|
||||
return profilePublicKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #profilePublicKey} to specified value
|
||||
*
|
||||
* @param profilePublicKey
|
||||
* profilePublicKey to use
|
||||
*/
|
||||
public void setProfilePublicKey(Object profilePublicKey) {
|
||||
this.profilePublicKey = profilePublicKey;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
+7
-44
@@ -1,18 +1,21 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
/**
|
||||
* A class representing platform specific packet class
|
||||
*/
|
||||
@Data @AllArgsConstructor
|
||||
public class PacketPlayOutPlayerListHeaderFooter implements TabPacket {
|
||||
|
||||
/** TabList header */
|
||||
private final IChatBaseComponent header;
|
||||
@NonNull private final IChatBaseComponent header;
|
||||
|
||||
/** TabList footer */
|
||||
private final IChatBaseComponent footer;
|
||||
@NonNull private final IChatBaseComponent footer;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters. They are converted to {@link IChatBaseComponent}
|
||||
@@ -23,48 +26,8 @@ public class PacketPlayOutPlayerListHeaderFooter implements TabPacket {
|
||||
* @param footer
|
||||
* TabList footer
|
||||
*/
|
||||
public PacketPlayOutPlayerListHeaderFooter(String header, String footer) {
|
||||
Preconditions.checkNotNull(header, "header");
|
||||
Preconditions.checkNotNull(footer, "footer");
|
||||
public PacketPlayOutPlayerListHeaderFooter(@NonNull String header, @NonNull String footer) {
|
||||
this.header = IChatBaseComponent.optimizedComponent(header);
|
||||
this.footer = IChatBaseComponent.optimizedComponent(footer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters.
|
||||
*
|
||||
* @param header
|
||||
* TabList header
|
||||
* @param footer
|
||||
* TabList footer
|
||||
*/
|
||||
public PacketPlayOutPlayerListHeaderFooter(IChatBaseComponent header, IChatBaseComponent footer) {
|
||||
Preconditions.checkNotNull(header, "header");
|
||||
Preconditions.checkNotNull(footer, "footer");
|
||||
this.header = header;
|
||||
this.footer = footer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutPlayerListHeaderFooter{header=%s,footer=%s}", header, footer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #header}
|
||||
*
|
||||
* @return header
|
||||
*/
|
||||
public IChatBaseComponent getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #footer}
|
||||
*
|
||||
* @return footer
|
||||
*/
|
||||
public IChatBaseComponent getFooter() {
|
||||
return footer;
|
||||
}
|
||||
}
|
||||
+4
-42
@@ -1,10 +1,12 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
|
||||
/**
|
||||
* A class representing platform specific packet class
|
||||
*/
|
||||
@Data
|
||||
public class PacketPlayOutScoreboardDisplayObjective implements TabPacket {
|
||||
|
||||
/**
|
||||
@@ -16,45 +18,5 @@ public class PacketPlayOutScoreboardDisplayObjective implements TabPacket {
|
||||
private final int slot;
|
||||
|
||||
/** Up to 16 characters long objective name */
|
||||
private final String objectiveName;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters
|
||||
*
|
||||
* @param slot
|
||||
* Display slot
|
||||
* @param objectiveName
|
||||
* Internal name of the objective
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code objectiveName} is null or longer than 16 characters
|
||||
*/
|
||||
public PacketPlayOutScoreboardDisplayObjective(int slot, String objectiveName) {
|
||||
Preconditions.checkNotNull(objectiveName, "objective name");
|
||||
Preconditions.checkMaxLength(objectiveName, 16, "objective name");
|
||||
this.slot = slot;
|
||||
this.objectiveName = objectiveName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutScoreboardDisplayObjective{slot=%s,objectiveName=%s}", slot, objectiveName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #slot}
|
||||
*
|
||||
* @return slot
|
||||
*/
|
||||
public int getSlot() {
|
||||
return slot;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #objectiveName}
|
||||
*
|
||||
* @return objectiveName
|
||||
*/
|
||||
public String getObjectiveName() {
|
||||
return objectiveName;
|
||||
}
|
||||
@NonNull private final String objectiveName;
|
||||
}
|
||||
+13
-77
@@ -1,21 +1,16 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
/**
|
||||
* A class representing platform specific packet class
|
||||
*/
|
||||
@Data @AllArgsConstructor
|
||||
public class PacketPlayOutScoreboardObjective implements TabPacket {
|
||||
|
||||
/** Up to 16 characters long objective name */
|
||||
private final String objectiveName;
|
||||
|
||||
/** Display name */
|
||||
private final String displayName;
|
||||
|
||||
/** Display type, only takes effect in PlayerList */
|
||||
private final EnumScoreboardHealthDisplay renderType;
|
||||
|
||||
/**
|
||||
* Packet action.
|
||||
* 0 = register,
|
||||
@@ -24,30 +19,14 @@ public class PacketPlayOutScoreboardObjective implements TabPacket {
|
||||
*/
|
||||
private final int action;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters.
|
||||
*
|
||||
* @param action
|
||||
* Packet action (0 = add, 1 = remove, 2 = update title)
|
||||
* @param objectiveName
|
||||
* objective name, up to 16 characters long
|
||||
* @param displayName
|
||||
* title
|
||||
* @param renderType
|
||||
* display type
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code objectiveName} is null or longer than 16 characters
|
||||
*/
|
||||
public PacketPlayOutScoreboardObjective(int action, String objectiveName, String displayName, EnumScoreboardHealthDisplay renderType) {
|
||||
Preconditions.checkRange(action, 0, 2, "action");
|
||||
Preconditions.checkNotNull(objectiveName, "objective name");
|
||||
Preconditions.checkMaxLength(objectiveName, 16, "objective name");
|
||||
Preconditions.checkNotNull(renderType, "render type");
|
||||
this.objectiveName = objectiveName;
|
||||
this.displayName = displayName;
|
||||
this.renderType = renderType;
|
||||
this.action = action;
|
||||
}
|
||||
/** Up to 16 characters long objective name */
|
||||
@NonNull private final String objectiveName;
|
||||
|
||||
/** Display name */
|
||||
private final String displayName;
|
||||
|
||||
/** Display type, only takes effect in PlayerList */
|
||||
private final EnumScoreboardHealthDisplay renderType;
|
||||
|
||||
/**
|
||||
* Constructs new packet with given objective name and 1 (unregister) action.
|
||||
@@ -57,8 +36,7 @@ public class PacketPlayOutScoreboardObjective implements TabPacket {
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code objectiveName} is null or longer than 16 characters
|
||||
*/
|
||||
public PacketPlayOutScoreboardObjective(String objectiveName) {
|
||||
Preconditions.checkNotNull(objectiveName, "objective name");
|
||||
public PacketPlayOutScoreboardObjective(@NonNull String objectiveName) {
|
||||
Preconditions.checkMaxLength(objectiveName, 16, "objective name");
|
||||
this.objectiveName = objectiveName;
|
||||
this.displayName = ""; //avoiding NPE on <1.7
|
||||
@@ -66,48 +44,6 @@ public class PacketPlayOutScoreboardObjective implements TabPacket {
|
||||
this.renderType = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutScoreboardObjective{objectiveName=%s,displayName=%s,renderType=%s,method=%s}",
|
||||
objectiveName, displayName, renderType, action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #displayName}
|
||||
*
|
||||
* @return displayName
|
||||
*/
|
||||
public String getDisplayName() {
|
||||
return displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #objectiveName}
|
||||
*
|
||||
* @return objectiveName
|
||||
*/
|
||||
public String getObjectiveName() {
|
||||
return objectiveName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #renderType}
|
||||
*
|
||||
* @return renderType
|
||||
*/
|
||||
public EnumScoreboardHealthDisplay getRenderType() {
|
||||
return renderType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #action}
|
||||
*
|
||||
* @return packet action
|
||||
*/
|
||||
public int getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum representing available display types.
|
||||
* They only take effect in PlayerList position.
|
||||
|
||||
@@ -1,91 +1,26 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
|
||||
/**
|
||||
* A class representing platform specific packet class
|
||||
*/
|
||||
@Data
|
||||
public class PacketPlayOutScoreboardScore implements TabPacket {
|
||||
|
||||
/** Packet action */
|
||||
private final Action action;
|
||||
@NonNull private final Action action;
|
||||
|
||||
/** Objective name */
|
||||
private final String objectiveName;
|
||||
@NonNull private final String objectiveName;
|
||||
|
||||
/** Affected player */
|
||||
private final String player;
|
||||
@NonNull private final String player;
|
||||
|
||||
/** Player's score */
|
||||
private final int score;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters
|
||||
*
|
||||
* @param action
|
||||
* Packet action
|
||||
* @param objectiveName
|
||||
* Objective name
|
||||
* @param player
|
||||
* Affected player
|
||||
* @param score
|
||||
* Player's score
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code objectiveName} is null or longer than 16 characters
|
||||
*/
|
||||
public PacketPlayOutScoreboardScore(Action action, String objectiveName, String player, int score) {
|
||||
Preconditions.checkNotNull(action, "action");
|
||||
Preconditions.checkNotNull(objectiveName, "objective name");
|
||||
Preconditions.checkMaxLength(objectiveName, 16, "objective name");
|
||||
Preconditions.checkNotNull(player, "player");
|
||||
this.action = action;
|
||||
this.objectiveName = objectiveName;
|
||||
this.player = player;
|
||||
this.score = score;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutScoreboardScore{action=%s,objectiveName=%s,player=%s,score=%s}",
|
||||
action, objectiveName, player, score);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #action}
|
||||
*
|
||||
* @return packet action
|
||||
*/
|
||||
public Action getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #objectiveName}
|
||||
*
|
||||
* @return objective name
|
||||
*/
|
||||
public String getObjectiveName() {
|
||||
return objectiveName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #player}
|
||||
*
|
||||
* @return player
|
||||
*/
|
||||
public String getPlayer() {
|
||||
return player;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #score}
|
||||
*
|
||||
* @return score
|
||||
*/
|
||||
public int getScore() {
|
||||
return score;
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum representing action types
|
||||
*/
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
|
||||
/**
|
||||
* A class representing platform specific packet class
|
||||
*/
|
||||
@Data
|
||||
public class PacketPlayOutScoreboardTeam implements TabPacket {
|
||||
|
||||
/**
|
||||
@@ -18,12 +21,12 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
|
||||
* Players with teams are below them, players of the same team are
|
||||
* sorted alphabetically by usernames.
|
||||
*/
|
||||
private final String name;
|
||||
@NonNull private final String name;
|
||||
|
||||
/** Team prefix, limited to 16 characters, on <1.13 */
|
||||
/** Team prefix, limited to 16 characters, on <1.13 */
|
||||
private String playerPrefix;
|
||||
|
||||
/** Team suffix, limited to 16 characters, on <1.13 */
|
||||
/** Team suffix, limited to 16 characters, on <1.13 */
|
||||
private String playerSuffix;
|
||||
|
||||
/**
|
||||
@@ -46,10 +49,10 @@ 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 <1.8, 40 characters since 1.8.
|
||||
* Entity UUID option was added in 1.8.
|
||||
*/
|
||||
private Collection<String> players = Collections.emptyList();
|
||||
@NonNull private Collection<String> players = Collections.emptyList();
|
||||
|
||||
/**
|
||||
* Packet action.
|
||||
@@ -80,8 +83,7 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code name} is null, empty or longer than 16 characters
|
||||
*/
|
||||
private PacketPlayOutScoreboardTeam(int action, String name) {
|
||||
Preconditions.checkNotNull(name, "team name");
|
||||
private PacketPlayOutScoreboardTeam(int action, @NonNull String name) {
|
||||
Preconditions.checkMaxLength(name, 16, "team name");
|
||||
this.action = action;
|
||||
this.name = name;
|
||||
@@ -107,13 +109,9 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code name} is null, empty or longer than 16 characters
|
||||
*/
|
||||
public PacketPlayOutScoreboardTeam(String team, String prefix, String suffix, String visibility, String collision, Collection<String> players, int options) {
|
||||
public PacketPlayOutScoreboardTeam(String team, @NonNull String prefix, @NonNull String suffix, String visibility,
|
||||
String collision, @NonNull Collection<String> players, int options) {
|
||||
this(0, team);
|
||||
Preconditions.checkNotNull(prefix, "prefix");
|
||||
Preconditions.checkNotNull(suffix, "suffix");
|
||||
Preconditions.checkNotNull(visibility, "visibility");
|
||||
Preconditions.checkNotNull(collision, "collision");
|
||||
Preconditions.checkNotNull(players, "players");
|
||||
this.playerPrefix = prefix;
|
||||
this.playerSuffix = suffix;
|
||||
this.nameTagVisibility = visibility;
|
||||
@@ -152,13 +150,9 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code name} is null, empty or longer than 16 characters
|
||||
*/
|
||||
public PacketPlayOutScoreboardTeam(String team, String prefix, String suffix, String visibility, String collision, int options) {
|
||||
public PacketPlayOutScoreboardTeam(String team, @NonNull String prefix, @NonNull String suffix,
|
||||
String visibility, String collision, int options) {
|
||||
this(2, team);
|
||||
Preconditions.checkNotNull(prefix, "prefix");
|
||||
Preconditions.checkNotNull(suffix, "suffix");
|
||||
Preconditions.checkNotNull(visibility, "visibility");
|
||||
Preconditions.checkNotNull(collision, "collision");
|
||||
Preconditions.checkNotNull(players, "players");
|
||||
this.playerPrefix = prefix;
|
||||
this.playerSuffix = suffix;
|
||||
this.nameTagVisibility = visibility;
|
||||
@@ -179,107 +173,8 @@ public class PacketPlayOutScoreboardTeam implements TabPacket {
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code name} is null, empty or longer than 16 characters
|
||||
*/
|
||||
public PacketPlayOutScoreboardTeam(String team, Collection<String> players, boolean add) {
|
||||
public PacketPlayOutScoreboardTeam(String team, @NonNull Collection<String> players, boolean add) {
|
||||
this(add ? 3 : 4, team);
|
||||
Preconditions.checkNotNull(players, "players");
|
||||
this.players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutScoreboardTeam{name=%s,playerPrefix=%s,playerSuffix=%s,nameTagVisibility=%s,"
|
||||
+ "collisionRule=%s,color=%s,players=%s,method=%s,options=%s}",
|
||||
name, playerPrefix, playerSuffix, nameTagVisibility, collisionRule, color, players, action, options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #name}
|
||||
*
|
||||
* @return team name
|
||||
*/
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #playerPrefix}
|
||||
*
|
||||
* @return playerPrefix
|
||||
*/
|
||||
public String getPlayerPrefix() {
|
||||
return playerPrefix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #playerSuffix}
|
||||
*
|
||||
* @return playerSuffix
|
||||
*/
|
||||
public String getPlayerSuffix() {
|
||||
return playerSuffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #nameTagVisibility}
|
||||
*
|
||||
* @return nameTagVisibility
|
||||
*/
|
||||
public String getNameTagVisibility() {
|
||||
return nameTagVisibility;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #collisionRule}
|
||||
*
|
||||
* @return collisionRule
|
||||
*/
|
||||
public String getCollisionRule() {
|
||||
return collisionRule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets {@link #color} to specified color
|
||||
*
|
||||
* @param color
|
||||
* color to use
|
||||
*/
|
||||
public void setColor(EnumChatFormat color) {
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #color}
|
||||
*
|
||||
* @return color
|
||||
*/
|
||||
public EnumChatFormat getColor() {
|
||||
return color;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #players}
|
||||
*
|
||||
* @return players
|
||||
*/
|
||||
public Collection<String> getPlayers() {
|
||||
return players;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #options}
|
||||
*
|
||||
* @return team options
|
||||
*/
|
||||
public int getOptions() {
|
||||
return options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link #action}
|
||||
*
|
||||
* @return packet action
|
||||
*/
|
||||
public int getAction() {
|
||||
return action;
|
||||
}
|
||||
}
|
||||
@@ -1,59 +1,18 @@
|
||||
package me.neznamy.tab.api.protocol;
|
||||
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
|
||||
/**
|
||||
* Class representing a minecraft skin as a value - signature pair.
|
||||
*/
|
||||
@Data @AllArgsConstructor
|
||||
public class Skin {
|
||||
|
||||
/** Skin value */
|
||||
private final String value;
|
||||
@NonNull private final String value;
|
||||
|
||||
/** Skin signature, can be null */
|
||||
private final String signature;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters
|
||||
*
|
||||
* @param value
|
||||
* skin value
|
||||
* @param signature
|
||||
* skin signature
|
||||
* @throws IllegalArgumentException
|
||||
* if {@code value} is {@code null}
|
||||
*/
|
||||
public Skin(String value, String signature) {
|
||||
Preconditions.checkNotNull(value, "value");
|
||||
this.value = value;
|
||||
this.signature = signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns skin value
|
||||
*
|
||||
* @return skin value
|
||||
*/
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns skin signature
|
||||
*
|
||||
* @return skin signature
|
||||
*/
|
||||
public String getSignature() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns user-friendly representation of this object
|
||||
*
|
||||
* @return user-friendly representation of this object
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("Skin{value=%s,signature=%s}", value, signature);
|
||||
}
|
||||
}
|
||||
@@ -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 < 0 || index >= {@link #getLines()}.size())
|
||||
* if the index is out of range (index < 0 || index >= {@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;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package me.neznamy.tab.api.util;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@AllArgsConstructor
|
||||
public class ComponentCache<K, V> {
|
||||
|
||||
private final int cacheSize;
|
||||
private final BiFunctionWithException<K, ProtocolVersion, V> function;
|
||||
private final Map<K, V> cacheModern = new HashMap<>();
|
||||
private final Map<K, V> cacheLegacy = new HashMap<>();
|
||||
|
||||
public V get(K key, ProtocolVersion clientVersion) {
|
||||
try {
|
||||
if (key == null) return null;
|
||||
Map<K, V> cache = clientVersion == null || clientVersion.getMinorVersion() >= 16 ? cacheModern : cacheLegacy;
|
||||
if (cache.containsKey(key)) return cache.get(key);
|
||||
V value = function.apply(key, clientVersion);
|
||||
if (cache.size() > cacheSize) cache.clear();
|
||||
cache.put(key, value);
|
||||
return value;
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,25 +1,63 @@
|
||||
package me.neznamy.tab.api.util;
|
||||
|
||||
import lombok.NonNull;
|
||||
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){
|
||||
if (obj == null) throw new IllegalArgumentException(name + " cannot be null");
|
||||
}
|
||||
|
||||
public static void checkMaxLength(String string, int maxLength, String name) {
|
||||
checkNotNull(string, name);
|
||||
/**
|
||||
* 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(@NonNull String string, int maxLength, 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,24 @@
|
||||
package me.neznamy.tab.api.util;
|
||||
|
||||
/**
|
||||
* Utility class storing methods working with reflection
|
||||
*/
|
||||
public class ReflectionUtils {
|
||||
|
||||
/**
|
||||
* Returns {@code true} if class with given full name exists,
|
||||
* {@code false} if not.
|
||||
*
|
||||
* @param path
|
||||
* Full class path and name
|
||||
* @return {@code true} if exists, {@code false} if not
|
||||
*/
|
||||
public static boolean classExists(String path) {
|
||||
try {
|
||||
Class.forName(path);
|
||||
return true;
|
||||
} catch (ClassNotFoundException | NullPointerException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -2,11 +2,16 @@ plugins {
|
||||
`kotlin-dsl`
|
||||
}
|
||||
|
||||
repositories {
|
||||
gradlePluginPortal()
|
||||
mavenCentral()
|
||||
dependencies {
|
||||
implementation(libs.plugin.shadow)
|
||||
implementation(libs.plugin.lombok)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
@@ -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"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import org.apache.tools.ant.filters.ReplaceTokens
|
||||
|
||||
plugins {
|
||||
`java-library`
|
||||
id("io.freefair.lombok")
|
||||
}
|
||||
|
||||
tasks {
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
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`
|
||||
|
||||
+3
-2
@@ -11,8 +11,9 @@ allprojects {
|
||||
val platforms = setOf(
|
||||
projects.bukkit,
|
||||
projects.bungeecord,
|
||||
// projects.krypton,
|
||||
projects.velocity
|
||||
projects.krypton,
|
||||
projects.velocity,
|
||||
//projects.sponge
|
||||
).map { it.dependencyProject }
|
||||
|
||||
val special = setOf(
|
||||
|
||||
@@ -6,7 +6,4 @@ dependencies {
|
||||
compileOnly(libs.vault)
|
||||
compileOnly(libs.via)
|
||||
compileOnly(libs.authlib)
|
||||
compileOnly(libs.essentials) {
|
||||
exclude("org.spigotmc", "spigot-api")
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.EventPriority;
|
||||
@@ -12,20 +13,11 @@ import org.bukkit.event.player.PlayerQuitEvent;
|
||||
/**
|
||||
* The core for bukkit forwarding events into all enabled features
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class BukkitEventListener implements Listener {
|
||||
|
||||
/** Platform instance */
|
||||
private final BukkitPlatform platform;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameter
|
||||
*
|
||||
* @param platform
|
||||
* Platform instance
|
||||
*/
|
||||
public BukkitEventListener(BukkitPlatform platform) {
|
||||
this.platform = platform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener to PlayerQuitEvent to remove player data and forward the event to features
|
||||
@@ -48,7 +40,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 +53,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 +66,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,19 @@ 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.api.util.ComponentCache;
|
||||
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 {
|
||||
@@ -30,10 +36,14 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
private final EnumMap<EntityType, Integer> entityIds = new EnumMap<>(EntityType.class);
|
||||
|
||||
/** Component cache for better performance (1.16+ players) */
|
||||
private final Map<IChatBaseComponent, Object> componentCacheModern = new HashMap<>();
|
||||
|
||||
private final ComponentCache<IChatBaseComponent, Object> componentCacheModern = new ComponentCache<>(10000,
|
||||
(component, clientVersion) -> nms.ChatSerializer_DESERIALIZE.invoke(null, component.toString(clientVersion)));
|
||||
|
||||
/** Component cache for better performance (1.15- players) */
|
||||
private final Map<IChatBaseComponent, Object> componentCacheLegacy = new HashMap<>();
|
||||
private final ComponentCache<IChatBaseComponent, Object> componentCacheLegacy = new ComponentCache<>(10000,
|
||||
(component, clientVersion) -> nms.ChatSerializer_DESERIALIZE.invoke(null, component.toString(clientVersion)));
|
||||
|
||||
|
||||
/**
|
||||
* Constructs new instance
|
||||
@@ -76,9 +86,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 +99,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 +173,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 +181,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 +190,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 +230,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 +268,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 +310,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 +368,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 +390,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 +474,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
|
||||
@@ -409,14 +495,14 @@ public class BukkitPacketBuilder extends PacketBuilder {
|
||||
if (packet.getAction() == Action.UPDATE_PCT || packet.getAction() == Action.ADD) {
|
||||
float health = 300*packet.getPct();
|
||||
if (health == 0) health = 1;
|
||||
w.helper().setHealth(health);
|
||||
w.getHelper().setHealth(health);
|
||||
}
|
||||
if (packet.getAction() == Action.UPDATE_NAME || packet.getAction() == Action.ADD) {
|
||||
w.helper().setCustomName(packet.getName(), clientVersion);
|
||||
w.getHelper().setCustomName(packet.getName(), clientVersion);
|
||||
}
|
||||
if (packet.getAction() == Action.ADD) {
|
||||
w.helper().setEntityFlags((byte) 32);
|
||||
w.helper().setWitherInvulnerableTime(880); // Magic number
|
||||
w.getHelper().setEntityFlags((byte) 32);
|
||||
w.getHelper().setWitherInvulnerableTime(880); // Magic number
|
||||
return build(new PacketPlayOutSpawnEntityLiving(entityId, new UUID(0, 0), EntityType.WITHER, new Location(null, 0,0,0), w));
|
||||
} else {
|
||||
return build(new PacketPlayOutEntityMetadata(entityId, w));
|
||||
@@ -436,25 +522,21 @@ 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
|
||||
*/
|
||||
public Object toNMSComponent(IChatBaseComponent component, ProtocolVersion clientVersion) throws ReflectiveOperationException {
|
||||
if (component == null) return null;
|
||||
if (component instanceof WrappedChatComponent) return ((WrappedChatComponent) component).get();
|
||||
Map<IChatBaseComponent, Object> cache = clientVersion.getMinorVersion() >= 16 ? componentCacheModern : componentCacheLegacy;
|
||||
if (cache.containsKey(component)) return cache.get(component);
|
||||
if (cache.size() > 10000) cache.clear();
|
||||
Object chat = nms.ChatSerializer_DESERIALIZE.invoke(null, component.toString(clientVersion));
|
||||
cache.put(component, chat);
|
||||
return chat;
|
||||
return (clientVersion.getMinorVersion() >= 16 ? componentCacheModern : componentCacheLegacy).get(component, clientVersion);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -7,12 +7,14 @@ import java.util.function.Function;
|
||||
import io.netty.channel.ChannelDuplexHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import lombok.AllArgsConstructor;
|
||||
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;
|
||||
|
||||
/**
|
||||
@@ -38,21 +40,12 @@ public class BukkitPipelineInjector extends PipelineInjector {
|
||||
/**
|
||||
* Custom channel duplex handler override
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class BukkitChannelDuplexHandler extends ChannelDuplexHandler {
|
||||
|
||||
/** Injected player */
|
||||
private final TabPlayer player;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given player
|
||||
*
|
||||
* @param player
|
||||
* player to inject
|
||||
*/
|
||||
public BukkitChannelDuplexHandler(TabPlayer player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead(@NotNull ChannelHandlerContext context, @NotNull Object packet) {
|
||||
try {
|
||||
@@ -66,11 +59,12 @@ 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;
|
||||
}
|
||||
if (antiOverrideTeams && nms.PacketPlayOutScoreboardTeam != null && nms.PacketPlayOutScoreboardTeam.isInstance(packet)) {
|
||||
if (antiOverrideTeams && nms.PacketPlayOutScoreboardTeam.isInstance(packet)) {
|
||||
long time = System.nanoTime();
|
||||
modifyPlayers(packet);
|
||||
TAB.getInstance().getCPUManager().addTime("NameTags", TabConstants.CpuUsageCategory.ANTI_OVERRIDE, System.nanoTime()-time);
|
||||
@@ -117,9 +111,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);
|
||||
}
|
||||
|
||||
+46
-45
@@ -1,29 +1,21 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.placeholder.PlaceholderManager;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import me.neznamy.tab.shared.placeholders.UniversalPlaceholderRegistry;
|
||||
import net.milkbowl.vault.chat.Chat;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.text.NumberFormat;
|
||||
import java.util.Locale;
|
||||
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.placeholder.PlayerPlaceholder;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import net.ess3.api.events.NickChangeEvent;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.EventHandler;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.event.Listener;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
|
||||
import com.earth2me.essentials.Essentials;
|
||||
|
||||
import me.neznamy.tab.api.placeholder.PlaceholderManager;
|
||||
import me.neznamy.tab.shared.placeholders.UniversalPlaceholderRegistry;
|
||||
import net.milkbowl.vault.chat.Chat;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Bukkit registry to register bukkit-only and universal placeholders
|
||||
@@ -33,17 +25,28 @@ public class BukkitPlaceholderRegistry extends UniversalPlaceholderRegistry {
|
||||
/** Number formatter for 2 decimal places */
|
||||
private final NumberFormat numberFormat = NumberFormat.getNumberInstance(Locale.US);
|
||||
|
||||
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;
|
||||
|
||||
public BukkitPlaceholderRegistry(JavaPlugin plugin) {
|
||||
this.plugin = plugin;
|
||||
/**
|
||||
* Constructs new instance and loads hooks
|
||||
*/
|
||||
public BukkitPlaceholderRegistry() {
|
||||
numberFormat.setMaximumFractionDigits(2);
|
||||
if (Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.VAULT)) {
|
||||
RegisteredServiceProvider<Chat> rspChat = Bukkit.getServicesManager().getRegistration(Chat.class);
|
||||
@@ -63,7 +66,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,29 +83,20 @@ 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;
|
||||
if (essentials != null) {
|
||||
try {
|
||||
Object user = essentials.getClass().getMethod("getUser", UUID.class).invoke(essentials, p.getUniqueId());
|
||||
if ((boolean) user.getClass().getMethod("isAfk").invoke(user)) return true;
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get AFK status of " + p.getName() + " using Essentials", e);
|
||||
}
|
||||
}
|
||||
return purpurIsAfk != null && ((Player)p.getPlayer()).isAfk();
|
||||
});
|
||||
if (essentials != null) {
|
||||
PlayerPlaceholder nick = manager.registerPlayerPlaceholder(TabConstants.Placeholder.ESSENTIALS_NICK, -1, p -> {
|
||||
String nickname = ((Essentials)essentials).getUser(p.getUniqueId()).getNickname();
|
||||
return nickname == null ? p.getName() : nickname;
|
||||
});
|
||||
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());
|
||||
if (player == null) return;
|
||||
nick.updateValue(player, name);
|
||||
}
|
||||
};
|
||||
nick.enableTriggerMode(() -> Bukkit.getPluginManager().registerEvents(nickListener, plugin),
|
||||
() -> HandlerList.unregisterAll(nickListener));
|
||||
} else {
|
||||
manager.registerPlayerPlaceholder(TabConstants.Placeholder.ESSENTIALS_NICK, -1, TabPlayer::getName);
|
||||
}
|
||||
// Removed placeholder, keeping the implementation to avoid placeholder breaking for users on update
|
||||
manager.registerPlayerPlaceholder(TabConstants.Placeholder.ESSENTIALS_NICK, -1, TabPlayer::getName);
|
||||
|
||||
if (chat != null) {
|
||||
manager.registerPlayerPlaceholder(TabConstants.Placeholder.VAULT_PREFIX, 1000, p -> chat.getPlayerPrefix((Player) p.getPlayer()));
|
||||
@@ -113,8 +106,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,22 +1,26 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import com.earth2me.essentials.Essentials;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import me.clip.placeholderapi.PlaceholderAPI;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
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.api.util.ReflectionUtils;
|
||||
import me.neznamy.tab.platforms.bukkit.features.BukkitTabExpansion;
|
||||
import me.neznamy.tab.platforms.bukkit.features.PerWorldPlayerList;
|
||||
import me.neznamy.tab.platforms.bukkit.features.PetFix;
|
||||
import me.neznamy.tab.platforms.bukkit.features.WitherBossBar;
|
||||
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.backend.BackendPlatform;
|
||||
import me.neznamy.tab.shared.features.PipelineInjector;
|
||||
import me.neznamy.tab.shared.features.PlaceholderManagerImpl;
|
||||
import me.neznamy.tab.shared.features.TabExpansion;
|
||||
import me.neznamy.tab.shared.features.bossbar.BossBarManagerImpl;
|
||||
import me.neznamy.tab.shared.features.nametags.NameTag;
|
||||
import me.neznamy.tab.shared.permission.LuckPerms;
|
||||
@@ -29,21 +33,21 @@ import org.bukkit.entity.Player;
|
||||
import org.bukkit.plugin.Plugin;
|
||||
import org.bukkit.plugin.RegisteredServiceProvider;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Implementation of Platform interface for Bukkit platform
|
||||
*/
|
||||
public class BukkitPlatform extends Platform {
|
||||
public class BukkitPlatform extends BackendPlatform {
|
||||
|
||||
/** Plugin instance for registering tasks and events */
|
||||
private final JavaPlugin plugin;
|
||||
|
||||
/** 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);
|
||||
@Getter @Setter private boolean libsDisguisesEnabled = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.LIBS_DISGUISES);
|
||||
private Plugin viaVersion;
|
||||
private final boolean protocolSupport = Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.PROTOCOL_SUPPORT);
|
||||
|
||||
@@ -56,6 +60,15 @@ public class BukkitPlatform extends Platform {
|
||||
public BukkitPlatform(JavaPlugin plugin) {
|
||||
super(new BukkitPacketBuilder());
|
||||
this.plugin = plugin;
|
||||
if (Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.VIAVERSION)) {
|
||||
if (ReflectionUtils.classExists("com.viaversion.viaversion.api.Via")) {
|
||||
viaVersion = Bukkit.getPluginManager().getPlugin(TabConstants.Plugin.VIAVERSION);
|
||||
} else {
|
||||
TAB.getInstance().sendConsoleMessage("&cAn outdated version of ViaVersion (" + getPluginVersion(TabConstants.Plugin.VIAVERSION) + ") was detected.", true);
|
||||
TAB.getInstance().sendConsoleMessage("&cTAB only supports ViaVersion 4.0.0 and above. Disabling ViaVersion hook.", true);
|
||||
TAB.getInstance().sendConsoleMessage("&cThis might cause problems, such as limitations still being present for latest MC clients as well as RGB not working.", true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -71,49 +84,8 @@ public class BukkitPlatform extends Platform {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFeatures() {
|
||||
if (Bukkit.getPluginManager().isPluginEnabled(TabConstants.Plugin.VIAVERSION)) {
|
||||
try {
|
||||
Class.forName("com.viaversion.viaversion.api.Via");
|
||||
viaVersion = Bukkit.getPluginManager().getPlugin(TabConstants.Plugin.VIAVERSION);
|
||||
} catch (ClassNotFoundException e) {
|
||||
TAB.getInstance().sendConsoleMessage("&cAn outdated version of ViaVersion (" + getPluginVersion(TabConstants.Plugin.VIAVERSION) + ") was detected.", true);
|
||||
TAB.getInstance().sendConsoleMessage("&cTAB only supports ViaVersion 4.0.0 and above. Disabling ViaVersion hook.", true);
|
||||
TAB.getInstance().sendConsoleMessage("&cThis might cause problems, such as limitations still being present for latest MC clients as well as RGB not working.", true);
|
||||
}
|
||||
}
|
||||
TAB tab = TAB.getInstance();
|
||||
if (tab.getConfiguration().isPipelineInjection())
|
||||
tab.getFeatureManager().registerFeature(TabConstants.Feature.PIPELINE_INJECTION, new BukkitPipelineInjector());
|
||||
new BukkitPlaceholderRegistry(plugin).registerPlaceholders(tab.getPlaceholderManager());
|
||||
if (tab.getConfiguration().getConfig().getBoolean("scoreboard-teams.enabled", true)) {
|
||||
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 {
|
||||
tab.getFeatureManager().registerFeature(TabConstants.Feature.NAME_TAGS, new NameTag());
|
||||
}
|
||||
}
|
||||
tab.loadUniversalFeatures();
|
||||
if (tab.getConfiguration().getConfig().getBoolean("bossbar.enabled", false)) {
|
||||
if (tab.getServerVersion().getMinorVersion() < 9) {
|
||||
tab.getFeatureManager().registerFeature(TabConstants.Feature.BOSS_BAR, new WitherBossBar(plugin));
|
||||
} else {
|
||||
tab.getFeatureManager().registerFeature(TabConstants.Feature.BOSS_BAR, new BossBarManagerImpl());
|
||||
}
|
||||
}
|
||||
if (tab.getServerVersion().getMinorVersion() >= 9 && tab.getConfiguration().getConfig().getBoolean("fix-pet-names.enabled", false))
|
||||
tab.getFeatureManager().registerFeature(TabConstants.Feature.PET_FIX, new PetFix());
|
||||
if (tab.getConfiguration().getConfig().getBoolean("per-world-playerlist.enabled", false))
|
||||
tab.getFeatureManager().registerFeature(TabConstants.Feature.PER_WORLD_PLAYER_LIST, new PerWorldPlayerList(plugin));
|
||||
if (placeholderAPI && tab.getConfiguration().getConfig().getBoolean("placeholders.register-tab-expansion", true)) {
|
||||
BukkitTabExpansion expansion = new BukkitTabExpansion();
|
||||
expansion.register();
|
||||
TAB.getInstance().getPlaceholderManager().setTabExpansion(expansion);
|
||||
}
|
||||
for (Player p : getOnlinePlayers()) {
|
||||
tab.addPlayer(new BukkitTabPlayer(p, getProtocolVersion(p)));
|
||||
}
|
||||
public BossBarManagerImpl getLegacyBossBar() {
|
||||
return new WitherBossBar(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -122,6 +94,48 @@ public class BukkitPlatform extends Platform {
|
||||
return pl == null ? null : pl.getDescription().getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadPlayers() {
|
||||
for (Player p : getOnlinePlayers()) {
|
||||
TAB.getInstance().addPlayer(new BukkitTabPlayer(p, getProtocolVersion(p)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerPlaceholders() {
|
||||
new BukkitPlaceholderRegistry().registerPlaceholders(TAB.getInstance().getPlaceholderManager());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable PipelineInjector getPipelineInjector() {
|
||||
return new BukkitPipelineInjector();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NameTag getUnlimitedNametags() {
|
||||
return new BukkitNameTagX(plugin);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TabExpansion getTabExpansion() {
|
||||
if (placeholderAPI) {
|
||||
BukkitTabExpansion expansion = new BukkitTabExpansion();
|
||||
expansion.register();
|
||||
return expansion;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TabFeature getPetFix() {
|
||||
return new PetFix();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable TabFeature getPerWorldPlayerlist() {
|
||||
return new PerWorldPlayerList(plugin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns online players from Bukkit API
|
||||
*
|
||||
@@ -182,51 +196,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
|
||||
*
|
||||
* @return {@code true} if plugin is enabled, {@code false} if not
|
||||
*/
|
||||
public boolean isLibsDisguisesEnabled() {
|
||||
return libsDisguises;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets LibsDisguises presence status to provided value. This is used
|
||||
* to disable LibsDisguises hook in case the plugin is not correctly loaded
|
||||
* for any reason to avoid error spam in the hook.
|
||||
*
|
||||
* @param enabled
|
||||
* New status of LibsDisguises presence
|
||||
*/
|
||||
public void setLibsDisguisesEnabled(boolean enabled) {
|
||||
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 +267,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 +276,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));
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,20 @@
|
||||
package me.neznamy.tab.platforms.bukkit;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.authlib.properties.Property;
|
||||
import com.viaversion.viaversion.api.Via;
|
||||
import com.viaversion.viaversion.api.legacy.bossbar.BossColor;
|
||||
import com.viaversion.viaversion.api.legacy.bossbar.BossFlag;
|
||||
import com.viaversion.viaversion.api.legacy.bossbar.BossStyle;
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.chat.rgb.RGBUtils;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutBoss;
|
||||
import me.neznamy.tab.api.protocol.PacketPlayOutChat;
|
||||
import me.neznamy.tab.api.protocol.Skin;
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
|
||||
import me.neznamy.tab.shared.ITabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.boss.BarColor;
|
||||
import org.bukkit.boss.BarFlag;
|
||||
@@ -16,19 +25,10 @@ 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;
|
||||
import com.viaversion.viaversion.api.legacy.bossbar.BossFlag;
|
||||
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.shared.ITabPlayer;
|
||||
import me.neznamy.tab.shared.TAB;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* TabPlayer implementation for Bukkit platform
|
||||
@@ -56,7 +56,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);
|
||||
@@ -66,8 +66,7 @@ public class BukkitTabPlayer extends ITabPlayer {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission(String permission) {
|
||||
Preconditions.checkNotNull(permission, "permission");
|
||||
public boolean hasPermission(@NonNull String permission) {
|
||||
long time = System.nanoTime();
|
||||
boolean value = getPlayer().hasPermission(permission);
|
||||
TAB.getInstance().getCPUManager().addMethodTime("hasPermission", System.nanoTime()-time);
|
||||
@@ -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")
|
||||
@@ -289,19 +285,6 @@ public class BukkitTabPlayer extends ITabPlayer {
|
||||
return getPlayer().getGameMode().getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to get profile key 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;
|
||||
}
|
||||
}
|
||||
+12
-4
@@ -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());
|
||||
|
||||
+19
-7
@@ -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
|
||||
|
||||
+79
-272
@@ -1,106 +1,54 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
|
||||
|
||||
import me.neznamy.tab.api.*;
|
||||
import me.neznamy.tab.api.chat.EnumChatFormat;
|
||||
import me.neznamy.tab.api.chat.IChatBaseComponent;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
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.shared.backend.features.unlimitedtags.BackendArmorStand;
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendArmorStandManager;
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendNameTagX;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.EntityType;
|
||||
import org.bukkit.entity.Player;
|
||||
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 {
|
||||
public class BukkitArmorStand extends BackendArmorStand {
|
||||
|
||||
//entity id counter to pick unique entity IDs
|
||||
private static int idCounter = 2000000000;
|
||||
|
||||
//NameTag feature
|
||||
private final BukkitNameTagX manager = (BukkitNameTagX) TabAPI.getInstance().getFeatureManager().getFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS);
|
||||
|
||||
private final BukkitArmorStandManager asm;
|
||||
|
||||
//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
|
||||
private double yOffset;
|
||||
/** Entity destroy packet */
|
||||
protected TabPacket destroyPacket = new PacketPlayOutEntityDestroy(entityId);
|
||||
|
||||
//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
|
||||
private final boolean staticOffset;
|
||||
|
||||
//entity destroy packet
|
||||
private final PacketPlayOutEntityDestroy destroyPacket = new PacketPlayOutEntityDestroy(entityId);
|
||||
|
||||
public BukkitArmorStand(BukkitArmorStandManager asm, TabPlayer owner, String propertyName, double yOffset, boolean staticOffset) {
|
||||
this.asm = asm;
|
||||
this.owner = owner;
|
||||
this.staticOffset = staticOffset;
|
||||
/**
|
||||
* 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(BackendNameTagX feature, BackendArmorStandManager asm, TabPlayer owner, String propertyName, double yOffset, boolean staticOffset) {
|
||||
super(feature, asm, owner, propertyName, yOffset, staticOffset);
|
||||
player = (Player) owner.getPlayer();
|
||||
this.yOffset = yOffset;
|
||||
this.property = owner.getProperty(propertyName);
|
||||
visible = getVisibility();
|
||||
sneaking = player.isSneaking();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void refresh() {
|
||||
visible = getVisibility();
|
||||
updateMetadata();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Property getProperty() {
|
||||
return property;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasStaticOffset() {
|
||||
return staticOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getOffset() {
|
||||
return yOffset;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOffset(double offset) {
|
||||
if (yOffset == offset) return;
|
||||
yOffset = offset;
|
||||
for (TabPlayer all : asm.getNearbyPlayers()) {
|
||||
all.sendCustomPacket(getTeleportPacket(all), TabConstants.PacketCategory.UNLIMITED_NAMETAGS_OFFSET_CHANGE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void spawn(TabPlayer viewer) {
|
||||
for (TabPacket packet : getSpawnPackets(viewer)) {
|
||||
@@ -108,158 +56,84 @@ public class BukkitArmorStand implements ArmorStand {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
for (TabPlayer all : asm.getNearbyPlayers()) all.sendCustomPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_DESPAWN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy(TabPlayer viewer) {
|
||||
viewer.sendCustomPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_DESPAWN);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void teleport() {
|
||||
for (TabPlayer all : asm.getNearbyPlayers()) {
|
||||
all.sendCustomPacket(getTeleportPacket(all), TabConstants.PacketCategory.UNLIMITED_NAMETAGS_TELEPORT);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void teleport(TabPlayer viewer) {
|
||||
if (!asm.isNearby(viewer) && viewer != owner) {
|
||||
asm.spawn(viewer);
|
||||
} else {
|
||||
viewer.sendCustomPacket(getTeleportPacket(viewer), TabConstants.PacketCategory.UNLIMITED_NAMETAGS_TELEPORT);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sneak(boolean sneaking) {
|
||||
if (this.sneaking == sneaking) return; //idk
|
||||
this.sneaking = sneaking;
|
||||
for (TabPlayer viewer : asm.getNearbyPlayers()) {
|
||||
if (viewer.getVersion().getMinorVersion() == 14 && !manager.isArmorStandsAlwaysVisible()) {
|
||||
//1.14.x client sided bug, de-spawning completely
|
||||
if (sneaking) {
|
||||
viewer.sendCustomPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_SNEAK);
|
||||
} else {
|
||||
spawn(viewer);
|
||||
}
|
||||
} else {
|
||||
//respawning so there's no animation and it's instant
|
||||
respawn(viewer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateVisibility(boolean force) {
|
||||
boolean visibility = getVisibility();
|
||||
if (visible != visibility || force) {
|
||||
refresh();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns teleport packet for specified viewer
|
||||
*
|
||||
* @param viewer
|
||||
* player to get location for
|
||||
* @return teleport packet
|
||||
*/
|
||||
public PacketPlayOutEntityTeleport getTeleportPacket(TabPlayer viewer) {
|
||||
return new PacketPlayOutEntityTeleport(entityId, getArmorStandLocationFor(viewer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates armor stand's metadata
|
||||
*/
|
||||
public void updateMetadata() {
|
||||
for (TabPlayer viewer : asm.getNearbyPlayers()) {
|
||||
viewer.sendCustomPacket(new PacketPlayOutEntityMetadata(entityId, createDataWatcher(property.getFormat(viewer), viewer)), TabConstants.PacketCategory.UNLIMITED_NAMETAGS_METADATA);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns general visibility rule for everyone with limited info
|
||||
*
|
||||
* @return true if armor stand should be visible, false if not
|
||||
*/
|
||||
public boolean getVisibility() {
|
||||
if (manager.isArmorStandsAlwaysVisible()) return true;
|
||||
if (owner.isDisguised() || manager.getVehicleManager().isOnBoat(owner)) return false;
|
||||
return !owner.hasInvisibilityPotion() && owner.getGamemode() != 3 && !manager.hasHiddenNametag(owner) && property.get().length() > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
double y = getY() + yOffset;
|
||||
double z = player.getLocation().getZ();
|
||||
if (!player.isSleeping()) {
|
||||
if (sneaking) {
|
||||
if (viewer.getVersion().getMinorVersion() >= 15) {
|
||||
y += 1.37;
|
||||
} else if (viewer.getVersion().getMinorVersion() >= 9) {
|
||||
y += 1.52;
|
||||
} else {
|
||||
y += 1.7;
|
||||
}
|
||||
} else {
|
||||
y += viewer.getVersion().getMinorVersion() >= 9 ? 1.8 : 1.84; // Normal
|
||||
}
|
||||
} else {
|
||||
y += viewer.getVersion().getMinorVersion() >= 9 ? 0.2 : 0.26; // Sleeping
|
||||
}
|
||||
return new Location(null,x,y,z);
|
||||
}
|
||||
Location loc = player.getLocation();
|
||||
double y = loc.getY();
|
||||
|
||||
/**
|
||||
* Returns Y where player is based on player's vehicle due to bukkit API bug
|
||||
*
|
||||
* @return correct player's Y
|
||||
*/
|
||||
protected double getY() {
|
||||
//1.14+ server sided bug
|
||||
Entity vehicle = player.getVehicle();
|
||||
if (vehicle != null) {
|
||||
if (vehicle.getType().toString().contains("HORSE")) { //covering all 3 horse types
|
||||
return vehicle.getLocation().getY() + 0.85;
|
||||
y = vehicle.getLocation().getY() + 0.85;
|
||||
}
|
||||
if (vehicle.getType().toString().equals("DONKEY")) { //1.11+
|
||||
return vehicle.getLocation().getY() + 0.525;
|
||||
y = vehicle.getLocation().getY() + 0.525;
|
||||
}
|
||||
if (vehicle.getType() == EntityType.PIG) {
|
||||
return vehicle.getLocation().getY() + 0.325;
|
||||
y = vehicle.getLocation().getY() + 0.325;
|
||||
}
|
||||
if (vehicle.getType().toString().equals("STRIDER")) { //1.16+
|
||||
return vehicle.getLocation().getY() + 1.15;
|
||||
y = vehicle.getLocation().getY() + 1.15;
|
||||
}
|
||||
} else {
|
||||
//1.13+ swimming or 1.9+ flying with elytra
|
||||
if (isSwimming() || (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 9 && player.isGliding())) {
|
||||
y = loc.getY()-1.22;
|
||||
}
|
||||
}
|
||||
//1.13+ swimming or 1.9+ flying with elytra
|
||||
if (isSwimming() || (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 9 && player.isGliding())) {
|
||||
return player.getLocation().getY()-1.22;
|
||||
}
|
||||
return player.getLocation().getY();
|
||||
y += getYAdd(player.isSleeping(), sneaking, viewer);
|
||||
return new Location(null, loc.getX(), y, loc.getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateMetadata(TabPlayer viewer) {
|
||||
viewer.sendCustomPacket(new PacketPlayOutEntityMetadata(entityId, createDataWatcher(property.getFormat(viewer), viewer)), TabConstants.PacketCategory.UNLIMITED_NAMETAGS_METADATA);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendTeleportPacket(TabPlayer viewer) {
|
||||
viewer.sendCustomPacket(new PacketPlayOutEntityTeleport(entityId, getLocation(viewer)),
|
||||
TabConstants.PacketCategory.UNLIMITED_NAMETAGS_TELEPORT);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
private TabPacket[] getSpawnPackets(TabPlayer viewer) {
|
||||
visible = calculateVisibility();
|
||||
DataWatcher dataWatcher = createDataWatcher(property.getFormat(viewer), viewer);
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 15) {
|
||||
return new TabPacket[] {
|
||||
new PacketPlayOutSpawnEntityLiving(entityId, uuid, EntityType.ARMOR_STAND, getLocation(viewer), null),
|
||||
new PacketPlayOutEntityMetadata(entityId, dataWatcher)
|
||||
};
|
||||
} else {
|
||||
return new TabPacket[] {
|
||||
new PacketPlayOutSpawnEntityLiving(entityId, uuid, EntityType.ARMOR_STAND, getLocation(viewer), dataWatcher),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates data watcher with specified display name for viewer
|
||||
*
|
||||
@@ -271,78 +145,11 @@ public class BukkitArmorStand implements ArmorStand {
|
||||
*/
|
||||
public DataWatcher createDataWatcher(String displayName, TabPlayer viewer) {
|
||||
DataWatcher datawatcher = new DataWatcher();
|
||||
|
||||
byte flag = 32; //invisible
|
||||
if (sneaking) flag += (byte)2;
|
||||
datawatcher.helper().setEntityFlags(flag);
|
||||
datawatcher.helper().setCustomName(displayName, viewer.getVersion());
|
||||
|
||||
boolean visibility;
|
||||
if (isNameVisiblyEmpty(displayName) || !((Player) viewer.getPlayer()).canSee(player) ||
|
||||
manager.hasHiddenNametag(owner, viewer) || manager.hasHiddenNameTagVisibilityView(viewer)) {
|
||||
visibility = false;
|
||||
} else {
|
||||
visibility = visible;
|
||||
}
|
||||
datawatcher.helper().setCustomNameVisible(visibility);
|
||||
|
||||
if (viewer.getVersion().getMinorVersion() > 8 || manager.isMarkerFor18x()) datawatcher.helper().setArmorStandFlags((byte)16);
|
||||
datawatcher.getHelper().setEntityFlags((byte) (sneaking ? 34 : 32));
|
||||
datawatcher.getHelper().setCustomName(displayName, viewer.getVersion());
|
||||
datawatcher.getHelper().setCustomNameVisible(!shouldBeInvisibleFor(viewer, displayName) && visible);
|
||||
if (viewer.getVersion().getMinorVersion() > 8 || manager.isMarkerFor18x())
|
||||
datawatcher.getHelper().setArmorStandFlags((byte)16);
|
||||
return datawatcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if display name is in fact empty, for example only containing color codes
|
||||
*
|
||||
* @param displayName
|
||||
* string to check
|
||||
* @return true if it's empty, false if not
|
||||
*/
|
||||
private boolean isNameVisiblyEmpty(String displayName) {
|
||||
if (displayName.length() == 0) return true;
|
||||
if (!displayName.startsWith(EnumChatFormat.COLOR_STRING) && !displayName.startsWith("&") && !displayName.startsWith("#")) return false;
|
||||
String text = IChatBaseComponent.fromColoredText(displayName).toRawText();
|
||||
if (text.contains(" ")) text = text.replace(" ", "");
|
||||
return text.length() == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns list of packets to send to make armor stand spawn with metadata
|
||||
*
|
||||
* @param viewer
|
||||
* viewer to apply relational placeholders for
|
||||
* @return List of packets that spawn the armor stand
|
||||
*/
|
||||
public TabPacket[] getSpawnPackets(TabPlayer viewer) {
|
||||
visible = getVisibility();
|
||||
DataWatcher dataWatcher = createDataWatcher(property.getFormat(viewer), viewer);
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 15) {
|
||||
return new TabPacket[] {
|
||||
new PacketPlayOutSpawnEntityLiving(entityId, uuid, EntityType.ARMOR_STAND, getArmorStandLocationFor(viewer), null),
|
||||
new PacketPlayOutEntityMetadata(entityId, dataWatcher)
|
||||
};
|
||||
} else {
|
||||
return new TabPacket[] {
|
||||
new PacketPlayOutSpawnEntityLiving(entityId, uuid, EntityType.ARMOR_STAND, getArmorStandLocationFor(viewer), dataWatcher),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns location where armor stand should be for specified viewer
|
||||
*
|
||||
* @param viewer
|
||||
* player to get location for
|
||||
* @return location of armor stand
|
||||
*/
|
||||
public Location getArmorStandLocationFor(TabPlayer viewer) {
|
||||
return viewer.getVersion().getMinorVersion() == 8 && !manager.isMarkerFor18x() ? getLocation(viewer).add(0,-2,0) : getLocation(viewer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void respawn(TabPlayer viewer) {
|
||||
viewer.sendCustomPacket(destroyPacket, TabConstants.PacketCategory.UNLIMITED_NAMETAGS_DESPAWN);
|
||||
// 1.8.0 will not see entity that respawned in the same tick
|
||||
// creating new delayed task every time someone sneaks can be abused and cause OOM
|
||||
spawn(viewer);
|
||||
}
|
||||
}
|
||||
+109
-126
@@ -3,167 +3,150 @@ package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
import me.neznamy.tab.shared.features.nametags.unlimited.NameTagX;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendArmorStand;
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendArmorStandManager;
|
||||
import me.neznamy.tab.shared.backend.features.unlimitedtags.BackendNameTagX;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.event.HandlerList;
|
||||
import org.bukkit.plugin.java.JavaPlugin;
|
||||
|
||||
/**
|
||||
* The core class for unlimited NameTag mode
|
||||
*/
|
||||
public class BukkitNameTagX extends NameTagX {
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
//bukkit event listener
|
||||
/**
|
||||
* The core class for unlimited NameTag mode on Bukkit
|
||||
*/
|
||||
public class BukkitNameTagX extends BackendNameTagX {
|
||||
|
||||
/** Reference to NMS storage for quick access */
|
||||
private final NMSStorage nms = NMSStorage.getInstance();
|
||||
|
||||
/** Bukkit event listener */
|
||||
private final EventListener eventListener = new EventListener(this);
|
||||
|
||||
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
|
||||
*/
|
||||
public BukkitNameTagX(JavaPlugin plugin) {
|
||||
super(BukkitArmorStandManager::new);
|
||||
Bukkit.getPluginManager().registerEvents(eventListener, plugin);
|
||||
TabAPI.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_PACKET_LISTENER, new PacketListener(this));
|
||||
TabAPI.getInstance().getFeatureManager().registerFeature(TabConstants.Feature.UNLIMITED_NAME_TAGS_VEHICLE_REFRESHER, vehicleManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
super.load();
|
||||
for (TabPlayer all : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
if (isPlayerDisabled(all)) continue;
|
||||
for (TabPlayer viewer : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
spawnArmorStands(viewer, all);
|
||||
public boolean onPacketReceive(TabPlayer sender, Object packet) throws ReflectiveOperationException {
|
||||
if (sender.getVersion().getMinorVersion() == 8 && nms.PacketPlayInUseEntity.isInstance(packet)) {
|
||||
int entityId = nms.PacketPlayInUseEntity_ENTITY.getInt(packet);
|
||||
TabPlayer attacked = null;
|
||||
for (TabPlayer all : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
if (all.isLoaded() && getArmorStandManager(all).hasArmorStandWithID(entityId)) {
|
||||
attacked = all;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (attacked != null && attacked != sender) {
|
||||
nms.setField(packet, nms.PacketPlayInUseEntity_ENTITY, getEntityId(attacked));
|
||||
}
|
||||
}
|
||||
startVisibilityRefreshTask();
|
||||
}
|
||||
|
||||
private void startVisibilityRefreshTask() {
|
||||
TabAPI.getInstance().getThreadManager().startRepeatingMeasuredTask(500, this, TabConstants.CpuUsageCategory.REFRESHING_NAME_TAG_VISIBILITY, () -> {
|
||||
|
||||
for (TabPlayer p : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
if (isPlayerDisabled(p)) continue;
|
||||
getArmorStandManager(p).updateVisibility(false);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void unload() {
|
||||
super.unload();
|
||||
HandlerList.unregisterAll(eventListener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onJoin(TabPlayer connectedPlayer) {
|
||||
super.onJoin(connectedPlayer);
|
||||
if (isPlayerDisabled(connectedPlayer)) return;
|
||||
for (TabPlayer viewer : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
spawnArmorStands(viewer, connectedPlayer);
|
||||
spawnArmorStands(connectedPlayer, viewer);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOnBoat(TabPlayer player) {
|
||||
return vehicleManager != null && vehicleManager.isOnBoat(player);
|
||||
}
|
||||
|
||||
private void spawnArmorStands(TabPlayer viewer, TabPlayer target) {
|
||||
if (viewer.getVersion().getMinorVersion() < 8) return;
|
||||
if (target == viewer || isPlayerDisabled(target)) return;
|
||||
if (((Player) viewer.getPlayer()).getWorld() != ((Player) target.getPlayer()).getWorld()) return;
|
||||
if (getDistance(viewer, target) <= 48 && ((Player)viewer.getPlayer()).canSee((Player)target.getPlayer()) && !target.isVanished())
|
||||
getArmorStandManager(target).spawn(viewer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNameTagPreview(TabPlayer player, boolean status) {
|
||||
if (status) {
|
||||
getArmorStandManager(player).spawn(player);
|
||||
} else {
|
||||
getArmorStandManager(player).destroy(player);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resumeArmorStands(TabPlayer player) {
|
||||
if (isPlayerDisabled(player)) return;
|
||||
for (TabPlayer viewer : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
spawnArmorStands(viewer, player);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pauseArmorStands(TabPlayer player) {
|
||||
getArmorStandManager(player).destroy();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateNameTagVisibilityView(TabPlayer player) {
|
||||
for (TabPlayer all : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
getArmorStandManager(all).updateVisibility(true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuit(TabPlayer disconnectedPlayer) {
|
||||
super.onQuit(disconnectedPlayer);
|
||||
for (TabPlayer all : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
getArmorStandManager(all).unregisterPlayer(disconnectedPlayer);
|
||||
}
|
||||
armorStandManagerMap.get(disconnectedPlayer).destroy();
|
||||
armorStandManagerMap.remove(disconnectedPlayer); // WeakHashMap doesn't clear this due to value referencing the key
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWorldChange(TabPlayer p, String from, String to) {
|
||||
super.onWorldChange(p, from, to);
|
||||
if (isUnlimitedDisabled(p.getServer(), to)) {
|
||||
getDisabledUnlimitedPlayers().add(p);
|
||||
updateTeamData(p);
|
||||
} else if (getDisabledUnlimitedPlayers().remove(p)) {
|
||||
updateTeamData(p);
|
||||
}
|
||||
if (isPreviewingNametag(p)) {
|
||||
getArmorStandManager(p).spawn(p);
|
||||
}
|
||||
//for some reason this is needed for some users
|
||||
for (TabPlayer viewer : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
if (viewer.getWorld().equals(from)) {
|
||||
getArmorStandManager(p).destroy(viewer);
|
||||
public void onPacketSend(TabPlayer receiver, Object packet) throws ReflectiveOperationException {
|
||||
if (receiver.getVersion().getMinorVersion() < 8) return;
|
||||
if (!receiver.isLoaded() || isDisabledPlayer(receiver) || getDisabledUnlimitedPlayers().contains(receiver)) return;
|
||||
if (nms.PacketPlayOutEntity.isInstance(packet) && !nms.PacketPlayOutEntityLook.isInstance(packet)) { //ignoring head rotation only packets
|
||||
packetListener.onEntityMove(receiver, nms.PacketPlayOutEntity_ENTITYID.getInt(packet));
|
||||
} else if (nms.PacketPlayOutEntityTeleport.isInstance(packet)) {
|
||||
packetListener.onEntityMove(receiver, nms.PacketPlayOutEntityTeleport_ENTITYID.getInt(packet));
|
||||
} else if (nms.PacketPlayOutNamedEntitySpawn.isInstance(packet)) {
|
||||
packetListener.onEntitySpawn(receiver, nms.PacketPlayOutNamedEntitySpawn_ENTITYID.getInt(packet));
|
||||
} else if (nms.PacketPlayOutEntityDestroy.isInstance(packet)) {
|
||||
if (nms.getMinorVersion() >= 17) {
|
||||
Object entities = nms.PacketPlayOutEntityDestroy_ENTITIES.get(packet);
|
||||
if (entities instanceof List) {
|
||||
packetListener.onEntityDestroy(receiver, (List<Integer>) entities);
|
||||
} else {
|
||||
//1.17.0
|
||||
packetListener.onEntityDestroy(receiver, (int) entities);
|
||||
}
|
||||
} else {
|
||||
packetListener.onEntityDestroy(receiver, (int[]) nms.PacketPlayOutEntityDestroy_ENTITIES.get(packet));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns flat distance between two players ignoring Y value
|
||||
*
|
||||
* @param player1
|
||||
* first player
|
||||
* @param player2
|
||||
* second player
|
||||
* @return flat distance in blocks
|
||||
*/
|
||||
private double getDistance(TabPlayer player1, TabPlayer player2) {
|
||||
@Override
|
||||
public double getDistance(TabPlayer player1, TabPlayer player2) {
|
||||
Location loc1 = ((Player) player1.getPlayer()).getLocation();
|
||||
Location loc2 = ((Player) player2.getPlayer()).getLocation();
|
||||
return Math.sqrt(Math.pow(loc1.getX()-loc2.getX(), 2) + Math.pow(loc1.getZ()-loc2.getZ(), 2));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BukkitArmorStandManager getArmorStandManager(TabPlayer player) {
|
||||
return (BukkitArmorStandManager) armorStandManagerMap.get(player);
|
||||
public boolean areInSameWorld(TabPlayer player1, TabPlayer player2) {
|
||||
return ((Player) player1.getPlayer()).getWorld() == ((Player) player1.getPlayer()).getWorld();
|
||||
}
|
||||
|
||||
public VehicleRefresher getVehicleManager() {
|
||||
return vehicleManager;
|
||||
@Override
|
||||
public boolean canSee(TabPlayer viewer, TabPlayer target) {
|
||||
return ((Player)viewer.getPlayer()).canSee((Player)target.getPlayer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unregisterListener() {
|
||||
HandlerList.unregisterAll(eventListener);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public List<Integer> getPassengers(Object entity) {
|
||||
Entity vehicle = (Entity) entity;
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 11) {
|
||||
return vehicle.getPassengers().stream().map(Entity::getEntityId).collect(Collectors.toList());
|
||||
} else {
|
||||
if (vehicle.getPassenger() != null) {
|
||||
return Collections.singletonList(vehicle.getPassenger().getEntityId());
|
||||
} else {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerVehiclePlaceholder() {
|
||||
TabAPI.getInstance().getPlaceholderManager().registerPlayerPlaceholder(TabConstants.Placeholder.VEHICLE, 100, p -> {
|
||||
Entity v = ((Player)p.getPlayer()).getVehicle();
|
||||
//There's a bug in Bukkit 1.19.3 throwing NPE on .toString(), use default toString implementation
|
||||
return v == null ? "" : v.getClass().getName() + "@" + Integer.toHexString(v.hashCode());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getVehicle(TabPlayer player) {
|
||||
return ((Player)player.getPlayer()).getVehicle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getEntityId(Object entity) {
|
||||
return ((Entity)entity).getEntityId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getEntityType(Object entity) {
|
||||
return ((Entity)entity).getType().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public BackendArmorStand createArmorStand(BackendArmorStandManager feature, TabPlayer owner, String lineName, double yOffset, boolean staticOffset) {
|
||||
return new BukkitArmorStand(this, feature, owner, lineName, yOffset, staticOffset);
|
||||
}
|
||||
}
|
||||
+5
-12
@@ -1,5 +1,6 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
@@ -10,23 +11,15 @@ 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.
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
public class EventListener implements Listener {
|
||||
|
||||
//the NameTag feature handler
|
||||
/** Reference to the main feature */
|
||||
private final BukkitNameTagX feature;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters
|
||||
*
|
||||
* @param feature
|
||||
* NameTag feature handler
|
||||
*/
|
||||
public EventListener(BukkitNameTagX feature) {
|
||||
this.feature = feature;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sneak event listener to de-spawn and spawn armor stands to skip animation
|
||||
*
|
||||
|
||||
-158
@@ -1,158 +0,0 @@
|
||||
package me.neznamy.tab.platforms.bukkit.features.unlimitedtags;
|
||||
|
||||
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 org.bukkit.entity.Entity;
|
||||
import org.bukkit.entity.Player;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* The packet listening part for securing proper functionality of armor stands
|
||||
* Bukkit events are too unreliable and delayed/ahead which causes de-sync
|
||||
*/
|
||||
public class PacketListener extends TabFeature {
|
||||
|
||||
//main feature
|
||||
private final BukkitNameTagX nameTagX;
|
||||
|
||||
//player data by entityId, used for better performance
|
||||
private final Map<Integer, TabPlayer> entityIdMap = new ConcurrentHashMap<>();
|
||||
|
||||
//nms storage
|
||||
private final NMSStorage nms = NMSStorage.getInstance();
|
||||
|
||||
/**
|
||||
* Constructs new instance with given parameters and loads config options
|
||||
*
|
||||
* @param nameTagX
|
||||
* main feature
|
||||
*/
|
||||
public PacketListener(BukkitNameTagX nameTagX) {
|
||||
super(nameTagX.getFeatureName(), null);
|
||||
this.nameTagX = nameTagX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load() {
|
||||
for (TabPlayer all : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
entityIdMap.put(((Player) all.getPlayer()).getEntityId(), all);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onJoin(TabPlayer connectedPlayer) {
|
||||
entityIdMap.put(((Player) connectedPlayer.getPlayer()).getEntityId(), connectedPlayer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onQuit(TabPlayer disconnectedPlayer) {
|
||||
entityIdMap.remove(((Player) disconnectedPlayer.getPlayer()).getEntityId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPacketReceive(TabPlayer sender, Object packet) throws ReflectiveOperationException {
|
||||
if (sender.getVersion().getMinorVersion() == 8 && nms.PacketPlayInUseEntity.isInstance(packet)) {
|
||||
int entityId = nms.PacketPlayInUseEntity_ENTITY.getInt(packet);
|
||||
TabPlayer attacked = null;
|
||||
for (TabPlayer all : TabAPI.getInstance().getOnlinePlayers()) {
|
||||
if (all.isLoaded() && nameTagX.getArmorStandManager(all).hasArmorStandWithID(entityId)) {
|
||||
attacked = all;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (attacked != null && attacked != sender) {
|
||||
nms.setField(packet, nms.PacketPlayInUseEntity_ENTITY, ((Player) attacked.getPlayer()).getEntityId());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void onPacketSend(TabPlayer receiver, Object packet) throws ReflectiveOperationException {
|
||||
if (receiver.getVersion().getMinorVersion() < 8) return;
|
||||
if (!receiver.isLoaded() || nameTagX.isDisabledPlayer(receiver) || nameTagX.getDisabledUnlimitedPlayers().contains(receiver)) return;
|
||||
if (nms.PacketPlayOutEntity.isInstance(packet) && !nms.PacketPlayOutEntityLook.isInstance(packet)) { //ignoring head rotation only packets
|
||||
onEntityMove(receiver, nms.PacketPlayOutEntity_ENTITYID.getInt(packet));
|
||||
} else if (nms.PacketPlayOutEntityTeleport.isInstance(packet)) {
|
||||
onEntityMove(receiver, nms.PacketPlayOutEntityTeleport_ENTITYID.getInt(packet));
|
||||
} else if (nms.PacketPlayOutNamedEntitySpawn.isInstance(packet)) {
|
||||
onEntitySpawn(receiver, nms.PacketPlayOutNamedEntitySpawn_ENTITYID.getInt(packet));
|
||||
} else if (nms.PacketPlayOutEntityDestroy.isInstance(packet)) {
|
||||
if (nms.getMinorVersion() >= 17) {
|
||||
Object entities = nms.PacketPlayOutEntityDestroy_ENTITIES.get(packet);
|
||||
if (entities instanceof List) {
|
||||
onEntityDestroy(receiver, (List<Integer>) entities);
|
||||
} else {
|
||||
//1.17.0
|
||||
onEntityDestroy(receiver, (int) entities);
|
||||
}
|
||||
} else {
|
||||
onEntityDestroy(receiver, (int[]) nms.PacketPlayOutEntityDestroy_ENTITIES.get(packet));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes entity move packet
|
||||
*
|
||||
* @param receiver
|
||||
* packet receiver
|
||||
* @param entityId
|
||||
* entity that moved
|
||||
*/
|
||||
private void onEntityMove(TabPlayer receiver, int entityId) {
|
||||
TabPlayer pl = entityIdMap.get(entityId);
|
||||
List<Entity> vehicleList;
|
||||
if (pl != null) {
|
||||
//player moved
|
||||
if (nameTagX.isPlayerDisabled(pl) || !pl.isLoaded()) return;
|
||||
TabAPI.getInstance().getThreadManager().runMeasuredTask(nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_MOVE,
|
||||
() -> nameTagX.getArmorStandManager(pl).teleport(receiver));
|
||||
} else if ((vehicleList = nameTagX.getVehicleManager().getVehicles().get(entityId)) != null){
|
||||
//a vehicle carrying something moved
|
||||
for (Entity entity : vehicleList) {
|
||||
TabPlayer passenger = entityIdMap.get(entity.getEntityId());
|
||||
if (passenger != null && nameTagX.getArmorStandManager(passenger) != null) {
|
||||
TabAPI.getInstance().getThreadManager().runMeasuredTask(nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_MOVE_PASSENGER,
|
||||
() -> nameTagX.getArmorStandManager(passenger).teleport(receiver));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onEntitySpawn(TabPlayer receiver, int entityId) {
|
||||
TabPlayer spawnedPlayer = entityIdMap.get(entityId);
|
||||
if (spawnedPlayer != null && spawnedPlayer.isLoaded() && !nameTagX.isPlayerDisabled(spawnedPlayer)) {
|
||||
TabAPI.getInstance().getThreadManager().runMeasuredTask(nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_SPAWN,
|
||||
() -> nameTagX.getArmorStandManager(spawnedPlayer).spawn(receiver));
|
||||
}
|
||||
}
|
||||
|
||||
private void onEntityDestroy(TabPlayer receiver, List<Integer> entities) {
|
||||
for (int entity : entities) {
|
||||
onEntityDestroy(receiver, entity);
|
||||
}
|
||||
}
|
||||
|
||||
private void onEntityDestroy(TabPlayer receiver, int... entities) {
|
||||
for (int entity : entities) {
|
||||
onEntityDestroy(receiver, entity);
|
||||
}
|
||||
}
|
||||
|
||||
private void onEntityDestroy(TabPlayer receiver, int entity) {
|
||||
TabPlayer deSpawnedPlayer = entityIdMap.get(entity);
|
||||
if (deSpawnedPlayer != null && deSpawnedPlayer.isLoaded() && !nameTagX.isPlayerDisabled(deSpawnedPlayer)) {
|
||||
BukkitArmorStandManager asm = nameTagX.getArmorStandManager(deSpawnedPlayer);
|
||||
TabAPI.getInstance().getThreadManager().runMeasuredTask(nameTagX, TabConstants.CpuUsageCategory.PACKET_ENTITY_DESTROY,
|
||||
() -> asm.destroy(receiver));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
+12
-11
@@ -1,23 +1,24 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import lombok.Data;
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
|
||||
/**
|
||||
* Custom class for holding data used in PacketPlayOutEntityDestroy minecraft packet.
|
||||
*/
|
||||
@Data
|
||||
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;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutEntityDestroy{entities=%s}", Arrays.toString(entities));
|
||||
}
|
||||
|
||||
public int[] getEntities() {
|
||||
return entities;
|
||||
}
|
||||
}
|
||||
+9
-21
@@ -1,31 +1,19 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms;
|
||||
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
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.
|
||||
*/
|
||||
@Data
|
||||
public class PacketPlayOutEntityMetadata implements TabPacket {
|
||||
|
||||
/** Entity's id */
|
||||
private final int entityId;
|
||||
|
||||
private final DataWatcher dataWatcher;
|
||||
|
||||
public PacketPlayOutEntityMetadata(int entityId, DataWatcher dataWatcher) {
|
||||
Preconditions.checkNotNull(dataWatcher, "data watcher");
|
||||
this.entityId = entityId;
|
||||
this.dataWatcher = dataWatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutEntityMetadata{entityId=%s,dataWatcher=%s}", entityId, dataWatcher);
|
||||
}
|
||||
|
||||
public int getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
public DataWatcher getDataWatcher() {
|
||||
return dataWatcher;
|
||||
}
|
||||
/** Entity's data that changed */
|
||||
@NonNull private final DataWatcher dataWatcher;
|
||||
}
|
||||
+10
-23
@@ -1,32 +1,19 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms;
|
||||
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
import org.bukkit.Location;
|
||||
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
|
||||
/**
|
||||
* Custom class for holding data used in PacketPlayOutEntityTeleport minecraft packet.
|
||||
*/
|
||||
@Data
|
||||
public class PacketPlayOutEntityTeleport implements TabPacket {
|
||||
|
||||
/** Entity's id */
|
||||
private final int entityId;
|
||||
|
||||
private final Location location;
|
||||
|
||||
public PacketPlayOutEntityTeleport(int entityId, Location location) {
|
||||
Preconditions.checkNotNull(location, "location");
|
||||
this.entityId = entityId;
|
||||
this.location = location;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutEntityTeleport{entityId=%s,location=%s}", entityId, location);
|
||||
}
|
||||
|
||||
public int getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
public Location getLocation() {
|
||||
return location;
|
||||
}
|
||||
/** Entity's new location */
|
||||
@NonNull private final Location location;
|
||||
}
|
||||
+17
-45
@@ -1,60 +1,32 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import lombok.Data;
|
||||
import lombok.NonNull;
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.EntityType;
|
||||
|
||||
import me.neznamy.tab.api.protocol.TabPacket;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcher;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Custom class for holding data used in PacketPlayOutSpawnEntityLiving minecraft packet.
|
||||
*/
|
||||
@Data
|
||||
public class PacketPlayOutSpawnEntityLiving implements TabPacket {
|
||||
|
||||
/** Entity's id */
|
||||
private final int entityId;
|
||||
|
||||
private final UUID uuid;
|
||||
/** Entity's UUID (for 1.9+ servers) */
|
||||
@NonNull private final UUID uniqueId;
|
||||
|
||||
private final EntityType entityType;
|
||||
/** Type of entity */
|
||||
@NonNull private final EntityType entityType;
|
||||
|
||||
private final Location location;
|
||||
/** Entity's spawn location */
|
||||
@NonNull private final Location location;
|
||||
|
||||
/** Entity's data for 1.14- servers */
|
||||
private final DataWatcher dataWatcher;
|
||||
|
||||
public PacketPlayOutSpawnEntityLiving(int entityId, UUID uuid, EntityType entityType, Location location, DataWatcher dataWatcher) {
|
||||
Preconditions.checkNotNull(uuid, "uuid");
|
||||
Preconditions.checkNotNull(entityType, "entityType");
|
||||
Preconditions.checkNotNull(location, "location");
|
||||
this.entityId = entityId;
|
||||
this.uuid = uuid;
|
||||
this.entityType = entityType;
|
||||
this.location = location;
|
||||
this.dataWatcher = dataWatcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format("PacketPlayOutSpawnEntityLiving{entityId=%s,uuid=%s,entityType=%s,location=%s,dataWatcher=%s}",
|
||||
entityId, uuid, entityType, location, dataWatcher);
|
||||
}
|
||||
|
||||
public int getEntityId() {
|
||||
return entityId;
|
||||
}
|
||||
|
||||
public UUID getUniqueId() {
|
||||
return uuid;
|
||||
}
|
||||
|
||||
public EntityType getEntityType() {
|
||||
return entityType;
|
||||
}
|
||||
|
||||
public Location getLocation() {
|
||||
return location;
|
||||
}
|
||||
|
||||
public DataWatcher getDataWatcher() {
|
||||
return dataWatcher;
|
||||
}
|
||||
}
|
||||
+14
-23
@@ -1,22 +1,25 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms.datawatcher;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NonNull;
|
||||
import lombok.ToString;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import me.neznamy.tab.api.util.Preconditions;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
|
||||
/**
|
||||
* A class representing the n.m.s.DataWatcher class to make work with it much easier
|
||||
*/
|
||||
@ToString
|
||||
public class DataWatcher {
|
||||
|
||||
//DataWatcher data
|
||||
/** Watched data */
|
||||
private final Map<Integer, DataWatcherItem> dataValues = new HashMap<>();
|
||||
|
||||
//a helper for easier data write
|
||||
private final DataWatcherHelper helper = new DataWatcherHelper(this);
|
||||
/** Helper for easier data write */
|
||||
@Getter private final DataWatcherHelper helper = new DataWatcherHelper(this);
|
||||
|
||||
/**
|
||||
* Sets value into data values
|
||||
@@ -26,9 +29,7 @@ public class DataWatcher {
|
||||
* @param value
|
||||
* value
|
||||
*/
|
||||
public void setValue(DataWatcherObject type, Object value){
|
||||
Preconditions.checkNotNull(type, "type");
|
||||
Preconditions.checkNotNull(value, "value");
|
||||
public void setValue(@NonNull DataWatcherObject type, @NonNull Object value){
|
||||
dataValues.put(type.getPosition(), new DataWatcherItem(type, value));
|
||||
}
|
||||
|
||||
@@ -53,15 +54,6 @@ public class DataWatcher {
|
||||
return dataValues.get(position);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns helper created by this instance
|
||||
*
|
||||
* @return data write helper
|
||||
*/
|
||||
public DataWatcherHelper helper() {
|
||||
return helper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the class into an instance of NMS.DataWatcher
|
||||
*
|
||||
@@ -73,19 +65,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;
|
||||
}
|
||||
@@ -100,8 +92,7 @@ public class DataWatcher {
|
||||
* if thrown by reflective operation
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static DataWatcher fromNMS(Object nmsWatcher) throws ReflectiveOperationException {
|
||||
Preconditions.checkNotNull(nmsWatcher, "nmsWatcher");
|
||||
public static DataWatcher fromNMS(@NonNull Object nmsWatcher) throws ReflectiveOperationException {
|
||||
DataWatcher watcher = new DataWatcher();
|
||||
List<Object> items = (List<Object>) nmsWatcher.getClass().getMethod("c").invoke(nmsWatcher);
|
||||
if (items != null) {
|
||||
|
||||
+10
-10
@@ -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;
|
||||
|
||||
/**
|
||||
@@ -65,7 +65,7 @@ public class DataWatcherHelper {
|
||||
* flags to write
|
||||
*/
|
||||
public void setEntityFlags(byte flags) {
|
||||
data.setValue(new DataWatcherObject(0, registry.getByte()), flags);
|
||||
data.setValue(new DataWatcherObject(0, registry.getTypeByte()), flags);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -79,12 +79,12 @@ public class DataWatcherHelper {
|
||||
public void setCustomName(String customName, ProtocolVersion clientVersion) {
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 13) {
|
||||
try {
|
||||
data.setValue(new DataWatcherObject(2, registry.getOptionalComponent()), Optional.ofNullable(((BukkitPacketBuilder)TAB.getInstance().getPlatform().getPacketBuilder()).toNMSComponent(IChatBaseComponent.optimizedComponent(customName), clientVersion)));
|
||||
data.setValue(new DataWatcherObject(2, registry.getTypeOptionalComponent()), Optional.ofNullable(((BukkitPacketBuilder)TAB.getInstance().getPlatform().getPacketBuilder()).toNMSComponent(IChatBaseComponent.optimizedComponent(customName), clientVersion)));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
TAB.getInstance().getErrorManager().printError("Failed to create component", e);
|
||||
}
|
||||
} else if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 8){
|
||||
data.setValue(new DataWatcherObject(2, registry.getString()), customName);
|
||||
data.setValue(new DataWatcherObject(2, registry.getTypeString()), customName);
|
||||
} else {
|
||||
//name length is limited to 64 characters on <1.8
|
||||
String cutName = (customName.length() > 64 ? customName.substring(0, 64) : customName);
|
||||
@@ -104,7 +104,7 @@ public class DataWatcherHelper {
|
||||
*/
|
||||
public void setCustomNameVisible(boolean visible) {
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 9) {
|
||||
data.setValue(new DataWatcherObject(3, registry.getBoolean()), visible);
|
||||
data.setValue(new DataWatcherObject(3, registry.getTypeBoolean()), visible);
|
||||
} else {
|
||||
data.setValue(new DataWatcherObject(3, null), (byte)(visible?1:0));
|
||||
}
|
||||
@@ -118,7 +118,7 @@ public class DataWatcherHelper {
|
||||
*/
|
||||
public void setHealth(float health) {
|
||||
if (TabAPI.getInstance().getServerVersion().getMinorVersion() >= 6) {
|
||||
data.setValue(new DataWatcherObject(6, registry.getFloat()), health);
|
||||
data.setValue(new DataWatcherObject(6, registry.getTypeFloat()), health);
|
||||
} else {
|
||||
data.setValue(new DataWatcherObject(16, null), (int)health);
|
||||
}
|
||||
@@ -131,7 +131,7 @@ public class DataWatcherHelper {
|
||||
* flags to write
|
||||
*/
|
||||
public void setArmorStandFlags(byte flags) {
|
||||
data.setValue(new DataWatcherObject(armorStandFlagsPosition, registry.getByte()), flags);
|
||||
data.setValue(new DataWatcherObject(armorStandFlagsPosition, registry.getTypeByte()), flags);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+14
-30
@@ -1,32 +1,21 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms.datawatcher;
|
||||
|
||||
import lombok.Data;
|
||||
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
|
||||
*/
|
||||
@Data
|
||||
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;
|
||||
|
||||
/**
|
||||
* Constructs new instance of the object with given parameters
|
||||
*
|
||||
* @param type
|
||||
* value type
|
||||
* @param value
|
||||
* value
|
||||
*/
|
||||
public DataWatcherItem(DataWatcherObject type, Object value){
|
||||
Preconditions.checkNotNull(type, "type");
|
||||
Preconditions.checkNotNull(value, "value");
|
||||
this.type = type;
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns and instance of this class from given NMS item
|
||||
*
|
||||
@@ -38,19 +27,14 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
public DataWatcherObject getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public Object getValue() {
|
||||
return value;
|
||||
return new DataWatcherItem(object, value);
|
||||
}
|
||||
}
|
||||
+7
-24
@@ -1,34 +1,17 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms.datawatcher;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* A class representing the n.m.s.DataWatcherObject class to make work with it much easier
|
||||
*/
|
||||
@Data @AllArgsConstructor
|
||||
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;
|
||||
|
||||
/**
|
||||
* Constructs a new instance of this class with given parameters
|
||||
*
|
||||
* @param position
|
||||
* position in DataWatcher
|
||||
* @param classType
|
||||
* value class type
|
||||
*/
|
||||
public DataWatcherObject(int position, Object classType){
|
||||
this.position = position;
|
||||
this.classType = classType;
|
||||
}
|
||||
|
||||
public int getPosition() {
|
||||
return position;
|
||||
}
|
||||
|
||||
public Object getClassType() {
|
||||
return classType;
|
||||
}
|
||||
/** Value encoder for 1.9+ */
|
||||
private final Object serializer;
|
||||
}
|
||||
+32
-37
@@ -1,6 +1,7 @@
|
||||
package me.neznamy.tab.platforms.bukkit.nms.datawatcher;
|
||||
|
||||
import me.neznamy.tab.platforms.bukkit.nms.NMSStorage;
|
||||
import lombok.Getter;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.storage.NMSStorage;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@@ -9,11 +10,20 @@ import java.util.Map;
|
||||
*/
|
||||
public class DataWatcherRegistry {
|
||||
|
||||
private Object registryByte;
|
||||
private Object registryFloat;
|
||||
private Object registryString;
|
||||
private Object registryOptionalIChatBaseComponent;
|
||||
private Object registryBoolean;
|
||||
/** Byte encoder */
|
||||
@Getter private Object typeByte;
|
||||
|
||||
/** Float encoder */
|
||||
@Getter private Object typeFloat;
|
||||
|
||||
/** String encoder */
|
||||
@Getter private Object typeString;
|
||||
|
||||
/** Encoder for optional component */
|
||||
@Getter private Object typeOptionalComponent;
|
||||
|
||||
/** Boolean encoder */
|
||||
@Getter private Object typeBoolean;
|
||||
|
||||
/**
|
||||
* Initializes required NMS classes and fields
|
||||
@@ -23,43 +33,28 @@ public class DataWatcherRegistry {
|
||||
Map<String, Object> fields = nms.getStaticFields(nms.DataWatcherRegistry);
|
||||
if (fields.containsKey("a")) {
|
||||
// Bukkit mapping
|
||||
registryByte = fields.get("a");
|
||||
registryFloat = fields.get("c");
|
||||
registryString = fields.get("d");
|
||||
typeByte = fields.get("a");
|
||||
typeFloat = fields.get("c");
|
||||
typeString = fields.get("d");
|
||||
if (nms.getMinorVersion() >= 13) {
|
||||
registryOptionalIChatBaseComponent = fields.get("f");
|
||||
registryBoolean = fields.get("i");
|
||||
if (nms.is1_19_3Plus()) {
|
||||
typeOptionalComponent = fields.get("g");
|
||||
typeBoolean = fields.get("j");
|
||||
} else {
|
||||
typeOptionalComponent = fields.get("f");
|
||||
typeBoolean = fields.get("i");
|
||||
}
|
||||
} else {
|
||||
registryBoolean = fields.get("h");
|
||||
typeBoolean = fields.get("h");
|
||||
}
|
||||
} else {
|
||||
// Mojang mapping
|
||||
registryByte = fields.get("BYTE");
|
||||
registryFloat = fields.get("FLOAT");
|
||||
registryString = fields.get("STRING");
|
||||
registryOptionalIChatBaseComponent = fields.get("OPTIONAL_COMPONENT");
|
||||
registryBoolean = fields.get("BOOLEAN");
|
||||
typeByte = fields.get("BYTE");
|
||||
typeFloat = fields.get("FLOAT");
|
||||
typeString = fields.get("STRING");
|
||||
typeOptionalComponent = fields.get("OPTIONAL_COMPONENT");
|
||||
typeBoolean = fields.get("BOOLEAN");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Object getByte() {
|
||||
return registryByte;
|
||||
}
|
||||
|
||||
public Object getFloat() {
|
||||
return registryFloat;
|
||||
}
|
||||
|
||||
public Object getString() {
|
||||
return registryString;
|
||||
}
|
||||
|
||||
public Object getOptionalComponent() {
|
||||
return registryOptionalIChatBaseComponent;
|
||||
}
|
||||
|
||||
public Object getBoolean() {
|
||||
return registryBoolean;
|
||||
}
|
||||
}
|
||||
+29
-41
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
+46
-26
@@ -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");
|
||||
}
|
||||
}
|
||||
+51
-33
@@ -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");
|
||||
}
|
||||
}
|
||||
+200
-141
@@ -1,35 +1,41 @@
|
||||
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;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import me.neznamy.tab.api.ProtocolVersion;
|
||||
import me.neznamy.tab.api.util.ReflectionUtils;
|
||||
import me.neznamy.tab.platforms.bukkit.nms.datawatcher.DataWatcherRegistry;
|
||||
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
|
||||
private static NMSStorage instance;
|
||||
/** Instance of this class */
|
||||
@Getter @Setter 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"
|
||||
protected final int minorVersion = Integer.parseInt(serverPackage.split("_")[1]);
|
||||
/** Server's minor version */
|
||||
@Getter 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 */
|
||||
@Getter private final boolean is1_19_3Plus = ReflectionUtils.classExists("net.minecraft.network.protocol.game.ClientboundPlayerInfoUpdatePacket");
|
||||
|
||||
//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;
|
||||
@@ -39,24 +45,21 @@ public abstract class NMSStorage {
|
||||
public Field PLAYER_CONNECTION;
|
||||
public Field NETWORK_MANAGER;
|
||||
public Field CHANNEL;
|
||||
public Field EntityHuman_ProfilePublicKey;
|
||||
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;
|
||||
@@ -69,10 +72,17 @@ public abstract class NMSStorage {
|
||||
public Field DataWatcherObject_SLOT;
|
||||
public Field DataWatcherObject_SERIALIZER;
|
||||
public Method DataWatcher_REGISTER;
|
||||
private final DataWatcherRegistry registry;
|
||||
@Getter private final DataWatcherRegistry dataWatcherRegistry;
|
||||
//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 +93,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 +105,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 +134,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 +146,23 @@ 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$Data;
|
||||
public Method ClientboundPlayerInfoRemovePacket_getEntries;
|
||||
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 +170,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 +188,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,14 +211,17 @@ 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 {
|
||||
ProtocolVersion.UNKNOWN_SERVER_VERSION.setMinorVersion(minorVersion); //fixing compatibility with forks that set version field value to "Unknown"
|
||||
loadClasses();
|
||||
@@ -220,8 +238,7 @@ 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);
|
||||
dataWatcherRegistry = new DataWatcherRegistry(this);
|
||||
dataWatcher();
|
||||
entityTeleport();
|
||||
spawnEntityLiving();
|
||||
@@ -235,28 +252,12 @@ public abstract class NMSStorage {
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets new instance
|
||||
* Loads PacketPlayOutChat's methods, fields and constructors
|
||||
*
|
||||
* @param instance
|
||||
* new instance
|
||||
* @throws ReflectiveOperationException
|
||||
* If some method, field or constructor was not found
|
||||
*/
|
||||
public static void setInstance(NMSStorage instance) {
|
||||
NMSStorage.instance = instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns instance
|
||||
*
|
||||
* @return instance
|
||||
*/
|
||||
public static NMSStorage getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
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 +276,55 @@ 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);
|
||||
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);
|
||||
}
|
||||
}
|
||||
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 +355,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 +381,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 +426,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 +440,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 +469,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 +487,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 +509,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 +547,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 +569,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 +606,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 +625,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 +655,29 @@ 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];
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
+49
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
@@ -2,6 +2,5 @@ dependencies {
|
||||
implementation(projects.shared)
|
||||
implementation(libs.bstats.bungeecord)
|
||||
compileOnly(libs.bungeecord)
|
||||
compileOnly(libs.premiumVanish)
|
||||
compileOnly(libs.redisBungee)
|
||||
}
|
||||
+3
-4
@@ -1,5 +1,6 @@
|
||||
package me.neznamy.tab.platforms.bungeecord;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import me.neznamy.tab.api.TabAPI;
|
||||
import me.neznamy.tab.api.TabConstants;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
@@ -13,14 +14,12 @@ import net.md_5.bungee.event.EventHandler;
|
||||
/**
|
||||
* The core for BungeeCord forwarding events into all enabled features
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class BungeeEventListener implements Listener {
|
||||
|
||||
/** Reference to BungeeCord platform implementation */
|
||||
private final BungeePlatform platform;
|
||||
|
||||
public BungeeEventListener(BungeePlatform platform) {
|
||||
this.platform = platform;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disconnect event listener to forward the event to all features
|
||||
*
|
||||
|
||||
+56
-13
@@ -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,12 @@ public class BungeePacketBuilder extends PacketBuilder {
|
||||
|
||||
@Override
|
||||
public Object build(PacketPlayOutPlayerInfo packet, ProtocolVersion clientVersion) {
|
||||
if (clientVersion.getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId() &&
|
||||
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();
|
||||
@@ -56,7 +62,8 @@ public class BungeePacketBuilder extends PacketBuilder {
|
||||
} else if (clientVersion.getMinorVersion() < 8) {
|
||||
item.setDisplayName(String.valueOf(data.getName())); //avoiding NPE, 1.7 client requires this, 1.8 added a leading boolean
|
||||
}
|
||||
if (data.getGameMode() != null) item.setGamemode(data.getGameMode().ordinal()-1);
|
||||
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())});
|
||||
@@ -65,13 +72,25 @@ public class BungeePacketBuilder extends PacketBuilder {
|
||||
}
|
||||
item.setUsername(data.getName());
|
||||
item.setUuid(data.getUniqueId());
|
||||
item.setChatSessionId(data.getChatSessionId());
|
||||
item.setPublicKey((PlayerPublicKey) data.getProfilePublicKey());
|
||||
items.add(item);
|
||||
}
|
||||
PlayerListItem bungeePacket = new PlayerListItem();
|
||||
bungeePacket.setAction(PlayerListItem.Action.valueOf(packet.getAction().toString().replace("GAME_MODE", "GAMEMODE")));
|
||||
bungeePacket.setItems(items.toArray(new Item[0]));
|
||||
return bungeePacket;
|
||||
if (clientVersion.getNetworkId() >= ProtocolVersion.V1_19_3.getNetworkId()) {
|
||||
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;
|
||||
} else {
|
||||
PlayerListItem bungeePacket = new PlayerListItem();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -106,13 +125,37 @@ 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 (bungeePacket instanceof PlayerListItemRemove) {
|
||||
return new PacketPlayOutPlayerInfo(
|
||||
EnumPlayerInfoAction.REMOVE_PLAYER,
|
||||
Arrays.stream(((PlayerListItemRemove) bungeePacket).getUuids()).map(PlayerInfoData::new).collect(Collectors.toList())
|
||||
);
|
||||
}
|
||||
return new PacketPlayOutPlayerInfo(EnumPlayerInfoAction.valueOf(item.getAction().toString().replace("GAMEMODE", "GAME_MODE")), listData);
|
||||
Item[] items = bungeePacket instanceof PlayerListItemUpdate ? ((PlayerListItemUpdate) bungeePacket).getItems() : ((PlayerListItem) bungeePacket).getItems();
|
||||
List<PlayerInfoData> listData = new ArrayList<>();
|
||||
for (Item i : items) {
|
||||
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()));
|
||||
}
|
||||
if (bungeePacket instanceof PlayerListItemUpdate) {
|
||||
EnumPlayerInfoAction[] array = ((PlayerListItemUpdate) bungeePacket).getActions().stream().map(this::toCustomAction).toArray(EnumPlayerInfoAction[]::new);
|
||||
return new PacketPlayOutPlayerInfo(EnumSet.of(array[0], array), listData);
|
||||
} else {
|
||||
return new PacketPlayOutPlayerInfo(toCustomAction(((PlayerListItem) bungeePacket).getAction()), listData);
|
||||
}
|
||||
}
|
||||
|
||||
private EnumPlayerInfoAction toCustomAction(Object action) {
|
||||
return EnumPlayerInfoAction.valueOf(action.toString().replace("GAMEMODE", "GAME_MODE"));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+11
-15
@@ -5,12 +5,14 @@ import io.netty.buffer.ByteBuf;
|
||||
import io.netty.channel.ChannelDuplexHandler;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelPromise;
|
||||
import lombok.AllArgsConstructor;
|
||||
import me.neznamy.tab.api.TabFeature;
|
||||
import me.neznamy.tab.api.TabPlayer;
|
||||
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;
|
||||
@@ -48,26 +50,19 @@ public class BungeePipelineInjector extends PipelineInjector {
|
||||
/**
|
||||
* Custom channel duplex handler override
|
||||
*/
|
||||
@AllArgsConstructor
|
||||
public class BungeeChannelDuplexHandler extends ChannelDuplexHandler {
|
||||
|
||||
/** Injected player */
|
||||
protected final TabPlayer player;
|
||||
|
||||
/**
|
||||
* Constructs new instance with given player
|
||||
*
|
||||
* @param player
|
||||
* player to inject
|
||||
*/
|
||||
public BungeeChannelDuplexHandler(TabPlayer player) {
|
||||
this.player = player;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(ChannelHandlerContext context, Object packet, ChannelPromise channelPromise) {
|
||||
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 +86,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,18 +106,19 @@ 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());
|
||||
}
|
||||
}
|
||||
RedisBungeeSupport redis = (RedisBungeeSupport) TAB.getInstance().getFeatureManager().getFeature(TabConstants.Feature.REDIS_BUNGEE);
|
||||
if (redis != null) {
|
||||
for (RedisPlayer p : redis.getRedisPlayers().values()) {
|
||||
if (col.contains(p.getNickName()) && !packet.getName().equals(p.getTeamName())) {
|
||||
if (col.contains(p.getNickname()) && !packet.getName().equals(p.getTeamName())) {
|
||||
logTeamOverride(packet.getName(), p.getName(), p.getTeamName());
|
||||
col.remove(p.getNickName());
|
||||
col.remove(p.getNickname());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,10 +3,13 @@ package me.neznamy.tab.platforms.bungeecord;
|
||||
import com.imaginarycode.minecraft.redisbungee.RedisBungeeAPI;
|
||||
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.RedisSupport;
|
||||
import me.neznamy.tab.shared.proxy.ProxyPlatform;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* BungeeCord implementation of Platform
|
||||
@@ -14,29 +17,27 @@ 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());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadFeatures() {
|
||||
TAB tab = TAB.getInstance();
|
||||
if (tab.getConfiguration().isPipelineInjection())
|
||||
tab.getFeatureManager().registerFeature(TabConstants.Feature.PIPELINE_INJECTION, new BungeePipelineInjector());
|
||||
tab.getPlaceholderManager().registerPlayerPlaceholder(TabConstants.Placeholder.DISPLAY_NAME, 500, p -> ((ProxiedPlayer) p.getPlayer()).getDisplayName());
|
||||
super.loadFeatures();
|
||||
public @Nullable PipelineInjector getPipelineInjector() {
|
||||
return new BungeePipelineInjector();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable RedisSupport getRedisSupport() {
|
||||
if (ProxyServer.getInstance().getPluginManager().getPlugin(TabConstants.Plugin.REDIS_BUNGEE) != null) {
|
||||
if (RedisBungeeAPI.getRedisBungeeApi() != null) {
|
||||
tab.getFeatureManager().registerFeature(TabConstants.Feature.REDIS_BUNGEE, new RedisBungeeSupport());
|
||||
return new RedisBungeeSupport();
|
||||
} else {
|
||||
TAB.getInstance().getErrorManager().criticalError("RedisBungee plugin was detected, but it returned null API instance. Disabling hook.", null);
|
||||
}
|
||||
}
|
||||
for (ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) {
|
||||
tab.addPlayer(new BungeeTabPlayer(p));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -44,4 +45,11 @@ public class BungeePlatform extends ProxyPlatform {
|
||||
Plugin pl = ProxyServer.getInstance().getPluginManager().getPlugin(plugin);
|
||||
return pl == null ? null : pl.getDescription().getVersion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadPlayers() {
|
||||
for (ProxiedPlayer p : ProxyServer.getInstance().getPlayers()) {
|
||||
TAB.getInstance().addPlayer(new BungeeTabPlayer(p));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,22 +1,24 @@
|
||||
package me.neznamy.tab.platforms.bungeecord;
|
||||
|
||||
import de.myzelyam.api.vanish.BungeeVanishAPI;
|
||||
import io.netty.channel.Channel;
|
||||
import lombok.NonNull;
|
||||
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 +26,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,15 +53,14 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasPermission0(String permission) {
|
||||
Preconditions.checkNotNull(permission, "permission");
|
||||
public boolean hasPermission0(@NonNull String permission) {
|
||||
long time = System.nanoTime();
|
||||
boolean value = getPlayer().hasPermission(permission);
|
||||
TAB.getInstance().getCPUManager().addMethodTime("hasPermission", System.nanoTime()-time);
|
||||
@@ -99,17 +81,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
|
||||
@@ -124,8 +100,7 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
|
||||
* packet class
|
||||
* @return packet ID
|
||||
*/
|
||||
public int getPacketId(Class<? extends DefinedPacket> clazz) {
|
||||
Preconditions.checkNotNull(clazz, "class");
|
||||
public int getPacketId(@NonNull Class<? extends DefinedPacket> clazz) {
|
||||
try {
|
||||
return (int) getId.invoke(directionData, clazz, getPlayer().getPendingConnection().getVersion());
|
||||
} catch (ReflectiveOperationException e) {
|
||||
@@ -142,7 +117,8 @@ public class BungeeTabPlayer extends ProxyTabPlayer {
|
||||
@Override
|
||||
public boolean isVanished() {
|
||||
try {
|
||||
if (ProxyServer.getInstance().getPluginManager().getPlugin(TabConstants.Plugin.PREMIUM_VANISH) != null && BungeeVanishAPI.isInvisible(getPlayer())) return true;
|
||||
if (ProxyServer.getInstance().getPluginManager().getPlugin(TabConstants.Plugin.PREMIUM_VANISH) != null &&
|
||||
(boolean) Class.forName("de.myzelyam.api.vanish.BungeeVanishAPI").getMethod("isInvisible", ProxiedPlayer.class).invoke(null, getPlayer())) return true;
|
||||
} catch (Exception e) {
|
||||
TAB.getInstance().getErrorManager().printError("PremiumVanish v" + TAB.getInstance().getPlatform().getPluginVersion(TabConstants.Plugin.PREMIUM_VANISH) +
|
||||
" generated an error when retrieving vanish status of " + getName(), e);
|
||||
@@ -157,27 +133,21 @@ 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() {
|
||||
return ((InitialHandler)getPlayer().getPendingConnection()).getLoginRequest().getUuid();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendPluginMessage(byte[] message) {
|
||||
Preconditions.checkNotNull(message, "message");
|
||||
if (getPlayer().getServer() == null) return;
|
||||
getPlayer().getServer().sendData(TabConstants.PLUGIN_MESSAGE_CHANNEL_NAME, message);
|
||||
TAB.getInstance().getCPUManager().packetSent("Plugin Message (" + new String(message) + ")");
|
||||
|
||||
@@ -2,6 +2,7 @@ package me.neznamy.tab.platforms.bungeecord;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import me.neznamy.tab.api.util.ReflectionUtils;
|
||||
import org.bstats.bungeecord.Metrics;
|
||||
import org.bstats.charts.SimplePie;
|
||||
|
||||
@@ -25,8 +26,8 @@ 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/"));
|
||||
if (!ReflectionUtils.classExists("net.md_5.bungee.protocol.packet.PlayerListItemUpdate")) {
|
||||
getLogger().info(EnumChatFormat.color("&cThe plugin requires BungeeCord build #1671 and up (or an equivalent fork) to work."));
|
||||
return;
|
||||
}
|
||||
BungeePlatform platform = new BungeePlatform();
|
||||
@@ -40,20 +41,6 @@ public class Main extends Plugin {
|
||||
metrics.addCustomChart(new SimplePie(TabConstants.MetricsChart.GLOBAL_PLAYER_LIST_ENABLED, () -> TAB.getInstance().getFeatureManager().isFeatureEnabled(TabConstants.Feature.GLOBAL_PLAYER_LIST) ? "Yes" : "No"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for compatibility and returns true if version is supported, false if not
|
||||
*
|
||||
* @return true if version is compatible, false if not
|
||||
*/
|
||||
private boolean isVersionSupported() {
|
||||
try {
|
||||
Class.forName("net.md_5.bungee.protocol.Property");
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDisable() {
|
||||
if (TAB.getInstance() != null) TAB.getInstance().unload();
|
||||
|
||||
+1
-1
@@ -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.5
|
||||
|
||||
# Gradle properties
|
||||
org.gradle.daemon=true
|
||||
|
||||
+22
-16
@@ -3,36 +3,39 @@ 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
|
||||
bukkit = "1.16.5-R0.1-SNAPSHOT"
|
||||
bungeecord = "1.19-R0.1-SNAPSHOT"
|
||||
krypton = "0.59.2"
|
||||
krypton = "98fe09edf8" # Krypton uses commit-based versioning
|
||||
velocity = "3.1.2-SNAPSHOT"
|
||||
sponge = "8.1.0"
|
||||
|
||||
# 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"
|
||||
redisBungee = "0.10.1"
|
||||
|
||||
# Plugins
|
||||
shadow = "7.1.2"
|
||||
lombok = "6.6.1"
|
||||
|
||||
[libraries]
|
||||
|
||||
@@ -56,22 +59,25 @@ 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" }
|
||||
velocity = { group = "com.velocitypowered", name = "velocity-api", version.ref = "velocity" }
|
||||
sponge = { group = "org.spongepowered", name = "spongeapi", version.ref = "sponge" }
|
||||
|
||||
# Platform extras
|
||||
placeholderapi = { group = "me.clip", name = "placeholderapi", version.ref = "placeholderapi" }
|
||||
vault = { group = "com.github.MilkBowl", name = "VaultAPI", version.ref = "vault" }
|
||||
via = { group = "com.viaversion", name = "viaversion-api", version.ref = "via" }
|
||||
authlib = { group = "com.mojang", name = "authlib", version.ref = "authlib" }
|
||||
essentials = { group = "net.essentialsx", name = "EssentialsX", version.ref = "essentials" }
|
||||
premiumVanish = { group = "com.github.MyzelYam", name = "PremiumVanishAPI", version.ref = "premiumVanish" }
|
||||
redisBungee = { group = "com.github.limework.redisbungee", name = "RedisBungee-Bungee", version.ref = "redisBungee" }
|
||||
|
||||
# Metrics
|
||||
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-shadow = { group = "gradle.plugin.com.github.johnrengelman", name = "shadow", version.ref = "shadow" }
|
||||
plugin-lombok = { group = "io.freefair.gradle", name = "lombok-plugin", version.ref = "lombok"}
|
||||
Vendored
BIN
Binary file not shown.
Loaded 100 of 230 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user